Compare commits

...

55 Commits

Author SHA1 Message Date
github-actions[bot]
5fed49e05b chore: update versions (#3370)
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## @nhost/hasura-storage-js@2.8.0

### Minor Changes

-   aee9a80: chore: update typescript version to the latest

## @nhost/nhost-js@3.3.0

### Minor Changes

-   aee9a80: chore: update typescript version to the latest

### Patch Changes

-   Updated dependencies [aee9a80]
    -   @nhost/hasura-storage-js@2.8.0

## @nhost/apollo@9.0.0

### Patch Changes

-   Updated dependencies [aee9a80]
    -   @nhost/nhost-js@3.3.0

## @nhost/react-apollo@18.0.1

### Patch Changes

-   @nhost/apollo@9.0.0
-   @nhost/react@3.11.1

## @nhost/react-urql@15.0.1

### Patch Changes

-   @nhost/react@3.11.1

## @nhost/nextjs@2.2.9

### Patch Changes

-   @nhost/react@3.11.1

## @nhost/react@3.11.1

### Patch Changes

-   Updated dependencies [aee9a80]
    -   @nhost/nhost-js@3.3.0

## @nhost/vue@2.9.7

### Patch Changes

-   Updated dependencies [aee9a80]
    -   @nhost/nhost-js@3.3.0

## @nhost/dashboard@2.33.0

### Minor Changes

-   aee9a80: chore: update typescript version to the latest
-   5ef3f76: chore (dashboard): Use the new SDK in the Dashboard

### Patch Changes

- 9ed8ce8: fix (dashboard): Request new Mfa ticket after an invalid totp
when signing in
- fd3b5c7: fix (dashboard): Limit new project's name to a maximum of 32
charachters in E2E tests

## @nhost/docs@2.33.0

### Minor Changes

-   4ca9641: feat: added cloud development documentation

## @nhost-examples/cli@0.3.23

### Patch Changes

-   Updated dependencies [aee9a80]
    -   @nhost/nhost-js@3.3.0

## @nhost-examples/codegen-react-apollo@0.8.2

### Patch Changes

-   @nhost/react@3.11.1
-   @nhost/react-apollo@18.0.1

## @nhost-examples/codegen-react-query@0.8.2

### Patch Changes

-   @nhost/react@3.11.1

## @nhost-examples/codegen-react-urql@0.7.2

### Patch Changes

-   @nhost/react@3.11.1
-   @nhost/react-urql@15.0.1

## @nhost-examples/multi-tenant-one-to-many@2.2.24

### Patch Changes

-   Updated dependencies [aee9a80]
    -   @nhost/nhost-js@3.3.0

## @nhost-examples/nextjs@0.4.9

### Patch Changes

-   @nhost/react@3.11.1
-   @nhost/react-apollo@18.0.1
-   @nhost/nextjs@2.2.9

## @nhost-examples/node-storage@0.2.23

### Patch Changes

-   Updated dependencies [aee9a80]
    -   @nhost/nhost-js@3.3.0

## @nhost-examples/nextjs-server-components@0.6.2

### Patch Changes

-   Updated dependencies [aee9a80]
    -   @nhost/nhost-js@3.3.0

## @nhost-examples/sveltekit@0.8.2

### Patch Changes

-   Updated dependencies [aee9a80]
    -   @nhost/nhost-js@3.3.0

## @nhost-examples/react-apollo@1.6.2

### Patch Changes

-   @nhost/react@3.11.1
-   @nhost/react-apollo@18.0.1

## @nhost-examples/react-gqty@1.6.2

### Patch Changes

-   @nhost/react@3.11.1

## @nhost-examples/react-native@0.1.10

### Patch Changes

-   @nhost/react@3.11.1
-   @nhost/react-apollo@18.0.1

## @nhost-examples/vue-apollo@0.12.2

### Patch Changes

-   Updated dependencies [aee9a80]
    -   @nhost/nhost-js@3.3.0
    -   @nhost/apollo@9.0.0
    -   @nhost/vue@2.9.7

## @nhost-examples/vue-quickstart@0.6.2

### Patch Changes

-   @nhost/apollo@9.0.0
-   @nhost/vue@2.9.7

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-07-01 13:00:12 +02:00
robertkasza
aee9a80ac8 chore: update typescript to latest version (#3376)
### **PR Type**
Enhancement, Other


___

### **Description**
- Update TypeScript to version 5.8.3

- Upgrade react-merge-refs to version 3.0.2

- Refactor import syntax for mergeRefs

- Update tsconfig settings for modern JavaScript


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Enhancement</strong></td><td><details><summary>11
files</summary><table>
<tr>
<td><strong>ControlledAutocomplete.tsx</strong><dd><code>Update import
syntax for mergeRefs</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3376/files#diff-e1bbef866e556facc768a4239b443e193f460321689e368fcaae31c1a7c90478">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>ControlledCheckbox.tsx</strong><dd><code>Update import and
type reference for mergeRefs</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3376/files#diff-6f7d29735900a65f96663da4719bf151c5c98224c0a4e39d84b9e5e1db6b8c42">+2/-2</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>ControlledSelect.tsx</strong><dd><code>Update import and
type reference for mergeRefs</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3376/files#diff-4dda68ad8ea568203b515c9cb81eabd05109f55b10e96712924744aba8c22468">+2/-2</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>ControlledSwitch.tsx</strong><dd><code>Update import syntax
for mergeRefs</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3376/files#diff-173b17a003d4d330b2a7157258d0d34cbbbac6ae10840245294f22a8e6ef89ed">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>Checkbox.tsx</strong><dd><code>Update ForwardedRef type for
Checkbox component</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3376/files#diff-16e6ada0fb8f9c0ecaa00fcfc61c166d0c4c051efe5345d58ee5e4ab618ca0c5">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>Input.tsx</strong><dd><code>Update import syntax for
mergeRefs</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3376/files#diff-16643cae2ae39d51faa2ed4d7e045f4ec2af31ec5f91768cff742e0364400cb1">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>Radio.tsx</strong><dd><code>Update ForwardedRef type for
Radio component</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3376/files#diff-cab2e6a6d80963fb89d93b52ac228ab8f2e2d9d60d3d2567547da562e922ac63">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>LogsServiceFilter.tsx</strong><dd><code>Update ForwardedRef
type for LogsServiceFilter</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3376/files#diff-a590a7298a9f040df9f26c4eb37d10fc36f47c32996f71aec47796f08c44e892">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>ReferencedSchemaSelect.tsx</strong><dd><code>Update
ForwardedRef type for ReferencedSchemaSelect</code>&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3376/files#diff-450038e1607012a27e047ef0fa3e7c3ba58a4be7750940aa21c7166713b15c76">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>DataGrid.tsx</strong><dd><code>Update import syntax for
mergeRefs</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3376/files#diff-3bc6476aed14d8e4f26134fa452d22c41b6d3ecb0989871a8a99230a82496474">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>DataGridBooleanCell.tsx</strong><dd><code>Update
useDataGridCell generic type</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3376/files#diff-b700eacab9c73b147e248ce58d47a208c1e499124a20444efd73db7ecb68505f">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

</table></details></td></tr><tr><td><strong>Tests</strong></td><td><details><summary>1
files</summary><table>
<tr>
<td><strong>getAllocatedResources.test.ts</strong><dd><code>Remove
replicas property from test cases</code>&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3376/files#diff-c7db4fcf0770eb6e4eba76db64b1c705404538a3c204ea212028cad591c6d85a">+0/-2</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></details></td></tr><tr><td><strong>Configuration
changes</strong></td><td><details><summary>4 files</summary><table>
<tr>
<td><strong>tsconfig.base.json</strong><dd><code>Update TypeScript
compiler options for ES2022</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3376/files#diff-1542b47836923af953e9f14d4db6843171edb79232ba834276394b4ed3035c63">+4/-15</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>tsconfig.json</strong><dd><code>Update TypeScript target and
adjust paths</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3376/files#diff-9c3967d850eef2a3a17b5169df09de68ecb0f24ec46f0a9dd1b3ca7c6da7a384">+9/-4</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>tsconfig.json</strong><dd><code>Add typeRoots to compiler
options</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3376/files#diff-aa2c7a9ede2c1897e087efac48742cf228b8af42e73dec31ec6403461b98c63a">+6/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>tsconfig.json</strong><dd><code>Add typeRoots to compiler
options</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3376/files#diff-6b59df76b8f353267e50f13e5fdd23ff7490a417bdc9b7e9f4e94aaafa448dcd">+7/-2</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

</table></details></td></tr><tr><td><strong>Dependencies</strong></td><td><details><summary>2
files</summary><table>
<tr>
<td><strong>package.json</strong><dd><code>Upgrade react-merge-refs to
version 3.0.2</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3376/files#diff-2d8d55c799cd71f1b35e831f075f8178ed1734c4820a2ad548b4dd24d6938d7c">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>package.json</strong><dd><code>Upgrade TypeScript to version
5.8.3</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3376/files#diff-7ae45ad102eab3b6d7e7896acd08c427a9b25b346470d7bc6507b6481575d519">+2/-2</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></details></td></tr></tr></tbody></table>

___

> <details> <summary> Need help?</summary><li>Type <code>/help how to
...</code> in the comments thread for any questions about PR-Agent
usage.</li><li>Check out the <a
href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a>
for more information.</li></details>
2025-07-01 11:07:17 +02:00
robertkasza
5ef3f76ea0 chore (dashboard): use new sdk on the dashboard (#3374)
### **PR Type**
Enhancement, Bug fix


___

### **Description**
- Migrate from @nhost/nextjs to @nhost/nhost-js-beta

- Update authentication flow and components

- Refactor Apollo client and GraphQL operations

- Implement new session management and storage


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Error
handling</strong></td><td><details><summary>1 files</summary><table>
<tr>
<td><strong>MfaOtpForm.tsx</strong><dd><code>Refactor MFA OTP form error
handling</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-88ee3610a0658d5eead85db025a5e91e74a4d2f2a836adf7eb44ff80888a613b">+11/-9</a>&nbsp;
&nbsp; </td>

</tr>

</table></details></td></tr><tr><td><strong>Enhancement</strong></td><td><details><summary>66
files</summary><table>
<tr>
<td><strong>AccountMenu.tsx</strong><dd><code>Update account menu to use
new SDK hooks</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-57e70c7e6a22a5ab5271b2f36a54eabf544d9f62cd18dae83e2e89b125e77e0c">+10/-10</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>AuthenticatedLayout.tsx</strong><dd><code>Refactor
authenticated layout with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-2d69ccffd267658f76d77a864cdece93fc222e08f6025955795fc6f4697f60e7">+15/-13</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>MobileNav.tsx</strong><dd><code>Update mobile navigation to
use new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-88408885daaec8805bd085b53462c9f2d95db32f7e523912837a8167211b4fb2">+14/-8</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>UnauthenticatedLayout.tsx</strong><dd><code>Refactor
unauthenticated layout with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-54ee5ad9c01e99ffb05218020a6b97d091cd97cc53ad27e950480a3e675f2220">+2/-2</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>ErrorToast.tsx</strong><dd><code>Update error toast to use
new SDK hooks</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-189ba99303a20e964b5e3f3d6f1cf95c6376780a59604d1dee98aa84d9a2a9dc">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>DisableMfaButton.tsx</strong><dd><code>Refactor MFA disable
button with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-8548174093cd8b3bcd754b630c0bcc946e7cdc80176f8e0f0540fd60c9e47486">+17/-18</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>MfaQRCodeAndTOTPSecret.tsx</strong><dd><code>Update MFA
enable process with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-91870bba41e6e807ced8d185a9d61282540ca82741a938b12f20c4f452bdabf8">+30/-29</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>useMfaEnabled.ts</strong><dd><code>Refactor MFA enabled hook
with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-31d2af339a8dd32beff8cce79962fa0dd23b6c89687b21aa75663ebeccb0b154">+3/-3</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>CreatePATForm.tsx</strong><dd><code>Update PAT creation form
with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-75aecb06ebb3e3cd0de6bf253af6966e245e46e9b739314d49073ba2c80a3a90">+6/-6</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>DeleteAccount.tsx</strong><dd><code>Refactor account
deletion with new SDK hooks</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-3d84927ffa4b91d986ff6c6f601b3476503220e1c1d8cde25ebf72c8d0ed6b9e">+4/-3</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>DisplayNameSetting.tsx</strong><dd><code>Update display name
setting with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-a1daec18d5c3196aee5b2c5303db5654724f8d37cfa427594951a4d02fbe32db">+4/-2</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>EmailSetting.tsx</strong><dd><code>Refactor email setting
with new SDK hooks</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-98bdf4ebec67ab2b4cd475c9df16a39a66505da961a8448eb5e41a33544dcb38">+4/-3</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>useOnChangePasswordHandler.ts</strong><dd><code>Update
password change handler with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-be0c02de792e4ba1b71258eb3992bbc531bc37658cbad0e01e2db4419a9285f1">+4/-4</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>useOnAddNewSecurityKeyHandler.ts</strong><dd><code>Refactor
security key addition with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-3514a6d1514269a83f37fc25e9cb24add9d5d74f9cf3341293c0e0f2a4c2e286">+10/-2</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>useRemoveSecurityKey.ts</strong><dd><code>Update security
key removal with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-5683e00a14f39018d8fe58a3116c2a8ea6d2f2a83abb2177bbf0ee8ddf0f97b5">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>SocialProvidersSettings.tsx</strong><dd><code>Refactor
social providers settings with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-82d7c0c9eb3a23586998b6eadff9e56b123b14d03179212ca82439d3bdcd6e96">+14/-5</a>&nbsp;
&nbsp; </td>

</tr>

<tr>

<td><strong>useActionWithElevatedPermissions.ts</strong><dd><code>Update
elevated permissions hook with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-3390879c4a0cd70aa1db6672a1607c5c2444c0bf653b711d73eda8ee466aa61a">+10/-16</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>useElevatedPermissions.ts</strong><dd><code>Refactor
elevated permissions hook with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-c1e4f573300c771149cc2e59918c9acf2ae5f8a6680800a899707c70800ba144">+11/-10</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>useGetSecurityKeys.ts</strong><dd><code>Update security keys
fetching with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-1f9fed870cab61f15e304342e4913edab0f5537eeb6230070de4b4f7173fa138">+3/-3</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>useGithubAuthentication.ts</strong><dd><code>Refactor GitHub
authentication with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-fad4875e0f07391dadcfc7e2dd481cafd5172dbb740c47e56fa75beb271618e1">+16/-8</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>SignInWithSecurityKey.tsx</strong><dd><code>Update security
key sign-in with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-dc892fd3d9fd3cc7efca35c813cea43c63aa691b1d55d376ac69a2d75065bde9">+9/-3</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>VerifyEmailDialog.tsx</strong><dd><code>Refactor email
verification dialog for security key</code>&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-24699641924d25d9b2d72b7df5da44d837e1c1e5c77b9f4b00f7c07d12c72c42">+4/-10</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>useSignInWithSecurityKey.ts</strong><dd><code>Update
security key sign-in hook with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-14ce9eae9e1ec03512bdd55198fbce47a81ce8ce769d002164926d2cc76e91aa">+34/-21</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>MfaSignInOtpForm.tsx</strong><dd><code>Refactor MFA OTP form
for sign-in</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-91eba232beb0543b1e972ed9a21a0be797ed94b720487834bb3316a5dbd732f5">+1/-2</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>SignInWithEmailAndPassword.tsx</strong><dd><code>Update
email/password sign-in with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-a2b70644663baf4f6f2cdffd846d4d743a5ca1f2a64c4b278b6f04c6c5c92161">+34/-12</a>&nbsp;
</td>

</tr>

<tr>

<td><strong>SignInWithEmailAndPasswordForm.tsx</strong><dd><code>Refactor
email/password sign-in form with loading state</code>&nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-a07fd6bd20c97d0c9c875e690cd3a80068fc58f74d3579feb210e189d32f5031">+5/-2</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>

<td><strong>useOnSignInWithEmailAndPasswordHandler.ts</strong><dd><code>Update
email/password sign-in handler with new SDK</code>&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-1a253bfc02c3267ab1c6b58c07aa06142b7e711d613b672c8420ff2861b12d27">+35/-37</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>useRequestNewMfaTicket.ts</strong><dd><code>Refactor MFA
ticket request with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-64a3a91cf75c8faf5bf6a9fdd23978659d68888744a92f82602b1a2f7290c1f6">+12/-11</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>useOnSignUpWithPasswordHandler.ts</strong><dd><code>Update
email/password sign-up handler with new SDK</code>&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-d1a80c5b1076129735ffff9ea879ca8c8fe88e548d06e98a1fb6bfd7147dae01">+14/-19</a>&nbsp;
</td>

</tr>

<tr>

<td><strong>useSignupWithSecurityKeyHandler.ts</strong><dd><code>Refactor
security key sign-up with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-cef4f710ea89c67e27e9fe77db2d2ebc6d774657e0671b21b7353f3e927126bd">+24/-17</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>CreateOrgFormDialog.tsx</strong><dd><code>Update
organization creation dialog with new SDK</code>&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-9a1ed9e851328393b81356d80ade3509016aa55c254ed1f4deb692b0bd96f02e">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>TransferProjectForm.tsx</strong><dd><code>Refactor project
transfer form with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-3324c79d8b4d48777467132ba0f13a95d4b0f1a9fbb4df9fd7f67735ac40cbbd">+2/-2</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>Soc2Download.tsx</strong><dd><code>Update SOC2 report
download with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-3768eb3fc718d4780028c34b5c76388e8d93cbbac94868f82c1a262fb9cc1100">+12/-19</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>AnnouncementsTray.tsx</strong><dd><code>Refactor
announcements tray with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-88fdcce3e90fa9e4d172858ae702855f86e6ece724ba443d8a6ed918999a1630">+2/-2</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>NotificationsTray.tsx</strong><dd><code>Update notifications
tray with new SDK hooks</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-8b559ee1d3176203e8a4e1588924d57944d09d792117ed578b27cd5401ee5d4f">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>OrgMember.tsx</strong><dd><code>Refactor organization member
component with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-a50b1baab968a7d3bd1459ba01107a13bd25e5077b6ad49a0d7e9dd88992276a">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>useFinishOrgCreation.ts</strong><dd><code>Update
organization creation finish hook with new SDK</code>&nbsp; &nbsp;
&nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-3b8bf7608ab36d8ab0df895e400f0d2d9e29fad2055b40b33d8d9912a27c99c3">+2/-2</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>useIsOrgAdmin.ts</strong><dd><code>Refactor organization
admin check with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-216a850dad38e829d0a8892c34d87426cd68f10c92f4c647673667dbbd11464d">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>MessageBox.tsx</strong><dd><code>Update dev assistant
message box with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-d4dd97b5a55f8246836226333d35a1c18c2907e47bdd2654707ed43ac54f2fb8">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>ApplicationInfo.tsx</strong><dd><code>Refactor application
info component with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-7372ad22d70c3c354d8e0dd442eb7e49f70f65a386b934b6eee7f8c4b89c3a3f">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>ApplicationPausedBanner.tsx</strong><dd><code>Update paused
application banner with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-fa94285530f7118d9f27a2d9088f5cf6ba71879d14957d91eb01dba16b6b6f1c">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>RemoveApplicationModal.tsx</strong><dd><code>Refactor
application removal modal with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-e454a42c12dcbfcfaa463ab3421037408634e3a539f460525c79d68adfc118ab">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>useAppPausedReason.ts</strong><dd><code>Update app paused
reason hook with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-51d220574b3da84f08d2cb134682172ed11b908c4d855ccc8d9de30805921a00">+2/-2</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>useCheckProvisioning.ts</strong><dd><code>Refactor
provisioning check hook with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-e1758bb8d3381f814d6619dc33eee8b36e39d2fcb6486d5c8cc3c46bbe62c555">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>useIsCurrentUserOwner.ts</strong><dd><code>Update current
user ownership check with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-3941cc4f23c66f12e94850e88e05ca142a627ab2d9ec797ff757dab679c58c0f">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>useProjectRedirectWhenReady.ts</strong><dd><code>Refactor
project redirect hook with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-a234bc908266de3091b23b5134a01fd769f96759eb52aa108d2ad4b796b0303f">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>ResetDatabasePasswordSettings.tsx</strong><dd><code>Update
database password reset with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-46fc60a26a2de3efb98e9778b1c6e82d62823ae5c7534037eb120728cba26288">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>DeploymentListItem.tsx</strong><dd><code>Refactor deployment
list item with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-2a548c457ff2ab8fc1bee326a6a3b5eae9d0d6eb18f5ae95bbdb437c3f6b0a73">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>

<td><strong>SystemEnvironmentVariableSettings.tsx</strong><dd><code>Update
system environment variables with new SDK</code>&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-b952daa2a34e49a14c5a471477fa2d50583091e420d88a3b941503b092d18e5c">+7/-4</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>BaseDirectorySettings.tsx</strong><dd><code>Refactor base
directory settings with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-50bcccdf949a19ce69fa86acdd63b5291fa2beaba07191a62c87d40ea5b94e88">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>DeploymentBranchSettings.tsx</strong><dd><code>Update
deployment branch settings with new SDK</code>&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-d8fc80cc734f593c686f873536856bf9103efb1115ca865709bbeb7bd940895e">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>useAppClient.ts</strong><dd><code>Refactor app client hook
with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-0aa83222c0e0eac6f0058070de2b199e5e78514cbba405eb98d3693329a93e65">+13/-6</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>useCurrentOrg.tsx</strong><dd><code>Update current
organization hook with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-97e1dbde4beece374834d5e81dd56fddeb5f1756a3358f6afecf88df93f6b0b0">+60/-3</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>useOrgs.ts</strong><dd><code>Refactor organizations hook
with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-995629b13bac07ec5c0e79caa8f5f8df19fc842d1b8cfe8fd2b1fcd9448868c4">+5/-6</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>useProject.ts</strong><dd><code>Update project hook with new
SDK</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-ef96f340af7a87a1fc60c42d8f4de846a2a54fde830a9461c64cfbc99dc11128">+8/-10</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>useProjectLogs.ts</strong><dd><code>Update project logs hook
with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-10efc67700b3f024dd03442eacd339802e951696d04caa76bd5a864bd5c7c83f">+2/-3</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>useProjectWithState.ts</strong><dd><code>Refactor project
with state hook using new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-4fa0e580d9f12e35ff5d2751597bf443bd055cd1c854cf6b356110724d424188">+7/-7</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>DataGridPreviewCell.tsx</strong><dd><code>Update data grid
preview cell with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-d7bffe5896d2c9bac505fa9675790c59549d4fb35a2ad0cce903cc0aa31a8321">+7/-3</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>FilesDataGrid.tsx</strong><dd><code>Refactor files data grid
with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-18c8df727e1a4fc6a94d03bd4a3a7a8cb3ad44d754803c4c7988c1c00a4b7caf">+20/-15</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>FilesDataGridControls.tsx</strong><dd><code>Update files
data grid controls with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-b85b40168e9c149331a68cb1a0cbec570c75233fa34385945e094b8f4c032974">+15/-30</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>useAccessToken.ts</strong><dd><code>Add new hook for
accessing token with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-1e7322930841a7ee092650eedafab9b83a8eb2d376aa299f3dfd790304a7ad21">+9/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>useDecodedAccessToken.ts</strong><dd><code>Add new hook for
decoding access token</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-ff3f112dfd0dae55404d17d972f5309d9a8cf0859222061e1c6f10e52c442390">+28/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>useElevateEmail.ts</strong><dd><code>Add new hook for email
elevation with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-b38d4c8f500fe9f40d4649e78907fc2f8691bd950b377e85be9142226b2b3460">+26/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>useHasuraClaims.ts</strong><dd><code>Add new hook for Hasura
claims with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-f5352373468a509527f74db8d16a632905284009ed386ea50cd9fb7f42817431">+11/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>useUserData.ts</strong><dd><code>Add new hook for user data
with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-3e3f3684aba10abe1d06d0be625a8077efe2e7d6a17b79d5ecddd43cfc190224">+12/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>index.ts</strong><dd><code>Remove old remote application
GraphQL client hook</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-d2d725306920bf5413fc010843e4ca13570b225febb200330e8c6902ae0b085c">+0/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

</table></details></td></tr><tr><td><strong>Tests</strong></td><td><details><summary>1
files</summary><table>
<tr>
<td><strong>useProjectLogs.test.ts</strong><dd><code>Refactor project
logs test with new SDK</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3374/files#diff-13d900aa08d06962a09628136b893801ad62a96c3ff89d380c5c4b7ae92d891e">+19/-31</a>&nbsp;
</td>

</tr>
</table></details></td></tr></tr></tbody></table>

___

> <details> <summary> Need help?</summary><li>Type <code>/help how to
...</code> in the comments thread for any questions about PR-Agent
usage.</li><li>Check out the <a
href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a>
for more information.</li></details>
2025-06-30 14:31:29 +02:00
David Barroso
4ca9641304 feat (docs): added cloud development documentation (#3377) 2025-06-25 23:24:10 +02:00
robertkasza
fd3b5c77e4 fix (dashboard): Limit new project name to a max of 32 chars (#3371)
### **PR Type**
Bug fix, Tests


___

### **Description**
- Limit new project name to 32 characters in E2E tests

- Update project creation test in upgrade-project.test.ts

- Add changeset for @nhost/dashboard patch


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Bug
fix</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>upgrade-project.test.ts</strong><dd><code>Limit project
name length in E2E test</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

dashboard/e2e/upgrade-project/upgrade-project.test.ts

<li>Modify project name generation to limit to 32 characters<br> <li>
Use <code>slice(0, 32)</code> on faker-generated project name


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3371/files#diff-8bafbe707eb1dff0c5ae24d6b0a514ff6e80889237de6c89ec330a93be138a12">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr><tr><td><strong>Documentation</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>weak-bottles-stare.md</strong><dd><code>Add changeset
for project name length fix</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; </dd></summary>
<hr>

.changeset/weak-bottles-stare.md

<li>Add new changeset file for @nhost/dashboard patch<br> <li> Document
fix for limiting project name to 32 characters


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3371/files#diff-352316104f52e31fc130d1016f300d58c243319c6e6e434c9bc6912402117d6a">+5/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr></tr></tbody></table>

___

> <details> <summary> Need help?</summary><li>Type <code>/help how to
...</code> in the comments thread for any questions about PR-Agent
usage.</li><li>Check out the <a
href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a>
for more information.</li></details>
2025-06-18 15:50:56 +02:00
robertkasza
9ed8ce8a5e fix (dashboard): request new mfa ticket after error when signing in (#3369)
### **PR Type**
Bug fix


___

### **Description**
- Request new MFA ticket after invalid TOTP

- Improve error handling in MFA flow

- Add comprehensive tests for MfaOtpForm

- Refactor SignInWithEmailAndPassword component


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Tests</strong></td><td><details><summary>1
files</summary><table>
<tr>
<td><strong>MfaOtpForm.test.tsx</strong><dd><code>Add comprehensive
tests for MfaOtpForm component</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3369/files#diff-0a0a9d4aa607a60fb4f38712686101d583426536ff6c177ea625cf8ce1946971">+519/-0</a>&nbsp;
</td>

</tr>

</table></details></td></tr><tr><td><strong>Enhancement</strong></td><td><details><summary>5
files</summary><table>
<tr>
<td><strong>MfaOtpForm.tsx</strong><dd><code>Implement MFA ticket
renewal and improve error handling</code>&nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3369/files#diff-88ee3610a0658d5eead85db025a5e91e74a4d2f2a836adf7eb44ff80888a613b">+39/-12</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>MfaSignInOtpForm.tsx</strong><dd><code>Add
requestNewMfaTicket prop to MfaSignInOtpForm</code>&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3369/files#diff-91eba232beb0543b1e972ed9a21a0be797ed94b720487834bb3316a5dbd732f5">+7/-2</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>SignInWithEmailAndPassword.tsx</strong><dd><code>Implement
requestNewMfaTicket function and pass to
MfaSignInOtpForm</code></dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3369/files#diff-a2b70644663baf4f6f2cdffd846d4d743a5ca1f2a64c4b278b6f04c6c5c92161">+20/-4</a>&nbsp;
&nbsp; </td>

</tr>

<tr>

<td><strong>useOnSignInWithEmailAndPasswordHandler.ts</strong><dd><code>Add
emailPasswordRef to store credentials for MFA renewal</code></dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3369/files#diff-1a253bfc02c3267ab1c6b58c07aa06142b7e711d613b672c8420ff2861b12d27">+19/-2</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>useRequestNewMfaTicket.ts</strong><dd><code>Create new hook
for requesting new MFA ticket</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3369/files#diff-64a3a91cf75c8faf5bf6a9fdd23978659d68888744a92f82602b1a2f7290c1f6">+28/-0</a>&nbsp;
&nbsp; </td>

</tr>
</table></details></td></tr><tr><td><strong>Bug
fix</strong></td><td><details><summary>1 files</summary><table>
<tr>
<td><strong>email.tsx</strong><dd><code>Fix page title for sign-in
page</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3369/files#diff-b5d7db4460066bc114cb766771612d6f908bd6e440f40de98e4ac311a26b50cd">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

</table></details></td></tr><tr><td><strong>Documentation</strong></td><td><details><summary>1
files</summary><table>
<tr>
<td><strong>lovely-days-whisper.md</strong><dd><code>Add changeset for
MFA ticket renewal fix</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3369/files#diff-1d7e7e258210abb910bb9c392731a3195ffca03024082c4b357e61c475dd3e3f">+5/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></details></td></tr></tr></tbody></table>

___

> <details> <summary> Need help?</summary><li>Type <code>/help how to
...</code> in the comments thread for any questions about PR-Agent
usage.</li><li>Check out the <a
href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a>
for more information.</li></details>
2025-06-18 14:02:18 +02:00
Ivan Kuznetsov
e7762cb2b5 fix (examples/nextjs-server-components): added missing await (#3364) 2025-06-17 11:53:38 +02:00
gssakash-nhost
e353d99de8 feat (examples/react-apollo): add more social providers (#3339)
### **User description**
___

### **PR Type**
Enhancement


___

### **Description**
- **Added OAuth Integrations**  
Enabled OAuth for Spotify, Twitch, GitLab, Bitbucket, WorkOS, Discord,
AzureAD, Facebook, Strava, Windows Live, and Twitter.
- **Enhanced UI**  
  Added login buttons for each provider.
- **Updated Configuration**  
Configured OAuth settings in `nhost.toml` and updated the authentication
version as necessary.
- **Included Example Credentials**  
  Added client credentials to `.secrets.example` for easy setup.

___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Enhancement</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>oauth-links.tsx</strong><dd><code>Add Spotify OAuth
button to login options</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></summary>
<hr>

examples/react-apollo/src/components/auth/oauth-links.tsx

<li>Imported Spotify icon from react-simple-icons<br> <li> Added Spotify
to useProviderLink hook<br> <li> Implemented Spotify login button with
styling


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3339/files#diff-f5999fa99948c7a83619e69ab669da87ca10146ad5742f93112e21b00932bc0e">+14/-2</a>&nbsp;
&nbsp; </td>

</tr>
</table></td></tr><tr><td><strong>Configuration
changes</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>nhost.toml</strong><dd><code>Enable Spotify OAuth in
e2e test project</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></summary>
<hr>

dashboard/e2e/e2e-tests-project/nhost/nhost.toml

- Enabled Spotify OAuth method


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3339/files#diff-9803f45ffb53ea1df64006a0bb2d3cb01ae0a3104f021945c546e97ee0e63f9e">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td>
    <details>
<summary><strong>.secrets.example</strong><dd><code>Add Spotify
credentials to secrets example</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></summary>
<hr>

examples/react-apollo/.secrets.example

- Added placeholders for Spotify client ID and secret


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3339/files#diff-8df9deedb2d447b8e687d9bddcdeb89e013380307483034c4fc8973354877703">+2/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td>
    <details>
<summary><strong>nhost.toml</strong><dd><code>Configure Spotify OAuth in
example project</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

examples/react-apollo/nhost/nhost.toml

<li>Enabled Spotify OAuth method<br> <li> Added configuration for
Spotify client ID and secret


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3339/files#diff-268d6c8dddd6990d60d62c1c923955c4e0e7549a80f0f5856192f889378416a0">+3/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr></tr></tbody></table>

___

> <details> <summary> Need help?</summary><li>Type <code>/help how to
...</code> in the comments thread for any questions about PR-Agent
usage.</li><li>Check out the <a
href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a>
for more information.</li></details>

---------

Co-authored-by: David Barroso <dbarrosop@dravetech.com>
2025-06-13 11:06:43 +02:00
David Barroso
c4d289a4d5 chore (docs): added limits section (#3367)
### **PR Type**
Documentation, Enhancement


___

### **Description**
- Added new 'Limits' section to Functions overview

- Detailed function execution timeout limits by project tier

- Included custom timeout option for Enterprise tier


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Documentation</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>overview.mdx</strong><dd><code>Add Function Execution
Timeout Limits Section</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

docs/products/functions/overview.mdx

<li>Added new 'Limits' section after deployment information<br> <li>
Listed function execution timeout limits for different project tiers<br>
<li> Included Starter, Pro, Teams, and Enterprise tier limits<br> <li>
Mentioned custom timeout values for Enterprise tier


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3367/files#diff-c1e4e354976e7a602620f2540bb357b7d4d73853f8310342a75e1e14d4fd35f3">+9/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr></tr></tbody></table>

___

> <details> <summary> Need help?</summary><li>Type <code>/help how to
...</code> in the comments thread for any questions about PR-Agent
usage.</li><li>Check out the <a
href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a>
for more information.</li></details>
2025-06-12 14:24:32 +02:00
github-actions[bot]
e2065e22df chore: update versions (#3360)
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## @nhost/dashboard@2.32.0

### Minor Changes

-   736862c: fix: update link to base directory docs in git settings
-   ea99fb3: chore: dashboard: improve messaging when git connected

### Patch Changes

-   d738884: chore (dashboard): Add link about antivirus integration

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-06-10 14:17:38 +02:00
robertkasza
d738884d7d chore (dashboard): Add link about antivirus integration (#3363)
### **PR Type**
Enhancement, Documentation


___

### **Description**
- Add antivirus integration documentation link

- Update HasuraStorageAVSettings component

- Create changeset for dashboard patch


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Enhancement</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>HasuraStorageAVSettings.tsx</strong><dd><code>Add
antivirus documentation link to settings component</code>&nbsp; &nbsp;
&nbsp; </dd></summary>
<hr>


dashboard/src/features/orgs/projects/storage/settings/components/StorageAVSettings/HasuraStorageAVSettings.tsx

- Add `docsLink` prop to component with antivirus documentation URL


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3363/files#diff-8670582543f3e6a6b3352202b89823cd68828328a987107e6c538e962347e781">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr><tr><td><strong>Documentation</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>pink-grapes-cheat.md</strong><dd><code>Add changeset
for dashboard antivirus link update</code>&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

.changeset/pink-grapes-cheat.md

<li>Create new changeset file for dashboard patch<br> <li> Add
description for antivirus integration link addition


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3363/files#diff-08b9cb5cc1ddca46a96721dbb3e6982d1833985dc4342ec17b9c1e7dfefa7cb4">+5/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr></tr></tbody></table>

___

> <details> <summary> Need help?</summary><li>Type <code>/help how to
...</code> in the comments thread for any questions about PR-Agent
usage.</li><li>Check out the <a
href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a>
for more information.</li></details>
2025-06-10 13:46:09 +02:00
robertkasza
b50404566f feat (dashboard): Add logs view to deployment page (#3352)
### **PR Type**
Enhancement, Documentation


___

### **Description**
- Add logs view to deployment page

- Refactor logs components for reusability

- Improve log filtering and service selection

- Update GraphQL queries and fragments


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Enhancement</strong></td><td><details><summary>22
files</summary><table>
<tr>
<td><strong>LogsRegexFilter.tsx</strong><dd><code>Add reusable
LogsRegexFilter component</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3352/files#diff-349fe23e29895116a6e03cd9277c455860af99f3690cc267b2cc7a07628c2530">+87/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>index.ts</strong><dd><code>Export LogsRegexFilter
component</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3352/files#diff-7fa93132c67731a94b80233a459cab4e83f6c9f3f5adaa21661876bc4c4240bd">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>LogsServiceFilter.tsx</strong><dd><code>Add reusable
LogsServiceFilter component</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3352/files#diff-a590a7298a9f040df9f26c4eb37d10fc36f47c32996f71aec47796f08c44e892">+69/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>index.ts</strong><dd><code>Export LogsServiceFilter
component</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3352/files#diff-e75bdc4d69b5725a63a6867b58a41fe4a9892d20c528f91382342959954e6ec7">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>DeploymentDetails.tsx</strong><dd><code>Create
DeploymentDetails component with logs view</code>&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3352/files#diff-e95736b80d6545cd9bab82a81263adef8e9c39f8428eae50420e4b65339b774b">+161/-0</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>DeploymentInfo.tsx</strong><dd><code>Add DeploymentInfo
component for log timestamps</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3352/files#diff-37a0e29e73146c3434fe95b0aa93b93f51db32211b2f2646e7248f7ac2dce1a6">+38/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>DeploymentServiceLogs.tsx</strong><dd><code>Implement
DeploymentServiceLogs component</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3352/files#diff-333a9783713e9a4bad1b5327e117cbe69148091abe8b9038d36132b5f4635bbe">+59/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>DeploymentServiceLogsHeader.tsx</strong><dd><code>Create
DeploymentServiceLogsHeader component</code>&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3352/files#diff-4f102c06ed32bb3d8245e415e76b0b14d2d4ae3abca6e234edf69278325c7a95">+81/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>index.tsx</strong><dd><code>Export DeploymentServiceLogs
component</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3352/files#diff-acd5fadae2f4f5f8a9de9717237d93ada3da2c3502bda78cfab123b9b8b4e8f3">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>index.ts</strong><dd><code>Export useDeployment
hook</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3352/files#diff-d45a44082ae4d2d5148c908d077ff69b69cf6ebcd8389e21f5381842cd1896f5">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>useDeployment.ts</strong><dd><code>Implement useDeployment
hook</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3352/files#diff-e733d29eeab1252ca05a48aa009938205b5f9b0b5e3f90535b6159c1e7ec9137">+59/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>index.ts</strong><dd><code>Export useProjectLogs
hook</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3352/files#diff-ae67977c734fbd0f10114658db5b715c09eb8cfe5ae720a96ae7692905e9071e">+2/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>useProjectLogs.ts</strong><dd><code>Implement useProjectLogs
hook</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3352/files#diff-10efc67700b3f024dd03442eacd339802e951696d04caa76bd5a864bd5c7c83f">+122/-0</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>LogsBody.tsx</strong><dd><code>Update LogsBody
component</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3352/files#diff-b628e511a7fb9b237ac691b27ab9585eed0d0803144cde66c3af7fa6f9a2dc40">+49/-31</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>LogsHeader.tsx</strong><dd><code>Refactor LogsHeader
component</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3352/files#diff-ebb3285aa776c9c5ea8b72672c4aafd55994c6c694998bbf56ca9c56d1e77664">+12/-125</a></td>

</tr>

<tr>
<td><strong>LogsServiceFilter.tsx</strong><dd><code>Remove
LogsServiceFilter component</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3352/files#diff-52634b3870eb08646192600c3ec6bb2737750327dcfa5c08435d99a108fb057c">+0/-29</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>LogsRangeSelector.tsx</strong><dd><code>Update
LogsRangeSelector component</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3352/files#diff-46dd7c795a79e4b443213ed10089651423d13e5c776ca72e3a95ae5e0f7f63c8">+14/-11</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>services.ts</strong><dd><code>Add custom templates fetcher
service</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3352/files#diff-8fcdaed33322718091b613ae22c65cc3eb61972904b5af46866b160c9bbbe48c">+2/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>[deploymentId].tsx</strong><dd><code>Update deployment
details page</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3352/files#diff-fbf95e7970ecb8157795fe4d1803c6913b1ba78183fa8a9b0ca9b9e4e9eccba2">+3/-144</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>logs.tsx</strong><dd><code>Refactor logs page to use new
hooks</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3352/files#diff-77489a68a7526d74f06d59019ad68c44728b7620637308d70fba38d6649b73fa">+9/-119</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>graphql.ts</strong><dd><code>Update generated GraphQL
types</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3352/files#diff-fbd5db84b560b1c91675004448c6c7fa0dcbfb28b9eb05d53b03e6cb7b83ebac">+75/-23</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>deployments.graphql</strong><dd><code>Update GraphQL queries
and fragments</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3352/files#diff-c8c4668b1999c73e78eb706631ce9d0e0e41debf66e616350436af4ae3095b76">+31/-22</a>&nbsp;
</td>

</tr>

</table></details></td></tr><tr><td><strong>Tests</strong></td><td><details><summary>3
files</summary><table>
<tr>
<td><strong>useDeployment.test.ts</strong><dd><code>Add tests for
useDeployment hook</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3352/files#diff-37c7c86c6c93931c8794f241db0605267d1a051786972c1ab763ca0fe94f2b2b">+183/-0</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>updateQuery.test.ts</strong><dd><code>Add tests for
updateQuery function</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3352/files#diff-9eeaaad41f9097bab3be3769f17e4de74c642d5a2313899df3663e1bd3856b30">+324/-0</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>useProjectLogs.test.ts</strong><dd><code>Add tests for
useProjectLogs hook</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3352/files#diff-13d900aa08d06962a09628136b893801ad62a96c3ff89d380c5c4b7ae92d891e">+328/-0</a>&nbsp;
</td>

</tr>
</table></details></td></tr></tr></tbody></table>

___

> <details> <summary> Need help?</summary><li>Type <code>/help how to
...</code> in the comments thread for any questions about PR-Agent
usage.</li><li>Check out the <a
href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a>
for more information.</li></details>
2025-06-10 12:01:45 +02:00
Emory Mercera
8caf3daa54 Improve sentence flow of paragraph. (#3359)
Improve sentence flow of paragraph.
2025-06-10 11:08:27 +02:00
Calvin
8a07613cbe fix(examples/docker-compose): fix postgres persistent data storage path (#3346) 2025-06-10 11:08:21 +02:00
David BM
736862c9cc fix (dashboard): update link to base directory docs in git settings (#3358)
### **PR Type**
Bug fix


___

### **Description**
- Update link to base directory docs in git settings

- Correct URL for base directory documentation


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Bug
fix</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>BaseDirectorySettings.tsx</strong><dd><code>Update Base
Directory Documentation Link</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; </dd></summary>
<hr>


dashboard/src/features/orgs/projects/git/settings/components/BaseDirectorySettings/BaseDirectorySettings.tsx

<li>Updated <code>docsLink</code> prop with correct URL for base
directory <br>documentation


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3358/files#diff-50bcccdf949a19ce69fa86acdd63b5291fa2beaba07191a62c87d40ea5b94e88">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr><tr><td><strong>Documentation</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>rich-cups-nail.md</strong><dd><code>Add Changeset for
Documentation Link Update</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></summary>
<hr>

.changeset/rich-cups-nail.md

<li>Added changeset file for minor version bump<br> <li> Described fix
for updating base directory docs link


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3358/files#diff-d085c64fa4e5869bd8d8aa20ef902840240daf6463c463f5a2ae9001932e961b">+5/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr></tr></tbody></table>

___

> <details> <summary> Need help?</summary><li>Type <code>/help how to
...</code> in the comments thread for any questions about PR-Agent
usage.</li><li>Check out the <a
href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a>
for more information.</li></details>
2025-06-06 16:12:41 +02:00
Nuno Pato
ea99fb31d7 chore: dashboard: improve messaging then git connected (#3348)
### **PR Type**
Enhancement, Documentation


___

### **Description**
- Improved GitHub connection messaging in SettingsLayout

- Updated DataBrowserSidebar for GitHub-connected projects

- Added 'graphite' to READ_ONLY_SCHEMAS list

- Created changeset for dashboard version bump


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Enhancement</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>SettingsLayout.tsx</strong><dd><code>Redesign GitHub
connection alert in SettingsLayout</code>&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

dashboard/src/features/orgs/layout/SettingsLayout/SettingsLayout.tsx

<li>Redesigned GitHub connection alert<br> <li> Changed alert severity
from warning to info<br> <li> Improved text content and styling<br> <li>
Added link to configuration overlays documentation


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3348/files#diff-aa21cda513a125d8cefc5e7b5e1c755128aa904657350abf0ce1cde21e27ca75">+36/-30</a>&nbsp;
</td>

</tr>

<tr>
  <td>
    <details>
<summary><strong>DataBrowserSidebar.tsx</strong><dd><code>Update GitHub
connection message in DataBrowserSidebar</code>&nbsp; &nbsp; &nbsp;
</dd></summary>
<hr>


dashboard/src/features/orgs/projects/database/dataGrid/components/DataBrowserSidebar/DataBrowserSidebar.tsx

<li>Updated GitHub connection message in DataBrowserSidebar<br> <li>
Simplified text and added InfoIcon<br> <li> Improved layout and styling
of the message


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3348/files#diff-3a9ff7af4a31fbf7e501a77399b2b35306d9e635b021c93f1bc13fc4e225219c">+5/-7</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr><tr><td><strong>Configuration
changes</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>common.ts</strong><dd><code>Add 'graphite' to
READ_ONLY_SCHEMAS constant</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

dashboard/src/utils/constants/common/common.ts

- Added 'graphite' to READ_ONLY_SCHEMAS list


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3348/files#diff-91900e60832d94013a6834c72bb53bb89419ad2412d9f46aa16b60ff4090d0a6">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr><tr><td><strong>Documentation</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>rich-pillows-teach.md</strong><dd><code>Add changeset
for dashboard version bump</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; </dd></summary>
<hr>

.changeset/rich-pillows-teach.md

<li>Created changeset file for minor version bump<br> <li> Added
description of changes for @nhost/dashboard


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3348/files#diff-80c4d9304d4ac2cca6acd40a4909dd85be2d333b64c3a8a25d84bfa6201d9122">+5/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr></tr></tbody></table>

___

> <details> <summary> Need help?</summary><li>Type <code>/help how to
...</code> in the comments thread for any questions about PR-Agent
usage.</li><li>Check out the <a
href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a>
for more information.</li></details>

---------

Co-authored-by: robertkasza <167509084+robertkasza@users.noreply.github.com>
2025-06-05 21:01:23 +00:00
github-actions[bot]
70433187cc chore: update versions (#3355)
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## @nhost/hasura-auth-js@2.12.0

### Minor Changes

-   39b10a2: feat (dashboard): Add multi-factor authentication

## @nhost/react@3.11.0

### Minor Changes

-   39b10a2: feat (dashboard): Add multi-factor authentication
-   4b84780: feat (dashboard): Add Webauthn to dashboard

### Patch Changes

-   @nhost/nhost-js@3.2.9

## @nhost/apollo@8.0.9

### Patch Changes

-   @nhost/nhost-js@3.2.9

## @nhost/react-apollo@18.0.0

### Patch Changes

-   Updated dependencies [39b10a2]
-   Updated dependencies [4b84780]
    -   @nhost/react@3.11.0
    -   @nhost/apollo@8.0.9

## @nhost/react-urql@15.0.0

### Patch Changes

-   Updated dependencies [39b10a2]
-   Updated dependencies [4b84780]
    -   @nhost/react@3.11.0

## @nhost/nextjs@2.2.8

### Patch Changes

-   Updated dependencies [39b10a2]
-   Updated dependencies [4b84780]
    -   @nhost/react@3.11.0

## @nhost/nhost-js@3.2.9

### Patch Changes

-   Updated dependencies [39b10a2]
    -   @nhost/hasura-auth-js@2.12.0

## @nhost/vue@2.9.6

### Patch Changes

-   @nhost/nhost-js@3.2.9

## @nhost/dashboard@2.31.0

### Minor Changes

-   39b10a2: feat (dashboard): Add multi-factor authentication
-   4b84780: feat (dashboard): Add Webauthn to dashboard

### Patch Changes

-   61eb6cd: fix (dashboard): Fix update project e2e test
    -   @nhost/react-apollo@18.0.0
    -   @nhost/nextjs@2.2.8

## @nhost-examples/cli@0.3.22

### Patch Changes

-   @nhost/nhost-js@3.2.9

## @nhost-examples/codegen-react-apollo@0.8.1

### Patch Changes

-   Updated dependencies [39b10a2]
-   Updated dependencies [4b84780]
    -   @nhost/react@3.11.0
    -   @nhost/react-apollo@18.0.0

## @nhost-examples/codegen-react-query@0.8.1

### Patch Changes

-   Updated dependencies [39b10a2]
-   Updated dependencies [4b84780]
    -   @nhost/react@3.11.0

## @nhost-examples/codegen-react-urql@0.7.1

### Patch Changes

-   Updated dependencies [39b10a2]
-   Updated dependencies [4b84780]
    -   @nhost/react@3.11.0
    -   @nhost/react-urql@15.0.0

## @nhost-examples/multi-tenant-one-to-many@2.2.23

### Patch Changes

-   @nhost/nhost-js@3.2.9

## @nhost-examples/nextjs@0.4.8

### Patch Changes

-   Updated dependencies [39b10a2]
-   Updated dependencies [4b84780]
    -   @nhost/react@3.11.0
    -   @nhost/react-apollo@18.0.0
    -   @nhost/nextjs@2.2.8

## @nhost-examples/node-storage@0.2.22

### Patch Changes

-   @nhost/nhost-js@3.2.9

## @nhost-examples/nextjs-server-components@0.6.1

### Patch Changes

-   @nhost/nhost-js@3.2.9

## @nhost-examples/sveltekit@0.8.1

### Patch Changes

-   @nhost/nhost-js@3.2.9

## @nhost-examples/react-apollo@1.6.1

### Patch Changes

-   Updated dependencies [39b10a2]
-   Updated dependencies [4b84780]
    -   @nhost/react@3.11.0
    -   @nhost/react-apollo@18.0.0

## @nhost-examples/react-gqty@1.6.1

### Patch Changes

-   Updated dependencies [39b10a2]
-   Updated dependencies [4b84780]
    -   @nhost/react@3.11.0

## @nhost-examples/react-native@0.1.9

### Patch Changes

-   Updated dependencies [39b10a2]
-   Updated dependencies [4b84780]
    -   @nhost/react@3.11.0
    -   @nhost/react-apollo@18.0.0

## @nhost-examples/vue-apollo@0.12.1

### Patch Changes

-   @nhost/nhost-js@3.2.9
-   @nhost/apollo@8.0.9
-   @nhost/vue@2.9.6

## @nhost-examples/vue-quickstart@0.6.1

### Patch Changes

-   @nhost/apollo@8.0.9
-   @nhost/vue@2.9.6

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-06-05 12:39:48 +02:00
robertkasza
39b10a2e9f feat (dashboard): Add mfa (#3342)
### **PR Type**
Enhancement


___

### **Description**
- Add multi-factor authentication (MFA) to dashboard

- Implement MFA OTP form and QR code generation

- Create MFA settings and activation components

- Update sign-in process to support MFA


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Enhancement</strong></td><td><details><summary>19
files</summary><table>
<tr>
<td><strong>MfaOtpForm.tsx</strong><dd><code>Create reusable MFA OTP
form component</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3342/files#diff-88ee3610a0658d5eead85db025a5e91e74a4d2f2a836adf7eb44ff80888a613b">+61/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>index.ts</strong><dd><code>Export MfaOtpForm
component</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3342/files#diff-9c1deb50c3a92ca5494be705635984a97e1b41b07cd0847168a4eeddf0e375d0">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>AccountMfaSettings.tsx</strong><dd><code>Implement MFA
settings component</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3342/files#diff-4eb33e0f23780eaf93fd7d86850b263d83b05dc2d7a3f6ed9e30d1ca811f17af">+32/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>GenerateMfaQRCodeButton.tsx</strong><dd><code>Create button
to generate MFA QR code</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3342/files#diff-7310648a5e879bb76ba6c3136fe555ed3bbdacddc33eef4ce8fc9c21a547ec82">+50/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>MfaQRCode.tsx</strong><dd><code>Implement MFA QR code
generation component</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3342/files#diff-0c60d61f12b47e421c67c389c66399da76af4b32241610fe94c6635353e57da2">+49/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>useActivateMfa.ts</strong><dd><code>Create hook for MFA
activation</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3342/files#diff-0ae70fc9df5a3a6828f7a266db8036107ce9ea705cd318d3a1c4b7304d8522ba">+46/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>useMfaEnabled.ts</strong><dd><code>Create hook to check MFA
status</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3342/files#diff-31d2af339a8dd32beff8cce79962fa0dd23b6c89687b21aa75663ebeccb0b154">+17/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>index.ts</strong><dd><code>Export AccountMfaSettings
component</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3342/files#diff-8c530fc016dd3569f2b7ec7e9085b99c99922ed077357bec562b8c9acaead24a">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>SecurityKeyList.tsx</strong><dd><code>Update import path for
useGetSecurityKeys</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3342/files#diff-966a157d381be33bc876e76b28f804e80cae6edb1aa088e78f883063966be3ba">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>useOnAddNewSecurityKeyHandler.ts</strong><dd><code>Update
import paths for hooks</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3342/files#diff-3514a6d1514269a83f37fc25e9cb24add9d5d74f9cf3341293c0e0f2a4c2e286">+2/-2</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>useRemoveSecurityKey.ts</strong><dd><code>Update import
paths for hooks</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3342/files#diff-5683e00a14f39018d8fe58a3116c2a8ea6d2f2a83abb2177bbf0ee8ddf0f97b5">+2/-2</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>useElevatedPermissions.ts</strong><dd><code>Create hook for
elevated permissions</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3342/files#diff-c1e4f573300c771149cc2e59918c9acf2ae5f8a6680800a899707c70800ba144">[link]</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>useGetSecurityKeys.ts</strong><dd><code>Create hook to fetch
security keys</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3342/files#diff-1f9fed870cab61f15e304342e4913edab0f5537eeb6230070de4b4f7173fa138">[link]</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>MfaSignInOtpForm.tsx</strong><dd><code>Create MFA sign-in
OTP form component</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3342/files#diff-91eba232beb0543b1e972ed9a21a0be797ed94b720487834bb3316a5dbd732f5">+26/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>SignInWithEmailAndPassword.tsx</strong><dd><code>Update
sign-in component to support MFA</code>&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3342/files#diff-a2b70644663baf4f6f2cdffd846d4d743a5ca1f2a64c4b278b6f04c6c5c92161">+16/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>

<td><strong>SignInWithEmailAndPasswordForm.tsx</strong><dd><code>Implement
sign-in form with MFA support</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3342/files#diff-a07fd6bd20c97d0c9c875e690cd3a80068fc58f74d3579feb210e189d32f5031">+91/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>

<td><strong>useOnSignInWithEmailAndPasswordHandler.ts</strong><dd><code>Create
hook for sign-in with MFA</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3342/files#diff-1a253bfc02c3267ab1c6b58c07aa06142b7e711d613b672c8420ff2861b12d27">+56/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>

<td><strong>useSignInWithEmailAndPasswordForm.ts</strong><dd><code>Create
hook for sign-in form validation</code>&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3342/files#diff-b908e474c0fb54db9c922d9fef7cf1ef6c4ccb0dd7519da0c45a18e5bb26ed40">+30/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>index.ts</strong><dd><code>Export SignInWithEmailAndPassword
component</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3342/files#diff-d3fd195b5ca8ece9eac446129e8501793e5bd6e5c167ed36c8c6d0adc1723fda">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></details></td></tr><tr><td><strong>Additional
files</strong></td><td><details><summary>8 files</summary><table>
<tr>
  <td><strong>mighty-onions-crash.md</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3342/files#diff-834d585225de297c20c9e325a231c6d3a72227fc1d8cc84b0c1f8fe0dbb1c523">+7/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>getActiveMfaType.gql</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3342/files#diff-ac5aa6c409363b550d15aace147448c5e267a3cf0fb7f86faf5060f8cbe35302">+5/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>index.tsx</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3342/files#diff-61a48d15d3a2e29160a6d91cd01501ac94cf9f70995c6a84fbb6d6e2c2d4fca1">+4/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>email.tsx</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3342/files#diff-b5d7db4460066bc114cb766771612d6f908bd6e440f40de98e4ac311a26b50cd">+16/-152</a></td>

</tr>

<tr>
  <td><strong>graphql.ts</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3342/files#diff-fbd5db84b560b1c91675004448c6c7fa0dcbfb28b9eb05d53b03e6cb7b83ebac">+80/-35</a>&nbsp;
</td>

</tr>

<tr>
  <td><strong>enable-mfa.ts</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3342/files#diff-8ed0174991b707a5c54f54ec881656403b4409cd0e3d7004045a80dbeb7b4444">+1/-4</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>index.ts</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3342/files#diff-1cee8646d2cfba37d6ce6a6e9a8d16f8caba0b99fc3a1ad0cb997ed8c7384d2e">+7/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>useSignInEmailPassword.ts</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3342/files#diff-107884d4022cd6c01459f001fa97d2b2ce11566a2c88c8deaec4727c1af44aba">+6/-8</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></details></td></tr></tr></tbody></table>

___

> <details> <summary> Need help?</summary><li>Type <code>/help how to
...</code> in the comments thread for any questions about PR-Agent
usage.</li><li>Check out the <a
href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a>
for more information.</li></details>
2025-06-05 11:38:49 +02:00
robertkasza
4b8478004e feat (dashboard): Add Webauthn to dashboard (#3320) 2025-06-05 10:28:42 +02:00
robertkasza
61eb6cdc2d fix (dashboard): fix e2e test (#3354)
### **PR Type**
Bug fix, Tests


___

### **Description**
- Refactored e2e test for project upgrade

- Removed unnecessary 'await' keywords

- Added step to save payment information

- Commented out local dashboard image build and tests


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Enhancement</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>upgrade-project.test.ts</strong><dd><code>Refactor and
enhance upgrade project e2e test</code>&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

dashboard/e2e/upgrade-project/upgrade-project.test.ts

<li>Removed redundant 'await' keywords<br> <li> Added step to save
payment information<br> <li> Simplified assertions and variable
assignments<br> <li> Minor code cleanup and formatting


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3354/files#diff-8bafbe707eb1dff0c5ae24d6b0a514ff6e80889237de6c89ec330a93be138a12">+18/-16</a>&nbsp;
</td>

</tr>
</table></td></tr><tr><td><strong>Configuration
changes</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>ci.yaml</strong><dd><code>Modify CI workflow to focus
on specific tests</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

.github/workflows/ci.yaml

<li>Commented out local dashboard image build step<br> <li> Removed
Nhost CLI installation and local tests<br> <li> Disabled general e2e
tests run


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3354/files#diff-944291df2c9c06359d37cc8833d182d705c9e8c3108e7cfe132d61a06e9133dd">+21/-21</a>&nbsp;
</td>

</tr>
</table></td></tr></tr></tbody></table>

___

> <details> <summary> Need help?</summary><li>Type <code>/help how to
...</code> in the comments thread for any questions about PR-Agent
usage.</li><li>Check out the <a
href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a>
for more information.</li></details>
2025-06-05 09:17:46 +02:00
github-actions[bot]
14187d381f chore: update versions (#3332)
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## @nhost/dashboard@2.30.0

### Minor Changes

-   f6947a2: fix: fetch job-backup services logs using Live filter
- 44a3e6b: fix: collapsed main navigation sidebar overlaps mobile navbar
-   99b78f1: feat: dashboard: add download button for soc2 report
-   9acae7d: fix: e2e tests, stop on error when refreshing metadata

### Patch Changes

- 31e636a: fix (dashboard): Use the correct payload to reset metadata
before the e2e tests

## @nhost/docs@2.32.0

### Minor Changes

-   df51c3e: fix: added installation instructions for the CLI

### Patch Changes

-   4d835c4: fix: remove nodejs18
-   2aa81a6: fix (docs): fix audit

## @nhost-examples/nextjs-server-components@0.6.0

### Minor Changes

-   6ee2d1f: fix: proper use of onError in middleware

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-06-03 18:33:21 +00:00
Nuno Pato
99b78f147e feat: dashboard: add download button for soc2 report (#3349)
### **PR Type**
Enhancement


___

### **Description**
- Add SOC2 report download button for eligible organizations

- Implement Soc2Download component with error handling

- Update settings page to include Soc2Download component

- Add environment variable for SOC2 report file ID


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Enhancement</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>Soc2Download.tsx</strong><dd><code>Implement
Soc2Download Component</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></summary>
<hr>


dashboard/src/features/orgs/components/general/components/Soc2Download/Soc2Download.tsx

<li>Create Soc2Download component with download functionality<br> <li>
Implement eligibility check for Team and Enterprise plans<br> <li> Add
error handling and user feedback for download process<br> <li> Design UI
for SOC2 report download section


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3349/files#diff-3768eb3fc718d4780028c34b5c76388e8d93cbbac94868f82c1a262fb9cc1100">+88/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td>
    <details>
<summary><strong>index.ts</strong><dd><code>Export Soc2Download
Component</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></summary>
<hr>


dashboard/src/features/orgs/components/general/components/Soc2Download/index.ts

- Export Soc2Download component as default


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3349/files#diff-6c2a578aa4d31c64a57076a451d5bc1b5144f3ea25f5d37f1b52a0080a92f42a">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td>
    <details>
<summary><strong>settings.tsx</strong><dd><code>Add Soc2Download to
Settings Page</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

dashboard/src/pages/orgs/[orgSlug]/settings.tsx

<li>Import Soc2Download component<br> <li> Add Soc2Download component to
OrgSettings page


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3349/files#diff-cb1b8a7cd77683034723d5f9a8ac429959db814ea2916ceaf0a3a5a5dae1515b">+2/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr><tr><td><strong>Documentation</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>spicy-walls-joke.md</strong><dd><code>Add Changeset for
SOC2 Download Feature</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; </dd></summary>
<hr>

.changeset/spicy-walls-joke.md

<li>Add changeset for minor version bump<br> <li> Describe feature
addition for SOC2 report download


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3349/files#diff-071df030006660706aa82d8a7cb94423abdc0b687bccbf6668dfbcd0dde43725">+5/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr><tr><td><strong>Configuration
changes</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>.env.example</strong><dd><code>Add SOC2 Report File ID
Environment Variable</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

dashboard/.env.example

- Add NEXT_PUBLIC_SOC2_REPORT_FILE_ID environment variable


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3349/files#diff-b47cf46119af2f0298d96e5657e53e57161833e8b02d87526ac5c1ed9393d477">+3/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr></tr></tbody></table>

___

> <details> <summary> Need help?</summary><li>Type <code>/help how to
...</code> in the comments thread for any questions about PR-Agent
usage.</li><li>Check out the <a
href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a>
for more information.</li></details>
2025-06-03 15:04:05 +00:00
robertkasza
2aa81a6cb9 fix (docs): fix audit (#3351)
### **PR Type**
Bug fix, Documentation


___

### **Description**
- Update audit-ci.jsonc to allow CVE-2025-48068 for 'next'

- Add new security patches in package.json

- Create changeset for @nhost/docs patch

- Improve documentation and audit compliance


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Documentation</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>yellow-drinks-sort.md</strong><dd><code>Add changeset
for @nhost/docs patch</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

.changeset/yellow-drinks-sort.md

<li>Add new changeset file for @nhost/docs<br> <li> Specify patch
version update<br> <li> Include fix message for documentation audit


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3351/files#diff-217398d60b2d9d1a3e219e315bd0d9f9fee076071dcdc83e5d649491689a11f0">+5/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr><tr><td><strong>Configuration
changes</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>audit-ci.jsonc</strong><dd><code>Update audit-ci.jsonc
allowlist for security exception</code>&nbsp; &nbsp; &nbsp;
</dd></summary>
<hr>

audit-ci.jsonc

- Add allowlist entry for CVE-2025-48068 in 'next' path


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3351/files#diff-4ede69da2a1704e53e08b8d647a315c202f037cc9277f16c94176d9622d261c6">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr><tr><td><strong>Dependencies</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>package.json</strong><dd><code>Update package.json with
new security patches</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

package.json

<li>Add new security patches for undici and tar-fs<br> <li> Update
existing security patch versions<br> <li> Adjust formatting for
consistency


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3351/files#diff-7ae45ad102eab3b6d7e7896acd08c427a9b25b346470d7bc6507b6481575d519">+4/-2</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr></tr></tbody></table>

___

> <details> <summary> Need help?</summary><li>Type <code>/help how to
...</code> in the comments thread for any questions about PR-Agent
usage.</li><li>Check out the <a
href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a>
for more information.</li></details>
2025-06-03 16:01:52 +02:00
David BM
a1edaf18ea fix: update requirements in DEVELOPERS.md (#3345)
### **User description**
Update requirements for development to Node.js 20


___

### **PR Type**
Documentation


___

### **Description**
- Update Node.js requirement to v20 or later

- Remove mention of Node.js v16 support

- Simplify Node.js version requirement section


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Documentation</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>DEVELOPERS.md</strong><dd><code>Update Node.js version
requirement</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

DEVELOPERS.md

<li>Updated Node.js requirement to v20 or later<br> <li> Removed mention
of Node.js v16 support<br> <li> Simplified Node.js version requirement
section


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3345/files#diff-bd017515eb79a7fb7569b1d15e8963ea380123d4fdf779978dd4b3ab7500fd10">+1/-3</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr></tr></tbody></table>

___

> <details> <summary> Need help?</summary><li>Type <code>/help how to
...</code> in the comments thread for any questions about PR-Agent
usage.</li><li>Check out the <a
href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a>
for more information.</li></details>

Co-authored-by: David Barroso Murcia <davidbm@air-m4.local>
2025-05-22 12:14:15 +02:00
David Barroso
4d835c4b9c fix (docs): remove nodejs18 (#3343)
### **PR Type**
Documentation


___

### **Description**
- Remove Node.js 18 runtime from supported versions

- Update documentation to reflect current runtimes

- Remove Node.js 18 configuration example

- Add changeset for patch update to @nhost/docs


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Documentation</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>brave-garlics-act.md</strong><dd><code>Add changeset
for removing Node.js 18</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

.changeset/brave-garlics-act.md

<li>Add new changeset file for @nhost/docs patch update<br> <li>
Describe fix to remove Node.js 18


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3343/files#diff-af4dedaee65af3f90491e3eea21a6996fec17988247ad9779ed1b623f386eba6">+5/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td>
    <details>
<summary><strong>runtimes.mdx</strong><dd><code>Update runtimes
documentation to remove Node.js 18</code>&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

docs/products/functions/runtimes.mdx

<li>Remove Node.js 18 from list of supported runtimes<br> <li> Delete
Node.js 18 configuration example<br> <li> Update supported versions to
Node.js 20 and 22


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3343/files#diff-41cc586838cadca39a91bf32878fb7cc5473d5815dec595547a4089684b5d489">+0/-9</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr></tr></tbody></table>

___

> <details> <summary> Need help?</summary><li>Type <code>/help how to
...</code> in the comments thread for any questions about PR-Agent
usage.</li><li>Check out the <a
href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a>
for more information.</li></details>
2025-05-20 22:19:18 +02:00
David BM
44a3e6bd41 fix (dashboard): collapsed main navigation sidebar overlaps mobile navbar (#3341) 2025-05-20 22:05:58 +02:00
Dimitri POSTOLOV
6ee2d1f5bf fix (examples/nextjs-server-components): proper use of onError in middleware (#3330)
onError should be returned since it can contain `NextResponse.redirect`
like in the example
e87505c564/examples/quickstarts/nextjs-server-components/src/middleware.ts (L5-L7)

---------

Co-authored-by: David Barroso <dbarrosop@dravetech.com>
2025-05-19 11:21:06 +02:00
Calvin
df51c3e64e fix (docs): added installation instructions for the CLI (#3329)
Co-authored-by: David Barroso <dbarrosop@dravetech.com>
2025-05-16 09:01:45 +02:00
David BM
9acae7d1c4 fix (dashboard ci): stop playwright e2e tests run after first failure (#3334)
### **PR Type**
Bug fix, Enhancement


___

### **Description**
- Reload metadata for Graphite's remote schemas in e2e tests

- Add 'reload_remote_schemas' parameter to metadata reload

- Include Changeset for version tracking


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Enhancement</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>refresh-metadata.setup.ts</strong><dd><code>Add
Graphite remote schema to metadata reload</code>&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></summary>
<hr>

dashboard/e2e/setup/refresh-metadata.setup.ts

<li>Add 'reload_remote_schemas' parameter with value ['graphite']<br>
<li> Include Graphite remote schema in metadata reload


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3334/files#diff-e32371f0138a80b833384d09a2b1fcc0de848b555823c16c0e9a96ed441edc8a">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr><tr><td><strong>Documentation</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>wild-stingrays-help.md</strong><dd><code>Add changeset
for dashboard version update</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></summary>
<hr>

.changeset/wild-stingrays-help.md

<li>Create new changeset file<br> <li> Specify minor version bump for
'@nhost/dashboard'<br> <li> Add description of the fix


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3334/files#diff-b5a0c57a4f2bd08515f9feef3c38804e7f948ffb4b76418f6549131f61cf20fd">+5/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr></tr></tbody></table>

___

> <details> <summary> Need help?</summary><li>Type <code>/help how to
...</code> in the comments thread for any questions about PR-Agent
usage.</li><li>Check out the <a
href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a>
for more information.</li></details>
2025-05-15 18:03:29 +02:00
David BM
f6947a2194 fix (dashboard): logs page, fetch job-backup services logs using Live filter (#3333)
### **User description**
Adds `job-backup.+` regex pattern to the service filter in the logs
subscription, to match any job-backup services


___

### **PR Type**
Bug fix, Enhancement


___

### **Description**
- Fix logs fetching for job-backup services

- Implement regex pattern for job-backup service filter

- Improve live log subscription functionality

- Add changeset for version tracking


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Enhancement</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>logs.tsx</strong><dd><code>Improve log fetching for
job-backup services</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

dashboard/src/pages/orgs/[orgSlug]/projects/[appSubdomain]/logs.tsx

<li>Modify service filter in logs subscription<br> <li> Implement regex
pattern 'job-backup.+' for job-backup services<br> <li> Enhance
conditional logic for service filtering


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3333/files#diff-77489a68a7526d74f06d59019ad68c44728b7620637308d70fba38d6649b73fa">+4/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr><tr><td><strong>Documentation</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>hungry-waves-destroy.md</strong><dd><code>Add changeset
for version tracking</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

.changeset/hungry-waves-destroy.md

<li>Add new changeset file<br> <li> Specify minor version bump for
@nhost/dashboard<br> <li> Document fix for job-backup services logs
fetching


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3333/files#diff-45dd073b01febac00c055bb63f51182ff043ee7a5df2c12058232caf2d2a31ca">+5/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr></tr></tbody></table>

___

> <details> <summary> Need help?</summary><li>Type <code>/help how to
...</code> in the comments thread for any questions about PR-Agent
usage.</li><li>Check out the <a
href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a>
for more information.</li></details>
2025-05-14 13:55:11 +02:00
robertkasza
31e636a9c8 fix (dashboard): Use the correct payload to reset metadata before e2 tests (#3331)
### **PR Type**
Bug fix, Tests


___

### **Description**
- Fix metadata reset payload in e2e tests

- Improve error handling and logging for metadata consistency

- Add changeset for patch version bump


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Bug
fix</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>refresh-metadata.setup.ts</strong><dd><code>Refactor
metadata reset and improve consistency checks</code>&nbsp; &nbsp; &nbsp;
</dd></summary>
<hr>

dashboard/e2e/setup/refresh-metadata.setup.ts

<li>Updated payload for metadata reset (removed
<code>reload_remote_schemas</code>, set <br><code>reload_sources</code>
to false)<br> <li> Enhanced error handling and logging for metadata
consistency<br> <li> Added detailed console logging for inconsistent
metadata


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3331/files#diff-e32371f0138a80b833384d09a2b1fcc0de848b555823c16c0e9a96ed441edc8a">+10/-3</a>&nbsp;
&nbsp; </td>

</tr>
</table></td></tr><tr><td><strong>Documentation</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>lemon-flies-live.md</strong><dd><code>Add changeset for
dashboard patch update</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; </dd></summary>
<hr>

.changeset/lemon-flies-live.md

<li>Added changeset file for patch version bump<br> <li> Described fix
for metadata reset in e2e tests


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3331/files#diff-25789650a83994d924e29e67aafc8011da8f98e2178211207de1ef049d53e4f6">+5/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr></tr></tbody></table>

___

> <details> <summary> Need help?</summary><li>Type <code>/help how to
...</code> in the comments thread for any questions about PR-Agent
usage.</li><li>Check out the <a
href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a>
for more information.</li></details>
2025-05-13 13:18:29 +02:00
github-actions[bot]
0fdff345ac chore: update versions (#3327)
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## @nhost/dashboard@2.29.0

### Minor Changes

-   c97b43f: fix: update vite to address vulnerability audit
-   a0931e2: fix: improve logs time range and filter selection
- c0635ae: feat (dashboard): Add information about that free
organization cannot be upgraded.
- e87505c: fix: can downsize postgres storage capacity using local
dashboard

## @nhost-examples/codegen-react-apollo@0.8.0

### Minor Changes

-   c97b43f: fix: update vite to address vulnerability audit

## @nhost-examples/codegen-react-query@0.8.0

### Minor Changes

-   c97b43f: fix: update vite to address vulnerability audit

## @nhost-examples/codegen-react-urql@0.7.0

### Minor Changes

-   c97b43f: fix: update vite to address vulnerability audit

## @nhost-examples/sveltekit@0.8.0

### Minor Changes

-   c97b43f: fix: update vite to address vulnerability audit

## @nhost-examples/react-apollo@1.6.0

### Minor Changes

-   c97b43f: fix: update vite to address vulnerability audit

### Patch Changes

-   97db637: fix: fix settings

## @nhost-examples/react-gqty@1.6.0

### Minor Changes

-   c97b43f: fix: update vite to address vulnerability audit

## @nhost-examples/vue-apollo@0.12.0

### Minor Changes

-   c97b43f: fix: update vite to address vulnerability audit

## @nhost-examples/vue-quickstart@0.6.0

### Minor Changes

-   c97b43f: fix: update vite to address vulnerability audit

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-05-12 20:05:18 +00:00
David Barroso
97db63791b fix (examples/react-apollo): fix settings (#3301)
### **PR Type**
Enhancement, Configuration changes


___

### **Description**
- Update auth settings in nhost.toml

- Remove rate limiting configurations

- Remove SMTP provider settings

- Add changeset for patch version bump


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Documentation</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>lazy-years-kneel.md</strong><dd><code>Add changeset for
patch version bump</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

.changeset/lazy-years-kneel.md

<li>Add new changeset file for patch version bump<br> <li> Specify
'@nhost-examples/react-apollo' package<br> <li> Include fix description


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3301/files#diff-019ff1fe3f65a591a01a6cbc0fa05c44c065e2611417ac0b3dcf6bd5a6eda7c2">+5/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr><tr><td><strong>Configuration
changes</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>nhost.toml</strong><dd><code>Update Nhost configuration
settings</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

examples/react-apollo/nhost/nhost.toml

<li>Change auth.elevatedPrivileges.mode to 'recommended'<br> <li> Remove
auth.rateLimit configurations<br> <li> Remove provider.smtp settings<br>
<li> Update allowed URLs for auth redirections


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3301/files#diff-268d6c8dddd6990d60d62c1c923955c4e0e7549a80f0f5856192f889378416a0">+1/-30</a>&nbsp;
&nbsp; </td>

</tr>
</table></td></tr></tr></tbody></table>

___

> <details> <summary> Need help?</summary><li>Type <code>/help how to
...</code> in the comments thread for any questions about PR-Agent
usage.</li><li>Check out the <a
href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a>
for more information.</li></details>
2025-05-12 19:30:59 +02:00
David BM
a0931e282f fix (dashboard): logs persist time range selector, fix validation (#3300)
### **User description**
Resolves #3154


___

### **PR Type**
Bug fix, Enhancement


___

### **Description**
- Fix logs search functionality with selected service filter

- Implement persistent time range selector

- Add interval-based date range selection

- Improve form validation and submission logic


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Enhancement</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>LogsDatePicker.tsx</strong><dd><code>Enhance
LogsDatePicker with form context integration</code>&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; </dd></summary>
<hr>


dashboard/src/features/orgs/projects/logs/components/LogsDatePicker/LogsDatePicker.tsx

<li>Import LogsFilterFormValues type and useFormContext hook<br> <li>
Add setValue function from form context<br> <li> Implement
handleDateChange to update selected date and reset interval<br> <li>
Update DatePicker onChange to use new handleDateChange function


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3300/files#diff-0768cb2a5cee4ab57a64580c34213950a042a9893b5da51b8886e166cb7a9060">+9/-3</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td>
    <details>
<summary><strong>LogsHeader.tsx</strong><dd><code>Improve LogsHeader
with interval support and form handling</code></dd></summary>
<hr>


dashboard/src/features/orgs/projects/logs/components/LogsHeader/LogsHeader.tsx

<li>Add interval to validation schema and form default values<br> <li>
Implement interval-based date recalculation in handleSubmit<br> <li>
Update form mode to 'onChange' for real-time validation<br> <li> Modify
useEffect to trigger submission on service change


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3300/files#diff-ebb3285aa776c9c5ea8b72672c4aafd55994c6c694998bbf56ca9c56d1e77664">+27/-3</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td>
    <details>
<summary><strong>LogsRangeSelector.tsx</strong><dd><code>Enhance
LogsRangeSelector with interval selection and UI
updates</code></dd></summary>
<hr>


dashboard/src/features/orgs/projects/logs/components/LogsRangeSelector/LogsRangeSelector.tsx

<li>Add interval handling in LogsToDatePickerLiveButton<br> <li>
Implement interval setting in handleIntervalChange function<br> <li>
Update Button variant based on selected interval<br> <li> Add interval
to useWatch hook for reactive updates


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3300/files#diff-46dd7c795a79e4b443213ed10089651423d13e5c776ca72e3a95ae5e0f7f63c8">+9/-2</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr></tr></tbody></table>

___

> <details> <summary> Need help?</summary><li>Type <code>/help how to
...</code> in the comments thread for any questions about PR-Agent
usage.</li><li>Check out the <a
href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a>
for more information.</li></details>
2025-05-12 18:31:58 +02:00
David BM
e87505c564 fix (dashboard): downsize postgres using local dashboard (#3292)
### **User description**
Fixes #3265


___

### **PR Type**
Bug fix, Enhancement


___

### **Description**
- Fix PostgreSQL version parsing for empty strings

- Enable downsizing Postgres in local dashboard

- Improve handling of free project and platform checks

- Update refetch queries with optional chaining


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Bug
fix</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>useGetPostgresVersion.ts</strong><dd><code>Fix
PostgreSQL version parsing for empty strings</code>&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>


dashboard/src/features/orgs/projects/database/common/hooks/useGetPostgresVersion/useGetPostgresVersion.ts

<li>Add fallback to empty string for <code>version</code> in
<br><code>splitPostgresMajorMinorVersions</code>


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3292/files#diff-1836b93836a54ca5a8fd4d9fe64ec944a0ef37dfcb81909225115ba2a6d98928">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td>
    <details>
<summary><strong>index.tsx</strong><dd><code>Update refetch queries with
optional chaining</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>


dashboard/src/pages/orgs/[orgSlug]/projects/[appSubdomain]/settings/index.tsx

- Add optional chaining for `userData.id` in refetch queries


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3292/files#diff-b4185be97a505e25badcdefe31ea86fa9d69f72264c4bb35eae17fba936a3d47">+8/-2</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr><tr><td><strong>Enhancement</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>DatabaseStorageCapacity.tsx</strong><dd><code>Improve
database storage capacity handling and UI</code>&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>


dashboard/src/features/orgs/projects/database/settings/components/DatabaseStorageCapacity/DatabaseStorageCapacity.tsx

<li>Add <code>isEmptyValue</code> check for <code>org</code> object<br>
<li> Introduce <code>shouldShowUpdateCapacityWarning</code> variable<br>
<li> Modify <code>submitDisabled</code> logic for non-platform
environments<br> <li> Update conditional rendering of
<code>DatabaseStorageCapacityWarning</code>


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3292/files#diff-097a59d13b44816051386182a444eadfe2dcacd69b88c121af6733d7eca3ee43">+16/-3</a>&nbsp;
&nbsp; </td>

</tr>
</table></td></tr></tr></tbody></table>

___

> <details> <summary> Need help?</summary><li>Type <code>/help how to
...</code> in the comments thread for any questions about PR-Agent
usage.</li><li>Check out the <a
href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a>
for more information.</li></details>
2025-05-08 14:02:09 +02:00
robertkasza
c0635ae1c7 feat (dashboard): Add information about that free organization cannot be upgraded (#3316)
### **PR Type**
Enhancement


___

### **Description**
- Add info about free org upgrade limitations

- Introduce new 'NewOrgButton' component

- Update UI for subscription plan section

- Improve text link component functionality


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Enhancement</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>InfoAlert.tsx</strong><dd><code>Enhance AlertTitle
styling</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

dashboard/src/features/orgs/components/InfoAlert/InfoAlert.tsx

- Added 'font-semibold' class to AlertTitle


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3316/files#diff-4b78a2f0e1c6c2f6b37e430bc6cad016e884bb34735bd6aaebac906743748d7b">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td>
    <details>
<summary><strong>SubscriptionPlan.tsx</strong><dd><code>Update
subscription plan UI and add free org info</code>&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>


dashboard/src/features/orgs/components/billing/components/SubscriptionPlan/SubscriptionPlan.tsx

<li>Added InfoAlert for free organizations<br> <li> Introduced
NewOrgButton component<br> <li> Updated layout and styling of
subscription plan section<br> <li> Replaced Link component with TextLink


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3316/files#diff-2a5f070869055286b669e382b18d656935752803b9a1ef13390ac028c2a48ac4">+32/-30</a>&nbsp;
</td>

</tr>

<tr>
  <td>
    <details>
<summary><strong>TextLink.tsx</strong><dd><code>Enhance TextLink
component with optional icon</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>


dashboard/src/features/orgs/projects/common/components/TextLink/TextLink.tsx

<li>Added optional icon to TextLink component<br> <li> Introduced
withIcon prop for flexibility


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3316/files#diff-2f49ce51c83fab712173974ec09621f291ef56a7ad056df587c1bfd525ae6983">+4/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr><tr><td><strong>Documentation</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>good-frogs-share.md</strong><dd><code>Add changeset for
dashboard feature</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

.changeset/good-frogs-share.md

- Added changeset file for version bump


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3316/files#diff-ff1c12916da9254a5d59fef39d5220a0ccdd20a7e66e1436a860da9a014d31ee">+5/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr></tr></tbody></table>

___

> <details> <summary> Need help?</summary><li>Type <code>/help how to
...</code> in the comments thread for any questions about PR-Agent
usage.</li><li>Check out the <a
href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a>
for more information.</li></details>
2025-05-07 13:31:01 +02:00
robertkasza
d2a9a9ae1d fix: update labeler config (#3328)
### **PR Type**
Enhancement


___

### **Description**
- Update labeler configuration for all categories

- Standardize format using 'any' key for all rules

- Remove redundant 'any' key for documentation category

- Maintain existing category definitions and file patterns


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Configuration
changes</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>labeler.yml</strong><dd><code>Standardize and simplify
labeler configuration</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

.github/labeler.yml

<li>Standardized all rules using 'any' key<br> <li> Removed redundant
'any' key for documentation<br> <li> Maintained existing category
definitions and file patterns<br> <li> Simplified overall configuration
structure


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3328/files#diff-a22c263686553013feaeb0677d26eeb0b8778a756c4311c1fce13384258026aa">+8/-9</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr></tr></tbody></table>

___

> <details> <summary> Need help?</summary><li>Type <code>/help how to
...</code> in the comments thread for any questions about PR-Agent
usage.</li><li>Check out the <a
href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a>
for more information.</li></details>
2025-05-07 10:00:34 +02:00
David BM
c97b43f149 fix (ci): update vite to solve vulnerability audit (#3323)
### **PR Type**
Bug fix, Enhancement


___

### **Description**
- Update Vite to address vulnerability audit

- Upgrade Vite in multiple project dependencies

- Update package overrides for security

- Add changeset for minor version bumps


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Documentation</strong></td><td><details><summary>1
files</summary><table>
<tr>
<td><strong>chatty-kids-exist.md</strong><dd><code>Add changeset for
minor version updates</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3323/files#diff-22d27113acb695bcdab878d71e0e553a23f87070faeb4672ce09bf2108c56064">+13/-0</a>&nbsp;
&nbsp; </td>

</tr>

</table></details></td></tr><tr><td><strong>Dependencies</strong></td><td><details><summary>10
files</summary><table>
<tr>
<td><strong>package.json</strong><dd><code>Update Vite to
v5.4.19</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3323/files#diff-2d8d55c799cd71f1b35e831f075f8178ed1734c4820a2ad548b4dd24d6938d7c">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>package.json</strong><dd><code>Update Vite to
v5.4.19</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3323/files#diff-83675898dc6ed88838763232d022f6e100e07d71681cc8a1f02aee99ee3f229b">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>package.json</strong><dd><code>Update Vite to
v5.4.19</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3323/files#diff-9fb3a23f389ab1d192d7e018d2acbe512bd8792278662101401caa98692735db">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>package.json</strong><dd><code>Update Vite to
v5.4.19</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3323/files#diff-cb7094614884e8cd2c8fb67dadedb1887c46c31b888840def0b7042273bfbb28">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>package.json</strong><dd><code>Update Vite to
v6.2.7</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3323/files#diff-6288951fff74ec246c9cc023b7b7e3e9aad31423891bc4ea25b5d84a5f5b061f">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>package.json</strong><dd><code>Update Vite to
v5.4.19</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3323/files#diff-d95dc3391741287366ea2e61f70e9ccc64452e0d22b1db91d6bf524f5aa4331c">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>package.json</strong><dd><code>Update Vite to
v5.4.19</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3323/files#diff-8a3e5ed0f618f15211c31f700e0da998e2eae58f60353624b7a7e637bd63b153">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>package.json</strong><dd><code>Update Vite to
v5.4.19</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3323/files#diff-fc4298d3512fdd9a3d871f9f182fe871c8beccd1580f864a271ddfb32005feef">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>package.json</strong><dd><code>Update Vite to
v5.4.19</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3323/files#diff-85166d1137e29a5275f991e1e94a0c9d5b83ac7504463ba76f9187b2b750c895">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>package.json</strong><dd><code>Update Vite and adjust
overrides</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3323/files#diff-7ae45ad102eab3b6d7e7896acd08c427a9b25b346470d7bc6507b6481575d519">+4/-4</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></details></td></tr></tr></tbody></table>

___

> <details> <summary> Need help?</summary><li>Type <code>/help how to
...</code> in the comments thread for any questions about PR-Agent
usage.</li><li>Check out the <a
href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a>
for more information.</li></details>

---------

Co-authored-by: robertkasza <robert.kasza@bishop-co.com>
2025-05-06 14:39:39 +02:00
github-actions[bot]
2026bb7a9c chore: update versions (#3298)
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## @nhost/apollo@8.0.8

### Patch Changes

-   @nhost/nhost-js@3.2.8

## @nhost/react-apollo@17.0.4

### Patch Changes

-   @nhost/apollo@8.0.8
-   @nhost/react@3.10.4

## @nhost/react-urql@14.0.4

### Patch Changes

-   @nhost/react@3.10.4

## @nhost/hasura-auth-js@2.11.1

### Patch Changes

- 5ff4dd6: fix (hasura-auth-js|hasura-storage-js): update e2e config for
packages

## @nhost/hasura-storage-js@2.7.1

### Patch Changes

- 5ff4dd6: fix (hasura-auth-js|hasura-storage-js): update e2e config for
packages

## @nhost/nextjs@2.2.7

### Patch Changes

-   d9eb906: fix: update vite and nextjs because of vulnerability
    -   @nhost/react@3.10.4

## @nhost/nhost-js@3.2.8

### Patch Changes

-   Updated dependencies [5ff4dd6]
    -   @nhost/hasura-storage-js@2.7.1
    -   @nhost/hasura-auth-js@2.11.1

## @nhost/react@3.10.4

### Patch Changes

-   @nhost/nhost-js@3.2.8

## @nhost/vue@2.9.5

### Patch Changes

-   @nhost/nhost-js@3.2.8

## @nhost/dashboard@2.28.0

### Minor Changes

-   8552678: feat: dashboard: add additional events to segment
-   0bf2808: chore: refresh metadata before end-to-end tests
-   72a365c: fix: correct graphql page roles dropdown's source
-   cef6471: fix: dashboard: add anonid to user's metadata

### Patch Changes

-   d9eb906: fix: update vite and nextjs because of vulnerability
-   233232b: feat (dashboard): improve Upgrade project dialog
-   Updated dependencies [d9eb906]
    -   @nhost/nextjs@2.2.7
    -   @nhost/react-apollo@17.0.4

## @nhost/docs@2.31.0

### Minor Changes

-   b302dbd: feat: added sveltekit quickstart

### Patch Changes

-   5e96230: fix: fixing mintlify breaking our docs

## @nhost-examples/cli@0.3.21

### Patch Changes

-   @nhost/nhost-js@3.2.8

## @nhost-examples/codegen-react-apollo@0.7.1

### Patch Changes

-   d9eb906: fix: update vite and nextjs because of vulnerability
    -   @nhost/react@3.10.4
    -   @nhost/react-apollo@17.0.4

## @nhost-examples/codegen-react-query@0.7.1

### Patch Changes

-   d9eb906: fix: update vite and nextjs because of vulnerability
    -   @nhost/react@3.10.4

## @nhost-examples/codegen-react-urql@0.6.1

### Patch Changes

-   d9eb906: fix: update vite and nextjs because of vulnerability
    -   @nhost/react@3.10.4
    -   @nhost/react-urql@14.0.4

## @nhost-examples/multi-tenant-one-to-many@2.2.22

### Patch Changes

-   @nhost/nhost-js@3.2.8

## @nhost-examples/nextjs@0.4.7

### Patch Changes

-   fad7f64: chore: fix typo
-   d9eb906: fix: update vite and nextjs because of vulnerability
-   Updated dependencies [d9eb906]
    -   @nhost/nextjs@2.2.7
    -   @nhost/react@3.10.4
    -   @nhost/react-apollo@17.0.4

## @nhost-examples/node-storage@0.2.21

### Patch Changes

-   @nhost/nhost-js@3.2.8

## @nhost-examples/nextjs-server-components@0.5.6

### Patch Changes

-   d9eb906: fix: update vite and nextjs because of vulnerability
    -   @nhost/nhost-js@3.2.8

## @nhost-examples/sveltekit@0.7.1

### Patch Changes

-   f8243f9: chore (examples/svelte): update @sveltejs/kit
-   d9eb906: fix: update vite and nextjs because of vulnerability
    -   @nhost/nhost-js@3.2.8

## @nhost-examples/react-apollo@1.5.1

### Patch Changes

-   d9eb906: fix: update vite and nextjs because of vulnerability
- efd68c3: chore (react-apollo): use preview build instead of local dev
server for e2e tests
    -   @nhost/react@3.10.4
    -   @nhost/react-apollo@17.0.4

## @nhost-examples/react-gqty@1.5.1

### Patch Changes

-   d9eb906: fix: update vite and nextjs because of vulnerability
    -   @nhost/react@3.10.4

## @nhost-examples/react-native@0.1.8

### Patch Changes

-   @nhost/react@3.10.4
-   @nhost/react-apollo@17.0.4

## @nhost-examples/vue-apollo@0.11.1

### Patch Changes

-   d9eb906: fix: update vite and nextjs because of vulnerability
    -   @nhost/nhost-js@3.2.8
    -   @nhost/apollo@8.0.8
    -   @nhost/vue@2.9.5

## @nhost-examples/vue-quickstart@0.5.1

### Patch Changes

-   d9eb906: fix: update vite and nextjs because of vulnerability
    -   @nhost/apollo@8.0.8
    -   @nhost/vue@2.9.5

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-04-28 13:56:36 +00:00
David Barroso
1bc1e30f5e chore (ci): send message to discord (#3317)
### **PR Type**
Enhancement


___

### **Description**
- Add Discord notifications for dashboard deployment status

- Implement success and failure notifications separately

- Include deployment details in Discord messages

- Use tsickert/discord-webhook action for notifications


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Enhancement</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>deploy-dashboard.yaml</strong><dd><code>Implement
Discord notifications for deployment status</code>&nbsp; &nbsp; &nbsp;
&nbsp; </dd></summary>
<hr>

.github/workflows/deploy-dashboard.yaml

<li>Added success notification step using Discord webhook<br> <li> Added
failure notification step using Discord webhook<br> <li> Both
notifications include deployment status, trigger user, and git
<br>ref<br> <li> Used different embed colors for success (green) and
failure (red)


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3317/files#diff-634642357deb8c43286f58a5b454c8f10aeab2fb9937c9cb0c4300ac84dc00cf">+28/-0</a>&nbsp;
&nbsp; </td>

</tr>
</table></td></tr></tr></tbody></table>

___

> <details> <summary> Need help?</summary><li>Type <code>/help how to
...</code> in the comments thread for any questions about PR-Agent
usage.</li><li>Check out the <a
href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a>
for more information.</li></details>
2025-04-28 14:37:33 +02:00
Nuno Pato
85526782f2 feat: dashboard: add additional segment events (#3313)
### **PR Type**
Enhancement


___

### **Description**
- Added Segment analytics tracking for key actions

- Implemented event tracking for project upgrades

- Added tracking for organization invites

- Included analytics for GitHub project connections

- Implemented tracking for new project creation


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Enhancement</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>UpgradeProjectDialogContent.tsx</strong><dd><code>Add
Segment tracking for project upgrades</code>&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; </dd></summary>
<hr>


dashboard/src/features/orgs/components/common/TransferOrUpgradeProjectDialog/UpgradeProjectDialogContent.tsx

<li>Imported useCurrentOrg and analytics<br> <li> Added Segment tracking
for 'Project Upgraded' event<br> <li> Included detailed project and
organization data in the event


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3313/files#diff-ced98d2b8b0e83e41fd9bd569a6dd3fb5c4013861d3352628e63abe0c285d2ba">+20/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td>
    <details>
<summary><strong>PendingInvites.tsx</strong><dd><code>Implement Segment
tracking for organization invites</code>&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; </dd></summary>
<hr>


dashboard/src/features/orgs/components/members/components/PendingInvites/PendingInvites.tsx

<li>Imported analytics from Segment<br> <li> Added tracking for
'Organization Invite Sent' event<br> <li> Included organization and
invitee details in the event


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3313/files#diff-dec81af68d9403085c09185e0153335ddf7d629f64f626cae394108bcb42d685">+11/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td>
    <details>
<summary><strong>EditRepositorySettingsModal.tsx</strong><dd><code>Add
Segment tracking for GitHub project connections</code>&nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>


dashboard/src/features/orgs/projects/git/common/components/EditRepositorySettingsModal/EditRepositorySettingsModal.tsx

<li>Imported analytics from Segment<br> <li> Added tracking for 'Project
Connected to GitHub' event<br> <li> Included project and repository
details in the event


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3313/files#diff-eb88f4f79aa0286c7f1d06ff73908f34009e7e9e8b982f54866f157fd81c5c3a">+12/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td>
    <details>
<summary><strong>new.tsx</strong><dd><code>Implement Segment tracking
for new project creation</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></summary>
<hr>

dashboard/src/pages/orgs/[orgSlug]/projects/new.tsx

<li>Imported analytics from Segment<br> <li> Added tracking for 'Project
Created' event<br> <li> Included project, organization, and region
details in the event


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3313/files#diff-ef97470126e3edc146dda51337aaec556387e2f8a37afa70810d1dc94958f4fd">+10/-0</a>&nbsp;
&nbsp; </td>

</tr>
</table></td></tr><tr><td><strong>Documentation</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>hungry-terms-retire.md</strong><dd><code>Add changeset
for Segment analytics feature</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></summary>
<hr>

.changeset/hungry-terms-retire.md

<li>Added changeset file for minor version bump<br> <li> Described
feature addition of Segment events


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3313/files#diff-3accee3677ac6171593ed474c4c867ce1d27b490b69c9fd738f1cff121791ba9">+5/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr></tr></tbody></table>

___

> <details> <summary> Need help?</summary><li>Type <code>/help how to
...</code> in the comments thread for any questions about PR-Agent
usage.</li><li>Check out the <a
href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a>
for more information.</li></details>
2025-04-28 11:14:51 +00:00
Russians tortured my 18yo friend Ivan bc of ukr flag in mobile phone
fad7f640de fix (examples/nextjs): typo (#3309)
Co-authored-by: David Barroso <dbarrosop@dravetech.com>
2025-04-28 09:58:08 +02:00
robertkasza
5ff4dd6e40 fix (packages): update storage/auth e2e config (#3306)
### **PR Type**
Enhancement, Tests


___

### **Description**
- Update e2e configuration for hasura-auth-js and hasura-storage-js

- Modify CI workflow for package-specific Nhost CLI shutdown

- Adjust test scripts in package.json files

- Add changeset for patch updates


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Documentation</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>fluffy-shoes-cross.md</strong><dd><code>Add changeset
for auth and storage package updates</code>&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

.changeset/fluffy-shoes-cross.md

<li>Add new changeset file for patch updates<br> <li> Specify changes
for @nhost/hasura-storage-js and @nhost/hasura-auth-js


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3306/files#diff-bfec01342bb3adca8f892b1223015496f20919e642eb0cd4f2fc42f4a659f93b">+6/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr><tr><td><strong>Enhancement</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>ci.yaml</strong><dd><code>Update CI workflow for
package-specific Nhost CLI shutdown</code></dd></summary>
<hr>

.github/workflows/ci.yaml

<li>Add new step to stop Nhost CLI for specific packages<br> <li> Ensure
Nhost CLI stops even if previous steps fail


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3306/files#diff-944291df2c9c06359d37cc8833d182d705c9e8c3108e7cfe132d61a06e9133dd">+6/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr><tr><td><strong>Tests</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>package.json</strong><dd><code>Modify test scripts for
hasura-auth-js package</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

packages/hasura-auth-js/package.json

<li>Update ci:test script to use vite.config.e2e.json<br> <li> Remove
Nhost CLI shutdown from ci:test script


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3306/files#diff-69ad7e7b51fc8532cb472f319780be5355e41c386f1cc223ce929369c3b87500">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td>
    <details>
<summary><strong>package.json</strong><dd><code>Update test script for
hasura-storage-js package</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

packages/hasura-storage-js/package.json

- Remove Nhost CLI shutdown from ci:test script


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3306/files#diff-eca15b254298d1b63d7c80b470d31e046d63ae93b1f09eb6dc3959e3a326560d">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr></tr></tbody></table>

___

> <details> <summary> Need help?</summary><li>Type <code>/help how to
...</code> in the comments thread for any questions about PR-Agent
usage.</li><li>Check out the <a
href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a>
for more information.</li></details>
2025-04-25 18:21:09 +02:00
David BM
0bf28085b7 chore (dashboard CI): refresh hasura metadata before e2e tests (#3314) 2025-04-25 17:40:29 +02:00
Alexander Mart
b302dbd27d docs: add sveltekit quickstart (#3302)
Co-authored-by: David Barroso <dbarrosop@dravetech.com>
Co-authored-by: Nuno Pato <nunopato@gmail.com>
2025-04-24 14:04:48 +02:00
David BM
72a365c5fc fix (dashboard): correct graphql role dropdown source (#3291) 2025-04-21 18:19:09 +02:00
David Barroso
d11363a74c chore (observability): make alerts less sensitive (#3310)
### **PR Type**
Enhancement


___

### **Description**
- Increase alert sensitivity time from 5m to 15m

- Change NoData state to Alerting for most rules

- Modify execErrState to Alerting or OK

- Adjust noDataState for specific alert rules


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Configuration
changes</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>rules_nhost.yaml</strong><dd><code>Adjust alert
sensitivity and error handling configurations</code></dd></summary>
<hr>

observability/grafana/rules_nhost.yaml

<li>Increased 'for' duration from 5m to 15m for multiple alerts<br> <li>
Changed 'noDataState' from NoData to Alerting for most rules<br> <li>
Modified 'execErrState' to Alerting or OK depending on the rule<br> <li>
Adjusted 'noDataState' for specific alert rules (e.g., OK to Alerting)


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3310/files#diff-27165812186176e21d13a35136e43511b837700a599d3a00c61a1f6b36c55af2">+13/-13</a>&nbsp;
</td>

</tr>
</table></td></tr></tr></tbody></table>

___

> <details> <summary> Need help?</summary><li>Type <code>/help how to
...</code> in the comments thread for any questions about PR-Agent
usage.</li><li>Check out the <a
href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a>
for more information.</li></details>
2025-04-20 13:59:31 +02:00
David BM
1bc2fabe59 chore (CI): skip CI runs on documentation change (#3307)
### **User description**
Skips CI running if we only changed under `docs/`


___

### **PR Type**
Enhancement


___

### **Description**
- Skip CI runs for changes in 'docs/' directory

- Update CI workflow configuration in GitHub Actions


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Configuration
changes</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>ci.yaml</strong><dd><code>Update CI workflow to ignore
documentation changes</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; </dd></summary>
<hr>

.github/workflows/ci.yaml

<li>Add 'docs/**' to paths-ignore for push and pull_request events<br>
<li> Prevent CI from running on documentation-only changes


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3307/files#diff-944291df2c9c06359d37cc8833d182d705c9e8c3108e7cfe132d61a06e9133dd">+2/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr></tr></tbody></table>

___

> <details> <summary> Need help?</summary><li>Type <code>/help how to
...</code> in the comments thread for any questions about PR-Agent
usage.</li><li>Check out the <a
href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a>
for more information.</li></details>
2025-04-16 15:35:41 +02:00
robertkasza
f8243f9434 chore (examples/svelte): update @sveltejs/kit (#3305)
### **PR Type**
Enhancement, Documentation


___

### **Description**
- Update @sveltejs/kit to version 2.20.6

- Add changeset for @nhost-examples/sveltekit patch

- Update package resolutions for security


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Documentation</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>neat-eggs-chew.md</strong><dd><code>Add changeset for
sveltekit example update</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></summary>
<hr>

.changeset/neat-eggs-chew.md

<li>Add new changeset file for @nhost-examples/sveltekit<br> <li>
Specify patch update for the package<br> <li> Include description of the
change


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3305/files#diff-f07ba45e000aecdb0b0c45fbc4a50ccc0749e7755fdd86d630b9025cdde187ee">+5/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr><tr><td><strong>Dependencies</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>package.json</strong><dd><code>Update @sveltejs/kit
dependency version</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; </dd></summary>
<hr>

examples/quickstarts/sveltekit/package.json

- Update @sveltejs/kit from 2.11.1 to 2.20.6


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3305/files#diff-6288951fff74ec246c9cc023b7b7e3e9aad31423891bc4ea25b5d84a5f5b061f">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr><tr><td><strong>Configuration
changes</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>package.json</strong><dd><code>Add security resolution
for @sveltejs/kit</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></summary>
<hr>

package.json

- Add resolution for @sveltejs/kit >= 2.20.6


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3305/files#diff-7ae45ad102eab3b6d7e7896acd08c427a9b25b346470d7bc6507b6481575d519">+2/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr></tr></tbody></table>

___

> <details> <summary> Need help?</summary><li>Type <code>/help how to
...</code> in the comments thread for any questions about PR-Agent
usage.</li><li>Check out the <a
href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a>
for more information.</li></details>
2025-04-16 12:48:40 +02:00
robertkasza
d9eb90604d fix: update vite and nextjs because of vulnerability (#3303)
### **PR Type**
Bug fix


___

### **Description**
- Update Vite and Next.js versions for security

- Add new version constraints for Vite and Next.js

- Create changeset for patch updates to multiple packages


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Documentation</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>neat-mugs-bake.md</strong><dd><code>Add changeset for
Vite and Next.js updates</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></summary>
<hr>

.changeset/neat-mugs-bake.md

<li>Add new changeset file for patch updates<br> <li> List affected
packages including dashboard and examples<br> <li> Describe fix as
updating Vite and Next.js


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3303/files#diff-6ec609b553758bab0278f440f07a0af9e742df25dbdc038b6212bb683f88eb59">+13/-0</a>&nbsp;
&nbsp; </td>

</tr>
</table></td></tr><tr><td><strong>Dependencies</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>package.json</strong><dd><code>Update dependency
version constraints</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

package.json

<li>Add new version constraint for Next.js (>=14.2.26)<br> <li> Update
Vite version constraints (>=5.4.18 and >=6.2.6)<br> <li> Remove outdated
Vite version constraint


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3303/files#diff-7ae45ad102eab3b6d7e7896acd08c427a9b25b346470d7bc6507b6481575d519">+4/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr></tr></tbody></table>

___

> <details> <summary> Need help?</summary><li>Type <code>/help how to
...</code> in the comments thread for any questions about PR-Agent
usage.</li><li>Check out the <a
href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a>
for more information.</li></details>
2025-04-16 09:17:12 +02:00
Nuno Pato
cef647194d fix: dashboard: add anonid to user's metadata (#3282)
### **PR Type**
Enhancement


___

### **Description**
- Add anonymous ID to user metadata during signup

- Integrate Segment analytics for anonymous ID retrieval

- Update GitHub sign-in to include anonymous ID

- Add changeset for version bump and changelog


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Enhancement</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>signup.tsx</strong><dd><code>Integrate anonymous ID in
signup process</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></summary>
<hr>

dashboard/src/pages/signup.tsx

<li>Import Segment analytics<br> <li> Add state for anonymous ID<br>
<li> Fetch anonymous ID on component mount<br> <li> Include anonymous ID
in email and GitHub signup


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3282/files#diff-fc2b5989e3bbafda1d3d8b2317d24c39ef2b8cec0c4dc410170fa2da13464f68">+19/-1</a>&nbsp;
&nbsp; </td>

</tr>
</table></td></tr><tr><td><strong>Documentation</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>tall-eggs-battle.md</strong><dd><code>Add changeset for
dashboard update</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

.changeset/tall-eggs-battle.md

<li>Add changeset file for version bump<br> <li> Describe change as
adding anonid to user's metadata


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3282/files#diff-26ba0d1f688299d031611809f726356bdec0104a9264ec7dcca0757985023a58">+5/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr></tr></tbody></table>

___

> <details> <summary> Need help?</summary><li>Type <code>/help how to
...</code> in the comments thread for any questions about PR-Agent
usage.</li><li>Check out the <a
href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a>
for more information.</li></details>
2025-04-10 14:29:56 +00:00
robertkasza
efd68c3f92 chore (react-apollo): run e2e on preview instead of dev server (#3295)
### **PR Type**
Enhancement


___

### **Description**
- Run e2e tests on preview build instead of dev server

- Update Playwright configuration for better test reliability

- Add new script for building and previewing in one step

- Improve clean and install process with new script


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Enhancement</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>playwright.config.ts</strong><dd><code>Update
Playwright config for preview build and improved
tracing</code></dd></summary>
<hr>

examples/react-apollo/playwright.config.ts

<li>Changed webServer command from 'pnpm dev' to 'pnpm
build:preview'<br> <li> Updated trace option from 'on-first-retry' to
'retain-on-failure'


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3295/files#diff-b22b77d29b0086af8ed742e35817880023261afccab0a6a41093e2a6d58715aa">+2/-2</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td>
    <details>
<summary><strong>package.json</strong><dd><code>Add build:preview script
and specify preview port</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; </dd></summary>
<hr>

examples/react-apollo/package.json

<li>Added port 3000 to preview script<br> <li> Introduced new
'build:preview' script combining build and preview


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3295/files#diff-d95dc3391741287366ea2e61f70e9ccc64452e0d22b1db91d6bf524f5aa4331c">+2/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td>
    <details>
<summary><strong>package.json</strong><dd><code>Add clean:install script
for project maintenance</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

package.json

- Added new 'clean:install' script for cleaning and reinstalling


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3295/files#diff-7ae45ad102eab3b6d7e7896acd08c427a9b25b346470d7bc6507b6481575d519">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr></tr></tbody></table>

___

> <details> <summary> Need help?</summary><li>Type <code>/help how to
...</code> in the comments thread for any questions about PR-Agent
usage.</li><li>Check out the <a
href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a>
for more information.</li></details>
2025-04-10 15:51:05 +02:00
robertkasza
233232b06f feat (dashboard): improve upgrade project (#3257)
### **PR Type**
Enhancement, Tests


___

### **Description**
- Introduced `TransferOrUpgradeProjectDialog` to unify transfer and
upgrade dialogs.

- Enhanced project upgrade flow with new components and logic.

- Added comprehensive tests for the new upgrade and transfer
functionalities.

- Replaced `TransferProjectDialog` with `TransferOrUpgradeProjectDialog`
across the codebase.


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Miscellaneous</strong></td><td><details><summary>2
files</summary><table>
<tr>
<td><strong>SelectOrgAndProject.tsx</strong><dd><code>Removed unused
import statement.</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3257/files#diff-7d86c6e5bc51696bf1aa421c920e01a1447699456c37b025bdc407050c7b5613">+0/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>OverviewTopBar.tsx</strong><dd><code>Updated import for
`UpgradeProjectDialog`.</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3257/files#diff-560ae107ed8e458fa4b4a226b9f5c24e24b042b5f9bcea9317c78e75929faa4b">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

</table></details></td></tr><tr><td><strong>Enhancement</strong></td><td><details><summary>16
files</summary><table>
<tr>
<td><strong>UpgradeToProBanner.tsx</strong><dd><code>Updated to use
`TransferOrUpgradeProjectDialog`.</code>&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3257/files#diff-f38fc14d24ec6ee22f9a100cc473c641dcdc66284d41d030c456bf505094ed9d">+2/-2</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>StripeEmbeddedForm.tsx</strong><dd><code>Wrapped
`EmbeddedCheckoutProvider` with a scrollable container.</code></dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3257/files#diff-d8e63f9bdc9c2c672a4caabd406bf77bec4e4988e716d2b9e101182a863eb495">+10/-8</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>TransferProject.tsx</strong><dd><code>Replaced
<code>TransferProjectDialog</code> with
<code>TransferOrUpgradeProjectDialog</code>.</code></dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3257/files#diff-bb5ac90e4fcb5841e3fef912beec1b1dbe83b273eea7a9e39fb258ff0361e7e3">+2/-2</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>FinishOrgCreationProcess.tsx</strong><dd><code>Refactored to
use <code>useFinishOrgCreation</code> hook for dynamic status
<br>handling.</code></dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3257/files#diff-7602855e6aaab1dd3810c866acbedd5b9eb22c271806969eb9a3435f1c76ca8d">+13/-5</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>FinishOrgCreation.tsx</strong><dd><code>Simplified
`FinishOrgCreation` component logic.</code>&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3257/files#diff-9e3ccc4f3c0168746e53b68211d07391593712d5d74847861248cfa7da31dd7d">+4/-5</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>

<td><strong>TransferOrUpgradeProjectDialog.tsx</strong><dd><code>Introduced
`TransferOrUpgradeProjectDialog` component.</code>&nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3257/files#diff-06d6ae707f06c0db49a8930a8756195899ece09f08affa44aeadedce4b208948">+105/-0</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>TransferProjectDialogContent.tsx</strong><dd><code>Added
`TransferProjectDialogContent` for transfer logic.</code>&nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3257/files#diff-3f66f2e8af0175d1c3f9d4940b8dc965fefa18967c8f4977739ac73000708763">+100/-0</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>TransferProjectForm.tsx</strong><dd><code>Added
`TransferProjectForm` for organization selection and
transfer.</code></dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3257/files#diff-3324c79d8b4d48777467132ba0f13a95d4b0f1a9fbb4df9fd7f67735ac40cbbd">+186/-0</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>UpgradeProjectDialogContent.tsx</strong><dd><code>Added
`UpgradeProjectDialogContent` for project upgrade flow.</code></dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3257/files#diff-ced98d2b8b0e83e41fd9bd569a6dd3fb5c4013861d3352628e63abe0c285d2ba">+96/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>index.ts</strong><dd><code>Exported
`TransferOrUpgradeProjectDialog`.</code>&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3257/files#diff-bd61908ca8ab41f1a88cdcc3bafe4264b1e8120d7f65ff64f158631dd4e65a58">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>NotificationsTray.tsx</strong><dd><code>Added router
readiness check in `NotificationsTray`.</code>&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3257/files#diff-8b559ee1d3176203e8a4e1588924d57944d09d792117ed578b27cd5401ee5d4f">+3/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>useFinishOrgCreation.ts</strong><dd><code>Added router
readiness check to `useFinishOrgCreation`.</code>&nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3257/files#diff-3b8bf7608ab36d8ab0df895e400f0d2d9e29fad2055b40b33d8d9912a27c99c3">+1/-2</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>ApplicationPaused.tsx</strong><dd><code>Replaced
<code>TransferProjectDialog</code> with
<code>TransferOrUpgradeProjectDialog</code>.</code></dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3257/files#diff-14afdf5ac20f058c26563a6992a3751f11cf173eec27206001262b5d1b3b979f">+2/-2</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>UpgradeNotification.tsx</strong><dd><code>Updated to use
`TransferOrUpgradeProjectDialog`.</code>&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3257/files#diff-f712e65a6e88f2731fc5597117f716594311087f8090e3e8f5f76e1a67c95188">+2/-2</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>UpgradeProjectDialog.tsx</strong><dd><code>Updated to use
`TransferOrUpgradeProjectDialog` for upgrades.</code></dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3257/files#diff-7bfab4ad088dbc503c1304f5620e22e02f70602bf14ba6b495969b882b2eb30e">+2/-2</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>verify.tsx</strong><dd><code>Refactored to use
`FinishOrgCreationProcess` with hooks.</code>&nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3257/files#diff-5fa0ea2519bed6649a8aa98826526945868bd7a925c5ce5edb3fd14e81273947">+1/-5</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

</table></details></td></tr><tr><td><strong>Tests</strong></td><td><details><summary>2
files</summary><table>
<tr>

<td><strong>TransferOrUpgradeProjectDialog.test.tsx</strong><dd><code>Added
tests for `TransferOrUpgradeProjectDialog`.</code>&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3257/files#diff-1b274953c536fcd901f72765ab134a34641442655988bde5595f63265a9e7ce9">+155/-12</a></td>

</tr>

<tr>
<td><strong>NotificationsTray.test.tsx</strong><dd><code>Added tests for
router readiness in `NotificationsTray`.</code>&nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3257/files#diff-727f6debec6a102557407e55c56363e0c75486e30a732158f85c81ada892f77c">+39/-4</a>&nbsp;
&nbsp; </td>

</tr>

</table></details></td></tr><tr><td><strong>Cleanup</strong></td><td><details><summary>2
files</summary><table>
<tr>
<td><strong>TransferProjectDialog.tsx</strong><dd><code>Removed
deprecated `TransferProjectDialog`.</code>&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3257/files#diff-b68d4641a67e07a8bf8c14e1f705059c564e1bca53e591783581af27a488d86e">+0/-306</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>index.ts</strong><dd><code>Removed export for deprecated
`TransferProjectDialog`.</code>&nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3257/files#diff-ed023a2c08c77e3693789305cf9b9f2cd871090acf7b0775c7d7434903710c42">+0/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

</table></details></td></tr><tr><td><strong>Documentation</strong></td><td><details><summary>1
files</summary><table>
<tr>
<td><strong>tame-planes-sleep.md</strong><dd><code>Added changeset for
project upgrade dialog improvements.</code>&nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3257/files#diff-c83c4e28de9a00c1ee2cb4ad9867d2c42415c01c80e990205c351e6f5c8a6f83">+5/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></details></td></tr></tr></tbody></table>

___

> <details> <summary> Need help?</summary><li>Type <code>/help how to
...</code> in the comments thread for any questions about PR-Agent
usage.</li><li>Check out the <a
href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a>
for more information.</li></details>
2025-04-10 15:15:25 +02:00
David Barroso
5e962300f6 fix (docs): fixing mintlify breaking our docs (#3297) 2025-04-10 13:23:31 +02:00
Nuno Pato
048b3389e6 chore: docs: add segment analytics (#3294)
### **PR Type**
Enhancement


___

### **Description**
- Added Segment analytics integration to documentation

- Configured Segment key in docs.json file


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Configuration
changes</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>docs.json</strong><dd><code>Configure Segment analytics
in docs.json</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></summary>
<hr>

docs/docs.json

<li>Added 'integrations' object with Segment configuration<br> <li>
Included Segment API key for analytics tracking


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3294/files#diff-873ce17c654718debe2fe308a2f2279bde8663686423c51f97fab2dd0722b8d9">+5/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr></tr></tbody></table>

___

> <details> <summary> Need help?</summary><li>Type <code>/help how to
...</code> in the comments thread for any questions about PR-Agent
usage.</li><li>Check out the <a
href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a>
for more information.</li></details>
2025-04-08 12:17:05 +00:00
375 changed files with 9871 additions and 3169 deletions

19
.github/labeler.yml vendored
View File

@@ -1,24 +1,25 @@
dashboard:
- dashboard/**/*
- any:
- changed-files:
- any-glob-to-any-file: ['dashboard/**/*']
documentation:
- any:
- docs/**/*
- any: ['docs/**/*']
examples:
- examples/**/*
- any: ['examples/**/*']
sdk:
- packages/**/*
- any: ['packages/**/*']
integrations:
- integrations/**/*
- any: ['integrations/**/*']
react:
- '{packages,examples,integrations}/*react*/**/*'
- any: ['{packages,examples,integrations}/*react*/**/*']
nextjs:
- '{packages,examples}/*next*/**/*'
- any: ['{packages,examples}/*next*/**/*']
vue:
- '{packages,examples,integrations}/*vue*/**/*'
- any: ['{packages,examples,integrations}/*vue*/**/*']

View File

@@ -7,12 +7,14 @@ on:
- 'assets/**'
- '**.md'
- 'LICENSE'
- 'docs/**'
pull_request:
types: [opened, synchronize]
paths-ignore:
- 'assets/**'
- '**.md'
- 'LICENSE'
- 'docs/**'
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: nhost
@@ -27,7 +29,8 @@ env:
NHOST_PRO_TEST_PROJECT_NAME: ${{ vars.NHOST_PRO_TEST_PROJECT_NAME }}
NHOST_TEST_USER_EMAIL: ${{ secrets.NHOST_TEST_USER_EMAIL }}
NHOST_TEST_USER_PASSWORD: ${{ secrets.NHOST_TEST_USER_PASSWORD }}
NHOST_TEST_PROJECT_ADMIN_SECRET: ${{ secrets.NHOST_TEST_PROJECT_ADMIN_SECRET }}
NHOST_TEST_PROJECT_ADMIN_SECRET: '${{ secrets.NHOST_TEST_PROJECT_ADMIN_SECRET }}'
NHOST_TEST_FREE_USER_EMAILS: ${{ secrets.NHOST_TEST_FREE_USER_EMAILS }}
jobs:
build:
@@ -169,6 +172,10 @@ jobs:
- name: Set Dashboard Preview URL
if: steps.fetch-dashboard-preview-url.outputs.preview_url != ''
run: echo "NHOST_TEST_DASHBOARD_URL=https://${{ steps.fetch-dashboard-preview-url.outputs.preview_url }}" >> $GITHUB_ENV
- name: Run Upgrade project Dashboard e2e tests
if: matrix.package.path == 'dashboard'
timeout-minutes: 10
run: pnpm --filter="${{ matrix.package.name }}" run e2e:upgrade-project
# * Run the `ci` script of the current package of the matrix. Dependencies build is cached by Turborepo
- name: Run e2e tests
timeout-minutes: 20
@@ -177,13 +184,16 @@ jobs:
- name: Run Local Dashboard e2e tests
if: matrix.package.path == 'dashboard'
timeout-minutes: 5
run: |
pnpm --filter="${{ matrix.package.name }}" run e2e-local
run: pnpm --filter="${{ matrix.package.name }}" run e2e:local
- name: Stop Nhost CLI
if: matrix.package.path == 'dashboard'
working-directory: ./nhost-test-project
run: nhost down
- name: Stop Nhost CLI for packages
if: always() && (matrix.package.path == 'packages/hasura-auth-js' || matrix.package.path == 'packages/hasura-storage-js')
working-directory: ./${{ matrix.package.path }}
run: nhost down
- id: file-name
if: ${{ failure() }}
name: Transform package name into a valid file name

View File

@@ -56,3 +56,31 @@ jobs:
vercel pull --environment=production --token=${{ secrets.DASHBOARD_VERCEL_DEPLOY_TOKEN }}
vercel build --prod --token=${{ secrets.DASHBOARD_VERCEL_DEPLOY_TOKEN }}
vercel deploy --prebuilt --prod --token=${{ secrets.DASHBOARD_VERCEL_DEPLOY_TOKEN }}
- name: Send Discord notification (success)
if: success()
uses: tsickert/discord-webhook@v7.0.0
with:
webhook-url: ${{ secrets.DISCORD_WEBHOOK_PRODUCTION }}
embed-title: "Dashboard Deployment"
embed-description: |
**Status**: success
**Triggered by**: ${{ github.actor }}
**Inputs**:
- Git Ref: ${{ inputs.git_ref }}
embed-color: '5763719'
- name: Send Discord notification (failure)
if: failure()
uses: tsickert/discord-webhook@v7.0.0
with:
webhook-url: ${{ secrets.DISCORD_WEBHOOK_PRODUCTION }}
embed-title: "Dashboard Deployment"
embed-description: |
**Status**: failure
**Triggered by**: ${{ github.actor }}
**Inputs**:
- Git Ref: ${{ inputs.git_ref }}
embed-color: '15548997'

View File

@@ -3,13 +3,12 @@ on:
- pull_request_target
jobs:
triage:
labeler:
permissions:
contents: read
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v4
- uses: actions/labeler@v5
with:
repo-token: '${{ secrets.GH_PAT }}'
sync-labels: ''
repo-token: ${{ secrets.GH_PAT }}

View File

@@ -2,9 +2,7 @@
## Requirements
### Node.js v18
_⚠️ Node.js v16 is also supported for the time being but support will be dropped in the near future_.
### Node.js v20 or later
### [pnpm](https://pnpm.io/) package manager

View File

@@ -2,5 +2,5 @@
// $schema provides code completion hints to IDEs.
"$schema": "https://github.com/IBM/audit-ci/raw/main/docs/schema.json",
"moderate": true,
"allowlist": ["vue-template-compiler"]
"allowlist": ["vue-template-compiler", { "id": "CVE-2025-48068", "path": "next" }]
}

View File

@@ -11,20 +11,9 @@
"allowSyntheticDefaultImports": true,
"skipLibCheck": true,
"moduleResolution": "node",
"target": "ES6",
"target": "ESNext",
"module": "CommonJS",
"lib": [
"es5",
"dom",
"es2015.promise",
"es2015.symbol",
"es2015.iterable",
"es2015.collection",
"es2015.symbol.wellknown",
"es2015.core",
"es2017.object",
"es2017.string"
],
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"resolveJsonModule": true,
"esModuleInterop": true,
"sourceMap": true,
@@ -79,4 +68,4 @@
"**/*/__tests__",
"**/*/__mocks__"
]
}
}

View File

@@ -25,4 +25,6 @@ NEXT_PUBLIC_ZENDESK_USER_EMAIL=
CODEGEN_GRAPHQL_URL=https://local.graphql.local.nhost.run/v1
CODEGEN_HASURA_ADMIN_SECRET=nhost-admin-secret
NEXT_PUBLIC_TURNSTILE_SITE_KEY=FIXME
NEXT_PUBLIC_TURNSTILE_SITE_KEY=FIXME
NEXT_PUBLIC_SOC2_REPORT_FILE_ID=

View File

@@ -76,6 +76,13 @@ module.exports = {
],
},
],
'jsx-a11y/label-has-associated-control': [
2,
{
controlComponents: ['Input'],
depth: 3,
},
],
},
overrides: [
{

View File

@@ -1,8 +1,9 @@
import { NhostProvider } from '@/providers/nhost';
import '@fontsource/inter';
import '@fontsource/inter/500.css';
import '@fontsource/inter/700.css';
import { CssBaseline, ThemeProvider } from '@mui/material';
import { NhostClient, NhostProvider } from '@nhost/nextjs';
import { createClient } from '@nhost/nhost-js-beta';
import { NhostApolloProvider } from '@nhost/react-apollo';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { Buffer } from 'buffer';
@@ -58,7 +59,9 @@ export const decorators = [
</NhostApolloProvider>
),
(Story) => (
<NhostProvider nhost={new NhostClient({ subdomain: 'local' })}>
<NhostProvider
nhost={createClient({ subdomain: 'local', region: 'local' })}
>
<Story />
</NhostProvider>
),

View File

@@ -1,5 +1,80 @@
# @nhost/dashboard
## 2.33.0
### Minor Changes
- aee9a80: chore: update typescript version to the latest
- 5ef3f76: chore (dashboard): Use the new SDK in the Dashboard
### Patch Changes
- 9ed8ce8: fix (dashboard): Request new Mfa ticket after an invalid totp when signing in
- fd3b5c7: fix (dashboard): Limit new project's name to a maximum of 32 charachters in E2E tests
## 2.32.0
### Minor Changes
- 736862c: fix: update link to base directory docs in git settings
- ea99fb3: chore: dashboard: improve messaging when git connected
### Patch Changes
- d738884: chore (dashboard): Add link about antivirus integration
## 2.31.0
### Minor Changes
- 39b10a2: feat (dashboard): Add multi-factor authentication
- 4b84780: feat (dashboard): Add Webauthn to dashboard
### Patch Changes
- 61eb6cd: fix (dashboard): Fix update project e2e test
- @nhost/react-apollo@18.0.0
- @nhost/nextjs@2.2.8
## 2.30.0
### Minor Changes
- f6947a2: fix: fetch job-backup services logs using Live filter
- 44a3e6b: fix: collapsed main navigation sidebar overlaps mobile navbar
- 99b78f1: feat: dashboard: add download button for soc2 report
- 9acae7d: fix: e2e tests, stop on error when refreshing metadata
### Patch Changes
- 31e636a: fix (dashboard): Use the correct payload to reset metadata before the e2e tests
## 2.29.0
### Minor Changes
- c97b43f: fix: update vite to address vulnerability audit
- a0931e2: fix: improve logs time range and filter selection
- c0635ae: feat (dashboard): Add information about that free organization cannot be upgraded.
- e87505c: fix: can downsize postgres storage capacity using local dashboard
## 2.28.0
### Minor Changes
- 8552678: feat: dashboard: add additional events to segment
- 0bf2808: chore: refresh metadata before end-to-end tests
- 72a365c: fix: correct graphql page roles dropdown's source
- cef6471: fix: dashboard: add anonid to user's metadata
### Patch Changes
- d9eb906: fix: update vite and nextjs because of vulnerability
- 233232b: feat (dashboard): improve Upgrade project dialog
- Updated dependencies [d9eb906]
- @nhost/nextjs@2.2.7
- @nhost/react-apollo@17.0.4
## 2.27.0
### Minor Changes

View File

@@ -40,3 +40,7 @@ export const TEST_USER_EMAIL = process.env.NHOST_TEST_USER_EMAIL;
export const TEST_USER_PASSWORD = process.env.NHOST_TEST_USER_PASSWORD;
export const TEST_PERSONAL_ORG_SLUG = process.env.NHOST_TEST_PERSONAL_ORG_SLUG;
const freeUserEmails = process.env.NHOST_TEST_FREE_USER_EMAILS;
export const TEST_FREE_USER_EMAILS = JSON.parse(freeUserEmails);

View File

@@ -14,6 +14,7 @@ export const test = base.extend<{ authenticatedNhostPage: Page }>({
);
await use(page);
// update the context to get the new refresh token
await page.waitForLoadState('networkidle');
await page.context().storageState({ path: AUTH_CONTEXT });
await page.close();
},

View File

@@ -0,0 +1,55 @@
/* eslint-disable no-console */
import { TEST_PROJECT_ADMIN_SECRET, TEST_PROJECT_SUBDOMAIN } from '@/e2e/env';
import { test as setup } from '@playwright/test';
setup('refresh metadata', async () => {
try {
const response = await fetch(
`https://${TEST_PROJECT_SUBDOMAIN}.hasura.eu-central-1.staging.nhost.run/v1/metadata`,
{
method: 'POST',
headers: {
'x-hasura-admin-secret': TEST_PROJECT_ADMIN_SECRET,
},
body: JSON.stringify({
args: [
{
type: 'reload_metadata',
args: {
reload_sources: false,
},
},
{
args: {},
type: 'get_inconsistent_metadata',
},
],
source: 'default',
type: 'bulk',
}),
},
);
const body = await response.json();
if (!response.ok) {
const message = `[${body.code}]:${body.error}`;
throw new Error(message);
} else {
const isConsistent = body[0].is_consistent;
if (isConsistent) {
console.log('Metadata is consistent.');
} else {
console.error('Metadata is not consistent.');
console.error(body[0].inconsistent_objects);
throw new Error('Metadata is not consistent');
}
}
} catch (error) {
// Log safe error information
console.error(
'Failed to refresh metadata:',
error instanceof Error ? error.message : 'Unknown error',
);
throw new Error('Failed to refresh metadata');
}
});

View File

@@ -0,0 +1,142 @@
import { expect, test } from '@/e2e/fixtures/auth-hook';
import {
getCardExpiration,
getFreeUserStarterOrgSlug,
getNewOrgSlug,
getNewProjectName,
getNewProjectSlug,
getOrgSlugFromUrl,
getProjectSlugFromUrl,
gotoUrl,
loginWithFreeUser,
setNewOrgSlug,
setNewProjectName,
setNewProjectSlug,
} from '@/e2e/utils';
import { faker } from '@faker-js/faker';
import type { Page } from '@playwright/test';
let page: Page;
test.beforeAll(async ({ browser }) => {
page = await browser.newPage();
await loginWithFreeUser(page);
});
test('should create a new project', async () => {
await gotoUrl(page, `/orgs/${getFreeUserStarterOrgSlug()}/projects/new`);
const projectName = faker.lorem.words(3).slice(0, 32);
await page.getByLabel('Project Name').fill(projectName);
await page.getByText('Create Project').click();
expect(page.getByText('Creating the project...')).toBeVisible();
expect(page.getByText('Internal info')).toBeVisible();
await page.waitForSelector('button:has-text("Upgrade project")', {
timeout: 180000,
});
const newProjectSlug = getProjectSlugFromUrl(page.url());
setNewProjectSlug(newProjectSlug);
setNewProjectName(projectName);
});
test('should upgrade the project', async () => {
await gotoUrl(
page,
`/orgs/${getFreeUserStarterOrgSlug()}/projects/${getNewProjectSlug()}`,
);
const upgradeProject = page.getByText('Upgrade project');
expect(upgradeProject).toBeVisible();
await upgradeProject.click();
await page.waitForSelector('h2:has-text("Upgrade project")');
await page.getByRole('button', { name: 'Continue' }).click();
await page.waitForSelector('h2:has-text("New Organization")');
const newOrgName = faker.lorem.words(3);
await page.getByLabel('Organization Name').fill(newOrgName);
await page.getByText('Create organization').click();
await page.waitForSelector('button:has-text("Create organization")', {
state: 'hidden',
});
const stripeFrame = page
.frameLocator('iframe[name="embedded-checkout"]')
.first();
stripeFrame.getByText('Subscribe to Nhost');
await stripeFrame.getByLabel('Email').fill(faker.internet.email());
await stripeFrame
.getByPlaceholder('1234 1234 1234 1234')
.fill('4242424242424242');
await stripeFrame.getByPlaceholder('MM / YY').fill(getCardExpiration());
await stripeFrame.getByPlaceholder('CVC').fill('123');
await stripeFrame
.getByPlaceholder('Full name on card')
.fill('EndyTo EndyTest');
await stripeFrame.locator('#billingCountry').scrollIntoViewIfNeeded();
// Need to comment out for local testing START
await stripeFrame.getByPlaceholder('Address', { exact: true }).click();
stripeFrame.locator('span:has-text("Enter address manually")');
await stripeFrame.getByText('Enter address manually').click();
await stripeFrame
.getByPlaceholder('Address line 1', { exact: true })
.fill('123 Main Street');
await stripeFrame
.getByPlaceholder('City', { exact: true })
.fill('Springfield');
await stripeFrame.getByPlaceholder('ZIP', { exact: true }).fill('62701');
await stripeFrame.locator('#enableStripePass').click({ force: true });
// local Comment end
await stripeFrame
.getByTestId('hosted-payment-submit-button')
.scrollIntoViewIfNeeded();
await stripeFrame
.getByTestId('hosted-payment-submit-button')
.click({ force: true });
await page.waitForSelector('h2:has-text("Upgrade project")');
await page.waitForSelector(
'div:has-text("Organization created successfully.")',
);
await page.waitForSelector(
'div:has-text("Project has been upgraded successfully!")',
);
page.getByRole('button', { name: 'Create project' });
await page.waitForSelector(`div:has-text("${newOrgName}")`);
await page.waitForSelector(`p:has-text("${getNewProjectName()}")`);
setNewOrgSlug(getOrgSlugFromUrl(page.url()));
});
test('should delete the new organization', async () => {
await gotoUrl(page, `/orgs/${getNewOrgSlug()}/projects`);
await page.getByRole('link', { name: 'Settings' }).click();
await page.waitForSelector('h3:has-text("Delete Organization")');
await page.getByRole('button', { name: 'Delete' }).click();
await page.waitForSelector('h2:has-text("Delete Organization")');
expect(page.getByTestId('deleteOrgButton')).toBeDisabled();
await page.getByLabel("I'm sure I want to delete this Organization").click();
expect(page.getByTestId('deleteOrgButton')).toBeDisabled();
await page.getByLabel('I understand this action cannot be undone').click();
expect(page.getByTestId('deleteOrgButton')).not.toBeDisabled();
await page.getByTestId('deleteOrgButton').click();
await page.waitForSelector('div:has-text("Deleting the organization")');
await page.waitForSelector(
'div:has-text("Successfully deleted the organization")',
);
await page.waitForSelector(`div:has-text("Personal Organization")`);
});

View File

@@ -1,6 +1,12 @@
import { TEST_ORGANIZATION_SLUG, TEST_PROJECT_SUBDOMAIN } from '@/e2e/env';
import {
TEST_FREE_USER_EMAILS,
TEST_ORGANIZATION_SLUG,
TEST_PROJECT_SUBDOMAIN,
TEST_USER_PASSWORD,
} from '@/e2e/env';
import { faker } from '@faker-js/faker';
import type { Page } from '@playwright/test';
import { type Page, expect } from '@playwright/test';
import { add, format } from 'date-fns-v4';
/**
* Open a project by navigating to the project's overview page.
@@ -213,8 +219,96 @@ export async function clickPermissionButton({
.click();
}
export async function gotoAuthURL(page) {
export async function gotoAuthURL(page: Page) {
const authUrl = `/orgs/${TEST_ORGANIZATION_SLUG}/projects/${TEST_PROJECT_SUBDOMAIN}/users`;
await page.goto(authUrl);
await page.waitForURL(authUrl, { waitUntil: 'networkidle' });
}
export async function gotoUrl(page: Page, url: string) {
await page.url;
await page.goto(url);
await page.waitForURL(url, { waitUntil: 'networkidle' });
}
let newOrgSlug: string;
export function getNewOrgSlug() {
return newOrgSlug;
}
export function setNewOrgSlug(slug: string) {
newOrgSlug = slug;
}
let freeUserStarterOrgSlug: string;
export function getFreeUserStarterOrgSlug() {
return freeUserStarterOrgSlug;
}
export function setFreeUserStarterOrgSlug(slug: string) {
freeUserStarterOrgSlug = slug;
}
let newProjectSlug: string;
export function getNewProjectSlug() {
return newProjectSlug;
}
export function setNewProjectSlug(slug: string) {
newProjectSlug = slug;
}
export function getProjectSlugFromUrl(url: string) {
const [, projectSlug] = url.split('/projects/');
return projectSlug;
}
export function getOrgSlugFromUrl(url: string) {
const orgSlug = url.split('/orgs/')[1].replace('/projects', '');
return orgSlug;
}
export function getCardExpiration() {
const now = add(new Date(), { years: 3 });
return format(now, 'MMyy');
}
let newProjectName: string;
export function getNewProjectName() {
return newProjectName;
}
export function setNewProjectName(name: string) {
newProjectName = name;
}
function getRandomUserIndex(): number {
return Math.floor(Math.random() * 5);
}
export async function loginWithFreeUser(page: Page) {
const userIndex = getRandomUserIndex();
const freeUserEmail = TEST_FREE_USER_EMAILS[userIndex];
// eslint-disable-next-line no-console
console.log(`Selected userIndex: ${userIndex}`);
await page.goto('/');
await page.waitForURL('/signin');
await page.getByRole('link', { name: /continue with email/i }).click();
await page.waitForURL('/signin/email');
await page.getByLabel('Email').fill(freeUserEmail);
await page.getByLabel('Password').fill(TEST_USER_PASSWORD);
await page.getByRole('button', { name: /sign in/i }).click();
expect(
await page.getByRole('button', { name: 'Create project' }),
).not.toBeVisible();
await page.waitForSelector('h2:has-text("Welcome to")', { timeout: 20000 });
setFreeUserStarterOrgSlug(getOrgSlugFromUrl(await page.url()));
}

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/dashboard",
"version": "2.27.0",
"version": "2.33.0",
"private": true,
"scripts": {
"preinstall": "npx only-allow pnpm",
@@ -16,8 +16,10 @@
"storybook": "start-storybook -p 6006 -s public",
"build-storybook": "build-storybook",
"install-browsers": "pnpm playwright install && pnpm playwright install-deps",
"e2e": "pnpm install-browsers && pnpm playwright test --config=playwright.config.ts",
"e2e-local": "pnpm install-browsers && pnpm playwright test --config=playwright.local.config.ts"
"e2e:tests": "pnpm install-browsers && pnpm playwright test --config=playwright.config.ts -x",
"e2e": "pnpm e2e:tests --project=main",
"e2e:local": "pnpm e2e:tests --project=local",
"e2e:upgrade-project": "pnpm e2e:tests --project=upgrade-project"
},
"dependencies": {
"@apollo/client": "^3.9.9",
@@ -37,13 +39,13 @@
"@heroicons/react": "^1.0.6",
"@hookform/resolvers": "^3.9.0",
"@iarna/toml": "^2.2.5",
"@icons-pack/react-simple-icons": "^9.6.0",
"@marsidev/react-turnstile": "^1.0.2",
"@mui/base": "5.0.0-beta.31",
"@mui/material": "^5.15.14",
"@mui/system": "^5.15.14",
"@mui/x-date-pickers": "^5.0.20",
"@nhost/nextjs": "workspace:*",
"@nhost/react-apollo": "workspace:*",
"@nhost/nhost-js-beta": "npm:@nhost/nhost-js@5.0.0-beta.7",
"@radix-ui/react-accordion": "^1.2.1",
"@radix-ui/react-alert-dialog": "^1.1.2",
"@radix-ui/react-checkbox": "^1.1.2",
@@ -60,6 +62,7 @@
"@radix-ui/react-tabs": "^1.1.3",
"@radix-ui/react-tooltip": "^1.1.2",
"@segment/analytics-next": "^1.77.0",
"@simplewebauthn/browser": "^9.0.1",
"@stripe/react-stripe-js": "^2.6.2",
"@stripe/stripe-js": "^1.54.2",
"@tailwindcss/forms": "^0.5.7",
@@ -85,9 +88,10 @@
"graphql-tag": "^2.12.6",
"graphql-ws": "^5.16.0",
"just-kebab-case": "^4.2.0",
"jwt-decode": "^4.0.0",
"lodash.debounce": "^4.0.8",
"lucide-react": "^0.416.0",
"next": "^14.2.25",
"next": "^14.2.26",
"next-nprogress-bar": "^2.3.13",
"next-seo": "^6.5.0",
"next-themes": "^0.3.0",
@@ -105,7 +109,7 @@
"react-is": "18.2.0",
"react-loading-skeleton": "^2.2.0",
"react-markdown": "^9.0.1",
"react-merge-refs": "^1.1.0",
"react-merge-refs": "^3.0.2",
"react-resizable-layout": "^0.7.2",
"react-table": "^7.8.0",
"recoil": "^0.7.7",
@@ -194,7 +198,7 @@
"tailwindcss": "^3.4.12",
"ts-node": "^10.9.2",
"tsconfig-paths-webpack-plugin": "^4.1.0",
"vite": "^5.4.17",
"vite": "^5.4.19",
"vite-tsconfig-paths": "^4.3.2",
"vitest": "^0.32.4"
},

View File

@@ -17,7 +17,7 @@ export default defineConfig({
reporter: 'html',
use: {
actionTimeout: 0,
trace: 'on-first-retry',
trace: 'retain-on-failure',
baseURL: process.env.NHOST_TEST_DASHBOARD_URL,
launchOptions: {
slowMo: 500,
@@ -34,13 +34,28 @@ export default defineConfig({
testMatch: ['**/teardown/*.teardown.ts'],
},
{
name: 'chromium',
name: 'main',
use: {
...devices['Desktop Chrome'],
storageState: 'e2e/.auth/user.json',
},
dependencies: ['setup'],
grepInvert: [/Local Dashboard CLI e2e tests/],
testIgnore: ['upgrade-project.test.ts', 'cli-local-dashboard.test.ts'],
},
{
name: 'local',
use: {
...devices['Desktop Chrome'],
baseURL: '', // Local dashboard URL
},
testMatch: 'cli-local-dashboard.test.ts',
},
{
name: 'upgrade-project',
testMatch: 'upgrade-project.test.ts',
use: {
...devices['Desktop Chrome'],
},
},
],
});

View File

@@ -1,31 +0,0 @@
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './e2e',
timeout: 30 * 1000,
expect: {
timeout: 5000,
},
fullyParallel: false,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: 1,
reporter: 'html',
use: {
actionTimeout: 0,
trace: 'on-first-retry',
baseURL: '', // Local dashboard URL
launchOptions: {
slowMo: 500,
},
},
projects: [
{
name: 'chromium',
use: {
...devices['Desktop Chrome'],
},
testMatch: ['**/e2e/cli-local-dashboard/**'],
},
],
});

View File

@@ -82,7 +82,7 @@ describe('DateTimePicker', () => {
const secondsInput = await screen.getByLabelText('Seconds');
await user.type(secondsInput, '13');
user.click(await screen.getByRole('button', { name: 'Select' }));
await user.click(await screen.getByRole('button', { name: 'Select' }));
await waitFor(async () =>
expect(

View File

@@ -0,0 +1,518 @@
import { render, screen, TestUserEvent, waitFor } from '@/tests/testUtils';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import MfaOtpForm from './MfaOtpForm';
const mocks = vi.hoisted(() => ({
toastError: vi.fn(),
}));
// Mock react-hot-toast
vi.mock('react-hot-toast', async () => {
const actualToast = await vi.importActual<any>('react-hot-toast');
return {
...actualToast,
default: {
...actualToast.default,
error: mocks.toastError,
},
};
});
// Mock the toast style props utility
vi.mock('@/utils/constants/settings', () => ({
getToastStyleProps: vi.fn(() => ({})),
}));
describe('MfaOtpForm', () => {
const mockSendMfaOtp = vi.fn();
const mockRequestNewMfaTicket = vi.fn();
const user = new TestUserEvent();
beforeEach(() => {
vi.clearAllMocks();
});
afterEach(() => {
vi.clearAllTimers();
});
const defaultProps = {
sendMfaOtp: mockSendMfaOtp,
loading: false,
requestNewMfaTicket: mockRequestNewMfaTicket,
} as any;
describe('Rendering and Initial State', () => {
it('renders with correct initial state', () => {
render(<MfaOtpForm {...defaultProps} />);
const input = screen.getByPlaceholderText('Enter TOTP');
const button = screen.getByRole('button', { name: 'Verify' });
expect(input).toBeInTheDocument();
expect(input).toHaveValue('');
expect(button).toBeInTheDocument();
expect(button).toBeDisabled();
});
it('focuses input on mount', () => {
render(<MfaOtpForm {...defaultProps} />);
const input = screen.getByPlaceholderText('Enter TOTP');
expect(input).toHaveFocus();
});
});
describe('Input Validation and Formatting', () => {
it('only accepts numeric characters', async () => {
render(<MfaOtpForm {...defaultProps} />);
const input = screen.getByPlaceholderText('Enter TOTP');
await user.type(input, 'abc123def456');
expect(input).toHaveValue('123456');
});
it('filters out non-numeric characters in real time', async () => {
render(<MfaOtpForm {...defaultProps} />);
const input = screen.getByPlaceholderText('Enter TOTP');
await user.type(input, '1a2b3c');
expect(input).toHaveValue('123');
});
it('button is disabled when input has fewer than 6 digits', async () => {
render(<MfaOtpForm {...defaultProps} />);
const input = screen.getByPlaceholderText('Enter TOTP');
const button = screen.getByRole('button', { name: 'Verify' });
await user.type(input, '12345');
expect(button).toBeDisabled();
});
it('button is enabled when input has exactly 6 digits', async () => {
render(<MfaOtpForm {...defaultProps} />);
const input = screen.getByPlaceholderText('Enter TOTP');
const button = screen.getByRole('button', { name: 'Verify' });
await user.type(input, '123456');
expect(button).toBeEnabled();
});
it('button is disabled when input has more than 6 digits', async () => {
render(<MfaOtpForm {...defaultProps} />);
const input = screen.getByPlaceholderText('Enter TOTP');
const button = screen.getByRole('button', { name: 'Verify' });
await user.type(input, '6123457');
expect(button).toBeDisabled();
});
});
describe('Loading States', () => {
it('disables input and button when loading prop is true', () => {
render(<MfaOtpForm {...defaultProps} loading />);
const input = screen.getByPlaceholderText('Enter TOTP');
const button = screen.getByRole('button');
expect(input).toBeDisabled();
expect(button).toBeDisabled();
expect(
screen.getByRole('button', { name: 'Verifying...' }),
).toBeInTheDocument();
});
it('input and button are disabled during submission', async () => {
// Mock sendMfaOtp to return a promise that we can control
const promise = new Promise(() => {}); // Never resolves
mockSendMfaOtp.mockReturnValue(promise);
render(<MfaOtpForm {...defaultProps} />);
const input = screen.getByPlaceholderText('Enter TOTP');
const button = screen.getByRole('button', { name: 'Verify' });
await user.type(input, '123456');
await user.click(button);
expect(input).toBeDisabled();
expect(button).toBeDisabled();
});
});
describe('Form Submission', () => {
it('triggers sendMfaOtp with correct code on button click', async () => {
mockSendMfaOtp.mockResolvedValue({ success: true });
render(<MfaOtpForm {...defaultProps} />);
const input = screen.getByPlaceholderText('Enter TOTP');
const button = screen.getByRole('button', { name: 'Verify' });
await user.type(input, '123456');
await user.click(button);
expect(mockSendMfaOtp).toHaveBeenCalledWith('123456');
expect(mockSendMfaOtp).toHaveBeenCalledTimes(1);
});
it('does not submit when input has fewer than 6 digits', async () => {
render(<MfaOtpForm {...defaultProps} />);
const input = screen.getByPlaceholderText('Enter TOTP');
const button = screen.getByRole('button', { name: 'Verify' });
await user.type(input, '12345');
await user.click(button);
expect(mockSendMfaOtp).not.toHaveBeenCalled();
});
it('does not submit multiple times when already submitting', async () => {
let resolvePromise: (value: any) => void;
const promise = new Promise((resolve) => {
resolvePromise = resolve;
});
mockSendMfaOtp.mockReturnValue(promise);
render(<MfaOtpForm {...defaultProps} />);
const input = screen.getByPlaceholderText('Enter TOTP');
const button = screen.getByRole('button', { name: 'Verify' });
await user.type(input, '123456');
await user.click(button);
await user.click(button); // Second click should be ignored
expect(mockSendMfaOtp).toHaveBeenCalledTimes(1);
// Resolve the promise to clean up
resolvePromise!({ success: true });
await waitFor(async () => {
await promise;
});
});
it('manages submission state properly', async () => {
let resolvePromise: (value: any) => void;
const promise = new Promise((resolve) => {
resolvePromise = resolve;
});
mockSendMfaOtp.mockReturnValue(promise);
render(<MfaOtpForm {...defaultProps} />);
const input = screen.getByPlaceholderText('Enter TOTP');
const button = screen.getByRole('button', { name: 'Verify' });
await user.type(input, '123456');
await user.click(button);
// During submission
expect(button).toBeDisabled();
expect(input).toBeDisabled();
// Resolve the promise
resolvePromise!({ success: true });
await waitFor(async () => {
await promise;
});
// After submission
await waitFor(() => {
expect(button).not.toBeDisabled();
expect(input).not.toBeDisabled();
});
});
});
describe('Error Handling', () => {
it('displays error toast when sendMfaOtp returns an error', async () => {
const errorMessage = 'Invalid TOTP code';
mockSendMfaOtp.mockRejectedValueOnce({ message: errorMessage });
render(<MfaOtpForm {...defaultProps} />);
const input = screen.getByPlaceholderText('Enter TOTP');
const button = screen.getByRole('button', { name: 'Verify' });
await user.type(input, '123456');
await user.click(button);
await waitFor(() => {
expect(mocks.toastError).toHaveBeenCalledWith(errorMessage, {});
});
});
it('shows generic error message when no specific error message is provided', async () => {
mockSendMfaOtp.mockRejectedValueOnce({});
render(<MfaOtpForm {...defaultProps} />);
const input = screen.getByPlaceholderText('Enter TOTP');
const button = screen.getByRole('button', { name: 'Verify' });
await user.type(input, '123456');
await user.click(button);
await waitFor(() => {
expect(mocks.toastError).toHaveBeenCalledWith(
'An error occurred. Please try again.',
{},
);
});
});
it('handles undefined error gracefully', async () => {
mockSendMfaOtp.mockResolvedValue({
error: undefined,
});
render(<MfaOtpForm {...defaultProps} />);
const input = screen.getByPlaceholderText('Enter TOTP');
const button = screen.getByRole('button', { name: 'Verify' });
await user.type(input, '123456');
await user.click(button);
// Should not throw an error
await waitFor(() => {
expect(mockSendMfaOtp).toHaveBeenCalled();
});
});
});
describe('MFA Ticket Renewal', () => {
it('calls requestNewMfaTicket when ticket is invalid', async () => {
// First call - set ticket as invalid
mockSendMfaOtp.mockRejectedValueOnce({ message: 'Invalid ticket' });
// Second call - should work
mockSendMfaOtp.mockResolvedValueOnce({ success: true });
render(<MfaOtpForm {...defaultProps} />);
const input = screen.getByPlaceholderText('Enter TOTP');
const button = screen.getByRole('button', { name: 'Verify' });
// First submission - creates error and marks ticket invalid
await user.type(input, '123456');
await user.click(button);
await waitFor(() => {
expect(mocks.toastError).toHaveBeenCalled();
});
// Clear input and try again
await user.clear(input);
await user.type(input, '654321');
await user.click(button);
await waitFor(() => {
expect(mockRequestNewMfaTicket).toHaveBeenCalled();
});
});
it('does not call requestNewMfaTicket on first submission', async () => {
mockSendMfaOtp.mockResolvedValue({ success: true });
render(<MfaOtpForm {...defaultProps} />);
const input = screen.getByPlaceholderText('Enter TOTP');
const button = screen.getByRole('button', { name: 'Verify' });
await user.type(input, '123456');
await user.click(button);
expect(mockRequestNewMfaTicket).not.toHaveBeenCalled();
});
it('works correctly when requestNewMfaTicket is not provided', async () => {
const propsWithoutTicketRenewal = {
sendMfaOtp: mockSendMfaOtp,
loading: false,
} as any;
mockSendMfaOtp.mockRejectedValueOnce({ message: 'Some error' });
render(<MfaOtpForm {...propsWithoutTicketRenewal} />);
const input = screen.getByPlaceholderText('Enter TOTP');
const button = screen.getByRole('button', { name: 'Verify' });
await user.type(input, '123456');
await user.click(button);
// Should not throw an error even without requestNewMfaTicket
await waitFor(() => {
expect(mocks.toastError).toHaveBeenCalled();
});
});
});
describe('User Interactions', () => {
it('updates input value correctly when typing', async () => {
render(<MfaOtpForm {...defaultProps} />);
const input = screen.getByPlaceholderText('Enter TOTP');
await user.type(input, '123');
expect(input).toHaveValue('123');
await user.type(input, '456');
expect(input).toHaveValue('123456');
});
it('can clear and retype input value', async () => {
render(<MfaOtpForm {...defaultProps} />);
const input = screen.getByPlaceholderText('Enter TOTP');
await user.type(input, '123456');
expect(input).toHaveValue('123456');
await user.clear(input);
expect(input).toHaveValue('');
await user.type(input, '654321');
expect(input).toHaveValue('654321');
});
it('button triggers submission with valid code', async () => {
mockSendMfaOtp.mockResolvedValue({ success: true });
render(<MfaOtpForm {...defaultProps} />);
const input = screen.getByPlaceholderText('Enter TOTP');
const button = screen.getByRole('button', { name: 'Verify' });
await user.type(input, '123456');
await user.click(button);
expect(mockSendMfaOtp).toHaveBeenCalledWith('123456');
});
it('submits form when pressing Enter key with valid code', async () => {
mockSendMfaOtp.mockResolvedValue({ success: true });
render(<MfaOtpForm {...defaultProps} />);
const input = screen.getByPlaceholderText('Enter TOTP');
await user.type(input, '123456');
await user.type(input, '{Enter}');
expect(mockSendMfaOtp).toHaveBeenCalledWith('123456');
expect(mockSendMfaOtp).toHaveBeenCalledTimes(1);
});
it('does not submit when pressing Enter with invalid code length', async () => {
render(<MfaOtpForm {...defaultProps} />);
const input = screen.getByPlaceholderText('Enter TOTP');
await user.type(input, '12345');
await user.type(input, '{Enter}');
expect(mockSendMfaOtp).not.toHaveBeenCalled();
});
it('does not submit when pressing Enter while loading', async () => {
render(<MfaOtpForm {...defaultProps} loading />);
const input = screen.getByPlaceholderText('Enter TOTP');
await user.type(input, '123456');
await user.type(input, '{Enter}');
expect(mockSendMfaOtp).not.toHaveBeenCalled();
});
it('does not submit multiple times when pressing Enter while submitting', async () => {
let resolvePromise: (value: any) => void;
const promise = new Promise((resolve) => {
resolvePromise = resolve;
});
mockSendMfaOtp.mockReturnValue(promise);
render(<MfaOtpForm {...defaultProps} />);
const input = screen.getByPlaceholderText('Enter TOTP');
await user.type(input, '123456');
await user.type(input, '{Enter}');
await user.type(input, '{Enter}'); // Second Enter should be ignored
expect(mockSendMfaOtp).toHaveBeenCalledTimes(1);
// Clean up
resolvePromise!({ success: true });
await waitFor(async () => {
await promise;
});
});
});
describe('Edge Cases', () => {
it('handles null error message gracefully', async () => {
mockSendMfaOtp.mockRejectedValueOnce({ message: null });
render(<MfaOtpForm {...defaultProps} />);
const input = screen.getByPlaceholderText('Enter TOTP');
const button = screen.getByRole('button', { name: 'Verify' });
await user.type(input, '123456');
await user.click(button);
await waitFor(() => {
expect(mocks.toastError).toHaveBeenCalledWith(
'An error occurred. Please try again.',
{},
);
});
});
it('prevents multiple rapid submissions', async () => {
let resolvePromise: (value: any) => void;
const promise = new Promise((resolve) => {
resolvePromise = resolve;
});
mockSendMfaOtp.mockReturnValue(promise);
render(<MfaOtpForm {...defaultProps} />);
const input = screen.getByPlaceholderText('Enter TOTP');
const button = screen.getByRole('button', { name: 'Verify' });
await user.type(input, '123456');
// Rapid clicks
await user.click(button);
await user.click(button);
await user.click(button);
expect(mockSendMfaOtp).toHaveBeenCalledTimes(1);
// Clean up
resolvePromise!({ success: true });
await waitFor(async () => {
await promise;
});
});
it('handles empty input correctly', async () => {
render(<MfaOtpForm {...defaultProps} />);
const button = screen.getByRole('button', { name: 'Verify' });
await user.click(button);
expect(mockSendMfaOtp).not.toHaveBeenCalled();
expect(button).toBeDisabled();
});
});
});

View File

@@ -0,0 +1,87 @@
import { Button } from '@/components/ui/v3/button';
import { Input } from '@/components/ui/v3/input';
import { getToastStyleProps } from '@/utils/constants/settings';
import {
useEffect,
useRef,
useState,
type ChangeEvent,
type KeyboardEvent,
} from 'react';
import toast from 'react-hot-toast';
interface Props {
sendMfaOtp: (code: string) => Promise<any>;
loading: boolean;
requestNewMfaTicket?: () => Promise<void>;
}
function MfaOtpForm({ sendMfaOtp, loading, requestNewMfaTicket }: Props) {
const [otpValue, setOtpValue] = useState<string>('');
const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
const inputRef = useRef<HTMLInputElement | null>(null);
const isMfaTicketInvalid = useRef(false);
useEffect(() => {
if (inputRef.current) {
inputRef.current.focus();
}
}, []);
async function submitTOTP() {
if (otpValue.length === 6 && !isSubmitting) {
try {
setIsSubmitting(true);
if (requestNewMfaTicket && isMfaTicketInvalid.current) {
await requestNewMfaTicket();
}
await sendMfaOtp(otpValue);
} catch (error) {
isMfaTicketInvalid.current = true;
toast.error(
error?.message || 'An error occurred. Please try again.',
getToastStyleProps(),
);
setTimeout(() => {
inputRef.current?.focus();
}, 10);
} finally {
setIsSubmitting(false);
}
}
}
async function handleChange(event: ChangeEvent<HTMLInputElement>) {
const code = event.target.value.replace(/[^0-9]/g, '');
setOtpValue(code);
}
async function handleKeyDown(event: KeyboardEvent<HTMLInputElement>) {
if (event.key === 'Enter') {
submitTOTP();
}
}
const isInputDisabled = loading || isSubmitting;
const isButtonDisabled = isInputDisabled || otpValue.length !== 6;
return (
<div className="relative grid w-full grid-flow-row gap-4 bg-transparent">
<Input
ref={inputRef}
value={otpValue}
placeholder="Enter TOTP"
className="!bg-transparent"
disabled={isInputDisabled}
onChange={handleChange}
onKeyDown={handleKeyDown}
/>
<Button disabled={isButtonDisabled} onClick={submitTOTP}>
{loading ? 'Verifying...' : 'Verify'}
</Button>
</div>
);
}
export default MfaOtpForm;

View File

@@ -0,0 +1 @@
export { default as MfaOtpForm } from './MfaOtpForm';

View File

@@ -7,7 +7,6 @@ import { List } from '@/components/ui/v2/List';
import { ListItem } from '@/components/ui/v2/ListItem';
import { Text } from '@/components/ui/v2/Text';
import { useOrgs } from '@/features/orgs/projects/hooks/useOrgs';
import {} from '@/utils/__generated__/graphql';
import { Divider } from '@mui/material';
import debounce from 'lodash.debounce';
import Image from 'next/image';

View File

@@ -3,7 +3,7 @@ import { Box } from '@/components/ui/v2/Box';
import { ArrowSquareOutIcon } from '@/components/ui/v2/icons/ArrowSquareOutIcon';
import { Link } from '@/components/ui/v2/Link';
import { Text } from '@/components/ui/v2/Text';
import { TransferProjectDialog } from '@/features/orgs/components/common/TransferProjectDialog';
import { TransferOrUpgradeProjectDialog } from '@/features/orgs/components/common/TransferOrUpgradeProjectDialog';
import { useState } from 'react';
import { OpenTransferDialogButton } from '@/components/common/OpenTransferDialogButton';
@@ -51,7 +51,7 @@ export default function UpgradeToProBanner({
<div className="flex flex-col gap-2 space-y-2 lg:flex-row lg:items-center lg:space-x-2 lg:space-y-0">
<OpenTransferDialogButton onClick={handleTransferDialogOpen} />
<TransferProjectDialog
<TransferOrUpgradeProjectDialog
open={transferProjectDialogOpen}
setOpen={setTransferProjectDialogOpen}
/>

View File

@@ -8,7 +8,7 @@ import type { ForwardedRef } from 'react';
import { forwardRef } from 'react';
import type { FieldValues, UseControllerProps } from 'react-hook-form';
import { useController, useFormContext } from 'react-hook-form';
import mergeRefs from 'react-merge-refs';
import { mergeRefs } from 'react-merge-refs';
export interface ControlledAutocompleteProps<
TOption extends AutocompleteOption = AutocompleteOption,

View File

@@ -5,7 +5,7 @@ import type { ForwardedRef } from 'react';
import { forwardRef } from 'react';
import type { FieldValues, UseControllerProps } from 'react-hook-form';
import { useController, useFormContext } from 'react-hook-form';
import mergeRefs from 'react-merge-refs';
import { mergeRefs } from 'react-merge-refs';
export interface ControlledCheckboxProps<TFieldValues extends FieldValues = any>
extends CheckboxProps {
@@ -38,7 +38,7 @@ function ControlledCheckbox(
uncheckWhenDisabled,
...props
}: ControlledCheckboxProps,
ref: ForwardedRef<HTMLInputElement>,
ref: ForwardedRef<HTMLButtonElement>,
) {
const { setValue } = useFormContext();
const { field } = useController({

View File

@@ -4,7 +4,7 @@ import type { ForwardedRef } from 'react';
import { forwardRef } from 'react';
import type { FieldValues, UseControllerProps } from 'react-hook-form';
import { useController, useFormContext } from 'react-hook-form';
import mergeRefs from 'react-merge-refs';
import { mergeRefs } from 'react-merge-refs';
export interface ControlledSelectProps<TFieldValues extends FieldValues = any>
extends SelectProps<TFieldValues> {
@@ -24,7 +24,7 @@ export interface ControlledSelectProps<TFieldValues extends FieldValues = any>
function ControlledSelect(
{ controllerProps, name, control, ...props }: ControlledSelectProps,
ref: ForwardedRef<HTMLInputElement>,
ref: ForwardedRef<HTMLButtonElement>,
) {
const { setValue } = useFormContext();
const { field } = useController({

View File

@@ -2,13 +2,13 @@ import type { SwitchProps } from '@/components/ui/v2/Switch';
import { Switch } from '@/components/ui/v2/Switch';
import type { ForwardedRef } from 'react';
import { forwardRef } from 'react';
import { useController, useFormContext } from 'react-hook-form';
import type {
ControllerProps,
FieldValues,
UseControllerProps,
} from 'react-hook-form/dist/types';
import mergeRefs from 'react-merge-refs';
} from 'react-hook-form';
import { useController, useFormContext } from 'react-hook-form';
import { mergeRefs } from 'react-merge-refs';
export interface ControlledSwitchProps<TFieldValues extends FieldValues = any>
extends SwitchProps {

View File

@@ -0,0 +1,59 @@
import {
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@/components/ui/v3/form';
import { Input } from '@/components/ui/v3/input';
import type { Control, FieldPath, FieldValues } from 'react-hook-form';
const inputClasses =
'!bg-transparent aria-[invalid=true]:border-red-500 aria-[invalid=true]:focus:border-red-500 aria-[invalid=true]:focus:ring-red-500';
interface FormInputProps<
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
> {
control: Control<TFieldValues>;
name: TName;
label: string;
placeholder?: string;
className?: string;
type?: string;
}
function FormInput<
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
>({
control,
name,
label,
placeholder,
className = '',
type = 'text',
}: FormInputProps<TFieldValues, TName>) {
return (
<FormField
control={control}
name={name}
render={({ field }) => (
<FormItem>
<FormLabel>{label}</FormLabel>
<FormControl>
<Input
type={type}
placeholder={placeholder || label}
{...field}
className={`${inputClasses} ${className}`}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
);
}
export default FormInput;

View File

@@ -0,0 +1 @@
export { default as FormInput } from './FormInput';

View File

@@ -6,19 +6,24 @@ import { Button } from '@/components/ui/v2/Button';
import { Divider } from '@/components/ui/v2/Divider';
import { Dropdown, useDropdown } from '@/components/ui/v2/Dropdown';
import { Text } from '@/components/ui/v2/Text';
import { useUserData } from '@/hooks/useUserData';
import { useAuth } from '@/providers/Auth';
import { useApolloClient } from '@apollo/client';
import { useSignOut, useUserData } from '@nhost/nextjs';
import getConfig from 'next/config';
import { useRouter } from 'next/router';
function AccountMenuContent() {
const user = useUserData();
const { signOut } = useSignOut();
const router = useRouter();
const { signout } = useAuth();
const apolloClient = useApolloClient();
const { handleClose } = useDropdown();
const { publicRuntimeConfig } = getConfig();
async function handleSignOut() {
handleClose();
await apolloClient.clearStore();
await signout();
}
return (
<Box className="grid grid-flow-row">
<Box className="grid grid-flow-col items-center justify-start gap-3 p-4">
@@ -70,12 +75,7 @@ function AccountMenuContent() {
color="error"
variant="borderless"
className="w-full justify-start"
onClick={async () => {
handleClose();
await apolloClient.clearStore();
await signOut();
await router.push('/signin');
}}
onClick={handleSignOut}
>
Sign out
</Button>

View File

@@ -2,22 +2,23 @@ import type { BaseLayoutProps } from '@/components/layout/BaseLayout';
import { BaseLayout } from '@/components/layout/BaseLayout';
import { Container } from '@/components/layout/Container';
import { Header } from '@/components/layout/Header';
import { MainNav } from '@/components/layout/MainNav';
import { useTreeNavState } from '@/components/layout/MainNav/TreeNavStateContext';
import { HighlightedText } from '@/components/presentational/HighlightedText';
import { RetryableErrorBoundary } from '@/components/presentational/RetryableErrorBoundary';
import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
import { Link } from '@/components/ui/v2/Link';
import { Text } from '@/components/ui/v2/Text';
import { useIsPlatform } from '@/features/orgs/projects/common/hooks/useIsPlatform';
import { useAuthenticationStatus } from '@nhost/nextjs';
import Analytics from '@/components/analytics/analytics';
import { useMediaQuery } from '@/components/common/useMediaQuery';
import { MainNav } from '@/components/layout/MainNav';
import PinnedMainNav from '@/components/layout/MainNav/PinnedMainNav';
import { useTreeNavState } from '@/components/layout/MainNav/TreeNavStateContext';
import { RetryableErrorBoundary } from '@/components/presentational/RetryableErrorBoundary';
import { OrgStatus } from '@/features/orgs/components/OrgStatus';
import { useIsHealthy } from '@/features/orgs/projects/common/hooks/useIsHealthy';
import { useNotFoundRedirect } from '@/features/orgs/projects/common/hooks/useNotFoundRedirect';
import { cn } from '@/lib/utils';
import { useAuth } from '@/providers/Auth';
import Image from 'next/image';
import { useRouter } from 'next/router';
import { useEffect, useState } from 'react';
@@ -32,7 +33,7 @@ export default function AuthenticatedLayout({
const isPlatform = useIsPlatform();
const isMdOrLarger = useMediaQuery('md');
const { isAuthenticated, isLoading } = useAuthenticationStatus();
const { isAuthenticated, isLoading } = useAuth();
const isHealthy = useIsHealthy();
const [mainNavContainer, setMainNavContainer] = useState(null);
const { mainNavPinned } = useTreeNavState();
@@ -43,7 +44,6 @@ export default function AuthenticatedLayout({
if (!isPlatform || isLoading || isAuthenticated) {
return;
}
router.push('/signin');
}, [isLoading, isAuthenticated, router, isPlatform]);
@@ -65,11 +65,15 @@ export default function AuthenticatedLayout({
if (isPlatform && isLoading) {
return (
<BaseLayout className="h-full" {...props}>
<Header className="flex max-h-[59px] flex-auto" />
<Header className="flex max-h-[59px] flex-auto py-1" />
</BaseLayout>
);
}
// if (isPlatform && !isLoading && !isAuthenticated) {
// return null;
// }
if (!isPlatform && !isHealthy) {
return (
<BaseLayout className="h-full" {...props}>
@@ -142,6 +146,7 @@ export default function AuthenticatedLayout({
>
<div className="flex h-full w-full flex-col overflow-auto">
<OrgStatus />
<Analytics />
{children}
</div>
</RetryableErrorBoundary>

View File

@@ -41,7 +41,7 @@ export default function MainNav({ container }: MainNavProps) {
return (
<Sheet open={open} onOpenChange={setOpen}>
<div
className="min- absolute left-0 z-50 flex h-full w-6 justify-center border-r-[1px] bg-background pt-1 hover:bg-accent"
className="min- absolute left-0 z-[39] flex h-full w-6 justify-center border-r-[1px] bg-background pt-1 hover:bg-accent"
onMouseEnter={() => setOpen(true)}
>
<Menu className="h-4 w-4" />

View File

@@ -10,8 +10,8 @@ import { List } from '@/components/ui/v2/List';
import { ListItem } from '@/components/ui/v2/ListItem';
import { Text } from '@/components/ui/v2/Text';
import { useIsPlatform } from '@/features/orgs/projects/common/hooks/useIsPlatform';
import { useAuth } from '@/providers/Auth';
import { useApolloClient } from '@apollo/client';
import { useSignOut } from '@nhost/nextjs';
import getConfig from 'next/config';
import { useRouter } from 'next/router';
import { useState } from 'react';
@@ -22,11 +22,18 @@ export interface MobileNavProps extends ButtonProps {}
export default function MobileNav({ className, ...props }: MobileNavProps) {
const isPlatform = useIsPlatform();
const [menuOpen, setMenuOpen] = useState(false);
const { signOut } = useSignOut();
const { signout } = useAuth();
const apolloClient = useApolloClient();
const router = useRouter();
const { publicRuntimeConfig } = getConfig();
async function handleSignOut() {
setMenuOpen(false);
await apolloClient.clearStore();
await signout();
await router.push('/signin');
}
return (
<>
<Button
@@ -120,12 +127,7 @@ export default function MobileNav({ className, ...props }: MobileNavProps) {
variant="borderless"
sx={{ color: 'error.main' }}
className="justify-start border-none px-2 py-2.5 text-[16px]"
onClick={async () => {
setMenuOpen(false);
await apolloClient.clearStore();
await signOut();
await router.push('/signin');
}}
onClick={handleSignOut}
>
Sign Out
</ListItem.Button>

View File

@@ -6,8 +6,8 @@ import { RetryableErrorBoundary } from '@/components/presentational/RetryableErr
import { Box } from '@/components/ui/v2/Box';
import { ThemeProvider } from '@/components/ui/v2/ThemeProvider';
import { useIsPlatform } from '@/features/orgs/projects/common/hooks/useIsPlatform';
import { useAuth } from '@/providers/Auth';
import GlobalStyles from '@mui/material/GlobalStyles';
import { useAuthenticationStatus } from '@nhost/nextjs';
import Image from 'next/image';
import { useRouter } from 'next/router';
import { useEffect } from 'react';
@@ -20,7 +20,7 @@ export default function UnauthenticatedLayout({
}: UnauthenticatedLayoutProps) {
const router = useRouter();
const isPlatform = useIsPlatform();
const { isAuthenticated, isLoading } = useAuthenticationStatus();
const { isAuthenticated, isLoading } = useAuth();
const isOnResetPassword = router.route === '/password/reset';
useEffect(() => {

View File

@@ -6,8 +6,8 @@ import {
type ReactElement,
} from 'react';
import { CopyToClipboardButton as CopyToClipboardButtonOriginal } from '@/components/presentational/CopyToClipboardButton';
import { Box } from '@/components/ui/v2/Box';
import { CopyToClipboardButton as CopyToClipboardButtonOriginal } from './CopyToClipboardButton';
import { getNodeText } from './getNodeText';
export interface CodeBlockPropsBase {

View File

@@ -1,14 +1,11 @@
import { Button, type ButtonProps } from '@/components/ui/v3/button';
import { isNotEmptyValue } from '@/lib/utils';
import { copy } from '@/utils/copy';
import { clsx } from 'clsx';
import { Copy } from 'lucide-react';
import { useEffect, useState } from 'react';
import {
IconButton,
type IconButtonProps,
} from '@/components/ui/v2/IconButton';
import { CopyIcon } from '@/components/ui/v2/icons/CopyIcon';
import { copy } from '@/utils/copy';
export function CopyToClipboardButton({
function CopyToClipboardButton({
textToCopy,
className,
title,
@@ -16,7 +13,7 @@ export function CopyToClipboardButton({
}: {
textToCopy: string;
title: string;
} & IconButtonProps) {
} & ButtonProps) {
const [disabled, setDisabled] = useState(true);
useEffect(() => {
@@ -35,12 +32,18 @@ export function CopyToClipboardButton({
if (!textToCopy || disabled) {
return null;
}
const hasChildren = isNotEmptyValue(props.children);
return (
<IconButton
variant="borderless"
color="secondary"
className={clsx('group', className)}
<Button
type="button"
size="icon"
variant="outline"
className={clsx(
'group h-fit w-fit border-0 bg-transparent p-[2px] hover:bg-[#d6eefb] dark:hover:bg-[#1e2942]',
className,
{ 'gap-3': hasChildren },
)}
onClick={(event) => {
event.stopPropagation();
@@ -49,7 +52,9 @@ export function CopyToClipboardButton({
aria-label={textToCopy}
{...props}
>
<CopyIcon className="top-5 h-4 w-4" />
</IconButton>
{props.children}
<Copy className="top-5 h-4 w-4" />
</Button>
);
}
export default CopyToClipboardButton;

View File

@@ -0,0 +1 @@
export { default as CopyToClipboardButton } from './CopyToClipboardButton';

View File

@@ -92,7 +92,7 @@ function Checkbox(
'aria-label': ariaLabel,
...props
}: CheckboxProps,
ref: ForwardedRef<HTMLInputElement>,
ref: ForwardedRef<HTMLButtonElement>,
) {
if (!label) {
return (

View File

@@ -3,10 +3,10 @@ import { ChevronUpIcon } from '@/components/ui/v2/icons/ChevronUpIcon';
import { CopyIcon } from '@/components/ui/v2/icons/CopyIcon';
import { XIcon } from '@/components/ui/v2/icons/XIcon';
import { useProject } from '@/features/orgs/projects/hooks/useProject';
import { useUserData } from '@/hooks/useUserData';
import { getToastBackgroundColor } from '@/utils/constants/settings';
import { copy } from '@/utils/copy';
import type { ApolloError } from '@apollo/client';
import { useUserData } from '@nhost/nextjs';
import { AnimatePresence, motion } from 'framer-motion';
import { useRouter } from 'next/router';
import { useState } from 'react';

View File

@@ -5,7 +5,7 @@ import type { InputBaseProps as MaterialInputBaseProps } from '@mui/material/Inp
import MaterialInputBase, { inputBaseClasses } from '@mui/material/InputBase';
import type { DetailedHTMLProps, ForwardedRef, HTMLProps } from 'react';
import { forwardRef } from 'react';
import mergeRefs from 'react-merge-refs';
import { mergeRefs } from 'react-merge-refs';
export interface InputProps
extends Omit<MaterialInputBaseProps, 'componentsProps' | 'slotProps'>,

View File

@@ -57,7 +57,7 @@ const StyledRadio = styled(MaterialRadio)(({ theme }) => ({
function Radio(
{ label, value, slotProps, ...props }: RadioProps,
ref: ForwardedRef<HTMLInputElement>,
ref: ForwardedRef<HTMLButtonElement>,
) {
return (
<StyledFormControlLabel

View File

@@ -0,0 +1,44 @@
import { Badge } from '@/components/ui/v3/badge';
import useMfaEnabled from '@/features/account/settings/components/AccountMfaSettings/hooks/useMfaEnabled';
import DisableMfaButton from './DisableMfaButton/DisableMfaButton';
import EnableMfaButton from './EnableMfaButton/EnableMfaButton';
function MFaEnabledBadge() {
return (
<Badge variant="outline" className="border-green-400 text-green-400">
Enabled
</Badge>
);
}
function MFaDisabledBadge() {
return (
<Badge
variant="outline"
className="text- border-destructive text-destructive"
>
Disabled
</Badge>
);
}
function AccountMfaSettings() {
const { isMfaEnabled } = useMfaEnabled();
return (
<div className="rounded-lg border border-[#EAEDF0] bg-white font-['Inter_var'] dark:border-[#2F363D] dark:bg-paper">
<div className="flex w-full flex-col items-start gap-6 p-4">
<div className="flex w-full items-center justify-between">
<h3 className="flex items-center text-[1.125rem] font-semibold leading-[1.75]">
<span className="mr-4">Multi-Factor Authentication </span>
{isMfaEnabled ? <MFaEnabledBadge /> : <MFaDisabledBadge />}
</h3>
</div>
</div>
<div className="flex w-full items-center border-t border-[#EAEDF0] px-4 py-2 dark:border-[#2F363D]">
{isMfaEnabled ? <DisableMfaButton /> : <EnableMfaButton />}
</div>
</div>
);
}
export default AccountMfaSettings;

View File

@@ -0,0 +1,69 @@
import { MfaOtpForm } from '@/components/common/MfaOtpForm';
import { Button } from '@/components/ui/v3/button';
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@/components/ui/v3/dialog';
import useMfaEnabled from '@/features/account/settings/components/AccountMfaSettings/hooks/useMfaEnabled';
import { useNhostClient } from '@/providers/nhost';
import { getToastStyleProps } from '@/utils/constants/settings';
import { useState } from 'react';
import { toast } from 'react-hot-toast';
function DisableMfaButton() {
const nhost = useNhostClient();
const [open, setOpen] = useState(false);
const [isDisabling, setIsDisabling] = useState(false);
const { loading, refetch } = useMfaEnabled();
const buttonDisabled = loading || isDisabling;
async function onSendMfaOtp(code: string) {
try {
setIsDisabling(true);
await nhost.auth.verifyChangeUserMfa({
code,
activeMfaType: '',
});
toast.success(
'Multi-factor authentication has been disabled.',
getToastStyleProps(),
);
await refetch();
setOpen(false);
return true;
} finally {
setIsDisabling(false);
}
}
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button
variant="outline"
disabled={buttonDisabled}
className="p-y[0.375rem] h-9 gap-2 border-destructive px-2 text-destructive hover:bg-destructive"
>
Disable multi-factor authentication
</Button>
</DialogTrigger>
<DialogContent className="z-[9999] max-w-[28rem] text-foreground">
<DialogHeader>
<DialogTitle>Disable multi-factor authentication</DialogTitle>
</DialogHeader>
<DialogDescription className="hidden">
Disable multi-factor authentication
</DialogDescription>
<MfaOtpForm loading={isDisabling} sendMfaOtp={onSendMfaOtp} />
</DialogContent>
</Dialog>
);
}
export default DisableMfaButton;

View File

@@ -0,0 +1 @@
export { default as DisableMfaButton } from './DisableMfaButton';

View File

@@ -0,0 +1,19 @@
import { CopyToClipboardButton } from '@/components/presentational/CopyToClipboardButton';
interface Props {
totpSecret: string | null;
}
function CopyMfaTOTPSecret({ totpSecret }: Props) {
return (
<CopyToClipboardButton
className="p-2"
textToCopy={totpSecret}
title="TOTP secret"
>
OR Copy TOTP secret
</CopyToClipboardButton>
);
}
export default CopyMfaTOTPSecret;

View File

@@ -0,0 +1,48 @@
import { Button } from '@/components/ui/v3/button';
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@/components/ui/v3/dialog';
import useMfaEnabled from '@/features/account/settings/components/AccountMfaSettings/hooks/useMfaEnabled';
import { useState } from 'react';
import MfaQRCodeAndTOTPSecret from './MfaQRCodeAndTOTPSecret';
function EnableMfaButton() {
const [open, setOpen] = useState(false);
const { isMfaEnabled, loading, refetch } = useMfaEnabled();
const buttonDisabled = loading || isMfaEnabled;
async function handleOnSuccess() {
setOpen(false);
await refetch();
}
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button
variant="outline"
disabled={buttonDisabled}
className="p-y[0.375rem] h-9 gap-2 border-green-600 px-2 text-green-600 hover:bg-destructive hover:bg-green-600"
>
Enable multi-factor authentication
</Button>
</DialogTrigger>
<DialogContent className="z-[9999] max-w-[28rem] text-foreground">
<DialogHeader>
<DialogTitle>Enable multi-factor authentication</DialogTitle>
</DialogHeader>
<DialogDescription className="hidden">
Enable multi-factor authentication
</DialogDescription>
<MfaQRCodeAndTOTPSecret onSuccess={handleOnSuccess} />
</DialogContent>
</Dialog>
);
}
export default EnableMfaButton;

View File

@@ -0,0 +1,75 @@
/* eslint-disable @next/next/no-img-element */
import { MfaOtpForm } from '@/components/common/MfaOtpForm';
import { Spinner } from '@/components/ui/v3/spinner';
import { useNhostClient } from '@/providers/nhost';
import { getToastStyleProps } from '@/utils/constants/settings';
import { useEffect, useState } from 'react';
import { toast } from 'react-hot-toast';
import CopyMfaTOTPSecret from './CopyMfaTOTPSecret';
interface Props {
onSuccess: () => void;
}
function MfaQRCodeAndTOTPSecret({ onSuccess }: Props) {
const [qrCodeDataUrl, setQrCodeDataUrl] = useState<string | undefined>();
const [totpSecret, setTotpSecret] = useState<string | undefined>();
const [isGenerating, setIsGenerating] = useState(false);
const [isActivating, setIsActivating] = useState(false);
const nhost = useNhostClient();
async function onSendMfaOtp(code: string) {
try {
setIsActivating(true);
await nhost.auth.verifyChangeUserMfa({
code,
activeMfaType: 'totp',
});
toast.success(
'Multi-factor authentication has been enabled.',
getToastStyleProps(),
);
onSuccess();
return true;
} finally {
setIsActivating(false);
}
}
useEffect(() => {
async function generate() {
try {
setIsGenerating(true);
const response = await nhost.auth.changeUserMfa();
setQrCodeDataUrl(response.body.imageUrl);
setTotpSecret(response.body.totpSecret);
} catch (error) {
toast.error(error?.message, getToastStyleProps());
} finally {
setIsGenerating(false);
}
}
generate();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<div className="flex w-full flex-col items-center justify-center gap-4">
{isGenerating && <Spinner />}
{qrCodeDataUrl && (
<>
<div className="flex flex-col justify-center gap-4">
<p className="text-base">
Scan the QR Code with your authenticator app
</p>
<img alt="qrcode" src={qrCodeDataUrl} className="mx-auto w-64" />
</div>
<CopyMfaTOTPSecret totpSecret={totpSecret} />
<MfaOtpForm loading={isActivating} sendMfaOtp={onSendMfaOtp} />
</>
)}
</div>
);
}
export default MfaQRCodeAndTOTPSecret;

View File

@@ -0,0 +1 @@
export { default as EnableMfaButton } from './EnableMfaButton';

View File

@@ -0,0 +1,17 @@
import { useUserData } from '@/hooks/useUserData';
import { isNotEmptyValue } from '@/lib/utils';
import { useGetActiveMfaTypeQuery } from '@/utils/__generated__/graphql';
function useMfaEnabled() {
const userData = useUserData();
const { data, loading, refetch } = useGetActiveMfaTypeQuery({
variables: { id: userData?.id },
fetchPolicy: 'cache-first',
});
const isMfaEnabled = isNotEmptyValue(data?.user.activeMfaType);
return { loading, isMfaEnabled, refetch };
}
export default useMfaEnabled;

View File

@@ -0,0 +1 @@
export { default as AccountMfaSettings } from './components/AccountMfaSettings';

View File

@@ -10,17 +10,16 @@ import { CopyIcon } from '@/components/ui/v2/icons/CopyIcon';
import { Input } from '@/components/ui/v2/Input';
import { Option } from '@/components/ui/v2/Option';
import { Text } from '@/components/ui/v2/Text';
import useActionWithElevatedPermissions from '@/features/account/settings/hooks/useActionWithElevatedPermissions';
import { useNhostClient } from '@/providers/nhost';
import type { DialogFormProps } from '@/types/common';
import { GetPersonalAccessTokensDocument } from '@/utils/__generated__/graphql';
import { getToastStyleProps } from '@/utils/constants/settings';
import { copy } from '@/utils/copy';
import { getDateComponents } from '@/utils/getDateComponents';
import { useApolloClient } from '@apollo/client';
import { yupResolver } from '@hookform/resolvers/yup';
import { useNhostClient } from '@nhost/nextjs';
import { useEffect, useState } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import { toast } from 'react-hot-toast';
import * as Yup from 'yup';
export const createPATFormValidationSchema = Yup.object({
@@ -84,6 +83,25 @@ export default function CreatePATForm({
resolver: yupResolver(createPATFormValidationSchema),
});
const createPAT = useActionWithElevatedPermissions({
actionFn: async (
expiresAt: string,
metadata?: Record<string, string | number>,
) => {
const result = await nhostClient.auth.createPAT({ expiresAt, metadata });
return result;
},
successMessage: 'The personal access token has been created successfully.',
onSuccess: ({ body }) => {
setPersonalAccessToken(body.personalAccessToken);
apolloClient.refetchQueries({
include: [GetPersonalAccessTokensDocument],
});
form.reset();
},
});
const { register, formState } = form;
const isDirty = Object.keys(formState.dirtyFields).length > 0;
@@ -93,44 +111,12 @@ export default function CreatePATForm({
}, [isDirty, location, onDirtyStateChange]);
async function handleSubmit(formValues: CreatePATFormValues) {
try {
const { error, data } = await nhostClient.auth.createPAT(
new Date(formValues.expiresAt),
{
name: formValues.name,
application: 'dashboard',
userAgent: window.navigator.userAgent,
},
);
const toastStyle = getToastStyleProps();
if (error) {
toast.error(error.message, {
style: toastStyle.style,
...toastStyle.error,
});
return;
}
toast.success(
'The personal access token has been created successfully.',
{
style: toastStyle.style,
...toastStyle.success,
},
);
setPersonalAccessToken(data?.personalAccessToken);
apolloClient.refetchQueries({
include: [GetPersonalAccessTokensDocument],
});
form.reset();
} catch {
// Note: This error is handled by the toast.
}
const expiresAt = new Date(formValues.expiresAt).toISOString();
await createPAT(expiresAt, {
name: formValues.name,
application: 'dashboard',
userAgent: window.navigator.userAgent,
});
}
if (personalAccessToken) {

View File

@@ -5,9 +5,9 @@ import { Button } from '@/components/ui/v2/Button';
import { Checkbox } from '@/components/ui/v2/Checkbox';
import { Text } from '@/components/ui/v2/Text';
import { execPromiseWithErrorToast } from '@/features/orgs/utils/execPromiseWithErrorToast';
import { useUserData } from '@/hooks/useUserData';
import { useAuth } from '@/providers/Auth';
import { useDeleteUserAccountMutation } from '@/utils/__generated__/graphql';
import { useSignOut, useUserData } from '@nhost/nextjs';
import { useRouter } from 'next/router';
import { useState } from 'react';
import { twMerge } from 'tailwind-merge';
@@ -82,14 +82,12 @@ function ConfirmDeleteAccountModal({
}
export default function DeleteAccount() {
const router = useRouter();
const { signOut } = useSignOut();
const { signout } = useAuth();
const { openDialog, closeDialog } = useDialog();
const onDelete = async () => {
await signOut();
await router.push('/signin');
await signout();
};
const confirmDeleteAccount = async () => {

View File

@@ -2,9 +2,9 @@ import { Form } from '@/components/form/Form';
import { SettingsContainer } from '@/components/layout/SettingsContainer';
import { Input } from '@/components/ui/v2/Input';
import { execPromiseWithErrorToast } from '@/features/orgs/utils/execPromiseWithErrorToast';
import { useUserData } from '@/hooks/useUserData';
import { useUpdateUserDisplayNameMutation } from '@/utils/__generated__/graphql';
import { yupResolver } from '@hookform/resolvers/yup';
import { useUserData } from '@nhost/nextjs';
import { FormProvider, useForm } from 'react-hook-form';
import * as Yup from 'yup';
@@ -19,7 +19,9 @@ export type DisplayNameSettingFormValues = Yup.InferType<
>;
export default function DisplayNameSetting() {
const { id: userID, displayName } = useUserData();
const user = useUserData();
const { id: userID, displayName } = user || {};
const [updateUserDisplayName] = useUpdateUserDisplayNameMutation();

View File

@@ -1,9 +1,10 @@
import { Form } from '@/components/form/Form';
import { SettingsContainer } from '@/components/layout/SettingsContainer';
import { Input } from '@/components/ui/v2/Input';
import { execPromiseWithErrorToast } from '@/features/orgs/utils/execPromiseWithErrorToast';
import useActionWithElevatedPermissions from '@/features/account/settings/hooks/useActionWithElevatedPermissions';
import { useUserData } from '@/hooks/useUserData';
import { useNhostClient } from '@/providers/nhost';
import { yupResolver } from '@hookform/resolvers/yup';
import { useNhostClient, useUserData } from '@nhost/nextjs';
import { FormProvider, useForm } from 'react-hook-form';
import * as Yup from 'yup';
@@ -15,36 +16,34 @@ export type EmailSettingFormValues = Yup.InferType<typeof validationSchema>;
export default function EmailSetting() {
const nhost = useNhostClient();
const { email } = useUserData();
const user = useUserData();
const form = useForm<EmailSettingFormValues>({
reValidateMode: 'onSubmit',
defaultValues: { email },
defaultValues: { email: user?.email },
resolver: yupResolver(validationSchema),
});
const { register, formState } = form;
const isDirty = Object.keys(formState.dirtyFields).length > 0;
const changeEmail = useActionWithElevatedPermissions({
actionFn: async (newEmail: string) => {
const result = await nhost.auth.changeUserEmail({
newEmail,
options: {
redirectTo: `${window.location.origin}/account`,
},
});
return result;
},
successMessage:
'Please check your inbox. Follow the link to finalize changing your email.',
onSuccess: () => form.reset(),
});
async function handleSubmit(formValues: EmailSettingFormValues) {
await execPromiseWithErrorToast(
async () => {
await nhost.auth.changeEmail({
newEmail: formValues.email,
options: {
redirectTo: `${window.location.origin}/account`,
},
});
form.reset({ email: formValues.email });
},
{
loadingMessage: 'Updating your email...',
successMessage:
'Please check your inbox. Follow the link to finalize changing your email.',
errorMessage:
'An error occurred while trying to update your email. Please try again.',
},
);
await changeEmail(formValues.email);
}
return (

View File

@@ -1,93 +0,0 @@
import { Form } from '@/components/form/Form';
import { SettingsContainer } from '@/components/layout/SettingsContainer';
import { Input } from '@/components/ui/v2/Input';
import { execPromiseWithErrorToast } from '@/features/orgs/utils/execPromiseWithErrorToast';
import { yupResolver } from '@hookform/resolvers/yup';
import { useChangePassword } from '@nhost/nextjs';
import { FormProvider, useForm } from 'react-hook-form';
import * as Yup from 'yup';
const validationSchema = Yup.object({
newPassword: Yup.string()
.label('New Password')
.nullable()
.required('This field is required.'),
confirmPassword: Yup.string()
.label('Confirm Password')
.nullable()
.required('This field is required.')
.test(
'passwords-match',
'Passwords must match.',
(value, ctx) => ctx.parent.newPassword === value,
),
});
export type PasswordSettingsFormValues = Yup.InferType<typeof validationSchema>;
export default function PasswordSettings() {
const { changePassword } = useChangePassword();
const form = useForm<PasswordSettingsFormValues>({
reValidateMode: 'onSubmit',
resolver: yupResolver(validationSchema),
});
const { register, formState } = form;
const isDirty = Object.keys(formState.dirtyFields).length > 0;
async function handleSubmit(formValues: PasswordSettingsFormValues) {
await execPromiseWithErrorToast(
async () => {
// TODO fix changePassword should throw an error if something happens
await changePassword(formValues.newPassword);
form.reset();
},
{
loadingMessage: 'Changing password...',
successMessage: 'The password has been changed successfully.',
errorMessage:
'An error occurred while trying to update the password. Please try again.',
},
);
}
return (
<FormProvider {...form}>
<Form onSubmit={handleSubmit}>
<SettingsContainer
title="Change Password"
description="Update your account password."
slotProps={{
submitButton: {
disabled: !isDirty,
loading: formState.isSubmitting,
},
}}
className="grid grid-flow-row lg:grid-cols-5"
>
<Input
{...register('newPassword')}
className="col-span-2"
type="password"
id="new-password"
label="New Password"
fullWidth
helperText={formState.errors.newPassword?.message}
error={Boolean(formState.errors.newPassword)}
/>
<Input
{...register('confirmPassword')}
className="col-span-2 row-start-2"
type="password"
id="confirm-password"
label="Confirm Password"
fullWidth
helperText={formState.errors.confirmPassword?.message}
error={Boolean(formState.errors.confirmPassword)}
/>
</SettingsContainer>
</Form>
</FormProvider>
);
}

View File

@@ -0,0 +1,50 @@
import { FormInput } from '@/components/form/FormInput';
import { Button } from '@/components/ui/v3/button';
import { Form } from '@/components/ui/v3/form';
import useChangePasswordForm from '@/features/account/settings/components/PasswordSettings/hooks/useChangePasswordForm';
import useOnChangePasswordHandler from '@/features/account/settings/components/PasswordSettings/hooks/useOnChangePasswordHandler';
export default function PasswordSettings() {
const form = useChangePasswordForm();
const onSubmit = useOnChangePasswordHandler({
onSuccess: () => form.reset(),
});
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<div className="rounded-lg border border-[#EAEDF0] bg-white font-['Inter_var'] dark:border-[#2F363D] dark:bg-paper">
<div className="flex w-full flex-col items-start gap-4 p-4">
<div className="flex w-full flex-col items-start">
<h3 className="text-[1.125rem] font-semibold leading-[1.75]">
Change Password
</h3>
<p className="text-[#556378] dark:text-[#A2B3BE]">
Update your account password.
</p>
</div>
<div className="flex w-[370px] flex-col gap-4">
<FormInput
control={form.control}
name="newPassword"
type="password"
label="New Password"
/>
<FormInput
control={form.control}
name="confirmPassword"
type="password"
label="Confirm Password"
/>
</div>
</div>
<div className="flex w-full items-center justify-end border-t border-[#EAEDF0] px-4 py-2 dark:border-[#2F363D]">
<Button type="submit" variant="outline">
Save
</Button>
</div>
</div>
</form>
</Form>
);
}

View File

@@ -0,0 +1,39 @@
import { zodResolver } from '@hookform/resolvers/zod';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
const validationSchema = z
.object({
newPassword: z
.string({
required_error: 'This field is required.',
})
.min(1, 'This field is required.'),
confirmPassword: z
.string({
required_error: 'This field is required.',
})
.min(1, 'This field is required.'),
})
.refine((data) => data.newPassword === data.confirmPassword, {
message: 'Passwords must match.',
path: ['confirmPassword'],
});
export type ChangePasswordFormValues = z.infer<typeof validationSchema>;
function useChangePasswordForm() {
const form = useForm<ChangePasswordFormValues>({
mode: 'onTouched',
reValidateMode: 'onBlur',
defaultValues: {
newPassword: '',
confirmPassword: '',
},
resolver: zodResolver(validationSchema),
});
return form;
}
export default useChangePasswordForm;

View File

@@ -0,0 +1,27 @@
import useActionWithElevatedPermissions from '@/features/account/settings/hooks/useActionWithElevatedPermissions';
import { useNhostClient } from '@/providers/nhost';
import { type ChangePasswordFormValues } from './useChangePasswordForm';
interface Props {
onSuccess: () => void;
}
function useOnChangePasswordHandler({ onSuccess }: Props) {
const nhost = useNhostClient();
const changePassword = useActionWithElevatedPermissions({
actionFn: nhost.auth.changeUserPassword,
onSuccess,
successMessage: 'The password has been changed successfully.',
});
async function onSubmit(values: ChangePasswordFormValues) {
const { newPassword } = values;
await changePassword({ newPassword });
}
return onSubmit;
}
export default useOnChangePasswordHandler;

View File

@@ -1,2 +1 @@
export * from './PasswordSettings';
export { default as PasswordSettings } from './PasswordSettings';
export { default as PasswordSettings } from './components/PasswordSettings';

View File

@@ -0,0 +1,45 @@
import { Button } from '@/components/ui/v3/button';
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@/components/ui/v3/dialog';
import { Plus } from 'lucide-react';
import { useState } from 'react';
import SecurityKeyForm from './NewSecurityKeyForm';
function AddSecurityKeyButton() {
const [open, setOpen] = useState(false);
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button
variant="ghost"
className="h-9 gap-2 px-2 py-[0.375rem] hover:bg-[#d6eefb] dark:hover:bg-[#1e2942]"
>
<Plus className="h-5 w-5" />
Add New Security Key
</Button>
</DialogTrigger>
<DialogContent
className="sr z-[9999] text-foreground sm:max-w-xl"
aria-describedby="Add a Security Key"
>
<DialogHeader>
<DialogTitle>Add a Security Key</DialogTitle>
</DialogHeader>
<DialogDescription className="sr-only">
Add a Security Key
</DialogDescription>
<SecurityKeyForm onSuccess={() => setOpen(false)} />
</DialogContent>
</Dialog>
);
}
export default AddSecurityKeyButton;

View File

@@ -0,0 +1,56 @@
import { Button } from '@/components/ui/v3/button';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@/components/ui/v3/form';
import { Input } from '@/components/ui/v3/input';
import useNewSecurityKeyForm from '@/features/account/settings/components/SecurityKeysSettings/hooks/useNewSecurityKeyForm';
import useOnAddNewSecurityKeyHandler from '@/features/account/settings/components/SecurityKeysSettings/hooks/useOnAddNewSecurityKeyHandler';
interface Props {
onSuccess: () => void;
}
function NewSecurityKeyForm({ onSuccess }: Props) {
const form = useNewSecurityKeyForm();
const onSubmit = useOnAddNewSecurityKeyHandler({ onSuccess });
return (
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="grid grid-flow-row gap-4 bg-transparent"
>
<FormField
control={form.control}
name="nickname"
render={({ field }) => (
<FormItem>
<FormLabel>Name</FormLabel>
<FormControl>
<Input
placeholder="Name"
{...field}
className="!bg-transparent"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button
type="submit"
variant="outline"
className="w-full !bg-transparent"
>
Add new security key
</Button>
</form>
</Form>
);
}
export default NewSecurityKeyForm;

View File

@@ -0,0 +1,34 @@
import { Button } from '@/components/ui/v3/button';
import useRemoveSecurityKey from '@/features/account/settings/components/SecurityKeysSettings/hooks/useRemoveSecurityKey';
import { execPromiseWithErrorToast } from '@/features/orgs/utils/execPromiseWithErrorToast';
import { Trash } from 'lucide-react';
import { memo } from 'react';
interface Props {
id: string;
}
function RemoveSecurityKeyButton({ id }: Props) {
const removeSecurityKey = useRemoveSecurityKey();
function handleClick() {
execPromiseWithErrorToast(async () => removeSecurityKey(id), {
loadingMessage: 'Removing security key...',
successMessage: 'Security key has been removed successfully.',
errorMessage:
'An error occurred while trying to remove security key. Please try again.',
});
}
return (
<Button
variant="ghost"
onClick={handleClick}
aria-label={`Remove security key ${id}`}
>
<Trash />
</Button>
);
}
export default memo(RemoveSecurityKeyButton);

View File

@@ -0,0 +1,43 @@
import { Spinner } from '@/components/ui/v3/spinner';
import useGetSecurityKeys from '@/features/account/settings/hooks/useGetSecurityKeys';
import { InfoAlert } from '@/features/orgs/components/InfoAlert';
import { Fingerprint } from 'lucide-react';
import { memo } from 'react';
import RemoveSecurityKeyButton from './RemoveSecurityKeyButton';
type SecurityKeyProps = {
id: string;
nickname?: string;
};
function SecurityKey({ id, nickname }: SecurityKeyProps) {
return (
<div className="flex w-full items-center justify-between rounded-lg border border-[#EAEDF0] px-2 py-2 dark:border-[#2F363D]">
<div className="flex justify-start gap-3">
<Fingerprint />
<span>{nickname || id}</span>
</div>
<RemoveSecurityKeyButton id={id} />
</div>
);
}
const MemoizedSecurityKey = memo(SecurityKey);
function SecurityKeyList() {
const { data, loading } = useGetSecurityKeys();
return (
<div>
{loading && <Spinner />}
{!loading && data?.authUserSecurityKeys.length === 0 && (
<InfoAlert>No security keys have been added yet!</InfoAlert>
)}
{data?.authUserSecurityKeys.map(({ id, nickname }) => (
<MemoizedSecurityKey key={id} id={id} nickname={nickname} />
))}
</div>
);
}
export default SecurityKeyList;

View File

@@ -0,0 +1,24 @@
import AddSecurityKeyButton from './AddSecurityKeyButton';
import SecurityKeyList from './SecurityKeyList';
function SecurityKeysSettings() {
return (
<div className="rounded-lg border border-[#EAEDF0] bg-white font-display dark:border-[#2F363D] dark:bg-paper">
<div className="flex w-full flex-col items-start gap-6 p-4">
<div className="flex w-full items-center justify-between">
<h3 className="text-[1.125rem] font-semibold leading-[1.75]">
Manage your security keys
</h3>
</div>
<div className="flex w-full flex-col gap-4">
<SecurityKeyList />
</div>
</div>
<div className="flex w-full items-center border-t border-[#EAEDF0] px-4 py-2 dark:border-[#2F363D]">
<AddSecurityKeyButton />
</div>
</div>
);
}
export default SecurityKeysSettings;

View File

@@ -0,0 +1,25 @@
import { zodResolver } from '@hookform/resolvers/zod';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
const validationSchema = z
.object({
nickname: z.string().min(1, { message: 'Nickname is required' }),
})
.required();
export type NewSecurityKeyFormValues = z.infer<typeof validationSchema>;
function useNewSecurityKeyForm() {
const form = useForm<NewSecurityKeyFormValues>({
reValidateMode: 'onSubmit',
defaultValues: {
nickname: '',
},
resolver: zodResolver(validationSchema),
});
return form;
}
export default useNewSecurityKeyForm;

View File

@@ -0,0 +1,38 @@
import useActionWithElevatedPermissions from '@/features/account/settings/hooks/useActionWithElevatedPermissions';
import useGetSecurityKeys from '@/features/account/settings/hooks/useGetSecurityKeys';
import { useNhostClient } from '@/providers/nhost';
import { startRegistration } from '@simplewebauthn/browser';
import { type NewSecurityKeyFormValues } from './useNewSecurityKeyForm';
interface Props {
onSuccess: () => void;
}
function useOnAddNewSecurityKeyHandler({ onSuccess }: Props) {
const { refetch } = useGetSecurityKeys();
const nhost = useNhostClient();
async function actionFn(nickname: string) {
const webAuthnOptions = await nhost.auth.addSecurityKey();
const credential = await startRegistration(webAuthnOptions.body);
await nhost.auth.verifyAddSecurityKey({ credential, nickname });
}
const addSecurityKey = useActionWithElevatedPermissions({
actionFn,
onSuccess: async () => {
await refetch();
onSuccess();
},
successMessage: 'Security key has been added.',
});
async function onSubmit(values: NewSecurityKeyFormValues) {
const { nickname } = values;
await addSecurityKey(nickname);
}
return onSubmit;
}
export default useOnAddNewSecurityKeyHandler;

View File

@@ -0,0 +1,22 @@
import useElevatedPermissions from '@/features/account/settings/hooks/useElevatedPermissions';
import useGetSecurityKeys from '@/features/account/settings/hooks/useGetSecurityKeys';
import { useRemoveSecurityKeyMutation } from '@/utils/__generated__/graphql';
function useRemoveSecurityKey() {
const [removeSecurityKeyMutation] = useRemoveSecurityKeyMutation();
const elevatePermissions = useElevatedPermissions();
const { refetch: refetchSecurityKeys } = useGetSecurityKeys();
async function removeSecurityKey(id: string) {
const permissionGranted = await elevatePermissions(true);
if (permissionGranted) {
await removeSecurityKeyMutation({ variables: { id } });
await refetchSecurityKeys();
}
}
return removeSecurityKey;
}
export default useRemoveSecurityKey;

View File

@@ -0,0 +1 @@
export { default as SecurityKeysSettings } from './components/SecurityKeysSettings';

View File

@@ -4,20 +4,29 @@ import { Box } from '@/components/ui/v2/Box';
import { Button } from '@/components/ui/v2/Button';
import { GitHubIcon } from '@/components/ui/v2/icons/GitHubIcon';
import { Text } from '@/components/ui/v2/Text';
import { useAccessToken } from '@/hooks/useAccessToken';
import { useNhostClient } from '@/providers/nhost';
import { useGetAuthUserProvidersQuery } from '@/utils/__generated__/graphql';
import { useProviderLink } from '@nhost/nextjs';
import NavLink from 'next/link';
import { useMemo } from 'react';
export default function SocialProvidersSettings() {
const nhost = useNhostClient();
const token = useAccessToken();
const { data, loading, error } = useGetAuthUserProvidersQuery();
const isGithubConnected = data?.authUserProviders?.some(
(item) => item.providerId === 'github',
);
const { github } = useProviderLink({
connect: true,
redirectTo: `${window.location.origin}/account`,
});
const github = useMemo(
() =>
nhost.auth.signInProviderURL('github', {
connect: token,
redirectTo: `${window.location.origin}/account`,
}),
// eslint-disable-next-line react-hooks/exhaustive-deps
[token],
);
if (!data && loading) {
return (

View File

@@ -0,0 +1,5 @@
query getActiveMfaType($id: uuid!) {
user(id: $id) {
activeMfaType
}
}

View File

@@ -0,0 +1,6 @@
query securityKeys($userId: uuid!) {
authUserSecurityKeys(where: { userId: { _eq: $userId } }) {
id
nickname
}
}

View File

@@ -0,0 +1,5 @@
mutation removeSecurityKey($id: uuid!) {
deleteAuthUserSecurityKey(id: $id) {
id
}
}

View File

@@ -0,0 +1,54 @@
import useElevatedPermissions from '@/features/account/settings/hooks/useElevatedPermissions';
import useGetSecurityKeys from '@/features/account/settings/hooks/useGetSecurityKeys';
import { toast } from 'react-hot-toast';
type Action = (...args: any[]) => Promise<any>;
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
interface Props<Fn extends Action> {
actionFn: Fn;
onSuccess?: (result: UnwrapPromise<ReturnType<Fn>>) => void;
onError?: () => void;
successMessage?: string;
}
function useActionWithElevatedPermissions<F extends Action>({
actionFn,
onSuccess,
onError,
successMessage,
}: Props<F>) {
const elevatePermissions = useElevatedPermissions();
const { data } = useGetSecurityKeys();
async function requestPermissions() {
if (data?.authUserSecurityKeys.length === 0) {
return true;
}
const isPermissionsElevated = await elevatePermissions();
return isPermissionsElevated;
}
async function actionWithElevatedPermissions(...args: Parameters<F>) {
let isSuccess = false;
const permissionGranted = await requestPermissions();
if (!permissionGranted) {
return isSuccess;
}
try {
const response = await actionFn(...args);
toast.success(successMessage || 'Success.');
onSuccess?.(response as UnwrapPromise<ReturnType<F>>);
isSuccess = true;
} catch (error) {
toast.error(error?.message || 'Something went wrong.');
onError?.();
}
return isSuccess;
}
return actionWithElevatedPermissions;
}
export default useActionWithElevatedPermissions;

View File

@@ -0,0 +1,37 @@
import { useElevateEmail } from '@/hooks/useElevateEmail';
import { useHasuraClaims } from '@/hooks/useHasuraClaims';
import { useUserData } from '@/hooks/useUserData';
import { getToastStyleProps } from '@/utils/constants/settings';
import { toast } from 'react-hot-toast';
function useElevatedPermissions() {
const user = useUserData();
const elevateEmail = useElevateEmail();
const claims = useHasuraClaims();
async function elevatePermissions(shouldThrowError = false) {
const elevated = user
? claims?.['x-hasura-auth-elevated'] === user?.id
: false;
if (elevated) {
return true;
}
try {
await elevateEmail();
return true;
} catch (e) {
if (shouldThrowError) {
throw e;
} else {
const message = e?.message || 'Could not elevate permissions';
toast.error(message, getToastStyleProps());
return false;
}
}
}
return elevatePermissions;
}
export default useElevatedPermissions;

View File

@@ -0,0 +1,15 @@
import { useUserData } from '@/hooks/useUserData';
import { useSecurityKeysQuery } from '@/utils/__generated__/graphql';
function useGetSecurityKeys() {
const user = useUserData();
const query = useSecurityKeysQuery({
variables: {
userId: user?.id,
},
});
return query;
}
export default useGetSecurityKeys;

View File

@@ -0,0 +1,35 @@
import { ButtonWithLoading as Button } from '@/components/ui/v3/button';
import {
useGithubAuthentication,
type UseGithubAuthenticationHookProps,
} from '@/features/auth/AuthProviders/Github/hooks/useGithubAuthentication';
import { SiGithub } from '@icons-pack/react-simple-icons';
interface Props extends UseGithubAuthenticationHookProps {
buttonText?: string;
withAnonId?: boolean;
redirectTo?: string;
}
function GithubAuthButton({
buttonText = 'Continue with GitHub',
withAnonId = false,
redirectTo,
}: Props) {
const { mutate: signInWithGithub, isLoading } = useGithubAuthentication({
withAnonId,
redirectTo,
});
return (
<Button
className="gap-2 !bg-white text-sm+ !text-black hover:ring-2 hover:ring-white hover:ring-opacity-50 disabled:!text-black disabled:!text-opacity-60"
disabled={isLoading}
loading={isLoading}
onClick={() => signInWithGithub()}
>
<SiGithub size={14} /> {buttonText}
</Button>
);
}
export default GithubAuthButton;

View File

@@ -0,0 +1 @@
export { default as GithubAuthButton } from './GithubAuthButton';

View File

@@ -0,0 +1,2 @@
export * from './useGithubAuthentication';
export { default as useGithubAuthentication } from './useGithubAuthentication';

View File

@@ -0,0 +1,47 @@
import { getAnonId } from '@/lib/segment';
import { isNotEmptyValue } from '@/lib/utils';
import { getToastStyleProps } from '@/utils/constants/settings';
import { nhost } from '@/utils/nhost';
import type { SignInProviderParams } from '@nhost/nhost-js-beta/auth';
import { useMutation } from '@tanstack/react-query';
import { toast } from 'react-hot-toast';
export interface UseGithubAuthenticationHookProps {
withAnonId?: boolean;
redirectTo?: string;
errorText?: string;
}
function useGithubAuthentication({
withAnonId = false,
redirectTo,
errorText,
}: UseGithubAuthenticationHookProps) {
const githubAuthenticationMutation = useMutation(
async () => {
let options: SignInProviderParams | undefined;
if (isNotEmptyValue(redirectTo)) {
options = {
redirectTo,
};
}
if (withAnonId) {
options = {
metadata: { anonId: await getAnonId() },
...options,
};
}
const redirectURl = nhost.auth.signInProviderURL('github', options);
window.location.href = redirectURl;
},
{
onError: () => {
toast.error(errorText, getToastStyleProps());
},
},
);
return githubAuthenticationMutation;
}
export default useGithubAuthentication;

View File

@@ -0,0 +1,31 @@
import { Button } from '@/components/ui/v3/button';
import { useSignInWithSecurityKey } from '@/features/auth/SignIn/SecurityKey/hooks/useSignInWithSecurityKey';
import { Fingerprint } from 'lucide-react';
import { useState } from 'react';
import { VerifyEmailDialog } from './VerifyEmailDialog';
function SignInWithSecurityKey() {
const [open, setOpen] = useState(false);
function onNeedsEmailVerification() {
setOpen(true);
}
const { disabled, signInWithSecurityKey } = useSignInWithSecurityKey({
onNeedsEmailVerification,
});
return (
<>
<VerifyEmailDialog open={open} setOpen={setOpen} />
<Button
variant="ghost"
className="gap-2 !bg-white text-sm+ !text-black hover:ring-2 hover:ring-white hover:ring-opacity-50 disabled:!text-black disabled:!text-opacity-60"
disabled={disabled}
onClick={signInWithSecurityKey}
>
<Fingerprint size={14} />
Continue with a security key
</Button>
</>
);
}
export default SignInWithSecurityKey;

View File

@@ -0,0 +1,28 @@
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from '@/components/ui/v3/dialog';
interface Props {
open: boolean;
setOpen: (openState: boolean) => void;
}
export function VerifyEmailDialog({ open, setOpen }: Props) {
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogContent className="text-foreground sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Email verification required</DialogTitle>
<DialogDescription>
You need to verify your email first. Please check your mailbox and
follow the confirmation link to complete the registration.
</DialogDescription>
</DialogHeader>
</DialogContent>
</Dialog>
);
}

View File

@@ -0,0 +1 @@
export { default as useSignInWithSecurityKey } from './useSignInWithSecurityKey';

View File

@@ -0,0 +1,47 @@
import { isNotEmptyValue } from '@/lib/utils';
import { useNhostClient } from '@/providers/nhost';
import { getToastStyleProps } from '@/utils/constants/settings';
import { startAuthentication } from '@simplewebauthn/browser';
import { useState } from 'react';
import { toast } from 'react-hot-toast';
interface Props {
onNeedsEmailVerification: () => void;
}
function useSignInWithSecurityKey({ onNeedsEmailVerification }: Props) {
const nhost = useNhostClient();
const [disabled, setDisabled] = useState(false);
async function signInWithSecurityKey() {
try {
setDisabled(true);
const signInWebauthnResponse = await nhost.auth.signInWebauthn();
const { body: options } = signInWebauthnResponse;
const credential = await startAuthentication(options);
await nhost.auth.verifySignInWebauthn({
credential,
});
} catch (error) {
let errorMessage =
error?.message ||
'An error occurred while signing in. Please try again.';
if (isNotEmptyValue(error?.body)) {
const errorCode = error.body.error;
if (errorCode === 'unverified-user') {
onNeedsEmailVerification();
return;
}
errorMessage = error.body.message;
}
toast.error(errorMessage, getToastStyleProps());
} finally {
setDisabled(false);
}
}
return { disabled, signInWithSecurityKey };
}
export default useSignInWithSecurityKey;

View File

@@ -0,0 +1 @@
export { default as SignInWithSecurityKey } from './components/SignInWithSecurityKey/SignInWithSecurityKey';

View File

@@ -0,0 +1,30 @@
import { MfaOtpForm } from '@/components/common/MfaOtpForm';
import { Smartphone } from 'lucide-react';
interface Props {
sendMfaOtp: (code: string) => Promise<any>;
loading: boolean;
requestNewMfaTicket: () => Promise<void>;
}
function MfaSignInOtpForm({ sendMfaOtp, loading, requestNewMfaTicket }: Props) {
return (
<div className="ws-full relative grid grid-flow-row gap-4 bg-transparent">
<div className="flex w-full flex-col items-center justify-center gap-3">
<Smartphone size={32} />
<h2 className="text-[1.25rem]">Authentication Code</h2>
</div>
<MfaOtpForm
loading={loading}
sendMfaOtp={sendMfaOtp}
requestNewMfaTicket={requestNewMfaTicket}
/>
<p className="text-center">
Open your authenticator app or browser extension to view your
authentication code.
</p>
</div>
);
}
export default MfaSignInOtpForm;

View File

@@ -0,0 +1,54 @@
import useOnSignInWithEmailAndPasswordHandler from '@/features/auth/SignIn/SignInWithEmailAndPassword/hooks/useOnSignInWithEmailAndPasswordHandler';
import useRequestNewMfaTicket from '@/features/auth/SignIn/SignInWithEmailAndPassword/hooks/useRequestNewMfaTicket';
import { useNhostClient } from '@/providers/nhost';
import { useRef, useState } from 'react';
import MfaSignInOtpForm from './MfaSignInOtpForm';
import SignInWithEmailAndPasswordForm from './SignInWithEmailAndPasswordForm';
function SignInWithEmailAndPassword() {
const [needsMfaOtp, setNeedsMfaOtp] = useState(false);
const mfaTicket = useRef<string | undefined>();
const [isMfaLoading, setIsMfaLoading] = useState(false);
const nhost = useNhostClient();
function onNeedsMfa(ticket: string) {
mfaTicket.current = ticket;
setNeedsMfaOtp(true);
}
const { onSignInWithEmailAndPassword, isLoading, emailAndPasswordRef } =
useOnSignInWithEmailAndPasswordHandler({ onNeedsMfa });
const requestNewMfaTicketFn = useRequestNewMfaTicket();
async function requestNewMfaTicket() {
const { email, password } = emailAndPasswordRef.current;
mfaTicket.current = await requestNewMfaTicketFn(email, password);
}
async function onHandleSendMfaOtp(otp: string) {
try {
setIsMfaLoading(true);
await nhost.auth.verifySignInMfaTotp({
ticket: mfaTicket.current,
otp,
});
} finally {
setIsMfaLoading(false);
}
}
return needsMfaOtp ? (
<MfaSignInOtpForm
sendMfaOtp={onHandleSendMfaOtp}
loading={isMfaLoading}
requestNewMfaTicket={requestNewMfaTicket}
/>
) : (
<SignInWithEmailAndPasswordForm
onSubmit={onSignInWithEmailAndPassword}
isLoading={isLoading}
/>
);
}
export default SignInWithEmailAndPassword;

View File

@@ -0,0 +1,60 @@
import { FormInput } from '@/components/form/FormInput';
import { ButtonWithLoading as Button } from '@/components/ui/v3/button';
import { Form } from '@/components/ui/v3/form';
import useSignInWithEmailAndPasswordForm, {
type SignInWithEmailAndPasswordFormValues,
} from '@/features/auth/SignIn/SignInWithEmailAndPassword/hooks/useSignInWithEmailAndPasswordForm';
import NextLink from 'next/link';
interface Props {
onSubmit: (values: SignInWithEmailAndPasswordFormValues) => void;
isLoading: boolean;
}
function SignInWithEmailAndPassword({ onSubmit, isLoading }: Props) {
const form = useSignInWithEmailAndPasswordForm();
return (
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="grid grid-flow-row gap-4 bg-transparent"
>
<FormInput
control={form.control}
label="Email"
name="email"
type="email"
/>
<FormInput
control={form.control}
label="Password"
name="password"
type="password"
/>
<NextLink
href="/password/new"
className="justify-self-start font-semibold"
>
Forgot password?
</NextLink>
<Button
type="submit"
variant="outline"
className="w-full !bg-white !text-black disabled:!text-black disabled:!text-opacity-60"
disabled={isLoading}
loading={isLoading}
>
Sign In
</Button>
<p color="secondary" className="text-center">
<span className="text-[#A2B3BE]">or </span>
<NextLink className="font-semibold" href="/signin">
sign in with GitHub
</NextLink>
</p>
</form>
</Form>
);
}
export default SignInWithEmailAndPassword;

View File

@@ -0,0 +1,65 @@
import { isNotEmptyValue } from '@/lib/utils';
import { useNhostClient } from '@/providers/nhost';
import { getToastStyleProps } from '@/utils/constants/settings';
import { useRouter } from 'next/router';
import { useRef, useState } from 'react';
import toast from 'react-hot-toast';
import type { SignInWithEmailAndPasswordFormValues } from './useSignInWithEmailAndPasswordForm';
interface Props {
onNeedsMfa: (mfaTicket: string) => void;
}
type EmailAndPasswordRef = {
email: string;
password: string;
} | null;
function useOnSignInWithEmailAndPasswordHandler({ onNeedsMfa }: Props) {
const [isLoading, setIsloading] = useState(false);
const nhost = useNhostClient();
const router = useRouter();
const emailAndPasswordRef = useRef<EmailAndPasswordRef>();
async function onSignInWithEmailAndPassword({
email,
password,
}: SignInWithEmailAndPasswordFormValues) {
try {
setIsloading(true);
const response = await nhost.auth.signInEmailPassword({
email,
password,
});
emailAndPasswordRef.current = {
email,
password,
};
if (response.body.mfa) {
onNeedsMfa(response.body.mfa.ticket);
}
} catch (error) {
let errorMessage =
error?.message ||
'An error occurred while signing in. Please try again.';
if (isNotEmptyValue(error?.body)) {
const errorCode = error.body.error;
if (errorCode === 'unverified-user') {
await nhost.auth.sendVerificationEmail({ email });
router.push(`/email/verify?email=${encodeURIComponent(email)}`);
return;
}
errorMessage = error.body.message;
}
toast.error(errorMessage, getToastStyleProps());
} finally {
setIsloading(false);
}
}
return { onSignInWithEmailAndPassword, isLoading, emailAndPasswordRef };
}
export default useOnSignInWithEmailAndPasswordHandler;

View File

@@ -0,0 +1,29 @@
import { useNhostClient } from '@/providers/nhost';
import { getToastStyleProps } from '@/utils/constants/settings';
import toast from 'react-hot-toast';
function useRequestNewMfaTicket() {
const nhost = useNhostClient();
async function requestNewMfaTicket(email: string, password: string) {
let mfaTicket: string;
try {
const response = await nhost.auth.signInEmailPassword({
email,
password,
});
mfaTicket = response.body?.mfa.ticket;
} catch (error) {
toast.error(
error?.message ||
'An error occurred while verifying TOTP. Please try again.',
getToastStyleProps(),
);
}
return mfaTicket;
}
return requestNewMfaTicket;
}
export default useRequestNewMfaTicket;

View File

@@ -0,0 +1,30 @@
import { zodResolver } from '@hookform/resolvers/zod';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
const validationSchema = z
.object({
email: z.string().email({ message: 'Invalid email address' }),
password: z.string().min(1, { message: 'Password is required' }),
})
.required();
export type SignInWithEmailAndPasswordFormValues = z.infer<
typeof validationSchema
>;
function useSignInWithEmailAndPasswordForm() {
const form = useForm<SignInWithEmailAndPasswordFormValues>({
mode: 'onTouched',
reValidateMode: 'onBlur',
defaultValues: {
email: '',
password: '',
},
resolver: zodResolver(validationSchema),
});
return form;
}
export default useSignInWithEmailAndPasswordForm;

View File

@@ -0,0 +1 @@
export { default as SignInWithEmailAndPassword } from './components/SignInWithEmailAndPassword';

View File

@@ -0,0 +1,15 @@
import { GithubAuthButton } from '@/features/auth/AuthProviders/Github/components/GithubAuthButton';
import { useHostName } from '@/features/orgs/projects/common/hooks/useHostName';
function SignInWithGithub() {
const redirectTo = useHostName();
return (
<GithubAuthButton
redirectTo={redirectTo}
buttonText="Continue with GitHub"
errorText="An error occurred while trying to sign in using GitHub. Please try again later."
/>
);
}
export default SignInWithGithub;

View File

@@ -0,0 +1 @@
export { default as SignInWithGithub } from './SignInWithGithub';

View File

@@ -0,0 +1,39 @@
import {
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from '@/components/ui/v3/tabs';
import { useState } from 'react';
import { SignUpWithEmailAndPasswordForm } from './SignUpWithEmailAndPassword';
import { SignUpWithSecurityKeyForm } from './SignUpWithSecurityKey';
function SignUpTabs() {
const [tab, setTab] = useState<string>('password');
return (
<Tabs value={tab} onValueChange={setTab} className="w-full">
<TabsList className="w-full">
<TabsTrigger value="password" className="w-full">
Sign Up with a Password
</TabsTrigger>
<TabsTrigger value="security-key" className="w-full">
Sign Up with a Security key
</TabsTrigger>
</TabsList>
<div className="pt-7">
{tab === 'password' && (
<TabsContent value="password">
<SignUpWithEmailAndPasswordForm />
</TabsContent>
)}
{tab === 'security-key' && (
<TabsContent value="security-key">
<SignUpWithSecurityKeyForm />
</TabsContent>
)}
</div>
</Tabs>
);
}
export default SignUpTabs;

View File

@@ -0,0 +1,81 @@
import { FormInput } from '@/components/form/FormInput';
import { Button } from '@/components/ui/v3/button';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@/components/ui/v3/form';
import useOnSignUpWithPasswordHandler from '@/features/auth/SignUp/SignUpTabs/SignUpWithEmailAndPassword/hooks/useOnSignUpWithPasswordHandler';
import useSignUpWithEmailAndPasswordForm from '@/features/auth/SignUp/SignUpTabs/SignUpWithEmailAndPassword/hooks/useSignUpWithEmailAndPasswordForm';
import { Turnstile } from '@marsidev/react-turnstile';
function SignUpWithEmailAndPasswordForm() {
const form = useSignUpWithEmailAndPasswordForm();
const onSignUpWithPassword = useOnSignUpWithPasswordHandler();
return (
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSignUpWithPassword)}
className="grid grid-flow-row gap-4 bg-transparent"
>
<FormInput control={form.control} label="Name" name="displayName" />
<FormInput
control={form.control}
label="Email"
name="email"
type="email"
/>
<FormInput
control={form.control}
label="Password"
name="password"
type="password"
/>
<FormField
control={form.control}
name="turnstileToken"
render={() => (
<FormItem>
<FormLabel>Verification</FormLabel>
<FormControl>
<Turnstile
siteKey={process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY}
options={{ theme: 'dark', size: 'flexible' }}
onSuccess={(token) => {
form.setValue('turnstileToken', token, {
shouldValidate: true,
});
}}
onError={() => {
form.setValue('turnstileToken', '', {
shouldValidate: true,
});
}}
onExpire={() => {
form.setValue('turnstileToken', '', {
shouldValidate: true,
});
}}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button
type="submit"
variant="outline"
className="w-full !bg-transparent"
>
Sign Up
</Button>
</form>
</Form>
);
}
export default SignUpWithEmailAndPasswordForm;

View File

@@ -0,0 +1,51 @@
import { getAnonId } from '@/lib/segment';
import { isEmptyValue } from '@/lib/utils';
import { useNhostClient } from '@/providers/nhost';
import { getToastStyleProps } from '@/utils/constants/settings';
import { useRouter } from 'next/router';
import toast from 'react-hot-toast';
import type { SignUpWithEmailAndPasswordFormValues } from './useSignUpWithEmailAndPasswordForm';
function useOnSignUpWithPasswordHandler() {
const nhost = useNhostClient();
const router = useRouter();
async function onSignUpWithPassword({
email,
password,
displayName,
turnstileToken,
}: SignUpWithEmailAndPasswordFormValues) {
try {
const response = await nhost.auth.signUpEmailPassword(
{
email,
password,
options: {
displayName,
metadata: { anonId: await getAnonId() },
},
},
{
headers: {
'x-cf-turnstile-response': turnstileToken,
},
},
);
if (response.status === 200 && isEmptyValue(response.body)) {
router.push(`/email/verify?email=${encodeURIComponent(email)}`);
}
} catch (error) {
toast.error(
error.message ||
'An error occurred while signing up. Please try again.',
getToastStyleProps(),
);
}
}
return onSignUpWithPassword;
}
export default useOnSignUpWithPasswordHandler;

View File

@@ -0,0 +1,43 @@
import { zodResolver } from '@hookform/resolvers/zod';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
const validationSchema = z
.object({
email: z.string().email({ message: 'Invalid email address' }),
password: z.string().min(1, { message: 'Password is required' }),
displayName: z
.string()
.regex(
/^[\p{L}\p{N}\p{S} ,.'-]+$/u,
'Use only letters, numbers, symbols and basic punctuation',
)
.min(1, { message: 'Name is required' })
.max(32, { message: 'Name must be 32 characters or less' }),
turnstileToken: z
.string()
.min(1, { message: 'Please complete the CAPTCHA' }),
})
.required();
export type SignUpWithEmailAndPasswordFormValues = z.infer<
typeof validationSchema
>;
function useSignUpWithEmailAndPasswordForm() {
const form = useForm<SignUpWithEmailAndPasswordFormValues>({
mode: 'onTouched',
reValidateMode: 'onBlur',
defaultValues: {
email: '',
password: '',
displayName: '',
turnstileToken: '',
},
resolver: zodResolver(validationSchema),
});
return form;
}
export default useSignUpWithEmailAndPasswordForm;

View File

@@ -0,0 +1 @@
export { default as SignUpWithEmailAndPasswordForm } from './components/SignUpWithEmailAndPasswordForm';

Some files were not shown because too many files have changed in this diff Show More