Compare commits
424 Commits
@nhost/rea
...
@nhost/das
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b9316bb668 | ||
|
|
bd4d0c2708 | ||
|
|
d81c52209b | ||
|
|
72744b3082 | ||
|
|
ff4efe2712 | ||
|
|
2982b90469 | ||
|
|
428a5df038 | ||
|
|
f79bf784b5 | ||
|
|
3b7449ac08 | ||
|
|
37bbfdb7ae | ||
|
|
eb570d2d09 | ||
|
|
c8c2a10b2d | ||
|
|
92c79eb2fb | ||
|
|
e70b45498d | ||
|
|
2e1ecfa731 | ||
|
|
8d323a7762 | ||
|
|
8aa0ff936a | ||
|
|
c6806d60c7 | ||
|
|
a13eb25ebc | ||
|
|
228d8a0686 | ||
|
|
0de1bc7ce3 | ||
|
|
6a94cad04b | ||
|
|
8643d25cc8 | ||
|
|
e820f11dda | ||
|
|
3555ab2b71 | ||
|
|
6e41d58131 | ||
|
|
6cf3beae1c | ||
|
|
022b76e784 | ||
|
|
2fbe88f806 | ||
|
|
9457bc32ca | ||
|
|
3de2639ae9 | ||
|
|
c43e549224 | ||
|
|
fc6fe5007b | ||
|
|
829febf33b | ||
|
|
ae99ba14b9 | ||
|
|
a158dc3a17 | ||
|
|
8420550990 | ||
|
|
156667cdbd | ||
|
|
7d388a8c91 | ||
|
|
d32a2fceae | ||
|
|
d690eb86bb | ||
|
|
d91271cce1 | ||
|
|
1e74a2da85 | ||
|
|
bc8837b961 | ||
|
|
78fdad8404 | ||
|
|
2e8a72d445 | ||
|
|
ce1ae32772 | ||
|
|
b51455d324 | ||
|
|
28a305d9be | ||
|
|
e23bf4500d | ||
|
|
d0457fe5c3 | ||
|
|
766d1e1c5a | ||
|
|
44d460cd01 | ||
|
|
adf934c871 | ||
|
|
0a963486e2 | ||
|
|
227d1704f2 | ||
|
|
2baef92988 | ||
|
|
2a10da128d | ||
|
|
62a51c9fc7 | ||
|
|
58977b173b | ||
|
|
b5e5dcf6de | ||
|
|
157e1b74b8 | ||
|
|
b3a475c60f | ||
|
|
3d62871db1 | ||
|
|
4f0368b95f | ||
|
|
0385093111 | ||
|
|
463cb50c27 | ||
|
|
a50174a0a1 | ||
|
|
21cbe7487e | ||
|
|
6e4b34126e | ||
|
|
fd3a1a44ef | ||
|
|
66e6021dc0 | ||
|
|
57fdba70e0 | ||
|
|
676c11f814 | ||
|
|
d8442a290b | ||
|
|
0db333353b | ||
|
|
7ea8120723 | ||
|
|
64a8f41d03 | ||
|
|
8e12ded94b | ||
|
|
564ce1ac2d | ||
|
|
b024817eb5 | ||
|
|
24f98630fd | ||
|
|
c1b024cf53 | ||
|
|
dbacbf140b | ||
|
|
eda9e57583 | ||
|
|
0a9af5075c | ||
|
|
f92d9d1fd2 | ||
|
|
15168539d8 | ||
|
|
0d74217a4c | ||
|
|
9721527324 | ||
|
|
fd4d024bfc | ||
|
|
c994c8f05b | ||
|
|
4c00a796eb | ||
|
|
2d3a77af76 | ||
|
|
ef05d69889 | ||
|
|
9b1d0f7a5b | ||
|
|
07abea4c16 | ||
|
|
8733961026 | ||
|
|
dfa8776b2b | ||
|
|
1b9f15cb67 | ||
|
|
b683615269 | ||
|
|
3dc97f17ae | ||
|
|
6d2963ffa7 | ||
|
|
d1ec8c0781 | ||
|
|
8b205e9c08 | ||
|
|
e2792cd453 | ||
|
|
a60ca2f6f5 | ||
|
|
14a2ead79f | ||
|
|
b625a6b4d4 | ||
|
|
fd12aa0a8d | ||
|
|
8871267b91 | ||
|
|
e3001ba4a5 | ||
|
|
1133b76a7e | ||
|
|
9b8a19a316 | ||
|
|
26b8519add | ||
|
|
f372b167d9 | ||
|
|
fbaea49b28 | ||
|
|
0b4a2041e2 | ||
|
|
444206b0f7 | ||
|
|
624ef79110 | ||
|
|
711ef45815 | ||
|
|
21e13db05a | ||
|
|
a1862b80ba | ||
|
|
42e8d102e9 | ||
|
|
aa3c629892 | ||
|
|
f016afed5d | ||
|
|
9d4ee99553 | ||
|
|
6cbe1f662f | ||
|
|
abd9c88e5a | ||
|
|
27e5ef8f5a | ||
|
|
788090e917 | ||
|
|
f16433ae67 | ||
|
|
33e3bba700 | ||
|
|
517bb6930a | ||
|
|
4b17964e8d | ||
|
|
ebc016377b | ||
|
|
eae670f4d1 | ||
|
|
c642649853 | ||
|
|
095f2c73b9 | ||
|
|
42753d2b1f | ||
|
|
328c0a6600 | ||
|
|
93101430c8 | ||
|
|
c14738b8c1 | ||
|
|
683ef4f2fe | ||
|
|
528f02874c | ||
|
|
5f72ba57d3 | ||
|
|
e71e3af530 | ||
|
|
aa05cd4b61 | ||
|
|
bd87a36c6e | ||
|
|
9c88947b86 | ||
|
|
1b03ce7259 | ||
|
|
de1137a876 | ||
|
|
770c4d1801 | ||
|
|
60e25b3425 | ||
|
|
f4d2a305a5 | ||
|
|
01eeef9de7 | ||
|
|
f395375cd0 | ||
|
|
ae3599d2b4 | ||
|
|
9a3c782fcb | ||
|
|
28c1633695 | ||
|
|
66822f8673 | ||
|
|
86b82bf5cf | ||
|
|
f77454a848 | ||
|
|
3426000edf | ||
|
|
7bd9da909c | ||
|
|
fcb84bfb3d | ||
|
|
991e4c0cc5 | ||
|
|
a7f03bc6ce | ||
|
|
5684fdbf0b | ||
|
|
39980e6c54 | ||
|
|
5b959cce0e | ||
|
|
117098d8da | ||
|
|
1fce4ca93d | ||
|
|
6a74a97cd0 | ||
|
|
7446fb30db | ||
|
|
291185e609 | ||
|
|
b1a2dd6ab3 | ||
|
|
d3f965048a | ||
|
|
9acecfd638 | ||
|
|
134773be86 | ||
|
|
88a4983f2e | ||
|
|
7526f573c3 | ||
|
|
9b0d4dde50 | ||
|
|
9934514dde | ||
|
|
c7084d1759 | ||
|
|
f0a122d39b | ||
|
|
34f1795e40 | ||
|
|
2032494b27 | ||
|
|
b5dce47370 | ||
|
|
60f1a03d33 | ||
|
|
881b2d752c | ||
|
|
db293808f3 | ||
|
|
7414b39f48 | ||
|
|
7ebc555e4c | ||
|
|
830e627da1 | ||
|
|
176e3d5ea6 | ||
|
|
759aa9c221 | ||
|
|
3acfd032bd | ||
|
|
b77711000a | ||
|
|
79fa77f28f | ||
|
|
bd6fe2f85f | ||
|
|
acb8a4f187 | ||
|
|
06d6ae0e86 | ||
|
|
1777c147b2 | ||
|
|
9d784b82c8 | ||
|
|
15d84a1966 | ||
|
|
8a1a1e06aa | ||
|
|
eb1d7137cf | ||
|
|
cf55e8e111 | ||
|
|
b47c797d73 | ||
|
|
dafc4b2635 | ||
|
|
97cfb5c514 | ||
|
|
0ccd9c82b1 | ||
|
|
f0be842325 | ||
|
|
30a0fd1698 | ||
|
|
ce45f3b707 | ||
|
|
8b9df13725 | ||
|
|
8f31258d11 | ||
|
|
877f857010 | ||
|
|
5773521b03 | ||
|
|
549dd83f3e | ||
|
|
e72c19c9da | ||
|
|
70d5dcf170 | ||
|
|
c439174821 | ||
|
|
2f7b2532ea | ||
|
|
faa21af7fb | ||
|
|
a5fdc46a57 | ||
|
|
04e8728753 | ||
|
|
34cf922643 | ||
|
|
0dd1883815 | ||
|
|
01ba429007 | ||
|
|
fdf5b3035c | ||
|
|
ae52ca6303 | ||
|
|
428222bf5f | ||
|
|
090ee51854 | ||
|
|
766c8431f1 | ||
|
|
4c62617472 | ||
|
|
01f3cbb07a | ||
|
|
62f5d8b69e | ||
|
|
88e2c05afc | ||
|
|
f1edbfdbf1 | ||
|
|
a9943eef3f | ||
|
|
431e61a684 | ||
|
|
87e7f7d4f5 | ||
|
|
5aab6b1896 | ||
|
|
e4e216da6d | ||
|
|
cd8ccdf59a | ||
|
|
9421dda73d | ||
|
|
f13dc75993 | ||
|
|
0a8b42d371 | ||
|
|
bd59d61e94 | ||
|
|
349622fca2 | ||
|
|
dfe8588a94 | ||
|
|
0167df5534 | ||
|
|
7c790b3afe | ||
|
|
1b3a8a1638 | ||
|
|
99edd0129a | ||
|
|
cfb759c34a | ||
|
|
6c5a645876 | ||
|
|
db459b09ec | ||
|
|
e4fa63a571 | ||
|
|
4ce552c8eb | ||
|
|
b847c6d003 | ||
|
|
daab7d8eeb | ||
|
|
1b4f074dfd | ||
|
|
cca7075721 | ||
|
|
4c1b96ccc6 | ||
|
|
809a2d35f8 | ||
|
|
084b7ce6d2 | ||
|
|
e17ec7fce7 | ||
|
|
241175b158 | ||
|
|
9b209ef419 | ||
|
|
a86d17c81f | ||
|
|
cc047b719a | ||
|
|
889b071134 | ||
|
|
51ceaf2696 | ||
|
|
eee3f5723e | ||
|
|
490b77cde4 | ||
|
|
7fea29a8b4 | ||
|
|
1a34e011ad | ||
|
|
395839f449 | ||
|
|
399009d66a | ||
|
|
12eb236c4a | ||
|
|
2218e5cd5b | ||
|
|
2dcf1b38c6 | ||
|
|
412520ac10 | ||
|
|
ad49c92879 | ||
|
|
13dd57eeb4 | ||
|
|
1345741b11 | ||
|
|
3cb63a6da9 | ||
|
|
985f648204 | ||
|
|
bbc3aa8896 | ||
|
|
3d51de4a60 | ||
|
|
b46afa37c6 | ||
|
|
8ad6358d76 | ||
|
|
f73f8366e4 | ||
|
|
511615f176 | ||
|
|
1198c201f1 | ||
|
|
cab803e5b7 | ||
|
|
59fea65eb6 | ||
|
|
f9b81a2ae9 | ||
|
|
23bac2d29c | ||
|
|
78739f77b1 | ||
|
|
dff0894f37 | ||
|
|
80f3645d57 | ||
|
|
7ddb9a654e | ||
|
|
71f3be15d8 | ||
|
|
96a9070836 | ||
|
|
329e5a91b9 | ||
|
|
6d559d6e23 | ||
|
|
f4f1450d06 | ||
|
|
a1eea9df7d | ||
|
|
26f2b665e6 | ||
|
|
8989202692 | ||
|
|
2ae463f11f | ||
|
|
18a786e880 | ||
|
|
c88fbe1e17 | ||
|
|
78c7109c46 | ||
|
|
c7b968868a | ||
|
|
77a3473166 | ||
|
|
224a5cc805 | ||
|
|
59125b3c77 | ||
|
|
91e2affa6f | ||
|
|
a8d747976b | ||
|
|
5b69e3efd8 | ||
|
|
c9a444d048 | ||
|
|
fc16fd5452 | ||
|
|
2eeac45718 | ||
|
|
afc9de7994 | ||
|
|
fe8ca8aba6 | ||
|
|
086ee46b08 | ||
|
|
37c1c18b43 | ||
|
|
67078b9a72 | ||
|
|
203bc97f51 | ||
|
|
b24af44aac | ||
|
|
cdaa6d4e73 | ||
|
|
09e2c8f5c7 | ||
|
|
4581677830 | ||
|
|
7bfa6c9f93 | ||
|
|
80ef430d70 | ||
|
|
bad8af0fd1 | ||
|
|
69caa34c43 | ||
|
|
1d898e2893 | ||
|
|
e87621cbde | ||
|
|
0d6fc42158 | ||
|
|
f6fbee6b13 | ||
|
|
1230b72222 | ||
|
|
6cc7704555 | ||
|
|
c0954dec09 | ||
|
|
6c25480a7a | ||
|
|
da03bf390c | ||
|
|
3b513be9f2 | ||
|
|
e450e9d636 | ||
|
|
ed1ee10879 | ||
|
|
a6120bf366 | ||
|
|
349aac369e | ||
|
|
5a84362c80 | ||
|
|
b23dc058a6 | ||
|
|
ad0dda7493 | ||
|
|
4063507d59 | ||
|
|
f59a77b1c8 | ||
|
|
30686bc4ce | ||
|
|
0c4ac8d368 | ||
|
|
85439307a9 | ||
|
|
0d8baa4065 | ||
|
|
7da0e5e256 | ||
|
|
4bca94425e | ||
|
|
9f948385c0 | ||
|
|
294c504b61 | ||
|
|
1469ec2969 | ||
|
|
8229101efe | ||
|
|
afad1778f8 | ||
|
|
28fc7b84c7 | ||
|
|
3f478a4e3c | ||
|
|
aa54666941 | ||
|
|
20fb69faba | ||
|
|
1caeb2a548 | ||
|
|
6356c5a2c8 | ||
|
|
6ec1dd3248 | ||
|
|
8a4b5031dc | ||
|
|
4790fee41f | ||
|
|
0a8033812d | ||
|
|
2b56ffc29e | ||
|
|
aa9b926cd7 | ||
|
|
575404ad62 | ||
|
|
3f6dfc7bcd | ||
|
|
682e64d7a3 | ||
|
|
30cee4f86c | ||
|
|
29dcc8c63e | ||
|
|
d926f15676 | ||
|
|
726c33d1b2 | ||
|
|
11b9cfbc0d | ||
|
|
d4a0aad2dd | ||
|
|
1030813279 | ||
|
|
917a14aa40 | ||
|
|
6381d1b095 | ||
|
|
8dbdc0bf50 | ||
|
|
8c072a4c6e | ||
|
|
fe341519f7 | ||
|
|
ea09384064 | ||
|
|
49b9972885 | ||
|
|
98c541ee52 | ||
|
|
79aaa91e67 | ||
|
|
df4d24320a | ||
|
|
757c888656 | ||
|
|
7c13eb5f9b | ||
|
|
a84608e086 | ||
|
|
e43c079b9c | ||
|
|
3f396a9ebb | ||
|
|
6ed605beb8 | ||
|
|
edd223d29c | ||
|
|
baa3ef794e | ||
|
|
da7ffbe523 | ||
|
|
15a985e079 | ||
|
|
8ff00a4258 | ||
|
|
7e27d7c0a1 | ||
|
|
49f9b8372a | ||
|
|
3ca70554c8 | ||
|
|
077b200510 | ||
|
|
b17e8d6f3c | ||
|
|
12e2855f01 | ||
|
|
c1080d9e63 | ||
|
|
e4972b8307 | ||
|
|
2f220db84a |
@@ -14,7 +14,7 @@ runs:
|
||||
steps:
|
||||
- uses: pnpm/action-setup@v2.2.4
|
||||
with:
|
||||
version: 7.17.0
|
||||
version: 8.6.2
|
||||
run_install: false
|
||||
- name: Get pnpm cache directory
|
||||
id: pnpm-cache-dir
|
||||
@@ -26,7 +26,7 @@ runs:
|
||||
path: ${{ steps.pnpm-cache-dir.outputs.dir }}
|
||||
key: ${{ runner.os }}-node-${{ hashFiles('pnpm-lock.yaml') }}
|
||||
restore-keys: ${{ runner.os }}-node-
|
||||
- name: Use Node.js 16
|
||||
- name: Use Node.js v16
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
|
||||
16
.github/actions/nhost-cli/action.yaml
vendored
16
.github/actions/nhost-cli/action.yaml
vendored
@@ -49,21 +49,9 @@ runs:
|
||||
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
|
||||
cp .secrets.example .secrets
|
||||
nhost up
|
||||
- name: Log on failure
|
||||
if: steps.wait.outcome == 'failure'
|
||||
shell: bash
|
||||
|
||||
2
.github/workflows/ci.yaml
vendored
2
.github/workflows/ci.yaml
vendored
@@ -43,7 +43,7 @@ jobs:
|
||||
BUILD: 'all'
|
||||
- name: Check if the pnpm lockfile changed
|
||||
id: changed-lockfile
|
||||
uses: tj-actions/changed-files@v35
|
||||
uses: tj-actions/changed-files@v36
|
||||
with:
|
||||
files: pnpm-lock.yaml
|
||||
# * Determine a pnpm filter argument for packages that have been modified.
|
||||
|
||||
36
.github/workflows/test-nhost-cli-action.yaml
vendored
36
.github/workflows/test-nhost-cli-action.yaml
vendored
@@ -28,7 +28,7 @@ jobs:
|
||||
path: packages/nhost-js
|
||||
start: true
|
||||
- name: should be running
|
||||
run: curl -sSf 'http://localhost:9695' > /dev/null
|
||||
run: curl -sSf 'https://local.hasura.nhost.run' > /dev/null
|
||||
|
||||
stop:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -48,28 +48,6 @@ jobs:
|
||||
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:
|
||||
@@ -79,14 +57,10 @@ jobs:
|
||||
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"}'
|
||||
VERSION=$(curl -sSf 'https://local.auth.nhost.run/v1/version')
|
||||
EXPECTED_VERSION='{"version":"v0.20.1"}'
|
||||
if [ "$VERSION" != "$EXPECTED_VERSION" ]; then
|
||||
echo "Expected version $EXPECTED_VERSION but got $VERSION"
|
||||
exit 1
|
||||
@@ -99,6 +73,6 @@ jobs:
|
||||
- name: Install the Nhost CLI
|
||||
uses: ./.github/actions/nhost-cli
|
||||
with:
|
||||
version: v0.8.10
|
||||
version: v1.0.1
|
||||
- name: should find the correct version
|
||||
run: nhost version | head -n 1 | grep v0.8.10 || exit 1
|
||||
run: nhost --version | head -n 1 | grep v1.0.1 || exit 1
|
||||
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -26,6 +26,7 @@ tmp/
|
||||
.pnpm-store
|
||||
.turbo
|
||||
.env
|
||||
.secrets
|
||||
out/
|
||||
|
||||
# Custom
|
||||
@@ -61,3 +62,7 @@ todo.md
|
||||
|
||||
# Nhost CLI data
|
||||
.nhost
|
||||
|
||||
# Nix
|
||||
.envrc
|
||||
.direnv/
|
||||
|
||||
@@ -36,6 +36,7 @@ export default defineConfig({
|
||||
}
|
||||
},
|
||||
build: {
|
||||
target: 'es2019',
|
||||
sourcemap: true,
|
||||
lib: {
|
||||
entry,
|
||||
|
||||
@@ -28,7 +28,6 @@ module.exports = {
|
||||
'import/prefer-default-export': 'off',
|
||||
'import/no-extraneous-dependencies': ['error', { devDependencies: true }],
|
||||
curly: ['error', 'all'],
|
||||
'no-restricted-exports': 'off',
|
||||
'no-undef': 'off',
|
||||
'no-use-before-define': 'off',
|
||||
'@typescript-eslint/no-use-before-define': [
|
||||
@@ -67,17 +66,7 @@ module.exports = {
|
||||
{
|
||||
group: ['..*'],
|
||||
message:
|
||||
'Please use absolute imports instead. (e.g: @/ui/, @/hooks/, etc.)',
|
||||
},
|
||||
{
|
||||
group: ['@/components/ui', '@/components/ui/*'],
|
||||
message:
|
||||
'Please use shorthand imports instead. (e.g: @/ui/ActivityIndicator, @/ui/Button, etc.)',
|
||||
},
|
||||
{
|
||||
group: ['@/components/ui/v2*'],
|
||||
message:
|
||||
'Please use shorthand imports instead. (e.g: @/ui/v2/ActivityIndicator, @/ui/v2/Button, etc.)',
|
||||
'Please use absolute imports instead. (e.g: @/components/, @/hooks/, etc.)',
|
||||
},
|
||||
{
|
||||
group: ['@testing-library/react*'],
|
||||
|
||||
@@ -1,5 +1,176 @@
|
||||
# @nhost/dashboard
|
||||
|
||||
## 0.17.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- bd4d0c270: chore(dashboard):add postgres 14.6-20230613-1 to the version selector
|
||||
|
||||
## 0.17.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- c8c2a10b2: fix(database): don't break the password reset flow
|
||||
- e70b45498: chore(deps): bump `@types/react` to `v18.2.12` and `@types/react-dom` to `v18.2.5`
|
||||
|
||||
## 0.17.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 842055099: chore(deps): bump `turbo` to `v1.10.3` and `pnpm` to `v8.6.2`
|
||||
- fd12aa0a8: chore(projects): remove the postgres password input from the project creation screen
|
||||
- 022b76e78: chore(deps): bump `@types/react` to `v18.2.11`
|
||||
- 3555ab2b7: chore(deps): bump `vitest` monorepo to `v0.32.0`
|
||||
- c43e54922: feat(backups): add download button to backups
|
||||
|
||||
## 0.17.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- d0457fe5c: feat(settings): improve the dashboard and config parity
|
||||
- @nhost/react-apollo@5.0.26
|
||||
- @nhost/nextjs@1.13.28
|
||||
|
||||
## 0.17.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 4f0368b95: fix(account): don't break account settings page
|
||||
|
||||
## 0.17.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 64a8f41d0: chore(resources): lower the maximum allowed resources per service
|
||||
|
||||
## 0.17.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/react-apollo@5.0.25
|
||||
- @nhost/nextjs@1.13.27
|
||||
|
||||
## 0.17.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 9b1d0f7a5: fix(deployments): use correct timestamp for deployment details
|
||||
- 6d2963ffa: chore(deps): bump `@types/react` to `v18.2.8`
|
||||
- 8871267b9: chore(deps): downgrade `pnpm` to `v8.5.1` because of no Turborepo support
|
||||
|
||||
## 0.17.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 01eeef9de: chore(misc): under the hood improvements
|
||||
- 21e13db05: chore(deps): bump `@types/react` to `v18.2.7` and `turbo` to `v1.10.1`
|
||||
- f16433ae6: chore(secrets): allow empty secrets and environment variables
|
||||
- aa3c62989: chore(cli): bump Nhost CLI version to v1.0
|
||||
- @nhost/react-apollo@5.0.24
|
||||
- @nhost/nextjs@1.13.26
|
||||
|
||||
## 0.17.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 88a4983f: chore(misc): under the hood improvements
|
||||
|
||||
## 0.17.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 9b0d4dde: feat(secrets): enable secrets
|
||||
|
||||
## 0.17.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 15d84a19: Add postgres 14.6-20230525
|
||||
|
||||
## 0.16.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 4c626174: chore: updated import paths, improved directory structure
|
||||
- cc047b71: chore(deps): bump `@fontsource` monorepo to `v5.0.0`
|
||||
- 99edd012: feat(account): add support for personal access tokens
|
||||
|
||||
## 0.16.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 78c7109c: feat(settings): allow selecting service versions
|
||||
|
||||
## 0.16.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 399009d6: fix(gql): don't enter an infinite loop when fetching remote app data
|
||||
- 329e5a91: fix(deployments): use the same sorting of deployments everywhere
|
||||
- 6d559d6e: chore(settings): add under the hood improvements to the settings page
|
||||
- 12eb236c: chore(deps): bump `prettier-plugin-tailwindcss` to `v0.3.0`
|
||||
- f9b81a2a: chore(deps): bump `turbo` to `v1.9.8`
|
||||
- 1345741b: fix(projects): don't redirect to 404 on project creation
|
||||
- Updated dependencies [7fea29a8]
|
||||
- @nhost/react-apollo@5.0.23
|
||||
- @nhost/nextjs@1.13.25
|
||||
|
||||
## 0.16.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 1230b722: fix(projects): don't redirect to 404 on when the project is renamed
|
||||
- @nhost/react-apollo@5.0.22
|
||||
- @nhost/nextjs@1.13.24
|
||||
|
||||
## 0.16.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [da03bf39]
|
||||
- @nhost/react-apollo@5.0.21
|
||||
- @nhost/nextjs@1.13.23
|
||||
|
||||
## 0.16.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 349aac36: fix(settings): use region domain when constructing the postgres connection string
|
||||
|
||||
## 0.16.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 20fb69fa: chore(projects): change the way how API URLs are constructed
|
||||
|
||||
## 0.16.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 49f9b837: chore(docker): bump `pnpm` to `v8.4.0` and `turbo` to `v1.9.3`
|
||||
- 3f478a4e: chore(deps): bump `vitest` to `v0.31.0`, `@types/react` to `v18.2.6` and `@types/react-dom` to `v18.2.4`
|
||||
|
||||
## 0.16.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- d926f156: fix(projects): redirect to 404 when an invalid project is opened
|
||||
- 49b99728: fix(projects): disable features for non-owner members of workspaces
|
||||
|
||||
## 0.16.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 12e2855f: chore(deps): bump `jsdom` to v22
|
||||
- e4972b83: feat(metrics): add Grafana page
|
||||
|
||||
## 0.16.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 3f396a9e: fix(projects): unpause after upgrading a paused project to pro
|
||||
- 3f396a9e: fix(projects): don't redirect to 404 page after project creation
|
||||
|
||||
## 0.16.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -3,7 +3,7 @@ RUN apk add --no-cache libc6-compat
|
||||
RUN apk update
|
||||
WORKDIR /app
|
||||
|
||||
RUN yarn global add turbo@1.8.6
|
||||
RUN yarn global add turbo@1.10.3
|
||||
COPY . .
|
||||
RUN turbo prune --scope="@nhost/dashboard" --docker
|
||||
|
||||
@@ -29,7 +29,7 @@ ENV NEXT_PUBLIC_NHOST_HASURA_CONSOLE_URL __NEXT_PUBLIC_NHOST_HASURA_CONSOLE_URL_
|
||||
ENV NEXT_PUBLIC_NHOST_HASURA_MIGRATIONS_API_URL __NEXT_PUBLIC_NHOST_HASURA_MIGRATIONS_API_URL__
|
||||
ENV NEXT_PUBLIC_NHOST_HASURA_API_URL __NEXT_PUBLIC_NHOST_HASURA_API_URL__
|
||||
|
||||
RUN yarn global add pnpm@7.17.0
|
||||
RUN yarn global add pnpm@8.6.2
|
||||
COPY .gitignore .gitignore
|
||||
COPY --from=pruner /app/out/json/ .
|
||||
COPY --from=pruner /app/out/pnpm-*.yaml .
|
||||
|
||||
@@ -13,7 +13,7 @@ pnpm install
|
||||
|
||||
Depending on the environment you wish to target you can configure environment variables in `.env.<target_environment>.local`.
|
||||
|
||||
- `.env.development`: This file is used if you run `nhost dev`
|
||||
- `.env.development`: This file is used if you run `nhost up`
|
||||
- `.env.development.local`: This file is used if you run `pnpm dev`. It takes precedence over `.env.local` if available.
|
||||
- `.env.production.local`: This file is used if you run `pnpm build`. It takes precedence over `.env.local` if available.
|
||||
- `.env.local`: This file is used if you run either `pnpm dev` or `pnpm build`.
|
||||
@@ -27,7 +27,7 @@ You can connect the Nhost Dashboard to your **locally running** Nhost backend in
|
||||
First, you need to run the following command to start your backend locally:
|
||||
|
||||
```bash
|
||||
cd <your_nhost_project> && nhost dev
|
||||
cd <your_nhost_project> && nhost up
|
||||
```
|
||||
|
||||
You can connect the Nhost Dashboard to your locally running backend by setting the following environment variables in `.env.development.local`:
|
||||
@@ -92,7 +92,7 @@ pnpm storybook
|
||||
| Name | Description |
|
||||
| -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `react/react-in-jsx-scope` | Disabled because we don't need to import `React` anymore. |
|
||||
| `react/jsx-props-no-spreading` | Disabled because we heavily rely on props spreading in our `@/ui/v2` components. |
|
||||
| `react/jsx-props-no-spreading` | Disabled because we heavily rely on props spreading in our `@/components/ui/v2` components. |
|
||||
| `react/require-default-props` | Disabled because we use TypeScript instead of PropTypes. |
|
||||
| `react-hooks/exhaustive-deps` | Because we already had several rule violations when proper ESLint rules were introduced, we changed this rule to a warning. |
|
||||
| `import/extensions` | JS / TS files should be imported without file extensions. |
|
||||
@@ -101,7 +101,6 @@ pnpm storybook
|
||||
| `import/order` | Until we have a better auto-formatter, we disable this rule. |
|
||||
| `import/no-extraneous-dependencies` | `devDependencies` should be excluded from the list of disallowed imports. |
|
||||
| `curly` | By default it only enforces curly braces for multi-line blocks, but it should be enforced for single-line blocks as well. |
|
||||
| `no-restricted-exports` | `export { default } from './module'` is used heavily in `@/ui/v2` which is a restricted export by default. |
|
||||
| `@typescript-eslint/no-use-before-define` | Order of type references should be ignored. |
|
||||
| `no-undef` | [Official TypeScript ESLint packages](https://github.com/typescript-eslint/typescript-eslint/issues/4671#issuecomment-1065948494) are turning off this rule. |
|
||||
| `@typescript-eslint/no-shadow` | TypeScript specific implementation of `no-shadow`. |
|
||||
|
||||
51
dashboard/e2e/account/pat/manage-pat.test.ts
Normal file
51
dashboard/e2e/account/pat/manage-pat.test.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { faker } from '@faker-js/faker';
|
||||
import type { Page } from '@playwright/test';
|
||||
import { expect, test } from '@playwright/test';
|
||||
|
||||
let page: Page;
|
||||
|
||||
test.beforeAll(async ({ browser }) => {
|
||||
page = await browser.newPage();
|
||||
});
|
||||
|
||||
test.beforeEach(async () => {
|
||||
await page.goto('/');
|
||||
});
|
||||
|
||||
test.afterAll(async () => {
|
||||
await page.close();
|
||||
});
|
||||
|
||||
test('should be able to create then delete a personal access token', async () => {
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.getByRole('banner').getByRole('button').last().click();
|
||||
await page.getByRole('link', { name: /account settings/i }).click();
|
||||
await page
|
||||
.getByRole('button', { name: /create personal access token/i })
|
||||
.click();
|
||||
|
||||
const patName = faker.lorem.slug(3);
|
||||
|
||||
await page.getByRole('textbox', { name: /name/i }).fill(patName);
|
||||
await page.getByRole('button', { name: /expiration/i }).click();
|
||||
await page.getByRole('option', { name: /7 days/i }).click();
|
||||
await page.getByRole('button', { name: /create/i }).click();
|
||||
|
||||
await expect(
|
||||
page.getByText(
|
||||
/this token will not be shown again. make sure to copy it now./i,
|
||||
),
|
||||
).toBeVisible();
|
||||
|
||||
await page.getByRole('button', { name: /close/i }).click();
|
||||
|
||||
await expect(page.getByText(patName)).toBeVisible();
|
||||
|
||||
await page
|
||||
.getByRole('button', { name: `More options for ${patName}`, exact: true })
|
||||
.click();
|
||||
await page.getByRole('menuitem', { name: /delete/i }).click();
|
||||
await page.getByRole('button', { name: /delete/i }).click();
|
||||
|
||||
await expect(page.getByText(patName)).not.toBeVisible();
|
||||
});
|
||||
@@ -30,7 +30,7 @@ test('should show a sidebar with menu items', async () => {
|
||||
const navLocator = page.getByRole('navigation', { name: /main navigation/i });
|
||||
await expect(navLocator).toBeVisible();
|
||||
await expect(navLocator.getByRole('list').getByRole('listitem')).toHaveCount(
|
||||
10,
|
||||
11,
|
||||
);
|
||||
await expect(
|
||||
navLocator.getByRole('link', { name: /overview/i }),
|
||||
@@ -53,6 +53,9 @@ test('should show a sidebar with menu items', async () => {
|
||||
navLocator.getByRole('link', { name: /backups/i }),
|
||||
).toBeVisible();
|
||||
await expect(navLocator.getByRole('link', { name: /logs/i })).toBeVisible();
|
||||
await expect(
|
||||
navLocator.getByRole('link', { name: /metrics/i }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
navLocator.getByRole('link', { name: /settings/i }),
|
||||
).toBeVisible();
|
||||
|
||||
@@ -46,7 +46,7 @@ async function globalTeardown() {
|
||||
await hasuraPage.locator('a', { hasText: /data/i }).click();
|
||||
await hasuraPage.getByRole('link', { name: /sql/i }).click();
|
||||
|
||||
await hasuraPage.getByRole('textbox').fill(`
|
||||
await hasuraPage.locator('#raw_sql > textarea').fill(`
|
||||
DO $$ DECLARE
|
||||
tablename text;
|
||||
BEGIN
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/dashboard",
|
||||
"version": "0.16.3",
|
||||
"version": "0.17.11",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
@@ -11,11 +11,11 @@
|
||||
"lint": "next lint --max-warnings 0",
|
||||
"test": "vitest",
|
||||
"codegen": "graphql-codegen --config graphql.config.yaml --errors-only",
|
||||
"nhost:dev": "nhost dev -d",
|
||||
"nhost:dev": "nhost up",
|
||||
"format": "prettier --write \"src/**/*.{js,ts,tsx,jsx,json,md}\" --plugin-search-dir=.",
|
||||
"storybook": "start-storybook -p 6006 -s public",
|
||||
"build-storybook": "build-storybook",
|
||||
"e2e": "npx playwright@1.31.2 install --with-deps && playwright test"
|
||||
"e2e": "npx playwright@1.34.0 install --with-deps && playwright test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.7.10",
|
||||
@@ -24,8 +24,8 @@
|
||||
"@emotion/react": "^11.10.5",
|
||||
"@emotion/server": "^11.4.0",
|
||||
"@emotion/styled": "^11.10.5",
|
||||
"@fontsource/inter": "^4.5.14",
|
||||
"@fontsource/roboto-mono": "^4.5.8",
|
||||
"@fontsource/inter": "^5.0.0",
|
||||
"@fontsource/roboto-mono": "^5.0.0",
|
||||
"@graphiql/react": "^0.17.0",
|
||||
"@graphiql/toolkit": "^0.8.2",
|
||||
"@headlessui/react": "^1.6.5",
|
||||
@@ -84,11 +84,10 @@
|
||||
"@faker-js/faker": "^7.6.0",
|
||||
"@graphql-codegen/cli": "^3.0.0",
|
||||
"@graphql-codegen/typescript": "^3.0.0",
|
||||
"@graphql-codegen/typescript-graphql-request": "^4.5.1",
|
||||
"@graphql-codegen/typescript-operations": "^3.0.0",
|
||||
"@graphql-codegen/typescript-react-apollo": "^3.3.1",
|
||||
"@next/bundle-analyzer": "^12.3.1",
|
||||
"@playwright/test": "^1.31.2",
|
||||
"@playwright/test": "^1.34.0",
|
||||
"@storybook/addon-actions": "^6.5.14",
|
||||
"@storybook/addon-essentials": "^6.5.14",
|
||||
"@storybook/addon-interactions": "^6.5.14",
|
||||
@@ -105,15 +104,15 @@
|
||||
"@types/lodash.debounce": "^4.0.7",
|
||||
"@types/node": "^16.11.7",
|
||||
"@types/pluralize": "^0.0.29",
|
||||
"@types/react": "18.2.0",
|
||||
"@types/react-dom": "18.2.1",
|
||||
"@types/react": "18.2.12",
|
||||
"@types/react-dom": "18.2.5",
|
||||
"@types/react-table": "^7.7.12",
|
||||
"@types/testing-library__jest-dom": "^5.14.5",
|
||||
"@types/validator": "^13.7.10",
|
||||
"@typescript-eslint/eslint-plugin": "^5.43.0",
|
||||
"@typescript-eslint/parser": "^5.43.0",
|
||||
"@vitejs/plugin-react": "^4.0.0",
|
||||
"@vitest/coverage-c8": "^0.30.0",
|
||||
"@vitest/coverage-v8": "^0.32.0",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"babel-loader": "^8.3.0",
|
||||
"babel-plugin-transform-remove-console": "^6.9.4",
|
||||
@@ -129,7 +128,7 @@
|
||||
"eslint-plugin-jsx-a11y": "^6.6.1",
|
||||
"eslint-plugin-react": "^7.31.11",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"jsdom": "^21.0.0",
|
||||
"jsdom": "^22.0.0",
|
||||
"lint-staged": ">=13",
|
||||
"msw": "^1.0.1",
|
||||
"msw-storybook-addon": "^1.6.3",
|
||||
@@ -137,7 +136,7 @@
|
||||
"postcss": "^8.4.19",
|
||||
"prettier": "^2.7.1",
|
||||
"prettier-plugin-organize-imports": "^3.2.0",
|
||||
"prettier-plugin-tailwindcss": "^0.2.0",
|
||||
"prettier-plugin-tailwindcss": "^0.3.0",
|
||||
"react-date-fns-hooks": "^0.9.4",
|
||||
"require-from-string": "^2.0.2",
|
||||
"snake-case": "^3.0.4",
|
||||
@@ -147,8 +146,7 @@
|
||||
"tsconfig-paths-webpack-plugin": "^4.0.0",
|
||||
"vite": "^4.0.2",
|
||||
"vite-tsconfig-paths": "^4.0.3",
|
||||
"vitest": "^0.30.1",
|
||||
"webpack": "^5.75.0"
|
||||
"vitest": "^0.32.0"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
|
||||
1
dashboard/public/assets/brands/bitbucket.svg
Normal file
1
dashboard/public/assets/brands/bitbucket.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none"><path fill="#2684FF" fill-rule="evenodd" d="M3.41 4.393a.563.563 0 0 1 .434-.195l16.416.003a.562.562 0 0 1 .563.652l-2.388 14.66a.562.562 0 0 1-.563.472H6.417a.765.765 0 0 1-.748-.639L3.281 4.851a.562.562 0 0 1 .13-.458Zm6.832 10.282h3.656l.886-5.173H9.252l.99 5.173Z" clip-rule="evenodd"/><path fill="url(#a)" d="M20.063 9.502h-5.279l-.886 5.173h-3.656l-4.317 5.124a.762.762 0 0 0 .492.186h11.458a.562.562 0 0 0 .563-.473l1.625-10.01Z"/><defs><linearGradient id="a" x1="16.692" x2="10.594" y1="7.717" y2="16.375" gradientUnits="userSpaceOnUse"><stop offset=".18" stop-color="#0052CC"/><stop offset="1" stop-color="#2684FF"/></linearGradient></defs></svg>
|
||||
|
After Width: | Height: | Size: 730 B |
1
dashboard/public/assets/brands/gitlab.svg
Normal file
1
dashboard/public/assets/brands/gitlab.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none"><path fill="#E24329" fill-rule="evenodd" d="m12 19.996 3.223-9.917H8.777L12 19.995Z" clip-rule="evenodd"/><path fill="#FC6D26" fill-rule="evenodd" d="m12 19.996-3.223-9.917H4.261L12 19.996Z" clip-rule="evenodd"/><path fill="#FCA326" fill-rule="evenodd" d="m4.262 10.079-.98 3.013a.667.667 0 0 0 .243.746L12 19.996l-7.738-9.917Z" clip-rule="evenodd"/><path fill="#E24329" fill-rule="evenodd" d="M4.261 10.079h4.517L6.837 4.106a.333.333 0 0 0-.635 0l-1.94 5.973Z" clip-rule="evenodd"/><path fill="#FC6D26" fill-rule="evenodd" d="m12 19.996 3.222-9.917h4.516L12 19.996Z" clip-rule="evenodd"/><path fill="#FCA326" fill-rule="evenodd" d="m19.738 10.079.98 3.013a.667.667 0 0 1-.243.746L12 19.996l7.738-9.917Z" clip-rule="evenodd"/><path fill="#E24329" fill-rule="evenodd" d="M19.739 10.079h-4.517l1.941-5.973a.334.334 0 0 1 .635 0l1.94 5.973Z" clip-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 941 B |
1
dashboard/public/assets/brands/strava.svg
Normal file
1
dashboard/public/assets/brands/strava.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none"><path fill="#FE7203" d="M4.125 5.25c0-.621.504-1.125 1.125-1.125h13.5c.621 0 1.125.504 1.125 1.125v13.5c0 .621-.504 1.125-1.125 1.125H5.25a1.125 1.125 0 0 1-1.125-1.125V5.25Z"/><path fill="url(#a)" d="M4.125 5.25c0-.621.504-1.125 1.125-1.125h13.5c.621 0 1.125.504 1.125 1.125v13.5c0 .621-.504 1.125-1.125 1.125H5.25a1.125 1.125 0 0 1-1.125-1.125V5.25Z"/><path fill="#fff" fill-rule="evenodd" d="m10.917 12.787 2.461 4.43 2.363-4.43h-1.477l-.886 1.674-.984-1.674h-1.477Z" clip-rule="evenodd" opacity=".6"/><path fill="#fff" fill-rule="evenodd" d="m11.213 6.586 3.051 6.201H8.063l3.15-6.201Zm0 3.74 1.18 2.461h-2.46l1.28-2.46Z" clip-rule="evenodd"/><defs><linearGradient id="a" x1="12" x2="12" y1="4.125" y2="19.875" gradientUnits="userSpaceOnUse"><stop stop-color="#FB2F01" stop-opacity="0"/><stop offset="1" stop-color="#FB2F01"/></linearGradient></defs></svg>
|
||||
|
After Width: | Height: | Size: 935 B |
1
dashboard/public/assets/grafana.svg
Normal file
1
dashboard/public/assets/grafana.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 5.3 KiB |
@@ -1,25 +1 @@
|
||||
<svg width="72" height="72" viewBox="0 0 72 72" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 8C0 3.58172 3.58172 0 8 0H64C68.4183 0 72 3.58172 72 8V64C72 68.4183 68.4183 72 64 72H8C3.58172 72 0 68.4183 0 64V8Z" fill="#1EB4D4"/>
|
||||
<path d="M0 8C0 3.58172 3.58172 0 8 0H64C68.4183 0 72 3.58172 72 8V64C72 68.4183 68.4183 72 64 72H8C3.58172 72 0 68.4183 0 64V8Z" fill="url(#paint0_linear_1_85)" fill-opacity="0.2"/>
|
||||
<g filter="url(#filter0_d_1_85)">
|
||||
<circle cx="36" cy="39" r="16" fill="#35BCD8"/>
|
||||
</g>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M51.3994 17.2212C52.831 21.3475 52.831 26.737 51.7362 29.3896C51.1889 30.6948 51.1467 32.2106 51.5678 33.558C52.1152 35.2001 52.4099 36.9264 52.4099 38.7791C52.4099 48.0001 44.9573 55.3685 35.7783 55.2001C27.0625 55.0738 19.6941 47.6633 19.4836 38.9896C19.4415 37.0948 19.7362 35.2001 20.3257 33.5159C20.7889 32.1685 20.7889 30.6948 20.2415 29.3896C19.1889 26.7791 19.1467 21.3896 20.5783 17.1791C20.9152 16.4212 22.0941 16.6738 22.0941 17.4738V17.7685C22.3467 21.7685 23.8625 24.1685 26.052 24.9685C26.3889 25.137 26.8099 25.0948 27.1467 24.8843C29.7152 23.3264 32.7046 22.358 35.9467 22.358C39.1889 22.358 42.2204 23.2843 44.7467 24.8843C45.1257 25.137 45.6731 25.137 46.0099 24.9685C48.1573 23.9159 49.5889 21.7685 49.8415 17.8106V17.5159C49.8836 16.7159 51.0204 16.4633 51.3994 17.2212ZM36.1994 26.2738C29.1257 26.1054 23.3152 31.9159 23.4836 39.0317C23.5678 45.7264 29.0836 51.158 35.7362 51.3264C42.852 51.4106 48.6204 45.6422 48.4941 38.5685C48.3678 31.8738 42.8941 26.358 36.1994 26.2738ZM34.9362 32.8844L37.8836 37.4318L40.7468 41.9792C40.9152 42.2318 40.9994 42.5265 40.9994 42.8213C40.9994 43.3686 40.7047 43.8739 40.2415 44.1686C39.6941 44.5055 38.9783 44.5055 38.431 44.1265C38.2204 44.0002 38.052 43.8739 37.9678 43.6634L36.3257 41.3055C36.1994 41.0528 35.9047 41.0528 35.6941 41.2634L33.3783 43.9581C33.0836 44.2528 32.7047 44.4634 32.2415 44.4634C31.8626 44.4634 31.4415 44.3371 31.1468 44.0844C30.5152 43.495 30.4731 42.4844 31.0626 41.8528L34.1783 38.4423C34.3047 38.2318 34.3889 37.9371 34.2204 37.6844L32.2415 34.5686C32.0731 34.316 31.9889 34.0213 31.9889 33.7265C31.9889 33.1792 32.2836 32.6739 32.7468 32.3792C33.5047 31.916 34.4731 32.1265 34.9362 32.8844Z" fill="white"/>
|
||||
<defs>
|
||||
<filter id="filter0_d_1_85" x="17" y="23" width="38" height="41" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feMorphology radius="4" operator="erode" in="SourceAlpha" result="effect1_dropShadow_1_85"/>
|
||||
<feOffset dy="6"/>
|
||||
<feGaussianBlur stdDeviation="3.5"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0.231373 0 0 0 0 0.278431 0 0 0 0.25 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1_85"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_1_85" result="shape"/>
|
||||
</filter>
|
||||
<linearGradient id="paint0_linear_1_85" x1="0" y1="0" x2="72" y2="72" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white"/>
|
||||
<stop offset="1" stop-color="white" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="72" height="72" fill="none"><path fill="#1EB4D4" d="M0 8a8 8 0 0 1 8-8h56a8 8 0 0 1 8 8v56a8 8 0 0 1-8 8H8a8 8 0 0 1-8-8V8Z"/><path fill="url(#a)" fill-opacity=".2" d="M0 8a8 8 0 0 1 8-8h56a8 8 0 0 1 8 8v56a8 8 0 0 1-8 8H8a8 8 0 0 1-8-8V8Z"/><g filter="url(#b)"><circle cx="36" cy="39" r="16" fill="#35BCD8"/></g><path fill="#fff" fill-rule="evenodd" d="M51.4 17.221c1.431 4.127 1.431 9.516.336 12.169-.547 1.305-.59 2.82-.168 4.168.547 1.642.842 3.368.842 5.221 0 9.221-7.453 16.59-16.632 16.421-8.715-.126-16.084-7.537-16.294-16.21-.043-1.895.252-3.79.842-5.474.463-1.347.463-2.821-.085-4.126-1.052-2.61-1.094-8 .337-12.21.337-.759 1.516-.506 1.516.294v.294c.253 4 1.768 6.4 3.958 7.2.337.169.758.127 1.095-.084 2.568-1.558 5.558-2.526 8.8-2.526 3.242 0 6.273.926 8.8 2.526.379.253.926.253 1.263.084 2.147-1.052 3.579-3.2 3.832-7.157v-.295c.042-.8 1.178-1.053 1.557-.295Zm-15.2 9.053c-7.074-.169-12.885 5.642-12.716 12.758.084 6.694 5.6 12.126 12.252 12.294 7.116.085 12.884-5.684 12.758-12.758-.126-6.694-5.6-12.21-12.295-12.294Zm-1.264 6.61 2.948 4.548 2.863 4.547c.168.253.252.547.252.842 0 .548-.294 1.053-.758 1.348a1.662 1.662 0 0 1-1.81-.042c-.21-.127-.379-.253-.463-.464l-1.642-2.357c-.127-.253-.421-.253-.632-.043l-2.316 2.695c-.294.295-.673.505-1.136.505-.38 0-.8-.126-1.095-.379a1.59 1.59 0 0 1-.084-2.231l3.115-3.41c.127-.211.21-.506.042-.759l-1.978-3.115a1.518 1.518 0 0 1-.253-.843c0-.547.295-1.052.758-1.347.758-.463 1.726-.252 2.19.505Z" clip-rule="evenodd"/><defs><linearGradient id="a" x1="0" x2="72" y1="0" y2="72" gradientUnits="userSpaceOnUse"><stop stop-color="#fff"/><stop offset="1" stop-color="#fff" stop-opacity="0"/></linearGradient><filter id="b" width="38" height="41" x="17" y="23" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feMorphology in="SourceAlpha" radius="4" result="effect1_dropShadow_1_85"/><feOffset dy="6"/><feGaussianBlur stdDeviation="3.5"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0.231373 0 0 0 0 0.278431 0 0 0 0.25 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_1_85"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_1_85" result="shape"/></filter></defs></svg>
|
||||
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 2.3 KiB |
@@ -1,120 +0,0 @@
|
||||
import Box from '@/ui/v2/Box';
|
||||
import Button from '@/ui/v2/Button';
|
||||
import Input from '@/ui/v2/Input';
|
||||
import Text from '@/ui/v2/Text';
|
||||
import { nhost } from '@/utils/nhost';
|
||||
import { triggerToast } from '@/utils/toast';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
export function ChangePasswordModal({ close }: any) {
|
||||
const [formState, setFormState] = useState<{
|
||||
loading: boolean;
|
||||
error: null | string;
|
||||
}>({
|
||||
loading: false,
|
||||
error: null,
|
||||
});
|
||||
|
||||
const [newPassword, setNewPassword] = useState('');
|
||||
const [newPasswordConfirm, setNewPasswordConfirm] = useState('');
|
||||
|
||||
const handleSubmit = async (e: React.SyntheticEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
|
||||
setFormState({
|
||||
loading: true,
|
||||
error: null,
|
||||
});
|
||||
|
||||
if (newPassword !== newPasswordConfirm) {
|
||||
setFormState({
|
||||
loading: false,
|
||||
error: 'Passwords do not match',
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const { error } = await nhost.auth.changePassword({ newPassword });
|
||||
|
||||
if (error) {
|
||||
setFormState({
|
||||
loading: false,
|
||||
error: error.message,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
triggerToast(`Your password has been updated`);
|
||||
close();
|
||||
};
|
||||
|
||||
return (
|
||||
<Box className="w-full max-w-md rounded-md px-6 py-6 text-left">
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="grid grid-flow-row gap-4">
|
||||
<div className="grid grid-flow-row gap-2">
|
||||
<Text variant="h3">Choose New Password</Text>
|
||||
|
||||
<Text className="mt-2 font-normal">
|
||||
Make sure to pick a strong password with at least 8 characters.
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-flow-row gap-2">
|
||||
<Input
|
||||
type="password"
|
||||
id="newPassword"
|
||||
name="newPassword"
|
||||
label="New Password"
|
||||
placeholder="New password"
|
||||
autoFocus
|
||||
value={newPassword}
|
||||
onChange={(event) => setNewPassword(event.target.value)}
|
||||
hideEmptyHelperText
|
||||
fullWidth
|
||||
required
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="password"
|
||||
id="confirmNewPassword"
|
||||
name="confirmNewPassword"
|
||||
label="Confirm New Password"
|
||||
placeholder="Confirm new password"
|
||||
value={newPasswordConfirm}
|
||||
onChange={(event) => setNewPasswordConfirm(event.target.value)}
|
||||
hideEmptyHelperText
|
||||
fullWidth
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-flow-row gap-2">
|
||||
{formState.error && (
|
||||
<Text className="w-full px-4 text-center" color="error">
|
||||
Error: {formState.error}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
<Button type="submit" loading={formState.loading}>
|
||||
Set New Password
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
type="reset"
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
onClick={close}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default ChangePasswordModal;
|
||||
@@ -1,117 +0,0 @@
|
||||
import { LoadingScreen } from '@/components/common/LoadingScreen';
|
||||
import useIsPlatform from '@/hooks/common/useIsPlatform';
|
||||
import { useCurrentWorkspaceAndProject } from '@/hooks/v2/useCurrentWorkspaceAndProject';
|
||||
import Box from '@/ui/v2/Box';
|
||||
import Button from '@/ui/v2/Button';
|
||||
import IconButton from '@/ui/v2/IconButton';
|
||||
import ArrowSquareOutIcon from '@/ui/v2/icons/ArrowSquareOutIcon';
|
||||
import CopyIcon from '@/ui/v2/icons/CopyIcon';
|
||||
import Text from '@/ui/v2/Text';
|
||||
import generateAppServiceUrl, {
|
||||
defaultLocalBackendSlugs,
|
||||
defaultRemoteBackendSlugs,
|
||||
} from '@/utils/common/generateAppServiceUrl';
|
||||
import { copy } from '@/utils/copy';
|
||||
import { getHasuraConsoleServiceUrl } from '@/utils/env';
|
||||
import Image from 'next/image';
|
||||
|
||||
interface HasuraDataProps {
|
||||
close?: () => void;
|
||||
}
|
||||
|
||||
export function HasuraData({ close }: HasuraDataProps) {
|
||||
const { currentProject } = useCurrentWorkspaceAndProject();
|
||||
const isPlatform = useIsPlatform();
|
||||
const projectAdminSecret = currentProject?.config?.hasura.adminSecret;
|
||||
|
||||
if (!currentProject?.subdomain || !projectAdminSecret) {
|
||||
return <LoadingScreen />;
|
||||
}
|
||||
|
||||
const hasuraUrl =
|
||||
process.env.NEXT_PUBLIC_ENV === 'dev' || !isPlatform
|
||||
? `${getHasuraConsoleServiceUrl()}`
|
||||
: generateAppServiceUrl(
|
||||
currentProject?.subdomain,
|
||||
currentProject?.region.awsName,
|
||||
'hasura',
|
||||
defaultLocalBackendSlugs,
|
||||
{ ...defaultRemoteBackendSlugs, hasura: '/console' },
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="mx-auto w-full max-w-md px-6 py-4 text-left">
|
||||
<div className="grid grid-flow-row gap-1">
|
||||
<div className="mx-auto">
|
||||
<Image
|
||||
src="/assets/hasuramodal.svg"
|
||||
width={72}
|
||||
height={72}
|
||||
alt="Hasura"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Text variant="h3" component="h1" className="text-center">
|
||||
Open Hasura
|
||||
</Text>
|
||||
|
||||
<Text className="text-center">
|
||||
Hasura is the dashboard you'll use to edit your schema and
|
||||
permissions as well as browse data. Copy the admin secret to your
|
||||
clipboard and enter it in the next screen.
|
||||
</Text>
|
||||
|
||||
<Box className="mt-6 border-y-1">
|
||||
<div className="grid w-full grid-cols-1 place-content-between items-center py-2 sm:grid-cols-3">
|
||||
<Text className="col-span-1 text-center font-medium sm:justify-start sm:text-left">
|
||||
Admin Secret
|
||||
</Text>
|
||||
|
||||
<div className="col-span-1 grid grid-flow-col items-center justify-center gap-2 sm:col-span-2 sm:justify-end">
|
||||
<Text className="font-medium" variant="subtitle2">
|
||||
{Array(projectAdminSecret.length).fill('•').join('')}
|
||||
</Text>
|
||||
|
||||
<IconButton
|
||||
onClick={() => copy(projectAdminSecret, 'Hasura admin secret')}
|
||||
variant="borderless"
|
||||
color="secondary"
|
||||
className="min-w-0 p-1"
|
||||
aria-label="Copy admin secret"
|
||||
>
|
||||
<CopyIcon className="h-4 w-4" />
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
</Box>
|
||||
|
||||
<div className="mt-6 grid grid-flow-row gap-2">
|
||||
<Button
|
||||
href={hasuraUrl}
|
||||
// Both `target` and `rel` are available when `href` is set. This is
|
||||
// a limitation of MUI.
|
||||
// @ts-ignore
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
endIcon={<ArrowSquareOutIcon className="h-4 w-4" />}
|
||||
>
|
||||
Open Hasura
|
||||
</Button>
|
||||
|
||||
{close && (
|
||||
<Button
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
className="text-sm+ font-normal"
|
||||
onClick={close}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default HasuraData;
|
||||
@@ -1,97 +0,0 @@
|
||||
import { useCurrentWorkspaceAndProject } from '@/hooks/v2/useCurrentWorkspaceAndProject';
|
||||
import Box from '@/ui/v2/Box';
|
||||
import Button from '@/ui/v2/Button';
|
||||
import Checkbox from '@/ui/v2/Checkbox';
|
||||
import Text from '@/ui/v2/Text';
|
||||
import { useRestoreApplicationDatabaseMutation } from '@/utils/__generated__/graphql';
|
||||
import { triggerToast } from '@/utils/toast';
|
||||
import { formatISO9075 } from 'date-fns';
|
||||
import { useState } from 'react';
|
||||
|
||||
export interface RestoreBackupModalModalProps {
|
||||
/**
|
||||
* Call this function to imperatively close the modal.
|
||||
*/
|
||||
close: any;
|
||||
/**
|
||||
* Arbitrary data passed down to the modal.
|
||||
*
|
||||
*/
|
||||
data: any;
|
||||
}
|
||||
|
||||
export function RestoreBackupModal({
|
||||
close,
|
||||
data,
|
||||
}: RestoreBackupModalModalProps) {
|
||||
const { id: backupId, createdAt } = data;
|
||||
|
||||
const [isSure, setIsSure] = useState(false);
|
||||
const [mutationIsCompleted, setMutationIsCompleted] = useState(false);
|
||||
const { currentProject } = useCurrentWorkspaceAndProject();
|
||||
|
||||
const [restoreApplicationDatabase, { loading }] =
|
||||
useRestoreApplicationDatabaseMutation();
|
||||
|
||||
const handleSubmit = async () => {
|
||||
setMutationIsCompleted(false);
|
||||
try {
|
||||
await restoreApplicationDatabase({
|
||||
variables: {
|
||||
backupId,
|
||||
appId: currentProject.id,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
setMutationIsCompleted(false);
|
||||
triggerToast('Database backup restoration failed');
|
||||
return;
|
||||
}
|
||||
setMutationIsCompleted(true);
|
||||
triggerToast('Database backup successfully scheduled for restoration.');
|
||||
};
|
||||
|
||||
if (mutationIsCompleted) {
|
||||
return (
|
||||
<Box className="w-modal rounded-lg p-6">
|
||||
<div className="flex flex-col">
|
||||
<Text className="text-center text-lg font-medium">
|
||||
The backup has been restored successfully.
|
||||
</Text>
|
||||
|
||||
<Button className="mt-5" onClick={close}>
|
||||
OK
|
||||
</Button>
|
||||
</div>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box className="w-modal rounded-lg px-6 py-6 text-left">
|
||||
<div className="flex flex-col">
|
||||
<Text className="text-center text-lg font-medium">
|
||||
Restore Database Backup
|
||||
</Text>
|
||||
<Text className="mt-2 text-center font-normal">
|
||||
You current database will be deleted, and the backup created{' '}
|
||||
<span className="font-semibold">
|
||||
{formatISO9075(new Date(createdAt))}
|
||||
</span>{' '}
|
||||
will be restored.
|
||||
</Text>
|
||||
|
||||
<Box className="my-4 border-y py-2 px-2">
|
||||
<Checkbox
|
||||
checked={isSure}
|
||||
onChange={(_event, checked) => setIsSure(checked)}
|
||||
label="I'm sure I want to restore this backup."
|
||||
/>
|
||||
</Box>
|
||||
<Button onClick={handleSubmit} disabled={!isSure} loading={loading}>
|
||||
Restore
|
||||
</Button>
|
||||
</div>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
import Status, { StatusEnum } from '@/ui/Status';
|
||||
import Box from '@/ui/v2/Box';
|
||||
import { isDevOrStaging } from '@/utils/helpers';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
|
||||
export function StagingMetadata({ children }: PropsWithChildren<unknown>) {
|
||||
return (
|
||||
isDevOrStaging() && (
|
||||
<div className="mx-auto mt-10 max-w-sm">
|
||||
<Box className="mx-auto flex flex-col rounded-md border p-5 text-center">
|
||||
<Status status={StatusEnum.Deploying}>Internal info</Status>
|
||||
{children}
|
||||
</Box>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export default StagingMetadata;
|
||||
@@ -1,45 +0,0 @@
|
||||
import { ChangePlanModal } from '@/components/applications/ChangePlanModal';
|
||||
import { useDialog } from '@/components/common/DialogProvider';
|
||||
import { Alert } from '@/ui/Alert';
|
||||
import Button from '@/ui/v2/Button';
|
||||
import Text from '@/ui/v2/Text';
|
||||
import type { DetailedHTMLProps, HTMLProps } from 'react';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
export interface UnlockFeatureByUpgradingProps
|
||||
extends DetailedHTMLProps<HTMLProps<HTMLDivElement>, HTMLDivElement> {
|
||||
/**
|
||||
* Message to display in the alert.
|
||||
*/
|
||||
message: string;
|
||||
}
|
||||
|
||||
export function UnlockFeatureByUpgrading({
|
||||
message,
|
||||
className,
|
||||
...props
|
||||
}: UnlockFeatureByUpgradingProps) {
|
||||
const { openDialog } = useDialog();
|
||||
|
||||
return (
|
||||
<div className={twMerge('flex', className)} {...props}>
|
||||
<Alert className="grid w-full grid-flow-col place-content-between items-center gap-2">
|
||||
<Text className="text-left">{message}</Text>
|
||||
|
||||
<Button
|
||||
variant="borderless"
|
||||
onClick={() => {
|
||||
openDialog({
|
||||
component: <ChangePlanModal />,
|
||||
props: {
|
||||
PaperProps: { className: 'p-0 max-w-xl w-full' },
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
Upgrade
|
||||
</Button>
|
||||
</Alert>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import Link from '@/ui/v2/Link';
|
||||
import Text from '@/ui/v2/Text';
|
||||
|
||||
export function CheckGithubConfiguration() {
|
||||
return (
|
||||
<Text className="mt-2 text-center text-xs">
|
||||
Do you miss a repository, or do you need to connect another GitHub
|
||||
account?{' '}
|
||||
<Link
|
||||
href={process.env.NEXT_PUBLIC_GITHUB_APP_INSTALL_URL}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
className="text-xs font-medium"
|
||||
underline="hover"
|
||||
>
|
||||
Manage your GitHub configuration
|
||||
</Link>
|
||||
.
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
export default CheckGithubConfiguration;
|
||||
@@ -1,35 +0,0 @@
|
||||
import GithubIcon from '@/components/icons/GithubIcon';
|
||||
import Button from '@/ui/v2/Button';
|
||||
import ArrowSquareOutIcon from '@/ui/v2/icons/ArrowSquareOutIcon';
|
||||
import Text from '@/ui/v2/Text';
|
||||
|
||||
export default function GitHubInstallNhostApplication() {
|
||||
return (
|
||||
<div className="grid grid-flow-row justify-center gap-2 p-0.5">
|
||||
<GithubIcon className="mx-auto h-8 w-8" />
|
||||
|
||||
<div className="text-center">
|
||||
<Text variant="h3" component="h2">
|
||||
Install the Nhost GitHub Application
|
||||
</Text>
|
||||
|
||||
<Text variant="subtitle2">
|
||||
Install the Nhost application on your GitHub account and update
|
||||
permissions to automatically track repositories.
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
href={process.env.NEXT_PUBLIC_GITHUB_APP_INSTALL_URL}
|
||||
// Both `target` and `rel` are available when `href` is set. This is
|
||||
// a limitation of MUI.
|
||||
// @ts-ignore
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
endIcon={<ArrowSquareOutIcon className="h-4 w-4" />}
|
||||
>
|
||||
Configure the Nhost application on GitHub
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
import { Avatar } from '@/ui/Avatar';
|
||||
import Divider from '@/ui/v2/Divider';
|
||||
import PlusCircleIcon from '@/ui/v2/icons/PlusCircleIcon';
|
||||
import Link from '@/ui/v2/Link';
|
||||
import List from '@/ui/v2/List';
|
||||
import { ListItem } from '@/ui/v2/ListItem';
|
||||
import Text from '@/ui/v2/Text';
|
||||
import { Fragment } from 'react';
|
||||
|
||||
export function GitHubNoRepositoriesAdded({
|
||||
filteredGitHubAppInstallations,
|
||||
}: any) {
|
||||
return (
|
||||
<div>
|
||||
<Text className="mt-1 text-center text-lg font-medium">
|
||||
No repositories found
|
||||
</Text>
|
||||
|
||||
<Text className="text-center text-xs">
|
||||
Check the Nhost app's settings on your GitHub account, or install
|
||||
the app on a new account.
|
||||
</Text>
|
||||
|
||||
<List className="my-2 border-y">
|
||||
{filteredGitHubAppInstallations.map((githubApp, index) => (
|
||||
<Fragment key={githubApp.id}>
|
||||
<ListItem.Root
|
||||
key={githubApp.id}
|
||||
className="grid grid-flow-col gap-2 py-2.5 justify-start items-center"
|
||||
>
|
||||
<ListItem.Avatar>
|
||||
<Avatar
|
||||
avatarUrl={githubApp.accountAvatarUrl as string}
|
||||
className="mr-1 h-5 w-5"
|
||||
/>
|
||||
</ListItem.Avatar>
|
||||
|
||||
<ListItem.Text primary={githubApp.accountLogin} />
|
||||
</ListItem.Root>
|
||||
|
||||
{index < filteredGitHubAppInstallations.length - 1 && (
|
||||
<Divider component="li" />
|
||||
)}
|
||||
</Fragment>
|
||||
))}
|
||||
</List>
|
||||
|
||||
<Link
|
||||
href={process.env.NEXT_PUBLIC_GITHUB_APP_INSTALL_URL}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
underline="hover"
|
||||
className="grid grid-flow-col gap-1 items-center justify-start"
|
||||
>
|
||||
<PlusCircleIcon className="w-4 h-4" />
|
||||
Configure the Nhost application on GitHub.
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default GitHubNoRepositoriesAdded;
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './Breadcrumbs';
|
||||
export { default } from './Breadcrumbs';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './ControlledAutocomplete';
|
||||
export { default } from './ControlledAutocomplete';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './ControlledCheckbox';
|
||||
export { default } from './ControlledCheckbox';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './ControlledSelect';
|
||||
export { default } from './ControlledSelect';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './ControlledSwitch';
|
||||
export { default } from './ControlledSwitch';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './DataGrid';
|
||||
export { default } from './DataGrid';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './DataGridBody';
|
||||
export { default } from './DataGridBody';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './DataGridBooleanCell';
|
||||
export { default } from './DataGridBooleanCell';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './DataGridDateCell';
|
||||
export { default } from './DataGridDateCell';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './DataGridFrame';
|
||||
export { default } from './DataGridFrame';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './DataGridHeader';
|
||||
export { default } from './DataGridHeader';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './DataGridNumericCell';
|
||||
export { default } from './DataGridNumericCell';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './DataGridPagination';
|
||||
export { default } from './DataGridPagination';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './DataGridPreviewCell';
|
||||
export { default } from './DataGridPreviewCell';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './DataGridTextCell';
|
||||
export { default } from './DataGridTextCell';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './DesktopNav';
|
||||
export { default } from './DesktopNav';
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { CommonDialogProps } from '@/components/ui/v2/Dialog';
|
||||
import type { DialogFormProps } from '@/types/common';
|
||||
import type { CommonDialogProps } from '@/ui/v2/Dialog';
|
||||
import type { ReactElement, ReactNode } from 'react';
|
||||
import { createContext } from 'react';
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import RetryableErrorBoundary from '@/components/common/RetryableErrorBoundary';
|
||||
import AlertDialog from '@/ui/v2/AlertDialog';
|
||||
import { BaseDialog } from '@/ui/v2/Dialog';
|
||||
import Drawer from '@/ui/v2/Drawer';
|
||||
import { RetryableErrorBoundary } from '@/components/presentational/RetryableErrorBoundary';
|
||||
import { AlertDialog } from '@/components/ui/v2/AlertDialog';
|
||||
import { BaseDialog } from '@/components/ui/v2/Dialog';
|
||||
import { Drawer } from '@/components/ui/v2/Drawer';
|
||||
import { useRouter } from 'next/router';
|
||||
import type { BaseSyntheticEvent, PropsWithChildren } from 'react';
|
||||
import {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { CommonDialogProps } from '@/ui/v2/Dialog';
|
||||
import type { CommonDialogProps } from '@/components/ui/v2/Dialog';
|
||||
import type { ReactElement, ReactNode } from 'react';
|
||||
import type { DialogConfig, OpenDialogOptions } from './DialogContext';
|
||||
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
import ErrorMessage from '@/components/common/ErrorMessage';
|
||||
import type { FallbackProps } from 'react-error-boundary';
|
||||
|
||||
export default function ErrorBoundaryFallback({
|
||||
error,
|
||||
resetErrorBoundary,
|
||||
}: FallbackProps) {
|
||||
return (
|
||||
<ErrorMessage onReset={resetErrorBoundary}>{error.message}</ErrorMessage>
|
||||
);
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
export { default } from './ErrorBoundaryFallback';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './ErrorMessage';
|
||||
export { default } from './ErrorMessage';
|
||||
@@ -1,5 +1,11 @@
|
||||
import { FeedbackReceived } from '@/components/home/FeedbackReceived';
|
||||
import { SendFeedback } from '@/components/home/SendFeedback';
|
||||
import { Avatar } from '@/components/ui/v2/Avatar';
|
||||
import { Button } from '@/components/ui/v2/Button';
|
||||
import { Input } from '@/components/ui/v2/Input';
|
||||
import { Text } from '@/components/ui/v2/Text';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
||||
import { useInsertFeedbackOneMutation } from '@/utils/__generated__/graphql';
|
||||
import { useUserData } from '@nhost/nextjs';
|
||||
import Image from 'next/image';
|
||||
import type { DetailedHTMLProps, HTMLProps } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
@@ -7,25 +13,136 @@ import { twMerge } from 'tailwind-merge';
|
||||
export interface FeedbackFormProps
|
||||
extends DetailedHTMLProps<HTMLProps<HTMLDivElement>, HTMLDivElement> {}
|
||||
|
||||
// TODO: Use `react-hook-form` here instead of the custom solution
|
||||
// TODO: Use `react-hook-form` here instead of the custom form implementation
|
||||
export default function FeedbackForm({
|
||||
className,
|
||||
...props
|
||||
}: FeedbackFormProps) {
|
||||
const { currentProject } = useCurrentWorkspaceAndProject();
|
||||
const [insertFeedback, { loading }] = useInsertFeedbackOneMutation();
|
||||
const user = useUserData();
|
||||
|
||||
const [feedback, setFeedback] = useState('');
|
||||
const [feedbackSent, setFeedbackSent] = useState(false);
|
||||
|
||||
return (
|
||||
<div className={twMerge('max-w-md py-4 px-5', className)} {...props}>
|
||||
{!feedbackSent ? (
|
||||
<SendFeedback
|
||||
setFeedbackSent={setFeedbackSent}
|
||||
feedback={feedback}
|
||||
setFeedback={setFeedback}
|
||||
function handleClose() {
|
||||
setTimeout(() => {
|
||||
setFeedbackSent(false);
|
||||
}, 500);
|
||||
}
|
||||
|
||||
async function handleSubmit(e: React.SyntheticEvent<HTMLFormElement>) {
|
||||
e.preventDefault();
|
||||
|
||||
const feedbackWithProjectInfo = [
|
||||
currentProject && `Project ID: ${currentProject.id}`,
|
||||
typeof window !== 'undefined' && `URL: ${window.location.href}`,
|
||||
feedback,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join('\n\n');
|
||||
|
||||
try {
|
||||
await insertFeedback({
|
||||
variables: {
|
||||
feedback: {
|
||||
feedback: feedbackWithProjectInfo,
|
||||
},
|
||||
},
|
||||
});
|
||||
setFeedbackSent(true);
|
||||
setFeedback('');
|
||||
} catch (error) {
|
||||
// TODO: Display error to user and use a logging solution
|
||||
}
|
||||
}
|
||||
|
||||
if (feedbackSent) {
|
||||
return (
|
||||
<div
|
||||
className={twMerge(
|
||||
'grid max-w-md grid-flow-row justify-center gap-4 py-4 px-5 text-center',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<Image
|
||||
src="/assets/FeedbackReceived.svg"
|
||||
alt="Light bulb with a checkmark"
|
||||
width={72}
|
||||
height={72}
|
||||
/>
|
||||
) : (
|
||||
<FeedbackReceived setFeedbackSent={setFeedbackSent} close={() => {}} />
|
||||
|
||||
<div className="grid grid-flow-row gap-2">
|
||||
<Text variant="h3" component="h2" className="text-center">
|
||||
Feedback Received
|
||||
</Text>
|
||||
|
||||
<Text>
|
||||
Thanks for sending us your thoughts! Feel free to send more feedback
|
||||
as you explore the beta, and stay tuned for updates.
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
className="mt-2 text-sm+ font-normal"
|
||||
onClick={handleClose}
|
||||
>
|
||||
Go Back
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={twMerge(
|
||||
'grid max-w-md grid-flow-row gap-2 py-4 px-5',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<Text variant="h3" component="h2">
|
||||
Leave Feedback
|
||||
</Text>
|
||||
|
||||
<Text>
|
||||
Nhost is still in beta and not everything is in place yet, but we'd
|
||||
love to know what you think of it so far.
|
||||
</Text>
|
||||
|
||||
<form onSubmit={handleSubmit} className="grid grid-flow-row gap-2">
|
||||
<div className="grid grid-flow-col place-content-between gap-2">
|
||||
<Text className="font-medium">
|
||||
What do you think we should improve?
|
||||
</Text>
|
||||
|
||||
<Avatar
|
||||
className="h-6 w-6 rounded-full"
|
||||
alt={user?.displayName}
|
||||
src={user?.avatarUrl}
|
||||
>
|
||||
{user?.displayName}
|
||||
</Avatar>
|
||||
</div>
|
||||
|
||||
<Input
|
||||
multiline
|
||||
value={feedback}
|
||||
onChange={(event) => setFeedback(event.target.value)}
|
||||
placeholder="Your feedback"
|
||||
rows={6}
|
||||
required
|
||||
fullWidth
|
||||
hideEmptyHelperText
|
||||
/>
|
||||
|
||||
<Button type="submit" disabled={!feedback} loading={loading}>
|
||||
Send Feedback
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export * from './FeedbackForm';
|
||||
export { default } from './FeedbackForm';
|
||||
export { default as FeedbackForm } from './FeedbackForm';
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './Form';
|
||||
export { default } from './Form';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './FormActivityIndicator';
|
||||
export { default } from './FormActivityIndicator';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './Header';
|
||||
export { default } from './Header';
|
||||
@@ -1 +0,0 @@
|
||||
export { default } from './HighlightedText';
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { NavLinkProps } from '@/components/common/NavLink';
|
||||
import NavLink from '@/components/common/NavLink';
|
||||
import type { SvgIconProps } from '@/ui/v2/icons/SvgIcon';
|
||||
import { NavLink } from '@/components/common/NavLink';
|
||||
import type { SvgIconProps } from '@/components/ui/v2/icons/SvgIcon';
|
||||
import type { ForwardedRef, PropsWithoutRef, ReactElement } from 'react';
|
||||
import { cloneElement, forwardRef, isValidElement } from 'react';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export * from './IconLink';
|
||||
export { default } from './IconLink';
|
||||
export { default as IconLink } from './IconLink';
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './InlineCode';
|
||||
export { default } from './InlineCode';
|
||||
@@ -1,23 +1,22 @@
|
||||
import { Box } from '@/components/ui/v2/Box';
|
||||
import { Button } from '@/components/ui/v2/Button';
|
||||
import { Text } from '@/components/ui/v2/Text';
|
||||
import { useIsPlatform } from '@/features/projects/common/hooks/useIsPlatform';
|
||||
import {
|
||||
GetAllWorkspacesAndProjectsDocument,
|
||||
GetWorkspaceMemberInvitesToManageDocument,
|
||||
useGetWorkspaceMemberInvitesToManageQuery,
|
||||
} from '@/generated/graphql';
|
||||
import useIsPlatform from '@/hooks/common/useIsPlatform';
|
||||
import { useSubmitState } from '@/hooks/useSubmitState';
|
||||
import Box from '@/ui/v2/Box';
|
||||
import Button from '@/ui/v2/Button';
|
||||
import Text from '@/ui/v2/Text';
|
||||
import { nhost } from '@/utils/nhost';
|
||||
import { triggerToast } from '@/utils/toast';
|
||||
import { updateOwnCache } from '@/utils/updateOwnCache';
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
import { alpha } from '@mui/system';
|
||||
import { useUserData } from '@nhost/nextjs';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export function InviteAnnounce() {
|
||||
export default function InviteNotification() {
|
||||
const user = useUserData();
|
||||
|
||||
const isPlatform = useIsPlatform();
|
||||
@@ -28,13 +27,18 @@ export function InviteAnnounce() {
|
||||
useSubmitState();
|
||||
|
||||
// @FIX: We probably don't want to poll every ten seconds for possible invites. (We can change later depending on how it works in production.) Maybe just on the workspace page?
|
||||
const { data, loading, error, refetch, startPolling } =
|
||||
useGetWorkspaceMemberInvitesToManageQuery({
|
||||
variables: {
|
||||
userId: user?.id,
|
||||
},
|
||||
skip: !isPlatform || !user,
|
||||
});
|
||||
const {
|
||||
data,
|
||||
loading,
|
||||
error,
|
||||
refetch: refetchInvitations,
|
||||
startPolling,
|
||||
} = useGetWorkspaceMemberInvitesToManageQuery({
|
||||
variables: {
|
||||
userId: user?.id,
|
||||
},
|
||||
skip: !isPlatform || !user,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
startPolling(15000);
|
||||
@@ -79,9 +83,14 @@ export function InviteAnnounce() {
|
||||
});
|
||||
}
|
||||
|
||||
await updateOwnCache(client);
|
||||
await client.refetchQueries({
|
||||
include: [
|
||||
GetAllWorkspacesAndProjectsDocument,
|
||||
GetWorkspaceMemberInvitesToManageDocument,
|
||||
],
|
||||
});
|
||||
await router.push(`/${invite.workspace.slug}`);
|
||||
await refetch();
|
||||
await refetchInvitations();
|
||||
triggerToast('Workspace invite accepted');
|
||||
return setSubmitState({
|
||||
error: null,
|
||||
@@ -189,5 +198,3 @@ export function InviteAnnounce() {
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default InviteAnnounce;
|
||||
@@ -0,0 +1 @@
|
||||
export { default as InviteNotification } from './InviteNotification';
|
||||
@@ -1,39 +0,0 @@
|
||||
import ThemeSwitcher from '@/components/common/ThemeSwitcher';
|
||||
import { Dropdown } from '@/ui/v2/Dropdown';
|
||||
import IconButton from '@/ui/v2/IconButton';
|
||||
import UserIcon from '@/ui/v2/icons/UserIcon';
|
||||
import Text from '@/ui/v2/Text';
|
||||
import getConfig from 'next/config';
|
||||
|
||||
export default function LocalAccountMenu() {
|
||||
const { publicRuntimeConfig } = getConfig();
|
||||
|
||||
return (
|
||||
<Dropdown.Root className="justify-self-center">
|
||||
<Dropdown.Trigger hideChevron asChild>
|
||||
<IconButton
|
||||
variant="borderless"
|
||||
color="secondary"
|
||||
className="h-7 w-7 rounded-full"
|
||||
sx={{
|
||||
backgroundColor: (theme) => `${theme.palette.grey[300]} !important`,
|
||||
}}
|
||||
>
|
||||
<UserIcon className="h-4 w-4" />
|
||||
</IconButton>
|
||||
</Dropdown.Trigger>
|
||||
|
||||
<Dropdown.Content
|
||||
PaperProps={{
|
||||
className: 'mt-1 p-6 grid grid-flow-row gap-4 w-full max-w-xs',
|
||||
}}
|
||||
>
|
||||
<ThemeSwitcher label="Theme" />
|
||||
|
||||
<Text className="text-center text-xs" color="disabled">
|
||||
Dashboard Version: {publicRuntimeConfig?.version || 'n/a'}
|
||||
</Text>
|
||||
</Dropdown.Content>
|
||||
</Dropdown.Root>
|
||||
);
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
export { default } from './LocalAccountMenu';
|
||||
@@ -1 +0,0 @@
|
||||
export { default } from './MaintenanceAlert';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './MobileNav';
|
||||
export { default } from './MobileNav';
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { LinkProps } from '@/ui/v2/Link';
|
||||
import Link from '@/ui/v2/Link';
|
||||
import type { LinkProps } from '@/components/ui/v2/Link';
|
||||
import { Link } from '@/components/ui/v2/Link';
|
||||
import NextLink from 'next/link';
|
||||
import type { ForwardedRef, PropsWithoutRef } from 'react';
|
||||
import { forwardRef } from 'react';
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export * from './NavLink';
|
||||
export { default } from './NavLink';
|
||||
export { default as NavLink } from './NavLink';
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { ButtonProps } from '@/ui/v2/Button';
|
||||
import Button from '@/ui/v2/Button';
|
||||
import ChevronLeftIcon from '@/ui/v2/icons/ChevronLeftIcon';
|
||||
import ChevronRightIcon from '@/ui/v2/icons/ChevronRightIcon';
|
||||
import Input from '@/ui/v2/Input';
|
||||
import Text from '@/ui/v2/Text';
|
||||
import type { ButtonProps } from '@/components/ui/v2/Button';
|
||||
import { Button } from '@/components/ui/v2/Button';
|
||||
import { ChevronLeftIcon } from '@/components/ui/v2/icons/ChevronLeftIcon';
|
||||
import { ChevronRightIcon } from '@/components/ui/v2/icons/ChevronRightIcon';
|
||||
import { Input } from '@/components/ui/v2/Input';
|
||||
import { Text } from '@/components/ui/v2/Text';
|
||||
import type { DetailedHTMLProps, HTMLProps } from 'react';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
export * from './Pagination';
|
||||
export { default } from './Pagination';
|
||||
|
||||
export { default as Pagination } from './Pagination';
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export { default } from './ReadOnlyToggle';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './RetryableErrorBoundary';
|
||||
export { default } from './RetryableErrorBoundary';
|
||||
@@ -1,7 +1,7 @@
|
||||
import Option from '@/ui/v2/Option';
|
||||
import type { SelectProps } from '@/ui/v2/Select';
|
||||
import Select from '@/ui/v2/Select';
|
||||
import useColorPreference from '@/ui/v2/useColorPreference';
|
||||
import { Option } from '@/components/ui/v2/Option';
|
||||
import type { SelectProps } from '@/components/ui/v2/Select';
|
||||
import { Select } from '@/components/ui/v2/Select';
|
||||
import { useColorPreference } from '@/components/ui/v2/useColorPreference';
|
||||
|
||||
export interface ThemeSwitcherProps extends SelectProps<any> {}
|
||||
|
||||
@@ -22,6 +22,7 @@ export default function ThemeSwitcher({
|
||||
onChange?.(event, value);
|
||||
}}
|
||||
slotProps={{
|
||||
...props?.slotProps,
|
||||
listbox: { className: 'min-w-0 w-full' },
|
||||
popper: {
|
||||
disablePortal: false,
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export * from './ThemeSwitcher';
|
||||
export { default } from './ThemeSwitcher';
|
||||
export { default as ThemeSwitcher } from './ThemeSwitcher';
|
||||
|
||||
19
dashboard/src/components/common/UIProvider/UIContext.ts
Normal file
19
dashboard/src/components/common/UIProvider/UIContext.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { createContext } from 'react';
|
||||
|
||||
export interface UIContextProps {
|
||||
/**
|
||||
* Determines whether or not the dashboard is in maintenance mode.
|
||||
*/
|
||||
maintenanceActive: boolean;
|
||||
/**
|
||||
* The date and time when maintenance mode will end.
|
||||
*/
|
||||
maintenanceEndDate: Date;
|
||||
}
|
||||
|
||||
const UIContext = createContext<UIContextProps>({
|
||||
maintenanceActive: false,
|
||||
maintenanceEndDate: null,
|
||||
});
|
||||
|
||||
export default UIContext;
|
||||
30
dashboard/src/components/common/UIProvider/UIProvider.tsx
Normal file
30
dashboard/src/components/common/UIProvider/UIProvider.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import { useRouter } from 'next/router';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import type { UIContextProps } from './UIContext';
|
||||
import UIContext from './UIContext';
|
||||
|
||||
export default function UIProvider(props: PropsWithChildren<unknown>) {
|
||||
const router = useRouter();
|
||||
|
||||
const maintenanceUnlocked =
|
||||
process.env.NEXT_PUBLIC_MAINTENANCE_UNLOCK_SECRET &&
|
||||
process.env.NEXT_PUBLIC_MAINTENANCE_UNLOCK_SECRET ===
|
||||
router.query.maintenanceUnlockSecret;
|
||||
|
||||
const value: UIContextProps = useMemo(
|
||||
() => ({
|
||||
maintenanceActive: maintenanceUnlocked
|
||||
? false
|
||||
: process.env.NEXT_PUBLIC_MAINTENANCE_ACTIVE === 'true',
|
||||
maintenanceEndDate:
|
||||
process.env.NEXT_PUBLIC_MAINTENANCE_END_DATE &&
|
||||
!Number.isNaN(Date.parse(process.env.NEXT_PUBLIC_MAINTENANCE_END_DATE))
|
||||
? new Date(Date.parse(process.env.NEXT_PUBLIC_MAINTENANCE_END_DATE))
|
||||
: null,
|
||||
}),
|
||||
[maintenanceUnlocked],
|
||||
);
|
||||
|
||||
return <UIContext.Provider value={value} {...props} />;
|
||||
}
|
||||
4
dashboard/src/components/common/UIProvider/index.ts
Normal file
4
dashboard/src/components/common/UIProvider/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export * from './UIContext';
|
||||
export { default as UIContext } from './UIContext';
|
||||
export { default as UIProvider } from './UIProvider';
|
||||
export { default as useUI } from './useUI';
|
||||
12
dashboard/src/components/common/UIProvider/useUI.ts
Normal file
12
dashboard/src/components/common/UIProvider/useUI.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { useContext } from 'react';
|
||||
import UIContext from './UIContext';
|
||||
|
||||
export default function useUI() {
|
||||
const context = useContext(UIContext);
|
||||
|
||||
if (!context) {
|
||||
throw new Error('useUI must be used within a UIProvider');
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
import { ChangePasswordModal } from '@/components/applications/ChangePasswordModal';
|
||||
import ThemeSwitcher from '@/components/common/ThemeSwitcher';
|
||||
import { Avatar } from '@/ui/Avatar';
|
||||
import { Modal } from '@/ui/Modal';
|
||||
import Box from '@/ui/v2/Box';
|
||||
import Button from '@/ui/v2/Button';
|
||||
import { Dropdown, useDropdown } from '@/ui/v2/Dropdown';
|
||||
import PowerIcon from '@/ui/v2/icons/PowerIcon';
|
||||
import Text from '@/ui/v2/Text';
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
import { useSignOut, useUserData } from '@nhost/nextjs';
|
||||
import getConfig from 'next/config';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
interface AccountMenuContentProps {
|
||||
onChangePasswordClick: VoidFunction;
|
||||
}
|
||||
|
||||
function AccountMenuContent({
|
||||
onChangePasswordClick,
|
||||
}: AccountMenuContentProps) {
|
||||
const user = useUserData();
|
||||
const { signOut } = useSignOut();
|
||||
const router = useRouter();
|
||||
const apolloClient = useApolloClient();
|
||||
const { handleClose } = useDropdown();
|
||||
const { publicRuntimeConfig } = getConfig();
|
||||
|
||||
return (
|
||||
<Box className="relative grid w-full grid-flow-row gap-5 p-6">
|
||||
<div className="grid grid-flow-row justify-center">
|
||||
<Avatar
|
||||
className="mx-auto mb-2 h-16 w-16 rounded-full"
|
||||
name={user?.displayName}
|
||||
avatarUrl={user?.avatarUrl}
|
||||
/>
|
||||
|
||||
<Text variant="h3" component="h2" className="text-center">
|
||||
{user?.displayName}
|
||||
</Text>
|
||||
|
||||
<Text className="text-center font-medium">{user?.email}</Text>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-flow-row gap-2">
|
||||
<Button
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
onClick={() => {
|
||||
onChangePasswordClick();
|
||||
handleClose();
|
||||
}}
|
||||
>
|
||||
Change Password
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
onClick={async () => {
|
||||
await apolloClient.clearStore();
|
||||
await signOut();
|
||||
await router.push('/signin');
|
||||
}}
|
||||
endIcon={<PowerIcon className="mr-1 h-4 w-4" />}
|
||||
>
|
||||
Sign Out
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<ThemeSwitcher label="Theme" />
|
||||
|
||||
<Text className="text-center text-xs" color="disabled">
|
||||
Dashboard Version: {publicRuntimeConfig?.version || 'n/a'}
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export function AccountMenu() {
|
||||
const user = useUserData();
|
||||
const [changePasswordModal, setChangePasswordModal] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (window.location.hash.search('type=passwordReset') !== -1) {
|
||||
setChangePasswordModal(true);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
showModal={changePasswordModal}
|
||||
close={() => setChangePasswordModal(false)}
|
||||
>
|
||||
<ChangePasswordModal close={() => setChangePasswordModal(false)} />
|
||||
</Modal>
|
||||
|
||||
<Dropdown.Root>
|
||||
<Dropdown.Trigger hideChevron className="rounded-full">
|
||||
<Avatar
|
||||
className="h-7 w-7 self-center rounded-full"
|
||||
name={user?.displayName}
|
||||
avatarUrl={user?.avatarUrl}
|
||||
/>
|
||||
</Dropdown.Trigger>
|
||||
|
||||
<Dropdown.Content PaperProps={{ className: 'mt-1 max-w-xs w-full' }}>
|
||||
<AccountMenuContent
|
||||
onChangePasswordClick={() => setChangePasswordModal(true)}
|
||||
/>
|
||||
</Dropdown.Content>
|
||||
</Dropdown.Root>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default AccountMenu;
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './BaseColumnForm';
|
||||
export { default } from './BaseColumnForm';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './BaseForeignKeyForm';
|
||||
export { BaseForeignKeyForm as default } from './BaseForeignKeyForm';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './BaseRecordForm';
|
||||
export { default } from './BaseRecordForm';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './BaseTableForm';
|
||||
export { default } from './BaseTableForm';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './ColumnAutocomplete';
|
||||
export { default } from './ColumnAutocomplete';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './CreateColumnForm';
|
||||
export { default } from './CreateColumnForm';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './CreateForeignKeyForm';
|
||||
export { default } from './CreateForeignKeyForm';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './CreateRecordForm';
|
||||
export { default } from './CreateRecordForm';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './CreateTableForm';
|
||||
export { default } from './CreateTableForm';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './DataBrowserEmptyState';
|
||||
export { default } from './DataBrowserEmptyState';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './DataBrowserGrid';
|
||||
export { default } from './DataBrowserGrid';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './DataBrowserGridControls';
|
||||
export { default } from './DataBrowserGridControls';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './DataBrowserLayout';
|
||||
export { default } from './DataBrowserLayout';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './DataBrowserSidebar';
|
||||
export { default } from './DataBrowserSidebar';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './DatabaseRecordInputGroup';
|
||||
export { default } from './DatabaseRecordInputGroup';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './EditColumnForm';
|
||||
export { default } from './EditColumnForm';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './EditForeignKeyForm';
|
||||
export { default } from './EditForeignKeyForm';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './EditPermissionsForm';
|
||||
export { default } from './EditPermissionsForm';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './EditTableForm';
|
||||
export { default } from './EditTableForm';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './RuleGroupEditor';
|
||||
export { default } from './RuleGroupEditor';
|
||||
@@ -1,19 +1,19 @@
|
||||
import DataGridBody from '@/components/common/DataGridBody';
|
||||
import DataGridFrame from '@/components/common/DataGridFrame';
|
||||
import type { DataGridHeaderProps } from '@/components/common/DataGridHeader';
|
||||
import DataGridHeader from '@/components/common/DataGridHeader';
|
||||
import DataBrowserEmptyState from '@/components/dataBrowser/DataBrowserEmptyState';
|
||||
import { DataGridProvider } from '@/context/DataGridContext';
|
||||
import type { UseDataGridOptions } from '@/hooks/useDataGrid';
|
||||
import useDataGrid from '@/hooks/useDataGrid';
|
||||
import type { DataBrowserGridColumn } from '@/types/dataBrowser';
|
||||
import ActivityIndicator from '@/ui/v2/ActivityIndicator';
|
||||
import Box from '@/ui/v2/Box';
|
||||
import type { UseDataGridOptions } from '@/components/dataGrid/DataGrid/useDataGrid';
|
||||
import { DataGridBody } from '@/components/dataGrid/DataGridBody';
|
||||
import { DataGridConfigProvider } from '@/components/dataGrid/DataGridConfigProvider';
|
||||
import { DataGridFrame } from '@/components/dataGrid/DataGridFrame';
|
||||
import type { DataGridHeaderProps } from '@/components/dataGrid/DataGridHeader';
|
||||
import { DataGridHeader } from '@/components/dataGrid/DataGridHeader';
|
||||
import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
|
||||
import { Box } from '@/components/ui/v2/Box';
|
||||
import { DataBrowserEmptyState } from '@/features/database/dataGrid/components/DataBrowserEmptyState';
|
||||
import type { DataBrowserGridColumn } from '@/features/database/dataGrid/types/dataBrowser';
|
||||
import type { ForwardedRef } from 'react';
|
||||
import { forwardRef, useEffect, useRef } from 'react';
|
||||
import mergeRefs from 'react-merge-refs';
|
||||
import type { Column, Row, SortingRule, TableOptions } from 'react-table';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
import useDataGrid from './useDataGrid';
|
||||
|
||||
export interface DataGridProps<TColumnData extends object>
|
||||
extends Omit<UseDataGridOptions<TColumnData>, 'tableRef'> {
|
||||
@@ -130,7 +130,7 @@ function DataGrid<TColumnData extends object>(
|
||||
}, [allowSort, dataGridProps.state.sortBy, onSort, toggleAllRowsSelected]);
|
||||
|
||||
return (
|
||||
<DataGridProvider
|
||||
<DataGridConfigProvider
|
||||
toggleAllRowsSelected={toggleAllRowsSelected}
|
||||
setSortBy={setSortBy}
|
||||
tableRef={tableRef}
|
||||
@@ -176,7 +176,7 @@ function DataGrid<TColumnData extends object>(
|
||||
|
||||
{loading && <ActivityIndicator delay={1000} className="my-4" />}
|
||||
</>
|
||||
</DataGridProvider>
|
||||
</DataGridConfigProvider>
|
||||
);
|
||||
}
|
||||
|
||||
4
dashboard/src/components/dataGrid/DataGrid/index.ts
Normal file
4
dashboard/src/components/dataGrid/DataGrid/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export * from './DataGrid';
|
||||
export { default as DataGrid } from './DataGrid';
|
||||
export * from './useDataGrid';
|
||||
export { default as useDataGrid } from './useDataGrid';
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user