Compare commits

...

405 Commits

Author SHA1 Message Date
Szilárd Dóró
7bbf6dbf1c Merge pull request #1387 from nhost/changeset-release/main
chore: update versions
2023-01-02 10:43:56 +01:00
Szilárd Dóró
689dc873b3 Update integrations/react-apollo/CHANGELOG.md
Co-authored-by: Pilou <24897252+plmercereau@users.noreply.github.com>
2023-01-02 10:07:28 +01:00
Szilárd Dóró
a0747d02e0 Update integrations/react-urql/CHANGELOG.md
Co-authored-by: Pilou <24897252+plmercereau@users.noreply.github.com>
2023-01-02 10:07:23 +01:00
Szilárd Dóró
be5bd1e446 Update integrations/apollo/CHANGELOG.md
Co-authored-by: Pilou <24897252+plmercereau@users.noreply.github.com>
2023-01-02 10:07:18 +01:00
Szilárd Dóró
2c60591580 fix(deps): revert major bumps 2023-01-02 10:02:12 +01:00
github-actions[bot]
6140bc5b3b chore: update versions 2022-12-27 13:49:10 +00:00
Pilou
9f7780ec91 Merge pull request #1438 from nhost/ci/renovate
ci: improve renovate configuration
2022-12-27 14:47:35 +01:00
Pilou
abc7d0c7a5 Merge pull request #1442 from nhost/chore/docgen-vite
chore: build docgen with vite
2022-12-27 09:44:03 +01:00
Pilou
074a36ea48 Merge pull request #1432 from nhost/fix/remove-accept-encoding-header
Fix/allow override of the accept-encoding header
2022-12-27 09:43:39 +01:00
Pierre-Louis Mercereau
64e806dc27 chore: build docgen with vite 2022-12-26 21:16:02 +01:00
Pilou
bd0e9748b6 Merge pull request #1440 from nhost/renovate-changesets
chore: create changesest from Renovate bumps
2022-12-26 21:04:45 +01:00
plmercereau
b21222b378 chore(deps): update dependency @types/node to v16 2022-12-26 20:01:13 +00:00
Pilou
7e217db128 Merge pull request #1294 from nhost/renovate/node-18.x
chore(deps): update dependency @types/node to v16
2022-12-26 20:59:25 +01:00
Pierre-Louis Mercereau
56c716d9fa Merge branch 'main' into renovate/node-18.x 2022-12-26 20:23:00 +01:00
Pilou
14ecbd1fb9 Merge pull request #1439 from nhost/renovate-changesets
chore: create changesest from Renovate bumps
2022-12-26 20:19:58 +01:00
plmercereau
a0242c4d6f chore(deps): update dependency tsup to v6 2022-12-26 19:13:36 +00:00
Pilou
4800b4a756 Merge pull request #1306 from nhost/renovate/tsup-6.x
chore(deps): update dependency tsup to v6
2022-12-26 20:12:14 +01:00
Pierre-Louis Mercereau
5b318d17d4 Merge branch 'main' into renovate/node-18.x 2022-12-26 20:08:46 +01:00
Pierre-Louis Mercereau
2f9be4f760 fix: @types/node v16 2022-12-26 20:06:00 +01:00
Pierre-Louis Mercereau
64777b6f30 ci: improve renovate configuration 2022-12-26 19:59:48 +01:00
Pilou
7e1489353e Merge pull request #1437 from nhost/ci/renovate-changeset-pr
ci: create renovate changeset pr
2022-12-26 19:44:20 +01:00
Pierre-Louis Mercereau
c53306a497 ci: correct PAT secret 2022-12-26 19:43:55 +01:00
Pierre-Louis Mercereau
83345579d0 ci: create renovate changeset pr 2022-12-26 19:41:08 +01:00
Pilou
2b4b9e0385 Merge pull request #1304 from nhost/renovate/graphql-16.x
chore(deps): update dependency graphql to v16
2022-12-26 19:08:25 +01:00
renovate[bot]
922349f550 chore(deps): update dependency graphql to v16 2022-12-26 17:47:32 +00:00
Pilou
d613f3fd04 Merge pull request #1434 from nhost/renovate/jsonwebtoken-9.x
ci: correct gh action
2022-12-26 18:41:24 +01:00
Pierre-Louis Mercereau
4d8a47777e ci: correct gh action 2022-12-26 18:40:44 +01:00
Pilou
229a7ab1f7 Merge pull request #1417 from nhost/renovate/jsonwebtoken-9.x
fix(deps): update dependency jsonwebtoken to v9
2022-12-26 18:38:09 +01:00
Pierre-Louis Mercereau
3dabb7b53a ci: adapt renovate action 2022-12-26 18:37:49 +01:00
renovate[bot]
abc3d6ce60 chore(deps): update dependency tsup to v6 2022-12-26 17:33:02 +00:00
Pilou
08d49bd1fd Merge pull request #1408 from nhost/renovate/vitest-monorepo
chore(deps): update dependency vitest to ^0.26.0
2022-12-26 18:24:07 +01:00
Pierre-Louis Mercereau
03435a2c66 chore: bump vitest to latest version 2022-12-26 14:55:39 +01:00
Pierre-Louis Mercereau
66208d6840 chore: allow custom values for Accept-Encoding 2022-12-26 13:15:04 +01:00
Pierre-Louis Mercereau
5be9abb0fa chore: changeset 2022-12-26 12:59:23 +01:00
Pierre-Louis Mercereau
8e504b5328 Merge branch 'main' into fix/remove-accept-encoding-header 2022-12-26 12:55:58 +01:00
renovate[bot]
0e3eb7204a fix(deps): update dependency jsonwebtoken to v9 2022-12-23 23:51:28 +00:00
renovate[bot]
a78cd2f18f chore(deps): update dependency vitest to ^0.26.0 2022-12-22 16:01:51 +00:00
Pilou
6ef340daad Merge pull request #1424 from nhost/ci/retry-cli-install
ci: retry the Nhost CLI installation
2022-12-22 16:31:46 +01:00
Pierre-Louis Mercereau
a96e3c9163 ci: retry the Nhost CLI installation 2022-12-22 16:21:36 +01:00
Szilárd Dóró
9c5b6532d3 Merge pull request #1422 from nhost/fix/persistent-drawer
fix(dashboard): close modals when navigating
2022-12-22 11:02:40 +01:00
Szilárd Dóró
889df8ca4d fix(dashboard): don't break unit tests 2022-12-22 10:54:02 +01:00
Pilou
b998e09e10 Merge pull request #1421 from nhost/chore/integrations-labeler
ci: label integrations
2022-12-22 10:48:33 +01:00
Szilárd Dóró
9e0486a362 chore(dashboard): add changeset 2022-12-22 10:01:12 +01:00
Szilárd Dóró
74037cec68 fix(dashboard): close modals when navigating 2022-12-22 10:00:21 +01:00
Pierre-Louis Mercereau
819e68b501 ci: label integrations 2022-12-21 19:36:05 +01:00
Pilou
10cc213933 Merge pull request #1418 from nhost/renovate/mermaid-9.x
fix(deps): update dependency mermaid to v9
2022-12-21 16:50:53 +01:00
Pilou
4157c012fd Merge pull request #1406 from nhost/chore/deprecate-axios
chore: deprecate axios
2022-12-21 16:02:26 +01:00
Pilou
9515096349 Update .changeset/kind-cycles-explain.md
Co-authored-by: Nuno Pato <nunopato@gmail.com>
2022-12-21 15:38:33 +01:00
Pilou
4dd5617855 Update .changeset/kind-cycles-explain.md
Co-authored-by: Nuno Pato <nunopato@gmail.com>
2022-12-21 15:37:29 +01:00
renovate[bot]
01dc358842 fix(deps): update dependency mermaid to v9 2022-12-21 13:22:15 +00:00
Pilou
925a1808e6 Merge pull request #1413 from nhost/renovate/vite-4.x
chore(deps): update dependency vite to v4
2022-12-21 14:10:23 +01:00
Pilou
ac80f88727 Merge pull request #1410 from nhost/chore/remove-query-string
chore: remove unused query-string dependency
2022-12-21 13:12:23 +01:00
Pilou
3078247629 Merge pull request #1412 from nhost/test/docker-compose
test(docker-compose): more retries
2022-12-21 13:12:05 +01:00
Pierre-Louis Mercereau
144c0084d2 chore: add Nhost prefix to client types 2022-12-21 12:27:19 +01:00
Pierre-Louis Mercereau
bbdfb77a07 refactor: types 2022-12-21 12:22:27 +01:00
Pilou
b5a9c1be47 Update packages/nhost-js/src/clients/functions.ts
Co-authored-by: Nuno Pato <nunopato@gmail.com>
2022-12-20 18:45:36 +01:00
Pierre-Louis Mercereau
5b5e7d9640 chore: update vite-related libraries 2022-12-20 15:38:28 +01:00
Pierre-Louis Mercereau
c02e0c63f2 refactor: simplify 2022-12-20 14:48:47 +01:00
renovate[bot]
788482fab2 chore(deps): update dependency vite to v4 2022-12-20 13:40:30 +00:00
Pilou
16d94821b8 Merge pull request #1411 from nhost/renovate/tj-actions-changed-files-35.x
chore(deps): update tj-actions/changed-files action to v35
2022-12-20 14:30:16 +01:00
Pierre-Louis Mercereau
5c4ab54c90 test(docker-compose): more retries 2022-12-20 14:28:05 +01:00
renovate[bot]
5941568bbb chore(deps): update tj-actions/changed-files action to v35 2022-12-20 13:15:40 +00:00
Pierre-Louis Mercereau
1a9e1fde1d chore: remove unused query-string dependency 2022-12-20 14:12:29 +01:00
Pierre-Louis Mercereau
32c0632526 docs: correct changeset 2022-12-20 10:28:09 +01:00
Pierre-Louis Mercereau
19cca7f45d chore: changeset 2022-12-20 10:22:00 +01:00
Pierre-Louis Mercereau
191580a819 Merge branch 'main' into chore/deprecate-axios 2022-12-20 10:12:29 +01:00
Pilou
4a57861354 Merge pull request #1396 from nhost/integrations-iaubsd98hasd
Integrations and README updates
2022-12-20 10:11:48 +01:00
Pierre-Louis Mercereau
d96b817476 refactor: simplify 2022-12-20 10:04:19 +01:00
renovate[bot]
db6db8d860 chore(deps): update dependency @types/node to v18 2022-12-20 08:35:37 +00:00
Pierre-Louis Mercereau
fe405ba123 fix: docgen and tsconfig 2022-12-20 09:33:53 +01:00
Pilou
972a5f652f Merge pull request #1281 from nhost/renovate/typescript-4.x
chore(deps): update dependency typescript to v4.9.4
2022-12-20 09:32:34 +01:00
Pierre-Louis Mercereau
7a2c140524 Merge branch 'main' into chore/deprecate-axios 2022-12-20 09:26:41 +01:00
Pierre-Louis Mercereau
3d717d68a9 Merge branch 'main' into integrations-iaubsd98hasd 2022-12-20 09:22:29 +01:00
Pierre-Louis Mercereau
13b6a47bef fix: ignore duplicate type 2022-12-20 09:13:50 +01:00
Pierre-Louis Mercereau
2a6caa47bd chore: deprecate axios 2022-12-20 09:00:59 +01:00
Szilárd Dóró
dbbfaef451 Merge pull request #1373 from nhost/feat/rule-group-editor
feat(dashboard): Rule Group Editor
2022-12-19 11:52:16 +01:00
Szilárd Dóró
b4c07f1723 Merge branch 'main' into feat/rule-group-editor 2022-12-19 11:51:18 +01:00
Szilárd Dóró
e983eb53d9 chore(dashboard): remove "is" prefix 2022-12-19 11:09:18 +01:00
Szilárd Dóró
49867fdcf7 fix(dashboard): add missing inputValue for freeSolo input 2022-12-19 11:07:50 +01:00
Szilárd Dóró
cc428d73ee fix(dashboard): async initialization 2022-12-19 11:02:16 +01:00
Pilou
940a1db0fc Merge branch 'main' into integrations-iaubsd98hasd 2022-12-19 11:00:35 +01:00
Pilou
d70f29a408 Merge pull request #1400 from nhost/contributors-readme-action-PL6rn2c5lN
docs(contributor): contributors readme action update
2022-12-19 10:58:17 +01:00
Pilou
52cb055520 Merge pull request #1402 from nhost/contributors-readme-action-IrWb62uoZo
docs(contributor): contributors readme action update
2022-12-19 10:57:46 +01:00
github-actions[bot]
8c406237a2 docs(contributor): contrib-readme-action has updated readme 2022-12-19 09:57:07 +00:00
Pilou
d036e282e5 Merge pull request #1395 from nhost/fix-asu9hda0sd
revert code to previously working
2022-12-19 10:56:53 +01:00
Szilárd Dóró
39530cd8e8 feat(dashboard): support Hasura array operators 2022-12-19 10:16:36 +01:00
github-actions[bot]
8b58627608 docs(contributor): contrib-readme-action has updated readme 2022-12-19 08:06:33 +00:00
Calvin Lang
c4e2d87e5c Removed accept headers * in axios requests 2022-12-19 16:06:26 +08:00
Johan Eliasson
66b0378d38 Merge pull request #1397 from muttenzer/docs/event-trigger-env-var
Replace deprecated "NHOST_BACKEND_URL" in event trigger example
2022-12-19 09:06:09 +01:00
Timo M
22d7a36247 update hasura event trigger video using the correct env var 2022-12-17 17:48:36 +01:00
Timo M
7d2eb2de66 update webhook url format image 2022-12-17 17:45:09 +01:00
Timo M
4bba002c30 replace NHOST_BACKEND_URL with NHOST_FUNCTIONS_URL env var 2022-12-17 17:43:42 +01:00
Johan Eliasson
881a3344d4 update lock file 2022-12-17 11:00:45 +01:00
Johan Eliasson
d1562d33fb update 2022-12-17 10:57:06 +01:00
Johan Eliasson
21ced66f22 moved apollo and react-apollo to integrations/ folder 2022-12-17 10:50:13 +01:00
Johan Eliasson
2a2d86904d revert to previously working 2022-12-17 10:46:12 +01:00
Johan Eliasson
82d46f716b Merge pull request #1381 from nhost/docs-ui8asud9
docs update
2022-12-17 10:42:33 +01:00
Johan Eliasson
5e26810868 using authentication everywhere 2022-12-17 10:36:39 +01:00
Johan Eliasson
d590258371 correct query 2022-12-17 10:34:06 +01:00
Johan Eliasson
697ef57cb8 Update docs/docs/authentication/users.mdx
Co-authored-by: Guido Curcio <guidomaurocurcio@gmail.com>
2022-12-17 10:29:28 +01:00
Johan Eliasson
93002dc8c3 Update docs/docs/authentication/users.mdx
Co-authored-by: Guido Curcio <guidomaurocurcio@gmail.com>
2022-12-17 10:29:09 +01:00
Johan Eliasson
baa1937d06 Merge pull request #1228 from nhost/react-urql
urql
2022-12-16 22:11:29 +01:00
Johan Eliasson
03e5662df9 Merge branch 'main' into react-urql 2022-12-16 22:10:54 +01:00
Pilou
e0711bdfc8 Merge pull request #1372 from kematzy/docs-update-vue-quickstart
Update of Vue Quickstart documentation
2022-12-16 22:05:55 +01:00
Johan Eliasson
caca27fde3 Merge branch 'main' into docs-ui8asud9 2022-12-16 21:55:55 +01:00
Johan Eliasson
b0d51033c6 typo fix 2022-12-16 21:43:02 +01:00
Johan Eliasson
088f9394fc Update docs/docs/graphql/permissions.mdx
Co-authored-by: Siarhei Lipchyk <contact@siarhei.dev>
2022-12-16 21:42:07 +01:00
Szilárd Dóró
a91361f971 feat(dashboard): add support for Hasura permission object converter 2022-12-16 21:10:04 +01:00
Pilou
a4f5be6ab9 Merge pull request #1390 from nhost/fix/strict-mode
fix: React 18 strict mode and other improvements
2022-12-16 19:09:55 +01:00
Szilárd Dóró
dbbccbf1cd fix(dashboard): mobile responsive design 2022-12-16 13:50:22 +01:00
Szilárd Dóró
a70dc7b352 chore(dashboard): improve rule group editor design 2022-12-16 12:57:03 +01:00
Pierre-Louis Mercereau
54df0df42b fix: 🐛 React 18 strict mode and other improvements 2022-12-16 12:08:04 +01:00
Szilárd Dóró
0bbb2598fd feat(dashboard): finalize Hasura permission converter 2022-12-16 11:01:25 +01:00
Szilárd Dóró
4ba34cc827 feat(dashboard): working on Hasura converters 2022-12-15 16:00:11 +01:00
Szilárd Dóró
42ece48ce3 feat(dashboard): added support for text truncation 2022-12-15 15:05:24 +01:00
Szilárd Dóró
f97ab31f69 feat(dashboard): added support for _c* operators 2022-12-15 10:21:09 +01:00
Johan Eliasson
21dc1ecd6b image didn't exist 2022-12-15 10:01:24 +01:00
Szilárd Dóró
c11adbe3e2 chore(dashboard): improve initialization 2022-12-15 09:58:27 +01:00
Johan Eliasson
6078e9c207 new sys env vars 2022-12-15 09:22:00 +01:00
Johan Eliasson
450582dc43 update 2022-12-15 09:12:11 +01:00
Johan Eliasson
ddec0e1be1 update 2022-12-15 09:09:31 +01:00
Johan Eliasson
698154b24b permissions updated 2022-12-15 09:05:42 +01:00
Johan Eliasson
9c87b0f67b updates 2022-12-14 20:48:43 +01:00
Pilou
85afe3d216 Merge pull request #1368 from nhost/refactor/less-peer-dependencies
refactor: less peer dependencies
2022-12-14 19:19:09 +01:00
Pierre-Louis Mercereau
0773e5215f Merge branch 'main' into refactor/less-peer-dependencies 2022-12-14 19:01:31 +01:00
Pierre-Louis Mercereau
10f25fcc4e chore: remove needless paths 2022-12-14 18:59:57 +01:00
Johan Eliasson
2ee90d6ea3 removed unused files 2022-12-14 18:16:33 +01:00
Johan Eliasson
5db5323a1d updates 2022-12-14 18:14:40 +01:00
Szilárd Dóró
ed93d4b583 feat(dashboard): add onInitialized to autocomplete 2022-12-14 16:15:40 +01:00
Szilárd Dóró
1ec1953eaa chore(dashboard): improve default value error tolerance 2022-12-14 15:41:17 +01:00
Szilárd Dóró
63e9c3933e chore(dashboard): improved column autocomplete value management 2022-12-14 15:33:29 +01:00
Szilárd Dóró
1b1620f633 chore(dashboard): cleaned up default value functionality 2022-12-14 14:24:41 +01:00
Johan Eliasson
e8e8d661e1 small exchange refactor 2022-12-14 13:50:44 +01:00
Johan Eliasson
ba08ec7f5c comments 2022-12-14 13:44:39 +01:00
Johan Eliasson
fb0c98c21d moved react-urql to integrations 2022-12-14 13:37:20 +01:00
Johan Eliasson
c9f575c40c database settings update 2022-12-14 13:24:25 +01:00
Johan Eliasson
6c8bed7ecc Update packages/react-urql/src/provider.tsx
Co-authored-by: Szilárd Dóró <doroszilard@icloud.com>
2022-12-14 13:23:37 +01:00
Szilárd Dóró
d5a712f7ef feat(dashboard): add support for default values 2022-12-14 13:07:11 +01:00
Johan Eliasson
83422f5ee6 updated README 2022-12-14 09:42:01 +01:00
Johan Eliasson
51909a6a8f remove 2022-12-14 09:34:05 +01:00
Johan Eliasson
2f30797556 update 2022-12-14 09:28:35 +01:00
Johan Eliasson
0b8f7d1661 Merge branch 'main' into react-urql 2022-12-14 09:27:54 +01:00
Johan Eliasson
52ee9d84b6 demo img 2022-12-14 09:11:17 +01:00
Szilárd Dóró
d9612b28b0 chore(dashboard): update pnpm-lock.yaml 2022-12-14 09:07:56 +01:00
Szilárd Dóró
0034791493 Merge remote-tracking branch 'origin/main' into feat/rule-group-editor 2022-12-14 09:06:15 +01:00
Johan Eliasson
80fed14a6b updated 2022-12-14 09:03:30 +01:00
Szilárd Dóró
d457ada435 Merge pull request #1380 from nhost/changeset-release/main
chore: update versions
2022-12-14 08:57:35 +01:00
github-actions[bot]
b41e5a9df5 chore: update versions 2022-12-14 07:46:43 +00:00
Szilárd Dóró
0c8ace1bd4 Merge pull request #1379 from nhost/fix/dashboard-urls
fix(dashboard): use correct service URLs
2022-12-14 08:45:12 +01:00
Johan Eliasson
3f800a068b update 2022-12-13 22:31:42 +01:00
Szilárd Dóró
7d490fe569 fix(dashboard): use correct GraphQL slug and admin secret 2022-12-13 21:34:11 +01:00
Szilárd Dóró
d6527122db fix(dashboard): use correct service URLs 2022-12-13 21:21:12 +01:00
Johan Eliasson
3211140dec remove changeset for now 2022-12-13 21:10:41 +01:00
Johan Eliasson
469352cd81 merge 2022-12-13 21:09:44 +01:00
Szilárd Dóró
88400f6b7c chore(dashboard): add delay to queries 2022-12-13 17:41:16 +01:00
Szilárd Dóró
f8c8a06d71 feat(dashboard): permission variable queries 2022-12-13 17:24:14 +01:00
Szilárd Dóró
ebc1730fce Merge pull request #1375 from nhost/changeset-release/main
chore: update versions
2022-12-13 16:55:15 +01:00
Pierre-Louis Mercereau
c1cd1e813c docs: update changelog 2022-12-13 16:34:41 +01:00
Pierre-Louis Mercereau
e08a074474 chore: un-bump major versions 2022-12-13 16:33:30 +01:00
Szilárd Dóró
2f819865bc chore(dashboard): move handlers to mocks folder 2022-12-13 16:32:01 +01:00
Szilárd Dóró
3888f3041f chore(dashboard): improve operator selectors 2022-12-13 16:15:01 +01:00
Pierre-Louis Mercereau
569c4004f6 Merge branch 'main' into refactor/less-peer-dependencies 2022-12-13 14:10:31 +01:00
github-actions[bot]
95932fa3f2 chore: update versions 2022-12-13 13:06:10 +00:00
Pilou
99402b77d1 Merge pull request #1376 from nhost/chore/docgen
chore: remove generated documentation from the repository
2022-12-13 14:04:48 +01:00
Szilárd Dóró
f6fb2cd8e6 Merge pull request #1377 from nhost/fix/local-development-region
fix(dashboard): don't break the UI when project is not loaded yet
2022-12-13 13:49:38 +01:00
Szilárd Dóró
5c2cf59b41 chore(dashboard): update CHANGELOG.md 2022-12-13 13:11:50 +01:00
Szilárd Dóró
a6d31dc260 fix(dashboard): don't break the UI when project is not loaded yet 2022-12-13 13:10:54 +01:00
Szilárd Dóró
872e50b635 chore(dashboard): improve Chip in autocomplete 2022-12-13 13:01:44 +01:00
Pierre-Louis Mercereau
bd73557a47 Merge branch 'main' into chore/docgen 2022-12-13 12:01:04 +01:00
Pilou
c95bab70c2 Merge pull request #1366 from nhost/refactor/merge-core
refactor: decommission `@nhost/core`
2022-12-13 10:50:36 +01:00
Szilárd Dóró
52d4b5de45 Merge pull request #1347 from nhost/changeset-release/main
chore: update versions
2022-12-13 10:29:25 +01:00
Szilárd Dóró
fe0742e278 chore(dashboard): update pnpm-lock.yaml 2022-12-13 10:15:29 +01:00
Szilárd Dóró
ded57d3b24 Merge remote-tracking branch 'origin/main' into feat/rule-group-editor 2022-12-13 10:13:03 +01:00
Szilárd Dóró
c30abaea22 chore(dashboard): add reference to storybook to the README 2022-12-13 10:11:31 +01:00
Szilárd Dóró
6b4ab50f74 fix(packages): nextjs and react-apollo versions 2022-12-13 09:50:42 +01:00
github-actions[bot]
ceba605d0b chore: update versions 2022-12-13 08:47:12 +00:00
Szilárd Dóró
9249a85ee5 Merge pull request #1371 from nhost/fix/aws-name-null-reference
fix(dashboard): don't break UI when region is nullish
2022-12-13 09:45:32 +01:00
Kematzy
22de3214f1 Minor typo & formatting changes to standardise 2022-12-13 11:52:08 +08:00
Kematzy
cf880f992f Update Vue Quickstart docs 2022-12-13 11:50:51 +08:00
Szilárd Dóró
f4d70f88e9 chore(dashboard): add changeset 2022-12-12 22:43:06 +01:00
Szilárd Dóró
0d09b80b12 fix(dashboard): fix nullish region in local mode 2022-12-12 22:42:09 +01:00
Pierre-Louis Mercereau
195adfb04a chore: remove generated documentation from the repository 2022-12-12 21:26:21 +01:00
Pilou
aee4cdcb72 Update .changeset/modern-days-eat.md
Co-authored-by: Johan Eliasson <johan@nhost.io>
2022-12-12 20:20:03 +01:00
Pilou
b09930c8a4 Merge pull request #1369 from nhost/contributors-readme-action-TvJAKaD1lO
docs(contributor): contributors readme action update
2022-12-12 19:06:31 +01:00
github-actions[bot]
687951281e docs(contributor): contrib-readme-action has updated readme 2022-12-12 18:05:20 +00:00
Pilou
c0f05acd9b Merge pull request #1365 from badgifter/patch-windowslive-url
Windows Live Provider displayed link updated to match backend url
2022-12-12 19:05:01 +01:00
Szilárd Dóró
87af60cc03 feat(dashboard): add experimental multiselect autocomplete 2022-12-12 17:30:09 +01:00
Pierre-Louis Mercereau
3d8dd39995 fix: dashboard tests 2022-12-12 17:00:41 +01:00
Pierre-Louis Mercereau
65687beecc chore: rewrite the pr so it fits with Nuno's mind 2022-12-12 16:41:16 +01:00
Szilárd Dóró
62aa859737 feat(dashboard): support for boolean input field 2022-12-12 15:53:50 +01:00
Pierre-Louis Mercereau
d8c2d369aa chore: changeset 2022-12-12 15:43:55 +01:00
Pierre-Louis Mercereau
a4e4926aeb Merge branch 'main' into refactor/merge-core 2022-12-12 15:28:26 +01:00
Szilárd Dóró
9bc3e755df feat(dashboard): add dynamic rule group editor input type 2022-12-12 14:43:22 +01:00
Pilou
4a9471cc16 Create rude-singers-carry.md 2022-12-12 14:42:55 +01:00
Szilárd Dóró
638a7ac11d feat(dashboard): finalize relationship discovery 2022-12-12 14:19:02 +01:00
Bad Gifter
567e370bdc microsoft changed to windowslive 2022-12-12 14:04:35 +03:00
Bad Gifter
a91f2db0e2 Windows Live Provider displayed link updated to match backend url 2022-12-12 13:49:08 +03:00
Szilárd Dóró
4e49c8db50 fix(dashboard): correct column autocomplete input value 2022-12-12 11:28:49 +01:00
Szilárd Dóró
210f65b4db feat(dashboard): added column autocomplete to rule group editor 2022-12-12 10:50:15 +01:00
Szilárd Dóró
1b6482126f feat(dashboard): add relationship discovery 2022-12-12 10:10:39 +01:00
Szilárd Dóró
699debb2b8 fix(dashboard): correct group rendering 2022-12-09 16:12:05 +01:00
Szilárd Dóró
3e08dc7f8c feat(dashboard): improve navigation in popover 2022-12-09 14:26:46 +01:00
Szilárd Dóró
6928b48781 fix(dashboard): custom autocomplete props 2022-12-09 14:14:58 +01:00
Szilárd Dóró
02886350ff chore(dashboard): use custom autocomplete for column selector 2022-12-09 14:11:32 +01:00
Szilárd Dóró
b3672f8246 feat(dashboard): column selector metadata support 2022-12-09 12:09:43 +01:00
Szilárd Dóró
aea99ad2c8 Merge pull request #1361 from nhost/fix/apple-sign-in-mutation
fix(dashboard): do not show error when submitting Apple provider settings
2022-12-09 09:45:33 +01:00
Szilárd Dóró
594488e435 chore(dashboard): add changeset 2022-12-09 09:29:32 +01:00
Szilárd Dóró
bb83b0f81a fix(dashboard): do not show error when submitting Apple provider settings 2022-12-09 09:18:46 +01:00
Pilou
0384d7c7c4 Merge pull request #1360 from nhost/contributors-readme-action-cDMbyYcNKN
docs(contributor): contributors readme action update
2022-12-08 19:40:40 +01:00
Pilou
7e356a9604 Merge pull request #1357 from nhost/contributors-readme-action-sNfze0TcKw
docs(contributor): contributors readme action update
2022-12-08 19:29:19 +01:00
github-actions[bot]
013e55a307 docs(contributor): contrib-readme-action has updated readme 2022-12-08 18:28:57 +00:00
Pilou
2a71257cde Merge pull request #1358 from nhost/fix/workspace-packages
fix: use workspace to resolve internal dependencies
2022-12-08 19:28:41 +01:00
Pierre-Louis Mercereau
583a4401d0 docs: add explanation to the CI workflow 2022-12-08 18:50:01 +01:00
Pierre-Louis Mercereau
914e91a0b0 ci: correct test 2022-12-08 18:40:49 +01:00
Pierre-Louis Mercereau
98698213e2 ci: run all e2e tests when lockfile changed 2022-12-08 18:29:54 +01:00
Pierre-Louis Mercereau
756daa97cd fix: use workspace to resolve internal dependencies 2022-12-08 16:58:47 +01:00
github-actions[bot]
ab5a2b119c docs(contributor): contrib-readme-action has updated readme 2022-12-08 15:18:12 +00:00
Johan Eliasson
ffdcce1463 Merge pull request #1352 from wollerman/patch-1
Add a section on managing local seed data
2022-12-08 16:17:54 +01:00
Szilárd Dóró
0dbbcc5595 chore(dashboard): improve column autocomplete 2022-12-08 15:16:13 +01:00
Szilárd Dóró
a3dcb6106e Merge pull request #1355 from nhost/chore/revert-types-react-bump
chore(dashboard): revert `@types/react` bump
2022-12-08 15:03:24 +01:00
Szilárd Dóró
208d224763 chore(dashboard): revert @types/react bump 2022-12-08 14:46:36 +01:00
Szilárd Dóró
816f8d069d fix(dashboard): pnpm-lock file 2022-12-08 14:20:28 +01:00
Szilárd Dóró
2ebf99ff8f chore(dashboard): improved column autocomplete 2022-12-08 09:32:51 +01:00
renovate[bot]
c13e492bbf chore(deps): update dependency typescript to v4.9.4 2022-12-08 01:41:29 +00:00
Matt Wollerman
564d000bfc add link to overview description of seed data 2022-12-07 20:21:09 -05:00
Szilárd Dóró
63476a2351 feat(dashboard): start work on column autocomplete 2022-12-07 16:49:55 +01:00
Matt Wollerman
266fda07ab add a section on managing local seed data 2022-12-07 08:45:40 -05:00
Szilárd Dóró
782252c059 chore(dashboard): improved Rule Group editor 2022-12-07 14:42:20 +01:00
Szilárd Dóró
e86978a1ff chore(dashboard): upgrade storybook
- chore(dashboard): improve mobile layout of the rule group editor
2022-12-07 13:32:05 +01:00
Szilárd Dóró
84cfd11953 chore(dashboard): extend rule group editor story 2022-12-07 13:12:13 +01:00
Szilárd Dóró
9a43e136f6 fix(dashboard): fixed rule group editor form structure 2022-12-07 11:22:50 +01:00
Szilárd Dóró
e9cff26fa0 chore(dashboard): improve rule group editor layout 2022-12-07 10:11:54 +01:00
Szilárd Dóró
3d32bca2b3 fix(dashboard): fix rule group editor delete button 2022-12-06 16:11:08 +01:00
Szilárd Dóró
4021feaf38 feat(dashboard): rule editor implementation 2022-12-06 14:02:07 +01:00
Pilou
6174e1ddcc Merge pull request #1341 from nhost/refactor/nhost-react-providers
refactor: nhost react providers
2022-12-06 12:57:26 +01:00
Pierre-Louis Mercereau
af2e3eae37 chore: minor bump 2022-12-06 12:39:30 +01:00
Pilou
d2b4c126f3 Merge pull request #1346 from nhost/chore/prettier-ignore-lockfile
chore: prettier ignore pnpm-lock.yaml
2022-12-06 11:01:00 +01:00
Pierre-Louis Mercereau
7f2e182c47 chore: prettier ignore pnpm-lock.yaml 2022-12-06 10:54:16 +01:00
Szilárd Dóró
306ec74356 chore(dashboard): update import paths 2022-12-06 10:16:42 +01:00
Szilárd Dóró
ae40bd54d4 Merge pull request #1302 from nhost/renovate/react-18.x
chore(deps): update dependency @types/react to v18.0.26
2022-12-06 10:01:27 +01:00
Pilou
b5cc47078a Update .changeset/big-pumpkins-march.md
Co-authored-by: Szilárd Dóró <doroszilard@icloud.com>
2022-12-06 09:59:25 +01:00
Pierre-Louis Mercereau
7f251111e2 chore: 🤖 changeset 2022-12-06 09:41:40 +01:00
Pierre-Louis Mercereau
c03dacc3a3 refactor: use NhostProvider in React and Nextjs 2022-12-06 09:39:44 +01:00
Pilou
8b9e1a0ce8 Merge pull request #1339 from nhost/contributors-readme-action-oSzExU64y7
docs(contributor): contributors readme action update
2022-12-06 07:04:10 +01:00
github-actions[bot]
cf9cfec330 docs(contributor): contrib-readme-action has updated readme 2022-12-06 05:52:28 +00:00
Pilou
1c94f56c59 Merge pull request #1338 from iangabrielsanchez/patch-1
Fix typo
2022-12-06 06:52:14 +01:00
Ian Gabriel Sanchez
f06d5deba3 Fix typo 2022-12-06 10:15:59 +08:00
Nuno Pato
8ff58d7f23 Merge pull request #1330 from nhost/changeset-release/main
chore: update versions
2022-12-05 15:22:02 -01:00
github-actions[bot]
8dd1c7415b chore: update versions 2022-12-05 15:56:54 +00:00
Nuno Pato
ebd2749e38 Merge pull request #1329 from nhost/fix/hasura-duplicate-slug
fix(dashboard): use correct Hasura slug
2022-12-05 14:55:28 -01:00
Szilárd Dóró
80b604adda fix(dashboard): use correct Hasura slug 2022-12-05 16:35:35 +01:00
Nuno Pato
9d73050792 Merge pull request #1322 from nhost/changeset-release/main
chore: update versions
2022-12-05 14:20:47 -01:00
renovate[bot]
2a86b8876c chore(deps): update dependency @types/react to v18.0.26 2022-12-05 14:47:07 +00:00
github-actions[bot]
91a1a41f5d chore: update versions 2022-12-05 14:41:11 +00:00
Nuno Pato
22e9c27c81 Merge pull request #1328 from nhost/fix/graphiql-editor
fix(dashboard): prevent error on GraphQL page
2022-12-05 13:39:28 -01:00
Szilárd Dóró
2d2beb53d2 fix(dashboard): prevent error on GraphQL page 2022-12-05 15:26:37 +01:00
Pilou
b403b0d6a0 Merge pull request #1327 from nhost/ci/unambiguous-git-log
chore: don't use PAT in renovate post PR
2022-12-05 14:21:43 +01:00
Pierre-Louis Mercereau
4dbac55cb4 chore: don't use PAT in renovate post PR 2022-12-05 14:20:56 +01:00
Pilou
c6e31ac741 Merge pull request #1324 from nhost/renovate/xstate-vue-2.x
fix(deps): update dependency @xstate/vue to v2
2022-12-05 13:06:56 +01:00
Pilou
0d3e8b3992 Merge pull request #1325 from nhost/ci/unambiguous-git-log
chore(gh-actions): `git log` unabiguously
2022-12-05 12:58:15 +01:00
Pierre-Louis Mercereau
b2afd14d61 chore(gh-actions): git log unabiguously 2022-12-05 12:57:46 +01:00
renovate[bot]
f28f28b6ee fix(deps): update dependency @xstate/vue to v2 2022-12-05 11:52:06 +00:00
Pilou
834b959271 Merge pull request #1323 from nhost/renovate/vueuse-core-9.x
fix(deps): update dependency @vueuse/core to v9
2022-12-05 12:44:43 +01:00
renovate[bot]
4dbc9ccc87 fix(deps): update dependency @vueuse/core to v9 2022-12-05 11:23:06 +00:00
Pierre-Louis Mercereau
2764a1c4b6 Merge branch 'main' into refactor/merge-core 2022-12-05 12:17:21 +01:00
Nuno Pato
1666ca2ec5 Merge pull request #1250 from nhost/chore/deprecate-old-dns-name
chore(dashboard): deprecate old DNS name
2022-12-05 10:10:37 -01:00
Pilou
346791d4d5 Merge pull request #1321 from nhost/renovate/pnpm-find-workspace-dir-5.x
fix(deps): update dependency @pnpm/find-workspace-dir to v5
2022-12-05 11:55:33 +01:00
Pierre-Louis Mercereau
94bdafe22f chore: adapt sync-versions to find-workspace-dir 5 2022-12-05 11:51:44 +01:00
renovate[bot]
33782e9d41 fix(deps): update dependency @pnpm/find-workspace-dir to v5 2022-12-05 10:46:45 +00:00
Johan Eliasson
ea02e1e104 Merge pull request #1311 from nhost/example-serverless-functions-stripe-webhooks
example(serverless functions): Stripe Webhooks
2022-12-05 11:39:36 +01:00
Johan Eliasson
98bf6e3792 Update examples/serverless-functions/functions/stripe-webhook/index.ts
Co-authored-by: Nuno Pato <nunopato@gmail.com>
2022-12-05 11:39:26 +01:00
Johan Eliasson
d9dcafd643 Update examples/serverless-functions/functions/stripe-webhook/index.ts
Co-authored-by: Nuno Pato <nunopato@gmail.com>
2022-12-05 11:39:20 +01:00
Johan Eliasson
4f3d97b5ad Merge pull request #1316 from nhost/elitan-patch-3
docs clarifications
2022-12-05 11:38:51 +01:00
Nuno Pato
d1801ceae9 dashboard: fix link 2022-12-05 09:12:40 -01:00
Szilárd Dóró
b6f9fe6304 Merge pull request #1301 from nhost/changeset-release/main
chore: update versions
2022-12-05 11:02:10 +01:00
github-actions[bot]
15b652d7e0 chore: update versions 2022-12-05 09:14:59 +00:00
Szilárd Dóró
cdc0047cb7 Merge pull request #1284 from nhost/renovate/graphiql-react-0.x
fix(deps): update dependency @graphiql/react to ^0.15.0
2022-12-05 10:13:19 +01:00
Pilou
7c0e71e8be Merge pull request #1303 from nhost/renovate/eslint-plugin-vue-9.x
chore(deps): update dependency eslint-plugin-vue to v9
2022-12-05 09:38:23 +01:00
Pilou
175d6d8cbc Merge pull request #1315 from plmercereau/ci/nhost-cli-action
ci: generic Nhost CLI action
2022-12-05 09:30:31 +01:00
Pierre-Louis Mercereau
2ae277409a ci: action test cleanup 2022-12-05 09:14:58 +01:00
Pierre-Louis Mercereau
ada10170b7 ci: always checkout 2022-12-05 09:08:04 +01:00
Pierre-Louis Mercereau
0ed77cbe8b ci: add cli action tests 2022-12-05 09:05:51 +01:00
Johan Eliasson
1abc68992f Update users.mdx 2022-12-05 08:15:14 +01:00
Pilou
795962e3c2 Merge pull request #1313 from nhost/ci/e2e-matrix-job-name
ci: improve the names of the e2e matrix jobs
2022-12-05 01:28:13 +01:00
Pilou
ba998eb632 Update README.md 2022-12-05 01:18:13 +01:00
Pierre-Louis Mercereau
d3fc1bbeb9 ci: simplify config injection 2022-12-05 00:07:35 +01:00
Pierre-Louis Mercereau
7b9c2016d0 ci: default is: do not start the app 2022-12-04 23:44:51 +01:00
Pierre-Louis Mercereau
ebf4070be6 ci: final adjustments 2022-12-04 23:39:01 +01:00
Pierre-Louis Mercereau
59e3f6abc6 ci: cli version 2022-12-04 23:32:04 +01:00
Pierre-Louis Mercereau
0c9a03a7ff docs: add nhost-cli action readme 2022-12-04 23:00:24 +01:00
Pierre-Louis Mercereau
42cbe27914 ci: revert 2022-12-04 22:51:04 +01:00
Pierre-Louis Mercereau
d7e7b0e51b ci: cleanup 2022-12-04 22:50:11 +01:00
Pierre-Louis Mercereau
036181dd75 ci: clean 2022-12-04 22:48:19 +01:00
Pierre-Louis Mercereau
2828a9fe01 ci: silence curl 2022-12-04 22:41:16 +01:00
Pierre-Louis Mercereau
88bc71dffc ci: try another version 2022-12-04 22:39:49 +01:00
Pierre-Louis Mercereau
17699870a6 ci: correct curl 2022-12-04 22:34:41 +01:00
Pierre-Louis Mercereau
5c0304ab73 ci: remove sS 2022-12-04 22:30:41 +01:00
Pierre-Louis Mercereau
637265e3d9 ci: again 2022-12-04 22:29:34 +01:00
Pierre-Louis Mercereau
229604d8e1 ci: correct retry 2022-12-04 22:25:45 +01:00
Pierre-Louis Mercereau
1165a33079 ci: --retry-connrefused 2022-12-04 22:22:26 +01:00
Pierre-Louis Mercereau
82c0dd9d87 ci: retry again 2022-12-04 22:17:00 +01:00
Pierre-Louis Mercereau
a9bfab1778 ci: 16.1 2022-12-04 22:15:29 +01:00
Pierre-Louis Mercereau
60b7a664d2 ci: improve wait-for 2022-12-04 22:14:53 +01:00
Pierre-Louis Mercereau
4f708d04d6 ci: correct path 2022-12-04 21:56:24 +01:00
Pierre-Louis Mercereau
a02af56056 ci: config 2022-12-04 21:52:17 +01:00
Pierre-Louis Mercereau
575136dcfb ci: don't fail, and fail 2022-12-04 16:45:44 +01:00
Pierre-Louis Mercereau
2bc6346cbc ci: not siltent 2022-12-04 16:42:17 +01:00
Pierre-Louis Mercereau
91cd494a3d ci: correct nhost dev 2022-12-04 16:37:21 +01:00
Pierre-Louis Mercereau
d57e0a5287 ci: path 2022-12-04 16:36:02 +01:00
Pierre-Louis Mercereau
dd5e7093f0 ci: start 2022-12-04 16:34:47 +01:00
Pierre-Louis Mercereau
3e9bb84f07 ci: don't build packages 2022-12-04 16:34:05 +01:00
Pierre-Louis Mercereau
6665b58ec8 ci: dedicated wip action 2022-12-04 16:33:13 +01:00
Pierre-Louis Mercereau
456e893cd6 ci: change branch reference 2022-12-04 16:24:47 +01:00
Pierre-Louis Mercereau
fd76170ca6 ci: nhost-cli action 2022-12-04 16:23:52 +01:00
Pierre-Louis Mercereau
682aef2d94 ci: improve the names of the e2e matrix jobs 2022-12-03 23:01:46 +01:00
Pilou
cb53f71d4a Merge pull request #1312 from plmercereau/main
Chore(docker-compose): fix tests
2022-12-03 17:28:42 +01:00
Pierre-Louis Mercereau
4fc0b40cb4 chore(docker-compose): increase timeout 2022-12-03 17:20:48 +01:00
Pierre-Louis Mercereau
9da2d01c55 chore(docker-compose): describe 2022-12-03 17:16:11 +01:00
Pierre-Louis Mercereau
0d5e7850f8 chore(docker-compose): retry 2022-12-03 17:14:23 +01:00
Johan Eliasson
8ed965c669 keep migrations file here, otherwise nhost dev won't work 2022-12-03 14:44:22 +01:00
Pilou
cb284b40b1 Merge pull request #1308 from nhost/ci/docker-compose
chore: improve docker-compose example
2022-12-03 13:59:10 +01:00
Johan Eliasson
c59f622feb fix 2022-12-03 13:48:40 +01:00
Johan Eliasson
9a30edd038 example 2022-12-03 13:47:42 +01:00
Johan Eliasson
4b2d2a4f55 merge 2022-12-03 13:36:02 +01:00
Johan Eliasson
eba2bd05b8 update 2022-12-03 13:35:04 +01:00
Johan Eliasson
84a1b28261 Create breezy-donuts-try.md 2022-12-03 13:34:20 +01:00
Johan Eliasson
ff3b9df41e cleanup 2022-12-03 13:32:06 +01:00
Pilou
b8b4e36175 Merge pull request #1307 from nhost/renovate/uuid-9.x
chore(deps): update dependency uuid to v9
2022-12-03 11:33:51 +01:00
Pierre-Louis Mercereau
6e47ef68d5 Merge branch 'ci/docker-compose' of https://github.com/nhost/nhost into ci/docker-compose 2022-12-02 23:11:24 +01:00
Pilou
2697414637 Create fair-students-enjoy.md 2022-12-02 23:08:13 +01:00
Pierre-Louis Mercereau
e753b2faed chore: quiet docker pull 2022-12-02 23:01:55 +01:00
Pierre-Louis Mercereau
6f62ec7d2a fix: use docker-compose v2 syntax 2022-12-02 22:43:32 +01:00
Pierre-Louis Mercereau
d365ef1953 test: 💍 create e2e tests for the docker-compose example 2022-12-02 22:20:12 +01:00
Szilárd Dóró
99ee9fd10d fix(dashboard): move Brazilian flag 2022-12-02 20:29:58 +01:00
Nuno Pato
9608a327c9 dashboard: add brazilian flag 2022-12-02 18:27:14 -01:00
Szilárd Dóró
492b83ef58 fix(dashboard): correct GraphQL URL 2022-12-02 20:25:11 +01:00
renovate[bot]
09f53ae43f chore(deps): update dependency uuid to v9 2022-12-02 18:30:25 +00:00
renovate[bot]
7724ac7e06 chore(deps): update dependency eslint-plugin-vue to v9 2022-12-02 15:56:01 +00:00
renovate[bot]
02343fe171 fix(deps): update dependency @graphiql/react to ^0.15.0 2022-12-02 15:52:13 +00:00
Szilárd Dóró
98dd8d039c Merge pull request #1305 from nhost/fix/pnpm-lock
fix(deps): correct pnpm-lock.yaml
2022-12-02 16:35:13 +01:00
Szilárd Dóró
456e057497 Merge remote-tracking branch 'origin/main' into fix/pnpm-lock 2022-12-02 16:34:46 +01:00
Pilou
93e9b58a58 Merge pull request #1289 from nhost/renovate/faker-6.x
chore(deps): update dependency @types/faker to v6
2022-12-02 16:33:33 +01:00
Szilárd Dóró
019a7c2335 fix: correct pnpm-lock.yaml 2022-12-02 16:26:51 +01:00
Pierre-Louis Mercereau
a0901914ac Merge branch 'main' into refactor/merge-core 2022-12-02 16:09:32 +01:00
Pilou
64882a8e16 Merge pull request #1279 from nhost/renovate/prettier-plugin-tailwindcss-0.x
chore(deps): update dependency prettier-plugin-tailwindcss to ^0.2.0
2022-12-02 15:59:13 +01:00
Pilou
1ed572fe39 Merge pull request #1275 from nhost/renovate/msw-0.x
chore(deps): update dependency msw to ^0.49.0
2022-12-02 15:58:56 +01:00
Pilou
e06271a8ae Merge pull request #1282 from nhost/renovate/vitest-monorepo
chore(deps): update dependency vitest to ^0.25.0
2022-12-02 15:56:09 +01:00
Szilárd Dóró
bca239ebb2 Merge pull request #1296 from nhost/chore/cleanup-deps
chore(deps): remove unused deps, sync @types/react and @types/react-dom
2022-12-02 15:51:40 +01:00
Szilárd Dóró
e33caa046d Merge pull request #1273 from nhost/fix/settings-env-var-empty-list
fix(dashboard): correct section paddings when no env vars
2022-12-02 15:51:05 +01:00
renovate[bot]
cbbd331341 chore(deps): update dependency prettier-plugin-tailwindcss to ^0.2.0 2022-12-02 14:42:50 +00:00
Pierre-Louis Mercereau
c41bfaffdd chore: 🤖 remove unused @types/faker 2022-12-02 15:39:20 +01:00
renovate[bot]
f2596b0b14 chore(deps): update dependency msw to ^0.49.0 2022-12-02 14:31:58 +00:00
Pilou
4d0c3111d1 Merge pull request #1299 from nhost/chore/labeler
chore: don't remove existing labels
2022-12-02 15:29:18 +01:00
Pierre-Louis Mercereau
7ff9644ac7 chore: don't remove existing labels 2022-12-02 15:28:35 +01:00
renovate[bot]
87fdaa7144 chore(deps): update dependency @types/faker to v6 2022-12-02 13:56:59 +00:00
Szilárd Dóró
132a4f4be9 chore(deps): synchronize @types/react-dom and @types/react versions 2022-12-02 13:36:26 +01:00
Szilárd Dóró
6b31e31430 Merge branch 'main' into chore/cleanup-deps 2022-12-02 13:31:09 +01:00
Szilárd Dóró
9ba2208dd7 chore(dashboard): remove unused dependencies 2022-12-02 13:30:02 +01:00
Szilárd Dóró
f1272947dd chore(deps): update @types/react and @types/react-dom 2022-12-02 10:17:32 +01:00
Johan Eliasson
e5041bfd30 Merge branch 'main' into react-urql 2022-12-02 09:59:38 +01:00
Johan Eliasson
3d7cc74feb env vars 2022-12-02 09:42:38 +01:00
Johan Eliasson
d007202783 update 2022-12-02 09:40:26 +01:00
Johan Eliasson
38e92a705d works outside strict mode now 2022-12-02 09:38:16 +01:00
Johan Eliasson
74cc63833a package updates 2022-12-01 22:05:28 +01:00
Johan Eliasson
425320bbb5 lockfile update 2022-12-01 21:31:57 +01:00
Johan Eliasson
499352ad8a Merge branch 'main' into react-urql 2022-12-01 21:30:53 +01:00
renovate[bot]
023060cee6 chore(deps): update dependency vitest to ^0.25.0 2022-12-01 20:02:01 +00:00
Szilárd Dóró
db57572f38 fix(dashboard): correct section paddings when no env vars 2022-12-01 15:40:16 +01:00
Szilárd Dóró
c9f88326b2 chore(dashboard): migrate app URLs to service URLs 2022-11-30 17:12:58 +01:00
Szilárd Dóró
ac8efcbdd5 chore(dashboard): add changeset 2022-11-30 16:13:30 +01:00
Szilárd Dóró
9bc346e8d4 fix(dashboard): minor layout fixes in provider alert 2022-11-30 16:12:11 +01:00
Nuno Pato
efed987d31 fix linter 2022-11-30 12:12:48 -01:00
Nuno Pato
bdab7da7d3 fix linter 2022-11-30 12:02:10 -01:00
Nuno Pato
c2d9993968 asd 2022-11-30 11:53:30 -01:00
Nuno Pato
508ba62207 update copy 2022-11-30 11:52:41 -01:00
Nuno Pato
a3318de06e incorporate minor changes from comments 2022-11-30 11:49:25 -01:00
Pierre-Louis Mercereau
26d577d7ae Merge branch 'main' into refactor/merge-core 2022-11-30 11:43:30 +01:00
Nuno Pato
fa9f7ca052 Deprecate old DNS name 2022-11-30 03:01:17 -01:00
Pierre-Louis Mercereau
622c48a94b chore: update changeset 2022-11-29 16:18:18 +01:00
Pierre-Louis Mercereau
e1a87a05b1 chore: update lock file 2022-11-29 16:15:49 +01:00
Pierre-Louis Mercereau
2148317282 Merge branch 'main' into refactor/merge-core 2022-11-29 16:15:05 +01:00
Pierre-Louis Mercereau
5f9c6c8346 Merge branch 'main' into refactor/merge-core 2022-11-28 21:16:06 +01:00
Johan Eliasson
2e30371086 Merge branch 'main' into react-urql 2022-11-28 17:04:51 +01:00
Pierre-Louis Mercereau
57db5b83d4 chore: 🤖 changeset 2022-11-28 15:39:21 +01:00
Johan Eliasson
66659bb293 update 2022-11-27 21:31:14 +01:00
Johan Eliasson
579d4f3170 update 2022-11-27 21:14:10 +01:00
Johan Eliasson
bb56548603 urql 2022-11-27 21:12:07 +01:00
Pierre-Louis Mercereau
3cab18713a Merge branch 'main' into refactor/merge-core 2022-11-24 17:33:18 +01:00
Pierre-Louis Mercereau
fb94dae43a chore: merge main 2022-11-24 17:17:04 +01:00
Pierre-Louis Mercereau
f694846eae chore: 🤖 remove hasura-storage-js to hasura-auth-js 2022-11-24 16:54:13 +01:00
Pierre-Louis Mercereau
322ab50138 Merge branch 'main' into refactor/merge-core 2022-11-22 09:52:45 +01:00
Pierre-Louis Mercereau
435efd2bc5 fix: 🐛 Set same-site cookie to lax 2022-11-21 15:22:05 +01:00
Szilárd Dóró
5501a5937e fix(docgen): complex types in markdown tables 2022-11-16 12:27:24 +01:00
Pierre-Louis Mercereau
80a6808a82 Merge branch 'main' into refactor/merge-core 2022-11-16 10:26:11 +01:00
Pierre-Louis Mercereau
987bd70312 Merge branch 'main' into refactor/merge-core 2022-11-15 14:14:58 +01:00
Pierre-Louis Mercereau
feb22e62c1 fix: add missing oauth provider: azuread 2022-11-14 23:48:45 +01:00
Pierre-Louis Mercereau
e7d4c77a6d refactor: 💡 restructure hasura-auth-js after the core merger 2022-11-14 23:20:49 +01:00
Pierre-Louis Mercereau
628e32464d Merge branch 'main' into refactor/merge-core 2022-11-14 21:45:05 +01:00
Pierre-Louis Mercereau
dc82043254 chore: only include the auth client typedoc 2022-11-09 22:13:20 +01:00
Pierre-Louis Mercereau
997e9d58a8 refactor: 💡 remove @nhost/core 2022-11-09 22:09:17 +01:00
Pierre-Louis Mercereau
8a3f1706fe chore: add auth as a dependency to storage 2022-11-09 22:01:11 +01:00
Pierre-Louis Mercereau
12cbe4d534 chore: lint 2022-11-09 21:52:15 +01:00
Pierre-Louis Mercereau
9f21931201 refactor: copy tests from core 2022-11-09 21:46:32 +01:00
Pierre-Louis Mercereau
09351e1910 Merge branch 'main' into refactor/merge-core 2022-11-09 20:49:33 +01:00
Pierre-Louis Mercereau
1c2ea5a407 refactor: 💡 merge @nhost/core 2022-11-09 20:49:13 +01:00
900 changed files with 18332 additions and 20119 deletions

View File

@@ -2,9 +2,22 @@
"$schema": "https://unpkg.com/@changesets/config@1.6.0/schema.json", "$schema": "https://unpkg.com/@changesets/config@1.6.0/schema.json",
"changelog": "@changesets/cli/changelog", "changelog": "@changesets/cli/changelog",
"commit": false, "commit": false,
"linked": [], "linked": [
[
"@nhost/nextjs",
"@nhost/react",
"@nhost/vue",
"@nhost/nhost-js",
"@nhost/hasura-auth-js",
"@nhost/hasura-storage-js"
],
[
"@nhost/react-apollo",
"@nhost/apollo"
]
],
"access": "restricted", "access": "restricted",
"baseBranch": "main", "baseBranch": "main",
"updateInternalDependencies": "patch", "updateInternalDependencies": "patch",
"ignore": [] "ignore": []
} }

108
.github/actions/nhost-cli/README.md vendored Normal file
View File

@@ -0,0 +1,108 @@
# Nhost CLI GitHub Action
## Usage
```yaml
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install the Nhost CLI
uses: ./.github/actions/nhost-cli
```
### Install the CLI and start the app
```yaml
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Nhost CLI and start the application
uses: ./.github/actions/nhost-cli
with:
start: true
```
### Set another working directory
```yaml
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Nhost CLI
uses: ./.github/actions/nhost-cli
with:
path: examples/react-apollo
start: true
```
### Don't wait for the app to be ready
```yaml
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Nhost CLI and start app
uses: ./.github/actions/nhost-cli
with:
start: true
wait: false
```
### Stop the app
```yaml
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Start app
uses: ./.github/actions/nhost-cli
with:
start: true
- name: Do something
cmd: echo "do something"
- name: Stop
uses: ./.github/actions/nhost-cli
with:
stop: true
```
### Install a given value of the CLI
```yaml
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Nhost CLI
uses: ./.github/actions/nhost-cli
with:
version: v0.8.10
```
### Inject values into nhost/config.yaml
```yaml
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Nhost CLI
uses: ./.github/actions/nhost-cli
with:
config: |
services:
auth:
image: nhost/hasura-auth:0.16.1
```

78
.github/actions/nhost-cli/action.yaml vendored Normal file
View File

@@ -0,0 +1,78 @@
name: Nhost CLI
description: 'Action to install the Nhost CLI and to run an application'
inputs:
start:
description: "Start the application. If false, the application won't be started"
default: 'false'
wait:
description: 'If starting the application, wait until it is ready'
default: 'true'
stop:
description: 'Stop the application'
default: 'false'
path:
description: 'Path to the application'
default: '.'
version:
description: 'Version of the Nhost CLI'
default: 'latest'
config:
description: 'Values to be injected into nhost/config.yaml'
runs:
using: 'composite'
steps:
- name: Check if Nhost CLI is already installed
id: check-nhost-cli
shell: bash
# TODO check if the version is the same
run: |
if [ -z "$(which nhost)" ]
then
echo "installed=false" >> $GITHUB_OUTPUT
else
echo "installed=true" >> $GITHUB_OUTPUT
fi
- name: Install Nhost CLI
if: ${{ steps.check-nhost-cli.outputs.installed == 'false' }}
uses: nick-fields/retry@v2
with:
timeout_minutes: 3
max_attempts: 3
command: bash <(curl --silent -L https://raw.githubusercontent.com/nhost/cli/main/get.sh) ${{ inputs.version }}
- name: Set custom configuration
if: ${{ inputs.config }}
shell: bash
working-directory: ${{ inputs.path }}
run: config="${{ inputs.config }}" yq -i '. *= env(config)' nhost/config.yaml
- name: Start the application
if: ${{ inputs.start == 'true' }}
shell: bash
working-directory: ${{ inputs.path }}
run: nhost dev --no-browser &
- name: Wait for the app to be ready
id: wait
if: ${{ inputs.start == 'true' && inputs.wait == 'true' }}
shell: bash
working-directory: ${{ inputs.path }}
continue-on-error: true
run: |
curl -sSf --connect-timeout 3 \
--max-time 5 \
--retry 300 \
--retry-delay 1 \
--retry-max-time 300 \
--retry-connrefused \
'http://localhost:9695' > /dev/null
- name: Log on failure
if: steps.wait.outcome == 'failure'
shell: bash
working-directory: ${{ inputs.path }}
run: |
nhost logs
exit 1
- name: Stop the application
if: ${{ inputs.stop == 'true' }}
shell: bash
working-directory: ${{ inputs.path }}
run: nhost down

7
.github/labeler.yml vendored
View File

@@ -12,11 +12,14 @@ examples:
sdk: sdk:
- packages/**/* - packages/**/*
integrations:
- integrations/**/*
react: react:
- '{packages,examples}/*react*/**/*' - '{packages,examples,integrations}/*react*/**/*'
nextjs: nextjs:
- '{packages,examples}/*next*/**/*' - '{packages,examples}/*next*/**/*'
vue: vue:
- '{packages,examples}/*vue*/**/*' - '{packages,examples,integrations}/*vue*/**/*'

12
.github/renovate.json vendored
View File

@@ -3,11 +3,21 @@
"extends": [ "extends": [
"config:base" "config:base"
], ],
"docker-compose": {
"enabled": true
},
"ignoreDeps": [ "ignoreDeps": [
"pnpm", "pnpm",
"node" "node",
"@types/node"
], ],
"labels": [ "labels": [
"dependencies" "dependencies"
],
"enabledManagers": [
"npm",
"dockerfile",
"docker-compose",
"github-actions"
] ]
} }

View File

@@ -36,13 +36,24 @@ jobs:
TURBO_TOKEN: ${{ env.TURBO_TOKEN }} TURBO_TOKEN: ${{ env.TURBO_TOKEN }}
TURBO_TEAM: ${{ env.TURBO_TEAM }} TURBO_TEAM: ${{ env.TURBO_TEAM }}
BUILD: 'all' BUILD: 'all'
- name: Check if the pnpm lockfile changed
id: changed-lockfile
uses: tj-actions/changed-files@v35
with:
files: pnpm-lock.yaml
# * Determine a pnpm filter argument for packages that have been modified.
# * If the lockfile has changed, we don't filter anything in order to run all the e2e tests.
- name: filter packages
id: filter-packages
if: steps.changed-lockfile.outputs.any_changed != 'true' && github.event_name == 'pull_request'
run: echo "filter=${{ format('--filter=...[origin/{0}]', github.base_ref) }}" >> $GITHUB_OUTPUT
# * List packagesthat has an `e2e` script, except the root, and return an array of their name and path # * List packagesthat has an `e2e` script, except the root, and return an array of their name and path
# * In a PR, only include packages that have been modified, and their dependencies # * In a PR, only include packages that have been modified, and their dependencies
- name: List examples with an e2e script - name: List examples with an e2e script
id: set-matrix id: set-matrix
run: | run: |
FILTER_MODIFIED="${{ github.event_name == 'pull_request' && format('--filter=...[origin/{0}]', github.base_ref) || '' }}" PACKAGES=$(pnpm recursive list --depth -1 --parseable --filter='!nhost-root' ${{ steps.filter-packages.outputs.filter }} \
PACKAGES=$(pnpm recursive list --depth -1 --parseable --filter='!nhost-root' $FILTER_MODIFIED \ | xargs -I@ realpath --relative-to=$PWD @ \
| xargs -I@ jq "if (.scripts.e2e | length) != 0 then {name: .name, path: \"@\"} else null end" @/package.json \ | xargs -I@ jq "if (.scripts.e2e | length) != 0 then {name: .name, path: \"@\"} else null end" @/package.json \
| awk "!/null/" \ | awk "!/null/" \
| jq -c --slurp) | jq -c --slurp)
@@ -51,7 +62,7 @@ jobs:
matrix: ${{ steps.set-matrix.outputs.matrix }} matrix: ${{ steps.set-matrix.outputs.matrix }}
e2e: e2e:
name: 'e2e: ${{ matrix.package.name }}' name: 'e2e (${{ matrix.package.path }})'
needs: build needs: build
if: ${{ needs.build.outputs.matrix != '[]' && needs.build.outputs.matrix != '' }} if: ${{ needs.build.outputs.matrix != '[]' && needs.build.outputs.matrix != '' }}
strategy: strategy:
@@ -71,7 +82,7 @@ jobs:
# * Install Nhost CLI if a `nhost/config.yaml` file is found # * Install Nhost CLI if a `nhost/config.yaml` file is found
- name: Install Nhost CLI - name: Install Nhost CLI
if: hashFiles(format('{0}/nhost/config.yaml', matrix.package.path)) != '' if: hashFiles(format('{0}/nhost/config.yaml', matrix.package.path)) != ''
run: curl -L https://raw.githubusercontent.com/nhost/cli/main/get.sh | bash uses: ./.github/actions/nhost-cli
# * Run the `ci` script of the current package of the matrix. Dependencies build is cached by Turborepo # * Run the `ci` script of the current package of the matrix. Dependencies build is cached by Turborepo
- name: Run e2e test - name: Run e2e test
run: pnpm --filter="${{ matrix.package.name }}" run e2e run: pnpm --filter="${{ matrix.package.name }}" run e2e

View File

@@ -1,15 +0,0 @@
name: Add contributors
on:
push:
branches:
- main
jobs:
contrib-readme-job:
runs-on: ubuntu-latest
name: A job to automate contrib in readme
steps:
- name: Contribute List
uses: akhilmhdh/contributors-readme-action@v2.3.6
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -12,3 +12,4 @@ jobs:
- uses: actions/labeler@v4 - uses: actions/labeler@v4
with: with:
repo-token: '${{ secrets.GH_PAT }}' repo-token: '${{ secrets.GH_PAT }}'
sync-labels: ''

View File

@@ -32,7 +32,7 @@ jobs:
- name: Determine bumps - name: Determine bumps
id: bumps id: bumps
run: | run: |
LAST_NON_PR_SHA=$(git log --no-merges main origin/${{ github.head_ref }} --format=format:%h | head -2 | tail -1) LAST_NON_PR_SHA=$(git log --no-merges main origin/${{ github.head_ref }} --format=format:%h -- | head -2 | tail -1)
echo "result<<EOF" >> $GITHUB_OUTPUT echo "result<<EOF" >> $GITHUB_OUTPUT
pnpm recursive list --depth -1 --parseable \ pnpm recursive list --depth -1 --parseable \
--filter='!nhost-root' \ --filter='!nhost-root' \
@@ -62,8 +62,28 @@ jobs:
${{ github.event.pull_request.title }} ${{ github.event.pull_request.title }}
EOF EOF
- uses: stefanzweifel/git-auto-commit-action@v4 - name: Create Pull Request
if: steps.bumps.outputs.result != '' id: cpr
uses: peter-evans/create-pull-request@v4
with: with:
commit_message: ${{ github.event.pull_request.title }} token: ${{ secrets.GH_PAT }}
branch: main commit-message: ${{ github.event.pull_request.title }}
branch: renovate-changesets
delete-branch: true
title: 'chore: create changesest from Renovate bumps'
labels: |
dependencies
body: |
This PR creates the changesets from the Renovate dependencies that have been merged to main.
- name: Enable Pull Request Automerge
if: steps.cpr.outputs.pull-request-operation == 'created'
uses: peter-evans/enable-pull-request-automerge@v2
with:
token: ${{ secrets.GH_PAT }}
pull-request-number: ${{ steps.cpr.outputs.pull-request-number }}
- name: Auto approve
if: steps.cpr.outputs.pull-request-operation == 'created'
uses: juliangruber/approve-pull-request-action@v2
with:
github-token: ${{ secrets.GH_PAT }}
number: ${{ steps.cpr.outputs.pull-request-number }}

View File

@@ -0,0 +1,104 @@
name: Test Nhost CLI action
on:
pull_request:
branches: [main]
types: [opened, synchronize]
paths:
- '.github/actions/nhost-cli/**'
- '!.github/actions/nhost-cli/**/*.md'
jobs:
install:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install the Nhost CLI
uses: ./.github/actions/nhost-cli
- name: should succeed running the nhost command
run: nhost
start:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install the Nhost CLI and start the application
uses: ./.github/actions/nhost-cli
with:
path: packages/nhost-js
start: true
- name: should be running
run: curl -sSf 'http://localhost:9695' > /dev/null
stop:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install the Nhost CLI, start and stop the application
uses: ./.github/actions/nhost-cli
with:
path: packages/nhost-js
start: true
stop: true
- name: should have no live docker container
run: |
if [ -z "docker ps -q" ]; then
echo "Some docker containers are still running"
docker ps
exit 1
fi
wait:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install the Nhost CLI and start the application
uses: ./.github/actions/nhost-cli
with:
path: packages/nhost-js
start: true
wait: false
- name: should not be ready
run: curl -sSf -o /dev/null 'http://localhost:9695' > /dev/null && exit 1 || true
- name: should eventually be ready
run: |
curl -sSf --connect-timeout 3 \
--max-time 5 \
--retry 300 \
--retry-delay 1 \
--retry-max-time 300 \
--retry-connrefused \
'http://localhost:9695' > /dev/null
config:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install the Nhost CLI and run the application
uses: ./.github/actions/nhost-cli
with:
path: packages/nhost-js
start: true
config: |
services:
auth:
image: nhost/hasura-auth:0.15.0
- name: should find the injected hasura-auth version
run: |
VERSION=$(curl -sSf 'http://localhost:1337/v1/auth/version')
EXPECTED_VERSION='{"version":"v0.15.0"}'
if [ "$VERSION" != "$EXPECTED_VERSION" ]; then
echo "Expected version $EXPECTED_VERSION but got $VERSION"
exit 1
fi
version:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install the Nhost CLI
uses: ./.github/actions/nhost-cli
with:
version: v0.8.10
- name: should find the correct version
run: nhost version | head -n 1 | grep v0.8.10 || exit 1

1
.npmrc Normal file
View File

@@ -0,0 +1 @@
prefer-workspace-packages = true

View File

@@ -14,4 +14,5 @@ package.json
tsconfig.json tsconfig.json
tsconfig.*.json tsconfig.*.json
*.d.ts *.d.ts
.next .next
**/pnpm-lock.yaml

435
README.md
View File

@@ -101,14 +101,17 @@ Nhost is frontend agnostic, which means Nhost works with all frontend frameworks
## Nhost Clients ## Nhost Clients
- [JavaScript/TypeScript SDK](https://docs.nhost.io/reference/javascript) - [JavaScript/TypeScript](https://docs.nhost.io/reference/javascript)
- [Dart and Flutter SDK](https://github.com/nhost/nhost-dart) - [Dart and Flutter](https://github.com/nhost/nhost-dart)
- [Nhost React](https://docs.nhost.io/reference/react) - [React](https://docs.nhost.io/reference/react)
- [Nhost Next.js](https://docs.nhost.io/reference/nextjs) - [Next.js](https://docs.nhost.io/reference/nextjs)
- [Nhost Vue](https://docs.nhost.io/reference/vue) - [Vue](https://docs.nhost.io/reference/vue)
## Integrations ## Integrations
- [Apollo](./integrations/apollo#nhostapollo)
- [React Apollo](./integrations/react-apollo#nhostreact-apollo)
- [React URQL](./integrations/react-urql#nhostreact-urql)
- [Stripe GraphQL API](./integrations/stripe-graphql-js#nhoststripe-graphql-js) - [Stripe GraphQL API](./integrations/stripe-graphql-js#nhoststripe-graphql-js)
- [Google Translation GraphQL API](./integrations/google-translation#nhostgoogle-translation) - [Google Translation GraphQL API](./integrations/google-translation#nhostgoogle-translation)
@@ -127,8 +130,8 @@ Also, follow Nhost on [GitHub Discussions](https://github.com/nhost/nhost/discus
This repository, and most of our other open source projects, are licensed under the MIT license. This repository, and most of our other open source projects, are licensed under the MIT license.
<a href="https://runacap.com/ross-index/q1-2022/" target="_blank" rel="noopener"> <a href="https://runacap.com/ross-index/" target="_blank" rel="noopener" >
<img style="width: 260px; height: 56px" src="https://runacap.com/wp-content/uploads/2022/06/ROSS_badge_black_Q1_2022.svg" alt="ROSS Index - Fastest Growing Open-Source Startups in Q1 2022 | Runa Capital" width="260" height="56" /> <img style="width: 260px; height: 56px" src="https://runacap.com/wp-content/uploads/2022/06/ROSS_black_edition_badge.svg" alt="ROSS Index - Fastest Growing Open-Source Startups | Runa Capital" width="260" height="56" />
</a> </a>
### How to contribute ### How to contribute
@@ -141,416 +144,8 @@ Here are some ways of contributing to making Nhost better:
### Contributors ### Contributors
<!-- readme: contributors -start --> <a href="https://github.com/nhost/nhost/graphs/contributors">
<table> <p align="center">
<tr> <img width="720" src="https://contrib.rocks/image?repo=nhost/nhost" alt="A table of avatars from the project's contributors" />
<td align="center"> </p>
<a href="https://github.com/plmercereau"> </a>
<img src="https://avatars.githubusercontent.com/u/24897252?v=4" width="100;" alt="plmercereau"/>
<br />
<sub><b>Pilou</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/elitan">
<img src="https://avatars.githubusercontent.com/u/331818?v=4" width="100;" alt="elitan"/>
<br />
<sub><b>Johan Eliasson</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/szilarddoro">
<img src="https://avatars.githubusercontent.com/u/310881?v=4" width="100;" alt="szilarddoro"/>
<br />
<sub><b>Szilárd Dóró</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/nunopato">
<img src="https://avatars.githubusercontent.com/u/1523504?v=4" width="100;" alt="nunopato"/>
<br />
<sub><b>Nuno Pato</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/gdangelo">
<img src="https://avatars.githubusercontent.com/u/4352286?v=4" width="100;" alt="gdangelo"/>
<br />
<sub><b>Grégory D'Angelo</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/ejkkan">
<img src="https://avatars.githubusercontent.com/u/32518962?v=4" width="100;" alt="ejkkan"/>
<br />
<sub><b>Erik Magnusson</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/guicurcio">
<img src="https://avatars.githubusercontent.com/u/20285232?v=4" width="100;" alt="guicurcio"/>
<br />
<sub><b>Guido Curcio</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/subatuba21">
<img src="https://avatars.githubusercontent.com/u/34824571?v=4" width="100;" alt="subatuba21"/>
<br />
<sub><b>Subha Das</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/sebagudelo">
<img src="https://avatars.githubusercontent.com/u/43288271?v=4" width="100;" alt="sebagudelo"/>
<br />
<sub><b>Sebagudelo</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/mrinalwahal">
<img src="https://avatars.githubusercontent.com/u/9859731?v=4" width="100;" alt="mrinalwahal"/>
<br />
<sub><b>Mrinal Wahal</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/timpratim">
<img src="https://avatars.githubusercontent.com/u/32492961?v=4" width="100;" alt="timpratim"/>
<br />
<sub><b>Pratim</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/chrtze">
<img src="https://avatars.githubusercontent.com/u/3797215?v=4" width="100;" alt="chrtze"/>
<br />
<sub><b>Christopher Möller</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/GavanWilhite">
<img src="https://avatars.githubusercontent.com/u/2085119?v=4" width="100;" alt="GavanWilhite"/>
<br />
<sub><b>Gavan Wilhite</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/FuzzyReason">
<img src="https://avatars.githubusercontent.com/u/62517920?v=4" width="100;" alt="FuzzyReason"/>
<br />
<sub><b>Vadim Smirnov</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/macmac49">
<img src="https://avatars.githubusercontent.com/u/831190?v=4" width="100;" alt="macmac49"/>
<br />
<sub><b>Macmac49</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/subhendukundu">
<img src="https://avatars.githubusercontent.com/u/20059141?v=4" width="100;" alt="subhendukundu"/>
<br />
<sub><b>Subhendu Kundu</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/heygambo">
<img src="https://avatars.githubusercontent.com/u/449438?v=4" width="100;" alt="heygambo"/>
<br />
<sub><b>Christian Gambardella</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/dbarrosop">
<img src="https://avatars.githubusercontent.com/u/6246622?v=4" width="100;" alt="dbarrosop"/>
<br />
<sub><b>David Barroso</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/hajek-raven">
<img src="https://avatars.githubusercontent.com/u/7288737?v=4" width="100;" alt="hajek-raven"/>
<br />
<sub><b>Filip Hájek</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/MelodicCrypter">
<img src="https://avatars.githubusercontent.com/u/18341500?v=4" width="100;" alt="MelodicCrypter"/>
<br />
<sub><b>Hugh Caluscusin</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/jerryjappinen">
<img src="https://avatars.githubusercontent.com/u/1101002?v=4" width="100;" alt="jerryjappinen"/>
<br />
<sub><b>Jerry Jäppinen</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/mdp18">
<img src="https://avatars.githubusercontent.com/u/11698527?v=4" width="100;" alt="mdp18"/>
<br />
<sub><b>Max</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/mustafa-hanif">
<img src="https://avatars.githubusercontent.com/u/30019262?v=4" width="100;" alt="mustafa-hanif"/>
<br />
<sub><b>Mustafa Hanif</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/nbourdin">
<img src="https://avatars.githubusercontent.com/u/5602476?v=4" width="100;" alt="nbourdin"/>
<br />
<sub><b>Nicolas Bourdin</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/piromsurang">
<img src="https://avatars.githubusercontent.com/u/17776837?v=4" width="100;" alt="piromsurang"/>
<br />
<sub><b>Piromsurang Rungserichai</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Savinvadim1312">
<img src="https://avatars.githubusercontent.com/u/16936043?v=4" width="100;" alt="Savinvadim1312"/>
<br />
<sub><b>Savin Vadim</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Svarto">
<img src="https://avatars.githubusercontent.com/u/24279217?v=4" width="100;" alt="Svarto"/>
<br />
<sub><b>Svarto</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/muttenzer">
<img src="https://avatars.githubusercontent.com/u/49474412?v=4" width="100;" alt="muttenzer"/>
<br />
<sub><b>Muttenzer</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/alexander-mart">
<img src="https://avatars.githubusercontent.com/u/14993551?v=4" width="100;" alt="alexander-mart"/>
<br />
<sub><b>Alexander Mart</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/ahmic">
<img src="https://avatars.githubusercontent.com/u/13452362?v=4" width="100;" alt="ahmic"/>
<br />
<sub><b>Amir Ahmic</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/akd-io">
<img src="https://avatars.githubusercontent.com/u/30059155?v=4" width="100;" alt="akd-io"/>
<br />
<sub><b>Anders Kjær Damgaard</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Sonichigo">
<img src="https://avatars.githubusercontent.com/u/53110238?v=4" width="100;" alt="Sonichigo"/>
<br />
<sub><b>Animesh Pathak</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/chrisli-03">
<img src="https://avatars.githubusercontent.com/u/11177048?v=4" width="100;" alt="chrisli-03"/>
<br />
<sub><b>Chris</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/massless">
<img src="https://avatars.githubusercontent.com/u/44389?v=4" width="100;" alt="massless"/>
<br />
<sub><b>Chris Wetherell</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/rustyb">
<img src="https://avatars.githubusercontent.com/u/53086?v=4" width="100;" alt="rustyb"/>
<br />
<sub><b>Colin Broderick</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/daguitosama">
<img src="https://avatars.githubusercontent.com/u/34744883?v=4" width="100;" alt="daguitosama"/>
<br />
<sub><b>Dago</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/dminkovsky">
<img src="https://avatars.githubusercontent.com/u/218725?v=4" width="100;" alt="dminkovsky"/>
<br />
<sub><b>Dmitry Minkovsky</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/dohomi">
<img src="https://avatars.githubusercontent.com/u/489221?v=4" width="100;" alt="dohomi"/>
<br />
<sub><b>Dominic Garms</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/gaurav1999">
<img src="https://avatars.githubusercontent.com/u/20752142?v=4" width="100;" alt="gaurav1999"/>
<br />
<sub><b>Gaurav Agrawal</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/alveshelio">
<img src="https://avatars.githubusercontent.com/u/8176422?v=4" width="100;" alt="alveshelio"/>
<br />
<sub><b>Helio Alves</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/nkhdo">
<img src="https://avatars.githubusercontent.com/u/26102306?v=4" width="100;" alt="nkhdo"/>
<br />
<sub><b>Hoang Do</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/eltociear">
<img src="https://avatars.githubusercontent.com/u/22633385?v=4" width="100;" alt="eltociear"/>
<br />
<sub><b>Ikko Ashimine</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/jladuval">
<img src="https://avatars.githubusercontent.com/u/1935359?v=4" width="100;" alt="jladuval"/>
<br />
<sub><b>Jacob Duval</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/leothorp">
<img src="https://avatars.githubusercontent.com/u/12928449?v=4" width="100;" alt="leothorp"/>
<br />
<sub><b>Leo Thorp</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/LucasBois1">
<img src="https://avatars.githubusercontent.com/u/44686060?v=4" width="100;" alt="LucasBois1"/>
<br />
<sub><b>Lucas Bois</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/MarcelloTheArcane">
<img src="https://avatars.githubusercontent.com/u/21159570?v=4" width="100;" alt="MarcelloTheArcane"/>
<br />
<sub><b>Max Reynolds</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/nachoaldamav">
<img src="https://avatars.githubusercontent.com/u/22749943?v=4" width="100;" alt="nachoaldamav"/>
<br />
<sub><b>Nacho Aldama</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/ghoshnirmalya">
<img src="https://avatars.githubusercontent.com/u/6391763?v=4" width="100;" alt="ghoshnirmalya"/>
<br />
<sub><b>Nirmalya Ghosh</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/quentin-decre">
<img src="https://avatars.githubusercontent.com/u/1137511?v=4" width="100;" alt="quentin-decre"/>
<br />
<sub><b>Quentin Decré</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/elephant3">
<img src="https://avatars.githubusercontent.com/u/48279149?v=4" width="100;" alt="elephant3"/>
<br />
<sub><b>Siarhei Lipchyk</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/altschuler">
<img src="https://avatars.githubusercontent.com/u/956928?v=4" width="100;" alt="altschuler"/>
<br />
<sub><b>Simon Altschuler</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/atapas">
<img src="https://avatars.githubusercontent.com/u/3633137?v=4" width="100;" alt="atapas"/>
<br />
<sub><b>Tapas Adhikary</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/uulwake">
<img src="https://avatars.githubusercontent.com/u/22399181?v=4" width="100;" alt="uulwake"/>
<br />
<sub><b>Ulrich Wake</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/kwarabei">
<img src="https://avatars.githubusercontent.com/u/102731455?v=4" width="100;" alt="kwarabei"/>
<br />
<sub><b>Vadim</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/TheRedLancer">
<img src="https://avatars.githubusercontent.com/u/58493767?v=4" width="100;" alt="TheRedLancer"/>
<br />
<sub><b>Zach Burnaby</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/komninoschat">
<img src="https://avatars.githubusercontent.com/u/29049104?v=4" width="100;" alt="komninoschat"/>
<br />
<sub><b>Komninos</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/meesvandongen">
<img src="https://avatars.githubusercontent.com/u/35409045?v=4" width="100;" alt="meesvandongen"/>
<br />
<sub><b>Meesvandongen</b></sub>
</a>
</td></tr>
</table>
<!-- readme: contributors -end -->

View File

@@ -19,7 +19,9 @@ module.exports = {
'*.spec.ts', '*.spec.ts',
'*.spec.tsx', '*.spec.tsx',
'tests/**/*.ts', 'tests/**/*.ts',
'tests/**/*.d.ts' 'tests/**/*.d.ts',
'e2e/**/*.ts',
'e2e/**/*.d.ts'
], ],
plugins: ['@typescript-eslint', 'cypress'], plugins: ['@typescript-eslint', 'cypress'],
extends: ['plugin:cypress/recommended'], extends: ['plugin:cypress/recommended'],

View File

@@ -1,6 +1,4 @@
module.exports = { module.exports = {
'(packages|integrations)/(docgen|hasura-auth-js|hasura-storage-js|nextjs|nhost-js|react|core|vue)/src/**/*.{js,ts,jsx,tsx}':
['pnpm docgen', 'git add docs'],
'(nhost-cloud.yaml|**/nhost/config.yaml)': () => [ '(nhost-cloud.yaml|**/nhost/config.yaml)': () => [
'pnpm sync-versions', 'pnpm sync-versions',
"git add ':(glob)**/nhost/config.yaml'" "git add ':(glob)**/nhost/config.yaml'"

View File

@@ -28,22 +28,44 @@
"resolveJsonModule": true, "resolveJsonModule": true,
"esModuleInterop": true, "esModuleInterop": true,
"sourceMap": true, "sourceMap": true,
"types": ["node"], "types": [
"node"
],
"typeRoots": [ "typeRoots": [
"./node_modules/@types", "**/*/dist", "**/*/build", "**/*/.next", "**/*/umd" "./node_modules/@types",
"**/*/dist",
"**/*/build",
"**/*/.next",
"**/*/umd"
], ],
"paths": { "paths": {
"@nhost/apollo": ["../packages/apollo/src/index.ts"], "@nhost/apollo": [
"@nhost/core": ["../packages/core/src/index.ts"], "../integrations/apollo/src/index.ts"
"@nhost/docgen": ["../packages/docgen/src/index.ts"], ],
"@nhost/hasura-auth-js": ["../packages/hasura-auth-js/src/index.ts"], "@nhost/docgen": [
"@nhost/hasura-storage-js": ["../packages/hasura-storage-js/src/index.ts"], "../packages/docgen/src/index.ts"
"@nhost/nextjs": ["../packages/nextjs/src/index.ts"], ],
"@nhost/nhost-js": ["../packages/nhost-js/src/index.ts"], "@nhost/hasura-auth-js": [
"@nhost/react": ["../packages/react/src/index.ts"], "../packages/hasura-auth-js/src/index.ts"
"@nhost/react-apollo": ["../packages/react-apollo/src/index.ts"], ],
"@nhost/react-auth": ["../packages/react-auth/src/index.ts"], "@nhost/hasura-storage-js": [
"@nhost/vue": ["../packages/vue/src/index.ts"] "../packages/hasura-storage-js/src/index.ts"
],
"@nhost/nextjs": [
"../packages/nextjs/src/index.ts"
],
"@nhost/nhost-js": [
"../packages/nhost-js/src/index.ts"
],
"@nhost/react": [
"../packages/react/src/index.ts"
],
"@nhost/react-apollo": [
"../integrations/react-apollo/src/index.ts"
],
"@nhost/vue": [
"../packages/vue/src/index.ts"
]
} }
}, },
"exclude": [ "exclude": [

View File

@@ -1,3 +1,4 @@
import replace from '@rollup/plugin-replace'
import fs from 'fs' import fs from 'fs'
import path from 'path' import path from 'path'
@@ -18,7 +19,9 @@ export default defineConfig({
tsconfigPaths(), tsconfigPaths(),
dts({ dts({
exclude: ['**/*.spec.ts', '**/*.test.ts', '**/tests/**'], exclude: ['**/*.spec.ts', '**/*.test.ts', '**/tests/**'],
entryRoot: 'src' entryRoot: 'src',
// Was defaulting to true until version 1.7
skipDiagnostics: true
}) })
], ],
test: { test: {
@@ -41,6 +44,12 @@ export default defineConfig({
}, },
rollupOptions: { rollupOptions: {
external: (id) => deps.some((dep) => id.startsWith(dep)), external: (id) => deps.some((dep) => id.startsWith(dep)),
plugins: [
replace({
preventAssignment: true,
'exports.hasOwnProperty(': 'Object.prototype.hasOwnProperty.call(exports,'
})
],
output: { output: {
globals: { globals: {
graphql: 'graphql', graphql: 'graphql',

View File

@@ -6,6 +6,7 @@ module.exports = {
'@storybook/addon-links', '@storybook/addon-links',
'@storybook/addon-essentials', '@storybook/addon-essentials',
'@storybook/addon-interactions', '@storybook/addon-interactions',
'storybook-addon-next-router',
{ {
/** /**
* Fix Storybook issue with PostCSS@8 * Fix Storybook issue with PostCSS@8
@@ -38,4 +39,10 @@ module.exports = {
}, },
}; };
}, },
env: (config) => ({
...config,
NEXT_PUBLIC_ENV: 'dev',
NEXT_PUBLIC_NHOST_BACKEND_URL: 'http://localhost:1337',
NEXT_PUBLIC_NHOST_PLATFORM: 'false',
}),
}; };

View File

@@ -2,9 +2,25 @@ import '@fontsource/inter';
import '@fontsource/inter/500.css'; import '@fontsource/inter/500.css';
import '@fontsource/inter/700.css'; import '@fontsource/inter/700.css';
import { CssBaseline, ThemeProvider } from '@mui/material'; import { CssBaseline, ThemeProvider } from '@mui/material';
import { NhostApolloProvider } from '@nhost/react-apollo';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { Buffer } from 'buffer';
import { initialize, mswDecorator } from 'msw-storybook-addon';
import { RouterContext } from 'next/dist/shared/lib/router-context';
import '../src/styles/globals.css';
import defaultTheme from '../src/theme/default'; import defaultTheme from '../src/theme/default';
global.Buffer = Buffer;
initialize({ onUnhandledRequest: 'bypass' });
const queryClient = new QueryClient();
export const parameters = { export const parameters = {
nextRouter: {
Provider: RouterContext.Provider,
isReady: true,
},
actions: { argTypesRegex: '^on[A-Z].*' }, actions: { argTypesRegex: '^on[A-Z].*' },
controls: { controls: {
matchers: { matchers: {
@@ -14,11 +30,25 @@ export const parameters = {
}, },
}; };
export const withMuiTheme = (Story) => ( export const decorators = [
<ThemeProvider theme={defaultTheme}> (Story) => (
<CssBaseline /> <ThemeProvider theme={defaultTheme}>
<Story /> <CssBaseline />
</ThemeProvider> <Story />
); </ThemeProvider>
),
export const decorators = [withMuiTheme]; (Story) => (
<QueryClientProvider client={queryClient}>
<Story />
</QueryClientProvider>
),
(Story) => (
<NhostApolloProvider
fetchPolicy="cache-first"
graphqlUrl="http://localhost:1337/v1/graphql"
>
<Story />
</NhostApolloProvider>
),
mswDecorator,
];

View File

@@ -1,5 +1,80 @@
# @nhost/dashboard # @nhost/dashboard
## 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 ## 0.7.4
### Patch Changes ### Patch Changes

View File

@@ -37,6 +37,14 @@ NEXT_PUBLIC_ENV=dev
NEXT_PUBLIC_NHOST_PLATFORM=false NEXT_PUBLIC_NHOST_PLATFORM=false
``` ```
### Storybook
Components are documented using [Storybook](https://storybook.js.org/). To run Storybook, run the following command:
```bash
pnpm storybook
```
### Full list of environment variables ### Full list of environment variables
| Name | Description | | Name | Description |

View File

@@ -1,6 +1,6 @@
{ {
"name": "@nhost/dashboard", "name": "@nhost/dashboard",
"version": "0.7.4", "version": "0.7.12",
"private": true, "private": true,
"scripts": { "scripts": {
"preinstall": "npx only-allow pnpm", "preinstall": "npx only-allow pnpm",
@@ -13,7 +13,7 @@
"codegen": "graphql-codegen --config graphql.config.yaml --errors-only", "codegen": "graphql-codegen --config graphql.config.yaml --errors-only",
"nhost:dev": "nhost dev -d", "nhost:dev": "nhost dev -d",
"format": "prettier --write \"src/**/*.{js,ts,tsx,jsx,json,md}\" --plugin-search-dir=.", "format": "prettier --write \"src/**/*.{js,ts,tsx,jsx,json,md}\" --plugin-search-dir=.",
"storybook": "start-storybook -p 6006", "storybook": "start-storybook -p 6006 -s public",
"build-storybook": "build-storybook" "build-storybook": "build-storybook"
}, },
"dependencies": { "dependencies": {
@@ -25,7 +25,7 @@
"@emotion/styled": "^11.10.5", "@emotion/styled": "^11.10.5",
"@fontsource/inter": "^4.5.14", "@fontsource/inter": "^4.5.14",
"@fontsource/roboto-mono": "^4.5.8", "@fontsource/roboto-mono": "^4.5.8",
"@graphiql/react": "^0.14.0", "@graphiql/react": "^0.15.0",
"@graphiql/toolkit": "^0.8.0", "@graphiql/toolkit": "^0.8.0",
"@headlessui/react": "^1.6.5", "@headlessui/react": "^1.6.5",
"@heroicons/react": "^1.0.6", "@heroicons/react": "^1.0.6",
@@ -34,12 +34,11 @@
"@mui/material": "^5.10.14", "@mui/material": "^5.10.14",
"@mui/system": "^5.10.14", "@mui/system": "^5.10.14",
"@mui/x-date-pickers": "^5.0.8", "@mui/x-date-pickers": "^5.0.8",
"@nhost/core": "workspace:*",
"@nhost/nextjs": "workspace:*", "@nhost/nextjs": "workspace:*",
"@nhost/nhost-js": "workspace:*",
"@nhost/react": "workspace:*",
"@nhost/react-apollo": "workspace:*", "@nhost/react-apollo": "workspace:*",
"@segment/snippet": "^4.15.3", "@segment/snippet": "^4.15.3",
"@stripe/react-stripe-js": "^1.10.0",
"@stripe/stripe-js": "^1.35.0",
"@tailwindcss/forms": "^0.5.3", "@tailwindcss/forms": "^0.5.3",
"@tanstack/react-query": "^4.16.1", "@tanstack/react-query": "^4.16.1",
"@tanstack/react-table": "^8.5.30", "@tanstack/react-table": "^8.5.30",
@@ -51,21 +50,20 @@
"cross-fetch": "^3.1.5", "cross-fetch": "^3.1.5",
"date-fns": "^2.29.3", "date-fns": "^2.29.3",
"generate-password": "^1.7.0", "generate-password": "^1.7.0",
"graphiql": "^2.1.0", "graphiql": "^2.2.0",
"graphql": "^16.6.0", "graphql": "^16.6.0",
"graphql-request": "^4.3.0", "graphql-request": "^4.3.0",
"graphql-tag": "^2.12.6", "graphql-tag": "^2.12.6",
"graphql-ws": "^5.11.2", "graphql-ws": "^5.11.2",
"jsonwebtoken": "^8.5.1",
"lodash.debounce": "^4.0.8", "lodash.debounce": "^4.0.8",
"next": "^12.3.1", "next": "^12.3.1",
"next-seo": "^5.14.1", "next-seo": "^5.14.1",
"node-pg-format": "^1.3.5", "node-pg-format": "^1.3.5",
"pluralize": "^8.0.0", "pluralize": "^8.0.0",
"prettysize": "^2.0.0", "prettysize": "^2.0.0",
"randomstring": "^1.2.3",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0", "react-dom": "18.2.0",
"react-error-boundary": "^3.1.4",
"react-hook-form": "^7.39.5", "react-hook-form": "^7.39.5",
"react-hot-toast": "^2.4.0", "react-hot-toast": "^2.4.0",
"react-is": "17.0.2", "react-is": "17.0.2",
@@ -75,7 +73,6 @@
"react-table": "^7.8.0", "react-table": "^7.8.0",
"sharp": "^0.31.2", "sharp": "^0.31.2",
"slugify": "^1.6.5", "slugify": "^1.6.5",
"smartlook-client": "^6.0.0",
"stripe": "^10.17.0", "stripe": "^10.17.0",
"tailwind-merge": "^1.8.0", "tailwind-merge": "^1.8.0",
"utility-types": "^3.10.0", "utility-types": "^3.10.0",
@@ -91,24 +88,21 @@
"@graphql-codegen/typescript-operations": "^2.5.1", "@graphql-codegen/typescript-operations": "^2.5.1",
"@graphql-codegen/typescript-react-apollo": "^3.3.1", "@graphql-codegen/typescript-react-apollo": "^3.3.1",
"@next/bundle-analyzer": "^12.3.1", "@next/bundle-analyzer": "^12.3.1",
"@storybook/addon-actions": "^6.5.13", "@storybook/addon-actions": "^6.5.14",
"@storybook/addon-essentials": "^6.5.13", "@storybook/addon-essentials": "^6.5.14",
"@storybook/addon-interactions": "^6.5.13", "@storybook/addon-interactions": "^6.5.14",
"@storybook/addon-links": "^6.5.13", "@storybook/addon-links": "^6.5.14",
"@storybook/addon-postcss": "^2.0.0", "@storybook/addon-postcss": "^2.0.0",
"@storybook/builder-webpack5": "^6.5.13", "@storybook/builder-webpack5": "^6.5.14",
"@storybook/manager-webpack5": "^6.5.13", "@storybook/manager-webpack5": "^6.5.14",
"@storybook/react": "^6.5.13", "@storybook/react": "^6.5.14",
"@storybook/testing-library": "^0.0.13", "@storybook/testing-library": "^0.0.13",
"@stripe/react-stripe-js": "^1.10.0",
"@stripe/stripe-js": "^1.35.0",
"@testing-library/dom": "^8.19.0", "@testing-library/dom": "^8.19.0",
"@testing-library/jest-dom": "^5.16.5", "@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0", "@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^14.4.3", "@testing-library/user-event": "^14.4.3",
"@types/express": "^4.17.14",
"@types/lodash.debounce": "^4.0.7", "@types/lodash.debounce": "^4.0.7",
"@types/node": "^18.11.9", "@types/node": "^16.11.7",
"@types/pluralize": "^0.0.29", "@types/pluralize": "^0.0.29",
"@types/react": "18.0.25", "@types/react": "18.0.25",
"@types/react-dom": "18.0.9", "@types/react-dom": "18.0.9",
@@ -117,15 +111,12 @@
"@types/validator": "^13.7.10", "@types/validator": "^13.7.10",
"@typescript-eslint/eslint-plugin": "^5.43.0", "@typescript-eslint/eslint-plugin": "^5.43.0",
"@typescript-eslint/parser": "^5.43.0", "@typescript-eslint/parser": "^5.43.0",
"@vitejs/plugin-react": "^2.2.0", "@vitejs/plugin-react": "^3.0.0",
"@vitest/coverage-c8": "^0.25.2", "@vitest/coverage-c8": "^0.26.0",
"autoprefixer": "^10.4.13", "autoprefixer": "^10.4.13",
"babel-loader": "^8.3.0", "babel-loader": "^8.3.0",
"babel-plugin-transform-remove-console": "^6.9.4", "babel-plugin-transform-remove-console": "^6.9.4",
"concurrently": "^6.3.0",
"critters": "^0.0.16",
"csstype": "^3.0.10", "csstype": "^3.0.10",
"dotenv": "^10.0.0",
"eslint": "^8.28.0", "eslint": "^8.28.0",
"eslint-config-airbnb": "19.0.4", "eslint-config-airbnb": "19.0.4",
"eslint-config-airbnb-typescript": "^17.0.0", "eslint-config-airbnb-typescript": "^17.0.0",
@@ -135,25 +126,24 @@
"eslint-plugin-jsx-a11y": "^6.6.1", "eslint-plugin-jsx-a11y": "^6.6.1",
"eslint-plugin-react": "^7.31.11", "eslint-plugin-react": "^7.31.11",
"eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-hooks": "^4.6.0",
"express": "^4.18.2",
"express-validator": "^6.14.2",
"jsdom": "^20.0.3", "jsdom": "^20.0.3",
"lint-staged": ">=13", "lint-staged": ">=13",
"msw": "^0.49.0", "msw": "^0.49.0",
"msw-storybook-addon": "^1.6.3",
"postcss": "^8.4.19", "postcss": "^8.4.19",
"postmark": "^2.7.8",
"prettier": "^2.7.1", "prettier": "^2.7.1",
"prettier-plugin-organize-imports": "^3.2.0", "prettier-plugin-organize-imports": "^3.2.0",
"prettier-plugin-tailwindcss": "^0.1.13", "prettier-plugin-tailwindcss": "^0.2.0",
"react-date-fns-hooks": "^0.9.4", "react-date-fns-hooks": "^0.9.4",
"react-error-boundary": "^3.1.4",
"require-from-string": "^2.0.2", "require-from-string": "^2.0.2",
"storybook-addon-next-router": "^4.0.1",
"tailwindcss": "^3.1.2", "tailwindcss": "^3.1.2",
"ts-node": "^10.9.1",
"tsconfig-paths-webpack-plugin": "^4.0.0", "tsconfig-paths-webpack-plugin": "^4.0.0",
"typescript": "^4.8.4", "typescript": "^4.8.4",
"vite": "^3.2.4", "vite": "^4.0.2",
"vite-tsconfig-paths": "^3.6.0", "vite-tsconfig-paths": "^4.0.3",
"vitest": "^0.25.2", "vitest": "^0.26.2",
"webpack": "^5.75.0" "webpack": "^5.75.0"
}, },
"browserslist": { "browserslist": {
@@ -167,5 +157,8 @@
"last 1 firefox version", "last 1 firefox version",
"last 1 safari version" "last 1 safari version"
] ]
},
"msw": {
"workerDirectory": "public"
} }
} }

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -0,0 +1,303 @@
/* eslint-disable */
/* tslint:disable */
/**
* Mock Service Worker (0.49.0).
* @see https://github.com/mswjs/msw
* - Please do NOT modify this file.
* - Please do NOT serve this file on production.
*/
const INTEGRITY_CHECKSUM = '3d6b9f06410d179a7f7404d4bf4c3c70'
const activeClientIds = new Set()
self.addEventListener('install', function () {
self.skipWaiting()
})
self.addEventListener('activate', function (event) {
event.waitUntil(self.clients.claim())
})
self.addEventListener('message', async function (event) {
const clientId = event.source.id
if (!clientId || !self.clients) {
return
}
const client = await self.clients.get(clientId)
if (!client) {
return
}
const allClients = await self.clients.matchAll({
type: 'window',
})
switch (event.data) {
case 'KEEPALIVE_REQUEST': {
sendToClient(client, {
type: 'KEEPALIVE_RESPONSE',
})
break
}
case 'INTEGRITY_CHECK_REQUEST': {
sendToClient(client, {
type: 'INTEGRITY_CHECK_RESPONSE',
payload: INTEGRITY_CHECKSUM,
})
break
}
case 'MOCK_ACTIVATE': {
activeClientIds.add(clientId)
sendToClient(client, {
type: 'MOCKING_ENABLED',
payload: true,
})
break
}
case 'MOCK_DEACTIVATE': {
activeClientIds.delete(clientId)
break
}
case 'CLIENT_CLOSED': {
activeClientIds.delete(clientId)
const remainingClients = allClients.filter((client) => {
return client.id !== clientId
})
// Unregister itself when there are no more clients
if (remainingClients.length === 0) {
self.registration.unregister()
}
break
}
}
})
self.addEventListener('fetch', function (event) {
const { request } = event
const accept = request.headers.get('accept') || ''
// Bypass server-sent events.
if (accept.includes('text/event-stream')) {
return
}
// Bypass navigation requests.
if (request.mode === 'navigate') {
return
}
// Opening the DevTools triggers the "only-if-cached" request
// that cannot be handled by the worker. Bypass such requests.
if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') {
return
}
// Bypass all requests when there are no active clients.
// Prevents the self-unregistered worked from handling requests
// after it's been deleted (still remains active until the next reload).
if (activeClientIds.size === 0) {
return
}
// Generate unique request ID.
const requestId = Math.random().toString(16).slice(2)
event.respondWith(
handleRequest(event, requestId).catch((error) => {
if (error.name === 'NetworkError') {
console.warn(
'[MSW] Successfully emulated a network error for the "%s %s" request.',
request.method,
request.url,
)
return
}
// At this point, any exception indicates an issue with the original request/response.
console.error(
`\
[MSW] Caught an exception from the "%s %s" request (%s). This is probably not a problem with Mock Service Worker. There is likely an additional logging output above.`,
request.method,
request.url,
`${error.name}: ${error.message}`,
)
}),
)
})
async function handleRequest(event, requestId) {
const client = await resolveMainClient(event)
const response = await getResponse(event, client, requestId)
// Send back the response clone for the "response:*" life-cycle events.
// Ensure MSW is active and ready to handle the message, otherwise
// this message will pend indefinitely.
if (client && activeClientIds.has(client.id)) {
;(async function () {
const clonedResponse = response.clone()
sendToClient(client, {
type: 'RESPONSE',
payload: {
requestId,
type: clonedResponse.type,
ok: clonedResponse.ok,
status: clonedResponse.status,
statusText: clonedResponse.statusText,
body:
clonedResponse.body === null ? null : await clonedResponse.text(),
headers: Object.fromEntries(clonedResponse.headers.entries()),
redirected: clonedResponse.redirected,
},
})
})()
}
return response
}
// Resolve the main client for the given event.
// Client that issues a request doesn't necessarily equal the client
// that registered the worker. It's with the latter the worker should
// communicate with during the response resolving phase.
async function resolveMainClient(event) {
const client = await self.clients.get(event.clientId)
if (client?.frameType === 'top-level') {
return client
}
const allClients = await self.clients.matchAll({
type: 'window',
})
return allClients
.filter((client) => {
// Get only those clients that are currently visible.
return client.visibilityState === 'visible'
})
.find((client) => {
// Find the client ID that's recorded in the
// set of clients that have registered the worker.
return activeClientIds.has(client.id)
})
}
async function getResponse(event, client, requestId) {
const { request } = event
const clonedRequest = request.clone()
function passthrough() {
// Clone the request because it might've been already used
// (i.e. its body has been read and sent to the client).
const headers = Object.fromEntries(clonedRequest.headers.entries())
// Remove MSW-specific request headers so the bypassed requests
// comply with the server's CORS preflight check.
// Operate with the headers as an object because request "Headers"
// are immutable.
delete headers['x-msw-bypass']
return fetch(clonedRequest, { headers })
}
// Bypass mocking when the client is not active.
if (!client) {
return passthrough()
}
// Bypass initial page load requests (i.e. static assets).
// The absence of the immediate/parent client in the map of the active clients
// means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet
// and is not ready to handle requests.
if (!activeClientIds.has(client.id)) {
return passthrough()
}
// Bypass requests with the explicit bypass header.
// Such requests can be issued by "ctx.fetch()".
if (request.headers.get('x-msw-bypass') === 'true') {
return passthrough()
}
// Notify the client that a request has been intercepted.
const clientMessage = await sendToClient(client, {
type: 'REQUEST',
payload: {
id: requestId,
url: request.url,
method: request.method,
headers: Object.fromEntries(request.headers.entries()),
cache: request.cache,
mode: request.mode,
credentials: request.credentials,
destination: request.destination,
integrity: request.integrity,
redirect: request.redirect,
referrer: request.referrer,
referrerPolicy: request.referrerPolicy,
body: await request.text(),
bodyUsed: request.bodyUsed,
keepalive: request.keepalive,
},
})
switch (clientMessage.type) {
case 'MOCK_RESPONSE': {
return respondWithMock(clientMessage.data)
}
case 'MOCK_NOT_FOUND': {
return passthrough()
}
case 'NETWORK_ERROR': {
const { name, message } = clientMessage.data
const networkError = new Error(message)
networkError.name = name
// Rejecting a "respondWith" promise emulates a network error.
throw networkError
}
}
return passthrough()
}
function sendToClient(client, message) {
return new Promise((resolve, reject) => {
const channel = new MessageChannel()
channel.port1.onmessage = (event) => {
if (event.data && event.data.error) {
return reject(event.data.error)
}
resolve(event.data)
}
client.postMessage(message, [channel.port2])
})
}
function sleep(timeMs) {
return new Promise((resolve) => {
setTimeout(resolve, timeMs)
})
}
async function respondWithMock(response) {
await sleep(response.delay)
return new Response(response.body, response)
}

View File

@@ -15,7 +15,7 @@ import {
useInsertFeatureFlagMutation, useInsertFeatureFlagMutation,
useUpdateApplicationMutation, useUpdateApplicationMutation,
} from '@/utils/__generated__/graphql'; } from '@/utils/__generated__/graphql';
import { useUserEmail } from '@nhost/react'; import { useUserEmail } from '@nhost/nextjs';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
/** /**

View File

@@ -11,7 +11,7 @@ import Text from '@/ui/v2/Text';
import { discordAnnounce } from '@/utils/discordAnnounce'; import { discordAnnounce } from '@/utils/discordAnnounce';
import { triggerToast } from '@/utils/toast'; import { triggerToast } from '@/utils/toast';
import { updateOwnCache } from '@/utils/updateOwnCache'; import { updateOwnCache } from '@/utils/updateOwnCache';
import { useUserData } from '@nhost/react'; import { useUserData } from '@nhost/nextjs';
import Image from 'next/image'; import Image from 'next/image';
import { useState } from 'react'; import { useState } from 'react';
import { RemoveApplicationModal } from './RemoveApplicationModal'; import { RemoveApplicationModal } from './RemoveApplicationModal';

View File

@@ -5,7 +5,7 @@ import { Modal } from '@/ui/Modal';
import Button from '@/ui/v2/Button'; import Button from '@/ui/v2/Button';
import { Dropdown } from '@/ui/v2/Dropdown'; import { Dropdown } from '@/ui/v2/Dropdown';
import Text from '@/ui/v2/Text'; import Text from '@/ui/v2/Text';
import { useUserData } from '@nhost/react'; import { useUserData } from '@nhost/nextjs';
import Image from 'next/image'; import Image from 'next/image';
import { useState } from 'react'; import { useState } from 'react';
import ApplicationInfo from './ApplicationInfo'; import ApplicationInfo from './ApplicationInfo';

View File

@@ -1,12 +1,16 @@
import { ConnectionDetail } from '@/components/applications/ConnectionDetail'; import { ConnectionDetail } from '@/components/applications/ConnectionDetail';
import { LoadingScreen } from '@/components/common/LoadingScreen'; import { LoadingScreen } from '@/components/common/LoadingScreen';
import useIsPlatform from '@/hooks/common/useIsPlatform';
import { useCurrentWorkspaceAndApplication } from '@/hooks/useCurrentWorkspaceAndApplication'; import { useCurrentWorkspaceAndApplication } from '@/hooks/useCurrentWorkspaceAndApplication';
import Button from '@/ui/v2/Button'; import Button from '@/ui/v2/Button';
import ArrowSquareOutIcon from '@/ui/v2/icons/ArrowSquareOutIcon'; import ArrowSquareOutIcon from '@/ui/v2/icons/ArrowSquareOutIcon';
import Link from '@/ui/v2/Link'; import Link from '@/ui/v2/Link';
import Text from '@/ui/v2/Text'; import Text from '@/ui/v2/Text';
import generateAppServiceUrl, {
defaultLocalBackendSlugs,
defaultRemoteBackendSlugs,
} from '@/utils/common/generateAppServiceUrl';
import { LOCAL_HASURA_URL } from '@/utils/env'; import { LOCAL_HASURA_URL } from '@/utils/env';
import { generateRemoteAppUrl } from '@/utils/helpers';
import Image from 'next/image'; import Image from 'next/image';
interface HasuraDataProps { interface HasuraDataProps {
@@ -15,6 +19,7 @@ interface HasuraDataProps {
export function HasuraData({ close }: HasuraDataProps) { export function HasuraData({ close }: HasuraDataProps) {
const { currentApplication } = useCurrentWorkspaceAndApplication(); const { currentApplication } = useCurrentWorkspaceAndApplication();
const isPlatform = useIsPlatform();
if ( if (
!currentApplication?.subdomain || !currentApplication?.subdomain ||
@@ -24,9 +29,15 @@ export function HasuraData({ close }: HasuraDataProps) {
} }
const hasuraUrl = const hasuraUrl =
process.env.NEXT_PUBLIC_ENV === 'dev' process.env.NEXT_PUBLIC_ENV === 'dev' || !isPlatform
? LOCAL_HASURA_URL ? `${LOCAL_HASURA_URL}/console`
: generateRemoteAppUrl(currentApplication.subdomain); : generateAppServiceUrl(
currentApplication?.subdomain,
currentApplication?.region.awsName,
'hasura',
defaultLocalBackendSlugs,
{ ...defaultRemoteBackendSlugs, hasura: '/console' },
);
return ( return (
<div className="mx-auto w-full max-w-md px-6 py-4 text-left"> <div className="mx-auto w-full max-w-md px-6 py-4 text-left">
@@ -60,7 +71,7 @@ export function HasuraData({ close }: HasuraDataProps) {
<div className="mt-6 grid grid-flow-row gap-2"> <div className="mt-6 grid grid-flow-row gap-2">
<Link <Link
href={`${hasuraUrl}/console`} href={hasuraUrl}
target="_blank" target="_blank"
rel="noreferrer noopener" rel="noreferrer noopener"
className="grid grid-flow-col items-center justify-center gap-1 rounded-[4px] bg-btn p-2 text-sm+ font-medium text-white hover:ring-2 motion-safe:transition-all" className="grid grid-flow-col items-center justify-center gap-1 rounded-[4px] bg-btn p-2 text-sm+ font-medium text-white hover:ring-2 motion-safe:transition-all"

View File

@@ -37,20 +37,27 @@ function ControlledAutocomplete(
}: ControlledAutocompleteProps<AutocompleteOption>, }: ControlledAutocompleteProps<AutocompleteOption>,
ref: ForwardedRef<HTMLInputElement>, ref: ForwardedRef<HTMLInputElement>,
) { ) {
const { setValue } = useFormContext(); const form = useFormContext();
const { field } = useController({ const { field } = useController({
...controllerProps, ...(controllerProps || {}),
name: controllerProps?.name || name || '', name: controllerProps?.name || name || '',
control: controllerProps?.control || control, control: controllerProps?.control || control,
}); });
if (!form) {
throw new Error('ControlledAutocomplete must be used in a FormContext.');
}
const { setValue } = form || {};
return ( return (
<Autocomplete <Autocomplete
{...props} {...props}
{...field} {...field}
inputValue={typeof field.value === 'string' ? field.value : undefined}
ref={mergeRefs([field.ref, ref])} ref={mergeRefs([field.ref, ref])}
onChange={(event, options, reason, details) => { onChange={(event, options, reason, details) => {
setValue(controllerProps?.name || name, options); setValue?.(controllerProps?.name || name, options);
if (props.onChange) { if (props.onChange) {
props.onChange(event, options, reason, details); props.onChange(event, options, reason, details);

View File

@@ -2,11 +2,11 @@ import DataGridBody from '@/components/common/DataGridBody';
import DataGridFrame from '@/components/common/DataGridFrame'; import DataGridFrame from '@/components/common/DataGridFrame';
import type { DataGridHeaderProps } from '@/components/common/DataGridHeader'; import type { DataGridHeaderProps } from '@/components/common/DataGridHeader';
import DataGridHeader from '@/components/common/DataGridHeader'; import DataGridHeader from '@/components/common/DataGridHeader';
import DataBrowserEmptyState from '@/components/data-browser/DataBrowserEmptyState'; import DataBrowserEmptyState from '@/components/dataBrowser/DataBrowserEmptyState';
import { DataGridProvider } from '@/context/DataGridContext'; import { DataGridProvider } from '@/context/DataGridContext';
import type { UseDataGridOptions } from '@/hooks/useDataGrid'; import type { UseDataGridOptions } from '@/hooks/useDataGrid';
import useDataGrid from '@/hooks/useDataGrid'; import useDataGrid from '@/hooks/useDataGrid';
import type { DataBrowserGridColumn } from '@/types/data-browser'; import type { DataBrowserGridColumn } from '@/types/dataBrowser';
import ActivityIndicator from '@/ui/v2/ActivityIndicator'; import ActivityIndicator from '@/ui/v2/ActivityIndicator';
import type { ForwardedRef } from 'react'; import type { ForwardedRef } from 'react';
import { forwardRef, useEffect, useRef } from 'react'; import { forwardRef, useEffect, useRef } from 'react';

View File

@@ -1,7 +1,7 @@
import type { DataGridProps } from '@/components/common/DataGrid'; import type { DataGridProps } from '@/components/common/DataGrid';
import DataGridCell from '@/components/common/DataGridCell'; import DataGridCell from '@/components/common/DataGridCell';
import useDataGridConfig from '@/hooks/useDataGridConfig'; import useDataGridConfig from '@/hooks/useDataGridConfig';
import type { DataBrowserGridColumn } from '@/types/data-browser'; import type { DataBrowserGridColumn } from '@/types/dataBrowser';
import Button from '@/ui/v2/Button'; import Button from '@/ui/v2/Button';
import PlusIcon from '@/ui/v2/icons/PlusIcon'; import PlusIcon from '@/ui/v2/icons/PlusIcon';
import type { DetailedHTMLProps, HTMLProps, KeyboardEvent } from 'react'; import type { DetailedHTMLProps, HTMLProps, KeyboardEvent } from 'react';

View File

@@ -3,7 +3,7 @@ import type {
ColumnType, ColumnType,
DataBrowserGridCell, DataBrowserGridCell,
DataBrowserGridCellProps, DataBrowserGridCellProps,
} from '@/types/data-browser'; } from '@/types/dataBrowser';
import Tooltip, { useTooltip } from '@/ui/v2/Tooltip'; import Tooltip, { useTooltip } from '@/ui/v2/Tooltip';
import { triggerToast } from '@/utils/toast'; import { triggerToast } from '@/utils/toast';
import type { import type {

View File

@@ -1,6 +1,6 @@
import type { DataGridProps } from '@/components/common/DataGrid'; import type { DataGridProps } from '@/components/common/DataGrid';
import useDataGridConfig from '@/hooks/useDataGridConfig'; import useDataGridConfig from '@/hooks/useDataGridConfig';
import type { DataBrowserGridColumn } from '@/types/data-browser'; import type { DataBrowserGridColumn } from '@/types/dataBrowser';
import Button from '@/ui/v2/Button'; import Button from '@/ui/v2/Button';
import Divider from '@/ui/v2/Divider'; import Divider from '@/ui/v2/Divider';
import { Dropdown } from '@/ui/v2/Dropdown'; import { Dropdown } from '@/ui/v2/Dropdown';

View File

@@ -1,6 +1,6 @@
import RetryableErrorBoundary from '@/components/common/RetryableErrorBoundary'; import RetryableErrorBoundary from '@/components/common/RetryableErrorBoundary';
import CreateForeignKeyForm from '@/components/data-browser/CreateForeignKeyForm'; import CreateForeignKeyForm from '@/components/dataBrowser/CreateForeignKeyForm';
import EditForeignKeyForm from '@/components/data-browser/EditForeignKeyForm'; import EditForeignKeyForm from '@/components/dataBrowser/EditForeignKeyForm';
import CreateEnvironmentVariableForm from '@/components/settings/environmentVariables/CreateEnvironmentVariableForm'; import CreateEnvironmentVariableForm from '@/components/settings/environmentVariables/CreateEnvironmentVariableForm';
import EditEnvironmentVariableForm from '@/components/settings/environmentVariables/EditEnvironmentVariableForm'; import EditEnvironmentVariableForm from '@/components/settings/environmentVariables/EditEnvironmentVariableForm';
import CreatePermissionVariableForm from '@/components/settings/permissions/CreatePermissionVariableForm'; import CreatePermissionVariableForm from '@/components/settings/permissions/CreatePermissionVariableForm';
@@ -12,13 +12,21 @@ import AlertDialog from '@/ui/v2/AlertDialog';
import { BaseDialog } from '@/ui/v2/Dialog'; import { BaseDialog } from '@/ui/v2/Dialog';
import Drawer from '@/ui/v2/Drawer'; import Drawer from '@/ui/v2/Drawer';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import { useRouter } from 'next/router';
import type { import type {
BaseSyntheticEvent, BaseSyntheticEvent,
DetailedHTMLProps, DetailedHTMLProps,
HTMLProps, HTMLProps,
PropsWithChildren, PropsWithChildren,
} from 'react'; } from 'react';
import { useCallback, useMemo, useReducer, useRef, useState } from 'react'; import {
useCallback,
useEffect,
useMemo,
useReducer,
useRef,
useState,
} from 'react';
import { twMerge } from 'tailwind-merge'; import { twMerge } from 'tailwind-merge';
import type { DialogConfig, DialogType } from './DialogContext'; import type { DialogConfig, DialogType } from './DialogContext';
import DialogContext from './DialogContext'; import DialogContext from './DialogContext';
@@ -49,31 +57,33 @@ function LoadingComponent({
} }
const CreateRecordForm = dynamic( const CreateRecordForm = dynamic(
() => import('@/components/data-browser/CreateRecordForm'), () => import('@/components/dataBrowser/CreateRecordForm'),
{ ssr: false, loading: () => LoadingComponent() }, { ssr: false, loading: () => LoadingComponent() },
); );
const CreateColumnForm = dynamic( const CreateColumnForm = dynamic(
() => import('@/components/data-browser/CreateColumnForm'), () => import('@/components/dataBrowser/CreateColumnForm'),
{ ssr: false, loading: () => LoadingComponent() }, { ssr: false, loading: () => LoadingComponent() },
); );
const EditColumnForm = dynamic( const EditColumnForm = dynamic(
() => import('@/components/data-browser/EditColumnForm'), () => import('@/components/dataBrowser/EditColumnForm'),
{ ssr: false, loading: () => LoadingComponent() }, { ssr: false, loading: () => LoadingComponent() },
); );
const CreateTableForm = dynamic( const CreateTableForm = dynamic(
() => import('@/components/data-browser/CreateTableForm'), () => import('@/components/dataBrowser/CreateTableForm'),
{ ssr: false, loading: () => LoadingComponent() }, { ssr: false, loading: () => LoadingComponent() },
); );
const EditTableForm = dynamic( const EditTableForm = dynamic(
() => import('@/components/data-browser/EditTableForm'), () => import('@/components/dataBrowser/EditTableForm'),
{ ssr: false, loading: () => LoadingComponent() }, { ssr: false, loading: () => LoadingComponent() },
); );
function DialogProvider({ children }: PropsWithChildren<unknown>) { function DialogProvider({ children }: PropsWithChildren<unknown>) {
const router = useRouter();
const [ const [
{ {
open: dialogOpen, open: dialogOpen,
@@ -161,26 +171,29 @@ function DialogProvider({ children }: PropsWithChildren<unknown>) {
alertDialogDispatch({ type: 'CLEAR_ALERT_CONTENT' }); alertDialogDispatch({ type: 'CLEAR_ALERT_CONTENT' });
} }
function openDirtyConfirmation(config?: Partial<DialogConfig<string>>) { const openDirtyConfirmation = useCallback(
const { props, ...restConfig } = config || {}; (config?: Partial<DialogConfig<string>>) => {
const { props, ...restConfig } = config || {};
openAlertDialog({ setShowDirtyConfirmation(true);
...config, openAlertDialog({
title: 'Unsaved changes', ...config,
payload: title: 'Unsaved changes',
'You have unsaved local changes. Are you sure you want to discard them?', payload:
props: { 'You have unsaved local changes. Are you sure you want to discard them?',
...props, props: {
primaryButtonText: 'Discard', ...props,
primaryButtonColor: 'error', primaryButtonText: 'Discard',
}, primaryButtonColor: 'error',
...restConfig, },
}); ...restConfig,
} });
},
[],
);
function closeDrawerWithDirtyGuard(event?: BaseSyntheticEvent) { function closeDrawerWithDirtyGuard(event?: BaseSyntheticEvent) {
if (isDrawerDirty.current && event?.type !== 'submit') { if (isDrawerDirty.current && event?.type !== 'submit') {
setShowDirtyConfirmation(true);
openDirtyConfirmation({ props: { onPrimaryAction: closeDrawer } }); openDirtyConfirmation({ props: { onPrimaryAction: closeDrawer } });
return; return;
} }
@@ -190,7 +203,6 @@ function DialogProvider({ children }: PropsWithChildren<unknown>) {
function closeDialogWithDirtyGuard(event?: BaseSyntheticEvent) { function closeDialogWithDirtyGuard(event?: BaseSyntheticEvent) {
if (isDialogDirty.current && event?.type !== 'submit') { if (isDialogDirty.current && event?.type !== 'submit') {
setShowDirtyConfirmation(true);
openDirtyConfirmation({ props: { onPrimaryAction: closeDialog } }); openDirtyConfirmation({ props: { onPrimaryAction: closeDialog } });
return; return;
} }
@@ -248,6 +260,32 @@ function DialogProvider({ children }: PropsWithChildren<unknown>) {
onCancel: closeDrawerWithDirtyGuard, onCancel: closeDrawerWithDirtyGuard,
}; };
useEffect(() => {
function handleCloseDrawerAndDialog() {
if (isDrawerDirty.current || isDialogDirty.current) {
openDirtyConfirmation({
props: {
onPrimaryAction: () => {
closeDialog();
closeDrawer();
},
},
});
throw new Error('Unsaved changes');
}
closeDrawer();
closeDialog();
}
router?.events?.on?.('routeChangeStart', handleCloseDrawerAndDialog);
return () => {
router?.events?.off?.('routeChangeStart', handleCloseDrawerAndDialog);
};
}, [closeDialog, closeDrawer, openDirtyConfirmation, router.events]);
return ( return (
<DialogContext.Provider value={contextValue}> <DialogContext.Provider value={contextValue}>
<AlertDialog <AlertDialog

View File

@@ -0,0 +1,48 @@
import type { ComponentMeta, ComponentStory } from '@storybook/react';
import type { PropsWithoutRef } from 'react';
import type { ReadOnlyToggleProps } from './ReadOnlyToggle';
import ReadOnlyToggle from './ReadOnlyToggle';
export default {
title: 'Common Components / ReadOnlyToggle',
component: ReadOnlyToggle,
argTypes: {
checked: {
options: [null, true, false],
control: { type: 'radio' },
},
},
} as ComponentMeta<typeof ReadOnlyToggle>;
const Template: ComponentStory<typeof ReadOnlyToggle> = function Template(
args: PropsWithoutRef<ReadOnlyToggleProps>,
) {
return <ReadOnlyToggle {...args} />;
};
export const Null = Template.bind({});
Null.args = {
checked: null,
};
export const True = Template.bind({});
True.args = {
checked: true,
};
export const False = Template.bind({});
False.args = {
checked: false,
};
export const CustomClasses = Template.bind({});
CustomClasses.args = {
checked: true,
className: '!bg-red',
slotProps: {
label: {
className: '!text-sm !text-white',
},
},
};

View File

@@ -1,39 +1,79 @@
import type { ForwardedRef } from 'react'; import type { TextProps } from '@/ui/v2/Text';
import Text from '@/ui/v2/Text';
import type { DetailedHTMLProps, ForwardedRef, HTMLProps } from 'react';
import { forwardRef } from 'react'; import { forwardRef } from 'react';
import { twMerge } from 'tailwind-merge'; import { twMerge } from 'tailwind-merge';
const ReadOnlyToggle = forwardRef( export interface ReadOnlyToggleProps
( extends DetailedHTMLProps<HTMLProps<HTMLSpanElement>, HTMLSpanElement> {
{ checked }: { checked: boolean | null }, /**
ref: ForwardedRef<HTMLSpanElement>, * Determines whether the toggle is checked or not.
) => ( */
checked?: boolean | null;
/**
* Props passed to specific component slots.
*/
slotProps?: {
/**
* Props passed to the root `<span />` element.
*/
root?: DetailedHTMLProps<HTMLProps<HTMLSpanElement>, HTMLSpanElement>;
/**
* Props passed to the label.
*/
label?: TextProps;
};
}
function ReadOnlyToggle(
{ checked, className, slotProps = {}, ...props }: ReadOnlyToggleProps,
ref: ForwardedRef<HTMLSpanElement>,
) {
return (
<span <span
className="inline-grid h-full w-full grid-flow-col items-center justify-start gap-1.5" {...props}
{...(slotProps?.root || {})}
className={twMerge(
'inline-grid h-full w-full grid-flow-col items-center justify-start gap-1.5',
slotProps?.root?.className,
className,
)}
ref={ref} ref={ref}
> >
<span <span
className={twMerge( className={twMerge(
'box-border inline-grid h-3 w-5 items-center rounded-full px-0.5', 'box-border inline-grid h-3 w-5 items-center rounded-full px-0.5',
checked === true && 'justify-end bg-greyscaleDark', checked === true &&
'border-1 border-transparent justify-end bg-greyscaleDark',
checked === false && 'border-1 border-greyscaleDark', checked === false && 'border-1 border-greyscaleDark',
checked === null && 'border-1 border-greyscaleDark', checked === null && 'border-1 border-greyscaleDark',
)} )}
> >
<span <span
className={twMerge( className={twMerge(
'inline rounded-full', 'inline-block rounded-full',
checked === true && 'h-2 w-2 bg-white', checked === true && 'h-2 w-2 bg-white',
checked === false && 'h-2 w-2 bg-greyscaleDark', checked === false && 'h-2 w-2 bg-greyscaleDark',
checked === null && 'h-px w-2 justify-self-center bg-greyscaleDark', checked === null &&
'h-px my-px w-2 justify-self-center bg-greyscaleDark',
)} )}
/> />
</span> </span>
<span className="truncate text-xs font-normal">{String(checked)}</span> <Text
{...(slotProps?.label || {})}
component="span"
className={twMerge(
'truncate !text-xs font-normal',
slotProps?.label?.className,
)}
>
{String(checked)}
</Text>
</span> </span>
), );
); }
ReadOnlyToggle.displayName = 'NhostReadOnlyToggle'; ReadOnlyToggle.displayName = 'NhostReadOnlyToggle';
export default ReadOnlyToggle; export default forwardRef(ReadOnlyToggle);

View File

@@ -3,7 +3,7 @@ import ControlledCheckbox from '@/components/common/ControlledCheckbox';
import { useDialog } from '@/components/common/DialogProvider'; import { useDialog } from '@/components/common/DialogProvider';
import Form from '@/components/common/Form'; import Form from '@/components/common/Form';
import InlineCode from '@/components/common/InlineCode'; import InlineCode from '@/components/common/InlineCode';
import type { ColumnType, DatabaseColumn } from '@/types/data-browser'; import type { ColumnType, DatabaseColumn } from '@/types/dataBrowser';
import Button from '@/ui/v2/Button'; import Button from '@/ui/v2/Button';
import Input from '@/ui/v2/Input'; import Input from '@/ui/v2/Input';
import { OptionBase } from '@/ui/v2/Option'; import { OptionBase } from '@/ui/v2/Option';

View File

@@ -1,6 +1,6 @@
import { useDialog } from '@/components/common/DialogProvider'; import { useDialog } from '@/components/common/DialogProvider';
import type { BaseForeignKeyFormValues } from '@/components/data-browser/BaseForeignKeyForm'; import type { BaseForeignKeyFormValues } from '@/components/dataBrowser/BaseForeignKeyForm';
import type { DatabaseColumn } from '@/types/data-browser'; import type { DatabaseColumn } from '@/types/dataBrowser';
import Button from '@/ui/v2/Button'; import Button from '@/ui/v2/Button';
import ArrowRightIcon from '@/ui/v2/icons/ArrowRightIcon'; import ArrowRightIcon from '@/ui/v2/icons/ArrowRightIcon';
import LinkIcon from '@/ui/v2/icons/LinkIcon'; import LinkIcon from '@/ui/v2/icons/LinkIcon';

View File

@@ -2,7 +2,7 @@ import ControlledSelect from '@/components/common/ControlledSelect';
import { useDialog } from '@/components/common/DialogProvider'; import { useDialog } from '@/components/common/DialogProvider';
import Form from '@/components/common/Form'; import Form from '@/components/common/Form';
import useDatabaseQuery from '@/hooks/dataBrowser/useDatabaseQuery'; import useDatabaseQuery from '@/hooks/dataBrowser/useDatabaseQuery';
import type { DatabaseColumn, ForeignKeyRelation } from '@/types/data-browser'; import type { DatabaseColumn, ForeignKeyRelation } from '@/types/dataBrowser';
import Button from '@/ui/v2/Button'; import Button from '@/ui/v2/Button';
import Divider from '@/ui/v2/Divider'; import Divider from '@/ui/v2/Divider';
import Option from '@/ui/v2/Option'; import Option from '@/ui/v2/Option';

View File

@@ -1,6 +1,6 @@
import type { ControlledSelectProps } from '@/components/common/ControlledSelect'; import type { ControlledSelectProps } from '@/components/common/ControlledSelect';
import ControlledSelect from '@/components/common/ControlledSelect'; import ControlledSelect from '@/components/common/ControlledSelect';
import type { NormalizedQueryDataRow } from '@/types/data-browser'; import type { NormalizedQueryDataRow } from '@/types/dataBrowser';
import Option from '@/ui/v2/Option'; import Option from '@/ui/v2/Option';
import type { ForwardedRef, PropsWithoutRef } from 'react'; import type { ForwardedRef, PropsWithoutRef } from 'react';
import { forwardRef } from 'react'; import { forwardRef } from 'react';

View File

@@ -1,5 +1,5 @@
import ControlledSelect from '@/components/common/ControlledSelect'; import ControlledSelect from '@/components/common/ControlledSelect';
import type { NormalizedQueryDataRow } from '@/types/data-browser'; import type { NormalizedQueryDataRow } from '@/types/dataBrowser';
import Option from '@/ui/v2/Option'; import Option from '@/ui/v2/Option';
import { useFormContext, useFormState, useWatch } from 'react-hook-form'; import { useFormContext, useFormState, useWatch } from 'react-hook-form';
import type { BaseForeignKeyFormValues } from './BaseForeignKeyForm'; import type { BaseForeignKeyFormValues } from './BaseForeignKeyForm';

View File

@@ -1,10 +1,10 @@
import { useDialog } from '@/components/common/DialogProvider'; import { useDialog } from '@/components/common/DialogProvider';
import Form from '@/components/common/Form'; import Form from '@/components/common/Form';
import DatabaseRecordInputGroup from '@/components/data-browser/DatabaseRecordInputGroup'; import DatabaseRecordInputGroup from '@/components/dataBrowser/DatabaseRecordInputGroup';
import type { import type {
ColumnInsertOptions, ColumnInsertOptions,
DataBrowserGridColumn, DataBrowserGridColumn,
} from '@/types/data-browser'; } from '@/types/dataBrowser';
import Button from '@/ui/v2/Button'; import Button from '@/ui/v2/Button';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useFormContext } from 'react-hook-form'; import { useFormContext } from 'react-hook-form';

View File

@@ -1,7 +1,7 @@
import { useDialog } from '@/components/common/DialogProvider'; import { useDialog } from '@/components/common/DialogProvider';
import Form from '@/components/common/Form'; import Form from '@/components/common/Form';
import { baseColumnValidationSchema } from '@/components/data-browser/BaseColumnForm'; import { baseColumnValidationSchema } from '@/components/dataBrowser/BaseColumnForm';
import type { DatabaseTable, ForeignKeyRelation } from '@/types/data-browser'; import type { DatabaseTable, ForeignKeyRelation } from '@/types/dataBrowser';
import Button from '@/ui/v2/Button'; import Button from '@/ui/v2/Button';
import Input from '@/ui/v2/Input'; import Input from '@/ui/v2/Input';
import { useEffect } from 'react'; import { useEffect } from 'react';

View File

@@ -1,7 +1,7 @@
import ControlledAutocomplete from '@/components/common/ControlledAutocomplete'; import ControlledAutocomplete from '@/components/common/ControlledAutocomplete';
import ControlledCheckbox from '@/components/common/ControlledCheckbox'; import ControlledCheckbox from '@/components/common/ControlledCheckbox';
import InlineCode from '@/components/common/InlineCode'; import InlineCode from '@/components/common/InlineCode';
import type { ColumnType, ForeignKeyRelation } from '@/types/data-browser'; import type { ColumnType, ForeignKeyRelation } from '@/types/dataBrowser';
import type { ButtonProps } from '@/ui/v2/Button'; import type { ButtonProps } from '@/ui/v2/Button';
import type { CheckboxProps } from '@/ui/v2/Checkbox'; import type { CheckboxProps } from '@/ui/v2/Checkbox';
import IconButton from '@/ui/v2/IconButton'; import IconButton from '@/ui/v2/IconButton';

View File

@@ -1,4 +1,4 @@
import type { ForeignKeyRelation } from '@/types/data-browser'; import type { ForeignKeyRelation } from '@/types/dataBrowser';
import Button from '@/ui/v2/Button'; import Button from '@/ui/v2/Button';
import ArrowRightIcon from '@/ui/v2/icons/ArrowRightIcon'; import ArrowRightIcon from '@/ui/v2/icons/ArrowRightIcon';
import LinkIcon from '@/ui/v2/icons/LinkIcon'; import LinkIcon from '@/ui/v2/icons/LinkIcon';

View File

@@ -1,6 +1,6 @@
import { useDialog } from '@/components/common/DialogProvider'; import { useDialog } from '@/components/common/DialogProvider';
import type { BaseForeignKeyFormValues } from '@/components/data-browser/BaseForeignKeyForm'; import type { BaseForeignKeyFormValues } from '@/components/dataBrowser/BaseForeignKeyForm';
import type { DatabaseColumn, ForeignKeyRelation } from '@/types/data-browser'; import type { DatabaseColumn, ForeignKeyRelation } from '@/types/dataBrowser';
import Button from '@/ui/v2/Button'; import Button from '@/ui/v2/Button';
import PlusIcon from '@/ui/v2/icons/PlusIcon'; import PlusIcon from '@/ui/v2/icons/PlusIcon';
import InputLabel from '@/ui/v2/InputLabel'; import InputLabel from '@/ui/v2/InputLabel';

View File

@@ -1,5 +1,5 @@
import ControlledSelect from '@/components/common/ControlledSelect'; import ControlledSelect from '@/components/common/ControlledSelect';
import type { ColumnType, DatabaseColumn } from '@/types/data-browser'; import type { ColumnType, DatabaseColumn } from '@/types/dataBrowser';
import Option from '@/ui/v2/Option'; import Option from '@/ui/v2/Option';
import { identityTypes } from '@/utils/dataBrowser/postgresqlConstants'; import { identityTypes } from '@/utils/dataBrowser/postgresqlConstants';
import { useMemo } from 'react'; import { useMemo } from 'react';

View File

@@ -1,5 +1,5 @@
import ControlledSelect from '@/components/common/ControlledSelect'; import ControlledSelect from '@/components/common/ControlledSelect';
import type { DatabaseColumn } from '@/types/data-browser'; import type { DatabaseColumn } from '@/types/dataBrowser';
import Option from '@/ui/v2/Option'; import Option from '@/ui/v2/Option';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { useFormState, useWatch } from 'react-hook-form'; import { useFormState, useWatch } from 'react-hook-form';

View File

@@ -0,0 +1,129 @@
import Form from '@/components/common/Form';
import Button from '@/ui/v2/Button';
import Text from '@/ui/v2/Text';
import hasuraMetadataQuery from '@/utils/msw/mocks/rest/hasuraMetadataQuery';
import tableQuery from '@/utils/msw/mocks/rest/tableQuery';
import type { ComponentMeta, ComponentStory } from '@storybook/react';
import { useState } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import type { ColumnAutocompleteProps } from './ColumnAutocomplete';
import ColumnAutocomplete from './ColumnAutocomplete';
export default {
title: 'Data Browser / ColumnAutocomplete',
component: ColumnAutocomplete,
parameters: {
docs: {
source: {
type: 'code',
},
},
},
} as ComponentMeta<typeof ColumnAutocomplete>;
const defaultParameters = {
nextRouter: {
path: '/[workspaceSlug]/[appSlug]/database/browser/[dataSourceSlug]/[schemaSlug]/[tableSlug]',
asPath: '/workspace/app/database/browser/default/public/users',
query: {
workspaceSlug: 'workspace',
appSlug: 'app',
dataSourceSlug: 'default',
schemaSlug: 'public',
tableSlug: 'books',
},
},
msw: {
handlers: [tableQuery, hasuraMetadataQuery],
},
};
const Template: ComponentStory<typeof ColumnAutocomplete> = function Template(
args: ColumnAutocompleteProps,
) {
const [submittedValues, setSubmittedValues] = useState<string>('');
const form = useForm<{ firstReference: string; secondReference: string }>({
defaultValues: {
firstReference: null,
secondReference: null,
},
});
function handleSubmit(values: {
firstReference: string;
secondReference: string;
}) {
setSubmittedValues(JSON.stringify(values, null, 2));
}
return (
<div className="grid grid-flow-row gap-2">
<FormProvider {...form}>
<Form onSubmit={handleSubmit} className="grid grid-flow-row gap-2">
<ColumnAutocomplete
{...args}
name="firstReference"
label="First Reference"
onChange={(_event, newValue) =>
form.setValue('firstReference', newValue.value, {
shouldDirty: true,
})
}
onInitialized={(newValue) => {
form.setValue('firstReference', newValue.value, {
shouldDirty: true,
});
}}
/>
<ColumnAutocomplete
{...args}
name="secondReference"
label="Second Reference"
onChange={(_event, newValue) =>
form.setValue('secondReference', newValue.value, {
shouldDirty: true,
})
}
onInitialized={(newValue) => {
form.setValue('secondReference', newValue.value, {
shouldDirty: true,
});
}}
/>
<Button type="submit" className="justify-self-start">
Submit
</Button>
</Form>
</FormProvider>
<Text component="pre" className="!font-mono !text-gray-700">
{submittedValues || 'The form has not been submitted yet.'}
</Text>
</div>
);
};
export const Basic = Template.bind({});
Basic.args = {
schema: 'public',
table: 'books',
};
Basic.parameters = defaultParameters;
export const DefaultValue = Template.bind({});
DefaultValue.args = {
schema: 'public',
table: 'books',
value: 'author.id',
};
DefaultValue.parameters = defaultParameters;
export const DisabledRelationships = Template.bind({});
DisabledRelationships.args = {
schema: 'public',
table: 'books',
disableRelationships: true,
};
DisabledRelationships.parameters = defaultParameters;

View File

@@ -0,0 +1,33 @@
import customClaimsQuery from '@/utils/msw/mocks/graphql/customClaimsQuery';
import hasuraMetadataQuery from '@/utils/msw/mocks/rest/hasuraMetadataQuery';
import tableQuery from '@/utils/msw/mocks/rest/tableQuery';
import { render, screen } from '@/utils/testUtils';
import { setupServer } from 'msw/node';
import { test, vi } from 'vitest';
import ColumnAutocomplete from './ColumnAutocomplete';
const server = setupServer(tableQuery, hasuraMetadataQuery, customClaimsQuery);
beforeAll(() => server.listen({ onUnhandledRequest: 'warn' }));
afterEach(() => server.resetHandlers());
afterAll(() => {
server.close();
vi.restoreAllMocks();
});
test('should render a combobox', () => {
render(
<ColumnAutocomplete
schema="public"
table="books"
label="Column Autocomplete"
/>,
);
expect(
screen.getByRole('combobox', { name: /column autocomplete/i }),
).toBeInTheDocument();
});
// Note: Network requests don't go through in tests, so we can't test the
// autocomplete functionality for now.

View File

@@ -0,0 +1,382 @@
import InlineCode from '@/components/common/InlineCode';
import useMetadataQuery from '@/hooks/dataBrowser/useMetadataQuery';
import useTableQuery from '@/hooks/dataBrowser/useTableQuery';
import ActivityIndicator from '@/ui/v2/ActivityIndicator';
import type { AutocompleteOption } from '@/ui/v2/Autocomplete';
import { AutocompletePopper } from '@/ui/v2/Autocomplete';
import IconButton from '@/ui/v2/IconButton';
import ArrowLeftIcon from '@/ui/v2/icons/ArrowLeftIcon';
import type { InputProps } from '@/ui/v2/Input';
import Input from '@/ui/v2/Input';
import List from '@/ui/v2/List';
import { OptionBase } from '@/ui/v2/Option';
import { OptionGroupBase } from '@/ui/v2/OptionGroup';
import Text from '@/ui/v2/Text';
import getTruncatedText from '@/utils/common/getTruncatedText';
import type { AutocompleteGroupedOption } from '@mui/base/AutocompleteUnstyled';
import { useAutocomplete } from '@mui/base/AutocompleteUnstyled';
import type { AutocompleteRenderGroupParams } from '@mui/material/Autocomplete';
import { autocompleteClasses } from '@mui/material/Autocomplete';
import type {
ForwardedRef,
HTMLAttributes,
PropsWithoutRef,
SyntheticEvent,
} from 'react';
import { forwardRef, useEffect, useState } from 'react';
import { twMerge } from 'tailwind-merge';
import type { UseAsyncInitialValueOptions } from './useAsyncInitialValue';
import useAsyncInitialValue from './useAsyncInitialValue';
import type { UseColumnGroupsOptions } from './useColumnGroups';
import useColumnGroups from './useColumnGroups';
export interface ColumnAutocompleteProps
extends Omit<PropsWithoutRef<InputProps>, 'onChange'> {
/**
* Schema where the `table` is located.
*/
schema: string;
/**
* Table to get the columns from.
*/
table: string;
/**
* Function to be called when the value changes.
*/
onChange?: (
event: SyntheticEvent,
value: {
value: string;
columnMetadata?: Record<string, any>;
disableReset?: boolean;
},
) => void;
/**
* Function to be called when the input is asynchronously initialized.
*/
onInitialized?: UseAsyncInitialValueOptions['onInitialized'];
/**
* Class name to be applied to the root element.
*/
rootClassName?: string;
/**
* Determines if the autocomplete should allow relationships.
*/
disableRelationships?: UseColumnGroupsOptions['disableRelationships'];
}
function renderGroup(params: AutocompleteRenderGroupParams) {
return (
<li key={params.key}>
<OptionGroupBase>{params.group}</OptionGroupBase>
<List>{params.children}</List>
</li>
);
}
function renderOption(
option: AutocompleteOption<string>,
optionProps: HTMLAttributes<HTMLLIElement>,
) {
return (
<OptionBase
{...optionProps}
className="grid grid-flow-col items-baseline justify-start justify-items-start gap-1.5"
>
<span>{option.label}</span>
{option.group === 'columns' && (
<InlineCode>{option.metadata?.type || option.value}</InlineCode>
)}
</OptionBase>
);
}
function ColumnAutocomplete(
{
rootClassName,
schema: defaultSchema,
table: defaultTable,
value: externalValue,
disableRelationships,
onChange,
onInitialized,
...props
}: ColumnAutocompleteProps,
ref: ForwardedRef<HTMLInputElement>,
) {
const [open, setOpen] = useState(false);
const [activeRelationship, setActiveRelationship] = useState<any>();
const selectedSchema = activeRelationship?.schema || defaultSchema;
const selectedTable = activeRelationship?.table || defaultTable;
const {
data: tableData,
status: tableStatus,
error: tableError,
isFetching: isTableFetching,
} = useTableQuery([`default.${selectedSchema}.${selectedTable}`], {
schema: selectedSchema,
table: selectedTable,
queryOptions: { refetchOnWindowFocus: false },
});
const {
data: metadata,
status: metadataStatus,
error: metadataError,
isFetching: isMetadataFetching,
} = useMetadataQuery([`default.metadata`], {
queryOptions: { refetchOnWindowFocus: false },
});
const {
initialized,
inputValue,
setInputValue,
selectedColumn,
setSelectedColumn,
selectedRelationships,
setSelectedRelationships,
relationshipDotNotation,
activeRelationship: asyncActiveRelationship,
} = useAsyncInitialValue({
selectedSchema,
selectedTable,
initialValue: externalValue as string,
isTableLoading: tableStatus === 'loading' || isTableFetching,
isMetadataLoading: metadataStatus === 'loading' || isMetadataFetching,
tableData,
metadata,
onInitialized,
});
useEffect(() => {
setActiveRelationship(asyncActiveRelationship);
}, [asyncActiveRelationship]);
function isOptionEqualToValue(
option: AutocompleteOption,
value: NonNullable<string | AutocompleteOption>,
) {
if (!value) {
return false;
}
if (typeof value === 'string') {
return option.value === value;
}
return option.value === value.value && option.custom === value.custom;
}
function handleChange(
event: SyntheticEvent,
value: NonNullable<string | AutocompleteOption>,
) {
if (typeof value === 'string' || Array.isArray(value) || !value) {
return;
}
if ('group' in value && value.group === 'columns') {
setSelectedColumn(value);
setOpen(false);
setInputValue(value.value);
onChange?.(event, {
value:
selectedRelationships.length > 0
? [relationshipDotNotation, value.value].join('.')
: value.value,
columnMetadata: value.metadata,
});
return;
}
setInputValue('');
setSelectedColumn(null);
setSelectedRelationships((currentRelationships) => [
...currentRelationships,
value.metadata?.target,
]);
}
const options = useColumnGroups({
selectedSchema,
selectedTable,
tableData,
metadata,
disableRelationships,
});
const {
popupOpen,
anchorEl,
setAnchorEl,
getRootProps,
getInputLabelProps,
getInputProps,
getListboxProps,
getOptionProps,
groupedOptions,
} = useAutocomplete({
open,
inputValue,
options,
id: props?.name,
openOnFocus: true,
disableCloseOnSelect: true,
value: selectedColumn,
onClose: () => setOpen(false),
groupBy: (option) => option.group,
isOptionEqualToValue,
onChange: handleChange,
});
return (
<>
<div {...getRootProps()} className={rootClassName}>
<Input
{...props}
ref={ref}
fullWidth
slotProps={{
...(props.slotProps || {}),
label: getInputLabelProps(),
input: { ...(props.slotProps?.input || {}), ref: setAnchorEl },
inputRoot: {
...getInputProps(),
className: twMerge(
Boolean(selectedColumn) || Boolean(relationshipDotNotation)
? '!pl-0'
: null,
props.slotProps?.inputRoot?.className,
),
},
}}
onFocus={() => setOpen(true)}
onClick={() => setOpen(true)}
error={Boolean(tableError || metadataError)}
helperText={String(tableError || metadataError || '')}
onChange={(event) => setInputValue(event.target.value)}
value={inputValue}
startAdornment={
selectedColumn || relationshipDotNotation ? (
<Text className="!ml-2 lg:max-w-[200px] flex-shrink-0 truncate">
<span className="text-greyscaleGrey">{defaultTable}</span>.
{relationshipDotNotation && (
<>
<span className="hidden lg:inline">
{getTruncatedText(relationshipDotNotation, 15, 'start')}.
</span>
<span className="inline lg:hidden">
{relationshipDotNotation}.
</span>
</>
)}
</Text>
) : null
}
endAdornment={
tableStatus === 'loading' ||
metadataStatus === 'loading' ||
!initialized ? (
<ActivityIndicator className="mr-2" delay={500} />
) : null
}
/>
</div>
<AutocompletePopper
onMouseDown={(event) => event.preventDefault()}
modifiers={[{ name: 'offset', options: { offset: [0, 10] } }]}
placement="bottom-start"
open={popupOpen}
anchorEl={anchorEl}
style={{ width: anchorEl?.parentElement?.clientWidth }}
>
<div className={autocompleteClasses.paper}>
<div className="px-3 py-2.5 border-b-1 border-greyscale-100 grid grid-flow-col gap-2 justify-start items-center">
{selectedRelationships.length > 0 && (
<IconButton
variant="borderless"
color="secondary"
onClick={(event) => {
event.stopPropagation();
setInputValue('');
setSelectedColumn(null);
setSelectedRelationships((activeRelationships) =>
activeRelationships.slice(0, -1),
);
}}
>
<ArrowLeftIcon className="w-4 h-4" />
</IconButton>
)}
<Text className="truncate direction-rtl text-left">
<span className="!text-greyscaleMedium">{defaultTable}</span>
{relationshipDotNotation && (
<>
<span className="hidden lg:inline">
.{getTruncatedText(relationshipDotNotation, 20, 'start')}
</span>
<span className="inline lg:hidden">
.{relationshipDotNotation}
</span>
</>
)}
</Text>
</div>
{(tableStatus === 'loading' ||
metadataStatus === 'loading' ||
!initialized) && (
<div className="p-2">
<ActivityIndicator label="Loading..." />
</div>
)}
{groupedOptions.length > 0 && (
<List
{...getListboxProps()}
className={autocompleteClasses.listbox}
>
{(
groupedOptions as AutocompleteGroupedOption<
typeof options[number]
>[]
).map((optionGroup) =>
renderGroup({
key: `${optionGroup.key}`,
group: optionGroup.group,
children: optionGroup.options.map((option, index) =>
renderOption(
option,
getOptionProps({
option,
index: optionGroup.index + index,
}),
),
),
}),
)}
</List>
)}
{groupedOptions.length === 0 && Boolean(anchorEl) && (
<Text className={autocompleteClasses.noOptions}>No options</Text>
)}
</div>
</AutocompletePopper>
</>
);
}
export default forwardRef(ColumnAutocomplete);

View File

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

View File

@@ -0,0 +1,263 @@
import type { FetchMetadataReturnType } from '@/hooks/dataBrowser/useMetadataQuery';
import type { FetchTableReturnType } from '@/hooks/dataBrowser/useTableQuery';
import type { HasuraMetadataTable } from '@/types/dataBrowser';
import type { AutocompleteOption } from '@/ui/v2/Autocomplete';
import { useEffect, useState } from 'react';
export interface UseAsyncInitialValueOptions {
/**
* Selected schema to be used to determine the initial value.
*/
selectedSchema?: string;
/**
* Selected table to be used to determine the initial value.
*/
selectedTable?: string;
/**
* Initial value to be used before the async value is loaded.
*/
initialValue?: string;
/**
* Determines whether or not the table data is loading.
*/
isTableLoading?: boolean;
/**
* Determines whether or not the metadata is loading.
*/
isMetadataLoading?: boolean;
/**
* Table data to be used to determine the initial value.
*/
tableData?: FetchTableReturnType;
/**
* Metadata to be used to determine the initial value.
*/
metadata?: FetchMetadataReturnType;
/**
* Function to be called when the input is asynchronously initialized.
*/
onInitialized?: (value: {
value: string;
columnMetadata?: Record<string, any>;
}) => void;
}
export default function useAsyncInitialValue({
selectedSchema,
selectedTable,
initialValue,
isTableLoading,
isMetadataLoading,
tableData,
metadata,
onInitialized,
}: UseAsyncInitialValueOptions) {
const currentTablePath = `${selectedSchema}.${selectedTable}`;
const [inputValue, setInputValue] = useState('');
const [initialized, setInitialized] = useState(false);
// We might not going to have the most up-to-date table data because the
// relationship is loaded asynchronously, so we need to make sure we don't
// look for the column in a stale table when initializing
const [asyncTablePath, setAsyncTablePath] = useState(currentTablePath);
const [remainingColumnPath, setRemainingColumnPath] = useState(
initialValue?.split('.') || [],
);
const [selectedRelationships, setSelectedRelationships] = useState<
{ schema: string; table: string; name: string }[]
>([]);
const relationshipDotNotation =
initialized && selectedRelationships?.length > 0
? selectedRelationships.map((relationship) => relationship.name).join('.')
: '';
const [selectedColumn, setSelectedColumn] =
useState<AutocompleteOption>(null);
const activeRelationship =
selectedRelationships[selectedRelationships.length - 1];
useEffect(() => {
if (remainingColumnPath?.length > 0 || initialized) {
return;
}
setInitialized(true);
if (!selectedColumn) {
return;
}
onInitialized?.({
value:
selectedRelationships.length > 0
? [relationshipDotNotation, selectedColumn.value].join('.')
: selectedColumn.value,
columnMetadata: selectedColumn.metadata,
});
}, [
initialized,
onInitialized,
relationshipDotNotation,
remainingColumnPath?.length,
selectedColumn,
selectedRelationships.length,
]);
useEffect(() => {
if (
remainingColumnPath?.length !== 1 ||
isTableLoading ||
!tableData?.columns ||
asyncTablePath !== currentTablePath
) {
return;
}
const [activeColumn] = remainingColumnPath;
// If there is a single column in the path, it means that we can look for it
// in the table columns
if (
!tableData?.columns.some((column) => column.column_name === activeColumn)
) {
setRemainingColumnPath([]);
return;
}
setSelectedColumn({
value: activeColumn,
label: activeColumn,
group: 'columns',
metadata: tableData.columns.find(
(column) => column.column_name === activeColumn,
),
});
setRemainingColumnPath((columnPath) => columnPath.slice(1));
setInputValue(activeColumn);
}, [
remainingColumnPath,
isTableLoading,
tableData?.columns,
asyncTablePath,
currentTablePath,
]);
useEffect(() => {
if (
remainingColumnPath.length < 2 ||
isTableLoading ||
isMetadataLoading ||
!tableData?.columns
) {
return;
}
const metadataMap = metadata.tables.reduce(
(map, metadataTable) =>
map.set(
`${metadataTable.table.schema}.${metadataTable.table.name}`,
metadataTable,
),
new Map<string, HasuraMetadataTable>(),
);
const [nextPath] = remainingColumnPath.slice(
0,
remainingColumnPath.length - 1,
);
const tableMetadata = metadataMap.get(`${selectedSchema}.${selectedTable}`);
const currentRelationship = [
...(tableMetadata.object_relationships || []),
...(tableMetadata.array_relationships || []),
].find(({ name }) => name === nextPath);
if (!currentRelationship) {
setRemainingColumnPath([]);
return;
}
const { foreign_key_constraint_on: metadataConstraint } =
currentRelationship.using || {};
// In some cases the metadata already contains the schema and table name
if (typeof metadataConstraint !== 'string') {
setAsyncTablePath(
`${metadataConstraint.table.schema || 'public'}.${
metadataConstraint.table.name
}`,
);
setSelectedRelationships((currentRelationships) => [
...currentRelationships,
{
schema: metadataConstraint.table.schema || 'public',
table: metadataConstraint.table.name,
name: nextPath,
},
]);
setRemainingColumnPath((columnPath) => columnPath.slice(1));
return;
}
const foreignKeyRelation = tableData?.foreignKeyRelations?.find(
({ columnName }) => {
const { foreign_key_constraint_on } = currentRelationship.using || {};
if (!foreign_key_constraint_on) {
return false;
}
if (typeof foreign_key_constraint_on === 'string') {
return foreign_key_constraint_on === columnName;
}
return foreign_key_constraint_on.column === columnName;
},
);
if (!foreignKeyRelation) {
setRemainingColumnPath([]);
return;
}
setAsyncTablePath(
`${foreignKeyRelation.referencedSchema || 'public'}.${
foreignKeyRelation.referencedTable
}`,
);
setSelectedRelationships((currentRelationships) => [
...currentRelationships,
{
schema: foreignKeyRelation.referencedSchema || 'public',
table: foreignKeyRelation.referencedTable,
name: nextPath,
},
]);
setRemainingColumnPath((columnPath) => columnPath.slice(1));
}, [
selectedSchema,
selectedTable,
metadata?.tables,
tableData?.columns,
tableData?.foreignKeyRelations,
remainingColumnPath,
isTableLoading,
isMetadataLoading,
]);
return {
initialized,
inputValue,
setInputValue,
activeRelationship,
selectedRelationships: initialized ? selectedRelationships : [],
selectedColumn: initialized ? selectedColumn : null,
setSelectedRelationships,
setSelectedColumn,
relationshipDotNotation,
};
}

View File

@@ -0,0 +1,102 @@
import type { FetchMetadataReturnType } from '@/hooks/dataBrowser/useMetadataQuery';
import type { FetchTableReturnType } from '@/hooks/dataBrowser/useTableQuery';
import type { AutocompleteOption } from '@/ui/v2/Autocomplete';
export interface UseColumnGroupsOptions {
/**
* Selected schema to be used to determines the column groups.
*/
selectedSchema?: string;
/**
* Selected table to be used to determine the column groups.
*/
selectedTable?: string;
/**
* Table data to be used to determine the column groups.
*/
tableData?: FetchTableReturnType;
/**
* Metadata to be used to determine the column groups.
*/
metadata?: FetchMetadataReturnType;
/**
* Determines whether or not to disable column groups.
*/
disableRelationships?: boolean;
}
export default function useColumnGroups({
selectedTable,
selectedSchema,
tableData,
metadata,
disableRelationships,
}: UseColumnGroupsOptions) {
const { columns, foreignKeyRelations } = tableData || {};
const columnTargetMap = foreignKeyRelations?.reduce(
(map, currentRelation) =>
map.set(currentRelation.columnName, {
schema: currentRelation.referencedSchema || 'public',
table: currentRelation.referencedTable,
}),
new Map<string, { schema: string; table: string }>(),
);
const columnOptions: AutocompleteOption[] =
columns?.map((column) => ({
label: column.column_name,
value: column.column_name,
group: 'columns',
metadata: column,
})) || [];
if (disableRelationships) {
return columnOptions;
}
const { object_relationships, array_relationships } =
metadata?.tables?.find(
({ table: metadataTable }) =>
metadataTable.name === selectedTable &&
metadataTable.schema === selectedSchema,
) || {};
const objectAndArrayRelationships = [
...(object_relationships || []),
...(array_relationships || []),
].map((relationship) => {
const { foreign_key_constraint_on } = relationship?.using || {};
if (typeof foreign_key_constraint_on === 'string') {
return {
schema: selectedSchema,
table: selectedTable,
column: foreign_key_constraint_on,
name: relationship.name,
};
}
return {
schema: foreign_key_constraint_on.table.schema,
table: foreign_key_constraint_on.table.name,
column: foreign_key_constraint_on.column,
name: relationship.name,
};
});
return [
...columnOptions,
...objectAndArrayRelationships.map((relationship) => ({
label: relationship.name,
value: relationship.name,
group: 'relationships',
metadata: {
target: {
...(columnTargetMap?.get(relationship.column) || {}),
name: relationship.name,
},
},
})),
];
}

View File

@@ -1,10 +1,10 @@
import type { import type {
BaseColumnFormProps, BaseColumnFormProps,
BaseColumnFormValues, BaseColumnFormValues,
} from '@/components/data-browser/BaseColumnForm'; } from '@/components/dataBrowser/BaseColumnForm';
import BaseColumnForm, { import BaseColumnForm, {
baseColumnValidationSchema, baseColumnValidationSchema,
} from '@/components/data-browser/BaseColumnForm'; } from '@/components/dataBrowser/BaseColumnForm';
import useCreateColumnMutation from '@/hooks/dataBrowser/useCreateColumnMutation'; import useCreateColumnMutation from '@/hooks/dataBrowser/useCreateColumnMutation';
import useTrackForeignKeyRelationsMutation from '@/hooks/dataBrowser/useTrackForeignKeyRelationsMutation'; import useTrackForeignKeyRelationsMutation from '@/hooks/dataBrowser/useTrackForeignKeyRelationsMutation';
import { Alert } from '@/ui/Alert'; import { Alert } from '@/ui/Alert';

View File

@@ -1,11 +1,11 @@
import type { import type {
BaseForeignKeyFormProps, BaseForeignKeyFormProps,
BaseForeignKeyFormValues, BaseForeignKeyFormValues,
} from '@/components/data-browser/BaseForeignKeyForm'; } from '@/components/dataBrowser/BaseForeignKeyForm';
import { import {
BaseForeignKeyForm, BaseForeignKeyForm,
baseForeignKeyValidationSchema, baseForeignKeyValidationSchema,
} from '@/components/data-browser/BaseForeignKeyForm'; } from '@/components/dataBrowser/BaseForeignKeyForm';
import { Alert } from '@/ui/Alert'; import { Alert } from '@/ui/Alert';
import Button from '@/ui/v2/Button'; import Button from '@/ui/v2/Button';
import { yupResolver } from '@hookform/resolvers/yup'; import { yupResolver } from '@hookform/resolvers/yup';

View File

@@ -1,7 +1,7 @@
import type { BaseRecordFormProps } from '@/components/data-browser/BaseRecordForm'; import type { BaseRecordFormProps } from '@/components/dataBrowser/BaseRecordForm';
import BaseRecordForm from '@/components/data-browser/BaseRecordForm'; import BaseRecordForm from '@/components/dataBrowser/BaseRecordForm';
import useCreateRecordMutation from '@/hooks/dataBrowser/useCreateRecordMutation'; import useCreateRecordMutation from '@/hooks/dataBrowser/useCreateRecordMutation';
import type { ColumnInsertOptions } from '@/types/data-browser'; import type { ColumnInsertOptions } from '@/types/dataBrowser';
import { Alert } from '@/ui/Alert'; import { Alert } from '@/ui/Alert';
import Button from '@/ui/v2/Button'; import Button from '@/ui/v2/Button';
import { createDynamicValidationSchema } from '@/utils/dataBrowser/validationSchemaHelpers'; import { createDynamicValidationSchema } from '@/utils/dataBrowser/validationSchemaHelpers';

View File

@@ -1,14 +1,14 @@
import type { import type {
BaseTableFormProps, BaseTableFormProps,
BaseTableFormValues, BaseTableFormValues,
} from '@/components/data-browser/BaseTableForm'; } from '@/components/dataBrowser/BaseTableForm';
import BaseTableForm, { import BaseTableForm, {
baseTableValidationSchema, baseTableValidationSchema,
} from '@/components/data-browser/BaseTableForm'; } from '@/components/dataBrowser/BaseTableForm';
import useCreateTableMutation from '@/hooks/dataBrowser/useCreateTableMutation'; import useCreateTableMutation from '@/hooks/dataBrowser/useCreateTableMutation';
import useTrackForeignKeyRelationMutation from '@/hooks/dataBrowser/useTrackForeignKeyRelationsMutation'; import useTrackForeignKeyRelationMutation from '@/hooks/dataBrowser/useTrackForeignKeyRelationsMutation';
import useTrackTableMutation from '@/hooks/dataBrowser/useTrackTableMutation'; import useTrackTableMutation from '@/hooks/dataBrowser/useTrackTableMutation';
import type { DatabaseTable } from '@/types/data-browser'; import type { DatabaseTable } from '@/types/dataBrowser';
import { Alert } from '@/ui/Alert'; import { Alert } from '@/ui/Alert';
import Button from '@/ui/v2/Button'; import Button from '@/ui/v2/Button';
import { triggerToast } from '@/utils/toast'; import { triggerToast } from '@/utils/toast';

View File

@@ -6,8 +6,8 @@ import DataGridNumericCell from '@/components/common/DataGridNumericCell';
import DataGridTextCell from '@/components/common/DataGridTextCell'; import DataGridTextCell from '@/components/common/DataGridTextCell';
import { useDialog } from '@/components/common/DialogProvider'; import { useDialog } from '@/components/common/DialogProvider';
import InlineCode from '@/components/common/InlineCode'; import InlineCode from '@/components/common/InlineCode';
import DataBrowserEmptyState from '@/components/data-browser/DataBrowserEmptyState'; import DataBrowserEmptyState from '@/components/dataBrowser/DataBrowserEmptyState';
import DataBrowserGridControls from '@/components/data-browser/DataBrowserGridControls'; import DataBrowserGridControls from '@/components/dataBrowser/DataBrowserGridControls';
import useDeleteColumnWithToastMutation from '@/hooks/dataBrowser/useDeleteColumnMutation/useDeleteColumnWithToastMutation'; import useDeleteColumnWithToastMutation from '@/hooks/dataBrowser/useDeleteColumnMutation/useDeleteColumnWithToastMutation';
import useTableQuery from '@/hooks/dataBrowser/useTableQuery'; import useTableQuery from '@/hooks/dataBrowser/useTableQuery';
import type { UpdateRecordVariables } from '@/hooks/dataBrowser/useUpdateRecordMutation'; import type { UpdateRecordVariables } from '@/hooks/dataBrowser/useUpdateRecordMutation';
@@ -17,7 +17,7 @@ import useTablePath from '@/hooks/useTablePath';
import type { import type {
DataBrowserGridColumn, DataBrowserGridColumn,
NormalizedQueryDataRow, NormalizedQueryDataRow,
} from '@/types/data-browser'; } from '@/types/dataBrowser';
import KeyIcon from '@/ui/v2/icons/KeyIcon'; import KeyIcon from '@/ui/v2/icons/KeyIcon';
import normalizeDefaultValue from '@/utils/dataBrowser/normalizeDefaultValue'; import normalizeDefaultValue from '@/utils/dataBrowser/normalizeDefaultValue';
import { import {

View File

@@ -4,7 +4,7 @@ import { useDialog } from '@/components/common/DialogProvider';
import useDeleteRecordMutation from '@/hooks/dataBrowser/useDeleteRecordMutation'; import useDeleteRecordMutation from '@/hooks/dataBrowser/useDeleteRecordMutation';
import { useCurrentWorkspaceAndApplication } from '@/hooks/useCurrentWorkspaceAndApplication'; import { useCurrentWorkspaceAndApplication } from '@/hooks/useCurrentWorkspaceAndApplication';
import useDataGridConfig from '@/hooks/useDataGridConfig'; import useDataGridConfig from '@/hooks/useDataGridConfig';
import type { DataBrowserGridColumn } from '@/types/data-browser'; import type { DataBrowserGridColumn } from '@/types/dataBrowser';
import Chip from '@/ui/Chip'; import Chip from '@/ui/Chip';
import Button from '@/ui/v2/Button'; import Button from '@/ui/v2/Button';
import { Dropdown } from '@/ui/v2/Dropdown'; import { Dropdown } from '@/ui/v2/Dropdown';

View File

@@ -1,5 +1,5 @@
import type { DataBrowserSidebarProps } from '@/components/data-browser/DataBrowserSidebar'; import type { DataBrowserSidebarProps } from '@/components/dataBrowser/DataBrowserSidebar';
import DataBrowserSidebar from '@/components/data-browser/DataBrowserSidebar'; import DataBrowserSidebar from '@/components/dataBrowser/DataBrowserSidebar';
import type { ProjectLayoutProps } from '@/components/layout/ProjectLayout'; import type { ProjectLayoutProps } from '@/components/layout/ProjectLayout';
import ProjectLayout from '@/components/layout/ProjectLayout'; import ProjectLayout from '@/components/layout/ProjectLayout';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';

View File

@@ -1,6 +1,6 @@
import InlineCode from '@/components/common/InlineCode'; import InlineCode from '@/components/common/InlineCode';
import ReadOnlyToggle from '@/components/common/ReadOnlyToggle'; import ReadOnlyToggle from '@/components/common/ReadOnlyToggle';
import type { DataBrowserGridColumn } from '@/types/data-browser'; import type { DataBrowserGridColumn } from '@/types/dataBrowser';
import KeyIcon from '@/ui/v2/icons/KeyIcon'; import KeyIcon from '@/ui/v2/icons/KeyIcon';
import Input from '@/ui/v2/Input'; import Input from '@/ui/v2/Input';
import Option from '@/ui/v2/Option'; import Option from '@/ui/v2/Option';
@@ -204,7 +204,7 @@ export default function DatabaseRecordInputGroup({
multiline={isMultiline} multiline={isMultiline}
rows={5} rows={5}
autoFocus={index === 0 && autoFocusFirstInput} autoFocus={index === 0 && autoFocusFirstInput}
componentsProps={{ slotProps={{
label: commonLabelProps, label: commonLabelProps,
inputRoot: { step: 1 }, inputRoot: { step: 1 },
}} }}

View File

@@ -1,13 +1,13 @@
import type { import type {
BaseColumnFormProps, BaseColumnFormProps,
BaseColumnFormValues, BaseColumnFormValues,
} from '@/components/data-browser/BaseColumnForm'; } from '@/components/dataBrowser/BaseColumnForm';
import BaseColumnForm, { import BaseColumnForm, {
baseColumnValidationSchema, baseColumnValidationSchema,
} from '@/components/data-browser/BaseColumnForm'; } from '@/components/dataBrowser/BaseColumnForm';
import useTrackForeignKeyRelationMutation from '@/hooks/dataBrowser/useTrackForeignKeyRelationsMutation'; import useTrackForeignKeyRelationMutation from '@/hooks/dataBrowser/useTrackForeignKeyRelationsMutation';
import useUpdateColumnMutation from '@/hooks/dataBrowser/useUpdateColumnMutation'; import useUpdateColumnMutation from '@/hooks/dataBrowser/useUpdateColumnMutation';
import type { DataBrowserGridColumn } from '@/types/data-browser'; import type { DataBrowserGridColumn } from '@/types/dataBrowser';
import { Alert } from '@/ui/Alert'; import { Alert } from '@/ui/Alert';
import type { AutocompleteOption } from '@/ui/v2/Autocomplete'; import type { AutocompleteOption } from '@/ui/v2/Autocomplete';
import Button from '@/ui/v2/Button'; import Button from '@/ui/v2/Button';

View File

@@ -1,12 +1,12 @@
import type { import type {
BaseForeignKeyFormProps, BaseForeignKeyFormProps,
BaseForeignKeyFormValues, BaseForeignKeyFormValues,
} from '@/components/data-browser/BaseForeignKeyForm'; } from '@/components/dataBrowser/BaseForeignKeyForm';
import { import {
BaseForeignKeyForm, BaseForeignKeyForm,
baseForeignKeyValidationSchema, baseForeignKeyValidationSchema,
} from '@/components/data-browser/BaseForeignKeyForm'; } from '@/components/dataBrowser/BaseForeignKeyForm';
import type { ForeignKeyRelation } from '@/types/data-browser'; import type { ForeignKeyRelation } from '@/types/dataBrowser';
import { Alert } from '@/ui/Alert'; import { Alert } from '@/ui/Alert';
import Button from '@/ui/v2/Button'; import Button from '@/ui/v2/Button';
import { yupResolver } from '@hookform/resolvers/yup'; import { yupResolver } from '@hookform/resolvers/yup';

View File

@@ -1,17 +1,17 @@
import type { import type {
BaseTableFormProps, BaseTableFormProps,
BaseTableFormValues, BaseTableFormValues,
} from '@/components/data-browser/BaseTableForm'; } from '@/components/dataBrowser/BaseTableForm';
import BaseTableForm, { import BaseTableForm, {
baseTableValidationSchema, baseTableValidationSchema,
} from '@/components/data-browser/BaseTableForm'; } from '@/components/dataBrowser/BaseTableForm';
import useTableQuery from '@/hooks/dataBrowser/useTableQuery'; import useTableQuery from '@/hooks/dataBrowser/useTableQuery';
import useTrackForeignKeyRelationsMutation from '@/hooks/dataBrowser/useTrackForeignKeyRelationsMutation'; import useTrackForeignKeyRelationsMutation from '@/hooks/dataBrowser/useTrackForeignKeyRelationsMutation';
import useUpdateTableMutation from '@/hooks/dataBrowser/useUpdateTableMutation'; import useUpdateTableMutation from '@/hooks/dataBrowser/useUpdateTableMutation';
import type { import type {
DatabaseTable, DatabaseTable,
NormalizedQueryDataRow, NormalizedQueryDataRow,
} from '@/types/data-browser'; } from '@/types/dataBrowser';
import { Alert } from '@/ui/Alert'; import { Alert } from '@/ui/Alert';
import ActivityIndicator from '@/ui/v2/ActivityIndicator'; import ActivityIndicator from '@/ui/v2/ActivityIndicator';
import Button from '@/ui/v2/Button'; import Button from '@/ui/v2/Button';

View File

@@ -0,0 +1,208 @@
import ControlledSelect from '@/components/common/ControlledSelect';
import ColumnAutocomplete from '@/components/dataBrowser/ColumnAutocomplete';
import type { HasuraOperator } from '@/types/dataBrowser';
import Option from '@/ui/v2/Option';
import Text from '@/ui/v2/Text';
import { useRouter } from 'next/router';
import type { DetailedHTMLProps, HTMLProps } from 'react';
import { useState } from 'react';
import { useController, useFormContext } from 'react-hook-form';
import { twMerge } from 'tailwind-merge';
import RuleRemoveButton from './RuleRemoveButton';
import RuleValueInput from './RuleValueInput';
export interface RuleEditorRowProps
extends DetailedHTMLProps<HTMLProps<HTMLDivElement>, HTMLDivElement> {
/**
* Name of the parent group editor.
*/
name: string;
/**
* Index of the rule.
*/
index: number;
/**
* Function to be called when the remove button is clicked.
*/
onRemove?: VoidFunction;
/**
* List of operators to be disabled for the rule editor.
*
* @default []
*/
disabledOperators?: HasuraOperator[];
}
const commonOperators: {
value: HasuraOperator;
label?: string;
helperText?: string;
}[] = [
{ value: '_eq', helperText: 'equal' },
{ value: '_neq', helperText: 'not equal' },
{ value: '_in_hasura', label: '_in', helperText: 'in (X-Hasura-)' },
{ value: '_in', helperText: 'in (array)' },
{ value: '_nin_hasura', label: '_nin', helperText: 'not in (X-Hasura-)' },
{ value: '_nin', helperText: 'not in (array)' },
{ value: '_gt', helperText: 'greater than' },
{ value: '_lt', helperText: 'lower than' },
{ value: '_gte', helperText: 'greater than or equal' },
{ value: '_lte', helperText: 'lower than or equal' },
{ value: '_ceq', helperText: 'equal to column' },
{ value: '_cne', helperText: 'not equal to column' },
{ value: '_cgt', helperText: 'greater than column' },
{ value: '_clt', helperText: 'lower than column' },
{ value: '_cgte', helperText: 'greater than or equal to column' },
{ value: '_clte', helperText: 'lower than or equal to column' },
{ value: '_is_null', helperText: 'null' },
];
const textSpecificOperators: typeof commonOperators = [
{ value: '_like', helperText: 'like' },
{ value: '_nlike', helperText: 'not like' },
{ value: '_ilike', helperText: 'like (case-insensitive)' },
{ value: '_nilike', helperText: 'not like (case-insensitive)' },
{ value: '_similar', helperText: 'similar' },
{ value: '_nsimilar', helperText: 'not similar' },
{ value: '_regex', helperText: 'matches regex' },
{ value: '_nregex', helperText: `doesn't match regex` },
{ value: '_iregex', helperText: 'matches case-insensitive regex' },
{ value: '_niregex', helperText: `doesn't match case-insensitive regex` },
];
function renderOption({
value,
label,
helperText,
}: typeof commonOperators[number]) {
return (
<Option key={value} value={value} className="grid grid-flow-col gap-2">
<Text component="span" className="inline-block w-16">
{label || value}
</Text>
{helperText && (
<Text component="span" className="!text-greyscaleGrey">
{helperText}
</Text>
)}
</Option>
);
}
export default function RuleEditorRow({
name,
index,
onRemove,
className,
disabledOperators = [],
...props
}: RuleEditorRowProps) {
const {
query: { schemaSlug, tableSlug },
} = useRouter();
const { control, setValue } = useFormContext();
const rowName = `${name}.rules.${index}`;
const [selectedTablePath, setSelectedTablePath] = useState<string>('');
const [selectedColumnType, setSelectedColumnType] = useState<string>('');
const { field: autocompleteField } = useController({
name: `${rowName}.column`,
control,
});
const disabledOperatorMap = disabledOperators.reduce(
(map, currentOperator) => map.set(currentOperator, true),
new Map<string, boolean>(),
);
const availableOperators = [
...commonOperators.filter(({ value }) => !disabledOperatorMap.has(value)),
...(selectedColumnType === 'text'
? textSpecificOperators.filter(
({ value }) => !disabledOperatorMap.get(value),
)
: []),
];
return (
<div
className={twMerge(
'flex lg:flex-row flex-col items-stretch lg:max-h-10 flex-1 space-y-1 lg:space-y-0',
className,
)}
{...props}
>
<ColumnAutocomplete
{...autocompleteField}
schema={schemaSlug as string}
table={tableSlug as string}
rootClassName="lg:flex-grow-0 lg:flex-shrink-0 lg:flex-[320px] h-10"
slotProps={{ input: { className: 'bg-white lg:!rounded-r-none' } }}
fullWidth
onChange={(_event, { value, columnMetadata, disableReset }) => {
setSelectedTablePath(
`${columnMetadata.table_schema}.${columnMetadata.table_name}`,
);
setSelectedColumnType(columnMetadata?.udt_name);
setValue(`${rowName}.column`, value, { shouldDirty: true });
if (disableReset) {
return;
}
setValue(`${rowName}.operator`, '_eq', { shouldDirty: true });
setValue(`${rowName}.value`, '', { shouldDirty: true });
}}
onInitialized={({ value, columnMetadata }) => {
setSelectedTablePath(
`${columnMetadata.table_schema}.${columnMetadata.table_name}`,
);
setSelectedColumnType(columnMetadata?.udt_name);
setValue(`${rowName}.column`, value, { shouldDirty: true });
}}
/>
<ControlledSelect
name={`${rowName}.operator`}
className="lg:flex-grow-0 lg:flex-shrink-0 lg:flex-[140px] h-10"
slotProps={{ root: { className: 'bg-white lg:!rounded-none' } }}
fullWidth
onChange={(_event, value: HasuraOperator) => {
if (!['_in', '_nin', '_in_hasura', '_nin_hasura'].includes(value)) {
return;
}
if (value === '_in_hasura' || value === '_nin_hasura') {
setValue(`${rowName}.value`, null, { shouldDirty: true });
return;
}
setValue(`${rowName}.value`, [], { shouldDirty: true });
}}
renderValue={(option) => {
if (!option?.value) {
return <span />;
}
if (option.value === '_in_hasura') {
return <span>_in</span>;
}
if (option.value === '_nin_hasura') {
return <span>_nin</span>;
}
return <span>{option.value}</span>;
}}
>
{availableOperators.map(renderOption)}
</ControlledSelect>
<RuleValueInput selectedTablePath={selectedTablePath} name={rowName} />
<RuleRemoveButton onRemove={onRemove} name={name} />
</div>
);
}

View File

@@ -0,0 +1,58 @@
import ControlledSelect from '@/components/common/ControlledSelect';
import type { RuleGroup } from '@/types/dataBrowser';
import Option from '@/ui/v2/Option';
import Text from '@/ui/v2/Text';
import type { DetailedHTMLProps, HTMLProps } from 'react';
import { useWatch } from 'react-hook-form';
import { twMerge } from 'tailwind-merge';
export interface RuleGroupControlsProps
extends DetailedHTMLProps<HTMLProps<HTMLDivElement>, HTMLDivElement> {
/**
* Name of the rule group to control.
*/
name: string;
/**
* Determines whether or not select should be shown or just a label with the
* operator name.
*/
showSelect?: boolean;
}
const operatorDictionary: Record<RuleGroup['operator'], string> = {
_and: 'and',
_or: 'or',
};
export default function RuleGroupControls({
name,
showSelect,
className,
...props
}: RuleGroupControlsProps) {
const currentOperator: RuleGroup['operator'] = useWatch({
name: `${name}.operator`,
});
return (
<div
className={twMerge('grid grid-flow-row gap-2 content-start', className)}
{...props}
>
{showSelect ? (
<ControlledSelect
name={`${name}.operator`}
slotProps={{ root: { className: 'bg-white' } }}
fullWidth
>
<Option value="_and">and</Option>
<Option value="_or">or</Option>
</ControlledSelect>
) : (
<Text className="p-2 !font-medium">
{operatorDictionary[currentOperator]}
</Text>
)}
</div>
);
}

View File

@@ -0,0 +1,91 @@
import Form from '@/components/common/Form';
import type { RuleGroup } from '@/types/dataBrowser';
import Button from '@/ui/v2/Button';
import Text from '@/ui/v2/Text';
import customClaimsQuery from '@/utils/msw/mocks/graphql/customClaimsQuery';
import hasuraMetadataQuery from '@/utils/msw/mocks/rest/hasuraMetadataQuery';
import tableQuery from '@/utils/msw/mocks/rest/tableQuery';
import type { ComponentMeta, ComponentStory } from '@storybook/react';
import { useState } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import type { RuleGroupEditorProps } from './RuleGroupEditor';
import RuleGroupEditor from './RuleGroupEditor';
export default {
title: 'Data Browser / RuleGroupEditor',
component: RuleGroupEditor,
parameters: {
docs: {
source: {
type: 'code',
},
},
},
} as ComponentMeta<typeof RuleGroupEditor>;
const defaultParameters = {
nextRouter: {
path: '/[workspaceSlug]/[appSlug]/database/browser/[dataSourceSlug]/[schemaSlug]/[tableSlug]',
asPath: '/workspace/app/database/browser/default/public/users',
query: {
workspaceSlug: 'workspace',
appSlug: 'app',
dataSourceSlug: 'default',
schemaSlug: 'public',
tableSlug: 'books',
},
},
msw: {
handlers: [tableQuery, hasuraMetadataQuery, customClaimsQuery],
},
};
const Template: ComponentStory<typeof RuleGroupEditor> = function Template(
args: RuleGroupEditorProps,
) {
const [submittedValues, setSubmittedValues] = useState<string>();
const form = useForm<{ ruleGroupEditor: RuleGroup }>({
defaultValues: {
ruleGroupEditor: {
operator: '_and',
rules: [{ column: '', operator: '_eq', value: '' }],
groups: [],
},
},
reValidateMode: 'onSubmit',
});
function handleSubmit(values: { ruleGroupEditor: RuleGroup }) {
setSubmittedValues(JSON.stringify(values, null, 2));
}
// note: Storybook passes `onRemove` as a prop, but we don't want to use it
return (
<div className="grid grid-flow-row gap-2">
<FormProvider {...form}>
<Form onSubmit={handleSubmit} className="grid grid-flow-row gap-2">
<RuleGroupEditor {...args} name="ruleGroupEditor" onRemove={null} />
<Button type="submit" className="justify-self-start">
Submit
</Button>
</Form>
</FormProvider>
<Text component="pre" className="!font-mono !text-gray-700">
{submittedValues || 'The form has not been submitted yet.'}
</Text>
</div>
);
};
export const Default = Template.bind({});
Default.args = {};
Default.parameters = defaultParameters;
export const DisabledOperators = Template.bind({});
DisabledOperators.args = {
disabledOperators: ['_in_hasura', '_nin_hasura', '_is_null'],
};
DisabledOperators.parameters = defaultParameters;

View File

@@ -0,0 +1,193 @@
import type { Rule, RuleGroup } from '@/types/dataBrowser';
import Button from '@/ui/v2/Button';
import PlusIcon from '@/ui/v2/icons/PlusIcon';
import Text from '@/ui/v2/Text';
import type { DetailedHTMLProps, HTMLProps } from 'react';
import { useFieldArray, useFormContext } from 'react-hook-form';
import { twMerge } from 'tailwind-merge';
import type { RuleEditorRowProps } from './RuleEditorRow';
import RuleEditorRow from './RuleEditorRow';
import RuleGroupControls from './RuleGroupControls';
export interface RuleGroupEditorProps
extends DetailedHTMLProps<HTMLProps<HTMLDivElement>, HTMLDivElement>,
Pick<RuleEditorRowProps, 'disabledOperators'> {
/**
* Name of the group editor.
*/
name: string;
/**
* Function to be called when the remove button is clicked.
*/
onRemove?: VoidFunction;
/**
* Determines whether or not remove should be disabled for the rule group.
*/
disableRemove?: boolean;
/**
* Group editor depth.
*
* @default 0
*/
depth?: number;
/**
* Maximum depth of the group editor.
*
* @default 7
*/
maxDepth?: number;
}
export default function RuleGroupEditor({
onRemove,
name,
className,
disableRemove,
disabledOperators = [],
depth = 0,
maxDepth = 7,
...props
}: RuleGroupEditorProps) {
const form = useFormContext();
const { control } = form;
// Note: Reason for the type cast to `never`
// https://github.com/react-hook-form/react-hook-form/issues/4055#issuecomment-950145092
const {
fields: rules,
append: appendRule,
remove: removeRule,
} = useFieldArray({
control,
name: `${name}.rules`,
} as never);
// Note: Reason for the type cast to `never`
// https://github.com/react-hook-form/react-hook-form/issues/4055#issuecomment-950145092
const {
fields: groups,
append: appendGroup,
remove: removeGroup,
} = useFieldArray({
control,
name: `${name}.groups`,
} as never);
if (!form) {
throw new Error('RuleGroupEditor must be used in a FormContext.');
}
return (
<div
className={twMerge(
'rounded-lg px-2',
depth === 0 && 'bg-greyscale-50',
depth === 1 && 'bg-greyscale-100',
depth === 2 && 'bg-greyscale-200',
depth === 3 && 'bg-greyscale-300',
depth === 4 && 'bg-greyscale-400',
depth === 5 && 'bg-greyscale-500',
depth >= 6 && 'bg-greyscale-600',
className,
)}
{...props}
>
<div className="flex flex-col flex-auto space-y-4 lg:space-y-2 py-4">
{(rules as (Rule & { id: string })[]).map((rule, ruleIndex) => (
<div className="flex flex-row flex-auto" key={rule.id}>
<div className="flex-[70px] flex-grow-0 flex-shrink-0 mr-2">
{ruleIndex === 0 && (
<Text className="p-2 !font-medium">Where</Text>
)}
{ruleIndex > 0 && (
<RuleGroupControls name={name} showSelect={ruleIndex === 1} />
)}
</div>
<RuleEditorRow
name={name}
index={ruleIndex}
onRemove={() => removeRule(ruleIndex)}
className="flex-auto"
disabledOperators={disabledOperators}
/>
</div>
))}
{(groups as (RuleGroup & { id: string })[]).map(
(ruleGroup, ruleGroupIndex) => (
<div
className="flex flex-row flex-auto items-start mt-2"
key={ruleGroup.id}
>
<div className="flex-[70px] flex-grow-0 flex-shrink-0 mr-2">
{rules.length === 0 && ruleGroupIndex === 0 && (
<Text className="p-2 !font-medium">Where</Text>
)}
<RuleGroupControls
name={name}
showSelect={
(rules.length === 0 && ruleGroupIndex === 1) ||
(rules.length === 1 && ruleGroupIndex === 0)
}
/>
</div>
<RuleGroupEditor
onRemove={() => removeGroup(ruleGroupIndex)}
disableRemove={rules.length === 0 && groups.length === 1}
disabledOperators={disabledOperators}
name={`${name}.groups.${ruleGroupIndex}`}
className="flex-auto"
depth={depth + 1}
/>
</div>
),
)}
</div>
<div className="grid grid-flow-row lg:grid-flow-col lg:justify-between gap-2 pb-2">
<div className="grid grid-flow-row lg:grid-flow-col gap-2 lg:justify-start">
<Button
startIcon={<PlusIcon />}
variant="borderless"
onClick={() =>
appendRule({ column: '', operator: '_eq', value: '' })
}
>
New Rule
</Button>
<Button
startIcon={<PlusIcon />}
variant="borderless"
onClick={() =>
appendGroup({
operator: '_and',
rules: [{ column: '', operator: '_eq', value: '' }],
groups: [],
})
}
disabled={depth >= maxDepth - 1}
>
New Group
</Button>
</div>
{onRemove && (
<Button
variant="borderless"
color="secondary"
onClick={onRemove}
disabled={disableRemove}
>
Delete Group
</Button>
)}
</div>
</div>
);
}

View File

@@ -0,0 +1,46 @@
import type { Rule, RuleGroup } from '@/types/dataBrowser';
import type { ButtonProps } from '@/ui/v2/Button';
import Button from '@/ui/v2/Button';
import XIcon from '@/ui/v2/icons/XIcon';
import { useWatch } from 'react-hook-form';
import { twMerge } from 'tailwind-merge';
export interface RuleRemoveButtonProps extends ButtonProps {
/**
* Name of the parent group editor.
*/
name: string;
/**
* Function to be called when the remove button is clicked.
*/
onRemove?: VoidFunction;
}
function RuleRemoveButton({
name,
onRemove,
className,
...props
}: RuleRemoveButtonProps) {
const rules: Rule[] = useWatch({ name: `${name}.rules` });
const groups: RuleGroup[] = useWatch({ name: `${name}.groups` });
return (
<Button
variant="outlined"
color="secondary"
className={twMerge(
'!bg-white lg:!rounded-l-none lg:flex-grow-0 lg:flex-shrink-0 lg:flex-[40px] !min-w-0 h-10',
className,
)}
disabled={rules.length === 1 && groups.length === 0}
aria-label="Remove Rule"
{...props}
onClick={onRemove}
>
<XIcon className="!w-4 !h-4" />
</Button>
);
}
export default RuleRemoveButton;

View File

@@ -0,0 +1,177 @@
import ControlledAutocomplete from '@/components/common/ControlledAutocomplete';
import ControlledSelect from '@/components/common/ControlledSelect';
import ReadOnlyToggle from '@/components/common/ReadOnlyToggle';
import type { ColumnAutocompleteProps } from '@/components/dataBrowser/ColumnAutocomplete';
import ColumnAutocomplete from '@/components/dataBrowser/ColumnAutocomplete';
import { useCurrentWorkspaceAndApplication } from '@/hooks/useCurrentWorkspaceAndApplication';
import type { HasuraOperator } from '@/types/dataBrowser';
import ActivityIndicator from '@/ui/v2/ActivityIndicator';
import Option from '@/ui/v2/Option';
import getPermissionVariablesArray from '@/utils/settings/getPermissionVariablesArray';
import { useGetAppCustomClaimsQuery } from '@/utils/__generated__/graphql';
import { useRouter } from 'next/router';
import { useController, useFormContext, useWatch } from 'react-hook-form';
export interface RuleValueInputProps {
/**
* Name of the parent group editor.
*/
name: string;
/**
* Path of the table selected through the column input.
*/
selectedTablePath?: string;
}
function ColumnSelectorInput({
name,
selectedTablePath,
schema,
table,
...props
}: ColumnAutocompleteProps & { selectedTablePath: string }) {
const { setValue, control } = useFormContext();
const { field } = useController({
name,
control,
});
return (
<ColumnAutocomplete
{...props}
{...field}
value={
// this array can either be ['$', 'columnName'] or ['columnName']
Array.isArray(field.value) ? field.value.slice(-1)[0] : field.value
}
schema={schema}
table={table}
disableRelationships
rootClassName="flex-auto"
slotProps={{
input: { className: 'lg:!rounded-none !bg-white !z-10' },
}}
onChange={(_event, { value }) => {
if (selectedTablePath === `${schema}.${table}`) {
setValue(name, [value], { shouldDirty: true });
return;
}
// For more information, see https://github.com/hasura/graphql-engine/issues/3459#issuecomment-1085666541
setValue(name, ['$', value], { shouldDirty: true });
}}
/>
);
}
export default function RuleValueInput({
name,
selectedTablePath,
}: RuleValueInputProps) {
const { currentApplication } = useCurrentWorkspaceAndApplication();
const { setValue } = useFormContext();
const inputName = `${name}.value`;
const operator: HasuraOperator = useWatch({ name: `${name}.operator` });
const isHasuraInput = operator === '_in_hasura' || operator === '_nin_hasura';
const {
query: { schemaSlug, tableSlug },
} = useRouter();
const { data, loading, error } = useGetAppCustomClaimsQuery({
variables: { id: currentApplication?.id },
skip: !isHasuraInput,
});
if (operator === '_is_null') {
return (
<ControlledSelect
name={inputName}
className="flex-auto"
fullWidth
slotProps={{ root: { className: 'bg-white lg:!rounded-none h-10' } }}
>
<Option value="true">
<ReadOnlyToggle
checked
slotProps={{ label: { className: '!text-sm' } }}
/>
</Option>
<Option value="false">
<ReadOnlyToggle
checked={false}
slotProps={{ label: { className: '!text-sm' } }}
/>
</Option>
</ControlledSelect>
);
}
if (operator === '_in' || operator === '_nin') {
return (
<ControlledAutocomplete
name={inputName}
multiple
freeSolo
limitTags={5}
className="flex-auto"
slotProps={{ input: { className: 'lg:!rounded-none !bg-white !z-10' } }}
options={[]}
fullWidth
filterSelectedOptions
/>
);
}
if (['_ceq', '_cne', '_cgt', '_clt', '_cgte', '_clte'].includes(operator)) {
return (
<ColumnSelectorInput
selectedTablePath={selectedTablePath}
schema={schemaSlug as string}
table={tableSlug as string}
name={inputName}
/>
);
}
const availableHasuraPermissionVariables = !loading
? getPermissionVariablesArray(data?.app?.authJwtCustomClaims).map(
({ key }) => ({
value: `X-Hasura-${key}`,
label: `X-Hasura-${key}`,
}),
)
: [];
return (
<ControlledAutocomplete
freeSolo={!isHasuraInput}
name={inputName}
className="flex-auto"
slotProps={{
input: { className: 'lg:!rounded-none !bg-white' },
formControl: { className: '!bg-transparent' },
}}
fullWidth
loading={loading}
loadingText={<ActivityIndicator label="Loading..." />}
error={!!error}
helperText={error?.message}
options={
isHasuraInput
? availableHasuraPermissionVariables
: [{ value: 'X-Hasura-User-Id', label: 'X-Hasura-User-Id' }]
}
onChange={(_event, _value, reason, details) => {
if (
reason !== 'selectOption' &&
details.option.value !== 'X-Hasura-User-Id'
) {
return;
}
setValue(inputName, details.option.value, { shouldDirty: true });
}}
/>
);
}

View File

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

View File

@@ -61,10 +61,8 @@ function LogsTimePicker({
value={format(selectedDate, 'HH:mm:ss')} value={format(selectedDate, 'HH:mm:ss')}
style={{ width: '135px' }} style={{ width: '135px' }}
id="time-picker" id="time-picker"
componentsProps={{ slotProps={{
formControl: { formControl: { className: 'grid grid-flow-col gap-x-3' },
className: 'grid grid-flow-col gap-x-3',
},
label: { sx: { fontSize: '14px' } }, label: { sx: { fontSize: '14px' } },
}} }}
onChange={handleTimePicking} onChange={handleTimePicking}

View File

@@ -5,6 +5,9 @@ import Image from 'next/image';
export default function OverviewProjectInfo() { export default function OverviewProjectInfo() {
const { currentApplication } = useCurrentWorkspaceAndApplication(); const { currentApplication } = useCurrentWorkspaceAndApplication();
const { region, subdomain } = currentApplication || {};
const isRegionAvailable =
region?.awsName && region?.countryCode && region?.city;
return ( return (
<div className="grid grid-flow-row content-start gap-6"> <div className="grid grid-flow-row content-start gap-6">
@@ -16,28 +19,28 @@ export default function OverviewProjectInfo() {
<div className="grid grid-flow-row gap-3"> <div className="grid grid-flow-row gap-3">
<InfoCard <InfoCard
title="Region" title="Region"
value={currentApplication.region?.awsName} value={region?.awsName}
customValue={ customValue={
currentApplication.region && ( region.countryCode &&
region.city && (
<div className="grid grid-flow-col items-center gap-1 self-center"> <div className="grid grid-flow-col items-center gap-1 self-center">
<Image <Image
src={`/assets/${currentApplication.region.countryCode}.svg`} src={`/assets/${region.countryCode}.svg`}
alt={`Logo of ${currentApplication.region.countryCode}`} alt={`Logo of ${region.countryCode}`}
width={16} width={16}
height={16} height={16}
/> />
<Text className="text-sm font-medium text-greyscaleDark"> <Text className="text-sm font-medium text-greyscaleDark">
{currentApplication.region.city} ( {region.city} ({region.awsName})
{currentApplication.region.awsName})
</Text> </Text>
</div> </div>
) )
} }
disableCopy={!currentApplication.region} disableCopy={!isRegionAvailable}
/> />
<InfoCard title="Subdomain" value={currentApplication.subdomain} /> <InfoCard title="Subdomain" value={subdomain} />
</div> </div>
)} )}
</div> </div>

View File

@@ -15,7 +15,7 @@ import generateRandomDatabasePassword from '@/utils/settings/generateRandomDatab
import { resetDatabasePasswordValidationSchema } from '@/utils/settings/resetDatabasePasswordValidationSchema'; import { resetDatabasePasswordValidationSchema } from '@/utils/settings/resetDatabasePasswordValidationSchema';
import { triggerToast } from '@/utils/toast'; import { triggerToast } from '@/utils/toast';
import { yupResolver } from '@hookform/resolvers/yup'; import { yupResolver } from '@hookform/resolvers/yup';
import { useUserData } from '@nhost/react'; import { useUserData } from '@nhost/nextjs';
import { FormProvider, useForm } from 'react-hook-form'; import { FormProvider, useForm } from 'react-hook-form';
import { twMerge } from 'tailwind-merge'; import { twMerge } from 'tailwind-merge';
@@ -117,7 +117,7 @@ export default function ResetDatabasePasswordSettings() {
error={Boolean(errors?.databasePassword)} error={Boolean(errors?.databasePassword)}
fullWidth fullWidth
hideEmptyHelperText hideEmptyHelperText
componentsProps={{ slotProps={{
input: { className: 'lg:w-1/2' }, input: { className: 'lg:w-1/2' },
helperText: { component: 'div' }, helperText: { component: 'div' },
}} }}

View File

@@ -125,7 +125,10 @@ export default function EnvironmentVariableSettings() {
docsLink="https://docs.nhost.io/platform/environment-variables" docsLink="https://docs.nhost.io/platform/environment-variables"
docsTitle="Environment Variables" docsTitle="Environment Variables"
rootClassName="gap-0" rootClassName="gap-0"
className="px-0 my-2" className={twMerge(
'px-0 my-2',
availableEnvironmentVariables.length === 0 && 'gap-2',
)}
slotProps={{ submitButton: { className: 'hidden' } }} slotProps={{ submitButton: { className: 'hidden' } }}
> >
<div className="grid grid-cols-2 lg:grid-cols-3 gap-2 border-b-1 border-gray-200 px-4 py-3"> <div className="grid grid-cols-2 lg:grid-cols-3 gap-2 border-b-1 border-gray-200 px-4 py-3">
@@ -134,91 +137,98 @@ export default function EnvironmentVariableSettings() {
</div> </div>
<div className="grid grid-flow-row gap-2"> <div className="grid grid-flow-row gap-2">
<List> {availableEnvironmentVariables.length > 0 && (
{availableEnvironmentVariables.map((environmentVariable, index) => { <List>
const timestamp = formatDistanceToNowStrict( {availableEnvironmentVariables.map((environmentVariable, index) => {
parseISO(environmentVariable.updatedAt), const timestamp = formatDistanceToNowStrict(
{ addSuffix: true, roundingMethod: 'floor' }, parseISO(environmentVariable.updatedAt),
); { addSuffix: true, roundingMethod: 'floor' },
);
return ( return (
<Fragment key={environmentVariable.id}> <Fragment key={environmentVariable.id}>
<ListItem.Root <ListItem.Root
className="px-4 grid grid-cols-2 lg:grid-cols-3 gap-2" className="px-4 grid grid-cols-2 lg:grid-cols-3 gap-2"
secondaryAction={ secondaryAction={
<Dropdown.Root> <Dropdown.Root>
<Dropdown.Trigger <Dropdown.Trigger
asChild asChild
hideChevron hideChevron
className="absolute right-4 top-1/2 -translate-y-1/2" className="absolute right-4 top-1/2 -translate-y-1/2"
>
<IconButton variant="borderless" color="secondary">
<DotsVerticalIcon />
</IconButton>
</Dropdown.Trigger>
<Dropdown.Content
menu
PaperProps={{ className: 'w-32' }}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'right',
}}
transformOrigin={{
vertical: 'top',
horizontal: 'right',
}}
>
<Dropdown.Item
onClick={() => handleOpenEditor(environmentVariable)}
> >
<Text className="font-medium">Edit</Text> <IconButton variant="borderless" color="secondary">
</Dropdown.Item> <DotsVerticalIcon />
</IconButton>
</Dropdown.Trigger>
<Divider component="li" /> <Dropdown.Content
menu
<Dropdown.Item PaperProps={{ className: 'w-32' }}
onClick={() => anchorOrigin={{
handleConfirmDelete(environmentVariable) vertical: 'bottom',
} horizontal: 'right',
}}
transformOrigin={{
vertical: 'top',
horizontal: 'right',
}}
> >
<Text <Dropdown.Item
className="font-medium" onClick={() =>
sx={{ handleOpenEditor(environmentVariable)
color: (theme) => theme.palette.error.main, }
}}
> >
Delete <Text className="font-medium">Edit</Text>
</Text> </Dropdown.Item>
</Dropdown.Item>
</Dropdown.Content>
</Dropdown.Root>
}
>
<ListItem.Text className="truncate">
{environmentVariable.name}
</ListItem.Text>
<Text variant="subtitle1" className="lg:col-span-2 truncate"> <Divider component="li" />
{timestamp === '0 seconds ago' ||
timestamp === 'in 0 seconds'
? 'Now'
: timestamp}
</Text>
</ListItem.Root>
<Divider <Dropdown.Item
component="li" onClick={() =>
className={twMerge( handleConfirmDelete(environmentVariable)
index === availableEnvironmentVariables.length - 1 }
? '!mt-4' >
: '!my-4', <Text
)} className="font-medium"
/> sx={{
</Fragment> color: (theme) => theme.palette.error.main,
); }}
})} >
</List> Delete
</Text>
</Dropdown.Item>
</Dropdown.Content>
</Dropdown.Root>
}
>
<ListItem.Text className="truncate">
{environmentVariable.name}
</ListItem.Text>
<Text
variant="subtitle1"
className="lg:col-span-2 truncate"
>
{timestamp === '0 seconds ago' ||
timestamp === 'in 0 seconds'
? 'Now'
: timestamp}
</Text>
</ListItem.Root>
<Divider
component="li"
className={twMerge(
index === availableEnvironmentVariables.length - 1
? '!mt-4'
: '!my-4',
)}
/>
</Fragment>
);
})}
</List>
)}
<Button <Button
className="justify-self-start mx-4" className="justify-self-start mx-4"

View File

@@ -1,6 +1,7 @@
import { useDialog } from '@/components/common/DialogProvider'; import { useDialog } from '@/components/common/DialogProvider';
import InlineCode from '@/components/common/InlineCode'; import InlineCode from '@/components/common/InlineCode';
import SettingsContainer from '@/components/settings/SettingsContainer'; import SettingsContainer from '@/components/settings/SettingsContainer';
import useIsPlatform from '@/hooks/common/useIsPlatform';
import { useAppClient } from '@/hooks/useAppClient'; import { useAppClient } from '@/hooks/useAppClient';
import { useCurrentWorkspaceAndApplication } from '@/hooks/useCurrentWorkspaceAndApplication'; import { useCurrentWorkspaceAndApplication } from '@/hooks/useCurrentWorkspaceAndApplication';
import ActivityIndicator from '@/ui/v2/ActivityIndicator'; import ActivityIndicator from '@/ui/v2/ActivityIndicator';
@@ -13,6 +14,10 @@ import Input from '@/ui/v2/Input';
import List from '@/ui/v2/List'; import List from '@/ui/v2/List';
import { ListItem } from '@/ui/v2/ListItem'; import { ListItem } from '@/ui/v2/ListItem';
import Text from '@/ui/v2/Text'; import Text from '@/ui/v2/Text';
import generateAppServiceUrl, {
defaultLocalBackendSlugs,
defaultRemoteBackendSlugs,
} from '@/utils/common/generateAppServiceUrl';
import { LOCAL_HASURA_URL } from '@/utils/env'; import { LOCAL_HASURA_URL } from '@/utils/env';
import { generateRemoteAppUrl } from '@/utils/helpers'; import { generateRemoteAppUrl } from '@/utils/helpers';
import { useGetAppInjectedVariablesQuery } from '@/utils/__generated__/graphql'; import { useGetAppInjectedVariablesQuery } from '@/utils/__generated__/graphql';
@@ -27,8 +32,9 @@ export default function SystemEnvironmentVariableSettings() {
const { data, loading, error } = useGetAppInjectedVariablesQuery({ const { data, loading, error } = useGetAppInjectedVariablesQuery({
variables: { id: currentApplication?.id }, variables: { id: currentApplication?.id },
}); });
const isPlatform = useIsPlatform();
const appClient = useAppClient({ start: false }); const appClient = useAppClient();
if (loading) { if (loading) {
return ( return (
@@ -71,11 +77,6 @@ export default function SystemEnvironmentVariableSettings() {
}); });
} }
const hasuraUrl =
process.env.NEXT_PUBLIC_ENV === 'dev'
? LOCAL_HASURA_URL
: generateRemoteAppUrl(currentApplication.subdomain);
const systemEnvironmentVariables = [ const systemEnvironmentVariables = [
{ {
key: 'NHOST_BACKEND_URL', key: 'NHOST_BACKEND_URL',
@@ -83,7 +84,19 @@ export default function SystemEnvironmentVariableSettings() {
}, },
{ key: 'NHOST_SUBDOMAIN', value: currentApplication.subdomain }, { key: 'NHOST_SUBDOMAIN', value: currentApplication.subdomain },
{ key: 'NHOST_REGION', value: currentApplication.region.awsName }, { key: 'NHOST_REGION', value: currentApplication.region.awsName },
{ key: 'NHOST_HASURA_URL', value: `${hasuraUrl}/console` }, {
key: 'NHOST_HASURA_URL',
value:
process.env.NEXT_PUBLIC_ENV === 'dev' || !isPlatform
? `${LOCAL_HASURA_URL}/console`
: generateAppServiceUrl(
currentApplication?.subdomain,
currentApplication?.region.awsName,
'hasura',
defaultLocalBackendSlugs,
{ ...defaultRemoteBackendSlugs, hasura: '/console' },
),
},
{ key: 'NHOST_AUTH_URL', value: appClient.auth.url }, { key: 'NHOST_AUTH_URL', value: appClient.auth.url },
{ key: 'NHOST_GRAPHQL_URL', value: appClient.graphql.url }, { key: 'NHOST_GRAPHQL_URL', value: appClient.graphql.url },
{ key: 'NHOST_STORAGE_URL', value: appClient.storage.url }, { key: 'NHOST_STORAGE_URL', value: appClient.storage.url },

View File

@@ -14,7 +14,7 @@ import List from '@/ui/v2/List';
import { ListItem } from '@/ui/v2/ListItem'; import { ListItem } from '@/ui/v2/ListItem';
import Text from '@/ui/v2/Text'; import Text from '@/ui/v2/Text';
import Tooltip from '@/ui/v2/Tooltip'; import Tooltip from '@/ui/v2/Tooltip';
import getPermissionVariables from '@/utils/settings/getPermissionVariablesArray'; import getPermissionVariablesArray from '@/utils/settings/getPermissionVariablesArray';
import { toastStyleProps } from '@/utils/settings/settingsConstants'; import { toastStyleProps } from '@/utils/settings/settingsConstants';
import { import {
useGetAppCustomClaimsQuery, useGetAppCustomClaimsQuery,
@@ -125,7 +125,7 @@ export default function PermissionVariableSettings() {
}); });
} }
const availablePermissionVariables = getPermissionVariables( const availablePermissionVariables = getPermissionVariablesArray(
data?.app?.authJwtCustomClaims, data?.app?.authJwtCustomClaims,
); );

View File

@@ -10,8 +10,8 @@ import IconButton from '@/ui/v2/IconButton';
import CopyIcon from '@/ui/v2/icons/CopyIcon'; import CopyIcon from '@/ui/v2/icons/CopyIcon';
import Input from '@/ui/v2/Input'; import Input from '@/ui/v2/Input';
import InputAdornment from '@/ui/v2/InputAdornment'; import InputAdornment from '@/ui/v2/InputAdornment';
import generateAppServiceUrl from '@/utils/common/generateAppServiceUrl';
import { copy } from '@/utils/copy'; import { copy } from '@/utils/copy';
import { generateRemoteAppUrl } from '@/utils/helpers';
import { toastStyleProps } from '@/utils/settings/settingsConstants'; import { toastStyleProps } from '@/utils/settings/settingsConstants';
import { FormProvider, useForm } from 'react-hook-form'; import { FormProvider, useForm } from 'react-hook-form';
import { toast } from 'react-hot-toast'; import { toast } from 'react-hot-toast';
@@ -23,7 +23,6 @@ export interface AppleProviderFormValues {
authAppleKeyId: string; authAppleKeyId: string;
authAppleClientId: string; authAppleClientId: string;
authApplePrivateKey: string; authApplePrivateKey: string;
authAppleScope: string;
} }
export default function AppleProviderSettings() { export default function AppleProviderSettings() {
@@ -38,7 +37,6 @@ export default function AppleProviderSettings() {
authAppleKeyId, authAppleKeyId,
authAppleClientId, authAppleClientId,
authApplePrivateKey, authApplePrivateKey,
authAppleScope,
}, },
}, },
loading, loading,
@@ -57,7 +55,6 @@ export default function AppleProviderSettings() {
authAppleKeyId, authAppleKeyId,
authAppleClientId, authAppleClientId,
authApplePrivateKey, authApplePrivateKey,
authAppleScope,
authAppleEnabled, authAppleEnabled,
}, },
}); });
@@ -83,9 +80,7 @@ export default function AppleProviderSettings() {
const updateAppMutation = updateApp({ const updateAppMutation = updateApp({
variables: { variables: {
id: currentApplication.id, id: currentApplication.id,
app: { app: values,
...values,
},
}, },
}); });
@@ -96,7 +91,7 @@ export default function AppleProviderSettings() {
success: `Apple settings have been updated successfully.`, success: `Apple settings have been updated successfully.`,
error: `An error occurred while trying to update the project's Apple settings.`, error: `An error occurred while trying to update the project's Apple settings.`,
}, },
{ ...toastStyleProps }, toastStyleProps,
); );
form.reset(values); form.reset(values);
@@ -108,9 +103,11 @@ export default function AppleProviderSettings() {
<SettingsContainer <SettingsContainer
title="Apple" title="Apple"
description="Allow users to sign in with Apple." description="Allow users to sign in with Apple."
primaryActionButtonProps={{ slotProps={{
disabled: !formState.isValid || !formState.isDirty, submitButton: {
loading: formState.isSubmitting, disabled: !formState.isValid || !formState.isDirty,
loading: formState.isSubmitting,
},
}} }}
docsLink="https://docs.nhost.io/authentication/sign-in-with-apple" docsLink="https://docs.nhost.io/authentication/sign-in-with-apple"
docsTitle="how to sign in users with Apple" docsTitle="how to sign in users with Apple"
@@ -134,9 +131,9 @@ export default function AppleProviderSettings() {
hideEmptyHelperText hideEmptyHelperText
/> />
<Input <Input
{...register('authAppleScope')} {...register('authAppleClientId')}
name="authAppleScope" name="authAppleClientId"
id="authAppleScope" id="authAppleClientId"
label="Service ID" label="Service ID"
placeholder="Apple Service ID" placeholder="Apple Service ID"
className="col-span-1" className="col-span-1"
@@ -168,9 +165,11 @@ export default function AppleProviderSettings() {
<Input <Input
name="redirectUrl" name="redirectUrl"
id="redirectUrl" id="redirectUrl"
defaultValue={`${generateRemoteAppUrl( defaultValue={`${generateAppServiceUrl(
currentApplication.subdomain, currentApplication.subdomain,
)}/v1/auth/signin/provider/apple/callback`} currentApplication.region.awsName,
'auth',
)}/signin/provider/apple/callback`}
className="col-span-2" className="col-span-2"
fullWidth fullWidth
hideEmptyHelperText hideEmptyHelperText
@@ -185,9 +184,11 @@ export default function AppleProviderSettings() {
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
copy( copy(
`${generateRemoteAppUrl( `${generateAppServiceUrl(
currentApplication.subdomain, currentApplication.subdomain,
)}/v1/auth/signin/provider/apple/callback`, currentApplication.region.awsName,
'auth',
)}/signin/provider/apple/callback`,
'Redirect URL', 'Redirect URL',
); );
}} }}

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