Compare commits

..

10 Commits

Author SHA1 Message Date
github-actions[bot]
2dd4df5170 chore: update versions (#3191)
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.20.0

# @nhost/dashboard

## 2.17.0

### Minor Changes

-   fd59918: fix: redirect to 404 with nhost cli dashboard

## 2.16.0

### Minor Changes

-   f8e6b61: fix: can add rule groups in table permissions
-   9e404c8: fix: not redirect to 404 page if using local Nhost backend
-   ac4aa01: fix: can delete column in database page
-   4385524: fix: update url to check service health in local dashboard

### Patch Changes

-   @nhost/react-apollo@16.0.1
-   @nhost/nextjs@2.2.2

## 2.15.0

### Minor Changes

- f1052a8: fix: improve stability of the dashboard when pausing projects
-   30daa41: fix: update links to docs in overview page
-   7537237: feat: add image preview toggle in storage

## 2.14.0

### Minor Changes

- d43931e: fix: invalid organization slug/project subdomain doesn't open
404 page
- 5df6fa2: feat: add unencrypted disk warning in storage capacity
settings

### Patch Changes

-   44c1e17: chore: update `msw` to v1.3.5 to fix vulnerabilities
    -   @nhost/react-apollo@16.0.0
    -   @nhost/nextjs@2.2.1

## 2.13.0

### Minor Changes

- 21e90da: chore: remove restrictions on SMTP sender so My Name
[name@acme.com](mailto:name@acme.com) can be added
- 865dd93: fix: duplicate Run placeholders when there is an error in the
backend
- 6902a36: fix: can remove resources if postgres capacity is higher than
10
-   a535aa3: fix: fetch user roles locally in auth section
-   0c50816: fix: allow decimal numbers in database row insert
- aea6d18: chore: add warning when pausing a project about losing Run
services persistent volume data
- d3b4fc3: feat: allow to change postgres settings if project is paused
-   29d27e1: chore: update `next` to v14.2.22 to fix vulnerabilities
-   c9dca09: feat: add reset password form
-   b3bcacb: fix: paused project banner cannot read null project name

### Patch Changes

-   Updated dependencies [46fc520]
-   Updated dependencies [29d27e1]
    -   @nhost/nextjs@2.2.0
    -   @nhost/react-apollo@15.0.1

## 2.12.0

### Minor Changes

- eb95562: fix: show all available permission variables in permission
dropdown select

### Patch Changes

- 8b5c4a0: chore: cleanup layout and add disable duplicate atom key
checking in development mode

## 2.11.3

### Patch Changes

- 714dffa: fix: improve project polling logic and unify usage across
components

## 2.11.2

### Patch Changes

- 6a34f89: fix: improve project polling logic and unify usage across
components

## 2.11.1

### Patch Changes

-   0f6ce52: fix: consolidate useProject hook and fix jwt expired error

## 2.11.0

### Minor Changes

-   cea3ef5: Feat: add org and project placeholders

## 2.10.0

### Minor Changes

-   86ecf27: feat: add support for additional metrics in overview
- 21708be: feat: dashboard: add support for storage buckets to AI
assistants

## 1.30.0

### Minor Changes

- 50441a8: feat: add ui for project autoscaler settings and run services
autoscaler settings

## 1.29.0

### Minor Changes

-   55d8bb5: feat: integrate turnstile for signup verification
-   2a2e54c: fix: update docs url in run services form tooltip
- 18f942f: fix: display long error messages in error toast without
overflow

### Patch Changes

-   @nhost/react-apollo@13.0.0
-   @nhost/nextjs@2.1.22

## 1.28.2

### Patch Changes

- 52a38fe: chore: update dependencies to address security
vulnerabilities
-   Updated dependencies [52a38fe]
    -   @nhost/nextjs@2.1.21

## 1.28.1

### Patch Changes

-   9735fa2: chore: remove broken link

## 1.28.0

### Minor Changes

- 526183a: feat: allow filtering users in "make request as" in graphql
section
-   be3b85b: feat: add conceal errors toggle on auth settings page

### Patch Changes

- 35a2f12: fix: prevent run service details from opening when attempting
to delete
    -   @nhost/react-apollo@12.0.6
    -   @nhost/nextjs@2.1.20

## 1.27.0

### Minor Changes

-   a7cd02c: fix: resolve rate limit query

## 1.26.0

### Minor Changes

-   3773ad7: chore: update pricing information
- b63250d: fix: not allow run service creation form resubmission while
creating a run service
-   a44a1d4: feat: add rate limits settings page

### Patch Changes

-   @nhost/react-apollo@12.0.5
-   @nhost/nextjs@2.1.19

## 1.25.0

### Minor Changes

- d1ceede: feat: add setting to migrate postgres major and/or minor
versions
- e5d3d1a: fix: allow manually typing column for custom check in
database row permissions

### Patch Changes

-   @nhost/react-apollo@12.0.4
-   @nhost/nextjs@2.1.18

## 1.24.1

### Patch Changes

- 49f2e55: fix: use service subdomain in service form and service
details dialog
- 598b988: fix: use current project subdomain in ServiceDetailsDialog
component

## 1.24.0

### Minor Changes

-   abb24af: chore: add redirect to support page when project is locked
- 18a6455: feat: show contact us info and locked reason when project is
locked

### Patch Changes

-   e31eefa: fix: include ingresses field when updating run services

## 1.23.0

### Minor Changes

-   33284d3: fix: don't show double scrollbar in configuration editor

### Patch Changes

-   @nhost/react-apollo@12.0.3
-   @nhost/nextjs@2.1.17

## 1.22.0

### Minor Changes

-   998c037: fix: align drop-down list in select component
- 807b8c0: fix: show city name in region selection for project creation

## 1.21.0

### Minor Changes

- a2efeed: fix: improve project health error handling, add unknown state
and polling interval for health state

## 1.20.0

### Minor Changes

- 8ea4210: fix: error toasts can be closed individually, instead of
dismissing all toasts at once
- 58919ba: chore: add blink animation when project health service is
updating

## 1.19.0

### Minor Changes

- b519862: fix: get configuration in configuration editor using local
development environment

## 1.18.0

### Minor Changes

- 502abad: feat: add services health checks indicators to the overview
page
-   b3ff6ad: chore: update title text on service status modal
- dbadf59: feat: add project configuration TOML editor to the settings
page

## 1.17.0

### Minor Changes

- 77fba27: fix: postgres version validation when activating ai in ai
settings page
-   ac6d1b6: feat: use name instead of awsName

## 1.16.3

### Patch Changes

- 87a37cf: fix: remove unnecessary isPlatform check from verify button
disable logic on custom domains
    -   @nhost/react-apollo@12.0.2
    -   @nhost/nextjs@2.1.16

## 1.16.2

### Patch Changes

- a9413af: fix: update `GetAllWorkspacesAndProjects` query polling to
use exponential backoff
    -   @nhost/react-apollo@12.0.1
    -   @nhost/nextjs@2.1.15

## 1.16.1

### Patch Changes

-   @nhost/react-apollo@12.0.0
-   @nhost/nextjs@2.1.14

## 1.16.0

### Minor Changes

- c6d5c5c: feat: add toggle switch to enable/disable public access in
the database settings

## 1.15.2

### Patch Changes

-   @nhost/react-apollo@11.0.4
-   @nhost/nextjs@2.1.13

## 1.15.1

### Patch Changes

-   @nhost/react-apollo@11.0.3
-   @nhost/nextjs@2.1.12

## 1.15.0

### Minor Changes

-   a7bde37: feat: send metadata in the edit form

### Patch Changes

- 1bc615b: feat: improve error message handling in `ErrorToast`
component
    -   @nhost/react-apollo@11.0.2
    -   @nhost/nextjs@2.1.11

## 1.14.0

### Minor Changes

-   a448d7d: feat: allow configuring postmark and delete SMTP settings

## 1.13.3

### Patch Changes

-   5924bc3: fix: include password in `GetSmtpSettings` query
- c5ad634: fix: resolved an issue where one-click install links were
broken on Safari
- 7278991: fix: update graphql auto-embeddings configuration to use
String type for model field

## 1.13.2

### Patch Changes

-   026f84f: fix: use configuration server URL from environment variable

## 1.13.1

### Patch Changes

-   7e9a2ce: fix: resolve issue where run services form fails to open

## 1.13.0

### Minor Changes

-   dd5d262: feat: add model field to the auto-embeddings form
- 09962be: feat: enable settings and run services when running the
dashboard locally
- 9cdecb6: feat: enable users to update their email address from the
account settings page

## 1.12.2

### Patch Changes

-   c195c51: fix: send email upon signin for unverified users

## 1.12.1

### Patch Changes

- 93ebdf8: fix: use service urls when initilizaing NhostClient running
local dashboard
    -   @nhost/react-apollo@11.0.1
    -   @nhost/nextjs@2.1.10

## 1.12.0

### Minor Changes

- f242e4b: feat: add connect with github to the user's account settings
-   768ca17: chore: update dependencies
- d62bd0f: fix: "Track this" option within the SQL editor now correctly
updates the metadata
- 91c2bb6: feat: refactor sign-in and sign-up pages to enforce email
verification

### Patch Changes

-   943831f: fix: resolve an error toast issue when unpausing a project
-   Updated dependencies [768ca17]
    -   @nhost/react-apollo@11.0.0
    -   @nhost/nextjs@2.1.9

## 1.11.2

### Patch Changes

-   @nhost/react-apollo@10.0.2
-   @nhost/nextjs@2.1.8

## 1.11.1

### Patch Changes

-   981404f: fix: set default value for healthCheck field validation

## 1.11.0

### Minor Changes

- 7789469: chore: upgrade dependency `@graphql-codegen/cli` to `5.0.2`
to address vulnerability
- 6c11b75: feat: add update user displayName section in account settings

### Patch Changes

-   @nhost/react-apollo@10.0.1
-   @nhost/nextjs@2.1.7

## 1.10.0

### Minor Changes

-   49a80c2: chore: update dependencies
-   150c04a: feat: add healthcheck config to run services

### Patch Changes

- e03f141: fix: allow insert, update and delete on tables in `auth` and
`storage` schemas
- 28676f4: feat: add min postgres version check to enable the ai service
-   Updated dependencies [49a80c2]
    -   @nhost/react-apollo@10.0.0
    -   @nhost/nextjs@2.1.6

## 1.9.0

### Minor Changes

-   d86e5c9: feat: add support for filtering the logs using a RegExp

## 1.8.3

### Patch Changes

-   @nhost/react-apollo@9.0.3
-   @nhost/nextjs@2.1.5

## 1.8.2

### Patch Changes

- 6df4f02: fix: use custom error toast and show correct message when
sending an invite

## 1.8.1

### Patch Changes

-   @nhost/react-apollo@9.0.2
-   @nhost/nextjs@2.1.4

## 1.8.0

### Minor Changes

- 713d53c: feat: add catch-all route for workspace/project - useful for
documentation

### Patch Changes

-   3db2999: fix: refresh table list after running SQL using the editor
- 3c4dd55: fix: handle `Error` objects properly in the `ErrorToast`
component
- 92b434e: fix: resolve an issue where the checkbox in the data-grid
header did not select all rows
    -   @nhost/react-apollo@9.0.1
    -   @nhost/nextjs@2.1.3

## 1.7.0

### Minor Changes

-   0d8d0eb: Update docs and dashboard references

## 1.6.9

### Patch Changes

-   @nhost/react-apollo@9.0.0
-   @nhost/nextjs@2.1.2

## 1.6.8

### Patch Changes

-   @nhost/react-apollo@8.0.1
-   @nhost/nextjs@2.1.1

## 1.6.7

### Patch Changes

-   5ef5189: fix: update `@apollo/client` to `3.9.4` to fix a cache bug

## 1.6.6

### Patch Changes

-   3ba485e: fix: added discord.com to connect-src
-   e5bab6a: chore: update dependencies
-   Updated dependencies [b19ffed]
-   Updated dependencies [e5bab6a]
    -   @nhost/nextjs@2.1.0
    -   @nhost/react-apollo@8.0.0

## 1.6.5

### Patch Changes

- ba73bb4: fix: update ErrorToast component to show the internal graphql
error
- d5337ff: fix: utilize accumulator in the creation of validation schema
within data grid utils

## 1.6.4

### Patch Changes

-   7c2a1c2: feat: show error and debug info in the error toast

## 1.6.3

### Patch Changes

-   6b8aad5: fix: add bare nhost.run to CSP

## 1.6.2

### Patch Changes

-   b18edc0: feat: added CSP and X-Frame-Options

## 1.6.1

### Patch Changes

-   8d91f71: chore: update deps and enable pnpm audit
- 3b8473b: chore: update turbo to `1.11.3` and pnpm to `8.10.5` in
Dockerfile
-   Updated dependencies [8d91f71]
    -   @nhost/react-apollo@7.0.2
    -   @nhost/nextjs@2.0.2

## 1.6.0

### Minor Changes

-   3ff1c2b53: fix: show upgrade option for pro projects

## 1.5.0

### Minor Changes

-   c2ef17c0a: feat: add support for new Team plan

## 1.4.0

### Minor Changes

-   7883bbcbd: feat: don't show deprecated plans
- 44be6dc0a: feat: set redirectTo during sign-in to support preview
environments

### Patch Changes

- 3c3594898: fix: allow access to graphite when configured running in
local dashboard
-   32c246b7a: chore: update docs icon

## 1.3.2

### Patch Changes

-   174b4165b: chore: use env variables when running graphql codegen
-   7c977e714: chore: change `Allowed Roles` to `Default Allowed Roles`
-   46f028b9f: fix: remove hardcoded ai version setting

## 1.3.1

### Patch Changes

- af33c21d1: chore: remove backendUrl deprecation notice and remove all
references to `providersUpdated`

## 1.3.0

### Minor Changes

-   04784d880: Fix graphite's default version

## 1.2.0

### Minor Changes

-   5733162ed: feat: add settings and ui for graphite

## 1.1.0

### Minor Changes

-   e2b79b5ec: chore: remove sharp from deps

## 1.0.1

### Patch Changes

-   @nhost/react-apollo@7.0.1
-   @nhost/nextjs@2.0.1

## 1.0.0

### Major Changes

- bc9eff6e4: chore: remove support for using backendUrl when
instantiating the Nhost client

### Patch Changes

-   Updated dependencies [bc9eff6e4]
    -   @nhost/nextjs@2.0.0
    -   @nhost/react-apollo@7.0.0

## 0.21.1

### Patch Changes

-   97ced73a3: fix(dashboard): prevent dashboard from resolving secrets

## 0.21.0

### Minor Changes

- ed1a8d458: Update alert message on increasing PostgreSQL's volume
capacity
-   2e2248fd4: feat(dashboard): add SQL editor

## 0.20.28

### Patch Changes

-   7c2c31082: feat: add support for users to delete their account
    -   @nhost/react-apollo@6.0.1
    -   @nhost/nextjs@1.13.40

## 0.20.27

### Patch Changes

- fa79b7709: chore(dashboard): tweaks and fixes to the service form and
dialog
-   8df84d782: fix(dashboard): allow resetting custom domains
    -   @nhost/react-apollo@6.0.0
    -   @nhost/nextjs@1.13.39

## 0.20.26

### Patch Changes

- 331ba0376: feat(dashboard): add postgres storage capacity modifier in
the settings
-   b7f801874: feat(dashboard): add new settings page for custom domains

## 0.20.25

### Patch Changes

-   @nhost/react-apollo@5.0.38

## 0.20.24

### Patch Changes

-   e10389ecf: fix(dashboard): disable run tab when developing locally
    -   @nhost/react-apollo@5.0.37

## 0.20.23

### Patch Changes

-   c01568a7d: chore(dashboard): show alert to update oauth providers

## 0.20.22

### Patch Changes

-   c3efb7ec8: feat(dashboard): query latest announcement from platform

## 0.20.21

### Patch Changes

-   3e46d3873: chore: update link to node18 announcement

## 0.20.20

### Patch Changes

-   @nhost/react-apollo@5.0.36
-   @nhost/nextjs@1.13.38

## 0.20.19

### Patch Changes

-   75c4c8ae3: feat(dashboard): make env value input multiline

## 0.20.18

### Patch Changes

- 425d485f8: fix(dashboard): make sure dedicated resources pricing
follows total resources

## 0.20.17

### Patch Changes

-   ae324f67f: fix(dashboard): remove unused graphql fields

## 0.20.16

### Patch Changes

-   df5b4302c: chore(dashboard): remove run feature flag
- bf4a1f6c2: feat(dashboard): fetch auth, postgres, hasura and storage
versions from dashboard
- 34fc08ca7: fix(dashboard/run): show correct private registry in
service details
-   885d10620: chore(dashboard): change feedback to contact us

## 0.20.15

### Patch Changes

- ed16c8b5d: feat(run): add a confirmation dialog when deleting a run
service
- 216990888: fix(run): center loading indicator when selecting a project

## 0.20.14

### Patch Changes

-   9fbea9787: feat: add node18 announcement

## 0.20.13

### Patch Changes

- e84acf469: fix(run): handle subdomain undefined error when creating a
new service

## 0.20.12

### Patch Changes

-   b7c799d62: feat(run): add dialog to copy registry and URLs

## 0.20.11

### Patch Changes

-   8903e6abd: fix(dashboard): show correct egress limit in usage stats

## 0.20.10

### Patch Changes

- 666a75a23: feat(dashboard): add functions execution time and egress
volume to usage stats

## 0.20.9

### Patch Changes

-   5e1e80aa8: fix(dashboard): show correct locales in user details
    -   @nhost/react-apollo@5.0.35
    -   @nhost/nextjs@1.13.37

## 0.20.8

### Patch Changes

-   @nhost/react-apollo@5.0.34
-   @nhost/nextjs@1.13.36

## 0.20.7

### Patch Changes

-   4a7ede11e: fix: distinguish files that were not uploaded
- 202b64723: feat(nhost-run): add support for one-click-install run
services
- 074a0fa11: feat(dashboard): add settings toggle to enable/disable
antivirus
    -   @nhost/react-apollo@5.0.33
    -   @nhost/nextjs@1.13.35

## 0.20.6

### Patch Changes

-   b20761e97: feat(services): add pricing info and confirmation dialog
-   90df6d81d: fix(services): handle null values when editing a service
-   aa8508467: fix: query service logs correctly
    feat: enable multiline support for environment value input

## 0.20.5

### Patch Changes

-   8d7f84b8d: fix: make announcement adapt to theme

## 0.20.4

### Patch Changes

-   3b75bfce2: fix: make announcement close properly
- f49819075: fix: show correct values when dedicated resources are
disabled

## 0.20.3

### Patch Changes

-   e643bd362: fix(services): fix errors when config is null
-   bcdab66bf: feat: add annoucement for nhost run
-   f967a2e59: added note about storage not being able to be downsized
-   311c7756d: chore(services): consistent naming for compute

## 0.20.2

### Patch Changes

-   9073182d5: chore(dashboard): bump `turbo` to 1.10.11
-   ece717d6e: feat(logs): show services in the logs page
- 82b335311: feat(metrics): change grafana link to point to the
dashboards
- b135ef695: fix(services): set command as optional and set min replicas
to 0

## 0.20.1

### Patch Changes

-   3d5c34f4c: fix(auth): fix users pagination limit

## 0.20.0

### Minor Changes

-   c99d117d1: feat(services): add support for custom services

## 0.19.2

### Patch Changes

-   face99ccd: chore(deps): bump turbo version
-   cfe527307: style: tweak pull config warning in dark mode
- a9d7da8af: chore(deps): update dependency @types/pluralize to ^0.0.30
-   9aa4371ef: chore: add hasura-auth version 0.21.2
- d14e112bf: chore(deps): update dependency prettier-plugin-tailwindcss
to ^0.4.0
-   d3e8bb94a: chore(deps): update dependency vite-plugin-dts to v3

## 0.19.1

### Patch Changes

-   @nhost/react-apollo@5.0.32
-   @nhost/nextjs@1.13.34

## 0.19.0

### Minor Changes

- 9c61c69a7: chore(dashboard):add postgres 14.6-20230705-1 to the
version selector

### Patch Changes

-   47bda15ff: feat(settings): add warning to pull config

## 0.18.0

### Minor Changes

- ee0b9b8ed: chore(dashboard):add hasura v2.28.2 and v2.29.0 to the
version selector

## 0.17.20

### Patch Changes

-   @nhost/react-apollo@5.0.31
-   @nhost/nextjs@1.13.33

## 0.17.19

### Patch Changes

-   f866120a6: fix(users): use the password length from the config

## 0.17.18

### Patch Changes

-   @nhost/react-apollo@5.0.30
-   @nhost/nextjs@1.13.32

## 0.17.17

### Patch Changes

-   ea7b102c0: fix(pat): highlight expired tokens

## 0.17.16

### Patch Changes

- b3b64a3b7: chore(deps): bump `@types/react` to `v18.2.14` and
`@types/react-dom` to `v18.2.6`
-   32b221f94: chore(deps): bump `graphiql` to `v3`
-   3a56c12df: chore(deps): bump `turbo` to `v1.10.6`
-   Updated dependencies [b3b64a3b7]
    -   @nhost/react-apollo@5.0.29
    -   @nhost/nextjs@1.13.31

## 0.17.15

### Patch Changes

-   f41fdc12a: chore(deps): bump `turbo` to `1.10.5`
-   6199c1c55: fix(projects): don't redirect to 404 page
-   Updated dependencies [07a45fde0]
    -   @nhost/react-apollo@5.0.28
    -   @nhost/nextjs@1.13.30

## 0.17.14

### Patch Changes

- 80b22724d: chore(deps): bump `@types/react` to `v18.2.13`,
`@types/react-dom` to `v18.2.6` and `@storybook/testing-library` to
`v0.2.0`

## 0.17.13

### Patch Changes

-   cc02902cb: chore(docs): update environment variable documentation

## 0.17.12

### Patch Changes

-   660d339e1: fix(storybook): don't break storybook
-   660d339e1: fix(tests): prevent warnings during tests
    -   @nhost/react-apollo@5.0.27
    -   @nhost/nextjs@1.13.29

## 0.17.11

### Patch Changes

- bd4d0c270: chore(dashboard):add postgres 14.6-20230613-1 to the
version selector

## 0.17.10

### Patch Changes

-   c8c2a10b2: fix(database): don't break the password reset flow
- e70b45498: chore(deps): bump `@types/react` to `v18.2.12` and
`@types/react-dom` to `v18.2.5`

## 0.17.9

### Patch Changes

- 842055099: chore(deps): bump `turbo` to `v1.10.3` and `pnpm` to
`v8.6.2`
- fd12aa0a8: chore(projects): remove the postgres password input from
the project creation screen
-   022b76e78: chore(deps): bump `@types/react` to `v18.2.11`
-   3555ab2b7: chore(deps): bump `vitest` monorepo to `v0.32.0`
-   c43e54922: feat(backups): add download button to backups

## 0.17.8

### Patch Changes

-   d0457fe5c: feat(settings): improve the dashboard and config parity
    -   @nhost/react-apollo@5.0.26
    -   @nhost/nextjs@1.13.28

## 0.17.7

### Patch Changes

-   4f0368b95: fix(account): don't break account settings page

## 0.17.6

### Patch Changes

- 64a8f41d0: chore(resources): lower the maximum allowed resources per
service

## 0.17.5

### Patch Changes

-   @nhost/react-apollo@5.0.25
-   @nhost/nextjs@1.13.27

## 0.17.4

### Patch Changes

- 9b1d0f7a5: fix(deployments): use correct timestamp for deployment
details
-   6d2963ffa: chore(deps): bump `@types/react` to `v18.2.8`
- 8871267b9: chore(deps): downgrade `pnpm` to `v8.5.1` because of no
Turborepo support

## 0.17.3

### Patch Changes

-   01eeef9de: chore(misc): under the hood improvements
- 21e13db05: chore(deps): bump `@types/react` to `v18.2.7` and `turbo`
to `v1.10.1`
- f16433ae6: chore(secrets): allow empty secrets and environment
variables
-   aa3c62989: chore(cli): bump Nhost CLI version to v1.0
    -   @nhost/react-apollo@5.0.24
    -   @nhost/nextjs@1.13.26

## 0.17.2

### Patch Changes

-   88a4983f: chore(misc): under the hood improvements

## 0.17.1

### Patch Changes

-   9b0d4dde: feat(secrets): enable secrets

## 0.17.0

### Minor Changes

-   15d84a19: Add postgres 14.6-20230525

## 0.16.14

### Patch Changes

-   4c626174: chore: updated import paths, improved directory structure
-   cc047b71: chore(deps): bump `@fontsource` monorepo to `v5.0.0`
-   99edd012: feat(account): add support for personal access tokens

## 0.16.13

### Patch Changes

-   78c7109c: feat(settings): allow selecting service versions

## 0.16.12

### Patch Changes

- 399009d6: fix(gql): don't enter an infinite loop when fetching remote
app data
- 329e5a91: fix(deployments): use the same sorting of deployments
everywhere
- 6d559d6e: chore(settings): add under the hood improvements to the
settings page
- 12eb236c: chore(deps): bump `prettier-plugin-tailwindcss` to `v0.3.0`
-   f9b81a2a: chore(deps): bump `turbo` to `v1.9.8`
-   1345741b: fix(projects): don't redirect to 404 on project creation
-   Updated dependencies [7fea29a8]
    -   @nhost/react-apollo@5.0.23
    -   @nhost/nextjs@1.13.25

## 0.16.11

### Patch Changes

- 1230b722: fix(projects): don't redirect to 404 on when the project is
renamed
    -   @nhost/react-apollo@5.0.22
    -   @nhost/nextjs@1.13.24

## 0.16.10

### Patch Changes

-   Updated dependencies [da03bf39]
    -   @nhost/react-apollo@5.0.21
    -   @nhost/nextjs@1.13.23

## 0.16.9

### Patch Changes

- 349aac36: fix(settings): use region domain when constructing the
postgres connection string

## 0.16.8

### Patch Changes

- 20fb69fa: chore(projects): change the way how API URLs are constructed

## 0.16.7

### Patch Changes

- 49f9b837: chore(docker): bump `pnpm` to `v8.4.0` and `turbo` to
`v1.9.3`
- 3f478a4e: chore(deps): bump `vitest` to `v0.31.0`, `@types/react` to
`v18.2.6` and `@types/react-dom` to `v18.2.4`

## 0.16.6

### Patch Changes

- d926f156: fix(projects): redirect to 404 when an invalid project is
opened
- 49b99728: fix(projects): disable features for non-owner members of
workspaces

## 0.16.5

### Patch Changes

-   12e2855f: chore(deps): bump `jsdom` to v22
-   e4972b83: feat(metrics): add Grafana page

## 0.16.4

### Patch Changes

- 3f396a9e: fix(projects): unpause after upgrading a paused project to
pro
- 3f396a9e: fix(projects): don't redirect to 404 page after project
creation

## 0.16.3

### Patch Changes

-   Updated dependencies [90c60311]
    -   @nhost/react-apollo@5.0.20
    -   @nhost/nextjs@1.13.22

## 0.16.2

### Patch Changes

-   0f34f0c6: fix(projects): disallow downgrading to free plan
- 8da291ad: chore(deps): bump `@types/react` to v18.2.0 and
`@types/react-dom` to v18.2.1

## 0.16.1

### Patch Changes

- adc828a5: fix(gql): don't enter an infinite loop when fetching remote
app data

## 0.16.0

### Minor Changes

-   2fb1145f: feat(compute): add support for replicas

### Patch Changes

- d8ceccec: chore(env): remove deprecated `NHOST_BACKEND_URL`
environment variable

## 0.15.2

### Patch Changes

-   84b84ab7: fix(projects): filter projects by workspace

## 0.15.1

### Patch Changes

-   2faf7907: chore(deps): bump `graphql-request` to v6
-   f1b5a944: chore(deps): bump `@vitejs/plugin-react` to v4
-   7f1785ac: chore(deps): bump `@types/react` to v18.0.37
    -   @nhost/react-apollo@5.0.19

## 0.15.0

### Minor Changes

-   85889ee8: feat(dashboard): add Compute management to the settings

## 0.14.8

### Patch Changes

-   668c8771: chore(dialogs): unify dialog management of payment dialogs

## 0.14.7

### Patch Changes

-   d4ccc656: chore: cleanup unused code
    -   @nhost/react-apollo@5.0.18
    -   @nhost/nextjs@1.13.21

## 0.14.6

### Patch Changes

-   b299cfc9: chore(deps): bump `vitest` to v0.30.0
-   411cb65b: chore(projects): refactor workspace and project hooks
- 43b1b144: chore(deps): bump `@types/react` to v18.0.34 and
`@types/react-dom` to v18.0.11
-   Updated dependencies [43b1b144]
    -   @nhost/react-apollo@5.0.17
    -   @nhost/nextjs@1.13.20

## 0.14.5

### Patch Changes

-   ba0d57ee: fix(i18n): revert i18n library
-   3328ed05: feat(projects): improve overview when there is an error

## 0.14.4

### Patch Changes

-   5e0920ba: chore(deps): bump `next-seo` to v6
-   706c9dc3: chore(deps): bump `@types/react` to 18.0.33
-   99f8f6b3: feat(metrics): show metrics on the overview

## 0.14.3

### Patch Changes

-   @nhost/react-apollo@5.0.16

## 0.14.2

### Patch Changes

-   3cb67300: fix(logs): don't break UI when clearing time picker
-   7453bf3b: feat(projects): show project creator info
-   c166dad0: chore(tests): improve auth page tests
-   6a290bb2: chore(deps): bump `@types/react` to 18.0.32

## 0.14.1

### Patch Changes

-   @nhost/react-apollo@5.0.15
-   @nhost/nextjs@1.13.19

## 0.14.0

### Minor Changes

-   6e1f03ea: feat(dashboard): add support for the Azure AD provider

### Patch Changes

-   1bd2c373: chore(deps): bump `turbo` to 1.8.6
-   d329b621: chore(deps): bump `@types/react` to 18.0.30
-   cb248f0d: fix(tests): avoid name collision in database tests
-   867c8076: chore(deps): bump `@types/react` to 18.0.29

## 0.13.10

### Patch Changes

- e93b06ab: fix(dashboard): remove left margin from workspace list on
mobile
-   1c4806bf: chore(deps): bump `sharp` to 0.32.0
    -   @nhost/react-apollo@5.0.14
    -   @nhost/nextjs@1.13.18

## 0.13.9

### Patch Changes

-   912ed76c: chore(dashboard): bump `@apollo/client` to 3.7.10
-   Updated dependencies [912ed76c]
    -   @nhost/react-apollo@5.0.13

## 0.13.8

### Patch Changes

-   7c127372: chore(dashboard): bump `react-error-boundary` to v4

## 0.13.7

### Patch Changes

- 9130ab12: chore(dashboard): bump `yup` to v1 and `@hookform/resolvers`
to v3

## 0.13.6

### Patch Changes

- 253dd235: using new mutation to create projects + refactor Create
Project page.

## 0.13.5

### Patch Changes

-   @nhost/react-apollo@5.0.12
-   @nhost/nextjs@1.13.17

## 0.13.4

### Patch Changes

-   b48bc034: fix(dashboard): disable new users
-   798e591b: fix(dashboard): show correct date in data grid

## 0.13.3

### Patch Changes

-   bfb4c1a6: chore(dashboard): remove `useAxios` property
-   d8d8394b: Dashboard: allow to override hasura admin secret in docker
-   Updated dependencies [ce1ee40d]
    -   @nhost/nextjs@1.13.16
    -   @nhost/react-apollo@5.0.11

## 0.13.2

### Patch Changes

-   beed2eba: Fix docker entrypoint for dashboard
- 2c8559a3: fix(dashboard): refresh project list after deleting a
project
-   4329d048: chore(dashboard): bump `graphiql` dependencies

## 0.13.1

### Patch Changes

-   cbb1fc5b: chore(dashboard): cleanup GraphQL operations

## 0.13.0

### Minor Changes

-   088584e7: feat(dashboard): add support for custom local subdomains

### Patch Changes

-   2ac90dfd: fix(dashboard): improve mobile responsive layout
-   Updated dependencies [f375eacc]
    -   @nhost/nextjs@1.13.15
    -   @nhost/react-apollo@5.0.10

## 0.12.4

### Patch Changes

-   @nhost/react-apollo@5.0.9
-   @nhost/nextjs@1.13.14

## 0.12.3

### Patch Changes

-   2b1338f7: chore(dashboard): bump `turbo` to 1.8.3
- 5223ee93: fix(dashboard): show correct deployment status on the main
page
-   850a049c: chore(deps): update docker/build-push-action action to v4
-   Updated dependencies [850a049c]
    -   @nhost/nextjs@1.13.13
    -   @nhost/react-apollo@5.0.8

## 0.12.2

### Patch Changes

-   4bf40995: chore(deps): bump `typescript` to `4.9.5`
-   8bb097c9: chore(deps): bump `vitest`
- 35d52aab: chore(deps): replace `cross-fetch` with `isomorphic-unfetch`
-   Updated dependencies [4bf40995]
-   Updated dependencies [8bb097c9]
-   Updated dependencies [35d52aab]
    -   @nhost/react-apollo@5.0.7
    -   @nhost/nextjs@1.13.12

## 0.12.1

### Patch Changes

-   c96d7ccd: fix(dashboard): fix docker builds

## 0.12.0

### Minor Changes

-   d1671210: feat(dashboard): use mimir to manage project configuration

### Patch Changes

-   f65e4de9: chore(deps): bump @graphql-codegen monorepo to v3

## 0.11.20

### Patch Changes

-   4b4f0d01: chore(dashboard): improve dialog management

## 0.11.19

### Patch Changes

-   @nhost/react-apollo@5.0.6
-   @nhost/nextjs@1.13.11

## 0.11.18

### Patch Changes

-   01318860: fix(nhost-js): use correct URL for functions requests
-   Updated dependencies [01318860]
    -   @nhost/react-apollo@5.0.5
    -   @nhost/nextjs@1.13.10

## 0.11.17

### Patch Changes

-   f673adea: fix(dashboard): set correct Content-Type for user creation
-   445d8ef4: chore(deps): bump `@nhost/react-apollo` to 5.0.4
-   445d8ef4: chore(deps): bump `@nhost/nextjs` to 1.13.9
- 0368663d: fix(dashboard): allow permission editing for auth and
storage schemas
-   Updated dependencies [445d8ef4]
-   Updated dependencies [445d8ef4]
    -   @nhost/react-apollo@5.0.4
    -   @nhost/nextjs@1.13.9

## 0.11.16

### Patch Changes

-   b755e908: fix(dashboard): use correct date for last seen
-   2d9145f9: chore(deps): revert GraphQL client
- 1ddf704c: fix(dashboard): don't show false positive message for failed
user creation
    -   @nhost/react-apollo@5.0.3
    -   @nhost/nextjs@1.13.8

## 0.11.15

### Patch Changes

-   @nhost/react-apollo@5.0.2
-   @nhost/nextjs@1.13.7

## 0.11.14

### Patch Changes

- 2cc18dcb: fix(dashboard): prevent permission editor dropdown from
being always open

## 0.11.13

### Patch Changes

- 3343a363: chore(dashboard): bump `@testing-library/react` to v14 and
`@testing-library/dom` to v9
    -   @nhost/react-apollo@5.0.1
    -   @nhost/nextjs@1.13.6

## 0.11.12

### Patch Changes

- 87eda76e: chore(dashboard): bump `@types/react` to v18.0.28 and
`@types/react-dom` to v18.0.11
-   6f0ac570: feat(dashboard): show dashboard version in account menu

## 0.11.11

### Patch Changes

-   bf1e4071: chore(dashboard): bump `react-is` version to `18.2.0`
-   Updated dependencies [bf1e4071]
-   Updated dependencies [5013213b]
    -   @nhost/nextjs@1.13.5
    -   @nhost/react-apollo@4.13.5

## 0.11.10

### Patch Changes

- a37a430b: fix(dashboard): don't break UI when deployments are
unavailable
    -   @nhost/react-apollo@4.13.4
    -   @nhost/nextjs@1.13.4

## 0.11.9

### Patch Changes

-   7b970e68: fix(dashboard): fix header link color

## 0.11.8

### Patch Changes

- f33242f2: feat(dashboard): add new sign up, sign in and reset password
pages

## 0.11.7

### Patch Changes

-   e9c8909c: fix(dashboard): use correct theme color in dark mode

## 0.11.6

### Patch Changes

-   902f486b: fix(dashboard): re-enable Hasura on logs page

## 0.11.5

### Patch Changes

-   1f9720fa: fix(dashboard): apply select permissions properly

## 0.11.4

### Patch Changes

-   deb14b51: fix(dashboard): don't break billing form

## 0.11.3

### Patch Changes

-   @nhost/react-apollo@4.13.3
-   @nhost/nextjs@1.13.3

## 0.11.2

### Patch Changes

-   f143e51d: chore(dashboard): pin Turborepo to 1.6.3

## 0.11.1

### Patch Changes

-   c2b5a41a: chore(dashboard): select system colors by default

## 0.11.0

### Minor Changes

-   1ebaf429: feat(dashboard): introduce Dark Mode 🌚

### Patch Changes

- 63b445c4: fixed duplicated logs bug and made to date count during live
mode

## 0.10.1

### Patch Changes

-   e146d32e: chore(deps): update dependency @types/react to v18.0.27
-   59347fcd: correct allowed role name
-   5b65cac9: updated authentication documentation
-   963f9b5e: feat(dashboard): include project info in feedback

## 0.10.0

### Minor Changes

-   ed4c7801: chore(dashboard): remove Functions section

## 0.9.10

### Patch Changes

-   4e2f8ccd: fix(dashboard): don't break Auth page in local mode

## 0.9.9

### Patch Changes

-   31abbe5f: fix(dashboard): enable toggle when settings are filled in

## 0.9.8

### Patch Changes

- 5bdd31ad: chore(dashboard): list fewer images per page on the Storage
page
- 5121851c: fix(dashboard): don't throw validation error for valid
permission rules

## 0.9.7

### Patch Changes

-   c126b20d: fix(dashboard): correct redeployment button

## 0.9.6

### Patch Changes

-   36c3519c: feat(dashboard): retrigger deployments

## 0.9.5

### Patch Changes

- 200e9f77: chore(deps): update dependency @types/react-dom to v18.0.10
-   Updated dependencies [200e9f77]
    -   @nhost/nextjs@1.13.2
    -   @nhost/react-apollo@4.13.2

## 0.9.4

### Patch Changes

- dbd3ded5: fix(dashboard): workspaces creation, new form, correct
redirects.

## 0.9.3

### Patch Changes

-   85f0f943: fix(dashboard): don't break the table creation process

## 0.9.2

### Patch Changes

-   Updated dependencies [d42c27ae]
-   Updated dependencies [927be4a2]
    -   @nhost/nextjs@1.13.1
    -   @nhost/react-apollo@4.13.1

## 0.9.1

### Patch Changes

-   d0f80811: fix(dashboard): don't show error when signing out the user

## 0.9.0

### Minor Changes

- d92891b2: feat(dashboard): add Permission Editor to the Database
section

### Patch Changes

-   3d379128: fix(dashboard): create new user
    -   @nhost/react-apollo@4.13.0
    -   @nhost/nextjs@1.13.0

## 0.8.1

### Patch Changes

-   7cadd944: fix(dashboard): display Twitter provider settings

## 0.8.0

### Minor Changes

-   9a1aa7bb: add functions to the log dashboard
-   f29abe62: feat(dashboard): Users Management v2

### Patch Changes

-   7766624b: feat(dashboard): add JWT secret editor modal
    -   @nhost/react-apollo@4.12.1
    -   @nhost/nextjs@1.12.1

## 0.7.13

### Patch Changes

-   dd0738d5: fix(dashboard): provisioning status polling

## 0.7.12

### Patch Changes

-   b21222b3: chore(deps): update dependency @types/node to v16
-   9e0486a3: fix(dashboard): close modals when navigating
-   Updated dependencies [b21222b3]
-   Updated dependencies [65687bee]
-   Updated dependencies [54df0df4]
    -   @nhost/nextjs@1.12.0
    -   @nhost/react-apollo@4.12.0

## 0.7.11

### Patch Changes

-   d6527122: fix(dashboard): use correct service URLs

## 0.7.10

### Patch Changes

-   Updated dependencies [57db5b83]
    -   @nhost/nextjs@1.11.0
    -   @nhost/nhost-js@1.7.0
    -   @nhost/react@0.17.0
    -   @nhost/react-apollo@4.11.0

## 0.7.9

### Patch Changes

- a6d31dc2: fix(dashboard): don't break the UI when project is not
loaded yet

## 0.7.8

### Patch Changes

- 7f251111: Use `NhostProvider` instead of `NhostReactProvider` and
`NhostNextProvider`

    `NhostReactProvider` and `NhostNextProvider` are now deprecated

-   f4d70f88: fix(dashboard): do not break when region is nullish

- 4a9471cc: Windows Live Provider displayed link updated to match
backend url

- 594488e4: fix(dashboard): do not show error when submitting Apple
provider settings

-   Updated dependencies [7f251111]
    -   @nhost/nextjs@1.10.0
    -   @nhost/react@0.16.0
    -   @nhost/react-apollo@4.10.0

## 0.7.7

### Patch Changes

-   80b604ad: fix(dashboard): use correct Hasura slug

## 0.7.6

### Patch Changes

-   2d2beb53: fix(dashboard): prevent error on GraphQL page
-   ac8efcbd: chore(dashboard): deprecate old DNS name

## 0.7.5

### Patch Changes

-   132a4f4b: chore(dashboard): remove unused dependencies
- 132a4f4b: chore(deps): synchronize @types/react-dom and @types/react
versions
-   db57572f: fix(dashboard): correct section paddings when no env vars
-   Updated dependencies [132a4f4b]
    -   @nhost/react@0.15.2
    -   @nhost/react-apollo@4.9.2
    -   @nhost/nextjs@1.9.3

## 0.7.4

### Patch Changes

-   34d85e54: chore(deps): update dependency critters to ^0.0.16
- 9b93cf95: chore(deps): update dependency @netlify/functions to ^0.11.0
-   e0439030: chore(deps): update dependency @types/react-dom to v18.0.9
-   Updated dependencies [82124329]
    -   @nhost/nextjs@1.9.2

## 0.7.3

### Patch Changes

-   a1193da4: fix(dashboard): remove character limit from env var inputs

## 0.7.2

### Patch Changes

-   44f13f62: chore(dashboard): cleanup unused files

## 0.7.1

### Patch Changes

- e01cb2ed: chore(dashboard): change settings sidebar menu item density

## 0.7.0

### Minor Changes

- db342f45: chore(dashboard): refactor Roles and Permissions settings
sections
-   8b9fa0b1: feat(dashboard): add Environment Variables page

### Patch Changes

-   Updated dependencies [66b4f3d0]
-   Updated dependencies [2e6923dc]
-   Updated dependencies [ef117c28]
-   Updated dependencies [aebb8225]
    -   @nhost/core@0.9.4
    -   @nhost/nhost-js@1.6.2
    -   @nhost/nextjs@1.9.1
    -   @nhost/react@0.15.1
    -   @nhost/react-apollo@4.9.1

## 0.6.0

### Minor Changes

-   eef9c914: feat(dashboard): add Roles and Permissions page

## 0.5.0

### Minor Changes

-   a48dd5bf: feat(dashboard): make backend port configurable

## 0.4.3

### Patch Changes

-   5de965d9: fix(dashboard): alphabetic ordering of providers
-   b9087a4a: fix(dashboard): console -> dashboard terminology
-   ca012d79: docs(workos): WorkOS Docs

## 0.4.2

### Patch Changes

-   89bd37bc: fix(dashboard): correct redirect URL input opacity
-   Updated dependencies [4601d84e]
-   Updated dependencies [843087cb]
    -   @nhost/react@0.15.0
    -   @nhost/nextjs@1.9.0
    -   @nhost/react-apollo@4.9.0

## 0.4.1

### Patch Changes

-   766cb612: fix(dashboard): correct redirect URL for oauth providers
-   Updated dependencies [53bdc294]
-   Updated dependencies [f2aaff05]
    -   @nhost/nextjs@1.8.3
    -   @nhost/core@0.9.3
    -   @nhost/react@0.14.3
    -   @nhost/nhost-js@1.6.1
    -   @nhost/react-apollo@4.8.3

## 0.4.0

### Minor Changes

-   9211743d: feat(dashboard): migrate Settings page features

## 0.3.0

### Minor Changes

-   73da6a67: fix(dashboard): avoid using BACKEND_URL locally

## 0.2.0

### Minor Changes

-   db118f97: feat(dashboard): generate Docker image

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-02-11 08:34:08 +01:00
David Barroso
403a45d2cf chore (dashboard): update schema (#3192)
### **PR Type**
Enhancement


___

### **Description**
- Update schema for resource configuration

- Remove replicas and autoscaler from Postgres

- Refactor initial resource retrieval logic

- Adjust GraphQL types for Postgres resources


___



### **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>ResourcesForm.tsx</strong><dd><code>Refactor initial
resource retrieval logic</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></summary>
<hr>


dashboard/src/features/orgs/projects/resources/settings/components/ResourcesForm/ResourcesForm.tsx

<li>Refactored <code>getInitialServiceResources</code> function<br> <li>
Added checks for <code>replicas</code> and <code>autoscaler</code>
properties<br> <li> Initialized <code>replicas</code> to 1 if not
present


</details>


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

</tr>

<tr>
  <td>
    <details>
<summary><strong>ResourcesForm.tsx</strong><dd><code>Update resource
form logic and submission</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; </dd></summary>
<hr>


dashboard/src/features/projects/resources/settings/components/ResourcesForm/ResourcesForm.tsx

<li>Updated <code>getInitialServiceResources</code> function similar to
org version<br> <li> Removed <code>replicas</code> and
<code>autoscaler</code> from database resources in form <br>submission


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3192/files#diff-6d00a7b503dbd4b76f86d3949458d7f0bd62622cf17c523e0d668e3b459b67b5">+11/-7</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td>
    <details>
<summary><strong>graphql.ts</strong><dd><code>Update GraphQL types for
Postgres resources</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

dashboard/src/utils/__generated__/graphql.ts

<li>Updated <code>ConfigPostgresResources</code> type<br> <li> Removed
<code>replicas</code>, <code>autoscaler</code>, and
<code>networking</code> fields<br> <li> Added
<code>ConfigPostgresResourcesStorage</code> type<br> <li> Updated
related comparison, insert, and update types


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3192/files#diff-fbd5db84b560b1c91675004448c6c7fa0dcbfb28b9eb05d53b03e6cb7b83ebac">+28/-46</a>&nbsp;
</td>

</tr>

<tr>
  <td>
    <details>
<summary><strong>getResources.gql</strong><dd><code>Update Postgres
resources GraphQL query</code>&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/projects/resources/settings/gql/getResources.gql

<li>Removed <code>replicas</code> and <code>autoscaler</code> fields
from Postgres resources query


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3192/files#diff-45c2f030236a2836bd4ba61e46a20bc0b40f2ab08874c056c49b285a9c2c80eb">+0/-4</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 <robert@nhost.io>
2025-02-11 08:29:06 +01:00
David BM
05f063b8e2 fix (dashboard): undefined is not an object in logs page (#3139)
### **PR Type**
Bug fix


___

### **Description**
- Fixed undefined object evaluation in logs page

- Improved error handling in LogsHeader component

- Enhanced data loading checks in LogsPage

- Refactored imports for better organization


___



### **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>LogsHeader.tsx</strong><dd><code>Improve error handling
in LogsHeader component</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>


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

<li>Added null check for <code>data</code> in useEffect hooks<br> <li>
Reordered imports for better organization


</details>


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

</tr>

<tr>
  <td>
    <details>
<summary><strong>logs.tsx</strong><dd><code>Enhance data loading and
error handling in LogsPage</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; </dd></summary>
<hr>

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

<li>Added <code>loadingProject</code> to useProject hook<br> <li>
Destructured <code>loadingLogs</code> from useGetProjectLogsQuery<br>
<li> Created combined <code>loading</code> state<br> <li> Fixed
potential undefined <code>project</code> in subscription


</details>


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

</tr>
</table></td></tr><tr><td><strong>Documentation</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>empty-dancers-do.md</strong><dd><code>Add changeset for
bug fix in logs page</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

.changeset/empty-dancers-do.md

<li>Added changeset file for version bump<br> <li> Described fix for
undefined object in logs page


</details>


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

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

___

> 💡 **PR-Agent usage**: Comment `/help "your question"` on any pull
request to receive relevant information
2025-02-06 21:23:03 +01:00
github-actions[bot]
09f5bed1e8 chore: update versions (#3187)
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.19.0

# @nhost/dashboard

## 2.17.0

### Minor Changes

-   fd59918: fix: redirect to 404 with nhost cli dashboard

## 2.16.0

### Minor Changes

-   f8e6b61: fix: can add rule groups in table permissions
-   9e404c8: fix: not redirect to 404 page if using local Nhost backend
-   ac4aa01: fix: can delete column in database page
-   4385524: fix: update url to check service health in local dashboard

### Patch Changes

-   @nhost/react-apollo@16.0.1
-   @nhost/nextjs@2.2.2

## 2.15.0

### Minor Changes

- f1052a8: fix: improve stability of the dashboard when pausing projects
-   30daa41: fix: update links to docs in overview page
-   7537237: feat: add image preview toggle in storage

## 2.14.0

### Minor Changes

- d43931e: fix: invalid organization slug/project subdomain doesn't open
404 page
- 5df6fa2: feat: add unencrypted disk warning in storage capacity
settings

### Patch Changes

-   44c1e17: chore: update `msw` to v1.3.5 to fix vulnerabilities
    -   @nhost/react-apollo@16.0.0
    -   @nhost/nextjs@2.2.1

## 2.13.0

### Minor Changes

- 21e90da: chore: remove restrictions on SMTP sender so My Name
[name@acme.com](mailto:name@acme.com) can be added
- 865dd93: fix: duplicate Run placeholders when there is an error in the
backend
- 6902a36: fix: can remove resources if postgres capacity is higher than
10
-   a535aa3: fix: fetch user roles locally in auth section
-   0c50816: fix: allow decimal numbers in database row insert
- aea6d18: chore: add warning when pausing a project about losing Run
services persistent volume data
- d3b4fc3: feat: allow to change postgres settings if project is paused
-   29d27e1: chore: update `next` to v14.2.22 to fix vulnerabilities
-   c9dca09: feat: add reset password form
-   b3bcacb: fix: paused project banner cannot read null project name

### Patch Changes

-   Updated dependencies [46fc520]
-   Updated dependencies [29d27e1]
    -   @nhost/nextjs@2.2.0
    -   @nhost/react-apollo@15.0.1

## 2.12.0

### Minor Changes

- eb95562: fix: show all available permission variables in permission
dropdown select

### Patch Changes

- 8b5c4a0: chore: cleanup layout and add disable duplicate atom key
checking in development mode

## 2.11.3

### Patch Changes

- 714dffa: fix: improve project polling logic and unify usage across
components

## 2.11.2

### Patch Changes

- 6a34f89: fix: improve project polling logic and unify usage across
components

## 2.11.1

### Patch Changes

-   0f6ce52: fix: consolidate useProject hook and fix jwt expired error

## 2.11.0

### Minor Changes

-   cea3ef5: Feat: add org and project placeholders

## 2.10.0

### Minor Changes

-   86ecf27: feat: add support for additional metrics in overview
- 21708be: feat: dashboard: add support for storage buckets to AI
assistants

## 1.30.0

### Minor Changes

- 50441a8: feat: add ui for project autoscaler settings and run services
autoscaler settings

## 1.29.0

### Minor Changes

-   55d8bb5: feat: integrate turnstile for signup verification
-   2a2e54c: fix: update docs url in run services form tooltip
- 18f942f: fix: display long error messages in error toast without
overflow

### Patch Changes

-   @nhost/react-apollo@13.0.0
-   @nhost/nextjs@2.1.22

## 1.28.2

### Patch Changes

- 52a38fe: chore: update dependencies to address security
vulnerabilities
-   Updated dependencies [52a38fe]
    -   @nhost/nextjs@2.1.21

## 1.28.1

### Patch Changes

-   9735fa2: chore: remove broken link

## 1.28.0

### Minor Changes

- 526183a: feat: allow filtering users in "make request as" in graphql
section
-   be3b85b: feat: add conceal errors toggle on auth settings page

### Patch Changes

- 35a2f12: fix: prevent run service details from opening when attempting
to delete
    -   @nhost/react-apollo@12.0.6
    -   @nhost/nextjs@2.1.20

## 1.27.0

### Minor Changes

-   a7cd02c: fix: resolve rate limit query

## 1.26.0

### Minor Changes

-   3773ad7: chore: update pricing information
- b63250d: fix: not allow run service creation form resubmission while
creating a run service
-   a44a1d4: feat: add rate limits settings page

### Patch Changes

-   @nhost/react-apollo@12.0.5
-   @nhost/nextjs@2.1.19

## 1.25.0

### Minor Changes

- d1ceede: feat: add setting to migrate postgres major and/or minor
versions
- e5d3d1a: fix: allow manually typing column for custom check in
database row permissions

### Patch Changes

-   @nhost/react-apollo@12.0.4
-   @nhost/nextjs@2.1.18

## 1.24.1

### Patch Changes

- 49f2e55: fix: use service subdomain in service form and service
details dialog
- 598b988: fix: use current project subdomain in ServiceDetailsDialog
component

## 1.24.0

### Minor Changes

-   abb24af: chore: add redirect to support page when project is locked
- 18a6455: feat: show contact us info and locked reason when project is
locked

### Patch Changes

-   e31eefa: fix: include ingresses field when updating run services

## 1.23.0

### Minor Changes

-   33284d3: fix: don't show double scrollbar in configuration editor

### Patch Changes

-   @nhost/react-apollo@12.0.3
-   @nhost/nextjs@2.1.17

## 1.22.0

### Minor Changes

-   998c037: fix: align drop-down list in select component
- 807b8c0: fix: show city name in region selection for project creation

## 1.21.0

### Minor Changes

- a2efeed: fix: improve project health error handling, add unknown state
and polling interval for health state

## 1.20.0

### Minor Changes

- 8ea4210: fix: error toasts can be closed individually, instead of
dismissing all toasts at once
- 58919ba: chore: add blink animation when project health service is
updating

## 1.19.0

### Minor Changes

- b519862: fix: get configuration in configuration editor using local
development environment

## 1.18.0

### Minor Changes

- 502abad: feat: add services health checks indicators to the overview
page
-   b3ff6ad: chore: update title text on service status modal
- dbadf59: feat: add project configuration TOML editor to the settings
page

## 1.17.0

### Minor Changes

- 77fba27: fix: postgres version validation when activating ai in ai
settings page
-   ac6d1b6: feat: use name instead of awsName

## 1.16.3

### Patch Changes

- 87a37cf: fix: remove unnecessary isPlatform check from verify button
disable logic on custom domains
    -   @nhost/react-apollo@12.0.2
    -   @nhost/nextjs@2.1.16

## 1.16.2

### Patch Changes

- a9413af: fix: update `GetAllWorkspacesAndProjects` query polling to
use exponential backoff
    -   @nhost/react-apollo@12.0.1
    -   @nhost/nextjs@2.1.15

## 1.16.1

### Patch Changes

-   @nhost/react-apollo@12.0.0
-   @nhost/nextjs@2.1.14

## 1.16.0

### Minor Changes

- c6d5c5c: feat: add toggle switch to enable/disable public access in
the database settings

## 1.15.2

### Patch Changes

-   @nhost/react-apollo@11.0.4
-   @nhost/nextjs@2.1.13

## 1.15.1

### Patch Changes

-   @nhost/react-apollo@11.0.3
-   @nhost/nextjs@2.1.12

## 1.15.0

### Minor Changes

-   a7bde37: feat: send metadata in the edit form

### Patch Changes

- 1bc615b: feat: improve error message handling in `ErrorToast`
component
    -   @nhost/react-apollo@11.0.2
    -   @nhost/nextjs@2.1.11

## 1.14.0

### Minor Changes

-   a448d7d: feat: allow configuring postmark and delete SMTP settings

## 1.13.3

### Patch Changes

-   5924bc3: fix: include password in `GetSmtpSettings` query
- c5ad634: fix: resolved an issue where one-click install links were
broken on Safari
- 7278991: fix: update graphql auto-embeddings configuration to use
String type for model field

## 1.13.2

### Patch Changes

-   026f84f: fix: use configuration server URL from environment variable

## 1.13.1

### Patch Changes

-   7e9a2ce: fix: resolve issue where run services form fails to open

## 1.13.0

### Minor Changes

-   dd5d262: feat: add model field to the auto-embeddings form
- 09962be: feat: enable settings and run services when running the
dashboard locally
- 9cdecb6: feat: enable users to update their email address from the
account settings page

## 1.12.2

### Patch Changes

-   c195c51: fix: send email upon signin for unverified users

## 1.12.1

### Patch Changes

- 93ebdf8: fix: use service urls when initilizaing NhostClient running
local dashboard
    -   @nhost/react-apollo@11.0.1
    -   @nhost/nextjs@2.1.10

## 1.12.0

### Minor Changes

- f242e4b: feat: add connect with github to the user's account settings
-   768ca17: chore: update dependencies
- d62bd0f: fix: "Track this" option within the SQL editor now correctly
updates the metadata
- 91c2bb6: feat: refactor sign-in and sign-up pages to enforce email
verification

### Patch Changes

-   943831f: fix: resolve an error toast issue when unpausing a project
-   Updated dependencies [768ca17]
    -   @nhost/react-apollo@11.0.0
    -   @nhost/nextjs@2.1.9

## 1.11.2

### Patch Changes

-   @nhost/react-apollo@10.0.2
-   @nhost/nextjs@2.1.8

## 1.11.1

### Patch Changes

-   981404f: fix: set default value for healthCheck field validation

## 1.11.0

### Minor Changes

- 7789469: chore: upgrade dependency `@graphql-codegen/cli` to `5.0.2`
to address vulnerability
- 6c11b75: feat: add update user displayName section in account settings

### Patch Changes

-   @nhost/react-apollo@10.0.1
-   @nhost/nextjs@2.1.7

## 1.10.0

### Minor Changes

-   49a80c2: chore: update dependencies
-   150c04a: feat: add healthcheck config to run services

### Patch Changes

- e03f141: fix: allow insert, update and delete on tables in `auth` and
`storage` schemas
- 28676f4: feat: add min postgres version check to enable the ai service
-   Updated dependencies [49a80c2]
    -   @nhost/react-apollo@10.0.0
    -   @nhost/nextjs@2.1.6

## 1.9.0

### Minor Changes

-   d86e5c9: feat: add support for filtering the logs using a RegExp

## 1.8.3

### Patch Changes

-   @nhost/react-apollo@9.0.3
-   @nhost/nextjs@2.1.5

## 1.8.2

### Patch Changes

- 6df4f02: fix: use custom error toast and show correct message when
sending an invite

## 1.8.1

### Patch Changes

-   @nhost/react-apollo@9.0.2
-   @nhost/nextjs@2.1.4

## 1.8.0

### Minor Changes

- 713d53c: feat: add catch-all route for workspace/project - useful for
documentation

### Patch Changes

-   3db2999: fix: refresh table list after running SQL using the editor
- 3c4dd55: fix: handle `Error` objects properly in the `ErrorToast`
component
- 92b434e: fix: resolve an issue where the checkbox in the data-grid
header did not select all rows
    -   @nhost/react-apollo@9.0.1
    -   @nhost/nextjs@2.1.3

## 1.7.0

### Minor Changes

-   0d8d0eb: Update docs and dashboard references

## 1.6.9

### Patch Changes

-   @nhost/react-apollo@9.0.0
-   @nhost/nextjs@2.1.2

## 1.6.8

### Patch Changes

-   @nhost/react-apollo@8.0.1
-   @nhost/nextjs@2.1.1

## 1.6.7

### Patch Changes

-   5ef5189: fix: update `@apollo/client` to `3.9.4` to fix a cache bug

## 1.6.6

### Patch Changes

-   3ba485e: fix: added discord.com to connect-src
-   e5bab6a: chore: update dependencies
-   Updated dependencies [b19ffed]
-   Updated dependencies [e5bab6a]
    -   @nhost/nextjs@2.1.0
    -   @nhost/react-apollo@8.0.0

## 1.6.5

### Patch Changes

- ba73bb4: fix: update ErrorToast component to show the internal graphql
error
- d5337ff: fix: utilize accumulator in the creation of validation schema
within data grid utils

## 1.6.4

### Patch Changes

-   7c2a1c2: feat: show error and debug info in the error toast

## 1.6.3

### Patch Changes

-   6b8aad5: fix: add bare nhost.run to CSP

## 1.6.2

### Patch Changes

-   b18edc0: feat: added CSP and X-Frame-Options

## 1.6.1

### Patch Changes

-   8d91f71: chore: update deps and enable pnpm audit
- 3b8473b: chore: update turbo to `1.11.3` and pnpm to `8.10.5` in
Dockerfile
-   Updated dependencies [8d91f71]
    -   @nhost/react-apollo@7.0.2
    -   @nhost/nextjs@2.0.2

## 1.6.0

### Minor Changes

-   3ff1c2b53: fix: show upgrade option for pro projects

## 1.5.0

### Minor Changes

-   c2ef17c0a: feat: add support for new Team plan

## 1.4.0

### Minor Changes

-   7883bbcbd: feat: don't show deprecated plans
- 44be6dc0a: feat: set redirectTo during sign-in to support preview
environments

### Patch Changes

- 3c3594898: fix: allow access to graphite when configured running in
local dashboard
-   32c246b7a: chore: update docs icon

## 1.3.2

### Patch Changes

-   174b4165b: chore: use env variables when running graphql codegen
-   7c977e714: chore: change `Allowed Roles` to `Default Allowed Roles`
-   46f028b9f: fix: remove hardcoded ai version setting

## 1.3.1

### Patch Changes

- af33c21d1: chore: remove backendUrl deprecation notice and remove all
references to `providersUpdated`

## 1.3.0

### Minor Changes

-   04784d880: Fix graphite's default version

## 1.2.0

### Minor Changes

-   5733162ed: feat: add settings and ui for graphite

## 1.1.0

### Minor Changes

-   e2b79b5ec: chore: remove sharp from deps

## 1.0.1

### Patch Changes

-   @nhost/react-apollo@7.0.1
-   @nhost/nextjs@2.0.1

## 1.0.0

### Major Changes

- bc9eff6e4: chore: remove support for using backendUrl when
instantiating the Nhost client

### Patch Changes

-   Updated dependencies [bc9eff6e4]
    -   @nhost/nextjs@2.0.0
    -   @nhost/react-apollo@7.0.0

## 0.21.1

### Patch Changes

-   97ced73a3: fix(dashboard): prevent dashboard from resolving secrets

## 0.21.0

### Minor Changes

- ed1a8d458: Update alert message on increasing PostgreSQL's volume
capacity
-   2e2248fd4: feat(dashboard): add SQL editor

## 0.20.28

### Patch Changes

-   7c2c31082: feat: add support for users to delete their account
    -   @nhost/react-apollo@6.0.1
    -   @nhost/nextjs@1.13.40

## 0.20.27

### Patch Changes

- fa79b7709: chore(dashboard): tweaks and fixes to the service form and
dialog
-   8df84d782: fix(dashboard): allow resetting custom domains
    -   @nhost/react-apollo@6.0.0
    -   @nhost/nextjs@1.13.39

## 0.20.26

### Patch Changes

- 331ba0376: feat(dashboard): add postgres storage capacity modifier in
the settings
-   b7f801874: feat(dashboard): add new settings page for custom domains

## 0.20.25

### Patch Changes

-   @nhost/react-apollo@5.0.38

## 0.20.24

### Patch Changes

-   e10389ecf: fix(dashboard): disable run tab when developing locally
    -   @nhost/react-apollo@5.0.37

## 0.20.23

### Patch Changes

-   c01568a7d: chore(dashboard): show alert to update oauth providers

## 0.20.22

### Patch Changes

-   c3efb7ec8: feat(dashboard): query latest announcement from platform

## 0.20.21

### Patch Changes

-   3e46d3873: chore: update link to node18 announcement

## 0.20.20

### Patch Changes

-   @nhost/react-apollo@5.0.36
-   @nhost/nextjs@1.13.38

## 0.20.19

### Patch Changes

-   75c4c8ae3: feat(dashboard): make env value input multiline

## 0.20.18

### Patch Changes

- 425d485f8: fix(dashboard): make sure dedicated resources pricing
follows total resources

## 0.20.17

### Patch Changes

-   ae324f67f: fix(dashboard): remove unused graphql fields

## 0.20.16

### Patch Changes

-   df5b4302c: chore(dashboard): remove run feature flag
- bf4a1f6c2: feat(dashboard): fetch auth, postgres, hasura and storage
versions from dashboard
- 34fc08ca7: fix(dashboard/run): show correct private registry in
service details
-   885d10620: chore(dashboard): change feedback to contact us

## 0.20.15

### Patch Changes

- ed16c8b5d: feat(run): add a confirmation dialog when deleting a run
service
- 216990888: fix(run): center loading indicator when selecting a project

## 0.20.14

### Patch Changes

-   9fbea9787: feat: add node18 announcement

## 0.20.13

### Patch Changes

- e84acf469: fix(run): handle subdomain undefined error when creating a
new service

## 0.20.12

### Patch Changes

-   b7c799d62: feat(run): add dialog to copy registry and URLs

## 0.20.11

### Patch Changes

-   8903e6abd: fix(dashboard): show correct egress limit in usage stats

## 0.20.10

### Patch Changes

- 666a75a23: feat(dashboard): add functions execution time and egress
volume to usage stats

## 0.20.9

### Patch Changes

-   5e1e80aa8: fix(dashboard): show correct locales in user details
    -   @nhost/react-apollo@5.0.35
    -   @nhost/nextjs@1.13.37

## 0.20.8

### Patch Changes

-   @nhost/react-apollo@5.0.34
-   @nhost/nextjs@1.13.36

## 0.20.7

### Patch Changes

-   4a7ede11e: fix: distinguish files that were not uploaded
- 202b64723: feat(nhost-run): add support for one-click-install run
services
- 074a0fa11: feat(dashboard): add settings toggle to enable/disable
antivirus
    -   @nhost/react-apollo@5.0.33
    -   @nhost/nextjs@1.13.35

## 0.20.6

### Patch Changes

-   b20761e97: feat(services): add pricing info and confirmation dialog
-   90df6d81d: fix(services): handle null values when editing a service
-   aa8508467: fix: query service logs correctly
    feat: enable multiline support for environment value input

## 0.20.5

### Patch Changes

-   8d7f84b8d: fix: make announcement adapt to theme

## 0.20.4

### Patch Changes

-   3b75bfce2: fix: make announcement close properly
- f49819075: fix: show correct values when dedicated resources are
disabled

## 0.20.3

### Patch Changes

-   e643bd362: fix(services): fix errors when config is null
-   bcdab66bf: feat: add annoucement for nhost run
-   f967a2e59: added note about storage not being able to be downsized
-   311c7756d: chore(services): consistent naming for compute

## 0.20.2

### Patch Changes

-   9073182d5: chore(dashboard): bump `turbo` to 1.10.11
-   ece717d6e: feat(logs): show services in the logs page
- 82b335311: feat(metrics): change grafana link to point to the
dashboards
- b135ef695: fix(services): set command as optional and set min replicas
to 0

## 0.20.1

### Patch Changes

-   3d5c34f4c: fix(auth): fix users pagination limit

## 0.20.0

### Minor Changes

-   c99d117d1: feat(services): add support for custom services

## 0.19.2

### Patch Changes

-   face99ccd: chore(deps): bump turbo version
-   cfe527307: style: tweak pull config warning in dark mode
- a9d7da8af: chore(deps): update dependency @types/pluralize to ^0.0.30
-   9aa4371ef: chore: add hasura-auth version 0.21.2
- d14e112bf: chore(deps): update dependency prettier-plugin-tailwindcss
to ^0.4.0
-   d3e8bb94a: chore(deps): update dependency vite-plugin-dts to v3

## 0.19.1

### Patch Changes

-   @nhost/react-apollo@5.0.32
-   @nhost/nextjs@1.13.34

## 0.19.0

### Minor Changes

- 9c61c69a7: chore(dashboard):add postgres 14.6-20230705-1 to the
version selector

### Patch Changes

-   47bda15ff: feat(settings): add warning to pull config

## 0.18.0

### Minor Changes

- ee0b9b8ed: chore(dashboard):add hasura v2.28.2 and v2.29.0 to the
version selector

## 0.17.20

### Patch Changes

-   @nhost/react-apollo@5.0.31
-   @nhost/nextjs@1.13.33

## 0.17.19

### Patch Changes

-   f866120a6: fix(users): use the password length from the config

## 0.17.18

### Patch Changes

-   @nhost/react-apollo@5.0.30
-   @nhost/nextjs@1.13.32

## 0.17.17

### Patch Changes

-   ea7b102c0: fix(pat): highlight expired tokens

## 0.17.16

### Patch Changes

- b3b64a3b7: chore(deps): bump `@types/react` to `v18.2.14` and
`@types/react-dom` to `v18.2.6`
-   32b221f94: chore(deps): bump `graphiql` to `v3`
-   3a56c12df: chore(deps): bump `turbo` to `v1.10.6`
-   Updated dependencies [b3b64a3b7]
    -   @nhost/react-apollo@5.0.29
    -   @nhost/nextjs@1.13.31

## 0.17.15

### Patch Changes

-   f41fdc12a: chore(deps): bump `turbo` to `1.10.5`
-   6199c1c55: fix(projects): don't redirect to 404 page
-   Updated dependencies [07a45fde0]
    -   @nhost/react-apollo@5.0.28
    -   @nhost/nextjs@1.13.30

## 0.17.14

### Patch Changes

- 80b22724d: chore(deps): bump `@types/react` to `v18.2.13`,
`@types/react-dom` to `v18.2.6` and `@storybook/testing-library` to
`v0.2.0`

## 0.17.13

### Patch Changes

-   cc02902cb: chore(docs): update environment variable documentation

## 0.17.12

### Patch Changes

-   660d339e1: fix(storybook): don't break storybook
-   660d339e1: fix(tests): prevent warnings during tests
    -   @nhost/react-apollo@5.0.27
    -   @nhost/nextjs@1.13.29

## 0.17.11

### Patch Changes

- bd4d0c270: chore(dashboard):add postgres 14.6-20230613-1 to the
version selector

## 0.17.10

### Patch Changes

-   c8c2a10b2: fix(database): don't break the password reset flow
- e70b45498: chore(deps): bump `@types/react` to `v18.2.12` and
`@types/react-dom` to `v18.2.5`

## 0.17.9

### Patch Changes

- 842055099: chore(deps): bump `turbo` to `v1.10.3` and `pnpm` to
`v8.6.2`
- fd12aa0a8: chore(projects): remove the postgres password input from
the project creation screen
-   022b76e78: chore(deps): bump `@types/react` to `v18.2.11`
-   3555ab2b7: chore(deps): bump `vitest` monorepo to `v0.32.0`
-   c43e54922: feat(backups): add download button to backups

## 0.17.8

### Patch Changes

-   d0457fe5c: feat(settings): improve the dashboard and config parity
    -   @nhost/react-apollo@5.0.26
    -   @nhost/nextjs@1.13.28

## 0.17.7

### Patch Changes

-   4f0368b95: fix(account): don't break account settings page

## 0.17.6

### Patch Changes

- 64a8f41d0: chore(resources): lower the maximum allowed resources per
service

## 0.17.5

### Patch Changes

-   @nhost/react-apollo@5.0.25
-   @nhost/nextjs@1.13.27

## 0.17.4

### Patch Changes

- 9b1d0f7a5: fix(deployments): use correct timestamp for deployment
details
-   6d2963ffa: chore(deps): bump `@types/react` to `v18.2.8`
- 8871267b9: chore(deps): downgrade `pnpm` to `v8.5.1` because of no
Turborepo support

## 0.17.3

### Patch Changes

-   01eeef9de: chore(misc): under the hood improvements
- 21e13db05: chore(deps): bump `@types/react` to `v18.2.7` and `turbo`
to `v1.10.1`
- f16433ae6: chore(secrets): allow empty secrets and environment
variables
-   aa3c62989: chore(cli): bump Nhost CLI version to v1.0
    -   @nhost/react-apollo@5.0.24
    -   @nhost/nextjs@1.13.26

## 0.17.2

### Patch Changes

-   88a4983f: chore(misc): under the hood improvements

## 0.17.1

### Patch Changes

-   9b0d4dde: feat(secrets): enable secrets

## 0.17.0

### Minor Changes

-   15d84a19: Add postgres 14.6-20230525

## 0.16.14

### Patch Changes

-   4c626174: chore: updated import paths, improved directory structure
-   cc047b71: chore(deps): bump `@fontsource` monorepo to `v5.0.0`
-   99edd012: feat(account): add support for personal access tokens

## 0.16.13

### Patch Changes

-   78c7109c: feat(settings): allow selecting service versions

## 0.16.12

### Patch Changes

- 399009d6: fix(gql): don't enter an infinite loop when fetching remote
app data
- 329e5a91: fix(deployments): use the same sorting of deployments
everywhere
- 6d559d6e: chore(settings): add under the hood improvements to the
settings page
- 12eb236c: chore(deps): bump `prettier-plugin-tailwindcss` to `v0.3.0`
-   f9b81a2a: chore(deps): bump `turbo` to `v1.9.8`
-   1345741b: fix(projects): don't redirect to 404 on project creation
-   Updated dependencies [7fea29a8]
    -   @nhost/react-apollo@5.0.23
    -   @nhost/nextjs@1.13.25

## 0.16.11

### Patch Changes

- 1230b722: fix(projects): don't redirect to 404 on when the project is
renamed
    -   @nhost/react-apollo@5.0.22
    -   @nhost/nextjs@1.13.24

## 0.16.10

### Patch Changes

-   Updated dependencies [da03bf39]
    -   @nhost/react-apollo@5.0.21
    -   @nhost/nextjs@1.13.23

## 0.16.9

### Patch Changes

- 349aac36: fix(settings): use region domain when constructing the
postgres connection string

## 0.16.8

### Patch Changes

- 20fb69fa: chore(projects): change the way how API URLs are constructed

## 0.16.7

### Patch Changes

- 49f9b837: chore(docker): bump `pnpm` to `v8.4.0` and `turbo` to
`v1.9.3`
- 3f478a4e: chore(deps): bump `vitest` to `v0.31.0`, `@types/react` to
`v18.2.6` and `@types/react-dom` to `v18.2.4`

## 0.16.6

### Patch Changes

- d926f156: fix(projects): redirect to 404 when an invalid project is
opened
- 49b99728: fix(projects): disable features for non-owner members of
workspaces

## 0.16.5

### Patch Changes

-   12e2855f: chore(deps): bump `jsdom` to v22
-   e4972b83: feat(metrics): add Grafana page

## 0.16.4

### Patch Changes

- 3f396a9e: fix(projects): unpause after upgrading a paused project to
pro
- 3f396a9e: fix(projects): don't redirect to 404 page after project
creation

## 0.16.3

### Patch Changes

-   Updated dependencies [90c60311]
    -   @nhost/react-apollo@5.0.20
    -   @nhost/nextjs@1.13.22

## 0.16.2

### Patch Changes

-   0f34f0c6: fix(projects): disallow downgrading to free plan
- 8da291ad: chore(deps): bump `@types/react` to v18.2.0 and
`@types/react-dom` to v18.2.1

## 0.16.1

### Patch Changes

- adc828a5: fix(gql): don't enter an infinite loop when fetching remote
app data

## 0.16.0

### Minor Changes

-   2fb1145f: feat(compute): add support for replicas

### Patch Changes

- d8ceccec: chore(env): remove deprecated `NHOST_BACKEND_URL`
environment variable

## 0.15.2

### Patch Changes

-   84b84ab7: fix(projects): filter projects by workspace

## 0.15.1

### Patch Changes

-   2faf7907: chore(deps): bump `graphql-request` to v6
-   f1b5a944: chore(deps): bump `@vitejs/plugin-react` to v4
-   7f1785ac: chore(deps): bump `@types/react` to v18.0.37
    -   @nhost/react-apollo@5.0.19

## 0.15.0

### Minor Changes

-   85889ee8: feat(dashboard): add Compute management to the settings

## 0.14.8

### Patch Changes

-   668c8771: chore(dialogs): unify dialog management of payment dialogs

## 0.14.7

### Patch Changes

-   d4ccc656: chore: cleanup unused code
    -   @nhost/react-apollo@5.0.18
    -   @nhost/nextjs@1.13.21

## 0.14.6

### Patch Changes

-   b299cfc9: chore(deps): bump `vitest` to v0.30.0
-   411cb65b: chore(projects): refactor workspace and project hooks
- 43b1b144: chore(deps): bump `@types/react` to v18.0.34 and
`@types/react-dom` to v18.0.11
-   Updated dependencies [43b1b144]
    -   @nhost/react-apollo@5.0.17
    -   @nhost/nextjs@1.13.20

## 0.14.5

### Patch Changes

-   ba0d57ee: fix(i18n): revert i18n library
-   3328ed05: feat(projects): improve overview when there is an error

## 0.14.4

### Patch Changes

-   5e0920ba: chore(deps): bump `next-seo` to v6
-   706c9dc3: chore(deps): bump `@types/react` to 18.0.33
-   99f8f6b3: feat(metrics): show metrics on the overview

## 0.14.3

### Patch Changes

-   @nhost/react-apollo@5.0.16

## 0.14.2

### Patch Changes

-   3cb67300: fix(logs): don't break UI when clearing time picker
-   7453bf3b: feat(projects): show project creator info
-   c166dad0: chore(tests): improve auth page tests
-   6a290bb2: chore(deps): bump `@types/react` to 18.0.32

## 0.14.1

### Patch Changes

-   @nhost/react-apollo@5.0.15
-   @nhost/nextjs@1.13.19

## 0.14.0

### Minor Changes

-   6e1f03ea: feat(dashboard): add support for the Azure AD provider

### Patch Changes

-   1bd2c373: chore(deps): bump `turbo` to 1.8.6
-   d329b621: chore(deps): bump `@types/react` to 18.0.30
-   cb248f0d: fix(tests): avoid name collision in database tests
-   867c8076: chore(deps): bump `@types/react` to 18.0.29

## 0.13.10

### Patch Changes

- e93b06ab: fix(dashboard): remove left margin from workspace list on
mobile
-   1c4806bf: chore(deps): bump `sharp` to 0.32.0
    -   @nhost/react-apollo@5.0.14
    -   @nhost/nextjs@1.13.18

## 0.13.9

### Patch Changes

-   912ed76c: chore(dashboard): bump `@apollo/client` to 3.7.10
-   Updated dependencies [912ed76c]
    -   @nhost/react-apollo@5.0.13

## 0.13.8

### Patch Changes

-   7c127372: chore(dashboard): bump `react-error-boundary` to v4

## 0.13.7

### Patch Changes

- 9130ab12: chore(dashboard): bump `yup` to v1 and `@hookform/resolvers`
to v3

## 0.13.6

### Patch Changes

- 253dd235: using new mutation to create projects + refactor Create
Project page.

## 0.13.5

### Patch Changes

-   @nhost/react-apollo@5.0.12
-   @nhost/nextjs@1.13.17

## 0.13.4

### Patch Changes

-   b48bc034: fix(dashboard): disable new users
-   798e591b: fix(dashboard): show correct date in data grid

## 0.13.3

### Patch Changes

-   bfb4c1a6: chore(dashboard): remove `useAxios` property
-   d8d8394b: Dashboard: allow to override hasura admin secret in docker
-   Updated dependencies [ce1ee40d]
    -   @nhost/nextjs@1.13.16
    -   @nhost/react-apollo@5.0.11

## 0.13.2

### Patch Changes

-   beed2eba: Fix docker entrypoint for dashboard
- 2c8559a3: fix(dashboard): refresh project list after deleting a
project
-   4329d048: chore(dashboard): bump `graphiql` dependencies

## 0.13.1

### Patch Changes

-   cbb1fc5b: chore(dashboard): cleanup GraphQL operations

## 0.13.0

### Minor Changes

-   088584e7: feat(dashboard): add support for custom local subdomains

### Patch Changes

-   2ac90dfd: fix(dashboard): improve mobile responsive layout
-   Updated dependencies [f375eacc]
    -   @nhost/nextjs@1.13.15
    -   @nhost/react-apollo@5.0.10

## 0.12.4

### Patch Changes

-   @nhost/react-apollo@5.0.9
-   @nhost/nextjs@1.13.14

## 0.12.3

### Patch Changes

-   2b1338f7: chore(dashboard): bump `turbo` to 1.8.3
- 5223ee93: fix(dashboard): show correct deployment status on the main
page
-   850a049c: chore(deps): update docker/build-push-action action to v4
-   Updated dependencies [850a049c]
    -   @nhost/nextjs@1.13.13
    -   @nhost/react-apollo@5.0.8

## 0.12.2

### Patch Changes

-   4bf40995: chore(deps): bump `typescript` to `4.9.5`
-   8bb097c9: chore(deps): bump `vitest`
- 35d52aab: chore(deps): replace `cross-fetch` with `isomorphic-unfetch`
-   Updated dependencies [4bf40995]
-   Updated dependencies [8bb097c9]
-   Updated dependencies [35d52aab]
    -   @nhost/react-apollo@5.0.7
    -   @nhost/nextjs@1.13.12

## 0.12.1

### Patch Changes

-   c96d7ccd: fix(dashboard): fix docker builds

## 0.12.0

### Minor Changes

-   d1671210: feat(dashboard): use mimir to manage project configuration

### Patch Changes

-   f65e4de9: chore(deps): bump @graphql-codegen monorepo to v3

## 0.11.20

### Patch Changes

-   4b4f0d01: chore(dashboard): improve dialog management

## 0.11.19

### Patch Changes

-   @nhost/react-apollo@5.0.6
-   @nhost/nextjs@1.13.11

## 0.11.18

### Patch Changes

-   01318860: fix(nhost-js): use correct URL for functions requests
-   Updated dependencies [01318860]
    -   @nhost/react-apollo@5.0.5
    -   @nhost/nextjs@1.13.10

## 0.11.17

### Patch Changes

-   f673adea: fix(dashboard): set correct Content-Type for user creation
-   445d8ef4: chore(deps): bump `@nhost/react-apollo` to 5.0.4
-   445d8ef4: chore(deps): bump `@nhost/nextjs` to 1.13.9
- 0368663d: fix(dashboard): allow permission editing for auth and
storage schemas
-   Updated dependencies [445d8ef4]
-   Updated dependencies [445d8ef4]
    -   @nhost/react-apollo@5.0.4
    -   @nhost/nextjs@1.13.9

## 0.11.16

### Patch Changes

-   b755e908: fix(dashboard): use correct date for last seen
-   2d9145f9: chore(deps): revert GraphQL client
- 1ddf704c: fix(dashboard): don't show false positive message for failed
user creation
    -   @nhost/react-apollo@5.0.3
    -   @nhost/nextjs@1.13.8

## 0.11.15

### Patch Changes

-   @nhost/react-apollo@5.0.2
-   @nhost/nextjs@1.13.7

## 0.11.14

### Patch Changes

- 2cc18dcb: fix(dashboard): prevent permission editor dropdown from
being always open

## 0.11.13

### Patch Changes

- 3343a363: chore(dashboard): bump `@testing-library/react` to v14 and
`@testing-library/dom` to v9
    -   @nhost/react-apollo@5.0.1
    -   @nhost/nextjs@1.13.6

## 0.11.12

### Patch Changes

- 87eda76e: chore(dashboard): bump `@types/react` to v18.0.28 and
`@types/react-dom` to v18.0.11
-   6f0ac570: feat(dashboard): show dashboard version in account menu

## 0.11.11

### Patch Changes

-   bf1e4071: chore(dashboard): bump `react-is` version to `18.2.0`
-   Updated dependencies [bf1e4071]
-   Updated dependencies [5013213b]
    -   @nhost/nextjs@1.13.5
    -   @nhost/react-apollo@4.13.5

## 0.11.10

### Patch Changes

- a37a430b: fix(dashboard): don't break UI when deployments are
unavailable
    -   @nhost/react-apollo@4.13.4
    -   @nhost/nextjs@1.13.4

## 0.11.9

### Patch Changes

-   7b970e68: fix(dashboard): fix header link color

## 0.11.8

### Patch Changes

- f33242f2: feat(dashboard): add new sign up, sign in and reset password
pages

## 0.11.7

### Patch Changes

-   e9c8909c: fix(dashboard): use correct theme color in dark mode

## 0.11.6

### Patch Changes

-   902f486b: fix(dashboard): re-enable Hasura on logs page

## 0.11.5

### Patch Changes

-   1f9720fa: fix(dashboard): apply select permissions properly

## 0.11.4

### Patch Changes

-   deb14b51: fix(dashboard): don't break billing form

## 0.11.3

### Patch Changes

-   @nhost/react-apollo@4.13.3
-   @nhost/nextjs@1.13.3

## 0.11.2

### Patch Changes

-   f143e51d: chore(dashboard): pin Turborepo to 1.6.3

## 0.11.1

### Patch Changes

-   c2b5a41a: chore(dashboard): select system colors by default

## 0.11.0

### Minor Changes

-   1ebaf429: feat(dashboard): introduce Dark Mode 🌚

### Patch Changes

- 63b445c4: fixed duplicated logs bug and made to date count during live
mode

## 0.10.1

### Patch Changes

-   e146d32e: chore(deps): update dependency @types/react to v18.0.27
-   59347fcd: correct allowed role name
-   5b65cac9: updated authentication documentation
-   963f9b5e: feat(dashboard): include project info in feedback

## 0.10.0

### Minor Changes

-   ed4c7801: chore(dashboard): remove Functions section

## 0.9.10

### Patch Changes

-   4e2f8ccd: fix(dashboard): don't break Auth page in local mode

## 0.9.9

### Patch Changes

-   31abbe5f: fix(dashboard): enable toggle when settings are filled in

## 0.9.8

### Patch Changes

- 5bdd31ad: chore(dashboard): list fewer images per page on the Storage
page
- 5121851c: fix(dashboard): don't throw validation error for valid
permission rules

## 0.9.7

### Patch Changes

-   c126b20d: fix(dashboard): correct redeployment button

## 0.9.6

### Patch Changes

-   36c3519c: feat(dashboard): retrigger deployments

## 0.9.5

### Patch Changes

- 200e9f77: chore(deps): update dependency @types/react-dom to v18.0.10
-   Updated dependencies [200e9f77]
    -   @nhost/nextjs@1.13.2
    -   @nhost/react-apollo@4.13.2

## 0.9.4

### Patch Changes

- dbd3ded5: fix(dashboard): workspaces creation, new form, correct
redirects.

## 0.9.3

### Patch Changes

-   85f0f943: fix(dashboard): don't break the table creation process

## 0.9.2

### Patch Changes

-   Updated dependencies [d42c27ae]
-   Updated dependencies [927be4a2]
    -   @nhost/nextjs@1.13.1
    -   @nhost/react-apollo@4.13.1

## 0.9.1

### Patch Changes

-   d0f80811: fix(dashboard): don't show error when signing out the user

## 0.9.0

### Minor Changes

- d92891b2: feat(dashboard): add Permission Editor to the Database
section

### Patch Changes

-   3d379128: fix(dashboard): create new user
    -   @nhost/react-apollo@4.13.0
    -   @nhost/nextjs@1.13.0

## 0.8.1

### Patch Changes

-   7cadd944: fix(dashboard): display Twitter provider settings

## 0.8.0

### Minor Changes

-   9a1aa7bb: add functions to the log dashboard
-   f29abe62: feat(dashboard): Users Management v2

### Patch Changes

-   7766624b: feat(dashboard): add JWT secret editor modal
    -   @nhost/react-apollo@4.12.1
    -   @nhost/nextjs@1.12.1

## 0.7.13

### Patch Changes

-   dd0738d5: fix(dashboard): provisioning status polling

## 0.7.12

### Patch Changes

-   b21222b3: chore(deps): update dependency @types/node to v16
-   9e0486a3: fix(dashboard): close modals when navigating
-   Updated dependencies [b21222b3]
-   Updated dependencies [65687bee]
-   Updated dependencies [54df0df4]
    -   @nhost/nextjs@1.12.0
    -   @nhost/react-apollo@4.12.0

## 0.7.11

### Patch Changes

-   d6527122: fix(dashboard): use correct service URLs

## 0.7.10

### Patch Changes

-   Updated dependencies [57db5b83]
    -   @nhost/nextjs@1.11.0
    -   @nhost/nhost-js@1.7.0
    -   @nhost/react@0.17.0
    -   @nhost/react-apollo@4.11.0

## 0.7.9

### Patch Changes

- a6d31dc2: fix(dashboard): don't break the UI when project is not
loaded yet

## 0.7.8

### Patch Changes

- 7f251111: Use `NhostProvider` instead of `NhostReactProvider` and
`NhostNextProvider`

    `NhostReactProvider` and `NhostNextProvider` are now deprecated

-   f4d70f88: fix(dashboard): do not break when region is nullish

- 4a9471cc: Windows Live Provider displayed link updated to match
backend url

- 594488e4: fix(dashboard): do not show error when submitting Apple
provider settings

-   Updated dependencies [7f251111]
    -   @nhost/nextjs@1.10.0
    -   @nhost/react@0.16.0
    -   @nhost/react-apollo@4.10.0

## 0.7.7

### Patch Changes

-   80b604ad: fix(dashboard): use correct Hasura slug

## 0.7.6

### Patch Changes

-   2d2beb53: fix(dashboard): prevent error on GraphQL page
-   ac8efcbd: chore(dashboard): deprecate old DNS name

## 0.7.5

### Patch Changes

-   132a4f4b: chore(dashboard): remove unused dependencies
- 132a4f4b: chore(deps): synchronize @types/react-dom and @types/react
versions
-   db57572f: fix(dashboard): correct section paddings when no env vars
-   Updated dependencies [132a4f4b]
    -   @nhost/react@0.15.2
    -   @nhost/react-apollo@4.9.2
    -   @nhost/nextjs@1.9.3

## 0.7.4

### Patch Changes

-   34d85e54: chore(deps): update dependency critters to ^0.0.16
- 9b93cf95: chore(deps): update dependency @netlify/functions to ^0.11.0
-   e0439030: chore(deps): update dependency @types/react-dom to v18.0.9
-   Updated dependencies [82124329]
    -   @nhost/nextjs@1.9.2

## 0.7.3

### Patch Changes

-   a1193da4: fix(dashboard): remove character limit from env var inputs

## 0.7.2

### Patch Changes

-   44f13f62: chore(dashboard): cleanup unused files

## 0.7.1

### Patch Changes

- e01cb2ed: chore(dashboard): change settings sidebar menu item density

## 0.7.0

### Minor Changes

- db342f45: chore(dashboard): refactor Roles and Permissions settings
sections
-   8b9fa0b1: feat(dashboard): add Environment Variables page

### Patch Changes

-   Updated dependencies [66b4f3d0]
-   Updated dependencies [2e6923dc]
-   Updated dependencies [ef117c28]
-   Updated dependencies [aebb8225]
    -   @nhost/core@0.9.4
    -   @nhost/nhost-js@1.6.2
    -   @nhost/nextjs@1.9.1
    -   @nhost/react@0.15.1
    -   @nhost/react-apollo@4.9.1

## 0.6.0

### Minor Changes

-   eef9c914: feat(dashboard): add Roles and Permissions page

## 0.5.0

### Minor Changes

-   a48dd5bf: feat(dashboard): make backend port configurable

## 0.4.3

### Patch Changes

-   5de965d9: fix(dashboard): alphabetic ordering of providers
-   b9087a4a: fix(dashboard): console -> dashboard terminology
-   ca012d79: docs(workos): WorkOS Docs

## 0.4.2

### Patch Changes

-   89bd37bc: fix(dashboard): correct redirect URL input opacity
-   Updated dependencies [4601d84e]
-   Updated dependencies [843087cb]
    -   @nhost/react@0.15.0
    -   @nhost/nextjs@1.9.0
    -   @nhost/react-apollo@4.9.0

## 0.4.1

### Patch Changes

-   766cb612: fix(dashboard): correct redirect URL for oauth providers
-   Updated dependencies [53bdc294]
-   Updated dependencies [f2aaff05]
    -   @nhost/nextjs@1.8.3
    -   @nhost/core@0.9.3
    -   @nhost/react@0.14.3
    -   @nhost/nhost-js@1.6.1
    -   @nhost/react-apollo@4.8.3

## 0.4.0

### Minor Changes

-   9211743d: feat(dashboard): migrate Settings page features

## 0.3.0

### Minor Changes

-   73da6a67: fix(dashboard): avoid using BACKEND_URL locally

## 0.2.0

### Minor Changes

-   db118f97: feat(dashboard): generate Docker image

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-02-06 14:36:09 +01:00
David BM
91d5fbba42 fix (dashboard): invalid slug/subdomain should open 404 (#3172)
### **User description**
Fixes #3119


___

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


___

### **Description**
- Fix invalid slug/subdomain to open 404 page

- Disable inaccessible pages in local CLI dashboard

- Improve project page navigation and accessibility

- Enhance not found redirect 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>OrgPagesComboBox.tsx</strong><dd><code>Disable
organization pages combo box for non-platform use</code></dd></summary>
<hr>

dashboard/src/components/layout/Header/OrgPagesComboBox.tsx

<li>Import useIsPlatform hook<br> <li> Disable PopoverTrigger when not
on platform


</details>


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

</tr>

<tr>
  <td>
    <details>
<summary><strong>ProjectPagesComboBox.tsx</strong><dd><code>Enhance
project pages combo box with platform-specific
disabling</code></dd></summary>
<hr>

dashboard/src/components/layout/Header/ProjectPagesComboBox.tsx

<li>Refactor projectPages to use useMemo<br> <li> Add 'disabled'
property to project page options<br> <li> Disable certain pages based on
isPlatform value


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3172/files#diff-70b3af41358f0a22b83e502409a70a0df15e8946d958dbaee4c32b6ebdb38cf6">+106/-83</a></td>

</tr>
</table></td></tr><tr><td><strong>Bug fix</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>useNotFoundRedirect.ts</strong><dd><code>Improve 404
redirect logic for invalid slugs and subdomains</code></dd></summary>
<hr>


dashboard/src/features/projects/common/hooks/useNotFoundRedirect/useNotFoundRedirect.ts

<li>Add checks for valid organization and project<br> <li> Implement
platform-specific redirect logic<br> <li> Include additional loading
states in redirect decision


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3172/files#diff-837279cf43199053bca09913f62c4af019063a2e8dc7bfb7643ec54b7cecd29d">+41/-10</a>&nbsp;
</td>

</tr>
</table></td></tr><tr><td><strong>Documentation</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>heavy-eyes-smile.md</strong><dd><code>Add changeset for
dashboard improvements</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; </dd></summary>
<hr>

.changeset/heavy-eyes-smile.md

<li>Add changeset for @nhost/dashboard package<br> <li> Describe fix for
invalid slug/subdomain and local CLI dashboard


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3172/files#diff-9a0418cf1a2622ce3bbec8df535fa44974433329d5386f0a90eee7e60167b1c6">+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-02-06 14:30:00 +01:00
David Barroso
498363db25 chore (dashboard): regenerate graphql bindings (#3188)
### **PR Type**
Enhancement


___

### **Description**
- Updated GraphQL bindings in dashboard

- Changed postgres config from optional to required

- Removed 'Graphite' from Software_Type_Enum

- Modified ConfigPostgres and related types


___



### **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>graphql.ts</strong><dd><code>Update GraphQL types and
queries for Postgres</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

dashboard/src/utils/__generated__/graphql.ts

<li>Changed <code>postgres</code> from optional to required in
ConfigConfig<br> <li> Updated ConfigPostgres and related types<br> <li>
Removed 'Graphite' from Software_Type_Enum<br> <li> Modified query and
mutation types to reflect changes


</details>


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

</tr>
</table></td></tr><tr><td><strong>Documentation</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>perfect-hairs-wave.md</strong><dd><code>Add changeset
for GraphQL bindings update</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; </dd></summary>
<hr>

.changeset/perfect-hairs-wave.md

<li>Added changeset file for patch version bump<br> <li> Described
change as regenerating GraphQL bindings


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3188/files#diff-a0885845ed7d68d0b6922f52b710db28275617df1ae791a850f16f58d90a6097">+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-02-06 14:08:33 +01:00
robertkasza
8c2779930b fix (dashboard): fetch allowed roles from the project's auth.roles table (#3179)
### **User description**
fetch allowed roles from the project's auth.roles table


___

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


___

### **Description**
- Fetch allowed roles from project's auth.roles table

- Implement useAllowedUserRoles custom hook

- Update UsersBody component to use new hook

- Add GraphQL query for fetching allowed roles


___



### **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>index.ts</strong><dd><code>Export new
useAllowedUserRoles hook</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>

dashboard/src/features/orgs/hooks/useAllowedUserRoles/index.ts

- Export useAllowedUserRoles hook


</details>


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

</tr>

<tr>
  <td>
    <details>
<summary><strong>useAllowedUserRoles.ts</strong><dd><code>Implement
useAllowedUserRoles custom hook</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; </dd></summary>
<hr>


dashboard/src/features/orgs/hooks/useAllowedUserRoles/useAllowedUserRoles.ts

<li>Implement useAllowedUserRoles custom hook<br> <li> Fetch auth roles
from remote application<br> <li> Process and return allowed user roles


</details>


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

</tr>

<tr>
  <td>
    <details>
<summary><strong>UsersBody.tsx</strong><dd><code>Refactor UsersBody to
use new useAllowedUserRoles hook</code>&nbsp; &nbsp; &nbsp;
</dd></summary>
<hr>


dashboard/src/features/orgs/projects/authentication/users/components/UsersBody/UsersBody.tsx

<li>Replace useGetRolesPermissionsQuery with useAllowedUserRoles<br>
<li> Remove unused imports and variables<br> <li> Simplify code by using
new hook


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3179/files#diff-33b33017f46d5cb8e4652c183619f3dc86c5377125ed3a612888739e0da22484">+3/-25</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td>
    <details>
<summary><strong>graphql.ts</strong><dd><code>Add generated types for
new allowed roles query</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

dashboard/src/utils/__generated__/graphql.ts

<li>Add GetRemoteAppAllowedRolesQuery type and related functions<br>
<li> Generate GraphQL hooks for new query


</details>


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

</tr>

<tr>
  <td>
    <details>
<summary><strong>getAllowedRoles.graphql</strong><dd><code>Add GraphQL
query for fetching allowed roles</code>&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></summary>
<hr>

dashboard/src/gql/remote-app/getAllowedRoles.graphql

- Add GraphQL query to fetch allowed roles from authRoles table


</details>


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

</tr>
</table></td></tr><tr><td><strong>Documentation</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>tall-houses-mix.md</strong><dd><code>Add changeset for
allowed roles fix</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/tall-houses-mix.md

<li>Add changeset for minor version bump<br> <li> Describe fix for
fetching allowed roles


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3179/files#diff-a7caacd8e8e971694232815f20297858ecefe39e86e80190e776b5fb47652191">+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-02-05 17:28:16 +01:00
robertkasza
0aa27a2fd1 feat (dashboard): allow to create new org from the transfer project dialog (#3169)
### **User description**
feat (dashboard): allow to create new org from the transfer project
dialog

___

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


___

### **Description**
- Allow creating new org from transfer project dialog

- Improve upgrade to pro button functionality

- Add unit tests for new features

- Fix error after redirecting from checkout


___



### **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>15
files</summary><table>
<tr>
<td><strong>OpenTransferDialogButton.tsx</strong><dd><code>Add new
OpenTransferDialogButton 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/3169/files#diff-e86a4216c48c54e1a2652ff3609575d6bf193d085ad42849a85dc6d4abba950c">+40/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>index.ts</strong><dd><code>Export OpenTransferDialogButton
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/3169/files#diff-c1fd683ba66a0e7f6a4622051d9e548ee32f215f29bcf0dd22b0827f8c38bc12">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>UpgradeToProBanner.tsx</strong><dd><code>Refactor
UpgradeToProBanner to use OpenTransferDialogButton</code></dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3169/files#diff-f38fc14d24ec6ee22f9a100cc473c641dcdc66284d41d030c456bf505094ed9d">+5/-28</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>CreateOrgFormDialog.tsx</strong><dd><code>Enhance
CreateOrgDialog with new props and functionality</code>&nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3169/files#diff-9a1ed9e851328393b81356d80ade3509016aa55c254ed1f4deb692b0bd96f02e">+47/-18</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>FinishOrgCreationProcess.tsx</strong><dd><code>Add new
FinishOrgCreationProcess 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/3169/files#diff-7602855e6aaab1dd3810c866acbedd5b9eb22c271806969eb9a3435f1c76ca8d">+53/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>index.ts</strong><dd><code>Export FinishOrgCreationProcess
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/3169/files#diff-a9faaff5f8d044125799178a6e33aa49ebed3aa1aaa8c2261162e2b25e24e0bd">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>FinishOrgCreation.tsx</strong><dd><code>Add new
FinishOrgCreation 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/3169/files#diff-bf22866bc3f80e45cdf18035146a8c7be6a38d1f6726b0e28d63879aacd263a4">+24/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>TransferProjectDialog.tsx</strong><dd><code>Enhance
TransferProjectDialog with new org creation</code>&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3169/files#diff-b68d4641a67e07a8bf8c14e1f705059c564e1bca53e591783581af27a488d86e">+221/-114</a></td>

</tr>

<tr>
<td><strong>NotificationsTray.tsx</strong><dd><code>Update
NotificationsTray to handle session_id</code>&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3169/files#diff-8b559ee1d3176203e8a4e1588924d57944d09d792117ed578b27cd5401ee5d4f">+15/-4</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>index.ts</strong><dd><code>Export useFinishOrgCreation
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/3169/files#diff-de59152f45a5fbf6b999d3cd9e4e14eefbde27e4cccf0d6ed6da5e2956db24ec">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>useFinishOrgCreation.ts</strong><dd><code>Add new
useFinishOrgCreation custom hook</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/3169/files#diff-3b8bf7608ab36d8ab0df895e400f0d2d9e29fad2055b40b33d8d9912a27c99c3">+92/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>OverviewTopBar.tsx</strong><dd><code>Add
UpgradeProjectDialog to OverviewTopBar</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/3169/files#diff-560ae107ed8e458fa4b4a226b9f5c24e24b042b5f9bcea9317c78e75929faa4b">+20/-15</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>UpgradeProjectDialog.tsx</strong><dd><code>Add new
UpgradeProjectDialog 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/3169/files#diff-7bfab4ad088dbc503c1304f5620e22e02f70602bf14ba6b495969b882b2eb30e">+20/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>utils.ts</strong><dd><code>Add isEmptyValue and
isNotEmptyValue utilities</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3169/files#diff-57035bfd3b91de326fec3e5a0bf19487f03130a9a09dc3e428c79f556677081b">+17/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>verify.tsx</strong><dd><code>Refactor verify page to use
FinishOrgCreationProcess</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3169/files#diff-5fa0ea2519bed6649a8aa98826526945868bd7a925c5ce5edb3fd14e81273947">+24/-104</a></td>

</tr>

</table></details></td></tr><tr><td><strong>Tests</strong></td><td><details><summary>5
files</summary><table>
<tr>
<td><strong>TransferProjectDialog.test.tsx</strong><dd><code>Add unit
tests for TransferProjectDialog</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/3169/files#diff-d4ebdb8af76a7c9e73606708718c3448445545259ad553d73b6d322408e3eb8c">+233/-0</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>NotificationsTray.test.tsx</strong><dd><code>Add unit tests
for NotificationsTray</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/3169/files#diff-727f6debec6a102557407e55c56363e0c75486e30a732158f85c81ada892f77c">+167/-0</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>utils.test.ts</strong><dd><code>Add unit tests for
isEmptyValue utility</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/3169/files#diff-d85d96942b9d8ae1eae8781e4f092ee9c8183affd76669a6195df713bc801184">+35/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>setupTests.ts</strong><dd><code>Mock ResizeObserver in test
setup</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/3169/files#diff-20b15fc9d6987586d0a853c077a4aff66b005fdb2e2b99bb5eb494d4c8c38366">+11/-1</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>mocks.ts</strong><dd><code>Add mock data for
organizations</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/3169/files#diff-d1ef12c0f15123bb4e23a0c513fc3d9b5c16af421c71c2909fde3717e09a9d89">+53/-0</a>&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>shiny-feet-rest.md</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3169/files#diff-a358ec02ec88d2af39c757f5cb97adcd184ba2614974df53dcb358591b6ca8d0">+5/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>settings.json</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3169/files#diff-5ea4d30be0c6c4cf5c0f1980f0dfe7cd7bdecee565746acb5ec90b0f79ff9314">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>getOrganizationQuery.ts</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3169/files#diff-8a0d854ee3a00b7a8b6661eb8008f3fe2140190fc6543a9674d287f786466d21">+22/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>getProjectQuery.ts</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3169/files#diff-01bab55f0611d27001640c4215cb6d97f8e2b1d45bc56e519d78b282a12c05e7">+12/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>organizationRequests.ts</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3169/files#diff-3cf4d908676a8050fe83c1bf53a57e6577a24fcae13b142ada89da7072d0975e">+22/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>prefetchNewAppQuery.ts</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3169/files#diff-0a3a444a14b5f5495ef86c90f200a3a672732770e90d4b7206468e2ac265d9fe">+73/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>testUtils.tsx</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3169/files#diff-6ebbd73e167641a1706f1b8d30b00569336d10f3c2ab7626d81e639015383e5e">+50/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>tsconfig.test.json</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3169/files#diff-e7bc653278c33281fda4e64da0f34c8c613b99891a16611366edc1c05870a935">+1/-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-02-05 17:04:18 +01:00
David BM
8656749e5a fix (dashboard CI): e2e tests teardown (#3186)
### **PR Type**
Tests, Enhancement


___

### **Description**
- Added new e2e tests for database permissions.

- Implemented a new teardown process for database cleanup.

- Removed global teardown script and updated configuration.

- Enhanced utility functions for test operations.


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Tests</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>permissions-table.test.ts</strong><dd><code>Add e2e
tests for database permissions</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

dashboard/e2e/database/permissions-table.test.ts

<li>Added tests for creating tables with role permissions.<br> <li>
Included tests for custom checks on row selection.<br> <li> Utilized
Playwright for browser automation in tests.


</details>


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

</tr>

<tr>
  <td>
    <details>
<summary><strong>database.teardown.ts</strong><dd><code>Implement
database cleanup teardown script</code>&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; </dd></summary>
<hr>

dashboard/e2e/teardown/database.teardown.ts

<li>Implemented new teardown script for database cleanup.<br> <li>
Automated table deletion using SQL in Playwright.<br> <li> Set up
browser context for teardown operations.


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3186/files#diff-4c0d8019f6c1586d9dc41b89d10e9a71f5913559112fdf6eed38094d832a126d">+61/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td>
    <details>
<summary><strong>global-teardown.ts</strong><dd><code>Remove global
teardown script</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/global-teardown.ts

<li>Removed global teardown script.<br> <li> Deprecated old teardown
approach using Hasura.


</details>


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

</tr>
</table></td></tr><tr><td><strong>Enhancement</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>utils.ts</strong><dd><code>Add utility function for
permission button clicks</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; </dd></summary>
<hr>

dashboard/e2e/utils.ts

<li>Added utility function for clicking permission buttons.<br> <li>
Enhanced test utilities for role-based operations.


</details>


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

</tr>
</table></td></tr><tr><td><strong>Configuration
changes</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>playwright.config.ts</strong><dd><code>Update
Playwright configuration for new teardown</code>&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

dashboard/playwright.config.ts

<li>Updated Playwright config to remove global teardown.<br> <li> Added
new teardown project configuration.


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3186/files#diff-3ce7004405593146d0b9c501fc50a6a5ae2da8bb48b57dee2faf79ca9c09cf62">+5/-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-02-05 16:08:00 +01:00
robertkasza
d097eb8feb fix (dashboard): fix edit permission form cannot be saved (#3183)
### **PR Type**
Bug fix


___

### **Description**
- Fix edit permission form saving issue

- Update validation schema for rule groups

- Modify operator validation in EditPermissionsForm

- Add new development script for dashboard


___



### **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>validationSchemas.ts</strong><dd><code>Update
validation schema for EditPermissionsForm</code>&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>


dashboard/src/features/orgs/projects/database/dataGrid/components/EditPermissionsForm/validationSchemas.ts

<li>Updated operator validation message<br> <li> Added custom test for
operator in ruleGroupSchema<br> <li> Implemented conditional validation
for operator based on filter <br>presence


</details>


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

</tr>
</table></td></tr><tr><td><strong>Documentation</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>curly-toys-peel.md</strong><dd><code>Add changeset for
dashboard patch</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>

.changeset/curly-toys-peel.md

<li>Added changeset file for @nhost/dashboard patch<br> <li> Described
fix for edit permission form saving issue


</details>


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

</tr>
</table></td></tr><tr><td><strong>Enhancement</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>package.json</strong><dd><code>Add development script
for dashboard</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>

package.json

<li>Added new script 'dev:dashboard' for running dashboard in
development <br>mode


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/3183/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-02-05 10:05:51 +01:00
58 changed files with 1856 additions and 641 deletions

2
.gitignore vendored
View File

@@ -63,3 +63,5 @@ out/
# Nix
.envrc
.direnv/
/.vscode/

View File

@@ -1,6 +1,6 @@
{
"typescript.tsdk": "node_modules/typescript/lib",
"editor.codeActionsOnSave": {
"source.organizeImports": true
"source.organizeImports": "explicit"
}
}

View File

@@ -33,7 +33,7 @@ ENV NEXT_PUBLIC_NHOST_CONFIGSERVER_URL=__NEXT_PUBLIC_NHOST_CONFIGSERVER_URL__
RUN yarn global add pnpm@9.15.0
COPY .gitignore .gitignore
COPY --from=pruner /app/out/json/ .
COPY --from=pruner /app/out/pnpm-*.yaml .
COPY --from=pruner /app/out/pnpm-*.yaml ./
RUN pnpm install --frozen-lockfile
COPY --from=pruner /app/out/full/ .

View File

@@ -0,0 +1,149 @@
import { TEST_ORGANIZATION_SLUG, TEST_PROJECT_SUBDOMAIN } from '@/e2e/env';
import {
clickPermissionButton,
navigateToProject,
prepareTable,
} from '@/e2e/utils';
import { faker } from '@faker-js/faker';
import type { Page } from '@playwright/test';
import { expect, test } from '@playwright/test';
import { snakeCase } from 'snake-case';
let page: Page;
test.beforeAll(async ({ browser }) => {
page = await browser.newPage();
});
test.beforeEach(async () => {
await page.goto('/');
await navigateToProject({
page,
orgSlug: TEST_ORGANIZATION_SLUG,
projectSubdomain: TEST_PROJECT_SUBDOMAIN,
});
const databaseRoute = `/orgs/${TEST_ORGANIZATION_SLUG}/projects/${TEST_PROJECT_SUBDOMAIN}/database/browser/default`;
await page.goto(databaseRoute);
await page.waitForURL(databaseRoute);
});
test.afterAll(async () => {
await page.close();
});
test('should create a table with role permissions to select row', async () => {
await page.getByRole('button', { name: /new table/i }).click();
await expect(page.getByText(/create a new table/i)).toBeVisible();
const tableName = snakeCase(faker.lorem.words(3));
await prepareTable({
page,
name: tableName,
primaryKey: 'id',
columns: [
{ name: 'id', type: 'uuid', defaultValue: 'gen_random_uuid()' },
{ name: 'title', type: 'text' },
],
});
// create table
await page.getByRole('button', { name: /create/i }).click();
await page.waitForURL(
`/orgs/${TEST_ORGANIZATION_SLUG}/projects/${TEST_PROJECT_SUBDOMAIN}/database/browser/default/public/${tableName}`,
);
await expect(
page.getByRole('link', { name: tableName, exact: true }),
).toBeVisible();
// Press three horizontal dots more options button next to the table name
await page
.locator(`li:has-text("${tableName}") #table-management-menu button`)
.click();
await page.getByRole('menuitem', { name: /edit permissions/i }).click();
await clickPermissionButton({ page, role: 'user', permission: 'Select' });
await page.getByLabel('Without any checks').click();
await page.getByRole('button', { name: /select all/i }).click();
await page.getByRole('button', { name: /save/i }).click();
await expect(
page.getByText(/permission has been saved successfully/i),
).toBeVisible();
});
test('should create a table with role permissions and a custom check to select rows', async () => {
await page.getByRole('button', { name: /new table/i }).click();
await expect(page.getByText(/create a new table/i)).toBeVisible();
const tableName = snakeCase(faker.lorem.words(3));
await prepareTable({
page,
name: tableName,
primaryKey: 'id',
columns: [
{ name: 'id', type: 'uuid', defaultValue: 'gen_random_uuid()' },
{ name: 'title', type: 'text' },
],
});
// create table
await page.getByRole('button', { name: /create/i }).click();
await page.waitForURL(
`/orgs/${TEST_ORGANIZATION_SLUG}/projects/${TEST_PROJECT_SUBDOMAIN}/database/browser/default/public/${tableName}`,
);
await expect(
page.getByRole('link', { name: tableName, exact: true }),
).toBeVisible();
// Press three horizontal dots more options button next to the table name
await page
.locator(`li:has-text("${tableName}") #table-management-menu button`)
.click();
await page.getByRole('menuitem', { name: /edit permissions/i }).click();
await clickPermissionButton({ page, role: 'user', permission: 'Select' });
await page.getByLabel('With custom check').click();
// await page.getByRole('combobox', { name: /select a column/i }).click();
await page.getByText('Select a column', { exact: true }).click();
const columnSelector = page.locator('input[role="combobox"]');
await columnSelector.fill('id');
await columnSelector.press('Enter');
await expect(page.getByText(/_eq/i)).toBeVisible();
// limit on number of rows fetched per request.
await page.locator('#limit').fill('100');
await page.getByText('Select variable...', { exact: true }).click();
const variableSelector = await page.locator('input[role="combobox"]');
await variableSelector.fill('X-Hasura-User-Id');
await variableSelector.press('Enter');
await page.getByRole('button', { name: /select all/i }).click();
await page.getByRole('button', { name: /save/i }).click();
await expect(
page.getByText(/permission has been saved successfully/i),
).toBeVisible();
});

View File

@@ -0,0 +1,61 @@
import {
TEST_DASHBOARD_URL,
TEST_ORGANIZATION_SLUG,
TEST_PROJECT_SUBDOMAIN,
} from '@/e2e/env';
import { navigateToProject } from '@/e2e/utils';
import { type Page, expect, test as teardown } from '@playwright/test';
let page: Page;
teardown.beforeAll(async ({ browser }) => {
const context = await browser.newContext({
baseURL: TEST_DASHBOARD_URL,
storageState: 'e2e/.auth/user.json',
});
page = await context.newPage();
});
teardown.beforeEach(async () => {
await page.goto('/');
await navigateToProject({
page,
orgSlug: TEST_ORGANIZATION_SLUG,
projectSubdomain: TEST_PROJECT_SUBDOMAIN,
});
const databaseRoute = `/orgs/${TEST_ORGANIZATION_SLUG}/projects/${TEST_PROJECT_SUBDOMAIN}/database/browser/default`;
await page.goto(databaseRoute);
await page.waitForURL(databaseRoute);
});
teardown.afterAll(async () => {
await page.close();
});
teardown('clean up database tables', async () => {
await page.getByRole('link', { name: /sql editor/i }).click();
await page.waitForURL(
`/orgs/${TEST_ORGANIZATION_SLUG}/projects/${TEST_PROJECT_SUBDOMAIN}/database/browser/default/editor`,
);
const inputField = page.locator('[contenteditable]');
await inputField.fill(`
DO $$ DECLARE
tablename text;
BEGIN
FOR tablename IN
SELECT table_name FROM information_schema.tables
WHERE table_schema = 'public'
LOOP
EXECUTE 'DROP TABLE IF EXISTS public.' || quote_ident(tablename) || ' CASCADE';
END LOOP;
END $$;
`);
await page.locator('button[type="button"]', { hasText: /run/i }).click();
await expect(page.getByText(/success/i)).toBeVisible();
});

View File

@@ -191,3 +191,23 @@ export function generateTestEmail(prefix: string = 'Nhost_Test_') {
return [prefix, email].join('');
}
export async function clickPermissionButton({
page,
role,
permission,
}: {
page: Page;
role: string;
permission: 'Insert' | 'Select' | 'Update' | 'Delete';
}) {
const permissionIndex =
['Insert', 'Select', 'Update', 'Delete'].indexOf(permission) + 1;
await page
.locator('tr', { hasText: role })
.locator('td')
.nth(permissionIndex)
.locator('button')
.click();
}

View File

@@ -1,67 +0,0 @@
import {
TEST_DASHBOARD_URL,
TEST_ORGANIZATION_SLUG,
TEST_PROJECT_ADMIN_SECRET,
TEST_PROJECT_SUBDOMAIN,
} from '@/e2e/env';
import { navigateToProject } from '@/e2e/utils';
import { chromium } from '@playwright/test';
async function globalTeardown() {
const browser = await chromium.launch({ slowMo: 1000 });
const context = await browser.newContext({
baseURL: TEST_DASHBOARD_URL,
storageState: 'e2e/.auth/user.json',
});
const page = await context.newPage();
await navigateToProject({
page,
orgSlug: TEST_ORGANIZATION_SLUG,
projectSubdomain: TEST_PROJECT_SUBDOMAIN,
});
const pagePromise = context.waitForEvent('page');
await page.getByRole('link', { name: /hasura/i }).click();
await page.getByRole('link', { name: /open hasura/i }).click();
const hasuraPage = await pagePromise;
await hasuraPage.waitForLoadState();
const adminSecretInput = hasuraPage.getByPlaceholder(/enter admin-secret/i);
// note: a more ideal way would be to paste from clipboard, but Playwright
// doesn't support that yet
await adminSecretInput.fill(TEST_PROJECT_ADMIN_SECRET);
await adminSecretInput.press('Enter');
// note: getByRole doesn't work here
await hasuraPage.locator('a', { hasText: /data/i }).nth(0).click();
await hasuraPage.locator('[data-test="sql-link"]').click();
// Set the value of the Ace code editor using JavaScript evaluation in the browser context
await hasuraPage.evaluate(() => {
const editor = ace.edit('raw_sql');
editor.setValue(`
DO $$ DECLARE
tablename text;
BEGIN
FOR tablename IN
SELECT table_name FROM information_schema.tables
WHERE table_schema = 'public'
LOOP
EXECUTE 'DROP TABLE IF EXISTS public.' || quote_ident(tablename) || ' CASCADE';
END LOOP;
END $$;
`);
});
await hasuraPage.getByRole('button', { name: /run!/i }).click();
await hasuraPage.getByText(/sql executed!/i).waitFor();
}
export default globalTeardown;

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/dashboard",
"version": "2.18.0",
"version": "2.20.0",
"private": true,
"scripts": {
"preinstall": "npx only-allow pnpm",

View File

@@ -15,7 +15,6 @@ export default defineConfig({
retries: process.env.CI ? 2 : 0,
workers: 1,
reporter: 'html',
globalTeardown: require.resolve('./global-teardown'),
use: {
actionTimeout: 0,
trace: 'on-first-retry',
@@ -28,6 +27,11 @@ export default defineConfig({
{
name: 'setup',
testMatch: ['**/setup/*.setup.ts'],
teardown: 'teardown',
},
{
name: 'teardown',
testMatch: ['**/teardown/*.teardown.ts'],
},
{
name: 'chromium',

View File

@@ -0,0 +1,40 @@
import { useDialog } from '@/components/common/DialogProvider';
import { Button } from '@/components/ui/v2/Button';
import { Text } from '@/components/ui/v2/Text';
import { useIsCurrentUserOwner } from '@/features/orgs/projects/common/hooks/useIsCurrentUserOwner';
interface Props {
buttonText?: string;
onClick?: () => void;
}
function OpenTransferDialogButton({ buttonText, onClick }: Props) {
const text = buttonText ?? 'Transfer Project';
const isOwner = useIsCurrentUserOwner();
const { openAlertDialog } = useDialog();
const handleClick = () => {
if (isOwner) {
onClick();
} else {
openAlertDialog({
title: "You can't migrate this project",
payload: (
<Text variant="subtitle1" component="span">
Ask an owner of this organization to migrate the project.
</Text>
),
props: {
secondaryButtonText: 'I understand',
hidePrimaryAction: true,
},
});
}
};
return (
<Button className="max-w-xs lg:w-auto" onClick={handleClick}>
{text}
</Button>
);
}
export default OpenTransferDialogButton;

View File

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

View File

@@ -1,14 +1,13 @@
import { useDialog } from '@/components/common/DialogProvider';
import { NhostIcon } from '@/components/presentational/NhostIcon';
import { Box } from '@/components/ui/v2/Box';
import { Button } from '@/components/ui/v2/Button';
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 { useIsCurrentUserOwner } from '@/features/orgs/projects/common/hooks/useIsCurrentUserOwner';
import { useState } from 'react';
import { OpenTransferDialogButton } from '@/components/common/OpenTransferDialogButton';
import Image from 'next/image';
import { type ReactNode } from 'react';
@@ -21,11 +20,11 @@ export default function UpgradeToProBanner({
title,
description,
}: UpgradeToProBannerProps) {
const isOwner = useIsCurrentUserOwner();
const { openAlertDialog } = useDialog();
const [transferProjectDialogOpen, setTransferProjectDialogOpen] =
useState(false);
const handleTransferDialogOpen = () => setTransferProjectDialogOpen(true);
return (
<Box
sx={{ backgroundColor: 'primary.light' }}
@@ -51,29 +50,7 @@ export default function UpgradeToProBanner({
</div>
<div className="flex flex-col gap-2 space-y-2 lg:flex-row lg:items-center lg:space-x-2 lg:space-y-0">
<Button
className="max-w-xs lg:w-auto"
onClick={() => {
if (isOwner) {
setTransferProjectDialogOpen(true);
} else {
openAlertDialog({
title: "You can't migrate this project",
payload: (
<Text variant="subtitle1" component="span">
Ask an owner of this organization to migrate the project.
</Text>
),
props: {
secondaryButtonText: 'I understand',
hidePrimaryAction: true,
},
});
}
}}
>
Transfer Project
</Button>
<OpenTransferDialogButton onClick={handleTransferDialogOpen} />
<TransferProjectDialog
open={transferProjectDialogOpen}
setOpen={setTransferProjectDialogOpen}

View File

@@ -18,6 +18,7 @@ import {
PopoverTrigger,
} from '@/components/ui/v3/popover';
import { useIsPlatform } from '@/features/orgs/projects/common/hooks/useIsPlatform';
import { Check, ChevronsUpDown } from 'lucide-react';
import { useEffect, useMemo, useState } from 'react';
@@ -40,6 +41,8 @@ export default function OrgPagesComboBox() {
asPath,
} = useRouter();
const isPlatform = useIsPlatform();
const pathSegments = useMemo(() => asPath.split('/'), [asPath]);
const orgPageFromUrl = pathSegments[3] || null;
@@ -64,7 +67,7 @@ export default function OrgPagesComboBox() {
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<PopoverTrigger disabled={!isPlatform} asChild>
<Button
variant="ghost"
size="sm"

View File

@@ -32,6 +32,7 @@ import {
PopoverContent,
PopoverTrigger,
} from '@/components/ui/v3/popover';
import { useIsPlatform } from '@/features/orgs/projects/common/hooks/useIsPlatform';
import { cn } from '@/lib/utils';
import { useRouter } from 'next/router';
import { useEffect, useMemo, useState, type ReactElement } from 'react';
@@ -40,88 +41,10 @@ type Option = {
value: string;
label: string;
icon: ReactElement;
disabled: boolean;
};
const projectPages = [
{
label: 'Overview',
value: 'overview',
icon: <HomeIcon className="h-4 w-4" />,
slug: '',
},
{
label: 'Database',
value: 'database',
icon: <DatabaseIcon className="h-4 w-4" />,
slug: '/database/browser/default',
},
{
label: 'GraphQL',
value: 'graphql',
icon: <GraphQLIcon className="h-4 w-4" />,
slug: 'graphql',
},
{
label: 'Hasura',
value: 'hasura',
icon: <HasuraIcon className="h-4 w-4" />,
slug: 'hasura',
},
{
label: 'Auth',
value: 'users',
icon: <UserIcon className="h-4 w-4" />,
slug: 'users',
},
{
label: 'Storage',
value: 'storage',
icon: <StorageIcon className="h-4 w-4" />,
slug: 'storage',
},
{
label: 'Run',
value: 'run',
icon: <ServicesIcon className="h-4 w-4" />,
slug: 'run',
},
{
label: 'AI',
value: 'ai',
icon: <AIIcon className="h-4 w-4" />,
slug: 'ai/auto-embeddings',
},
{
label: 'Deployments',
value: 'deployments',
icon: <RocketIcon className="h-4 w-4" />,
slug: 'deployments',
},
{
label: 'Backups',
value: 'backups',
icon: <CloudIcon className="h-4 w-4" />,
slug: 'backups',
},
{
label: 'Logs',
value: 'logs',
icon: <FileTextIcon className="h-4 w-4" />,
slug: 'logs',
},
{
label: 'Metrics',
value: 'metrics',
icon: <GaugeIcon className="h-4 w-4" />,
slug: 'metrics',
},
{
label: 'Settings',
value: 'settings',
icon: <CogIcon className="h-4 w-4" />,
slug: 'settings',
},
];
type SelectedOption = Omit<Option, 'disabled'>;
export default function ProjectPagesComboBox() {
const {
@@ -130,6 +53,105 @@ export default function ProjectPagesComboBox() {
asPath,
} = useRouter();
const isPlatform = useIsPlatform();
const projectPages = useMemo(
() => [
{
label: 'Overview',
value: 'overview',
icon: <HomeIcon className="h-4 w-4" />,
slug: '',
disabled: false,
},
{
label: 'Database',
value: 'database',
icon: <DatabaseIcon className="h-4 w-4" />,
slug: '/database/browser/default',
disabled: false,
},
{
label: 'GraphQL',
value: 'graphql',
icon: <GraphQLIcon className="h-4 w-4" />,
slug: 'graphql',
disabled: false,
},
{
label: 'Hasura',
value: 'hasura',
icon: <HasuraIcon className="h-4 w-4" />,
slug: 'hasura',
disabled: false,
},
{
label: 'Auth',
value: 'users',
icon: <UserIcon className="h-4 w-4" />,
slug: 'users',
disabled: false,
},
{
label: 'Storage',
value: 'storage',
icon: <StorageIcon className="h-4 w-4" />,
slug: 'storage',
disabled: false,
},
{
label: 'Run',
value: 'run',
icon: <ServicesIcon className="h-4 w-4" />,
slug: 'run',
disabled: false,
},
{
label: 'AI',
value: 'ai',
icon: <AIIcon className="h-4 w-4" />,
slug: 'ai/auto-embeddings',
disabled: false,
},
{
label: 'Deployments',
value: 'deployments',
icon: <RocketIcon className="h-4 w-4" />,
slug: 'deployments',
disabled: !isPlatform,
},
{
label: 'Backups',
value: 'backups',
icon: <CloudIcon className="h-4 w-4" />,
slug: 'backups',
disabled: !isPlatform,
},
{
label: 'Logs',
value: 'logs',
icon: <FileTextIcon className="h-4 w-4" />,
slug: 'logs',
disabled: !isPlatform,
},
{
label: 'Metrics',
value: 'metrics',
icon: <GaugeIcon className="h-4 w-4" />,
slug: 'metrics',
disabled: !isPlatform,
},
{
label: 'Settings',
value: 'settings',
icon: <CogIcon className="h-4 w-4" />,
slug: 'settings',
disabled: false,
},
],
[isPlatform],
);
const pathSegments = useMemo(() => asPath.split('/'), [asPath]);
const projectPageFromUrl = appSubdomain
? pathSegments[5] || 'overview'
@@ -137,9 +159,8 @@ export default function ProjectPagesComboBox() {
const selectedProjectPageFromUrl = projectPages.find(
(item) => item.value === projectPageFromUrl,
);
const [selectedProjectPage, setSelectedProjectPage] = useState<Option | null>(
null,
);
const [selectedProjectPage, setSelectedProjectPage] =
useState<SelectedOption | null>(null);
useEffect(() => {
if (selectedProjectPageFromUrl) {
@@ -155,6 +176,7 @@ export default function ProjectPagesComboBox() {
label: app.label,
value: app.slug,
icon: app.icon,
disabled: app.disabled,
}));
const [open, setOpen] = useState(false);
@@ -188,6 +210,7 @@ export default function ProjectPagesComboBox() {
<CommandItem
key={option.value}
value={option.label}
disabled={option.disabled}
onSelect={() => {
setSelectedProjectPage(option);
setOpen(false);

View File

@@ -20,7 +20,7 @@ import { getUserRoles } from '@/features/projects/roles/settings/utils/getUserRo
import { useRemoteApplicationGQLClient } from '@/hooks/useRemoteApplicationGQLClient';
import type { DialogFormProps } from '@/types/common';
import {
RemoteAppGetUsersDocument,
RemoteAppGetUsersAndAuthRolesDocument,
useGetProjectLocalesQuery,
useGetRolesPermissionsQuery,
useUpdateRemoteAppUserMutation,
@@ -116,7 +116,7 @@ export default function EditUserForm({
const [updateUser] = useUpdateRemoteAppUserMutation({
client: remoteProjectGQLClient,
refetchQueries: [RemoteAppGetUsersDocument],
refetchQueries: [RemoteAppGetUsersAndAuthRolesDocument],
});
const form = useForm<EditUserFormValues>({

View File

@@ -6,7 +6,7 @@ import { Input } from '@/components/ui/v2/Input';
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
import { useRemoteApplicationGQLClient } from '@/hooks/useRemoteApplicationGQLClient';
import type { DialogFormProps } from '@/types/common';
import type { RemoteAppGetUsersQuery } from '@/utils/__generated__/graphql';
import type { RemoteAppGetUsersAndAuthRolesQuery } from '@/utils/__generated__/graphql';
import {
useGetSignInMethodsQuery,
useUpdateRemoteAppUserMutation,
@@ -26,7 +26,7 @@ export interface EditUserPasswordFormProps extends DialogFormProps {
/**
* The selected user.
*/
user: RemoteAppGetUsersQuery['users'][0];
user: RemoteAppGetUsersAndAuthRolesQuery['users'][0];
}
export default function EditUserPasswordForm({

View File

@@ -175,7 +175,23 @@ function CreateOrgForm({ plans, onSubmit, onCancel }: CreateOrgFormProps) {
);
}
export default function CreateOrgDialog() {
interface CreateOrgDialogProps {
hideNewOrgButton?: boolean;
isOpen?: boolean;
onOpenStateChange?: (newState: boolean) => void;
redirectUrl?: string;
}
function isPropSet(prop: any) {
return prop !== undefined;
}
export default function CreateOrgDialog({
hideNewOrgButton,
isOpen,
onOpenStateChange,
redirectUrl,
}: CreateOrgDialogProps) {
const { maintenanceActive } = useUI();
const user = useUserData();
const isPlatform = useIsPlatform();
@@ -186,6 +202,16 @@ export default function CreateOrgDialog() {
const [createOrganizationRequest] = useCreateOrganizationRequestMutation();
const [stripeClientSecret, setStripeClientSecret] = useState('');
const handleOpenChange = (newOpenState: boolean) => {
const controlledFromOutSide =
isPropSet(isOpen) && isPropSet(onOpenStateChange);
if (controlledFromOutSide) {
onOpenStateChange(newOpenState);
} else {
setOpen(newOpenState);
}
};
const createOrg = async ({
name,
plan,
@@ -195,16 +221,17 @@ export default function CreateOrgDialog() {
}) => {
await execPromiseWithErrorToast(
async () => {
const defaultRedirectUrl = `${window.location.origin}/orgs/verify`;
const {
data: { billingCreateOrganizationRequest: clientSecret },
} = await createOrganizationRequest({
variables: {
organizationName: name,
planID: plan,
redirectURL: `${window.location.origin}/orgs/verify`,
redirectURL: redirectUrl ?? defaultRedirectUrl,
},
});
setStripeClientSecret(clientSecret);
},
{
@@ -224,20 +251,22 @@ export default function CreateOrgDialog() {
}
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button
disabled={maintenanceActive}
className={cn(
'flex h-8 w-full flex-row justify-start gap-3 px-2',
'bg-background text-foreground hover:bg-accent dark:hover:bg-muted',
)}
onClick={() => setStripeClientSecret('')}
>
<Plus className="h-4 w-4 font-bold" strokeWidth={3} />
New Organization
</Button>
</DialogTrigger>
<Dialog open={isOpen ?? open} onOpenChange={handleOpenChange}>
{!hideNewOrgButton && (
<DialogTrigger asChild>
<Button
disabled={maintenanceActive}
className={cn(
'flex h-8 w-full flex-row justify-start gap-3 px-2',
'bg-background text-foreground hover:bg-accent dark:hover:bg-muted',
)}
onClick={() => setStripeClientSecret('')}
>
<Plus className="h-4 w-4 font-bold" strokeWidth={3} />
New Organization
</Button>
</DialogTrigger>
)}
<DialogContent
className={cn(
'text-foreground sm:max-w-xl',
@@ -264,7 +293,7 @@ export default function CreateOrgDialog() {
<CreateOrgForm
plans={data?.plans}
onSubmit={createOrg}
onCancel={() => setOpen(false)}
onCancel={() => handleOpenChange(false)}
/>
)}
{!loading && stripeClientSecret && (

View File

@@ -0,0 +1,53 @@
import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
import { CheckoutStatus } from '@/utils/__generated__/graphql';
import { memo } from 'react';
interface Props {
loading: boolean;
status: CheckoutStatus | null;
successMessage: string;
loadingMessage: string;
errorMessage: string;
pendingMessage: string;
}
function FinishOrgCreationProcess({
loading,
status,
successMessage,
loadingMessage,
errorMessage,
pendingMessage,
}: Props) {
let message: string | undefined;
switch (status) {
case CheckoutStatus.Completed: {
message = successMessage;
break;
}
case CheckoutStatus.Expired: {
message = errorMessage;
break;
}
case CheckoutStatus.Open: {
message = pendingMessage;
break;
}
default:
message = loadingMessage;
}
return (
<div className="relative flex flex-auto overflow-x-hidden">
<div className="flex h-full w-full flex-col items-center justify-center space-y-2">
{(loading || status === CheckoutStatus.Completed) && (
<ActivityIndicator circularProgressProps={{ className: 'w-6 h-6' }} />
)}
<span>{message}</span>
</div>
</div>
);
}
export default memo(FinishOrgCreationProcess);

View File

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

View File

@@ -0,0 +1,24 @@
import { FinishOrgCreationProcess } from '@/features/orgs/components/common/FinishOrgCreationProcess';
import { useFinishOrgCreation } from '@/features/orgs/hooks/useFinishOrgCreation';
import { type FinishOrgCreationOnCompletedCb } from '@/features/orgs/hooks/useFinishOrgCreation/useFinishOrgCreation';
interface Props {
onCompleted: FinishOrgCreationOnCompletedCb;
onError: () => void;
}
function FinishOrgCreation({ onCompleted, onError }: Props) {
const [loading, status] = useFinishOrgCreation({ onCompleted, onError });
return (
<FinishOrgCreationProcess
loading={loading}
status={status}
loadingMessage="Processing new organization request"
successMessage="Organization created successfully."
pendingMessage="Organization creation is pending..."
errorMessage="Error occurred while creating the organization. Please try again."
/>
);
}
export default FinishOrgCreation;

View File

@@ -0,0 +1,233 @@
import {
mockMatchMediaValue,
mockOrganization,
mockOrganizations,
mockOrganizationsWithNewOrg,
newOrg,
} from '@/tests/mocks';
import { getOrganization } from '@/tests/msw/mocks/graphql/getOrganizationQuery';
import { getProjectQuery } from '@/tests/msw/mocks/graphql/getProjectQuery';
import { prefetchNewAppQuery } from '@/tests/msw/mocks/graphql/prefetchNewAppQuery';
import tokenQuery from '@/tests/msw/mocks/rest/tokenQuery';
import {
createGraphqlMockResolver,
fireEvent,
mockPointerEvent,
queryClient,
render,
screen,
waitFor,
} from '@/tests/orgs/testUtils';
import userEvent from '@testing-library/user-event';
import { setupServer } from 'msw/node';
import { useState } from 'react';
import { afterAll, beforeAll, vi } from 'vitest';
import TransferProjectDialog from './TransferProjectDialog';
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: vi.fn().mockImplementation(mockMatchMediaValue),
});
mockPointerEvent();
const getUseRouterObject = (session_id?: string) => ({
basePath: '',
pathname: '/orgs/xyz/projects/test-project',
route: '/orgs/[orgSlug]/projects/[appSubdomain]',
asPath: '/orgs/xyz/projects/test-project',
isLocaleDomain: false,
isReady: true,
isPreview: false,
query: {
orgSlug: 'xyz',
appSubdomain: 'test-project',
session_id,
},
push: vi.fn(),
replace: vi.fn(),
reload: vi.fn(),
back: vi.fn(),
prefetch: vi.fn(),
beforePopState: vi.fn(),
events: {
on: vi.fn(),
off: vi.fn(),
emit: vi.fn(),
},
isFallback: false,
});
const mocks = vi.hoisted(() => ({
useRouter: vi.fn(),
useOrgs: vi.fn(),
}));
vi.mock('@/features/orgs/projects/hooks/useOrgs', async () => {
const actualUseOrgs = await vi.importActual<any>(
'@/features/orgs/projects/hooks/useOrgs',
);
return {
...actualUseOrgs,
useOrgs: mocks.useOrgs,
};
});
const postOrganizationRequestResolver = createGraphqlMockResolver(
'postOrganizationRequest',
'mutation',
);
vi.mock('next/router', () => ({
useRouter: mocks.useRouter,
}));
export function DialogWrapper({
defaultOpen = true,
}: {
defaultOpen?: boolean;
}) {
const [open, setOpen] = useState(defaultOpen);
return <TransferProjectDialog open={open} setOpen={setOpen} />;
}
const server = setupServer(tokenQuery);
beforeAll(() => {
process.env.NEXT_PUBLIC_NHOST_PLATFORM = 'true';
process.env.NEXT_PUBLIC_ENV = 'production';
server.listen();
});
afterEach(() => {
queryClient.clear();
mocks.useRouter.mockRestore();
});
afterAll(() => {
server.close();
vi.restoreAllMocks();
});
test('opens create org dialog when selecting "create new org" and closes transfer dialog', async () => {
mocks.useRouter.mockImplementation(() => getUseRouterObject());
const user = userEvent.setup();
server.use(getProjectQuery);
server.use(getOrganization);
mocks.useOrgs.mockImplementation(() => ({
orgs: mockOrganizations,
currentOrg: mockOrganization,
loading: false,
refetch: vi.fn(),
}));
server.use(prefetchNewAppQuery);
render(<DialogWrapper />);
const organizationCombobox = await screen.findByRole('combobox', {
name: /Organization/i,
});
expect(organizationCombobox).toBeInTheDocument();
await user.click(organizationCombobox);
const newOrgOption = await screen.findByRole('option', {
name: 'New Organization',
});
await user.click(newOrgOption);
expect(organizationCombobox).toHaveTextContent('New Organization');
const submitButton = await screen.findByText('Continue');
expect(submitButton).toHaveTextContent('Continue');
fireEvent(
submitButton,
new MouseEvent('click', {
bubbles: true,
cancelable: true,
}),
);
await waitFor(() => {
expect(submitButton).not.toBeInTheDocument();
});
const newOrgTitle = await screen.findByText('New Organization');
expect(newOrgTitle).toBeInTheDocument();
const closeButton = await screen.findByText('Close');
fireEvent(
closeButton,
new MouseEvent('click', {
bubbles: true,
cancelable: true,
}),
);
await waitFor(() => {
expect(newOrgTitle).not.toBeInTheDocument();
});
const submitButtonAfterClosingNewOrgDialog =
await screen.findByText('Continue');
await waitFor(() => {
expect(submitButtonAfterClosingNewOrgDialog).toHaveTextContent('Continue');
});
});
test(`transfer dialog opens automatically when there is a session_id and selects the ${newOrg.name} from the dropdown`, async () => {
mocks.useRouter.mockImplementation(() => getUseRouterObject('session_id'));
server.use(getProjectQuery);
server.use(getOrganization);
mocks.useOrgs.mockImplementation(() => ({
orgs: mockOrganizations,
currentOrg: mockOrganization,
loading: false,
refetch: vi.fn(),
}));
server.use(prefetchNewAppQuery);
server.use(postOrganizationRequestResolver.handler);
render(<DialogWrapper defaultOpen={false} />);
const processingNewOrgText = await screen.findByText(
'Processing new organization request',
);
expect(processingNewOrgText).toBeInTheDocument();
const closeButton = await screen.findByText('Close');
fireEvent(
closeButton,
new MouseEvent('click', {
bubbles: true,
cancelable: true,
}),
);
await waitFor(() => {});
expect(closeButton).toBeInTheDocument();
postOrganizationRequestResolver.resolve({
billingPostOrganizationRequest: {
Status: 'COMPLETED',
Slug: newOrg.slug,
ClientSecret: null,
__typename: 'PostOrganizationRequestResponse',
},
});
mocks.useOrgs.mockImplementation(() => ({
orgs: mockOrganizationsWithNewOrg,
currentOrg: mockOrganization,
loading: false,
refetch: vi.fn(),
}));
const organizationCombobox = await screen.findByRole('combobox', {
name: /Organization/i,
});
expect(organizationCombobox).toHaveTextContent(newOrg.name);
const submitButton = await screen.findByText('Transfer');
expect(submitButton).not.toBeDisabled();
});

View File

@@ -1,5 +1,3 @@
import { Badge } from '@/components/ui/v3/badge';
import { Button } from '@/components/ui/v3/button';
import {
Dialog,
DialogContent,
@@ -10,6 +8,8 @@ import {
import { LoadingScreen } from '@/components/presentational/LoadingScreen';
import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
import { Badge } from '@/components/ui/v3/badge';
import { Button } from '@/components/ui/v3/button';
import {
Form,
FormControl,
@@ -25,20 +25,26 @@ import {
SelectTrigger,
SelectValue,
} from '@/components/ui/v3/select';
import FinishOrgCreation from '@/features/orgs/components/common/TransferProjectDialog/FinishOrgCreation';
import CreateOrgDialog from '@/features/orgs/components/CreateOrgFormDialog/CreateOrgFormDialog';
import type { FinishOrgCreationOnCompletedCb } from '@/features/orgs/hooks/useFinishOrgCreation/useFinishOrgCreation';
import { useOrgs, type Org } from '@/features/orgs/projects/hooks/useOrgs';
import { useProject } from '@/features/orgs/projects/hooks/useProject';
import { execPromiseWithErrorToast } from '@/features/orgs/utils/execPromiseWithErrorToast';
import { cn } from '@/lib/utils';
import { cn, isNotEmptyValue } from '@/lib/utils';
import {
Organization_Members_Role_Enum,
useBillingTransferAppMutation,
} from '@/utils/__generated__/graphql';
import { zodResolver } from '@hookform/resolvers/zod';
import { useUserId } from '@nhost/nextjs';
import { Plus } from 'lucide-react';
import { useRouter } from 'next/router';
import { useEffect, useState } from 'react';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
const CREATE_NEW_ORG = 'createNewOrg';
interface TransferProjectDialogProps {
open: boolean;
setOpen: (value: boolean) => void;
@@ -52,11 +58,21 @@ export default function TransferProjectDialog({
open,
setOpen,
}: TransferProjectDialogProps) {
const { push } = useRouter();
const { push, asPath, query, replace, pathname } = useRouter();
const { session_id, test, ...remainingQuery } = query;
const currentUserId = useUserId();
const { project, loading: projectLoading } = useProject();
const { orgs, currentOrg, loading: orgsLoading } = useOrgs();
const {
orgs,
currentOrg,
loading: orgsLoading,
refetch: refetchOrgs,
} = useOrgs();
const [transferProject] = useBillingTransferAppMutation();
const [showCreateOrgModal, setShowCreateOrgModal] = useState(false);
const [finishOrgCreation, setFinishOrgCreation] = useState(false);
const [preventClose, setPreventClose] = useState(false);
const [newOrgSlug, setNewOrgSlug] = useState<string | undefined>();
const form = useForm<z.infer<typeof transferProjectFormSchema>>({
resolver: zodResolver(transferProjectFormSchema),
@@ -65,29 +81,62 @@ export default function TransferProjectDialog({
},
});
useEffect(() => {
if (session_id) {
setOpen(true);
setFinishOrgCreation(true);
setPreventClose(true);
}
}, [session_id, setOpen]);
useEffect(() => {
if (isNotEmptyValue(newOrgSlug)) {
const newOrg = orgs.find((org) => org.slug === newOrgSlug);
if (newOrg) {
form.setValue('organization', newOrg?.id, { shouldDirty: true });
}
}
}, [newOrgSlug, orgs, form]);
const createNewFormSelected = form.watch('organization') === CREATE_NEW_ORG;
const submitButtonText = createNewFormSelected ? 'Continue' : 'Transfer';
const path = asPath.split('?')[0];
const redirectUrl = `${window.location.origin}${path}`;
const handleCreateDialogOpenStateChange = (newState: boolean) => {
setShowCreateOrgModal(newState);
setOpen(true);
};
const onSubmit = async (
values: z.infer<typeof transferProjectFormSchema>,
) => {
const { organization } = values;
await execPromiseWithErrorToast(
async () => {
await transferProject({
variables: {
appID: project?.id,
organizationID: organization,
},
});
if (organization === CREATE_NEW_ORG) {
setShowCreateOrgModal(true);
setOpen(false);
} else {
await execPromiseWithErrorToast(
async () => {
await transferProject({
variables: {
appID: project?.id,
organizationID: organization,
},
});
const targetOrg = orgs.find((o) => o.id === organization);
await push(`/orgs/${targetOrg.slug}/projects`);
},
{
loadingMessage: 'Transferring project...',
successMessage: 'Project transferred successfully!',
errorMessage: 'Error transferring project. Please try again.',
},
);
const targetOrg = orgs.find((o) => o.id === organization);
await push(`/orgs/${targetOrg.slug}/projects`);
},
{
loadingMessage: 'Transferring project...',
successMessage: 'Project transferred successfully!',
errorMessage: 'Error transferring project. Please try again.',
},
);
}
};
const isUserAdminOfOrg = (org: Org, userId: string) =>
@@ -97,103 +146,161 @@ export default function TransferProjectDialog({
member.user.id === userId,
);
const removeSessionIdFromQuery = () => {
replace({ pathname, query: remainingQuery }, undefined, { shallow: true });
};
const handleFinishOrgCreationCompleted: FinishOrgCreationOnCompletedCb =
async (data) => {
const { Slug } = data;
await refetchOrgs();
setNewOrgSlug(Slug);
setFinishOrgCreation(false);
removeSessionIdFromQuery();
setPreventClose(false);
};
const handleTransferProjectDialogOpenChange = (newValue: boolean) => {
if (preventClose) {
return;
}
if (!newValue) {
setNewOrgSlug(undefined);
}
form.reset();
setOpen(newValue);
};
if (projectLoading || orgsLoading) {
return <LoadingScreen />;
}
return (
<Dialog
open={open}
onOpenChange={(value) => {
form.reset();
setOpen(value);
}}
>
<DialogContent className="z-[9999] text-foreground sm:max-w-xl">
<DialogHeader className="flex gap-2">
<DialogTitle>
Move the current project to a different organization.
</DialogTitle>
<DialogDescription>
To transfer a project between organizations, you must be an{' '}
<span className="font-bold">ADMIN</span> in both.
</DialogDescription>
</DialogHeader>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
<FormField
control={form.control}
name="organization"
render={({ field }) => (
<FormItem>
<FormLabel>Organization</FormLabel>
<Select
onValueChange={field.onChange}
defaultValue={field.value}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Organization" />
</SelectTrigger>
</FormControl>
<SelectContent>
{orgs.map((org) => (
<SelectItem
key={org.id}
value={org.id}
disabled={
org.plan.isFree || // disable the personal org
org.id === currentOrg.id || // disable the current org as it can't be a destination org
!isUserAdminOfOrg(org, currentUserId) // disable orgs that the current user is not admin of
}
>
{org.name}
<Badge
variant={org.plan.isFree ? 'outline' : 'default'}
className={cn(
org.plan.isFree ? 'bg-muted' : '',
'hover:none ml-2 h-5 px-[6px] text-[10px]',
)}
>
{org.plan.name}
</Badge>
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<>
<Dialog open={open} onOpenChange={handleTransferProjectDialogOpenChange}>
<DialogContent className="z-[9999] text-foreground sm:max-w-xl">
<DialogHeader className="flex gap-2">
<DialogTitle>
Move the current project to a different organization.{' '}
</DialogTitle>
<div className="flex justify-end space-x-2">
<Button
variant="secondary"
type="button"
disabled={form.formState.isSubmitting}
onClick={() => {
form.reset();
setOpen(false);
}}
{!finishOrgCreation && (
<DialogDescription>
To transfer a project between organizations, you must be an{' '}
<span className="font-bold">ADMIN</span> in both.
<br />
When transferred to a new organization, the project will adopt
that organizations plan.
</DialogDescription>
)}
</DialogHeader>
{finishOrgCreation ? (
<FinishOrgCreation
onCompleted={handleFinishOrgCreationCompleted}
onError={() => setPreventClose(false)}
/>
) : (
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-4"
>
Cancel
</Button>
<Button
type="submit"
disabled={
form.formState.isSubmitting || !form.formState.isDirty
}
>
{form.formState.isSubmitting ? (
<ActivityIndicator />
) : (
'Transfer'
)}
</Button>
</div>
</form>
</Form>
</DialogContent>
</Dialog>
<FormField
control={form.control}
name="organization"
render={({ field }) => (
<FormItem>
<FormLabel>Organization</FormLabel>
<Select
onValueChange={field.onChange}
value={field.value}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Organization" />
</SelectTrigger>
</FormControl>
<SelectContent>
{orgs.map((org) => (
<SelectItem
key={org.id}
value={org.id}
disabled={
org.plan.isFree || // disable the personal org
org.id === currentOrg.id || // disable the current org as it can't be a destination org
!isUserAdminOfOrg(org, currentUserId) // disable orgs that the current user is not admin of
}
>
{org.name}
<Badge
variant={
org.plan.isFree ? 'outline' : 'default'
}
className={cn(
org.plan.isFree ? 'bg-muted' : '',
'hover:none ml-2 h-5 px-[6px] text-[10px]',
)}
>
{org.plan.name}
</Badge>
</SelectItem>
))}
<SelectItem
key={CREATE_NEW_ORG}
value={CREATE_NEW_ORG}
>
<div className="flex items-center justify-center gap-2">
<Plus
className="h-4 w-4 font-bold"
strokeWidth={3}
/>{' '}
<span>New Organization</span>
</div>
</SelectItem>
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<div className="flex justify-end space-x-2">
<Button
variant="secondary"
type="button"
disabled={form.formState.isSubmitting || preventClose}
onClick={() => {
form.reset();
setOpen(false);
}}
>
Cancel
</Button>
<Button
type="submit"
disabled={
form.formState.isSubmitting || !form.formState.isDirty
}
>
{form.formState.isSubmitting ? (
<ActivityIndicator />
) : (
submitButtonText
)}
</Button>
</div>
</form>
</Form>
)}
</DialogContent>
</Dialog>
<CreateOrgDialog
hideNewOrgButton
isOpen={showCreateOrgModal}
onOpenStateChange={handleCreateDialogOpenStateChange}
redirectUrl={redirectUrl}
/>
</>
);
}

View File

@@ -0,0 +1,167 @@
import { mockMatchMediaValue } from '@/tests/mocks';
import { getOrganizations } from '@/tests/msw/mocks/graphql/getOrganizationQuery';
import tokenQuery from '@/tests/msw/mocks/rest/tokenQuery';
import { mockSession } from '@/tests/orgs/mocks';
import { queryClient, render, waitFor } from '@/tests/orgs/testUtils';
import { CheckoutStatus } from '@/utils/__generated__/graphql';
import { setupServer } from 'msw/node';
import { afterAll, beforeAll, vi } from 'vitest';
import NotificationsTray from './NotificationsTray';
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: vi.fn().mockImplementation(mockMatchMediaValue),
});
export const getUseRouterObject = (session_id?: string) => ({
basePath: '',
pathname: '/orgs/xyz/projects/test-project',
route: '/orgs/[orgSlug]/projects/[appSubdomain]',
asPath: '/orgs/xyz/projects/test-project',
isLocaleDomain: false,
isReady: true,
isPreview: false,
query: {
orgSlug: 'xyz',
appSubdomain: 'test-project',
session_id,
},
push: vi.fn(),
replace: vi.fn(),
reload: vi.fn(),
back: vi.fn(),
prefetch: vi.fn(),
beforePopState: vi.fn(),
events: {
on: vi.fn(),
off: vi.fn(),
emit: vi.fn(),
},
isFallback: false,
});
const mocks = vi.hoisted(() => ({
useRouter: vi.fn(),
useOrganizationNewRequestsLazyQuery: vi.fn(),
usePostOrganizationRequestMutation: vi.fn(),
useOrganizationMemberInvitesLazyQuery: vi.fn(),
fetchPostOrganizationResponseMock: vi.fn(),
userData: vi.fn(),
}));
vi.mock('next/router', () => ({
useRouter: mocks.useRouter,
}));
vi.mock('@nhost/nextjs', async () => {
const actualNhostNextjs = await vi.importActual<any>('@nhost/nextjs');
return {
...actualNhostNextjs,
userData: mocks.userData,
};
});
vi.mock('@/utils/__generated__/graphql', async () => {
const actual = await vi.importActual<any>('@/utils/__generated__/graphql');
return {
...actual,
useOrganizationNewRequestsLazyQuery:
mocks.useOrganizationNewRequestsLazyQuery,
usePostOrganizationRequestMutation:
mocks.usePostOrganizationRequestMutation,
useOrganizationMemberInvitesLazyQuery:
mocks.useOrganizationMemberInvitesLazyQuery,
};
});
const server = setupServer(tokenQuery);
beforeAll(() => {
process.env.NEXT_PUBLIC_NHOST_PLATFORM = 'true';
process.env.NEXT_PUBLIC_ENV = 'production';
});
afterEach(() => {
queryClient.clear();
vi.restoreAllMocks();
});
afterAll(() => {
server.close();
});
const fetchOrganizationMemberInvitesMock = () => [
async () => ({ data: { organizationMemberInvites: [] } }),
{
loading: true,
refetch: vi.fn(),
data: { organizationMemberInvites: [] },
},
];
const fetchOrganizationNewRequestsResponseMock = async () => ({
data: {
organizationNewRequests: [
{
id: 'org-request-id-1',
sessionID: 'session-id-1',
__typename: 'organization_new_request',
},
],
},
});
const fetchPostOrganizationResponseMock = vi.fn();
test('if there is NO session_id in the url the billingPostOrganizationRequest is fetched from the server', async () => {
server.use(getOrganizations);
mocks.useOrganizationMemberInvitesLazyQuery.mockImplementation(
fetchOrganizationMemberInvitesMock,
);
mocks.useRouter.mockImplementation(() => getUseRouterObject());
mocks.userData.mockImplementation(() => mockSession.user);
mocks.useOrganizationNewRequestsLazyQuery.mockImplementation(() => [
fetchOrganizationNewRequestsResponseMock,
]);
mocks.usePostOrganizationRequestMutation.mockImplementation(() => [
fetchPostOrganizationResponseMock.mockImplementation(() => ({
data: {
billingPostOrganizationRequest: {
Status: CheckoutStatus.Open,
Slug: 'newOrgSlug',
ClientSecret: 'very_secret_secret',
__typename: 'PostOrganizationRequestResponse',
},
},
})),
]);
render(<NotificationsTray />);
await waitFor(() => {
/* Wait for the component to be update */
});
expect(fetchPostOrganizationResponseMock).toHaveBeenCalled();
});
test('if there is a session_id in the url the billingPostOrganizationRequest is NOT fetched from the server ', async () => {
server.use(getOrganizations);
mocks.useOrganizationMemberInvitesLazyQuery.mockImplementation(
fetchOrganizationMemberInvitesMock,
);
mocks.useRouter.mockImplementation(() => getUseRouterObject('SESSION_ID'));
mocks.userData.mockImplementation(() => mockSession.user);
mocks.useOrganizationNewRequestsLazyQuery.mockImplementation(() => [
fetchOrganizationNewRequestsResponseMock,
]);
mocks.usePostOrganizationRequestMutation.mockImplementation(() => [
fetchPostOrganizationResponseMock,
]);
render(<NotificationsTray />);
await waitFor(() => {
/* Wait for the component to be update */
});
expect(fetchPostOrganizationResponseMock).not.toHaveBeenCalled();
});

View File

@@ -18,6 +18,7 @@ import {
import { StripeEmbeddedForm } from '@/features/orgs/components/StripeEmbeddedForm';
import { useOrgs } from '@/features/orgs/projects/hooks/useOrgs';
import { execPromiseWithErrorToast } from '@/features/orgs/utils/execPromiseWithErrorToast';
import { isEmptyValue } from '@/lib/utils';
import {
CheckoutStatus,
useDeleteOrganizationMemberInviteMutation,
@@ -38,7 +39,8 @@ type Invite = OrganizationMemberInvitesQuery['organizationMemberInvites'][0];
export default function NotificationsTray() {
const userData = useUserData();
const { asPath, route, push } = useRouter();
const { asPath, route, push, query } = useRouter();
const { session_id } = query;
const { refetch: refetchOrgs } = useOrgs();
const [open, setOpen] = useState(false);
@@ -76,7 +78,6 @@ export default function NotificationsTray() {
userID: userData.id,
},
});
if (organizationNewRequests.length > 0) {
const { sessionID } = organizationNewRequests.at(0);
@@ -109,10 +110,20 @@ export default function NotificationsTray() {
}
};
if (userData && !['/', '/orgs/verify'].includes(route)) {
if (
userData &&
!['/', '/orgs/verify'].includes(route) &&
isEmptyValue(session_id)
) {
checkForPendingOrgRequests();
}
}, [route, userData, getOrganizationNewRequests, postOrganizationRequest]);
}, [
route,
userData,
getOrganizationNewRequests,
postOrganizationRequest,
session_id,
]);
const [acceptInvite] = useOrganizationMemberInviteAcceptMutation();
const [deleteInvite] = useDeleteOrganizationMemberInviteMutation();

View File

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

View File

@@ -0,0 +1,92 @@
import {
CheckoutStatus,
type PostOrganizationRequestMutation,
usePostOrganizationRequestMutation,
} from '@/utils/__generated__/graphql';
import { execPromiseWithErrorToast } from '@/utils/execPromiseWithErrorToast';
import { useAuthenticationStatus } from '@nhost/nextjs';
import { useRouter } from 'next/router';
import { useEffect, useState } from 'react';
export type FinishOrgCreationOnCompletedCb = (
data: PostOrganizationRequestMutation['billingPostOrganizationRequest'],
) => void;
interface UseFinishOrgCreationProps {
onCompleted: FinishOrgCreationOnCompletedCb;
onError?: () => void;
}
function useFinishOrgCreation({
onCompleted,
onError,
}: UseFinishOrgCreationProps): [boolean, CheckoutStatus] {
const router = useRouter();
const { session_id } = router.query;
const { isAuthenticated } = useAuthenticationStatus();
const [loading, setLoading] = useState(false);
const [postOrganizationRequest] = usePostOrganizationRequestMutation();
const [status, setPostOrganizationRequestStatus] =
useState<CheckoutStatus | null>(null);
useEffect(() => {
async function finishOrgCreation() {
if (session_id && isAuthenticated) {
setLoading(true);
execPromiseWithErrorToast(
async () => {
const {
data: { billingPostOrganizationRequest },
} = await postOrganizationRequest({
variables: {
sessionID: session_id as string,
},
});
const { Status } = billingPostOrganizationRequest;
setLoading(false);
setPostOrganizationRequestStatus(Status);
switch (Status) {
case CheckoutStatus.Completed:
onCompleted(billingPostOrganizationRequest);
break;
case CheckoutStatus.Expired:
onError();
throw new Error('Request to create organization has expired');
case CheckoutStatus.Open:
// TODO discuss what to do in this case
onError();
throw new Error(
'Request to create organization with status "Open"',
);
default:
break;
}
},
{
loadingMessage: 'Processing new organization request',
successMessage:
'The new organization has been created successfully.',
errorMessage:
'An error occurred while creating the new organization.',
onError,
},
);
}
}
finishOrgCreation();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [session_id, isAuthenticated]);
return [loading, status];
}
export default useFinishOrgCreation;

View File

@@ -24,7 +24,7 @@ import { getUserRoles } from '@/features/projects/roles/settings/utils/getUserRo
import { type RemoteAppUser } from '@/pages/orgs/[orgSlug]/projects/[appSubdomain]/users';
import type { DialogFormProps } from '@/types/common';
import {
RemoteAppGetUsersDocument,
RemoteAppGetUsersAndAuthRolesDocument,
useGetProjectLocalesQuery,
useGetRolesPermissionsQuery,
useUpdateRemoteAppUserMutation,
@@ -120,7 +120,7 @@ export default function EditUserForm({
const [updateUser] = useUpdateRemoteAppUserMutation({
client: remoteProjectGQLClient,
refetchQueries: [RemoteAppGetUsersDocument],
refetchQueries: [RemoteAppGetUsersAndAuthRolesDocument],
});
const form = useForm<EditUserFormValues>({

View File

@@ -7,7 +7,7 @@ import { useRemoteApplicationGQLClient } from '@/features/orgs/hooks/useRemoteAp
import { useProject } from '@/features/orgs/projects/hooks/useProject';
import { execPromiseWithErrorToast } from '@/features/orgs/utils/execPromiseWithErrorToast';
import type { DialogFormProps } from '@/types/common';
import type { RemoteAppGetUsersQuery } from '@/utils/__generated__/graphql';
import type { RemoteAppGetUsersAndAuthRolesQuery } from '@/utils/__generated__/graphql';
import {
useGetSignInMethodsQuery,
useUpdateRemoteAppUserMutation,
@@ -26,7 +26,7 @@ export interface EditUserPasswordFormProps extends DialogFormProps {
/**
* The selected user.
*/
user: RemoteAppGetUsersQuery['users'][0];
user: RemoteAppGetUsersAndAuthRolesQuery['users'][0];
}
export default function EditUserPasswordForm({

View File

@@ -15,15 +15,11 @@ import { Text } from '@/components/ui/v2/Text';
import { useRemoteApplicationGQLClient } from '@/features/orgs/hooks/useRemoteApplicationGQLClient';
import type { EditUserFormValues } from '@/features/orgs/projects/authentication/users/components/EditUserForm';
import { getReadableProviderName } from '@/features/orgs/projects/authentication/users/utils/getReadableProviderName';
import { useIsPlatform } from '@/features/orgs/projects/common/hooks/useIsPlatform';
import { useLocalMimirClient } from '@/features/orgs/projects/hooks/useLocalMimirClient';
import { useProject } from '@/features/orgs/projects/hooks/useProject';
import { execPromiseWithErrorToast } from '@/features/orgs/utils/execPromiseWithErrorToast';
import { getUserRoles } from '@/features/projects/roles/settings/utils/getUserRoles';
import type { RemoteAppUser } from '@/pages/orgs/[orgSlug]/projects/[appSubdomain]/users';
import type { Role } from '@/types/application';
import {
useDeleteRemoteAppUserRolesMutation,
useGetRolesPermissionsQuery,
useInsertRemoteAppUserRolesMutation,
useRemoteAppDeleteUserMutation,
useUpdateRemoteAppUserMutation,
@@ -33,7 +29,7 @@ import { formatDistance } from 'date-fns';
import kebabCase from 'just-kebab-case';
import dynamic from 'next/dynamic';
import Image from 'next/image';
import { Fragment, useMemo } from 'react';
import { Fragment } from 'react';
const EditUserForm = dynamic(
() =>
@@ -59,14 +55,16 @@ export interface UsersBodyProps {
* @example onSuccessfulAction={() => router.reload()}
*/
onSubmit?: () => Promise<any>;
allAvailableProjectRoles: Role[];
}
export default function UsersBody({ users, onSubmit }: UsersBodyProps) {
export default function UsersBody({
users,
onSubmit,
allAvailableProjectRoles,
}: UsersBodyProps) {
const theme = useTheme();
const isPlatform = useIsPlatform();
const localMimirClient = useLocalMimirClient();
const { openAlertDialog, openDrawer, closeDrawer } = useDialog();
const { project } = useProject();
const remoteProjectGQLClient = useRemoteApplicationGQLClient();
const [deleteUser] = useRemoteAppDeleteUserMutation({
@@ -85,23 +83,6 @@ export default function UsersBody({ users, onSubmit }: UsersBodyProps) {
client: remoteProjectGQLClient,
});
/**
* We want to fetch the queries of the application on this page since we're
* going to use once the user selects a user of their application; we use it
* in the drawer form.
*/
const { data: dataRoles } = useGetRolesPermissionsQuery({
variables: { appId: project?.id },
...(!isPlatform ? { client: localMimirClient } : {}),
});
const { allowed: allowedRoles } = dataRoles?.config?.auth?.user?.roles || {};
const allAvailableProjectRoles = useMemo(
() => getUserRoles(allowedRoles),
[allowedRoles],
);
async function handleEditUser(
values: EditUserFormValues,
user: RemoteAppUser,

View File

@@ -18,7 +18,24 @@ const ruleSchema = Yup.object().shape({
});
const ruleGroupSchema = Yup.object().shape({
operator: Yup.string().required('Please select an operator.'),
operator: Yup.string().test(
'operator',
'Please select an operator.',
(selectedOperator, ctx) => {
// `from` is part of the Yup API, but it's not typed.
// @ts-ignore
const [, { value }] = ctx.from;
if (
value.filter &&
Object.keys(value.filter).length > 0 &&
!selectedOperator
) {
return false;
}
return true;
},
),
rules: Yup.array().of(ruleSchema),
groups: Yup.array().of(Yup.lazy(() => ruleGroupSchema) as any),
});

View File

@@ -158,7 +158,7 @@ export default function LogsBody({ logsData, loading, error }: LogsBodyProps) {
);
}
if (!logsData || logsData.logs?.length === 0) {
if (logsData?.logs?.length === 0) {
return (
<LogsBodyCustomMessage>
<Text className="truncate font-mono text-xs- font-normal">

View File

@@ -56,17 +56,11 @@ export default function LogsHeader({
const { data, loading: loadingServiceLabelValues } =
useGetServiceLabelValuesQuery({
variables: { appID: project?.id },
skip: !project?.id,
});
useEffect(() => {
if (!loadingServiceLabelValues) {
const labels = data.getServiceLabelValues ?? [];
setServiceLabels(labels.map((l) => ({ label: l, value: l })));
}
}, [loadingServiceLabelValues, data]);
useEffect(() => {
if (!loadingServiceLabelValues) {
if (!loadingServiceLabelValues && data) {
const labels = data.getServiceLabelValues ?? [];
const labelMappings = {

View File

@@ -5,6 +5,7 @@ import { Button } from '@/components/ui/v3/button';
import { useIsPlatform } from '@/features/orgs/projects/common/hooks/useIsPlatform';
import { useCurrentOrg } from '@/features/orgs/projects/hooks/useCurrentOrg';
import { useProject } from '@/features/orgs/projects/hooks/useProject';
import UpgradeProjectDialog from '@/features/orgs/projects/overview/components/OverviewTopBar/UpgradeProjectDialog';
import { formatDistanceToNowStrict, parseISO } from 'date-fns';
import Image from 'next/image';
import Link from 'next/link';
@@ -15,6 +16,8 @@ export default function OverviewTopBar() {
const { project } = useProject();
const { maintenanceActive } = useUI();
const isFreeProject = !!org?.plan.isFree;
if (!isPlatform) {
return (
<div className="flex flex-row place-content-between items-center py-5">
@@ -87,22 +90,24 @@ export default function OverviewTopBar() {
</div>
</div>
</div>
<Link
href={`/orgs/${org?.slug}/projects/${project?.subdomain}/settings`}
passHref
legacyBehavior
>
<Button
variant="outline"
className="gap-2"
color="secondary"
disabled={maintenanceActive}
<div className="flex content-center gap-4">
{isFreeProject && <UpgradeProjectDialog />}
<Link
href={`/orgs/${org?.slug}/projects/${project?.subdomain}/settings`}
passHref
legacyBehavior
>
Settings
<CogIcon className="h-4 w-4" />
</Button>
</Link>
<Button
variant="outline"
className="gap-2"
color="secondary"
disabled={maintenanceActive}
>
Settings
<CogIcon className="h-4 w-4" />
</Button>
</Link>
</div>
</div>
);
}

View File

@@ -0,0 +1,20 @@
import { OpenTransferDialogButton } from '@/components/common/OpenTransferDialogButton';
import { TransferProjectDialog } from '@/features/orgs/components/common/TransferProjectDialog';
import { useCallback, useState } from 'react';
function UpgradeProjectDialog() {
const [open, setOpen] = useState(false);
const handleDialogOpen = useCallback(() => setOpen(true), []);
return (
<>
<OpenTransferDialogButton
buttonText="Upgrade project"
onClick={handleDialogOpen}
/>
<TransferProjectDialog open={open} setOpen={setOpen} />
</>
);
}
export default UpgradeProjectDialog;

View File

@@ -44,7 +44,7 @@ export default function ResourcesConfirmationDialog({
const billableResources = calculateBillableResources(
{
replicas: formValues.database?.replicas,
replicas: 1,
vcpu: formValues.database?.vcpu,
memory: formValues.database?.memory,
},
@@ -128,7 +128,6 @@ export default function ResourcesConfirmationDialog({
included in the {proPlan.name} plan.
</Text>
)}
<Box className="grid grid-flow-row gap-4">
{/* <Box className="grid justify-between grid-flow-col gap-2">
<Text className="font-medium">{proPlan.name} Plan</Text>
@@ -227,7 +226,6 @@ export default function ResourcesConfirmationDialog({
</Text>
)}
</Box>
<Box className="grid grid-flow-row gap-2">
<Button
color={totalBillableVCPU > 0 ? 'primary' : 'error'}

View File

@@ -40,9 +40,16 @@ function getInitialServiceResources(
data: GetResourcesQuery,
service: Exclude<keyof GetResourcesQuery['config'], '__typename'>,
) {
const { compute, replicas, autoscaler, ...rest } =
if (service === 'postgres') {
const { compute, ...rest } = data?.config?.[service]?.resources || {};
return {
vcpu: compute?.cpu || 0,
memory: compute?.memory || 0,
rest,
};
}
const { compute, autoscaler, replicas, ...rest } =
data?.config?.[service]?.resources || {};
return {
replicas,
vcpu: compute?.cpu || 0,
@@ -103,11 +110,8 @@ export default function ResourcesForm() {
totalAvailableVCPU: totalInitialVCPU || 2000,
totalAvailableMemory: totalInitialMemory || 4096,
database: {
replicas: initialDatabaseResources.replicas || 1,
vcpu: initialDatabaseResources.vcpu || 1000,
memory: initialDatabaseResources.memory || 2048,
autoscale: !!initialDatabaseResources.autoscale || false,
maxReplicas: initialDatabaseResources.autoscale?.maxReplicas || 10,
},
hasura: {
replicas: initialHasuraResources.replicas || 1,
@@ -160,7 +164,7 @@ export default function ResourcesForm() {
const billableResources = calculateBillableResources(
{
replicas: initialDatabaseResources.replicas,
replicas: 1,
vcpu: initialDatabaseResources.vcpu,
},
{
@@ -207,12 +211,6 @@ export default function ResourcesForm() {
cpu: sanitizedValues.database.vcpu,
memory: sanitizedValues.database.memory,
},
replicas: sanitizedValues.database.replicas,
autoscaler: sanitizedValues.database.autoscale
? {
maxReplicas: sanitizedValues.database.maxReplicas,
}
: null,
...sanitizedInitialDatabaseResources.rest,
},
},
@@ -268,8 +266,6 @@ export default function ResourcesForm() {
postgres: {
resources: {
compute: null,
replicas: null,
autoscaler: null,
...sanitizedInitialDatabaseResources.rest,
},
},
@@ -341,9 +337,6 @@ export default function ResourcesForm() {
totalAvailableVCPU: 2000,
totalAvailableMemory: 4096,
database: {
replicas: 1,
maxReplicas: 1,
autoscale: false,
vcpu: 1000,
memory: 2048,
},

View File

@@ -49,7 +49,7 @@ export default function ResourcesFormFooter() {
const billableResources = calculateBillableResources(
{
replicas: database?.replicas,
replicas: 1, // database replica is always one
vcpu: database?.vcpu,
},
{

View File

@@ -43,10 +43,6 @@ fragment ServiceResources on ConfigConfig {
cpu
memory
}
replicas
autoscaler {
maxReplicas
}
}
}
storage {

View File

@@ -76,7 +76,7 @@ export const MAX_SERVICE_MEMORY =
RESOURCE_VCPU_MEMORY_RATIO *
RESOURCE_MEMORY_MULTIPLIER;
const serviceValidationSchema = Yup.object({
const genericServiceValidationSchema = Yup.object({
replicas: Yup.number()
.label('Replicas')
.required()
@@ -115,6 +115,18 @@ const serviceValidationSchema = Yup.object({
),
});
const postgresServiceValidationSchema = Yup.object({
vcpu: Yup.number()
.label('vCPUs')
.required()
.min(MIN_SERVICE_VCPU)
.max(MAX_SERVICE_VCPU),
memory: Yup.number()
.required()
.min(MIN_SERVICE_MEMORY)
.max(MAX_SERVICE_MEMORY)
});
export const resourceSettingsValidationSchema = Yup.object({
enabled: Yup.boolean(),
totalAvailableVCPU: Yup.number()
@@ -147,10 +159,10 @@ export const resourceSettingsValidationSchema = Yup.object({
parent.storage.memory ===
totalAvailableMemory,
),
database: serviceValidationSchema.required(),
hasura: serviceValidationSchema.required(),
auth: serviceValidationSchema.required(),
storage: serviceValidationSchema.required(),
database: postgresServiceValidationSchema.required(),
hasura: genericServiceValidationSchema.required(),
auth: genericServiceValidationSchema.required(),
storage: genericServiceValidationSchema.required(),
});
export type ResourceSettingsFormValues = Yup.InferType<

View File

@@ -1,17 +1,35 @@
import { useIsPlatform } from '@/features/orgs/projects/common/hooks/useIsPlatform';
import { useCurrentOrg } from '@/features/orgs/projects/hooks/useCurrentOrg';
import { useProject } from '@/features/orgs/projects/hooks/useProject';
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
import { useRouter } from 'next/router';
import { useEffect } from 'react';
/**
* Redirects to 404 page if either currentWorkspace/currentProject resolves to undefined.
* Redirects to 404 page if either currentWorkspace/currentProject resolves to undefined
* or if the current pathname is not a valid organization/project.
* Not applicable if running dashboard with local Nhost backend.
*/
export default function useNotFoundRedirect() {
const router = useRouter();
const {
query: { orgSlug, workspaceSlug, appSubdomain, updating, appSlug },
query: {
orgSlug: urlOrgSlug,
workspaceSlug: urlWorkspaceSlug,
appSubdomain: urlAppSubdomain,
updating,
appSlug: urlAppSlug,
},
isReady,
} = router;
const { project, loading: projectLoading } = useProject();
const isPlatform = useIsPlatform();
const { org, loading: orgLoading } = useCurrentOrg();
const { subdomain: projectSubdomain } = project || {};
const { slug: currentOrgSlug } = org || {};
const { currentProject, currentWorkspace, loading } =
useCurrentWorkspaceAndProject();
@@ -23,6 +41,10 @@ export default function useNotFoundRedirect() {
!isReady ||
// If the current workspace and project are not loaded, we don't want to redirect to 404
loading ||
// If the project is loading, we don't want to redirect to 404
projectLoading ||
// If the org is loading, we don't want to redirect to 404
orgLoading ||
// If we're already on the 404 page, we don't want to redirect to 404
router.pathname === '/404' ||
router.pathname === '/' ||
@@ -31,12 +53,16 @@ export default function useNotFoundRedirect() {
router.pathname === '/run-one-click-install' ||
router.pathname.includes('/orgs/_') ||
router.pathname.includes('/orgs/_/projects/_') ||
orgSlug ||
(orgSlug && appSubdomain) ||
(!isPlatform && router.pathname.includes('/orgs/local')) ||
(!isPlatform && router.pathname.includes('/orgs/[orgSlug]/projects')) ||
// If we are on a valid org and project, we don't want to redirect to 404
(urlOrgSlug && currentOrgSlug && urlAppSubdomain && projectSubdomain) ||
// If we are on a valid org and no project is selected, we don't want to redirect to 404
(urlOrgSlug && currentOrgSlug && !urlAppSubdomain && !projectSubdomain) ||
// If we are on a valid workspace and project, we don't want to redirect to 404
(workspaceSlug && currentWorkspace && appSlug && currentProject) ||
(urlWorkspaceSlug && currentWorkspace && urlAppSlug && currentProject) ||
// If we are on a valid workspace and no project is selected, we don't want to redirect to 404
(workspaceSlug && currentWorkspace && !appSlug && !currentProject)
(urlWorkspaceSlug && currentWorkspace && !urlAppSlug && !currentProject)
) {
return;
}
@@ -47,11 +73,16 @@ export default function useNotFoundRedirect() {
currentWorkspace,
isReady,
loading,
appSubdomain,
appSlug,
urlAppSubdomain,
urlAppSlug,
router,
updating,
workspaceSlug,
orgSlug,
projectLoading,
orgLoading,
currentOrgSlug,
projectSubdomain,
urlWorkspaceSlug,
urlOrgSlug,
isPlatform,
]);
}

View File

@@ -37,14 +37,24 @@ function getInitialServiceResources(
data: GetResourcesQuery,
service: Exclude<keyof GetResourcesQuery['config'], '__typename'>,
) {
const { compute, replicas, autoscaler } =
if (service === 'postgres') {
const { compute, ...rest } = data?.config?.[service]?.resources || {};
return {
replicas: 1,
vcpu: compute?.cpu || 0,
memory: compute?.memory || 0,
autoscale: null,
rest,
};
}
const { compute, autoscaler, replicas, ...rest } =
data?.config?.[service]?.resources || {};
return {
replicas,
vcpu: compute?.cpu || 0,
memory: compute?.memory || 0,
autoscale: autoscaler || null,
rest,
};
}
@@ -190,12 +200,6 @@ export default function ResourcesForm() {
cpu: formValues.database.vcpu,
memory: formValues.database.memory,
},
replicas: formValues.database.replicas,
autoscaler: formValues.database.autoscale
? {
maxReplicas: formValues.database.maxReplicas,
}
: null,
}
: null,
},

View File

@@ -23,7 +23,11 @@ fragment RemoteAppGetUsers on users {
disabled
}
query remoteAppGetUsers($where: users_bool_exp!, $limit: Int!, $offset: Int!) {
query remoteAppGetUsersAndAuthRoles(
$where: users_bool_exp!
$limit: Int!
$offset: Int!
) {
users(
where: $where
limit: $limit
@@ -42,6 +46,9 @@ query remoteAppGetUsers($where: users_bool_exp!, $limit: Int!, $offset: Int!) {
count
}
}
authRoles {
role
}
}
query remoteAppGetUsersCustom(

View File

@@ -0,0 +1,35 @@
import { isEmptyValue } from './utils';
test('returns true when the value is undefined or "undefined"', () => {
expect(isEmptyValue(undefined)).toBe(true)
expect(isEmptyValue('undefined')).toBe(true)
})
test('returns true when the value is null or "null"', () => {
expect(isEmptyValue(null)).toBe(true)
expect(isEmptyValue('null')).toBe(true)
})
test('returns true when the value is an empty string', () => {
expect(isEmptyValue('')).toBe(true)
})
test('returns true when the value is "NaN" or not a number', () => {
expect(isEmptyValue("NaN")).toBe(true)
expect(isEmptyValue(NaN)).toBe(true)
})
test('returns true when the value is an empty object or array', () => {
expect(isEmptyValue({})).toBe(true)
expect(isEmptyValue([])).toBe(true)
})
test('returns false when the value is has at least one property or the array has at least on item in it', () => {
expect(isEmptyValue({foo: 'Bar'})).toBe(false)
expect(isEmptyValue(['foo', 'bar'])).toBe(false)
})
test('returns false when the value is either a number or string', () => {
expect(isEmptyValue(1234)).toBe(false)
expect(isEmptyValue('Hello there')).toBe(false)
})

View File

@@ -4,3 +4,20 @@ import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
export function isEmptyValue<T>(value: T) {
return (
value === undefined ||
value === 'undefined' ||
value === null ||
value === '' ||
value === 'null' ||
value === 'NaN' ||
(typeof value === 'number' && Number.isNaN(value)) ||
(typeof value === 'object' && Object.keys(value).length === 0)
)
}
export function isNotEmptyValue<T>(value: T): value is Exclude<T, undefined | null> {
return !isEmptyValue(value)
}

View File

@@ -13,15 +13,15 @@ import { CreateUserForm } from '@/features/authentication/users/components/Creat
import { UsersBody } from '@/features/authentication/users/components/UsersBody';
import { ProjectLayout } from '@/features/orgs/layout/ProjectLayout';
import { useRemoteApplicationGQLClient } from '@/hooks/useRemoteApplicationGQLClient';
import type { RemoteAppGetUsersQuery } from '@/utils/__generated__/graphql';
import { useRemoteAppGetUsersQuery } from '@/utils/__generated__/graphql';
import type { RemoteAppGetUsersAndAuthRolesQuery } from '@/utils/__generated__/graphql';
import { useRemoteAppGetUsersAndAuthRolesQuery } from '@/utils/__generated__/graphql';
import debounce from 'lodash.debounce';
import Router, { useRouter } from 'next/router';
import type { ChangeEvent, ReactElement } from 'react';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
export type RemoteAppUser = Exclude<
RemoteAppGetUsersQuery['users'][0],
RemoteAppGetUsersAndAuthRolesQuery['users'][0],
'__typename'
>;
@@ -75,7 +75,7 @@ export default function UsersPage() {
data: dataRemoteAppUsers,
refetch: refetchProjectUsers,
loading: loadingRemoteAppUsersQuery,
} = useRemoteAppGetUsersQuery({
} = useRemoteAppGetUsersAndAuthRolesQuery({
variables: remoteAppGetUserVariables,
client: remoteProjectGQLClient,
});

View File

@@ -30,7 +30,7 @@ interface LogsFilters {
}
export default function LogsPage() {
const { project } = useProject();
const { project, loading: loadingProject } = useProject();
// create a client that sends http requests to Hasura but websocket requests to Bragi
const clientWithSplit = useRemoteApplicationGQLClientWithSubscriptions();
@@ -43,13 +43,20 @@ export default function LogsPage() {
service: AvailableLogsService.ALL,
});
const { data, error, subscribeToMore, client, loading, refetch } =
useGetProjectLogsQuery({
variables: { appID: project?.id, ...filters },
client: clientWithSplit,
fetchPolicy: 'cache-and-network',
notifyOnNetworkStatusChange: true,
});
const {
data,
error,
subscribeToMore,
client,
loading: loadingLogs,
refetch,
} = useGetProjectLogsQuery({
variables: { appID: project?.id, ...filters },
client: clientWithSplit,
fetchPolicy: 'cache-and-network',
notifyOnNetworkStatusChange: true,
skip: !project,
});
const subscribeToMoreLogs = useCallback(
() =>
@@ -102,15 +109,19 @@ export default function LogsPage() {
);
useEffect(() => {
if (!project || loadingProject) {
return;
}
if (filters.to && subscriptionReturn.current !== null) {
subscriptionReturn.current();
subscriptionReturn.current = null;
return () => {};
return;
}
if (filters.to) {
return () => {};
return;
}
if (subscriptionReturn.current) {
@@ -120,9 +131,7 @@ export default function LogsPage() {
// This will open the websocket connection and it will return a function to close it.
subscriptionReturn.current = subscribeToMoreLogs();
return () => {};
}, [filters, subscribeToMoreLogs, client]);
}, [filters, subscribeToMoreLogs, client, project, loadingProject]);
const onSubmitFilterValues = useCallback(
async (values: LogsFilterFormValues) => {
@@ -132,6 +141,8 @@ export default function LogsPage() {
[setFilters, refetch],
);
const loading = loadingProject || loadingLogs;
return (
<div className="flex h-full w-full flex-col">
<RetryableErrorBoundary>

View File

@@ -13,15 +13,16 @@ import { useRemoteApplicationGQLClient } from '@/features/orgs/hooks/useRemoteAp
import { ProjectLayout } from '@/features/orgs/layout/ProjectLayout';
import { CreateUserForm } from '@/features/orgs/projects/authentication/users/components/CreateUserForm';
import { UsersBody } from '@/features/orgs/projects/authentication/users/components/UsersBody';
import type { RemoteAppGetUsersQuery } from '@/utils/__generated__/graphql';
import { useRemoteAppGetUsersQuery } from '@/utils/__generated__/graphql';
import { getUserRoles } from '@/features/projects/roles/settings/utils/getUserRoles';
import type { RemoteAppGetUsersAndAuthRolesQuery } from '@/utils/__generated__/graphql';
import { useRemoteAppGetUsersAndAuthRolesQuery } from '@/utils/__generated__/graphql';
import debounce from 'lodash.debounce';
import Router, { useRouter } from 'next/router';
import type { ChangeEvent, ReactElement } from 'react';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
export type RemoteAppUser = Exclude<
RemoteAppGetUsersQuery['users'][0],
RemoteAppGetUsersAndAuthRolesQuery['users'][0],
'__typename'
>;
@@ -72,14 +73,13 @@ export default function UsersPage() {
);
const {
data: dataRemoteAppUsers,
data: dataRemoteAppUsersAndAuthRoles,
refetch: refetchProjectUsers,
loading: loadingRemoteAppUsersQuery,
} = useRemoteAppGetUsersQuery({
} = useRemoteAppGetUsersAndAuthRolesQuery({
variables: remoteAppGetUserVariables,
client: remoteProjectGQLClient,
});
/**
* This function will remove query params from the URL.
*
@@ -175,13 +175,13 @@ export default function UsersPage() {
}
const userCount = searchString
? dataRemoteAppUsers?.filteredUsersAggreggate.aggregate.count
: dataRemoteAppUsers?.usersAggregate?.aggregate?.count;
? dataRemoteAppUsersAndAuthRoles?.filteredUsersAggreggate.aggregate.count
: dataRemoteAppUsersAndAuthRoles?.usersAggregate?.aggregate?.count;
setNrOfPages(Math.ceil(userCount / limit.current));
}, [
dataRemoteAppUsers?.filteredUsersAggreggate.aggregate.count,
dataRemoteAppUsers?.usersAggregate.aggregate.count,
dataRemoteAppUsersAndAuthRoles?.filteredUsersAggreggate.aggregate.count,
dataRemoteAppUsersAndAuthRoles?.usersAggregate.aggregate.count,
loadingRemoteAppUsersQuery,
searchString,
]);
@@ -208,17 +208,26 @@ export default function UsersPage() {
}
const users = useMemo(
() => dataRemoteAppUsers?.users.map((user) => user) ?? [],
[dataRemoteAppUsers],
() => dataRemoteAppUsersAndAuthRoles?.users.map((user) => user) ?? [],
[dataRemoteAppUsersAndAuthRoles],
);
const usersCount = useMemo(
() => dataRemoteAppUsers?.usersAggregate?.aggregate?.count ?? -1,
[dataRemoteAppUsers],
() =>
dataRemoteAppUsersAndAuthRoles?.usersAggregate?.aggregate?.count ?? -1,
[dataRemoteAppUsersAndAuthRoles],
);
const authRoles = (dataRemoteAppUsersAndAuthRoles?.authRoles || []).map(
(authRole) => authRole.role,
);
const allAvailableProjectRoles = useMemo(
() => getUserRoles(authRoles),
[authRoles],
);
const thereAreUsers =
dataRemoteAppUsers?.filteredUsersAggreggate.aggregate.count ||
dataRemoteAppUsersAndAuthRoles?.filteredUsersAggreggate.aggregate.count ||
usersCount <= 0;
if (loadingRemoteAppUsersQuery) {
@@ -315,8 +324,8 @@ export default function UsersPage() {
OAuth Providers
</Text>
</Box>
{dataRemoteAppUsers?.filteredUsersAggreggate.aggregate.count ===
0 &&
{dataRemoteAppUsersAndAuthRoles?.filteredUsersAggreggate.aggregate
.count === 0 &&
usersCount !== 0 && (
<Box className="flex flex-col items-center justify-center space-y-5 border-x border-b px-48 py-12">
<UserIcon
@@ -336,22 +345,27 @@ export default function UsersPage() {
)}
{thereAreUsers && (
<div className="grid grid-flow-row gap-4">
<UsersBody users={users} onSubmit={refetchProjectUsers} />
<UsersBody
users={users}
onSubmit={refetchProjectUsers}
allAvailableProjectRoles={allAvailableProjectRoles}
/>
<Pagination
className="px-2"
totalNrOfPages={nrOfPages}
currentPageNumber={currentPage}
totalNrOfElements={
searchString
? dataRemoteAppUsers?.filteredUsersAggreggate.aggregate
.count
: dataRemoteAppUsers?.usersAggregate?.aggregate?.count
? dataRemoteAppUsersAndAuthRoles?.filteredUsersAggreggate
.aggregate.count
: dataRemoteAppUsersAndAuthRoles?.usersAggregate
?.aggregate?.count
}
itemsLabel="users"
elementsPerPage={
searchString
? dataRemoteAppUsers?.filteredUsersAggreggate.aggregate
.count
? dataRemoteAppUsersAndAuthRoles?.filteredUsersAggreggate
.aggregate.count
: limit.current
}
onPrevPageClick={async () => {

View File

@@ -1,31 +1,20 @@
'use client';
import { BaseLayout } from '@/components/layout/BaseLayout';
import { Header } from '@/components/layout/Header';
import { RetryableErrorBoundary } from '@/components/presentational/RetryableErrorBoundary';
import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
import { FinishOrgCreationProcess } from '@/features/orgs/components/common/FinishOrgCreationProcess';
import { useFinishOrgCreation } from '@/features/orgs/hooks/useFinishOrgCreation';
import { useIsPlatform } from '@/features/projects/common/hooks/useIsPlatform';
import {
CheckoutStatus,
useGetOrganizationByIdLazyQuery,
usePostOrganizationRequestMutation,
} from '@/utils/__generated__/graphql';
import { execPromiseWithErrorToast } from '@/utils/execPromiseWithErrorToast';
import type { PostOrganizationRequestMutation } from '@/utils/__generated__/graphql';
import { useAuthenticationStatus } from '@nhost/nextjs';
import { useRouter } from 'next/router';
import { useEffect, useState } from 'react';
import { useCallback, useEffect } from 'react';
export default function PostCheckout() {
const router = useRouter();
const { session_id } = router.query;
const isPlatform = useIsPlatform();
const [loading, setLoading] = useState(false);
const [postOrganizationRequest] = usePostOrganizationRequestMutation();
const { isAuthenticated, isLoading } = useAuthenticationStatus();
const [fetchOrg] = useGetOrganizationByIdLazyQuery();
const [postOrganizationRequestStatus, setPostOrganizationRequestStatus] =
useState<CheckoutStatus | null>(null);
useEffect(() => {
if (!isPlatform || isLoading || isAuthenticated) {
return;
@@ -34,99 +23,30 @@ export default function PostCheckout() {
router.push('/signin');
}, [isLoading, isAuthenticated, router, isPlatform]);
useEffect(() => {
(async () => {
if (session_id && isAuthenticated) {
setLoading(true);
const onCompleted = useCallback(
(
data: PostOrganizationRequestMutation['billingPostOrganizationRequest'],
) => {
const { Slug } = data;
router.push(`/orgs/${Slug}/projects`);
},
[router],
);
execPromiseWithErrorToast(
async () => {
const {
data: { billingPostOrganizationRequest },
} = await postOrganizationRequest({
variables: {
sessionID: session_id as string,
},
});
const { Status, Slug } = billingPostOrganizationRequest;
setLoading(false);
setPostOrganizationRequestStatus(Status);
switch (Status) {
case CheckoutStatus.Completed:
await router.push(`/orgs/${Slug}/projects`);
break;
case CheckoutStatus.Expired:
throw new Error('Request to create organization has expired');
case CheckoutStatus.Open:
// TODO discuss what to do in this case
throw new Error(
'Request to create organization with status "Open"',
);
default:
break;
}
},
{
loadingMessage: 'Processing new organization request',
successMessage:
'The new organization has been created successfully.',
errorMessage:
'An error occurred while creating the new organization.',
},
);
}
})();
}, [session_id, postOrganizationRequest, router, fetchOrg, isAuthenticated]);
const [loading, status] = useFinishOrgCreation({ onCompleted });
return (
<BaseLayout className="flex h-screen flex-col">
<Header className="flex py-1" />
<div className="flex h-screen w-full flex-col">
<RetryableErrorBoundary errorMessageProps={{ className: 'pt-20' }}>
<div className="relative flex flex-auto overflow-x-hidden">
{loading && (
<div className="flex h-full w-full flex-col items-center justify-center space-y-2">
<ActivityIndicator
circularProgressProps={{ className: 'w-6 h-6' }}
/>
<span>Processing new organization request</span>
</div>
)}
{!loading &&
postOrganizationRequestStatus === CheckoutStatus.Completed && (
<div className="flex h-full w-full flex-col items-center justify-center space-y-2">
<ActivityIndicator
circularProgressProps={{ className: 'w-6 h-6' }}
/>
<span>Organization created successfully. Redirecting...</span>
</div>
)}
{!loading &&
postOrganizationRequestStatus === CheckoutStatus.Expired && (
<div className="flex h-full w-full flex-col items-center justify-center space-y-2">
<span>
Error occurred while creating the organization. Please try
again.
</span>
</div>
)}
{!loading &&
postOrganizationRequestStatus === CheckoutStatus.Open && (
<div className="flex h-full w-full flex-col items-center justify-center space-y-2">
<span>Organization creation is pending...</span>
</div>
)}
</div>
</RetryableErrorBoundary>
<FinishOrgCreationProcess
loading={loading}
status={status}
loadingMessage="Processing new organization request"
successMessage="Organization created successfully. Redirecting..."
pendingMessage="Organization creation is pending..."
errorMessage="Error occurred while creating the organization. Please try again."
/>
</div>
</BaseLayout>
);

View File

@@ -1,7 +1,17 @@
import '@testing-library/jest-dom/extend-expect';
import matchers from '@testing-library/jest-dom/matchers';
import fetch from 'node-fetch';
import { expect } from 'vitest';
import { expect, vi } from 'vitest';
// Mock the ResizeObserver
const ResizeObserverMock = vi.fn(() => ({
observe: vi.fn(),
unobserve: vi.fn(),
disconnect: vi.fn(),
}));
// Stub the global ResizeObserver
vi.stubGlobal('ResizeObserver', ResizeObserverMock);
// @ts-ignore
global.fetch = fetch;

View File

@@ -141,3 +141,56 @@ export const mockOrganization: Organization = {
],
__typename: 'organizations',
};
export const mockOrganizations: Organization[] = [
{
id: 'org-1',
name: "User's Personal Organization",
slug: 'ynrnjteywyxjhlmgzgif',
status: Organization_Status_Enum.Ok,
plan: {
id: 'starter',
name: 'Starter',
price: 0,
deprecated: false,
individual: true,
isFree: true,
featureMaxDbSize: 1,
__typename: 'plans',
},
apps: [],
members: [],
__typename: 'organizations',
},
{
id: 'org-2',
name: 'Second First Try',
slug: 'kbdoxvsoisppkrwzjhwl',
status: Organization_Status_Enum.Ok,
plan: {
id: 'pro',
name: 'Pro',
price: 25,
deprecated: false,
individual: false,
isFree: false,
featureMaxDbSize: 10,
__typename: 'plans',
},
apps: [],
members: [],
__typename: 'organizations',
},
];
export const newOrg: Organization = {
...mockOrganization,
id: 'newOrg',
name: 'New Org',
slug: 'new-org',
};
export const mockOrganizationsWithNewOrg: Organization[] = [
...mockOrganizations,
newOrg,
];

View File

@@ -0,0 +1,22 @@
import { mockOrganization, mockOrganizations } from '@/tests/mocks';
import nhostGraphQLLink from './nhostGraphQLLink';
export const getOrganizations = nhostGraphQLLink.query(
'getOrganizations',
(_req, res, ctx) =>
res(
ctx.data({
organizations: mockOrganizations,
}),
),
);
export const getOrganization = nhostGraphQLLink.query(
'getOrganization',
(_req, res, ctx) =>
res(
ctx.data({
organizations: [{ ...mockOrganization }],
}),
),
);

View File

@@ -0,0 +1,12 @@
import { mockApplication } from '@/tests/mocks';
import nhostGraphQLLink from './nhostGraphQLLink';
export const getProjectQuery = nhostGraphQLLink.query(
'getProject',
(_req, res, ctx) =>
res(
ctx.data({
apps: [{ ...mockApplication, githubRepository: null }],
}),
),
);

View File

@@ -0,0 +1,22 @@
import nhostGraphQLLink from './nhostGraphQLLink';
export const organizationMemberInvites = nhostGraphQLLink.query(
'organizationMemberInvites',
(_req, res, ctx) => res(ctx.data({ organizationMemberInvites: [] })),
);
export const organizationNewRequests = nhostGraphQLLink.query(
'organizationNewRequests',
(_req, res, ctx) =>
res(
ctx.data({
organizationNewRequests: [
{
id: 'org-request-id-1',
sessionID: 'session-id-1',
__typename: 'organization_new_request',
},
],
}),
),
);

View File

@@ -0,0 +1,73 @@
import nhostGraphQLLink from './nhostGraphQLLink';
export const prefetchNewAppQuery = nhostGraphQLLink.query(
'PrefetchNewApp',
(_req, res, ctx) =>
res(
ctx.data({
regions: [
{
id: 'dd6f8e01-35a9-4ba6-8dc6-ed972f2db93c',
city: 'Frankfurt',
active: true,
country: { code: 'DE', name: 'Germany', __typename: 'countries' },
__typename: 'regions',
},
{
id: 'd44dc594-022f-4aa7-84b2-0cebee5f1d13',
city: 'London',
active: false,
country: {
code: 'GB',
name: 'United Kingdom',
__typename: 'countries',
},
__typename: 'regions',
},
{
id: '55985cd4-af14-4d2a-90a5-2a1253ebc1db',
city: 'N. Virginia',
active: true,
country: {
code: 'US',
name: 'United States of America',
__typename: 'countries',
},
__typename: 'regions',
},
{
id: '8fe50a9b-ef48-459c-9dd0-a2fd28968224',
city: 'Singapore',
active: false,
country: { code: 'SG', name: 'Singapore', __typename: 'countries' },
__typename: 'regions',
},
],
plans: [
{
id: 'dc5e805e-1bef-4d43-809e-9fdf865e211a',
name: 'Pro',
isDefault: false,
isFree: false,
price: 25,
featureBackupEnabled: true,
featureCustomDomainsEnabled: true,
featureMaxDbSize: 10,
__typename: 'plans',
},
{
id: '9860b992-5658-4031-8580-d8135e18db7c',
name: 'Team',
isDefault: false,
isFree: false,
price: 599,
featureBackupEnabled: true,
featureCustomDomainsEnabled: true,
featureMaxDbSize: 10,
__typename: 'plans',
},
],
workspaces: [],
}),
),
);

View File

@@ -24,6 +24,8 @@ import {
import { RouterContext } from 'next/dist/shared/lib/router-context.shared-runtime';
import type { PropsWithChildren, ReactElement } from 'react';
import { Toaster } from 'react-hot-toast';
import { vi } from 'vitest';
import nhostGraphQLLink from '../msw/mocks/graphql/nhostGraphQLLink';
// Client-side cache, shared for the whole session of the user in the browser.
const emotionCache = createEmotionCache();
@@ -109,5 +111,53 @@ async function waitForElementToBeRemoved<T>(
}
}
const graphqlRequestHandlerFactory = (
operationName: string,
type: 'mutation' | 'query',
responsePromise,
) =>
nhostGraphQLLink[type](operationName, async (_req, res, ctx) => {
const data = await responsePromise;
return res(ctx.data(data));
});
/* Helper function to pause responses to be able to test loading states */
export const createGraphqlMockResolver = (
operationName: string,
type: 'mutation' | 'query',
defaultResponse?: any,
) => {
let resolver;
const responsePromise = new Promise((resolve) => {
resolver = resolve;
});
return {
handler: graphqlRequestHandlerFactory(operationName, type, responsePromise),
resolve: (response = undefined) => resolver(response ?? defaultResponse),
};
};
export const mockPointerEvent = () => {
// Note: Workaround based on https://github.com/radix-ui/primitives/issues/1382#issuecomment-1122069313
class MockPointerEvent extends Event {
button: number;
ctrlKey: boolean;
pointerType: string;
constructor(type: string, props: PointerEventInit) {
super(type, props);
this.button = props.button || 0;
this.ctrlKey = props.ctrlKey || false;
this.pointerType = props.pointerType || 'mouse';
}
}
window.PointerEvent = MockPointerEvent as any;
window.HTMLElement.prototype.scrollIntoView = vi.fn();
window.HTMLElement.prototype.releasePointerCapture = vi.fn();
window.HTMLElement.prototype.hasPointerCapture = vi.fn();
};
export * from '@testing-library/react';
export { render, waitForElementToBeRemoved };

View File

@@ -1283,7 +1283,7 @@ export type ConfigConfig = {
/** Configuration for observability service */
observability: ConfigObservability;
/** Configuration for postgres service */
postgres?: Maybe<ConfigPostgres>;
postgres: ConfigPostgres;
/** Configuration for third party providers like SMTP, SMS, etc. */
provider?: Maybe<ConfigProvider>;
/** Configuration for storage service */
@@ -1314,7 +1314,7 @@ export type ConfigConfigInsertInput = {
graphql?: InputMaybe<ConfigGraphqlInsertInput>;
hasura: ConfigHasuraInsertInput;
observability: ConfigObservabilityInsertInput;
postgres?: InputMaybe<ConfigPostgresInsertInput>;
postgres: ConfigPostgresInsertInput;
provider?: InputMaybe<ConfigProviderInsertInput>;
storage?: InputMaybe<ConfigStorageInsertInput>;
};
@@ -2247,7 +2247,7 @@ export type ConfigPortComparisonExp = {
export type ConfigPostgres = {
__typename?: 'ConfigPostgres';
/** Resources for the service */
resources?: Maybe<ConfigPostgresResources>;
resources: ConfigPostgresResources;
settings?: Maybe<ConfigPostgresSettings>;
/**
* Version of postgres, you can see available versions in the URL below:
@@ -2266,7 +2266,7 @@ export type ConfigPostgresComparisonExp = {
};
export type ConfigPostgresInsertInput = {
resources?: InputMaybe<ConfigPostgresResourcesInsertInput>;
resources: ConfigPostgresResourcesInsertInput;
settings?: InputMaybe<ConfigPostgresSettingsInsertInput>;
version?: InputMaybe<Scalars['String']>;
};
@@ -2274,43 +2274,50 @@ export type ConfigPostgresInsertInput = {
/** Resources for the service */
export type ConfigPostgresResources = {
__typename?: 'ConfigPostgresResources';
autoscaler?: Maybe<ConfigAutoscaler>;
compute?: Maybe<ConfigResourcesCompute>;
enablePublicAccess?: Maybe<Scalars['Boolean']>;
networking?: Maybe<ConfigNetworking>;
/** Number of replicas for a service */
replicas?: Maybe<Scalars['ConfigUint8']>;
storage?: Maybe<ConfigPostgresStorage>;
storage: ConfigPostgresResourcesStorage;
};
export type ConfigPostgresResourcesComparisonExp = {
_and?: InputMaybe<Array<ConfigPostgresResourcesComparisonExp>>;
_not?: InputMaybe<ConfigPostgresResourcesComparisonExp>;
_or?: InputMaybe<Array<ConfigPostgresResourcesComparisonExp>>;
autoscaler?: InputMaybe<ConfigAutoscalerComparisonExp>;
compute?: InputMaybe<ConfigResourcesComputeComparisonExp>;
enablePublicAccess?: InputMaybe<ConfigBooleanComparisonExp>;
networking?: InputMaybe<ConfigNetworkingComparisonExp>;
replicas?: InputMaybe<ConfigUint8ComparisonExp>;
storage?: InputMaybe<ConfigPostgresStorageComparisonExp>;
storage?: InputMaybe<ConfigPostgresResourcesStorageComparisonExp>;
};
export type ConfigPostgresResourcesInsertInput = {
autoscaler?: InputMaybe<ConfigAutoscalerInsertInput>;
compute?: InputMaybe<ConfigResourcesComputeInsertInput>;
enablePublicAccess?: InputMaybe<Scalars['Boolean']>;
networking?: InputMaybe<ConfigNetworkingInsertInput>;
replicas?: InputMaybe<Scalars['ConfigUint8']>;
storage?: InputMaybe<ConfigPostgresStorageInsertInput>;
storage: ConfigPostgresResourcesStorageInsertInput;
};
export type ConfigPostgresResourcesStorage = {
__typename?: 'ConfigPostgresResourcesStorage';
capacity: Scalars['ConfigUint32'];
};
export type ConfigPostgresResourcesStorageComparisonExp = {
_and?: InputMaybe<Array<ConfigPostgresResourcesStorageComparisonExp>>;
_not?: InputMaybe<ConfigPostgresResourcesStorageComparisonExp>;
_or?: InputMaybe<Array<ConfigPostgresResourcesStorageComparisonExp>>;
capacity?: InputMaybe<ConfigUint32ComparisonExp>;
};
export type ConfigPostgresResourcesStorageInsertInput = {
capacity: Scalars['ConfigUint32'];
};
export type ConfigPostgresResourcesStorageUpdateInput = {
capacity?: InputMaybe<Scalars['ConfigUint32']>;
};
export type ConfigPostgresResourcesUpdateInput = {
autoscaler?: InputMaybe<ConfigAutoscalerUpdateInput>;
compute?: InputMaybe<ConfigResourcesComputeUpdateInput>;
enablePublicAccess?: InputMaybe<Scalars['Boolean']>;
networking?: InputMaybe<ConfigNetworkingUpdateInput>;
replicas?: InputMaybe<Scalars['ConfigUint8']>;
storage?: InputMaybe<ConfigPostgresStorageUpdateInput>;
storage?: InputMaybe<ConfigPostgresResourcesStorageUpdateInput>;
};
export type ConfigPostgresSettings = {
@@ -2413,27 +2420,6 @@ export type ConfigPostgresSettingsUpdateInput = {
workMem?: InputMaybe<Scalars['String']>;
};
export type ConfigPostgresStorage = {
__typename?: 'ConfigPostgresStorage';
/** GiB */
capacity: Scalars['ConfigUint32'];
};
export type ConfigPostgresStorageComparisonExp = {
_and?: InputMaybe<Array<ConfigPostgresStorageComparisonExp>>;
_not?: InputMaybe<ConfigPostgresStorageComparisonExp>;
_or?: InputMaybe<Array<ConfigPostgresStorageComparisonExp>>;
capacity?: InputMaybe<ConfigUint32ComparisonExp>;
};
export type ConfigPostgresStorageInsertInput = {
capacity: Scalars['ConfigUint32'];
};
export type ConfigPostgresStorageUpdateInput = {
capacity?: InputMaybe<Scalars['ConfigUint32']>;
};
export type ConfigPostgresUpdateInput = {
resources?: InputMaybe<ConfigPostgresResourcesUpdateInput>;
settings?: InputMaybe<ConfigPostgresSettingsUpdateInput>;
@@ -27633,7 +27619,7 @@ export type GetAiSettingsQueryVariables = Exact<{
}>;
export type GetAiSettingsQuery = { __typename?: 'query_root', config?: { __typename?: 'ConfigConfig', postgres?: { __typename?: 'ConfigPostgres', version?: string | null } | null, ai?: { __typename?: 'ConfigAI', version?: string | null, webhookSecret: string, autoEmbeddings?: { __typename?: 'ConfigAIAutoEmbeddings', synchPeriodMinutes?: any | null } | null, openai: { __typename?: 'ConfigAIOpenai', apiKey: string, organization?: string | null }, resources: { __typename?: 'ConfigAIResources', compute: { __typename?: 'ConfigComputeResources', cpu: any, memory: any } } } | null } | null };
export type GetAiSettingsQuery = { __typename?: 'query_root', config?: { __typename?: 'ConfigConfig', postgres: { __typename?: 'ConfigPostgres', version?: string | null }, ai?: { __typename?: 'ConfigAI', version?: string | null, webhookSecret: string, autoEmbeddings?: { __typename?: 'ConfigAIAutoEmbeddings', synchPeriodMinutes?: any | null } | null, openai: { __typename?: 'ConfigAIOpenai', apiKey: string, organization?: string | null }, resources: { __typename?: 'ConfigAIResources', compute: { __typename?: 'ConfigComputeResources', cpu: any, memory: any } } } | null } | null };
export type GetAuthenticationSettingsQueryVariables = Exact<{
appId: Scalars['uuid'];
@@ -27647,7 +27633,7 @@ export type GetPostgresSettingsQueryVariables = Exact<{
}>;
export type GetPostgresSettingsQuery = { __typename?: 'query_root', systemConfig?: { __typename?: 'ConfigSystemConfig', postgres: { __typename?: 'ConfigSystemConfigPostgres', database: string } } | null, config?: { __typename: 'ConfigConfig', id: 'ConfigConfig', postgres?: { __typename?: 'ConfigPostgres', version?: string | null, resources?: { __typename?: 'ConfigPostgresResources', enablePublicAccess?: boolean | null, storage?: { __typename?: 'ConfigPostgresStorage', capacity: any } | null } | null } | null } | null };
export type GetPostgresSettingsQuery = { __typename?: 'query_root', systemConfig?: { __typename?: 'ConfigSystemConfig', postgres: { __typename?: 'ConfigSystemConfigPostgres', database: string } } | null, config?: { __typename: 'ConfigConfig', id: 'ConfigConfig', postgres: { __typename?: 'ConfigPostgres', version?: string | null, resources: { __typename?: 'ConfigPostgresResources', enablePublicAccess?: boolean | null, storage: { __typename?: 'ConfigPostgresResourcesStorage', capacity: any } } } } | null };
export type ResetDatabasePasswordMutationVariables = Exact<{
appId: Scalars['String'];
@@ -27703,14 +27689,14 @@ export type GetObservabilitySettingsQueryVariables = Exact<{
export type GetObservabilitySettingsQuery = { __typename?: 'query_root', config?: { __typename: 'ConfigConfig', id: 'ConfigConfig', observability: { __typename?: 'ConfigObservability', grafana: { __typename?: 'ConfigGrafana', alerting?: { __typename?: 'ConfigGrafanaAlerting', enabled?: boolean | null } | null, smtp?: { __typename?: 'ConfigGrafanaSmtp', host: string, password: string, port: any, sender: string, user: string } | null, contacts?: { __typename?: 'ConfigGrafanaContacts', emails?: Array<string> | null, discord?: Array<{ __typename?: 'ConfigGrafanacontactsDiscord', avatarUrl: string, url: string }> | null, pagerduty?: Array<{ __typename?: 'ConfigGrafanacontactsPagerduty', integrationKey: string, severity: string, class: string, component: string, group: string }> | null, slack?: Array<{ __typename?: 'ConfigGrafanacontactsSlack', recipient: string, token: string, username: string, iconEmoji: string, iconURL: string, mentionUsers: Array<string>, mentionGroups: Array<string>, mentionChannel: string, url: string, endpointURL: string }> | null, webhook?: Array<{ __typename?: 'ConfigGrafanacontactsWebhook', url: string, httpMethod: string, username: string, password: string, authorizationScheme: string, authorizationCredentials: string, maxAlerts: number }> | null } | null } } } | null };
export type ServiceResourcesFragment = { __typename?: 'ConfigConfig', auth?: { __typename?: 'ConfigAuth', resources?: { __typename?: 'ConfigResources', replicas?: any | null, compute?: { __typename?: 'ConfigResourcesCompute', cpu: any, memory: any } | null, autoscaler?: { __typename?: 'ConfigAutoscaler', maxReplicas: any } | null, networking?: { __typename?: 'ConfigNetworking', ingresses?: Array<{ __typename?: 'ConfigIngress', fqdn?: Array<string> | null }> | null } | null } | null } | null, hasura: { __typename?: 'ConfigHasura', resources?: { __typename?: 'ConfigResources', replicas?: any | null, compute?: { __typename?: 'ConfigResourcesCompute', cpu: any, memory: any } | null, autoscaler?: { __typename?: 'ConfigAutoscaler', maxReplicas: any } | null, networking?: { __typename?: 'ConfigNetworking', ingresses?: Array<{ __typename?: 'ConfigIngress', fqdn?: Array<string> | null }> | null } | null } | null }, postgres?: { __typename?: 'ConfigPostgres', resources?: { __typename?: 'ConfigPostgresResources', enablePublicAccess?: boolean | null, replicas?: any | null, storage?: { __typename?: 'ConfigPostgresStorage', capacity: any } | null, compute?: { __typename?: 'ConfigResourcesCompute', cpu: any, memory: any } | null, autoscaler?: { __typename?: 'ConfigAutoscaler', maxReplicas: any } | null } | null } | null, storage?: { __typename?: 'ConfigStorage', resources?: { __typename?: 'ConfigResources', replicas?: any | null, compute?: { __typename?: 'ConfigResourcesCompute', cpu: any, memory: any } | null, autoscaler?: { __typename?: 'ConfigAutoscaler', maxReplicas: any } | null } | null } | null };
export type ServiceResourcesFragment = { __typename?: 'ConfigConfig', auth?: { __typename?: 'ConfigAuth', resources?: { __typename?: 'ConfigResources', replicas?: any | null, compute?: { __typename?: 'ConfigResourcesCompute', cpu: any, memory: any } | null, autoscaler?: { __typename?: 'ConfigAutoscaler', maxReplicas: any } | null, networking?: { __typename?: 'ConfigNetworking', ingresses?: Array<{ __typename?: 'ConfigIngress', fqdn?: Array<string> | null }> | null } | null } | null } | null, hasura: { __typename?: 'ConfigHasura', resources?: { __typename?: 'ConfigResources', replicas?: any | null, compute?: { __typename?: 'ConfigResourcesCompute', cpu: any, memory: any } | null, autoscaler?: { __typename?: 'ConfigAutoscaler', maxReplicas: any } | null, networking?: { __typename?: 'ConfigNetworking', ingresses?: Array<{ __typename?: 'ConfigIngress', fqdn?: Array<string> | null }> | null } | null } | null }, postgres: { __typename?: 'ConfigPostgres', resources: { __typename?: 'ConfigPostgresResources', enablePublicAccess?: boolean | null, storage: { __typename?: 'ConfigPostgresResourcesStorage', capacity: any }, compute?: { __typename?: 'ConfigResourcesCompute', cpu: any, memory: any } | null } }, storage?: { __typename?: 'ConfigStorage', resources?: { __typename?: 'ConfigResources', replicas?: any | null, compute?: { __typename?: 'ConfigResourcesCompute', cpu: any, memory: any } | null, autoscaler?: { __typename?: 'ConfigAutoscaler', maxReplicas: any } | null } | null } | null };
export type GetResourcesQueryVariables = Exact<{
appId: Scalars['uuid'];
}>;
export type GetResourcesQuery = { __typename?: 'query_root', config?: { __typename?: 'ConfigConfig', auth?: { __typename?: 'ConfigAuth', resources?: { __typename?: 'ConfigResources', replicas?: any | null, compute?: { __typename?: 'ConfigResourcesCompute', cpu: any, memory: any } | null, autoscaler?: { __typename?: 'ConfigAutoscaler', maxReplicas: any } | null, networking?: { __typename?: 'ConfigNetworking', ingresses?: Array<{ __typename?: 'ConfigIngress', fqdn?: Array<string> | null }> | null } | null } | null } | null, hasura: { __typename?: 'ConfigHasura', resources?: { __typename?: 'ConfigResources', replicas?: any | null, compute?: { __typename?: 'ConfigResourcesCompute', cpu: any, memory: any } | null, autoscaler?: { __typename?: 'ConfigAutoscaler', maxReplicas: any } | null, networking?: { __typename?: 'ConfigNetworking', ingresses?: Array<{ __typename?: 'ConfigIngress', fqdn?: Array<string> | null }> | null } | null } | null }, postgres?: { __typename?: 'ConfigPostgres', resources?: { __typename?: 'ConfigPostgresResources', enablePublicAccess?: boolean | null, replicas?: any | null, storage?: { __typename?: 'ConfigPostgresStorage', capacity: any } | null, compute?: { __typename?: 'ConfigResourcesCompute', cpu: any, memory: any } | null, autoscaler?: { __typename?: 'ConfigAutoscaler', maxReplicas: any } | null } | null } | null, storage?: { __typename?: 'ConfigStorage', resources?: { __typename?: 'ConfigResources', replicas?: any | null, compute?: { __typename?: 'ConfigResourcesCompute', cpu: any, memory: any } | null, autoscaler?: { __typename?: 'ConfigAutoscaler', maxReplicas: any } | null } | null } | null } | null };
export type GetResourcesQuery = { __typename?: 'query_root', config?: { __typename?: 'ConfigConfig', auth?: { __typename?: 'ConfigAuth', resources?: { __typename?: 'ConfigResources', replicas?: any | null, compute?: { __typename?: 'ConfigResourcesCompute', cpu: any, memory: any } | null, autoscaler?: { __typename?: 'ConfigAutoscaler', maxReplicas: any } | null, networking?: { __typename?: 'ConfigNetworking', ingresses?: Array<{ __typename?: 'ConfigIngress', fqdn?: Array<string> | null }> | null } | null } | null } | null, hasura: { __typename?: 'ConfigHasura', resources?: { __typename?: 'ConfigResources', replicas?: any | null, compute?: { __typename?: 'ConfigResourcesCompute', cpu: any, memory: any } | null, autoscaler?: { __typename?: 'ConfigAutoscaler', maxReplicas: any } | null, networking?: { __typename?: 'ConfigNetworking', ingresses?: Array<{ __typename?: 'ConfigIngress', fqdn?: Array<string> | null }> | null } | null } | null }, postgres: { __typename?: 'ConfigPostgres', resources: { __typename?: 'ConfigPostgresResources', enablePublicAccess?: boolean | null, storage: { __typename?: 'ConfigPostgresResourcesStorage', capacity: any }, compute?: { __typename?: 'ConfigResourcesCompute', cpu: any, memory: any } | null } }, storage?: { __typename?: 'ConfigStorage', resources?: { __typename?: 'ConfigResources', replicas?: any | null, compute?: { __typename?: 'ConfigResourcesCompute', cpu: any, memory: any } | null, autoscaler?: { __typename?: 'ConfigAutoscaler', maxReplicas: any } | null } | null } | null } | null };
export type GetStorageSettingsQueryVariables = Exact<{
appId: Scalars['uuid'];
@@ -27770,7 +27756,7 @@ export type GetConfiguredVersionsQueryVariables = Exact<{
}>;
export type GetConfiguredVersionsQuery = { __typename?: 'query_root', config?: { __typename?: 'ConfigConfig', auth?: { __typename?: 'ConfigAuth', version?: string | null } | null, postgres?: { __typename?: 'ConfigPostgres', version?: string | null } | null, hasura: { __typename?: 'ConfigHasura', version?: string | null }, ai?: { __typename?: 'ConfigAI', version?: string | null } | null, storage?: { __typename?: 'ConfigStorage', version?: string | null } | null } | null };
export type GetConfiguredVersionsQuery = { __typename?: 'query_root', config?: { __typename?: 'ConfigConfig', auth?: { __typename?: 'ConfigAuth', version?: string | null } | null, postgres: { __typename?: 'ConfigPostgres', version?: string | null }, hasura: { __typename?: 'ConfigHasura', version?: string | null }, ai?: { __typename?: 'ConfigAI', version?: string | null } | null, storage?: { __typename?: 'ConfigStorage', version?: string | null } | null } | null };
export type GetProjectIsLockedQueryVariables = Exact<{
appId: Scalars['uuid'];
@@ -27969,7 +27955,7 @@ export type UpdateConfigMutationVariables = Exact<{
}>;
export type UpdateConfigMutation = { __typename?: 'mutation_root', updateConfig: { __typename?: 'ConfigConfig', id: 'ConfigConfig', postgres?: { __typename?: 'ConfigPostgres', resources?: { __typename?: 'ConfigPostgresResources', enablePublicAccess?: boolean | null, storage?: { __typename?: 'ConfigPostgresStorage', capacity: any } | null } | null } | null, ai?: { __typename?: 'ConfigAI', version?: string | null, webhookSecret: string, autoEmbeddings?: { __typename?: 'ConfigAIAutoEmbeddings', synchPeriodMinutes?: any | null } | null, openai: { __typename?: 'ConfigAIOpenai', organization?: string | null, apiKey: string }, resources: { __typename?: 'ConfigAIResources', compute: { __typename?: 'ConfigComputeResources', cpu: any, memory: any } } } | null } };
export type UpdateConfigMutation = { __typename?: 'mutation_root', updateConfig: { __typename?: 'ConfigConfig', id: 'ConfigConfig', postgres: { __typename?: 'ConfigPostgres', resources: { __typename?: 'ConfigPostgresResources', enablePublicAccess?: boolean | null, storage: { __typename?: 'ConfigPostgresResourcesStorage', capacity: any } } }, ai?: { __typename?: 'ConfigAI', version?: string | null, webhookSecret: string, autoEmbeddings?: { __typename?: 'ConfigAIAutoEmbeddings', synchPeriodMinutes?: any | null } | null, openai: { __typename?: 'ConfigAIOpenai', organization?: string | null, apiKey: string }, resources: { __typename?: 'ConfigAIResources', compute: { __typename?: 'ConfigComputeResources', cpu: any, memory: any } } } | null } };
export type UpdateDatabaseVersionMutationVariables = Exact<{
appId: Scalars['uuid'];
@@ -28435,14 +28421,14 @@ export type GetRemoteAppMetricsQuery = { __typename?: 'query_root', filesAggrega
export type RemoteAppGetUsersFragment = { __typename?: 'users', id: any, createdAt: any, displayName: string, avatarUrl: string, email?: any | null, emailVerified: boolean, phoneNumber?: string | null, phoneNumberVerified: boolean, disabled: boolean, defaultRole: string, lastSeen?: any | null, locale: string, metadata?: any | null, roles: Array<{ __typename?: 'authUserRoles', id: any, role: string }>, userProviders: Array<{ __typename?: 'authUserProviders', id: any, providerId: string }> };
export type RemoteAppGetUsersQueryVariables = Exact<{
export type RemoteAppGetUsersAndAuthRolesQueryVariables = Exact<{
where: Users_Bool_Exp;
limit: Scalars['Int'];
offset: Scalars['Int'];
}>;
export type RemoteAppGetUsersQuery = { __typename?: 'query_root', users: Array<{ __typename?: 'users', id: any, createdAt: any, displayName: string, avatarUrl: string, email?: any | null, emailVerified: boolean, phoneNumber?: string | null, phoneNumberVerified: boolean, disabled: boolean, defaultRole: string, lastSeen?: any | null, locale: string, metadata?: any | null, roles: Array<{ __typename?: 'authUserRoles', id: any, role: string }>, userProviders: Array<{ __typename?: 'authUserProviders', id: any, providerId: string }> }>, filteredUsersAggreggate: { __typename?: 'users_aggregate', aggregate?: { __typename?: 'users_aggregate_fields', count: number } | null }, usersAggregate: { __typename?: 'users_aggregate', aggregate?: { __typename?: 'users_aggregate_fields', count: number } | null } };
export type RemoteAppGetUsersAndAuthRolesQuery = { __typename?: 'query_root', users: Array<{ __typename?: 'users', id: any, createdAt: any, displayName: string, avatarUrl: string, email?: any | null, emailVerified: boolean, phoneNumber?: string | null, phoneNumberVerified: boolean, disabled: boolean, defaultRole: string, lastSeen?: any | null, locale: string, metadata?: any | null, roles: Array<{ __typename?: 'authUserRoles', id: any, role: string }>, userProviders: Array<{ __typename?: 'authUserProviders', id: any, providerId: string }> }>, filteredUsersAggreggate: { __typename?: 'users_aggregate', aggregate?: { __typename?: 'users_aggregate_fields', count: number } | null }, usersAggregate: { __typename?: 'users_aggregate', aggregate?: { __typename?: 'users_aggregate_fields', count: number } | null }, authRoles: Array<{ __typename?: 'authRoles', role: string }> };
export type RemoteAppGetUsersCustomQueryVariables = Exact<{
where: Users_Bool_Exp;
@@ -28721,10 +28707,6 @@ export const ServiceResourcesFragmentDoc = gql`
cpu
memory
}
replicas
autoscaler {
maxReplicas
}
}
}
storage {
@@ -34044,8 +34026,8 @@ export type GetRemoteAppMetricsQueryResult = Apollo.QueryResult<GetRemoteAppMetr
export function refetchGetRemoteAppMetricsQuery(variables?: GetRemoteAppMetricsQueryVariables) {
return { query: GetRemoteAppMetricsDocument, variables: variables }
}
export const RemoteAppGetUsersDocument = gql`
query remoteAppGetUsers($where: users_bool_exp!, $limit: Int!, $offset: Int!) {
export const RemoteAppGetUsersAndAuthRolesDocument = gql`
query remoteAppGetUsersAndAuthRoles($where: users_bool_exp!, $limit: Int!, $offset: Int!) {
users(
where: $where
limit: $limit
@@ -34064,20 +34046,23 @@ export const RemoteAppGetUsersDocument = gql`
count
}
}
authRoles {
role
}
}
${RemoteAppGetUsersFragmentDoc}`;
/**
* __useRemoteAppGetUsersQuery__
* __useRemoteAppGetUsersAndAuthRolesQuery__
*
* To run a query within a React component, call `useRemoteAppGetUsersQuery` and pass it any options that fit your needs.
* When your component renders, `useRemoteAppGetUsersQuery` returns an object from Apollo Client that contains loading, error, and data properties
* To run a query within a React component, call `useRemoteAppGetUsersAndAuthRolesQuery` and pass it any options that fit your needs.
* When your component renders, `useRemoteAppGetUsersAndAuthRolesQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useRemoteAppGetUsersQuery({
* const { data, loading, error } = useRemoteAppGetUsersAndAuthRolesQuery({
* variables: {
* where: // value for 'where'
* limit: // value for 'limit'
@@ -34085,19 +34070,19 @@ export const RemoteAppGetUsersDocument = gql`
* },
* });
*/
export function useRemoteAppGetUsersQuery(baseOptions: Apollo.QueryHookOptions<RemoteAppGetUsersQuery, RemoteAppGetUsersQueryVariables>) {
export function useRemoteAppGetUsersAndAuthRolesQuery(baseOptions: Apollo.QueryHookOptions<RemoteAppGetUsersAndAuthRolesQuery, RemoteAppGetUsersAndAuthRolesQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<RemoteAppGetUsersQuery, RemoteAppGetUsersQueryVariables>(RemoteAppGetUsersDocument, options);
return Apollo.useQuery<RemoteAppGetUsersAndAuthRolesQuery, RemoteAppGetUsersAndAuthRolesQueryVariables>(RemoteAppGetUsersAndAuthRolesDocument, options);
}
export function useRemoteAppGetUsersLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<RemoteAppGetUsersQuery, RemoteAppGetUsersQueryVariables>) {
export function useRemoteAppGetUsersAndAuthRolesLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<RemoteAppGetUsersAndAuthRolesQuery, RemoteAppGetUsersAndAuthRolesQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<RemoteAppGetUsersQuery, RemoteAppGetUsersQueryVariables>(RemoteAppGetUsersDocument, options);
return Apollo.useLazyQuery<RemoteAppGetUsersAndAuthRolesQuery, RemoteAppGetUsersAndAuthRolesQueryVariables>(RemoteAppGetUsersAndAuthRolesDocument, options);
}
export type RemoteAppGetUsersQueryHookResult = ReturnType<typeof useRemoteAppGetUsersQuery>;
export type RemoteAppGetUsersLazyQueryHookResult = ReturnType<typeof useRemoteAppGetUsersLazyQuery>;
export type RemoteAppGetUsersQueryResult = Apollo.QueryResult<RemoteAppGetUsersQuery, RemoteAppGetUsersQueryVariables>;
export function refetchRemoteAppGetUsersQuery(variables: RemoteAppGetUsersQueryVariables) {
return { query: RemoteAppGetUsersDocument, variables: variables }
export type RemoteAppGetUsersAndAuthRolesQueryHookResult = ReturnType<typeof useRemoteAppGetUsersAndAuthRolesQuery>;
export type RemoteAppGetUsersAndAuthRolesLazyQueryHookResult = ReturnType<typeof useRemoteAppGetUsersAndAuthRolesLazyQuery>;
export type RemoteAppGetUsersAndAuthRolesQueryResult = Apollo.QueryResult<RemoteAppGetUsersAndAuthRolesQuery, RemoteAppGetUsersAndAuthRolesQueryVariables>;
export function refetchRemoteAppGetUsersAndAuthRolesQuery(variables: RemoteAppGetUsersAndAuthRolesQueryVariables) {
return { query: RemoteAppGetUsersAndAuthRolesDocument, variables: variables }
}
export const RemoteAppGetUsersCustomDocument = gql`
query remoteAppGetUsersCustom($where: users_bool_exp!, $limit: Int!, $offset: Int!) {

View File

@@ -4,6 +4,7 @@
"jsx": "react-jsx",
"types": ["vitest/globals"],
"paths": {
"@/*": ["./*"],
"@/tests/*": ["tests/*"],
"@/e2e/*": ["../e2e/*"],
"@/components/*": ["components/*"],

View File

@@ -23,6 +23,7 @@
"build:@nhost-examples/nextjs-server-components": "turbo run build --filter=@nhost-examples/nextjs-server-components",
"build:@nhost-examples/sveltekit": "turbo run build --filter=@nhost-examples/sveltekit",
"dev": "turbo run dev --filter=!@nhost/dashboard --filter=!@nhost/docs --filter=!@nhost-examples/* --filter=!@nhost/docgen",
"dev:dashboard": "turbo run dev --filter=@nhost/dashboard",
"clean:all": "pnpm clean && rm -rf ./{{packages,examples/**,templates/**}/*,docs,dashboard}/{.nhost,node_modules} node_modules",
"clean": "rm -rf ./{{packages,examples/**}/*,docs,dashboard}/{dist,umd,.next,.turbo,coverage}",
"ci:version": "changeset version && pnpm install --frozen-lockfile false",