Compare commits
178 Commits
@nhost/das
...
@nhost/das
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a74a97cd0 | ||
|
|
291185e609 | ||
|
|
d3f965048a | ||
|
|
9b0d4dde50 | ||
|
|
06d6ae0e86 | ||
|
|
1777c147b2 | ||
|
|
9d784b82c8 | ||
|
|
15d84a1966 | ||
|
|
8a1a1e06aa | ||
|
|
eb1d7137cf | ||
|
|
cf55e8e111 | ||
|
|
b47c797d73 | ||
|
|
dafc4b2635 | ||
|
|
0ccd9c82b1 | ||
|
|
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 | ||
|
|
726c33d1b2 | ||
|
|
11b9cfbc0d | ||
|
|
79aaa91e67 | ||
|
|
df4d24320a | ||
|
|
baa3ef794e | ||
|
|
da7ffbe523 | ||
|
|
15a985e079 | ||
|
|
8ff00a4258 | ||
|
|
7e27d7c0a1 | ||
|
|
49f9b8372a | ||
|
|
3ca70554c8 | ||
|
|
077b200510 | ||
|
|
2f220db84a |
@@ -14,7 +14,7 @@ runs:
|
||||
steps:
|
||||
- uses: pnpm/action-setup@v2.2.4
|
||||
with:
|
||||
version: 7.17.0
|
||||
version: 8.4.0
|
||||
run_install: false
|
||||
- name: Get pnpm cache directory
|
||||
id: pnpm-cache-dir
|
||||
|
||||
@@ -36,6 +36,7 @@ export default defineConfig({
|
||||
}
|
||||
},
|
||||
build: {
|
||||
target: 'es2019',
|
||||
sourcemap: true,
|
||||
lib: {
|
||||
entry,
|
||||
|
||||
@@ -25,8 +25,11 @@ module.exports = {
|
||||
'error',
|
||||
{ allowArrowFunctions: true, allowFunctions: true },
|
||||
],
|
||||
'import/no-named-as-default': 'off',
|
||||
'import/prefer-default-export': 'off',
|
||||
'import/no-extraneous-dependencies': ['error', { devDependencies: true }],
|
||||
// TODO: Temporarily disable this rule because of a WIP refactoring
|
||||
'import/no-named-as-default': 'off',
|
||||
curly: ['error', 'all'],
|
||||
'no-restricted-exports': 'off',
|
||||
'no-undef': 'off',
|
||||
|
||||
@@ -1,5 +1,80 @@
|
||||
# @nhost/dashboard
|
||||
|
||||
## 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
|
||||
|
||||
@@ -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.9.8
|
||||
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.4.0
|
||||
COPY .gitignore .gitignore
|
||||
COPY --from=pruner /app/out/json/ .
|
||||
COPY --from=pruner /app/out/pnpm-*.yaml .
|
||||
|
||||
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();
|
||||
});
|
||||
@@ -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.6",
|
||||
"version": "0.17.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
@@ -15,7 +15,7 @@
|
||||
"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.6",
|
||||
"@types/react-dom": "18.2.4",
|
||||
"@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-c8": "^0.31.0",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"babel-loader": "^8.3.0",
|
||||
"babel-plugin-transform-remove-console": "^6.9.4",
|
||||
@@ -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.31.0"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
|
||||
@@ -114,6 +114,9 @@ export default function AppDeployments(props: AppDeploymentsProps) {
|
||||
const { deployments } = deploymentPageData || { deployments: [] };
|
||||
const { deployments: scheduledOrPendingDeployments } =
|
||||
scheduledOrPendingDeploymentsData || { deployments: [] };
|
||||
const isDeploymentInProgress = deployments?.some((deployment) =>
|
||||
['PENDING', 'SCHEDULED'].includes(deployment.deploymentStatus),
|
||||
);
|
||||
|
||||
const latestDeployment = latestDeploymentData?.deployments[0];
|
||||
const latestLiveDeployment = latestLiveDeploymentData?.deployments[0];
|
||||
@@ -135,7 +138,10 @@ export default function AppDeployments(props: AppDeploymentsProps) {
|
||||
deployment={deployment}
|
||||
isLive={liveDeploymentId === deployment.id}
|
||||
showRedeploy={latestDeployment.id === deployment.id}
|
||||
disableRedeploy={scheduledOrPendingDeployments?.length > 0}
|
||||
disableRedeploy={
|
||||
scheduledOrPendingDeployments?.length > 0 ||
|
||||
isDeploymentInProgress
|
||||
}
|
||||
/>
|
||||
|
||||
{index !== deployments.length - 1 && <Divider component="li" />}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import FeedbackForm from '@/components/common/FeedbackForm';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
||||
import { FeedbackForm } from '@/components/common/FeedbackForm';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/hooks/useCurrentWorkspaceAndProject';
|
||||
import { useInterval } from '@/hooks/useInterval';
|
||||
import ActivityIndicator from '@/ui/v2/ActivityIndicator';
|
||||
import Button from '@/ui/v2/Button';
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import FeedbackForm from '@/components/common/FeedbackForm';
|
||||
import { FeedbackForm } from '@/components/common/FeedbackForm';
|
||||
import Container from '@/components/layout/Container';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
||||
import { useIsCurrentUserOwner } from '@/features/projects/common/hooks/useIsCurrentUserOwner';
|
||||
import { useAppCreatedAt } from '@/hooks/useAppCreatedAt';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/hooks/useCurrentWorkspaceAndProject';
|
||||
import { useIsCurrentUserOwner } from '@/features/projects/hooks/useIsCurrentUserOwner';
|
||||
import { useCurrentDate } from '@/hooks/useCurrentDate';
|
||||
import type { ApplicationState } from '@/types/application';
|
||||
import { ApplicationStatus } from '@/types/application';
|
||||
@@ -11,18 +10,16 @@ import ActivityIndicator from '@/ui/v2/ActivityIndicator';
|
||||
import Button from '@/ui/v2/Button';
|
||||
import { Dropdown } from '@/ui/v2/Dropdown';
|
||||
import Text from '@/ui/v2/Text';
|
||||
import { discordAnnounce } from '@/utils/discordAnnounce';
|
||||
import { getPreviousApplicationState } from '@/utils/getPreviousApplicationState';
|
||||
import { getApplicationStatusString } from '@/utils/helpers';
|
||||
import { triggerToast } from '@/utils/toast';
|
||||
import {
|
||||
useDeleteApplicationMutation,
|
||||
useGetApplicationStateQuery,
|
||||
useInsertApplicationMutation,
|
||||
useUpdateApplicationMutation,
|
||||
} from '@/utils/__generated__/graphql';
|
||||
import { discordAnnounce } from '@/utils/discordAnnounce';
|
||||
import { getPreviousApplicationState } from '@/utils/getPreviousApplicationState';
|
||||
import { getApplicationStatusString } from '@/utils/helpers';
|
||||
import { triggerToast } from '@/utils/toast';
|
||||
import { updateOwnCache } from '@/utils/updateOwnCache';
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
import { useUserData } from '@nhost/nextjs';
|
||||
import Image from 'next/image';
|
||||
import { useState } from 'react';
|
||||
@@ -32,7 +29,11 @@ import { RemoveApplicationModal } from './RemoveApplicationModal';
|
||||
import { StagingMetadata } from './StagingMetadata';
|
||||
|
||||
export default function ApplicationErrored() {
|
||||
const { currentWorkspace, currentProject } = useCurrentWorkspaceAndProject();
|
||||
const {
|
||||
currentWorkspace,
|
||||
currentProject,
|
||||
refetch: refetchProject,
|
||||
} = useCurrentWorkspaceAndProject();
|
||||
const [changingApplicationStateLoading, setChangingApplicationStateLoading] =
|
||||
useState(false);
|
||||
|
||||
@@ -54,12 +55,11 @@ export default function ApplicationErrored() {
|
||||
const [showRecreateModal, setShowRecreateModal] = useState(false);
|
||||
const [showDeleteModal, setShowDeleteModal] = useState(false);
|
||||
const [insertApp] = useInsertApplicationMutation();
|
||||
const client = useApolloClient();
|
||||
const { currentDate } = useCurrentDate();
|
||||
const user = useUserData();
|
||||
const isOwner = useIsCurrentUserOwner();
|
||||
|
||||
const { appCreatedAt } = useAppCreatedAt();
|
||||
const appCreatedAt = new Date(currentProject.createdAt).getTime();
|
||||
|
||||
const FIVE_DAYS_IN_MILLISECONDS = 60 * 24 * 60 * 5 * 1000;
|
||||
const HALF_DAY_IN_MILLISECONDS = 60 * 12 * 60 * 1000;
|
||||
@@ -94,7 +94,7 @@ export default function ApplicationErrored() {
|
||||
});
|
||||
discordAnnounce(`Recreating: ${currentProject?.name} (${user.email})`);
|
||||
triggerToast(`Recreating ${currentProject?.name} `);
|
||||
await updateOwnCache(client);
|
||||
await refetchProject();
|
||||
} catch (e) {
|
||||
triggerToast(`Error trying to recreate: ${currentProject?.name}`);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/hooks/useCurrentWorkspaceAndProject';
|
||||
import {
|
||||
GetAllWorkspacesAndProjectsDocument,
|
||||
useDeleteApplicationMutation,
|
||||
} from '@/generated/graphql';
|
||||
import Button from '@/ui/v2/Button';
|
||||
import Link from '@/ui/v2/Link';
|
||||
import Text from '@/ui/v2/Text';
|
||||
import ArrowRightIcon from '@/ui/v2/icons/ArrowRightIcon';
|
||||
import { Button } from '@/ui/v2/Button';
|
||||
import { ArrowRightIcon } from '@/ui/v2/icons/ArrowRightIcon';
|
||||
import { Link } from '@/ui/v2/Link';
|
||||
import { Text } from '@/ui/v2/Text';
|
||||
import { copy } from '@/utils/copy';
|
||||
import { getApplicationStatusString } from '@/utils/helpers';
|
||||
import getServerError from '@/utils/settings/getServerError';
|
||||
import { getServerError } from '@/utils/settings/getServerError';
|
||||
import { getToastStyleProps } from '@/utils/settings/settingsConstants';
|
||||
import { formatDistance } from 'date-fns';
|
||||
import { useRouter } from 'next/router';
|
||||
@@ -18,18 +18,14 @@ import { toast } from 'react-hot-toast';
|
||||
export default function ApplicationInfo() {
|
||||
const { currentProject } = useCurrentWorkspaceAndProject();
|
||||
const [deleteApplication] = useDeleteApplicationMutation({
|
||||
refetchQueries: [GetAllWorkspacesAndProjectsDocument],
|
||||
refetchQueries: [{ query: GetAllWorkspacesAndProjectsDocument }],
|
||||
});
|
||||
const router = useRouter();
|
||||
|
||||
async function handleClickRemove() {
|
||||
try {
|
||||
await toast.promise(
|
||||
deleteApplication({
|
||||
variables: {
|
||||
appId: currentProject.id,
|
||||
},
|
||||
}),
|
||||
deleteApplication({ variables: { appId: currentProject.id } }),
|
||||
{
|
||||
loading: 'Deleting project...',
|
||||
success: 'The project has been deleted successfully.',
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import MaintenanceAlert from '@/components/common/MaintenanceAlert';
|
||||
import RetryableErrorBoundary from '@/components/common/RetryableErrorBoundary';
|
||||
import { MaintenanceAlert } from '@/components/common/MaintenanceAlert';
|
||||
import { RetryableErrorBoundary } from '@/components/common/RetryableErrorBoundary';
|
||||
import Container from '@/components/layout/Container';
|
||||
import OverviewDeployments from '@/components/overview/OverviewDeployments';
|
||||
import OverviewDocumentation from '@/components/overview/OverviewDocumentation';
|
||||
import OverviewMetrics from '@/components/overview/OverviewMetrics/OverviewMetrics';
|
||||
import OverviewProjectInfo from '@/components/overview/OverviewProjectInfo';
|
||||
import OverviewRepository from '@/components/overview/OverviewRepository';
|
||||
import OverviewTopBar from '@/components/overview/OverviewTopBar';
|
||||
import OverviewUsage from '@/components/overview/OverviewUsage';
|
||||
import { features } from '@/components/overview/features';
|
||||
import { frameworks } from '@/components/overview/frameworks';
|
||||
import useIsPlatform from '@/hooks/common/useIsPlatform';
|
||||
import { useIsPlatform } from '@/features/projects/hooks/useIsPlatform';
|
||||
import { OverviewDeployments } from '@/features/projects/overview/components/OverviewDeployments';
|
||||
import { OverviewDocumentation } from '@/features/projects/overview/components/OverviewDocumentation';
|
||||
import { OverviewMetrics } from '@/features/projects/overview/components/OverviewMetrics';
|
||||
import { OverviewProjectInfo } from '@/features/projects/overview/components/OverviewProjectInfo';
|
||||
import { OverviewRepository } from '@/features/projects/overview/components/OverviewRepository';
|
||||
import { OverviewTopBar } from '@/features/projects/overview/components/OverviewTopBar';
|
||||
import { OverviewUsage } from '@/features/projects/overview/components/OverviewUsage';
|
||||
import { features } from '@/features/projects/overview/features';
|
||||
import { frameworks } from '@/features/projects/overview/frameworks';
|
||||
import { Alert } from '@/ui/Alert';
|
||||
import Divider from '@/ui/v2/Divider';
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@ import { ChangePlanModal } from '@/components/applications/ChangePlanModal';
|
||||
import { StagingMetadata } from '@/components/applications/StagingMetadata';
|
||||
import { useDialog } from '@/components/common/DialogProvider';
|
||||
import Container from '@/components/layout/Container';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
||||
import { useIsCurrentUserOwner } from '@/features/projects/common/hooks/useIsCurrentUserOwner';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/hooks/useCurrentWorkspaceAndProject';
|
||||
import { useIsCurrentUserOwner } from '@/features/projects/hooks/useIsCurrentUserOwner';
|
||||
import {
|
||||
GetAllWorkspacesAndProjectsDocument,
|
||||
useGetFreeAndActiveProjectsQuery,
|
||||
@@ -35,12 +35,11 @@ export default function ApplicationPaused() {
|
||||
const [showDeletingModal, setShowDeletingModal] = useState(false);
|
||||
const [unpauseApplication, { loading: changingApplicationStateLoading }] =
|
||||
useUnpauseApplicationMutation({
|
||||
refetchQueries: [GetAllWorkspacesAndProjectsDocument],
|
||||
refetchQueries: [{ query: GetAllWorkspacesAndProjectsDocument }],
|
||||
});
|
||||
|
||||
const { data, loading } = useGetFreeAndActiveProjectsQuery({
|
||||
variables: { userId: user?.id },
|
||||
fetchPolicy: 'cache-and-network',
|
||||
skip: !user,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Container from '@/components/layout/Container';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/hooks/useCurrentWorkspaceAndProject';
|
||||
import { useCheckProvisioning } from '@/hooks/useCheckProvisioning';
|
||||
import { ApplicationStatus } from '@/types/application';
|
||||
import ActivityIndicator from '@/ui/v2/ActivityIndicator';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Container from '@/components/layout/Container';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/hooks/useCurrentWorkspaceAndProject';
|
||||
import { useCheckProvisioning } from '@/hooks/useCheckProvisioning';
|
||||
import { ApplicationStatus } from '@/types/application';
|
||||
import ActivityIndicator from '@/ui/v2/ActivityIndicator';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import FeedbackForm from '@/components/common/FeedbackForm';
|
||||
import { FeedbackForm } from '@/components/common/FeedbackForm';
|
||||
import Container from '@/components/layout/Container';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
||||
import { useIsCurrentUserOwner } from '@/features/projects/common/hooks/useIsCurrentUserOwner';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/hooks/useCurrentWorkspaceAndProject';
|
||||
import { useIsCurrentUserOwner } from '@/features/projects/hooks/useIsCurrentUserOwner';
|
||||
import { Modal } from '@/ui/Modal';
|
||||
import Button from '@/ui/v2/Button';
|
||||
import { Dropdown } from '@/ui/v2/Dropdown';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Container from '@/components/layout/Container';
|
||||
import { useProjectRedirectWhenReady } from '@/features/projects/common/hooks/useProjectRedirectWhenReady';
|
||||
import { useProjectRedirectWhenReady } from '@/features/projects/hooks/useProjectRedirectWhenReady';
|
||||
import Image from 'next/image';
|
||||
import { AppLoader } from './AppLoader';
|
||||
|
||||
|
||||
@@ -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,6 +1,6 @@
|
||||
import { useDialog } from '@/components/common/DialogProvider';
|
||||
import { BillingPaymentMethodForm } from '@/components/workspace/BillingPaymentMethodForm';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/hooks/useCurrentWorkspaceAndProject';
|
||||
import {
|
||||
refetchGetApplicationPlanQuery,
|
||||
useGetAppPlanAndGlobalPlansQuery,
|
||||
@@ -9,15 +9,15 @@ import {
|
||||
} from '@/generated/graphql';
|
||||
import useApplicationState from '@/hooks/useApplicationState';
|
||||
import { ApplicationStatus } from '@/types/application';
|
||||
import ActivityIndicator from '@/ui/v2/ActivityIndicator';
|
||||
import Box from '@/ui/v2/Box';
|
||||
import Button from '@/ui/v2/Button';
|
||||
import Checkbox from '@/ui/v2/Checkbox';
|
||||
import { ActivityIndicator } from '@/ui/v2/ActivityIndicator';
|
||||
import { Box } from '@/ui/v2/Box';
|
||||
import { Button } from '@/ui/v2/Button';
|
||||
import { Checkbox } from '@/ui/v2/Checkbox';
|
||||
import { BaseDialog } from '@/ui/v2/Dialog';
|
||||
import Link from '@/ui/v2/Link';
|
||||
import Text from '@/ui/v2/Text';
|
||||
import { Link } from '@/ui/v2/Link';
|
||||
import { Text } from '@/ui/v2/Text';
|
||||
import { planDescriptions } from '@/utils/planDescriptions';
|
||||
import getServerError from '@/utils/settings/getServerError';
|
||||
import { getServerError } from '@/utils/settings/getServerError';
|
||||
import { getToastStyleProps } from '@/utils/settings/settingsConstants';
|
||||
import Image from 'next/image';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import { LoadingScreen } from '@/components/common/LoadingScreen';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
||||
import useIsPlatform from '@/hooks/common/useIsPlatform';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/hooks/useCurrentWorkspaceAndProject';
|
||||
import { useIsPlatform } from '@/features/projects/hooks/useIsPlatform';
|
||||
import Box from '@/ui/v2/Box';
|
||||
import Button from '@/ui/v2/Button';
|
||||
import IconButton from '@/ui/v2/IconButton';
|
||||
import Text from '@/ui/v2/Text';
|
||||
import ArrowSquareOutIcon from '@/ui/v2/icons/ArrowSquareOutIcon';
|
||||
import CopyIcon from '@/ui/v2/icons/CopyIcon';
|
||||
import generateAppServiceUrl, {
|
||||
import Text from '@/ui/v2/Text';
|
||||
import {
|
||||
defaultLocalBackendSlugs,
|
||||
defaultRemoteBackendSlugs,
|
||||
generateAppServiceUrl,
|
||||
} from '@/utils/common/generateAppServiceUrl';
|
||||
import { copy } from '@/utils/copy';
|
||||
import { getHasuraConsoleServiceUrl } from '@/utils/env';
|
||||
@@ -33,7 +34,7 @@ export function HasuraData({ close }: HasuraDataProps) {
|
||||
? `${getHasuraConsoleServiceUrl()}`
|
||||
: generateAppServiceUrl(
|
||||
currentProject?.subdomain,
|
||||
currentProject?.region.awsName,
|
||||
currentProject?.region,
|
||||
'hasura',
|
||||
defaultLocalBackendSlugs,
|
||||
{ ...defaultRemoteBackendSlugs, hasura: '/console' },
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/hooks/useCurrentWorkspaceAndProject';
|
||||
import Box from '@/ui/v2/Box';
|
||||
import Button from '@/ui/v2/Button';
|
||||
import Checkbox from '@/ui/v2/Checkbox';
|
||||
@@ -46,7 +46,7 @@ export function RemoveApplicationModal({
|
||||
className,
|
||||
}: RemoveApplicationModalProps) {
|
||||
const [deleteApplication] = useDeleteApplicationMutation({
|
||||
refetchQueries: [GetAllWorkspacesAndProjectsDocument],
|
||||
refetchQueries: [{ query: GetAllWorkspacesAndProjectsDocument }],
|
||||
});
|
||||
const [loadingRemove, setLoadingRemove] = useState(false);
|
||||
const { currentProject } = useCurrentWorkspaceAndProject();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/hooks/useCurrentWorkspaceAndProject';
|
||||
import Box from '@/ui/v2/Box';
|
||||
import Button from '@/ui/v2/Button';
|
||||
import Checkbox from '@/ui/v2/Checkbox';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ChangePlanModal } from '@/components/applications/ChangePlanModal';
|
||||
import { useDialog } from '@/components/common/DialogProvider';
|
||||
import { useIsCurrentUserOwner } from '@/features/projects/common/hooks/useIsCurrentUserOwner';
|
||||
import { useIsCurrentUserOwner } from '@/features/projects/hooks/useIsCurrentUserOwner';
|
||||
import { Alert } from '@/ui/Alert';
|
||||
import Button from '@/ui/v2/Button';
|
||||
import Text from '@/ui/v2/Text';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { ConnectGithubModalState } from '@/components/applications/ConnectGithubModal';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/hooks/useCurrentWorkspaceAndProject';
|
||||
import { FormProvider, useForm } from 'react-hook-form';
|
||||
import { EditRepositorySettingsModal } from './EditRepositorySettingsModal';
|
||||
|
||||
|
||||
@@ -2,14 +2,12 @@ import type { EditRepositorySettingsFormData } from '@/components/applications/g
|
||||
import { useDialog } from '@/components/common/DialogProvider';
|
||||
import ErrorBoundaryFallback from '@/components/common/ErrorBoundaryFallback';
|
||||
import GithubIcon from '@/components/icons/GithubIcon';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/hooks/useCurrentWorkspaceAndProject';
|
||||
import { useUpdateApplicationMutation } from '@/generated/graphql';
|
||||
import Button from '@/ui/v2/Button';
|
||||
import Text from '@/ui/v2/Text';
|
||||
import { discordAnnounce } from '@/utils/discordAnnounce';
|
||||
import { triggerToast } from '@/utils/toast';
|
||||
import { updateOwnCache } from '@/utils/updateOwnCache';
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
import { ErrorBoundary } from 'react-error-boundary';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import { RepoAndBranch } from './RepoAndBranch';
|
||||
@@ -27,12 +25,11 @@ export function EditRepositorySettingsModal({
|
||||
const isNotCompleted = !watch('productionBranch') || !watch('repoBaseFolder');
|
||||
const { closeAlertDialog } = useDialog();
|
||||
|
||||
const { currentProject } = useCurrentWorkspaceAndProject();
|
||||
const { currentProject, refetch: refetchProject } =
|
||||
useCurrentWorkspaceAndProject();
|
||||
|
||||
const [updateApp, { loading }] = useUpdateApplicationMutation();
|
||||
|
||||
const client = useApolloClient();
|
||||
|
||||
const handleEditGitHubIntegration = async (
|
||||
data: EditRepositorySettingsFormData,
|
||||
) => {
|
||||
@@ -60,7 +57,8 @@ export function EditRepositorySettingsModal({
|
||||
});
|
||||
}
|
||||
|
||||
await updateOwnCache(client);
|
||||
await refetchProject();
|
||||
|
||||
if (close) {
|
||||
close();
|
||||
} else {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/hooks/useCurrentWorkspaceAndProject';
|
||||
import { useRemoteApplicationGQLClient } from '@/hooks/useRemoteApplicationGQLClient';
|
||||
import ActivityIndicator from '@/ui/v2/ActivityIndicator';
|
||||
import Option from '@/ui/v2/Option';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import NavLink from '@/components/common/NavLink';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
||||
import useIsPlatform from '@/hooks/common/useIsPlatform';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/hooks/useCurrentWorkspaceAndProject';
|
||||
import { useIsPlatform } from '@/features/projects/hooks/useIsPlatform';
|
||||
import type { BoxProps } from '@/ui/v2/Box';
|
||||
import Box from '@/ui/v2/Box';
|
||||
import Text from '@/ui/v2/Text';
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export * from './Breadcrumbs';
|
||||
export { default } from './Breadcrumbs';
|
||||
export { default as Breadcrumbs } from './Breadcrumbs';
|
||||
|
||||
@@ -2,7 +2,7 @@ import type {
|
||||
AutocompleteOption,
|
||||
AutocompleteProps,
|
||||
} from '@/ui/v2/Autocomplete';
|
||||
import Autocomplete from '@/ui/v2/Autocomplete';
|
||||
import { Autocomplete } from '@/ui/v2/Autocomplete';
|
||||
import callAll from '@/utils/callAll';
|
||||
import type { ForwardedRef } from 'react';
|
||||
import { forwardRef } from 'react';
|
||||
|
||||
@@ -1,2 +1,5 @@
|
||||
export * from './ControlledAutocomplete';
|
||||
export { default } from './ControlledAutocomplete';
|
||||
export {
|
||||
default as ControlledAutocomplete,
|
||||
default,
|
||||
} from './ControlledAutocomplete';
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export * from './ControlledCheckbox';
|
||||
export { default } from './ControlledCheckbox';
|
||||
export { default as ControlledCheckbox, default } from './ControlledCheckbox';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { SelectProps } from '@/ui/v2/Select';
|
||||
import Select from '@/ui/v2/Select';
|
||||
import { Select } from '@/ui/v2/Select';
|
||||
import type { ForwardedRef } from 'react';
|
||||
import { forwardRef } from 'react';
|
||||
import type { FieldValues, UseControllerProps } from 'react-hook-form';
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export * from './ControlledSelect';
|
||||
export { default } from './ControlledSelect';
|
||||
export { default as ControlledSelect, default } from './ControlledSelect';
|
||||
|
||||
@@ -2,11 +2,11 @@ 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 { DataBrowserEmptyState } from '@/features/database/dataGrid/components/DataBrowserEmptyState';
|
||||
import type { DataBrowserGridColumn } from '@/features/database/dataGrid/types/dataBrowser';
|
||||
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 { ForwardedRef } from 'react';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { DataGridProps } from '@/components/common/DataGrid';
|
||||
import DataGridCell from '@/components/common/DataGridCell';
|
||||
import type { DataBrowserGridColumn } from '@/features/database/dataGrid/types/dataBrowser';
|
||||
import useDataGridConfig from '@/hooks/useDataGridConfig';
|
||||
import type { DataBrowserGridColumn } from '@/types/dataBrowser';
|
||||
import type { BoxProps } from '@/ui/v2/Box';
|
||||
import Box from '@/ui/v2/Box';
|
||||
import Button from '@/ui/v2/Button';
|
||||
|
||||
@@ -3,7 +3,7 @@ import type {
|
||||
ColumnType,
|
||||
DataBrowserGridCell,
|
||||
DataBrowserGridCellProps,
|
||||
} from '@/types/dataBrowser';
|
||||
} from '@/features/database/dataGrid/types/dataBrowser';
|
||||
import type { BoxProps } from '@/ui/v2/Box';
|
||||
import Box from '@/ui/v2/Box';
|
||||
import Tooltip, { useTooltip } from '@/ui/v2/Tooltip';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { DataGridProps } from '@/components/common/DataGrid';
|
||||
import type { DataBrowserGridColumn } from '@/features/database/dataGrid/types/dataBrowser';
|
||||
import useDataGridConfig from '@/hooks/useDataGridConfig';
|
||||
import type { DataBrowserGridColumn } from '@/types/dataBrowser';
|
||||
import Box from '@/ui/v2/Box';
|
||||
import Button from '@/ui/v2/Button';
|
||||
import Divider from '@/ui/v2/Divider';
|
||||
|
||||
@@ -2,8 +2,8 @@ import AudioPreview from '@/components/icons/AudioPreview';
|
||||
import { FileIcon } from '@/components/icons/FileIcon';
|
||||
import PDFPreview from '@/components/icons/PDFPreview';
|
||||
import VideoPreview from '@/components/icons/VideoPreview';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
||||
import { useAppClient } from '@/hooks/useAppClient';
|
||||
import { useAppClient } from '@/features/projects/hooks/useAppClient';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/hooks/useCurrentWorkspaceAndProject';
|
||||
import { Modal } from '@/ui/Modal';
|
||||
import ActivityIndicator from '@/ui/v2/ActivityIndicator';
|
||||
import Box from '@/ui/v2/Box';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { IconLinkProps } from '@/components/common/IconLink';
|
||||
import IconLink from '@/components/common/IconLink';
|
||||
import { Nav } from '@/components/dashboard/Nav';
|
||||
import useProjectRoutes from '@/hooks/common/useProjectRoutes';
|
||||
import { useProjectRoutes } from '@/features/projects/hooks/useProjectRoutes';
|
||||
import type { BoxProps } from '@/ui/v2/Box';
|
||||
import Box from '@/ui/v2/Box';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export * from './FeedbackForm';
|
||||
export { default } from './FeedbackForm';
|
||||
export { default as FeedbackForm } from './FeedbackForm';
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export * from './Form';
|
||||
export { default } from './Form';
|
||||
export { default as Form, default } from './Form';
|
||||
|
||||
@@ -1,2 +1,5 @@
|
||||
export * from './FormActivityIndicator';
|
||||
export { default } from './FormActivityIndicator';
|
||||
export {
|
||||
default,
|
||||
default as FormActivityIndicator,
|
||||
} from './FormActivityIndicator';
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
import Breadcrumbs from '@/components/common/Breadcrumbs';
|
||||
import FeedbackForm from '@/components/common/FeedbackForm';
|
||||
import LocalAccountMenu from '@/components/common/LocalAccountMenu';
|
||||
import Logo from '@/components/common/Logo';
|
||||
import MobileNav from '@/components/common/MobileNav';
|
||||
import NavLink from '@/components/common/NavLink';
|
||||
import { Breadcrumbs } from '@/components/common/Breadcrumbs';
|
||||
import { FeedbackForm } from '@/components/common/FeedbackForm';
|
||||
import { LocalAccountMenu } from '@/components/common/LocalAccountMenu';
|
||||
import { Logo } from '@/components/common/Logo';
|
||||
import { MobileNav } from '@/components/common/MobileNav';
|
||||
import { NavLink } from '@/components/common/NavLink';
|
||||
import { AccountMenu } from '@/components/dashboard/AccountMenu';
|
||||
import useIsPlatform from '@/hooks/common/useIsPlatform';
|
||||
import Box from '@/ui/v2/Box';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/hooks/useCurrentWorkspaceAndProject';
|
||||
import { useIsPlatform } from '@/features/projects/hooks/useIsPlatform';
|
||||
import { ApplicationStatus } from '@/types/application';
|
||||
import { Box } from '@/ui/v2/Box';
|
||||
import { Chip } from '@/ui/v2/Chip';
|
||||
import { Dropdown } from '@/ui/v2/Dropdown';
|
||||
import { useRouter } from 'next/router';
|
||||
import type { DetailedHTMLProps, HTMLProps, PropsWithoutRef } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
export interface HeaderProps
|
||||
@@ -20,6 +24,25 @@ export interface HeaderProps
|
||||
export default function Header({ className, ...props }: HeaderProps) {
|
||||
const router = useRouter();
|
||||
const isPlatform = useIsPlatform();
|
||||
const { currentProject, refetch: refetchProject } =
|
||||
useCurrentWorkspaceAndProject();
|
||||
const isProjectUpdating =
|
||||
currentProject?.appStates[0]?.stateId === ApplicationStatus.Updating;
|
||||
|
||||
// Poll for project updates
|
||||
useEffect(() => {
|
||||
if (!isProjectUpdating) {
|
||||
return () => {};
|
||||
}
|
||||
|
||||
const interval = setInterval(async () => {
|
||||
await refetchProject();
|
||||
}, 5000);
|
||||
|
||||
return () => {
|
||||
clearInterval(interval);
|
||||
};
|
||||
}, [isProjectUpdating, refetchProject]);
|
||||
|
||||
return (
|
||||
<Box
|
||||
@@ -39,6 +62,10 @@ export default function Header({ className, ...props }: HeaderProps) {
|
||||
{(router.query.workspaceSlug || router.query.appSlug) && (
|
||||
<Breadcrumbs aria-label="Workspace breadcrumbs" />
|
||||
)}
|
||||
|
||||
{isProjectUpdating && (
|
||||
<Chip size="small" label="Updating" color="warning" />
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="hidden grid-flow-col items-center gap-2 sm:grid">
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { default } from './HighlightedText';
|
||||
export { default as HighlightedText } from './HighlightedText';
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export * from './InlineCode';
|
||||
export { default } from './InlineCode';
|
||||
export { default, default as InlineCode } from './InlineCode';
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import ThemeSwitcher from '@/components/common/ThemeSwitcher';
|
||||
import { ThemeSwitcher } from '@/components/common/ThemeSwitcher';
|
||||
import { Avatar } from '@/ui/Avatar';
|
||||
import { Box } from '@/ui/v2/Box';
|
||||
import { Divider } from '@/ui/v2/Divider';
|
||||
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 { 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() {
|
||||
@@ -25,14 +28,38 @@ export default function LocalAccountMenu() {
|
||||
|
||||
<Dropdown.Content
|
||||
PaperProps={{
|
||||
className: 'mt-1 p-6 grid grid-flow-row gap-4 w-full max-w-xs',
|
||||
className: 'mt-1 grid grid-flow-row w-full max-w-xs',
|
||||
}}
|
||||
>
|
||||
<ThemeSwitcher label="Theme" />
|
||||
<Box className="grid grid-flow-col items-center justify-start gap-4 p-4">
|
||||
<Avatar name="Local User" className="h-10 w-10" />
|
||||
|
||||
<Text className="text-center text-xs" color="disabled">
|
||||
Dashboard Version: {publicRuntimeConfig?.version || 'n/a'}
|
||||
</Text>
|
||||
<Box className="grid grid-flow-row gap-0.5">
|
||||
<Text className="font-semibold">Local User</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Box className="grid grid-flow-row gap-2 p-2">
|
||||
<ThemeSwitcher
|
||||
label="Theme"
|
||||
variant="inline"
|
||||
fullWidth
|
||||
className="grid-cols-auto justify-between px-2"
|
||||
slotProps={{
|
||||
label: { className: '!text-sm+' },
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Box className="py-4">
|
||||
<Text className="text-center text-xs" color="disabled">
|
||||
Dashboard Version: {publicRuntimeConfig?.version || 'n/a'}
|
||||
</Text>
|
||||
</Box>
|
||||
</Dropdown.Content>
|
||||
</Dropdown.Root>
|
||||
);
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { default } from './LocalAccountMenu';
|
||||
export { default as LocalAccountMenu } from './LocalAccountMenu';
|
||||
|
||||
1
dashboard/src/components/common/Logo/index.ts
Normal file
1
dashboard/src/components/common/Logo/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default as Logo } from './Logo';
|
||||
@@ -1 +1 @@
|
||||
export { default } from './MaintenanceAlert';
|
||||
export { default as MaintenanceAlert } from './MaintenanceAlert';
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
import { ChangePasswordModal } from '@/components/applications/ChangePasswordModal';
|
||||
import FeedbackForm from '@/components/common/FeedbackForm';
|
||||
import NavLink from '@/components/common/NavLink';
|
||||
import ThemeSwitcher from '@/components/common/ThemeSwitcher';
|
||||
import { FeedbackForm } from '@/components/common/FeedbackForm';
|
||||
import { NavLink } from '@/components/common/NavLink';
|
||||
import { ThemeSwitcher } from '@/components/common/ThemeSwitcher';
|
||||
import { Nav } from '@/components/dashboard/Nav';
|
||||
import useIsPlatform from '@/hooks/common/useIsPlatform';
|
||||
import useProjectRoutes from '@/hooks/common/useProjectRoutes';
|
||||
import { useIsPlatform } from '@/features/projects/hooks/useIsPlatform';
|
||||
import { useProjectRoutes } from '@/features/projects/hooks/useProjectRoutes';
|
||||
import { useNavigationVisible } from '@/hooks/useNavigationVisible';
|
||||
import { Modal } from '@/ui/Modal';
|
||||
import type { ButtonProps } from '@/ui/v2/Button';
|
||||
import Button from '@/ui/v2/Button';
|
||||
import Divider from '@/ui/v2/Divider';
|
||||
import Drawer from '@/ui/v2/Drawer';
|
||||
import { Button } from '@/ui/v2/Button';
|
||||
import { Divider } from '@/ui/v2/Divider';
|
||||
import { Drawer } from '@/ui/v2/Drawer';
|
||||
import { Dropdown } from '@/ui/v2/Dropdown';
|
||||
import MenuIcon from '@/ui/v2/icons/MenuIcon';
|
||||
import XIcon from '@/ui/v2/icons/XIcon';
|
||||
import List from '@/ui/v2/List';
|
||||
import { MenuIcon } from '@/ui/v2/icons/MenuIcon';
|
||||
import { XIcon } from '@/ui/v2/icons/XIcon';
|
||||
import { List } from '@/ui/v2/List';
|
||||
import type { ListItemButtonProps } from '@/ui/v2/ListItem';
|
||||
import { ListItem } from '@/ui/v2/ListItem';
|
||||
import Text from '@/ui/v2/Text';
|
||||
import { Text } from '@/ui/v2/Text';
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
import { useSignOut } from '@nhost/nextjs';
|
||||
import getConfig from 'next/config';
|
||||
@@ -86,7 +84,6 @@ export default function MobileNav({ className, ...props }: MobileNavProps) {
|
||||
const { allRoutes } = useProjectRoutes();
|
||||
const shouldDisplayNav = useNavigationVisible();
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
const [showChangePasswordModal, setShowChangePasswordModal] = useState(false);
|
||||
const { signOut } = useSignOut();
|
||||
const apolloClient = useApolloClient();
|
||||
const router = useRouter();
|
||||
@@ -94,14 +91,6 @@ export default function MobileNav({ className, ...props }: MobileNavProps) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
showModal={showChangePasswordModal}
|
||||
close={() => setShowChangePasswordModal(false)}
|
||||
>
|
||||
{/** TODO: Make use of `DialogProvider` here or create a separate page. */}
|
||||
<ChangePasswordModal close={() => setShowChangePasswordModal(false)} />
|
||||
</Modal>
|
||||
|
||||
<Button
|
||||
variant="borderless"
|
||||
color="secondary"
|
||||
@@ -228,15 +217,14 @@ export default function MobileNav({ className, ...props }: MobileNavProps) {
|
||||
<List className="grid grid-flow-row gap-2">
|
||||
<ListItem.Root>
|
||||
<ListItem.Button
|
||||
component={NavLink}
|
||||
variant="borderless"
|
||||
color="secondary"
|
||||
className="w-full justify-start border-none px-2 py-2.5 text-[16px]"
|
||||
onClick={() => {
|
||||
setMenuOpen(false);
|
||||
setShowChangePasswordModal(true);
|
||||
}}
|
||||
href="/account"
|
||||
onClick={() => setMenuOpen(false)}
|
||||
>
|
||||
Change Password
|
||||
Settings
|
||||
</ListItem.Button>
|
||||
</ListItem.Root>
|
||||
|
||||
@@ -245,7 +233,7 @@ export default function MobileNav({ className, ...props }: MobileNavProps) {
|
||||
<ListItem.Root>
|
||||
<ListItem.Button
|
||||
variant="borderless"
|
||||
color="secondary"
|
||||
sx={{ color: 'error.main' }}
|
||||
className="justify-start border-none px-2 py-2.5 text-[16px]"
|
||||
onClick={async () => {
|
||||
setMenuOpen(false);
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export * from './MobileNav';
|
||||
export { default } from './MobileNav';
|
||||
export { default as MobileNav } from './MobileNav';
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export * from './NavLink';
|
||||
export { default } from './NavLink';
|
||||
export { default as NavLink, default } from './NavLink';
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
export * from './Pagination';
|
||||
export { default } from './Pagination';
|
||||
|
||||
export { default as Pagination } from './Pagination';
|
||||
|
||||
@@ -1,2 +1,5 @@
|
||||
export * from './RetryableErrorBoundary';
|
||||
export { default } from './RetryableErrorBoundary';
|
||||
export {
|
||||
default,
|
||||
default as RetryableErrorBoundary,
|
||||
} from './RetryableErrorBoundary';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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;
|
||||
113
dashboard/src/components/dashboard/AccountMenu/AccountMenu.tsx
Normal file
113
dashboard/src/components/dashboard/AccountMenu/AccountMenu.tsx
Normal file
@@ -0,0 +1,113 @@
|
||||
import { NavLink } from '@/components/common/NavLink';
|
||||
import { ThemeSwitcher } from '@/components/common/ThemeSwitcher';
|
||||
import { Avatar } from '@/ui/Avatar';
|
||||
import { Box } from '@/ui/v2/Box';
|
||||
import { Button } from '@/ui/v2/Button';
|
||||
import { Divider } from '@/ui/v2/Divider';
|
||||
import { Dropdown, useDropdown } from '@/ui/v2/Dropdown';
|
||||
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';
|
||||
|
||||
function AccountMenuContent() {
|
||||
const user = useUserData();
|
||||
const { signOut } = useSignOut();
|
||||
const router = useRouter();
|
||||
const apolloClient = useApolloClient();
|
||||
const { handleClose } = useDropdown();
|
||||
const { publicRuntimeConfig } = getConfig();
|
||||
|
||||
return (
|
||||
<Box className="grid grid-flow-row">
|
||||
<Box className="grid grid-flow-col items-center justify-start gap-4 p-4">
|
||||
<Avatar
|
||||
name={user?.displayName}
|
||||
avatarUrl={user?.avatarUrl}
|
||||
className="h-10 w-10"
|
||||
/>
|
||||
|
||||
<Box className="grid grid-flow-row gap-0.5">
|
||||
<Text className="font-semibold">{user?.displayName}</Text>
|
||||
<Text color="secondary" className="text-sm">
|
||||
{user?.email}
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Box className="p-2">
|
||||
<ThemeSwitcher
|
||||
label="Theme"
|
||||
variant="inline"
|
||||
fullWidth
|
||||
className="grid-cols-auto justify-between px-2"
|
||||
slotProps={{
|
||||
label: { className: '!text-sm+' },
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Box className="grid grid-flow-row gap-1 p-2">
|
||||
<Button
|
||||
variant="borderless"
|
||||
color="secondary"
|
||||
className="w-full justify-start"
|
||||
LinkComponent={NavLink}
|
||||
href="/account"
|
||||
onClick={handleClose}
|
||||
>
|
||||
Account Settings
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
color="error"
|
||||
variant="borderless"
|
||||
className="w-full justify-start"
|
||||
onClick={async () => {
|
||||
handleClose();
|
||||
await apolloClient.clearStore();
|
||||
await signOut();
|
||||
await router.push('/signin');
|
||||
}}
|
||||
>
|
||||
Sign out
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Box className="py-4">
|
||||
<Text className="text-center text-xs" color="disabled">
|
||||
Dashboard Version: {publicRuntimeConfig?.version || 'n/a'}
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function AccountMenu() {
|
||||
const user = useUserData();
|
||||
|
||||
return (
|
||||
<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 />
|
||||
</Dropdown.Content>
|
||||
</Dropdown.Root>
|
||||
);
|
||||
}
|
||||
|
||||
export default AccountMenu;
|
||||
1
dashboard/src/components/dashboard/AccountMenu/index.ts
Normal file
1
dashboard/src/components/dashboard/AccountMenu/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default as AccountMenu } from './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,2 +1,5 @@
|
||||
export * from './AppDeploymentDuration';
|
||||
export { default } from './AppDeploymentDuration';
|
||||
export {
|
||||
default as AppDeploymentDuration,
|
||||
default,
|
||||
} from './AppDeploymentDuration';
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
import NavLink from '@/components/common/NavLink';
|
||||
import AppDeploymentDuration from '@/components/deployments/AppDeploymentDuration';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
||||
import { NavLink } from '@/components/common/NavLink';
|
||||
import { AppDeploymentDuration } from '@/components/deployments/AppDeploymentDuration';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/hooks/useCurrentWorkspaceAndProject';
|
||||
import { Avatar } from '@/ui/Avatar';
|
||||
import type { DeploymentStatus } from '@/ui/StatusCircle';
|
||||
import { StatusCircle } from '@/ui/StatusCircle';
|
||||
import Button from '@/ui/v2/Button';
|
||||
import Chip from '@/ui/v2/Chip';
|
||||
import { Button } from '@/ui/v2/Button';
|
||||
import { Chip } from '@/ui/v2/Chip';
|
||||
import { ArrowCounterclockwiseIcon } from '@/ui/v2/icons/ArrowCounterclockwiseIcon';
|
||||
import { ChevronRightIcon } from '@/ui/v2/icons/ChevronRightIcon';
|
||||
import { ListItem } from '@/ui/v2/ListItem';
|
||||
import Tooltip from '@/ui/v2/Tooltip';
|
||||
import ArrowCounterclockwiseIcon from '@/ui/v2/icons/ArrowCounterclockwiseIcon';
|
||||
import ChevronRightIcon from '@/ui/v2/icons/ChevronRightIcon';
|
||||
import type { DeploymentRowFragment } from '@/utils/__generated__/graphql';
|
||||
import { useInsertDeploymentMutation } from '@/utils/__generated__/graphql';
|
||||
import getServerError from '@/utils/settings/getServerError';
|
||||
import { Tooltip } from '@/ui/v2/Tooltip';
|
||||
import { getServerError } from '@/utils/settings/getServerError';
|
||||
import { getToastStyleProps } from '@/utils/settings/settingsConstants';
|
||||
import type { DeploymentRowFragment } from '@/utils/__generated__/graphql';
|
||||
import {
|
||||
GetAllWorkspacesAndProjectsDocument,
|
||||
useInsertDeploymentMutation,
|
||||
} from '@/utils/__generated__/graphql';
|
||||
import { formatDistanceToNowStrict, parseISO } from 'date-fns';
|
||||
import type { MouseEvent } from 'react';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
@@ -52,9 +56,39 @@ export default function DeploymentListItem({
|
||||
})
|
||||
: '';
|
||||
|
||||
const [insertDeployment, { loading }] = useInsertDeploymentMutation();
|
||||
const [insertDeployment, { loading }] = useInsertDeploymentMutation({
|
||||
refetchQueries: [{ query: GetAllWorkspacesAndProjectsDocument }],
|
||||
});
|
||||
const { commitMessage } = deployment;
|
||||
|
||||
async function redeployDeployment(event: MouseEvent<HTMLButtonElement>) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
const insertDeploymentPromise = insertDeployment({
|
||||
variables: {
|
||||
object: {
|
||||
appId: currentProject?.id,
|
||||
commitMessage: deployment.commitMessage,
|
||||
commitSHA: deployment.commitSHA,
|
||||
commitUserAvatarUrl: deployment.commitUserAvatarUrl,
|
||||
commitUserName: deployment.commitUserName,
|
||||
deploymentStatus: 'SCHEDULED',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await toast.promise(
|
||||
insertDeploymentPromise,
|
||||
{
|
||||
loading: 'Scheduling deployment...',
|
||||
success: 'Deployment has been scheduled successfully.',
|
||||
error: getServerError('An error occurred when scheduling deployment.'),
|
||||
},
|
||||
getToastStyleProps(),
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ListItem.Root>
|
||||
<ListItem.Button
|
||||
@@ -88,7 +122,7 @@ export default function DeploymentListItem({
|
||||
{showRedeploy && (
|
||||
<Tooltip
|
||||
title={
|
||||
!disableRedeploy && !loading
|
||||
disableRedeploy || loading
|
||||
? 'Deployments cannot be re-triggered when a deployment is in progress.'
|
||||
: ''
|
||||
}
|
||||
@@ -100,35 +134,7 @@ export default function DeploymentListItem({
|
||||
size="small"
|
||||
color="secondary"
|
||||
variant="outlined"
|
||||
onClick={async (event) => {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
const insertDeploymentPromise = insertDeployment({
|
||||
variables: {
|
||||
object: {
|
||||
appId: currentProject?.id,
|
||||
commitMessage: deployment.commitMessage,
|
||||
commitSHA: deployment.commitSHA,
|
||||
commitUserAvatarUrl: deployment.commitUserAvatarUrl,
|
||||
commitUserName: deployment.commitUserName,
|
||||
deploymentStatus: 'SCHEDULED',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await toast.promise(
|
||||
insertDeploymentPromise,
|
||||
{
|
||||
loading: 'Scheduling deployment...',
|
||||
success: 'Deployment has been scheduled successfully.',
|
||||
error: getServerError(
|
||||
'An error occurred when scheduling deployment.',
|
||||
),
|
||||
},
|
||||
getToastStyleProps(),
|
||||
);
|
||||
}}
|
||||
onClick={redeployDeployment}
|
||||
startIcon={
|
||||
<ArrowCounterclockwiseIcon className={twMerge('h-4 w-4')} />
|
||||
}
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export * from './DeploymentListItem';
|
||||
export { default } from './DeploymentListItem';
|
||||
export { default, default as DeploymentListItem } from './DeploymentListItem';
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './FilesDataGrid';
|
||||
export { default } from './FilesDataGrid';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './FilesDataGridControls';
|
||||
export { default } from './FilesDataGridControls';
|
||||
@@ -1,16 +1,16 @@
|
||||
import { useDialog } from '@/components/common/DialogProvider';
|
||||
import Form from '@/components/common/Form';
|
||||
import { Form } from '@/components/common/Form';
|
||||
import type { DialogFormProps } from '@/types/common';
|
||||
import Button from '@/ui/v2/Button';
|
||||
import Input from '@/ui/v2/Input';
|
||||
import { Button } from '@/ui/v2/Button';
|
||||
import { Input } from '@/ui/v2/Input';
|
||||
import { slugifyString } from '@/utils/helpers';
|
||||
import { getServerError } from '@/utils/settings/getServerError';
|
||||
import { getToastStyleProps } from '@/utils/settings/settingsConstants';
|
||||
import {
|
||||
GetAllWorkspacesAndProjectsDocument,
|
||||
useInsertWorkspaceMutation,
|
||||
useUpdateWorkspaceMutation,
|
||||
} from '@/utils/__generated__/graphql';
|
||||
import { slugifyString } from '@/utils/helpers';
|
||||
import getServerError from '@/utils/settings/getServerError';
|
||||
import { getToastStyleProps } from '@/utils/settings/settingsConstants';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import { useUserData } from '@nhost/nextjs';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import { useIsPlatform } from '@/features/projects/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';
|
||||
@@ -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,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/hooks/useCurrentWorkspaceAndProject';
|
||||
import { useInsertFeedbackOneMutation } from '@/generated/graphql';
|
||||
import { Avatar } from '@/ui/Avatar';
|
||||
import Button from '@/ui/v2/Button';
|
||||
|
||||
@@ -207,19 +207,6 @@ export default function WorkspaceAndProjectList({
|
||||
</div>
|
||||
))}
|
||||
</Box>
|
||||
|
||||
<Text className="font-medium" color="secondary">
|
||||
Looking for your old apps? They're still on{' '}
|
||||
<Link
|
||||
href="https://console.nhost.io"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
underline="always"
|
||||
>
|
||||
console.nhost.io
|
||||
</Link>{' '}
|
||||
during this beta.
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import Header from '@/components/common/Header';
|
||||
import HighlightedText from '@/components/common/HighlightedText';
|
||||
import RetryableErrorBoundary from '@/components/common/RetryableErrorBoundary';
|
||||
import { HighlightedText } from '@/components/common/HighlightedText';
|
||||
import { RetryableErrorBoundary } from '@/components/common/RetryableErrorBoundary';
|
||||
import { InviteAnnounce } from '@/components/home/InviteAnnounce';
|
||||
import type { BaseLayoutProps } from '@/components/layout/BaseLayout';
|
||||
import BaseLayout from '@/components/layout/BaseLayout';
|
||||
import Container from '@/components/layout/Container';
|
||||
import useIsHealthy from '@/hooks/common/useIsHealthy';
|
||||
import useIsPlatform from '@/hooks/common/useIsPlatform';
|
||||
import { useIsHealthy } from '@/features/projects/hooks/useIsHealthy';
|
||||
import { useIsPlatform } from '@/features/projects/hooks/useIsPlatform';
|
||||
import ActivityIndicator from '@/ui/v2/ActivityIndicator';
|
||||
import Link from '@/ui/v2/Link';
|
||||
import Text from '@/ui/v2/Text';
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export * from './AuthenticatedLayout';
|
||||
export { default } from './AuthenticatedLayout';
|
||||
export { default as AuthenticatedLayout, default } from './AuthenticatedLayout';
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export * from './Container';
|
||||
export { default } from './Container';
|
||||
export { default as Container, default } from './Container';
|
||||
|
||||
@@ -2,9 +2,9 @@ import DesktopNav from '@/components/common/DesktopNav';
|
||||
import { LoadingScreen } from '@/components/common/LoadingScreen';
|
||||
import type { AuthenticatedLayoutProps } from '@/components/layout/AuthenticatedLayout';
|
||||
import AuthenticatedLayout from '@/components/layout/AuthenticatedLayout';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
||||
import useIsPlatform from '@/hooks/common/useIsPlatform';
|
||||
import useProjectRoutes from '@/hooks/common/useProjectRoutes';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/hooks/useCurrentWorkspaceAndProject';
|
||||
import { useIsPlatform } from '@/features/projects/hooks/useIsPlatform';
|
||||
import { useProjectRoutes } from '@/features/projects/hooks/useProjectRoutes';
|
||||
import { useNavigationVisible } from '@/hooks/useNavigationVisible';
|
||||
import useNotFoundRedirect from '@/hooks/useNotFoundRedirect';
|
||||
import type { BoxProps } from '@/ui/v2/Box';
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export * from './ProjectLayout';
|
||||
export { default } from './ProjectLayout';
|
||||
export { default as ProjectLayout, default } from './ProjectLayout';
|
||||
|
||||
@@ -3,7 +3,7 @@ import RetryableErrorBoundary from '@/components/common/RetryableErrorBoundary';
|
||||
import type { BaseLayoutProps } from '@/components/layout/BaseLayout';
|
||||
import BaseLayout from '@/components/layout/BaseLayout';
|
||||
import Container from '@/components/layout/Container';
|
||||
import useIsPlatform from '@/hooks/common/useIsPlatform';
|
||||
import { useIsPlatform } from '@/features/projects/hooks/useIsPlatform';
|
||||
import Box from '@/ui/v2/Box';
|
||||
import ThemeProvider from '@/ui/v2/ThemeProvider';
|
||||
import GlobalStyles from '@mui/material/GlobalStyles';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import LogsDatePicker from '@/components/logs/LogsDatePicker';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/hooks/useCurrentWorkspaceAndProject';
|
||||
import type { AvailableLogsServices, LogsCustomInterval } from '@/types/logs';
|
||||
import type { BoxProps } from '@/ui/v2/Box';
|
||||
import Box from '@/ui/v2/Box';
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './InfoCard';
|
||||
export { default } from './InfoCard';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './OverviewCard';
|
||||
export { default } from './OverviewCard';
|
||||
@@ -1 +0,0 @@
|
||||
export { default } from './OverviewDeployments';
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user