Compare commits

..

4 Commits

Author SHA1 Message Date
github-actions[bot]
5b087257e4 chore: update versions (#2994)
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.3.0

# @nhost/dashboard

## 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>
2024-11-05 21:33:56 +01:00
Hassan Ben Jobrane
f1b2117c37 fix: ensure stripe link modals are interact-able (#2996) 2024-11-05 21:24:46 +01:00
David BM
c1514eb098 fix (dashboard): update link to CLI configuration overlays in connected repository banner (#2993) 2024-11-05 10:35:00 -05:00
David BM
21bddeed6a feat (dashboard): ui for metrics settings (#2918)
### **User description**
Resolves #2877


___

### **PR Type**
Enhancement


___

### **Description**
- Added a new Metrics Settings page to the project settings
- Implemented ContactPointsSettings component for managing various
notification channels (Email, PagerDuty, Discord, Slack, Webhook)
- Created MetricsAlertingSettings and MetricsSMTPSettings components
- Added Observability to the project settings navigation
- Implemented form validation and submission for all settings
- Created new GraphQL queries and mutations for handling observability
settings
- Added new types and interfaces for form values and API responses



___



### **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>ProjectSettingsPagesComboBox.tsx</strong><dd><code>Add
Observability to project settings</code>&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

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

- Added 'Observability' to the project settings pages list



</details>


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

</tr>                    

<tr>
  <td>
    <details>
<summary><strong>NavTree.tsx</strong><dd><code>Add Observability to
navigation tree</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>

dashboard/src/components/layout/MainNav/NavTree.tsx

- Added 'Observability' to the project settings pages list



</details>


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

</tr>                    

<tr>
  <td>
    <details>
<summary><strong>ContactPointsSettings.tsx</strong><dd><code>Implement
ContactPointsSettings component</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/metrics/settings/components/ContactPointsSettings/ContactPointsSettings.tsx

<li>Implemented ContactPointsSettings component<br> <li> Handles form
submission and validation for contact points<br> <li> Includes sections
for Email, PagerDuty, Discord, Slack, and Webhook<br>


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/2918/files#diff-50a024995bad7b420fd717104a1584009e9fa44c508889dd125155f33d99f48e">+201/-0</a>&nbsp;
</td>

</tr>                    

<tr>
  <td>
    <details>
<summary><strong>ContactPointsSettingsTypes.ts</strong><dd><code>Define
types and validation for ContactPointsSettings</code>&nbsp; &nbsp;
&nbsp; &nbsp; </dd></summary>
<hr>


dashboard/src/features/orgs/projects/metrics/settings/components/ContactPointsSettings/ContactPointsSettingsTypes.ts

<li>Defined validation schema for contact points form<br> <li> Created
types for form values<br>


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/2918/files#diff-4992babdafb1d8691d89b7f4c91d7d6eab2392734c56b7211e864149268b6000">+87/-0</a>&nbsp;
&nbsp; </td>

</tr>                    

<tr>
  <td>
    <details>
<summary><strong>metrics.tsx</strong><dd><code>Create metrics settings
page</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; </dd></summary>
<hr>


dashboard/src/pages/orgs/[orgSlug]/projects/[appSlug]/settings/metrics.tsx

<li>Created new page for metrics settings<br> <li> Implemented
MetricsAlertingSettings, MetricsSMTPSettings, and
<br>ContactPointsSettings components<br>


</details>


  </td>
<td><a
href="https://github.com/nhost/nhost/pull/2918/files#diff-7687873a3415c94f06aa403484607202a02a518edfea67021ffc13415c0d1bb8">+69/-0</a>&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

---------

Co-authored-by: Hassan Ben Jobrane <hsanbenjobrane@gmail.com>
2024-11-04 14:20:02 -05:00
29 changed files with 2404 additions and 9 deletions

View File

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

View File

@@ -69,6 +69,7 @@ const projectSettingsPages = [
},
{ name: 'AI', slug: 'ai', route: 'ai' },
{ name: 'Configuration Editor', slug: 'editor', route: 'editor' },
{ name: 'Observability', slug: 'metrics', route: 'metrics' },
].map((item) => ({
label: item.name,
value: item.slug,

View File

@@ -152,6 +152,7 @@ const projectSettingsPages = [
},
{ name: 'AI', slug: 'ai', route: 'ai' },
{ name: 'Configuration Editor', slug: 'editor', route: 'editor' },
{ name: 'Observability', slug: 'metrics', route: 'metrics' },
];
const createOrganization = (org: Org, isPlatform: boolean) => {

View File

@@ -17,18 +17,18 @@ export default function SettingsLayout({ children }: SettingsLayoutProps) {
return (
<Box
sx={{ backgroundColor: 'background.default' }}
className="flex w-full flex-auto flex-col overflow-y-auto overflow-x-hidden"
className="flex flex-col flex-auto w-full overflow-x-hidden overflow-y-auto"
>
<Box
sx={{ backgroundColor: 'background.default' }}
className="flex h-full flex-col"
className="flex flex-col h-full"
>
<RetryableErrorBoundary>
<div className="flex flex-col space-y-2">
{hasGitRepo && (
<Alert
severity="warning"
className="grid grid-flow-row place-content-center gap-2"
className="grid grid-flow-row gap-2 place-content-center"
>
<Text color="warning" className="text-sm">
As you have a connected repository, make sure to synchronize
@@ -52,9 +52,9 @@ export default function SettingsLayout({ children }: SettingsLayoutProps) {
target="_blank"
rel="noopener noreferrer"
className="underline"
href="https://docs.nhost.io/cli/overlays"
href="https://docs.nhost.io/guides/cli/configuration-overlays#configuration-overlays"
>
docs.nhost.io/cli/overlays
Configuration Overlays
</a>{' '}
for guidance.
</Text>

View File

@@ -52,9 +52,9 @@ export default function SettingsLayout({ children }: SettingsLayoutProps) {
target="_blank"
rel="noopener noreferrer"
className="underline"
href="https://docs.nhost.io/cli/overlays"
href="https://docs.nhost.io/guides/cli/configuration-overlays#configuration-overlays"
>
docs.nhost.io/cli/overlays
Configuration Overlays
</a>{' '}
for guidance.
</Text>

View File

@@ -0,0 +1,200 @@
import { ApplyLocalSettingsDialog } from '@/components/common/ApplyLocalSettingsDialog';
import { useDialog } from '@/components/common/DialogProvider';
import { useUI } from '@/components/common/UIProvider';
import { Form } from '@/components/form/Form';
import { SettingsContainer } from '@/components/layout/SettingsContainer';
import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
import {
GetObservabilitySettingsDocument,
useGetObservabilitySettingsQuery,
useUpdateConfigMutation,
type ConfigConfigUpdateInput,
} from '@/utils/__generated__/graphql';
import { FormProvider, useForm } from 'react-hook-form';
import { twMerge } from 'tailwind-merge';
import { Divider } from '@/components/ui/v2/Divider';
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 { DiscordFormSection } from '@/features/orgs/projects/metrics/settings/components/DiscordFormSection';
import { EmailsFormSection } from '@/features/orgs/projects/metrics/settings/components/EmailsFormSection';
import { PagerdutyFormSection } from '@/features/orgs/projects/metrics/settings/components/PagerdutyFormSection';
import type { EventSeverity } from '@/features/orgs/projects/metrics/settings/components/PagerdutyFormSection/PagerdutyFormSectionTypes';
import { SlackFormSection } from '@/features/orgs/projects/metrics/settings/components/SlackFormSection';
import { WebhookFormSection } from '@/features/orgs/projects/metrics/settings/components/WebhookFormSection';
import type { HttpMethod } from '@/features/orgs/projects/metrics/settings/components/WebhookFormSection/WebhookFormSectionTypes';
import { execPromiseWithErrorToast } from '@/features/orgs/utils/execPromiseWithErrorToast';
import { removeTypename } from '@/utils/helpers';
import { yupResolver } from '@hookform/resolvers/yup';
import type { ContactPointsFormValues } from './ContactPointsSettingsTypes';
import { validationSchema } from './ContactPointsSettingsTypes';
export default function ContactPointsSettings() {
const isPlatform = useIsPlatform();
const { maintenanceActive } = useUI();
const localMimirClient = useLocalMimirClient();
const { project, refetch: refetchProject } = useProject();
const { openDialog } = useDialog();
const { data, loading, error } = useGetObservabilitySettingsQuery({
variables: { appId: project?.id },
...(!isPlatform ? { client: localMimirClient } : {}),
});
const { emails, pagerduty, discord, slack, webhook } =
data?.config?.observability?.grafana?.contacts || {};
const form = useForm<ContactPointsFormValues>({
defaultValues: {
emails: [],
discord: [],
pagerduty: [],
slack: [],
webhook: [],
},
values: {
emails: emails?.map((email) => ({ email })) || [],
discord: discord || [],
pagerduty:
pagerduty?.map((elem) => ({
...elem,
severity: elem.severity as EventSeverity,
})) || [],
slack:
slack?.map((elem) => ({
...elem,
mentionUsers: elem.mentionUsers.join(','),
mentionGroups: elem.mentionGroups.join(','),
})) || [],
webhook:
webhook?.map((elem) => ({
...elem,
httpMethod: elem.httpMethod as HttpMethod,
})) || [],
},
reValidateMode: 'onSubmit',
resolver: yupResolver(validationSchema),
});
const [updateConfig] = useUpdateConfigMutation({
refetchQueries: [GetObservabilitySettingsDocument],
...(!isPlatform ? { client: localMimirClient } : {}),
});
const getFormattedConfig = (values: ContactPointsFormValues) => {
// Remove any __typename property from the values
const sanitizedValues = removeTypename(values) as ContactPointsFormValues;
const newEmails =
sanitizedValues.emails?.map((email) => email.email) ?? null;
const newPagerduty =
sanitizedValues.pagerduty?.length > 0 ? sanitizedValues.pagerduty : null;
const newDiscord =
sanitizedValues.discord?.length > 0 ? sanitizedValues.discord : null;
const newSlack =
sanitizedValues.slack?.length > 0
? sanitizedValues.slack.map((elem) => ({
...elem,
mentionUsers: elem.mentionUsers.split(','),
mentionGroups: elem.mentionGroups.split(','),
}))
: null;
const newWebhook =
sanitizedValues.webhook?.length > 0 ? sanitizedValues.webhook : null;
const config: ConfigConfigUpdateInput = {
observability: {
grafana: {
contacts: {
emails: newEmails,
pagerduty: newPagerduty,
discord: newDiscord,
slack: newSlack,
webhook: newWebhook,
},
},
},
};
return config;
};
if (loading) {
return <ActivityIndicator delay={1000} label="Loading contact points..." />;
}
if (error) {
throw error;
}
const handleSubmit = async (formValues: ContactPointsFormValues) => {
const config = getFormattedConfig(formValues);
const updateConfigPromise = updateConfig({
variables: {
appId: project.id,
config,
},
});
await execPromiseWithErrorToast(
async () => {
await updateConfigPromise;
form.reset(formValues);
await refetchProject();
if (!isPlatform) {
openDialog({
title: 'Apply your changes',
component: <ApplyLocalSettingsDialog />,
props: {
PaperProps: {
className: 'max-w-2xl',
},
},
});
}
},
{
loadingMessage: 'Contact points are being updated...',
successMessage: 'Contact points have been updated successfully.',
errorMessage:
'An error occurred while trying to update contact points.',
},
);
};
return (
<FormProvider {...form}>
<Form onSubmit={handleSubmit}>
<SettingsContainer
title="Contact Points"
description="Define the contact points where your notifications will be sent."
docsLink="https://docs.nhost.io/platform/metrics#configure-contact-points"
rootClassName="gap-0"
className={twMerge('my-2 px-0')}
slotProps={{
submitButton: {
disabled: !form.formState.isDirty || maintenanceActive,
loading: form.formState.isSubmitting,
},
}}
>
<Divider />
<EmailsFormSection />
<Divider />
<PagerdutyFormSection />
<Divider />
<DiscordFormSection />
<Divider />
<SlackFormSection />
<Divider />
<WebhookFormSection />
</SettingsContainer>
</Form>
</FormProvider>
);
}

View File

@@ -0,0 +1,91 @@
import { EventSeverity } from '@/features/orgs/projects/metrics/settings/components/PagerdutyFormSection/PagerdutyFormSectionTypes';
import { HttpMethod } from '@/features/orgs/projects/metrics/settings/components/WebhookFormSection/WebhookFormSectionTypes';
import * as Yup from 'yup';
export const validationSchema = Yup.object({
emails: Yup.array()
.of(
Yup.object({
email: Yup.string().email('Invalid email address').required(),
}),
)
.nullable(),
discord: Yup.array()
.of(
Yup.object({
url: Yup.string()
.url('Invalid Discord URL')
.required('Discord webhook URL is required'),
avatarUrl: Yup.string().url('Invalid avatar URL'),
}),
)
.nullable(),
pagerduty: Yup.array()
.of(
Yup.object({
integrationKey: Yup.string().required(
'PagerDuty integration key is required',
),
severity: Yup.string()
.oneOf(Object.values(EventSeverity))
.required('PagerDuty severity is required'),
class: Yup.string(),
component: Yup.string(),
group: Yup.string(),
}),
)
.nullable(),
slack: Yup.array()
.of(
Yup.object({
recipient: Yup.string(),
token: Yup.string(),
username: Yup.string(),
iconEmoji: Yup.string(),
iconURL: Yup.string().url('Invalid icon URL'),
mentionUsers: Yup.string(),
mentionGroups: Yup.string(),
mentionChannel: Yup.string(),
url: Yup.string().url('Invalid Slack webhook URL'),
endpointURL: Yup.string().url('Invalid endpoint URL'),
}),
)
.test(
'either-url-or-recipient-token',
'Either URL or both recipient and token must be provided',
(value) => {
if (!value) {
return true;
}
const result = value.every(
(item) => item.url || (item.recipient && item.token),
);
if (result) {
return true;
}
return false;
},
)
.nullable(),
webhook: Yup.array()
.of(
Yup.object({
url: Yup.string()
.url('Invalid webhook URL')
.required('URL is required'),
httpMethod: Yup.string()
.oneOf(Object.values(HttpMethod), 'Invalid HTTP method')
.required('HTTP method is required'),
username: Yup.string(),
password: Yup.string(),
authorizationScheme: Yup.string(),
authorizationCredentials: Yup.string(),
maxAlerts: Yup.number()
.min(0, 'Max alerts must be greater than 0')
.integer('Max alerts must be an integer'),
}),
)
.nullable(),
});
export type ContactPointsFormValues = Yup.InferType<typeof validationSchema>;

View File

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

View File

@@ -0,0 +1,92 @@
import { Box } from '@/components/ui/v2/Box';
import { Button } from '@/components/ui/v2/Button';
import { InfoIcon } from '@/components/ui/v2/icons/InfoIcon';
import { PlusIcon } from '@/components/ui/v2/icons/PlusIcon';
import { TrashIcon } from '@/components/ui/v2/icons/TrashIcon';
import { Input } from '@/components/ui/v2/Input';
import { Text } from '@/components/ui/v2/Text';
import { Tooltip } from '@/components/ui/v2/Tooltip';
import type { ContactPointsFormValues } from '@/features/orgs/projects/metrics/settings/components/ContactPointsSettings/ContactPointsSettingsTypes';
import { useFieldArray, useFormContext } from 'react-hook-form';
export default function DiscordFormSection() {
const {
register,
formState: { errors },
control,
} = useFormContext<ContactPointsFormValues>();
const { fields, append, remove } = useFieldArray({
control,
name: 'discord',
});
return (
<Box className="flex flex-col gap-4 p-4">
<Box className="flex flex-row items-center justify-between">
<Box className="flex flex-row items-center space-x-2">
<Text variant="h4" className="font-semibold">
Discord
</Text>
<Tooltip
title={
<span>
Receive alert notifications in your Discord channels when your
Grafana alert rules are triggered and resolved.
</span>
}
>
<InfoIcon aria-label="Info" className="h-4 w-4" color="primary" />
</Tooltip>
</Box>
<Button
variant="borderless"
onClick={() => append({ url: '', avatarUrl: '' })}
>
<PlusIcon className="h-5 w-5" />
</Button>
</Box>
{fields?.length > 0 ? (
<Box className="flex flex-col gap-12">
{fields.map((field, index) => (
<Box key={field.id} className="flex w-full items-center gap-2">
<Box className="flex flex-1 flex-col gap-2">
<Input
{...register(`discord.${index}.url`)}
id={`${field.id}-discord`}
label="Discord URL"
placeholder="https://discord.com/api/webhooks/..."
className="w-full"
hideEmptyHelperText
error={!!errors?.discord?.[index]?.url}
helperText={errors?.discord?.[index]?.url?.message}
fullWidth
autoComplete="off"
/>
<Input
{...register(`discord.${index}.avatarUrl`)}
id={`${field.id}-discord-avatar`}
label="Avatar URL"
placeholder="https://discord.com/api/avatar/..."
className="w-full"
hideEmptyHelperText
error={!!errors?.discord?.[index]?.avatarUrl}
helperText={errors?.discord?.[index]?.avatarUrl?.message}
fullWidth
autoComplete="off"
/>
</Box>
<Button
variant="borderless"
className=""
color="error"
onClick={() => remove(index)}
>
<TrashIcon className="h-6 w-4" />
</Button>
</Box>
))}
</Box>
) : null}
</Box>
);
}

View File

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

View File

@@ -0,0 +1,76 @@
import { Box } from '@/components/ui/v2/Box';
import { Button } from '@/components/ui/v2/Button';
import { InfoIcon } from '@/components/ui/v2/icons/InfoIcon';
import { PlusIcon } from '@/components/ui/v2/icons/PlusIcon';
import { TrashIcon } from '@/components/ui/v2/icons/TrashIcon';
import { Input } from '@/components/ui/v2/Input';
import { Text } from '@/components/ui/v2/Text';
import { Tooltip } from '@/components/ui/v2/Tooltip';
import type { ContactPointsFormValues } from '@/features/orgs/projects/metrics/settings/components/ContactPointsSettings/ContactPointsSettingsTypes';
import { useFieldArray, useFormContext } from 'react-hook-form';
export default function EmailsFormSection() {
const {
register,
formState: { errors },
control,
} = useFormContext<ContactPointsFormValues>();
const { fields, append, remove } = useFieldArray({
control,
name: 'emails',
});
return (
<Box className="flex flex-col gap-4 p-4">
<Box className="flex flex-row items-center justify-between">
<Box className="flex flex-row items-center space-x-2">
<Text variant="h4" className="font-semibold">
Email
</Text>
<Tooltip
title={
<span>
Select your preferred emails for receiving notifications when
your alert rules are firing.
</span>
}
>
<InfoIcon aria-label="Info" className="h-4 w-4" color="primary" />
</Tooltip>
</Box>
<Button variant="borderless" onClick={() => append({ email: '' })}>
<PlusIcon className="h-5 w-5" />
</Button>
</Box>
{fields?.length > 0 ? (
<Box className="flex flex-col gap-6">
{fields.map((field, index) => (
<Box key={field.id} className="flex w-full items-center gap-2">
<Input
{...register(`emails.${index}.email`)}
id={`${field.id}-email`}
placeholder="Enter email address"
className="w-full"
label={`Email #${index + 1}`}
hideEmptyHelperText
error={!!errors?.emails?.[index]?.email}
helperText={errors?.emails?.[index]?.email?.message}
fullWidth
autoComplete="off"
/>
<Button
variant="borderless"
className="h-10 self-end"
color="error"
onClick={() => remove(index)}
>
<TrashIcon className="h-6 w-4" />
</Button>
</Box>
))}
</Box>
) : null}
</Box>
);
}

View File

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

View File

@@ -0,0 +1,206 @@
import { ApplyLocalSettingsDialog } from '@/components/common/ApplyLocalSettingsDialog';
import { useDialog } from '@/components/common/DialogProvider';
import { useUI } from '@/components/common/UIProvider';
import { Form } from '@/components/form/Form';
import { SettingsContainer } from '@/components/layout/SettingsContainer';
import { Input } from '@/components/ui/v2/Input';
import {
GetSmtpSettingsDocument,
useGetObservabilitySettingsQuery,
useUpdateConfigMutation,
} from '@/utils/__generated__/graphql';
import { yupResolver } from '@hookform/resolvers/yup';
import { FormProvider, useForm } from 'react-hook-form';
import { type Optional } from 'utility-types';
import * as yup from 'yup';
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';
const smtpValidationSchema = yup
.object({
host: yup.string().label('SMTP Host').required(),
port: yup
.number()
.typeError('The SMTP port should contain only numbers.')
.required(),
user: yup.string().label('Username').required(),
password: yup.string().label('Password'),
sender: yup.string().label('SMTP Sender').email().required(),
})
.required();
export type MetricsSmtpFormValues = yup.InferType<typeof smtpValidationSchema>;
export default function MetricsSMTPSettings() {
const { maintenanceActive } = useUI();
const { openDialog } = useDialog();
const isPlatform = useIsPlatform();
const localMimirClient = useLocalMimirClient();
const { project } = useProject();
const { data } = useGetObservabilitySettingsQuery({
variables: { appId: project?.id },
...(!isPlatform ? { client: localMimirClient } : {}),
});
const { host, port, user, sender, password } =
data?.config?.observability?.grafana?.smtp || {};
const form = useForm<Optional<MetricsSmtpFormValues, 'password'>>({
reValidateMode: 'onSubmit',
resolver: yupResolver(smtpValidationSchema),
defaultValues: {
host: '',
port: undefined,
user: '',
password: '',
sender: '',
},
values: {
host: host || '',
port,
user: user || '',
password: password || '',
sender: sender || '',
},
mode: 'onSubmit',
criteriaMode: 'all',
});
const {
register: registerSmtp,
formState: { errors, isDirty, isSubmitting },
} = form;
const [updateConfig] = useUpdateConfigMutation({
refetchQueries: [GetSmtpSettingsDocument],
...(!isPlatform ? { client: localMimirClient } : {}),
});
const handleEditSMTPSettings = async (values: MetricsSmtpFormValues) => {
const { password: newPassword, ...valuesWithoutPassword } = values;
const updateConfigPromise = updateConfig({
variables: {
appId: project.id,
config: {
provider: {
smtp: newPassword ? values : valuesWithoutPassword,
},
},
},
});
await execPromiseWithErrorToast(
async () => {
await updateConfigPromise;
if (!isPlatform) {
openDialog({
title: 'Apply your changes',
component: <ApplyLocalSettingsDialog />,
props: {
PaperProps: {
className: 'max-w-2xl',
},
},
});
}
},
{
loadingMessage: 'Metrics SMTP settings are being updated...',
successMessage: 'Metrics SMTP settings have been updated successfully.',
errorMessage:
'An error occurred while trying to update the Metrics SMTP settings.',
},
);
};
return (
<FormProvider {...form}>
<Form onSubmit={handleEditSMTPSettings}>
<SettingsContainer
title="SMTP Settings"
description="Configure your SMTP settings to send emails as part of your alerting."
docsLink="https://docs.nhost.io/platform/metrics#smtp"
submitButtonText="Save"
className="grid gap-4 lg:grid-cols-9"
slotProps={{
submitButton: {
disabled: !isDirty || maintenanceActive,
loading: isSubmitting,
},
}}
>
<Input
{...registerSmtp('sender')}
id="sender"
name="sender"
label="From Email"
placeholder="admin@localhost"
className="lg:col-span-4"
hideEmptyHelperText
fullWidth
error={Boolean(errors.sender)}
helperText={errors.sender?.message}
/>
<Input
{...registerSmtp('host')}
id="host"
name="host"
label="SMTP Host"
className="lg:col-span-4"
placeholder="localhost"
hideEmptyHelperText
fullWidth
error={Boolean(errors.host)}
helperText={errors.host?.message}
/>
<Input
{...registerSmtp('port')}
id="port"
name="port"
label="Port"
type="number"
placeholder="25"
className="lg:col-span-1"
hideEmptyHelperText
fullWidth
error={Boolean(errors.port)}
helperText={errors.port?.message}
/>
<Input
{...registerSmtp('user')}
id="user"
label="SMTP Username"
placeholder="Enter SMTP Username"
className="lg:col-span-4"
hideEmptyHelperText
fullWidth
error={Boolean(errors.user)}
helperText={errors.user?.message}
/>
<Input
{...registerSmtp('password')}
id="password"
label="SMTP Password"
type="password"
placeholder="Enter SMTP password"
className="lg:col-span-5"
hideEmptyHelperText
fullWidth
error={Boolean(errors.password)}
helperText={errors.password?.message}
/>
</SettingsContainer>
</Form>
</FormProvider>
);
}

View File

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

View File

@@ -0,0 +1,155 @@
import { ApplyLocalSettingsDialog } from '@/components/common/ApplyLocalSettingsDialog';
import { useDialog } from '@/components/common/DialogProvider';
import { useUI } from '@/components/common/UIProvider';
import { Form } from '@/components/form/Form';
import { SettingsContainer } from '@/components/layout/SettingsContainer';
import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
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 { ContactPointsSettings } from '@/features/orgs/projects/metrics/settings/components/ContactPointsSettings';
import { MetricsSMTPSettings } from '@/features/orgs/projects/metrics/settings/components/MetricsSMTPSettings';
import { execPromiseWithErrorToast } from '@/features/orgs/utils/execPromiseWithErrorToast';
import {
GetObservabilitySettingsDocument,
useGetObservabilitySettingsQuery,
useUpdateConfigMutation,
} from '@/generated/graphql';
import { yupResolver } from '@hookform/resolvers/yup';
import { useEffect } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import * as Yup from 'yup';
const metricsAlertingValidationSchema = Yup.object({
enabled: Yup.boolean(),
});
export type MetricsAlertingFormValues = Yup.InferType<
typeof metricsAlertingValidationSchema
>;
export default function MetricsSettings() {
const { openDialog } = useDialog();
const isPlatform = useIsPlatform();
const { maintenanceActive } = useUI();
const localMimirClient = useLocalMimirClient();
const { project, refetch: refetchProject } = useProject();
const [updateConfig] = useUpdateConfigMutation({
refetchQueries: [GetObservabilitySettingsDocument],
...(!isPlatform ? { client: localMimirClient } : {}),
});
const { data, loading, error } = useGetObservabilitySettingsQuery({
variables: { appId: project?.id },
...(!isPlatform ? { client: localMimirClient } : {}),
});
const { enabled: alertingEnabled } =
data?.config?.observability.grafana.alerting || {};
const alertingForm = useForm<MetricsAlertingFormValues>({
reValidateMode: 'onSubmit',
defaultValues: {
enabled: alertingEnabled,
},
resolver: yupResolver(metricsAlertingValidationSchema),
});
const { watch } = alertingForm;
const alerting = watch('enabled');
useEffect(() => {
if (!loading) {
alertingForm.reset({
enabled: alertingEnabled,
});
}
}, [loading, alertingEnabled, alertingForm]);
if (loading) {
return (
<ActivityIndicator
delay={1000}
label="Loading Alerting settings..."
className="justify-center"
/>
);
}
if (error) {
throw error;
}
async function handleSubmit(formValues: MetricsAlertingFormValues) {
const updateConfigPromise = updateConfig({
variables: {
appId: project.id,
config: {
observability: {
grafana: {
alerting: {
enabled: formValues.enabled,
},
},
},
},
},
});
await execPromiseWithErrorToast(
async () => {
await updateConfigPromise;
alertingForm.reset(formValues);
await refetchProject();
if (!isPlatform) {
openDialog({
title: 'Apply your changes',
component: <ApplyLocalSettingsDialog />,
props: {
PaperProps: {
className: 'max-w-2xl',
},
},
});
}
},
{
loadingMessage: 'Alerting settings are being updated...',
successMessage: 'Alerting settings have been updated successfully.',
errorMessage:
'An error occurred while trying to update alerting settings.',
},
);
}
return (
<div className="grid max-w-5xl grid-flow-row bg-transparent gap-y-6">
<FormProvider {...alertingForm}>
<Form onSubmit={handleSubmit}>
<SettingsContainer
title="Alerting"
description="Enable or disable Alerting."
slotProps={{
submitButton: {
disabled: !alertingForm.formState.isDirty || maintenanceActive,
loading: alertingForm.formState.isSubmitting,
},
}}
switchId="enabled"
docsTitle="enabling or disabling Alerting"
docsLink="https://docs.nhost.io/platform/metrics#alerting"
showSwitch
className="hidden"
/>
</Form>
</FormProvider>
{alerting ? (
<>
<MetricsSMTPSettings />
<ContactPointsSettings />
</>
) : null}
</div>
);
}

View File

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

View File

@@ -0,0 +1,161 @@
import { Box } from '@/components/ui/v2/Box';
import { Button } from '@/components/ui/v2/Button';
import { InfoIcon } from '@/components/ui/v2/icons/InfoIcon';
import { PlusIcon } from '@/components/ui/v2/icons/PlusIcon';
import { TrashIcon } from '@/components/ui/v2/icons/TrashIcon';
import { Input } from '@/components/ui/v2/Input';
import { Option } from '@/components/ui/v2/Option';
import { Select } from '@/components/ui/v2/Select';
import { Text } from '@/components/ui/v2/Text';
import { Tooltip } from '@/components/ui/v2/Tooltip';
import type { ContactPointsFormValues } from '@/features/orgs/projects/metrics/settings/components/ContactPointsSettings/ContactPointsSettingsTypes';
import { useFieldArray, useFormContext, useWatch } from 'react-hook-form';
import { EventSeverity } from './PagerdutyFormSectionTypes';
export default function PagerdutyFormSection() {
const {
register,
formState: { errors },
setValue,
control,
} = useFormContext<ContactPointsFormValues>();
const formValues = useWatch<ContactPointsFormValues>();
const { fields, append, remove } = useFieldArray({
control,
name: 'pagerduty',
});
const onChangeSeverity = (value: string | undefined, index: number) =>
setValue(`pagerduty.${index}.severity`, value as EventSeverity);
return (
<Box className="flex flex-col gap-4 p-4">
<Box className="flex flex-row items-center justify-between">
<Box className="flex flex-row items-center space-x-2">
<Text variant="h4" className="font-semibold">
PagerDuty
</Text>
<Tooltip
title={
<span>
Receive notifications in PagerDuty when your alert rules are
firing.
</span>
}
>
<InfoIcon aria-label="Info" className="h-4 w-4" color="primary" />
</Tooltip>
</Box>
<Button
variant="borderless"
onClick={() =>
append({
class: '',
component: '',
group: '',
severity: EventSeverity.CRITICAL,
integrationKey: '',
})
}
>
<PlusIcon className="h-5 w-5" />
</Button>
</Box>
{fields?.length > 0 ? (
<Box className="flex flex-col gap-12">
{fields.map((field, index) => (
<Box key={field.id} className="flex w-full items-center gap-2">
<Box className="grid flex-grow gap-4 lg:grid-cols-9">
<Input
{...register(`pagerduty.${index}.integrationKey`)}
id={`${field.id}-integrationKey`}
placeholder="Enter PagerDuty Integration Key"
className="w-full lg:col-span-7"
hideEmptyHelperText
error={!!errors?.pagerduty?.[index]?.integrationKey}
helperText={
errors?.pagerduty?.[index]?.integrationKey?.message
}
fullWidth
label="Integration Key"
autoComplete="off"
/>
<Select
fullWidth
value={formValues.pagerduty.at(index)?.severity || ''}
className="lg:col-span-2"
label="Severity"
onChange={(_event, inputValue) =>
onChangeSeverity(inputValue as string, index)
}
placeholder="Select severity"
slotProps={{
listbox: { className: 'min-w-0 w-full' },
popper: {
disablePortal: false,
className: 'z-[10000] w-[270px]',
},
}}
>
{Object.values(EventSeverity).map((severity) => (
<Option key={severity} value={severity}>
{severity}
</Option>
))}
</Select>
<Input
{...register(`pagerduty.${index}.class`)}
id={`${field.id}-class`}
placeholder="Enter type of the event"
label="Class"
className="w-full lg:col-span-3"
hideEmptyHelperText
error={!!errors?.pagerduty?.[index]?.class}
helperText={errors?.pagerduty?.[index]?.class?.message}
fullWidth
autoComplete="off"
/>
<Input
{...register(`pagerduty.${index}.component`)}
id={`${field.id}-component`}
placeholder="Enter component of the event"
label="Component"
className="w-full lg:col-span-3"
hideEmptyHelperText
error={!!errors?.pagerduty?.[index]?.component}
helperText={errors?.pagerduty?.[index]?.component?.message}
fullWidth
autoComplete="off"
/>
<Input
{...register(`pagerduty.${index}.group`)}
id={`${field.id}-group`}
placeholder="Enter logical group of components"
label="Group"
className="w-full lg:col-span-3"
hideEmptyHelperText
error={!!errors?.pagerduty?.[index]?.group}
helperText={errors?.pagerduty?.[index]?.group?.message}
fullWidth
autoComplete="off"
/>
</Box>
<Button
variant="borderless"
color="error"
onClick={() => remove(index)}
>
<TrashIcon className="h-6 w-4" />
</Button>
</Box>
))}
</Box>
) : null}
</Box>
);
}

View File

@@ -0,0 +1,6 @@
export enum EventSeverity {
CRITICAL = 'critical',
ERROR = 'error',
WARNING = 'warning',
INFO = 'info',
}

View File

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

View File

@@ -0,0 +1,222 @@
import { Alert } from '@/components/ui/v2/Alert';
import { Box } from '@/components/ui/v2/Box';
import { Button } from '@/components/ui/v2/Button';
import { InfoIcon } from '@/components/ui/v2/icons/InfoIcon';
import { PlusIcon } from '@/components/ui/v2/icons/PlusIcon';
import { TrashIcon } from '@/components/ui/v2/icons/TrashIcon';
import { Input } from '@/components/ui/v2/Input';
import { Text } from '@/components/ui/v2/Text';
import { Tooltip } from '@/components/ui/v2/Tooltip';
import type { ContactPointsFormValues } from '@/features/orgs/projects/metrics/settings/components/ContactPointsSettings/ContactPointsSettingsTypes';
import { useFieldArray, useFormContext } from 'react-hook-form';
export default function SlackFormSection() {
const {
control,
register,
formState: { errors },
trigger: triggerValidation,
} = useFormContext<ContactPointsFormValues>();
const { fields, append, remove } = useFieldArray({
control,
name: 'slack',
});
const handleRemove = (index: number) => {
remove(index);
if (fields?.length === 1) {
triggerValidation();
}
};
return (
<Box className="flex flex-col gap-4 p-4">
<Box className="flex flex-row items-center justify-between">
<Box className="flex flex-row items-center space-x-2">
<Text variant="h4" className="font-semibold">
Slack
</Text>
<Tooltip
title={
<span>
Select your preferred Slack channels for receiving notifications
when your alert rules are firing.
</span>
}
>
<InfoIcon aria-label="Info" className="h-4 w-4" color="primary" />
</Tooltip>
</Box>
<Button
variant="borderless"
onClick={() =>
append({
recipient: '',
token: '',
username: '',
iconEmoji: '',
iconURL: '',
mentionGroups: '',
mentionUsers: '',
mentionChannel: '',
url: '',
endpointURL: '',
})
}
>
<PlusIcon className="h-5 w-5" />
</Button>
</Box>
{fields?.length > 0 ? (
<Box className="flex flex-col gap-12">
{!!errors?.slack?.root?.message && (
<Alert severity="error" className="w-full">
{errors?.slack?.root?.message}
</Alert>
)}
{fields.map((field, index) => (
<Box key={field.id} className="flex w-full items-center gap-2">
<Box className="grid flex-grow gap-4 lg:grid-cols-9">
<Input
{...register(`slack.${index}.recipient`)}
id={`${field.id}-recipient`}
placeholder="Enter recipient"
className="w-full lg:col-span-3"
hideEmptyHelperText
error={!!errors?.slack?.[index]?.recipient}
helperText={errors?.slack?.[index]?.recipient?.message}
fullWidth
label="Recipient"
autoComplete="off"
/>
<Input
{...register(`slack.${index}.token`)}
id={`${field.id}-token`}
placeholder="Enter Slack API token"
label="Token"
className="w-full lg:col-span-6"
hideEmptyHelperText
error={!!errors?.slack?.[index]?.token}
helperText={errors?.slack?.[index]?.token?.message}
fullWidth
autoComplete="off"
/>
<Input
{...register(`slack.${index}.username`)}
id={`${field.id}-username`}
placeholder="Enter bot's username"
label="Bot Username"
className="w-full lg:col-span-5"
hideEmptyHelperText
error={!!errors?.slack?.[index]?.username}
helperText={errors?.slack?.[index]?.username?.message}
fullWidth
autoComplete="off"
/>
<Input
{...register(`slack.${index}.mentionChannel`)}
id={`${field.id}-mentionChannel`}
placeholder="Enter channel to mention"
label="Mention Channel"
className="w-full lg:col-span-4"
hideEmptyHelperText
error={!!errors?.slack?.[index]?.mentionChannel}
helperText={errors?.slack?.[index]?.mentionChannel?.message}
fullWidth
autoComplete="off"
/>
<Input
{...register(`slack.${index}.mentionUsers`)}
id={`${field.id}-mentionUsers`}
placeholder="Enter users to mention (separated by commas)"
label="Mention Users"
className="w-full lg:col-span-9"
hideEmptyHelperText
error={!!errors?.slack?.[index]?.mentionUsers}
helperText={errors?.slack?.[index]?.mentionUsers?.message}
fullWidth
autoComplete="off"
/>
<Input
{...register(`slack.${index}.mentionGroups`)}
id={`${field.id}-mentionGroups`}
placeholder="Enter groups to mention (separated by commas)"
label="Mention Groups"
className="w-full lg:col-span-9"
hideEmptyHelperText
error={!!errors?.slack?.[index]?.mentionGroups}
helperText={errors?.slack?.[index]?.mentionGroups?.message}
fullWidth
autoComplete="off"
/>
<Input
{...register(`slack.${index}.iconEmoji`)}
id={`${field.id}-iconEmoji`}
placeholder="Enter emoji icon"
label="Emoji Icon"
className="w-full lg:col-span-3"
hideEmptyHelperText
error={!!errors?.slack?.[index]?.iconEmoji}
helperText={errors?.slack?.[index]?.iconEmoji?.message}
fullWidth
autoComplete="off"
/>
<Input
{...register(`slack.${index}.iconURL`)}
id={`${field.id}-iconURL`}
placeholder="Enter emoji icon URL"
label="Emoji Icon URL"
className="w-full lg:col-span-6"
hideEmptyHelperText
error={!!errors?.slack?.[index]?.iconURL}
helperText={errors?.slack?.[index]?.iconURL?.message}
fullWidth
autoComplete="off"
/>
<Input
{...register(`slack.${index}.url`)}
id={`${field.id}-url`}
placeholder="Enter Slack Webhook URL"
label="Slack Webhook URL"
className="w-full lg:col-span-9"
hideEmptyHelperText
error={!!errors?.slack?.[index]?.url}
helperText={errors?.slack?.[index]?.url?.message}
fullWidth
autoComplete="off"
/>
<Input
{...register(`slack.${index}.endpointURL`)}
id={`${field.id}-endpointURL`}
placeholder="Enter endpoint URL"
label="Endpoint URL"
className="w-full lg:col-span-9"
hideEmptyHelperText
error={!!errors?.slack?.[index]?.endpointURL}
helperText={errors?.slack?.[index]?.endpointURL?.message}
fullWidth
autoComplete="off"
/>
</Box>
<Button
variant="borderless"
className=""
color="error"
onClick={() => handleRemove(index)}
>
<TrashIcon className="h-6 w-4" />
</Button>
</Box>
))}
</Box>
) : null}
</Box>
);
}

View File

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

View File

@@ -0,0 +1,218 @@
import { Box } from '@/components/ui/v2/Box';
import { Button } from '@/components/ui/v2/Button';
import { IconButton } from '@/components/ui/v2/IconButton';
import { EyeIcon } from '@/components/ui/v2/icons/EyeIcon';
import { EyeOffIcon } from '@/components/ui/v2/icons/EyeOffIcon';
import { InfoIcon } from '@/components/ui/v2/icons/InfoIcon';
import { PlusIcon } from '@/components/ui/v2/icons/PlusIcon';
import { TrashIcon } from '@/components/ui/v2/icons/TrashIcon';
import { Input } from '@/components/ui/v2/Input';
import { InputAdornment } from '@/components/ui/v2/InputAdornment';
import { Option } from '@/components/ui/v2/Option';
import { Select } from '@/components/ui/v2/Select';
import { Text } from '@/components/ui/v2/Text';
import { Tooltip } from '@/components/ui/v2/Tooltip';
import type { ContactPointsFormValues } from '@/features/orgs/projects/metrics/settings/components/ContactPointsSettings/ContactPointsSettingsTypes';
import { useState } from 'react';
import { useFieldArray, useFormContext, useWatch } from 'react-hook-form';
import { HttpMethod } from './WebhookFormSectionTypes';
export default function WebhookFormSection() {
const {
register,
formState: { errors },
setValue,
control,
} = useFormContext<ContactPointsFormValues>();
const formValues = useWatch<ContactPointsFormValues>();
const { fields, append, remove } = useFieldArray({
control,
name: 'webhook',
});
const [showPassword, setShowPassword] = useState(false);
const onChangeHttpMethod = (value: string | undefined, index: number) =>
setValue(`webhook.${index}.httpMethod`, value as HttpMethod);
return (
<Box className="flex flex-col gap-4 p-4">
<Box className="flex flex-row items-center justify-between">
<Box className="flex flex-row items-center space-x-2">
<Text variant="h4" className="font-semibold">
Webhook
</Text>
<Tooltip
title={
<span>
Send information about a state change to an external service
over HTTP.
</span>
}
>
<InfoIcon aria-label="Info" className="h-4 w-4" color="primary" />
</Tooltip>
</Box>
<Button
variant="borderless"
onClick={() =>
append({
url: '',
httpMethod: HttpMethod.POST,
username: '',
password: '',
authorizationScheme: '',
authorizationCredentials: '',
maxAlerts: 0,
})
}
>
<PlusIcon className="h-5 w-5" />
</Button>
</Box>
{fields?.length > 0 ? (
<Box className="flex flex-col gap-12">
{fields.map((field, index) => (
<Box key={field.id} className="flex w-full items-center space-x-2">
<Box className="grid flex-grow gap-4 lg:grid-cols-9">
<Input
{...register(`webhook.${index}.url`)}
id={`${field.id}-url`}
placeholder="Enter URL"
className="w-full lg:col-span-7"
hideEmptyHelperText
error={!!errors?.webhook?.[index]?.url}
helperText={errors?.webhook?.[index]?.url?.message}
fullWidth
label="URL"
autoComplete="off"
/>
<Select
fullWidth
value={formValues.webhook.at(index)?.httpMethod || ''}
className="lg:col-span-2"
label="HTTP Method"
onChange={(_event, inputValue) =>
onChangeHttpMethod(inputValue as string, index)
}
placeholder="Select HTTP Method"
slotProps={{
listbox: { className: 'min-w-0 w-full' },
popper: {
disablePortal: false,
className: 'z-[10000] w-[270px]',
},
}}
>
{Object.values(HttpMethod).map((httpMethod) => (
<Option key={httpMethod} value={httpMethod}>
{httpMethod}
</Option>
))}
</Select>
<Input
{...register(`webhook.${index}.username`)}
id={`${field.id}-username`}
placeholder="Enter username"
label="Username"
className="w-full lg:col-span-3"
hideEmptyHelperText
error={!!errors?.webhook?.[index]?.username}
helperText={errors?.webhook?.[index]?.username?.message}
fullWidth
autoComplete="off"
/>
<Input
{...register(`webhook.${index}.password`)}
id={`${field.id}-password`}
type={showPassword ? 'text' : 'password'}
placeholder="Enter password"
label="Password"
className="w-full lg:col-span-4"
hideEmptyHelperText
error={!!errors?.webhook?.[index]?.password}
helperText={errors?.webhook?.[index]?.password?.message}
fullWidth
autoComplete="off"
endAdornment={
<InputAdornment className="px-2" position="end">
<IconButton
variant="borderless"
color="secondary"
aria-label={
showPassword ? 'Hide Password' : 'Show Password'
}
onClick={() => setShowPassword((show) => !show)}
>
{showPassword ? (
<EyeOffIcon className="h-5 w-5" />
) : (
<EyeIcon className="h-5 w-5" />
)}
</IconButton>
</InputAdornment>
}
/>
<Input
type="number"
{...register(`webhook.${index}.maxAlerts`)}
id={`${field.id}-maxAlerts`}
placeholder="Enter max alerts"
label="Max Alerts (0 means no limit)"
className="w-full lg:col-span-2"
hideEmptyHelperText
error={!!errors?.webhook?.[index]?.maxAlerts}
helperText={errors?.webhook?.[index]?.maxAlerts?.message}
fullWidth
autoComplete="off"
/>
<Input
{...register(`webhook.${index}.authorizationScheme`)}
id={`${field.id}-authorizationScheme`}
placeholder="Enter authorization scheme"
label="Authorization Scheme"
className="w-full lg:col-span-3"
hideEmptyHelperText
error={!!errors?.webhook?.[index]?.authorizationScheme}
helperText={
errors?.webhook?.[index]?.authorizationScheme?.message
}
fullWidth
autoComplete="off"
/>
<Input
{...register(`webhook.${index}.authorizationCredentials`)}
id={`${field.id}-authorizationCredentials`}
placeholder="Enter authorization credentials"
label="Authorization Credentials"
className="w-full lg:col-span-6"
hideEmptyHelperText
error={!!errors?.webhook?.[index]?.authorizationCredentials}
helperText={
errors?.webhook?.[index]?.authorizationCredentials?.message
}
fullWidth
autoComplete="off"
/>
</Box>
<Button
variant="borderless"
className=""
color="error"
onClick={() => remove(index)}
>
<TrashIcon className="h-6 w-4" />
</Button>
</Box>
))}
</Box>
) : null}
</Box>
);
}

View File

@@ -0,0 +1,4 @@
export enum HttpMethod {
POST = 'POST',
PUT = 'PUT',
}

View File

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

View File

@@ -0,0 +1,55 @@
query GetObservabilitySettings($appId: uuid!) {
config(appID: $appId, resolve: false) {
id: __typename
__typename
observability {
grafana {
alerting {
enabled
}
smtp {
host
password
port
sender
user
}
contacts {
emails
discord {
avatarUrl
url
}
pagerduty {
integrationKey
severity
class
component
group
}
slack {
recipient
token
username
iconEmoji
iconURL
mentionUsers
mentionGroups
mentionChannel
url
endpointURL
}
webhook {
url
httpMethod
username
password
authorizationScheme
authorizationCredentials
maxAlerts
}
}
}
}
}
}

View File

@@ -0,0 +1,63 @@
import { Container } from '@/components/layout/Container';
import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
import { ProjectLayout } from '@/features/orgs/layout/ProjectLayout';
import { SettingsLayout } from '@/features/orgs/layout/SettingsLayout';
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 { MetricsSettings } from '@/features/orgs/projects/metrics/settings/components/MetricsSettings';
import { useGetObservabilitySettingsQuery } from '@/generated/graphql';
import type { ReactElement } from 'react';
export default function MetricsSettingsPage() {
const isPlatform = useIsPlatform();
const localMimirClient = useLocalMimirClient();
const { project } = useProject();
const { loading, error } = useGetObservabilitySettingsQuery({
variables: { appId: project?.id },
...(!isPlatform ? { client: localMimirClient } : {}),
});
if (loading) {
return (
<ActivityIndicator
delay={1000}
label="Loading Observability settings..."
className="justify-center"
/>
);
}
if (error) {
throw error;
}
return (
<Container
className="grid max-w-5xl grid-flow-row bg-transparent gap-y-6"
rootClassName="bg-transparent"
>
<MetricsSettings />
</Container>
);
}
MetricsSettingsPage.getLayout = function getLayout(page: ReactElement) {
return (
<ProjectLayout
mainContainerProps={{
className: 'flex h-full',
}}
>
<SettingsLayout>
<Container
sx={{ backgroundColor: 'background.default' }}
className="max-w-5xl"
>
{page}
</Container>
</SettingsLayout>
</ProjectLayout>
);
};

View File

@@ -11,6 +11,7 @@ body {
width: 100%;
height: 100%;
overflow: hidden;
pointer-events: auto !important;
}
#__next {

File diff suppressed because it is too large Load Diff

2
pnpm-lock.yaml generated
View File

@@ -6883,7 +6883,7 @@ packages:
/@graphql-tools/relay-operation-optimizer@6.5.18(encoding@0.1.13)(graphql@16.8.1):
resolution: {integrity: sha512-mc5VPyTeV+LwiM+DNvoDQfPqwQYhPV/cl5jOBjTgSniyaq8/86aODfMkrE2OduhQ5E00hqrkuL2Fdrgk0w1QJg==}
peerDependencies:
graphql: '>=16.8.1'
graphql: 16.8.1
dependencies:
'@ardatan/relay-compiler': 12.0.0(encoding@0.1.13)(graphql@16.8.1)
'@graphql-tools/utils': 9.2.1(graphql@16.8.1)