Compare commits
131 Commits
@nhost/rea
...
@nhost/das
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
a6120bf366 | ||
|
|
b23dc058a6 | ||
|
|
ad0dda7493 | ||
|
|
4063507d59 | ||
|
|
85439307a9 | ||
|
|
0d8baa4065 | ||
|
|
4bca94425e | ||
|
|
9f948385c0 | ||
|
|
294c504b61 | ||
|
|
1469ec2969 | ||
|
|
726c33d1b2 | ||
|
|
11b9cfbc0d | ||
|
|
79aaa91e67 | ||
|
|
df4d24320a | ||
|
|
baa3ef794e | ||
|
|
da7ffbe523 | ||
|
|
3ca70554c8 | ||
|
|
077b200510 | ||
|
|
2f220db84a |
@@ -25,8 +25,11 @@ module.exports = {
|
|||||||
'error',
|
'error',
|
||||||
{ allowArrowFunctions: true, allowFunctions: true },
|
{ allowArrowFunctions: true, allowFunctions: true },
|
||||||
],
|
],
|
||||||
|
'import/no-named-as-default': 'off',
|
||||||
'import/prefer-default-export': 'off',
|
'import/prefer-default-export': 'off',
|
||||||
'import/no-extraneous-dependencies': ['error', { devDependencies: true }],
|
'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'],
|
curly: ['error', 'all'],
|
||||||
'no-restricted-exports': 'off',
|
'no-restricted-exports': 'off',
|
||||||
'no-undef': 'off',
|
'no-undef': 'off',
|
||||||
|
|||||||
@@ -1,5 +1,33 @@
|
|||||||
# @nhost/dashboard
|
# @nhost/dashboard
|
||||||
|
|
||||||
|
## 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
|
## 0.16.11
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ RUN apk add --no-cache libc6-compat
|
|||||||
RUN apk update
|
RUN apk update
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
RUN yarn global add turbo@1.9.3
|
RUN yarn global add turbo@1.9.8
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN turbo prune --scope="@nhost/dashboard" --docker
|
RUN turbo prune --scope="@nhost/dashboard" --docker
|
||||||
|
|
||||||
|
|||||||
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.locator('a', { hasText: /data/i }).click();
|
||||||
await hasuraPage.getByRole('link', { name: /sql/i }).click();
|
await hasuraPage.getByRole('link', { name: /sql/i }).click();
|
||||||
|
|
||||||
await hasuraPage.getByRole('textbox').fill(`
|
await hasuraPage.locator('#raw_sql > textarea').fill(`
|
||||||
DO $$ DECLARE
|
DO $$ DECLARE
|
||||||
tablename text;
|
tablename text;
|
||||||
BEGIN
|
BEGIN
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nhost/dashboard",
|
"name": "@nhost/dashboard",
|
||||||
"version": "0.16.11",
|
"version": "0.16.14",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"preinstall": "npx only-allow pnpm",
|
"preinstall": "npx only-allow pnpm",
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
"format": "prettier --write \"src/**/*.{js,ts,tsx,jsx,json,md}\" --plugin-search-dir=.",
|
"format": "prettier --write \"src/**/*.{js,ts,tsx,jsx,json,md}\" --plugin-search-dir=.",
|
||||||
"storybook": "start-storybook -p 6006 -s public",
|
"storybook": "start-storybook -p 6006 -s public",
|
||||||
"build-storybook": "build-storybook",
|
"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": {
|
"dependencies": {
|
||||||
"@apollo/client": "^3.7.10",
|
"@apollo/client": "^3.7.10",
|
||||||
@@ -24,8 +24,8 @@
|
|||||||
"@emotion/react": "^11.10.5",
|
"@emotion/react": "^11.10.5",
|
||||||
"@emotion/server": "^11.4.0",
|
"@emotion/server": "^11.4.0",
|
||||||
"@emotion/styled": "^11.10.5",
|
"@emotion/styled": "^11.10.5",
|
||||||
"@fontsource/inter": "^4.5.14",
|
"@fontsource/inter": "^5.0.0",
|
||||||
"@fontsource/roboto-mono": "^4.5.8",
|
"@fontsource/roboto-mono": "^5.0.0",
|
||||||
"@graphiql/react": "^0.17.0",
|
"@graphiql/react": "^0.17.0",
|
||||||
"@graphiql/toolkit": "^0.8.2",
|
"@graphiql/toolkit": "^0.8.2",
|
||||||
"@headlessui/react": "^1.6.5",
|
"@headlessui/react": "^1.6.5",
|
||||||
@@ -84,11 +84,10 @@
|
|||||||
"@faker-js/faker": "^7.6.0",
|
"@faker-js/faker": "^7.6.0",
|
||||||
"@graphql-codegen/cli": "^3.0.0",
|
"@graphql-codegen/cli": "^3.0.0",
|
||||||
"@graphql-codegen/typescript": "^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-operations": "^3.0.0",
|
||||||
"@graphql-codegen/typescript-react-apollo": "^3.3.1",
|
"@graphql-codegen/typescript-react-apollo": "^3.3.1",
|
||||||
"@next/bundle-analyzer": "^12.3.1",
|
"@next/bundle-analyzer": "^12.3.1",
|
||||||
"@playwright/test": "^1.31.2",
|
"@playwright/test": "^1.34.0",
|
||||||
"@storybook/addon-actions": "^6.5.14",
|
"@storybook/addon-actions": "^6.5.14",
|
||||||
"@storybook/addon-essentials": "^6.5.14",
|
"@storybook/addon-essentials": "^6.5.14",
|
||||||
"@storybook/addon-interactions": "^6.5.14",
|
"@storybook/addon-interactions": "^6.5.14",
|
||||||
@@ -137,7 +136,7 @@
|
|||||||
"postcss": "^8.4.19",
|
"postcss": "^8.4.19",
|
||||||
"prettier": "^2.7.1",
|
"prettier": "^2.7.1",
|
||||||
"prettier-plugin-organize-imports": "^3.2.0",
|
"prettier-plugin-organize-imports": "^3.2.0",
|
||||||
"prettier-plugin-tailwindcss": "^0.2.0",
|
"prettier-plugin-tailwindcss": "^0.3.0",
|
||||||
"react-date-fns-hooks": "^0.9.4",
|
"react-date-fns-hooks": "^0.9.4",
|
||||||
"require-from-string": "^2.0.2",
|
"require-from-string": "^2.0.2",
|
||||||
"snake-case": "^3.0.4",
|
"snake-case": "^3.0.4",
|
||||||
|
|||||||
@@ -114,6 +114,9 @@ export default function AppDeployments(props: AppDeploymentsProps) {
|
|||||||
const { deployments } = deploymentPageData || { deployments: [] };
|
const { deployments } = deploymentPageData || { deployments: [] };
|
||||||
const { deployments: scheduledOrPendingDeployments } =
|
const { deployments: scheduledOrPendingDeployments } =
|
||||||
scheduledOrPendingDeploymentsData || { deployments: [] };
|
scheduledOrPendingDeploymentsData || { deployments: [] };
|
||||||
|
const isDeploymentInProgress = deployments?.some((deployment) =>
|
||||||
|
['PENDING', 'SCHEDULED'].includes(deployment.deploymentStatus),
|
||||||
|
);
|
||||||
|
|
||||||
const latestDeployment = latestDeploymentData?.deployments[0];
|
const latestDeployment = latestDeploymentData?.deployments[0];
|
||||||
const latestLiveDeployment = latestLiveDeploymentData?.deployments[0];
|
const latestLiveDeployment = latestLiveDeploymentData?.deployments[0];
|
||||||
@@ -135,7 +138,10 @@ export default function AppDeployments(props: AppDeploymentsProps) {
|
|||||||
deployment={deployment}
|
deployment={deployment}
|
||||||
isLive={liveDeploymentId === deployment.id}
|
isLive={liveDeploymentId === deployment.id}
|
||||||
showRedeploy={latestDeployment.id === deployment.id}
|
showRedeploy={latestDeployment.id === deployment.id}
|
||||||
disableRedeploy={scheduledOrPendingDeployments?.length > 0}
|
disableRedeploy={
|
||||||
|
scheduledOrPendingDeployments?.length > 0 ||
|
||||||
|
isDeploymentInProgress
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{index !== deployments.length - 1 && <Divider component="li" />}
|
{index !== deployments.length - 1 && <Divider component="li" />}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import FeedbackForm from '@/components/common/FeedbackForm';
|
import { FeedbackForm } from '@/components/common/FeedbackForm';
|
||||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
import { useCurrentWorkspaceAndProject } from '@/features/projects/hooks/useCurrentWorkspaceAndProject';
|
||||||
import { useInterval } from '@/hooks/useInterval';
|
import { useInterval } from '@/hooks/useInterval';
|
||||||
import ActivityIndicator from '@/ui/v2/ActivityIndicator';
|
import ActivityIndicator from '@/ui/v2/ActivityIndicator';
|
||||||
import Button from '@/ui/v2/Button';
|
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 Container from '@/components/layout/Container';
|
||||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
import { useCurrentWorkspaceAndProject } from '@/features/projects/hooks/useCurrentWorkspaceAndProject';
|
||||||
import { useIsCurrentUserOwner } from '@/features/projects/common/hooks/useIsCurrentUserOwner';
|
import { useIsCurrentUserOwner } from '@/features/projects/hooks/useIsCurrentUserOwner';
|
||||||
import { useAppCreatedAt } from '@/hooks/useAppCreatedAt';
|
|
||||||
import { useCurrentDate } from '@/hooks/useCurrentDate';
|
import { useCurrentDate } from '@/hooks/useCurrentDate';
|
||||||
import type { ApplicationState } from '@/types/application';
|
import type { ApplicationState } from '@/types/application';
|
||||||
import { ApplicationStatus } 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 Button from '@/ui/v2/Button';
|
||||||
import { Dropdown } from '@/ui/v2/Dropdown';
|
import { Dropdown } from '@/ui/v2/Dropdown';
|
||||||
import Text from '@/ui/v2/Text';
|
import Text from '@/ui/v2/Text';
|
||||||
|
import { discordAnnounce } from '@/utils/discordAnnounce';
|
||||||
|
import { getPreviousApplicationState } from '@/utils/getPreviousApplicationState';
|
||||||
|
import { getApplicationStatusString } from '@/utils/helpers';
|
||||||
|
import { triggerToast } from '@/utils/toast';
|
||||||
import {
|
import {
|
||||||
useDeleteApplicationMutation,
|
useDeleteApplicationMutation,
|
||||||
useGetApplicationStateQuery,
|
useGetApplicationStateQuery,
|
||||||
useInsertApplicationMutation,
|
useInsertApplicationMutation,
|
||||||
useUpdateApplicationMutation,
|
useUpdateApplicationMutation,
|
||||||
} from '@/utils/__generated__/graphql';
|
} 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 { useUserData } from '@nhost/nextjs';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
@@ -32,7 +29,11 @@ import { RemoveApplicationModal } from './RemoveApplicationModal';
|
|||||||
import { StagingMetadata } from './StagingMetadata';
|
import { StagingMetadata } from './StagingMetadata';
|
||||||
|
|
||||||
export default function ApplicationErrored() {
|
export default function ApplicationErrored() {
|
||||||
const { currentWorkspace, currentProject } = useCurrentWorkspaceAndProject();
|
const {
|
||||||
|
currentWorkspace,
|
||||||
|
currentProject,
|
||||||
|
refetch: refetchProject,
|
||||||
|
} = useCurrentWorkspaceAndProject();
|
||||||
const [changingApplicationStateLoading, setChangingApplicationStateLoading] =
|
const [changingApplicationStateLoading, setChangingApplicationStateLoading] =
|
||||||
useState(false);
|
useState(false);
|
||||||
|
|
||||||
@@ -54,12 +55,11 @@ export default function ApplicationErrored() {
|
|||||||
const [showRecreateModal, setShowRecreateModal] = useState(false);
|
const [showRecreateModal, setShowRecreateModal] = useState(false);
|
||||||
const [showDeleteModal, setShowDeleteModal] = useState(false);
|
const [showDeleteModal, setShowDeleteModal] = useState(false);
|
||||||
const [insertApp] = useInsertApplicationMutation();
|
const [insertApp] = useInsertApplicationMutation();
|
||||||
const client = useApolloClient();
|
|
||||||
const { currentDate } = useCurrentDate();
|
const { currentDate } = useCurrentDate();
|
||||||
const user = useUserData();
|
const user = useUserData();
|
||||||
const isOwner = useIsCurrentUserOwner();
|
const isOwner = useIsCurrentUserOwner();
|
||||||
|
|
||||||
const { appCreatedAt } = useAppCreatedAt();
|
const appCreatedAt = new Date(currentProject.createdAt).getTime();
|
||||||
|
|
||||||
const FIVE_DAYS_IN_MILLISECONDS = 60 * 24 * 60 * 5 * 1000;
|
const FIVE_DAYS_IN_MILLISECONDS = 60 * 24 * 60 * 5 * 1000;
|
||||||
const HALF_DAY_IN_MILLISECONDS = 60 * 12 * 60 * 1000;
|
const HALF_DAY_IN_MILLISECONDS = 60 * 12 * 60 * 1000;
|
||||||
@@ -94,7 +94,7 @@ export default function ApplicationErrored() {
|
|||||||
});
|
});
|
||||||
discordAnnounce(`Recreating: ${currentProject?.name} (${user.email})`);
|
discordAnnounce(`Recreating: ${currentProject?.name} (${user.email})`);
|
||||||
triggerToast(`Recreating ${currentProject?.name} `);
|
triggerToast(`Recreating ${currentProject?.name} `);
|
||||||
await updateOwnCache(client);
|
await refetchProject();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
triggerToast(`Error trying to recreate: ${currentProject?.name}`);
|
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 {
|
import {
|
||||||
GetAllWorkspacesAndProjectsDocument,
|
GetAllWorkspacesAndProjectsDocument,
|
||||||
useDeleteApplicationMutation,
|
useDeleteApplicationMutation,
|
||||||
} from '@/generated/graphql';
|
} from '@/generated/graphql';
|
||||||
import Button from '@/ui/v2/Button';
|
import { Button } from '@/ui/v2/Button';
|
||||||
import Link from '@/ui/v2/Link';
|
import { ArrowRightIcon } from '@/ui/v2/icons/ArrowRightIcon';
|
||||||
import Text from '@/ui/v2/Text';
|
import { Link } from '@/ui/v2/Link';
|
||||||
import ArrowRightIcon from '@/ui/v2/icons/ArrowRightIcon';
|
import { Text } from '@/ui/v2/Text';
|
||||||
import { copy } from '@/utils/copy';
|
import { copy } from '@/utils/copy';
|
||||||
import { getApplicationStatusString } from '@/utils/helpers';
|
import { getApplicationStatusString } from '@/utils/helpers';
|
||||||
import getServerError from '@/utils/settings/getServerError';
|
import { getServerError } from '@/utils/settings/getServerError';
|
||||||
import { getToastStyleProps } from '@/utils/settings/settingsConstants';
|
import { getToastStyleProps } from '@/utils/settings/settingsConstants';
|
||||||
import { formatDistance } from 'date-fns';
|
import { formatDistance } from 'date-fns';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
@@ -18,18 +18,14 @@ import { toast } from 'react-hot-toast';
|
|||||||
export default function ApplicationInfo() {
|
export default function ApplicationInfo() {
|
||||||
const { currentProject } = useCurrentWorkspaceAndProject();
|
const { currentProject } = useCurrentWorkspaceAndProject();
|
||||||
const [deleteApplication] = useDeleteApplicationMutation({
|
const [deleteApplication] = useDeleteApplicationMutation({
|
||||||
refetchQueries: [GetAllWorkspacesAndProjectsDocument],
|
refetchQueries: [{ query: GetAllWorkspacesAndProjectsDocument }],
|
||||||
});
|
});
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
async function handleClickRemove() {
|
async function handleClickRemove() {
|
||||||
try {
|
try {
|
||||||
await toast.promise(
|
await toast.promise(
|
||||||
deleteApplication({
|
deleteApplication({ variables: { appId: currentProject.id } }),
|
||||||
variables: {
|
|
||||||
appId: currentProject.id,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
{
|
{
|
||||||
loading: 'Deleting project...',
|
loading: 'Deleting project...',
|
||||||
success: 'The project has been deleted successfully.',
|
success: 'The project has been deleted successfully.',
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import MaintenanceAlert from '@/components/common/MaintenanceAlert';
|
import { MaintenanceAlert } from '@/components/common/MaintenanceAlert';
|
||||||
import RetryableErrorBoundary from '@/components/common/RetryableErrorBoundary';
|
import { RetryableErrorBoundary } from '@/components/common/RetryableErrorBoundary';
|
||||||
import Container from '@/components/layout/Container';
|
import Container from '@/components/layout/Container';
|
||||||
import OverviewDeployments from '@/components/overview/OverviewDeployments';
|
import { useIsPlatform } from '@/features/projects/hooks/useIsPlatform';
|
||||||
import OverviewDocumentation from '@/components/overview/OverviewDocumentation';
|
import { OverviewDeployments } from '@/features/projects/overview/components/OverviewDeployments';
|
||||||
import OverviewMetrics from '@/components/overview/OverviewMetrics/OverviewMetrics';
|
import { OverviewDocumentation } from '@/features/projects/overview/components/OverviewDocumentation';
|
||||||
import OverviewProjectInfo from '@/components/overview/OverviewProjectInfo';
|
import { OverviewMetrics } from '@/features/projects/overview/components/OverviewMetrics';
|
||||||
import OverviewRepository from '@/components/overview/OverviewRepository';
|
import { OverviewProjectInfo } from '@/features/projects/overview/components/OverviewProjectInfo';
|
||||||
import OverviewTopBar from '@/components/overview/OverviewTopBar';
|
import { OverviewRepository } from '@/features/projects/overview/components/OverviewRepository';
|
||||||
import OverviewUsage from '@/components/overview/OverviewUsage';
|
import { OverviewTopBar } from '@/features/projects/overview/components/OverviewTopBar';
|
||||||
import { features } from '@/components/overview/features';
|
import { OverviewUsage } from '@/features/projects/overview/components/OverviewUsage';
|
||||||
import { frameworks } from '@/components/overview/frameworks';
|
import { features } from '@/features/projects/overview/features';
|
||||||
import useIsPlatform from '@/hooks/common/useIsPlatform';
|
import { frameworks } from '@/features/projects/overview/frameworks';
|
||||||
import { Alert } from '@/ui/Alert';
|
import { Alert } from '@/ui/Alert';
|
||||||
import Divider from '@/ui/v2/Divider';
|
import Divider from '@/ui/v2/Divider';
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import { ChangePlanModal } from '@/components/applications/ChangePlanModal';
|
|||||||
import { StagingMetadata } from '@/components/applications/StagingMetadata';
|
import { StagingMetadata } from '@/components/applications/StagingMetadata';
|
||||||
import { useDialog } from '@/components/common/DialogProvider';
|
import { useDialog } from '@/components/common/DialogProvider';
|
||||||
import Container from '@/components/layout/Container';
|
import Container from '@/components/layout/Container';
|
||||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
import { useCurrentWorkspaceAndProject } from '@/features/projects/hooks/useCurrentWorkspaceAndProject';
|
||||||
import { useIsCurrentUserOwner } from '@/features/projects/common/hooks/useIsCurrentUserOwner';
|
import { useIsCurrentUserOwner } from '@/features/projects/hooks/useIsCurrentUserOwner';
|
||||||
import {
|
import {
|
||||||
GetAllWorkspacesAndProjectsDocument,
|
GetAllWorkspacesAndProjectsDocument,
|
||||||
useGetFreeAndActiveProjectsQuery,
|
useGetFreeAndActiveProjectsQuery,
|
||||||
@@ -35,12 +35,11 @@ export default function ApplicationPaused() {
|
|||||||
const [showDeletingModal, setShowDeletingModal] = useState(false);
|
const [showDeletingModal, setShowDeletingModal] = useState(false);
|
||||||
const [unpauseApplication, { loading: changingApplicationStateLoading }] =
|
const [unpauseApplication, { loading: changingApplicationStateLoading }] =
|
||||||
useUnpauseApplicationMutation({
|
useUnpauseApplicationMutation({
|
||||||
refetchQueries: [GetAllWorkspacesAndProjectsDocument],
|
refetchQueries: [{ query: GetAllWorkspacesAndProjectsDocument }],
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data, loading } = useGetFreeAndActiveProjectsQuery({
|
const { data, loading } = useGetFreeAndActiveProjectsQuery({
|
||||||
variables: { userId: user?.id },
|
variables: { userId: user?.id },
|
||||||
fetchPolicy: 'cache-and-network',
|
|
||||||
skip: !user,
|
skip: !user,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import Container from '@/components/layout/Container';
|
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 { useCheckProvisioning } from '@/hooks/useCheckProvisioning';
|
||||||
import { ApplicationStatus } from '@/types/application';
|
import { ApplicationStatus } from '@/types/application';
|
||||||
import ActivityIndicator from '@/ui/v2/ActivityIndicator';
|
import ActivityIndicator from '@/ui/v2/ActivityIndicator';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import Container from '@/components/layout/Container';
|
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 { useCheckProvisioning } from '@/hooks/useCheckProvisioning';
|
||||||
import { ApplicationStatus } from '@/types/application';
|
import { ApplicationStatus } from '@/types/application';
|
||||||
import ActivityIndicator from '@/ui/v2/ActivityIndicator';
|
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 Container from '@/components/layout/Container';
|
||||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
import { useCurrentWorkspaceAndProject } from '@/features/projects/hooks/useCurrentWorkspaceAndProject';
|
||||||
import { useIsCurrentUserOwner } from '@/features/projects/common/hooks/useIsCurrentUserOwner';
|
import { useIsCurrentUserOwner } from '@/features/projects/hooks/useIsCurrentUserOwner';
|
||||||
import { Modal } from '@/ui/Modal';
|
import { Modal } from '@/ui/Modal';
|
||||||
import Button from '@/ui/v2/Button';
|
import Button from '@/ui/v2/Button';
|
||||||
import { Dropdown } from '@/ui/v2/Dropdown';
|
import { Dropdown } from '@/ui/v2/Dropdown';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import Container from '@/components/layout/Container';
|
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 Image from 'next/image';
|
||||||
import { AppLoader } from './AppLoader';
|
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 { useDialog } from '@/components/common/DialogProvider';
|
||||||
import { BillingPaymentMethodForm } from '@/components/workspace/BillingPaymentMethodForm';
|
import { BillingPaymentMethodForm } from '@/components/workspace/BillingPaymentMethodForm';
|
||||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
import { useCurrentWorkspaceAndProject } from '@/features/projects/hooks/useCurrentWorkspaceAndProject';
|
||||||
import {
|
import {
|
||||||
refetchGetApplicationPlanQuery,
|
refetchGetApplicationPlanQuery,
|
||||||
useGetAppPlanAndGlobalPlansQuery,
|
useGetAppPlanAndGlobalPlansQuery,
|
||||||
@@ -9,15 +9,15 @@ import {
|
|||||||
} from '@/generated/graphql';
|
} from '@/generated/graphql';
|
||||||
import useApplicationState from '@/hooks/useApplicationState';
|
import useApplicationState from '@/hooks/useApplicationState';
|
||||||
import { ApplicationStatus } from '@/types/application';
|
import { ApplicationStatus } from '@/types/application';
|
||||||
import ActivityIndicator from '@/ui/v2/ActivityIndicator';
|
import { ActivityIndicator } from '@/ui/v2/ActivityIndicator';
|
||||||
import Box from '@/ui/v2/Box';
|
import { Box } from '@/ui/v2/Box';
|
||||||
import Button from '@/ui/v2/Button';
|
import { Button } from '@/ui/v2/Button';
|
||||||
import Checkbox from '@/ui/v2/Checkbox';
|
import { Checkbox } from '@/ui/v2/Checkbox';
|
||||||
import { BaseDialog } from '@/ui/v2/Dialog';
|
import { BaseDialog } from '@/ui/v2/Dialog';
|
||||||
import Link from '@/ui/v2/Link';
|
import { Link } from '@/ui/v2/Link';
|
||||||
import Text from '@/ui/v2/Text';
|
import { Text } from '@/ui/v2/Text';
|
||||||
import { planDescriptions } from '@/utils/planDescriptions';
|
import { planDescriptions } from '@/utils/planDescriptions';
|
||||||
import getServerError from '@/utils/settings/getServerError';
|
import { getServerError } from '@/utils/settings/getServerError';
|
||||||
import { getToastStyleProps } from '@/utils/settings/settingsConstants';
|
import { getToastStyleProps } from '@/utils/settings/settingsConstants';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
import { LoadingScreen } from '@/components/common/LoadingScreen';
|
import { LoadingScreen } from '@/components/common/LoadingScreen';
|
||||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
import { useCurrentWorkspaceAndProject } from '@/features/projects/hooks/useCurrentWorkspaceAndProject';
|
||||||
import useIsPlatform from '@/hooks/common/useIsPlatform';
|
import { useIsPlatform } from '@/features/projects/hooks/useIsPlatform';
|
||||||
import Box from '@/ui/v2/Box';
|
import Box from '@/ui/v2/Box';
|
||||||
import Button from '@/ui/v2/Button';
|
import Button from '@/ui/v2/Button';
|
||||||
import IconButton from '@/ui/v2/IconButton';
|
import IconButton from '@/ui/v2/IconButton';
|
||||||
import Text from '@/ui/v2/Text';
|
|
||||||
import ArrowSquareOutIcon from '@/ui/v2/icons/ArrowSquareOutIcon';
|
import ArrowSquareOutIcon from '@/ui/v2/icons/ArrowSquareOutIcon';
|
||||||
import CopyIcon from '@/ui/v2/icons/CopyIcon';
|
import CopyIcon from '@/ui/v2/icons/CopyIcon';
|
||||||
import generateAppServiceUrl, {
|
import Text from '@/ui/v2/Text';
|
||||||
|
import {
|
||||||
defaultLocalBackendSlugs,
|
defaultLocalBackendSlugs,
|
||||||
defaultRemoteBackendSlugs,
|
defaultRemoteBackendSlugs,
|
||||||
|
generateAppServiceUrl,
|
||||||
} from '@/utils/common/generateAppServiceUrl';
|
} from '@/utils/common/generateAppServiceUrl';
|
||||||
import { copy } from '@/utils/copy';
|
import { copy } from '@/utils/copy';
|
||||||
import { getHasuraConsoleServiceUrl } from '@/utils/env';
|
import { getHasuraConsoleServiceUrl } from '@/utils/env';
|
||||||
|
|||||||
@@ -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 Box from '@/ui/v2/Box';
|
||||||
import Button from '@/ui/v2/Button';
|
import Button from '@/ui/v2/Button';
|
||||||
import Checkbox from '@/ui/v2/Checkbox';
|
import Checkbox from '@/ui/v2/Checkbox';
|
||||||
@@ -46,7 +46,7 @@ export function RemoveApplicationModal({
|
|||||||
className,
|
className,
|
||||||
}: RemoveApplicationModalProps) {
|
}: RemoveApplicationModalProps) {
|
||||||
const [deleteApplication] = useDeleteApplicationMutation({
|
const [deleteApplication] = useDeleteApplicationMutation({
|
||||||
refetchQueries: [GetAllWorkspacesAndProjectsDocument],
|
refetchQueries: [{ query: GetAllWorkspacesAndProjectsDocument }],
|
||||||
});
|
});
|
||||||
const [loadingRemove, setLoadingRemove] = useState(false);
|
const [loadingRemove, setLoadingRemove] = useState(false);
|
||||||
const { currentProject } = useCurrentWorkspaceAndProject();
|
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 Box from '@/ui/v2/Box';
|
||||||
import Button from '@/ui/v2/Button';
|
import Button from '@/ui/v2/Button';
|
||||||
import Checkbox from '@/ui/v2/Checkbox';
|
import Checkbox from '@/ui/v2/Checkbox';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { ChangePlanModal } from '@/components/applications/ChangePlanModal';
|
import { ChangePlanModal } from '@/components/applications/ChangePlanModal';
|
||||||
import { useDialog } from '@/components/common/DialogProvider';
|
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 { Alert } from '@/ui/Alert';
|
||||||
import Button from '@/ui/v2/Button';
|
import Button from '@/ui/v2/Button';
|
||||||
import Text from '@/ui/v2/Text';
|
import Text from '@/ui/v2/Text';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { ConnectGithubModalState } from '@/components/applications/ConnectGithubModal';
|
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 { FormProvider, useForm } from 'react-hook-form';
|
||||||
import { EditRepositorySettingsModal } from './EditRepositorySettingsModal';
|
import { EditRepositorySettingsModal } from './EditRepositorySettingsModal';
|
||||||
|
|
||||||
|
|||||||
@@ -2,14 +2,12 @@ import type { EditRepositorySettingsFormData } from '@/components/applications/g
|
|||||||
import { useDialog } from '@/components/common/DialogProvider';
|
import { useDialog } from '@/components/common/DialogProvider';
|
||||||
import ErrorBoundaryFallback from '@/components/common/ErrorBoundaryFallback';
|
import ErrorBoundaryFallback from '@/components/common/ErrorBoundaryFallback';
|
||||||
import GithubIcon from '@/components/icons/GithubIcon';
|
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 { useUpdateApplicationMutation } from '@/generated/graphql';
|
||||||
import Button from '@/ui/v2/Button';
|
import Button from '@/ui/v2/Button';
|
||||||
import Text from '@/ui/v2/Text';
|
import Text from '@/ui/v2/Text';
|
||||||
import { discordAnnounce } from '@/utils/discordAnnounce';
|
import { discordAnnounce } from '@/utils/discordAnnounce';
|
||||||
import { triggerToast } from '@/utils/toast';
|
import { triggerToast } from '@/utils/toast';
|
||||||
import { updateOwnCache } from '@/utils/updateOwnCache';
|
|
||||||
import { useApolloClient } from '@apollo/client';
|
|
||||||
import { ErrorBoundary } from 'react-error-boundary';
|
import { ErrorBoundary } from 'react-error-boundary';
|
||||||
import { useFormContext } from 'react-hook-form';
|
import { useFormContext } from 'react-hook-form';
|
||||||
import { RepoAndBranch } from './RepoAndBranch';
|
import { RepoAndBranch } from './RepoAndBranch';
|
||||||
@@ -27,12 +25,11 @@ export function EditRepositorySettingsModal({
|
|||||||
const isNotCompleted = !watch('productionBranch') || !watch('repoBaseFolder');
|
const isNotCompleted = !watch('productionBranch') || !watch('repoBaseFolder');
|
||||||
const { closeAlertDialog } = useDialog();
|
const { closeAlertDialog } = useDialog();
|
||||||
|
|
||||||
const { currentProject } = useCurrentWorkspaceAndProject();
|
const { currentProject, refetch: refetchProject } =
|
||||||
|
useCurrentWorkspaceAndProject();
|
||||||
|
|
||||||
const [updateApp, { loading }] = useUpdateApplicationMutation();
|
const [updateApp, { loading }] = useUpdateApplicationMutation();
|
||||||
|
|
||||||
const client = useApolloClient();
|
|
||||||
|
|
||||||
const handleEditGitHubIntegration = async (
|
const handleEditGitHubIntegration = async (
|
||||||
data: EditRepositorySettingsFormData,
|
data: EditRepositorySettingsFormData,
|
||||||
) => {
|
) => {
|
||||||
@@ -60,7 +57,8 @@ export function EditRepositorySettingsModal({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await updateOwnCache(client);
|
await refetchProject();
|
||||||
|
|
||||||
if (close) {
|
if (close) {
|
||||||
close();
|
close();
|
||||||
} else {
|
} 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 { useRemoteApplicationGQLClient } from '@/hooks/useRemoteApplicationGQLClient';
|
||||||
import ActivityIndicator from '@/ui/v2/ActivityIndicator';
|
import ActivityIndicator from '@/ui/v2/ActivityIndicator';
|
||||||
import Option from '@/ui/v2/Option';
|
import Option from '@/ui/v2/Option';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import NavLink from '@/components/common/NavLink';
|
import NavLink from '@/components/common/NavLink';
|
||||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
import { useCurrentWorkspaceAndProject } from '@/features/projects/hooks/useCurrentWorkspaceAndProject';
|
||||||
import useIsPlatform from '@/hooks/common/useIsPlatform';
|
import { useIsPlatform } from '@/features/projects/hooks/useIsPlatform';
|
||||||
import type { BoxProps } from '@/ui/v2/Box';
|
import type { BoxProps } from '@/ui/v2/Box';
|
||||||
import Box from '@/ui/v2/Box';
|
import Box from '@/ui/v2/Box';
|
||||||
import Text from '@/ui/v2/Text';
|
import Text from '@/ui/v2/Text';
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
export * from './Breadcrumbs';
|
export * from './Breadcrumbs';
|
||||||
export { default } from './Breadcrumbs';
|
export { default as Breadcrumbs } from './Breadcrumbs';
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import type {
|
|||||||
AutocompleteOption,
|
AutocompleteOption,
|
||||||
AutocompleteProps,
|
AutocompleteProps,
|
||||||
} from '@/ui/v2/Autocomplete';
|
} from '@/ui/v2/Autocomplete';
|
||||||
import Autocomplete from '@/ui/v2/Autocomplete';
|
import { Autocomplete } from '@/ui/v2/Autocomplete';
|
||||||
import callAll from '@/utils/callAll';
|
import callAll from '@/utils/callAll';
|
||||||
import type { ForwardedRef } from 'react';
|
import type { ForwardedRef } from 'react';
|
||||||
import { forwardRef } from 'react';
|
import { forwardRef } from 'react';
|
||||||
|
|||||||
@@ -1,2 +1,5 @@
|
|||||||
export * from './ControlledAutocomplete';
|
export * from './ControlledAutocomplete';
|
||||||
export { default } from './ControlledAutocomplete';
|
export {
|
||||||
|
default as ControlledAutocomplete,
|
||||||
|
default,
|
||||||
|
} from './ControlledAutocomplete';
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
export * from './ControlledCheckbox';
|
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 type { SelectProps } from '@/ui/v2/Select';
|
||||||
import Select from '@/ui/v2/Select';
|
import { Select } from '@/ui/v2/Select';
|
||||||
import type { ForwardedRef } from 'react';
|
import type { ForwardedRef } from 'react';
|
||||||
import { forwardRef } from 'react';
|
import { forwardRef } from 'react';
|
||||||
import type { FieldValues, UseControllerProps } from 'react-hook-form';
|
import type { FieldValues, UseControllerProps } from 'react-hook-form';
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
export * from './ControlledSelect';
|
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 DataGridFrame from '@/components/common/DataGridFrame';
|
||||||
import type { DataGridHeaderProps } from '@/components/common/DataGridHeader';
|
import type { DataGridHeaderProps } from '@/components/common/DataGridHeader';
|
||||||
import DataGridHeader from '@/components/common/DataGridHeader';
|
import DataGridHeader from '@/components/common/DataGridHeader';
|
||||||
import DataBrowserEmptyState from '@/components/dataBrowser/DataBrowserEmptyState';
|
|
||||||
import { DataGridProvider } from '@/context/DataGridContext';
|
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 type { UseDataGridOptions } from '@/hooks/useDataGrid';
|
||||||
import useDataGrid from '@/hooks/useDataGrid';
|
import useDataGrid from '@/hooks/useDataGrid';
|
||||||
import type { DataBrowserGridColumn } from '@/types/dataBrowser';
|
|
||||||
import ActivityIndicator from '@/ui/v2/ActivityIndicator';
|
import ActivityIndicator from '@/ui/v2/ActivityIndicator';
|
||||||
import Box from '@/ui/v2/Box';
|
import Box from '@/ui/v2/Box';
|
||||||
import type { ForwardedRef } from 'react';
|
import type { ForwardedRef } from 'react';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { DataGridProps } from '@/components/common/DataGrid';
|
import type { DataGridProps } from '@/components/common/DataGrid';
|
||||||
import DataGridCell from '@/components/common/DataGridCell';
|
import DataGridCell from '@/components/common/DataGridCell';
|
||||||
|
import type { DataBrowserGridColumn } from '@/features/database/dataGrid/types/dataBrowser';
|
||||||
import useDataGridConfig from '@/hooks/useDataGridConfig';
|
import useDataGridConfig from '@/hooks/useDataGridConfig';
|
||||||
import type { DataBrowserGridColumn } from '@/types/dataBrowser';
|
|
||||||
import type { BoxProps } from '@/ui/v2/Box';
|
import type { BoxProps } from '@/ui/v2/Box';
|
||||||
import Box from '@/ui/v2/Box';
|
import Box from '@/ui/v2/Box';
|
||||||
import Button from '@/ui/v2/Button';
|
import Button from '@/ui/v2/Button';
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import type {
|
|||||||
ColumnType,
|
ColumnType,
|
||||||
DataBrowserGridCell,
|
DataBrowserGridCell,
|
||||||
DataBrowserGridCellProps,
|
DataBrowserGridCellProps,
|
||||||
} from '@/types/dataBrowser';
|
} from '@/features/database/dataGrid/types/dataBrowser';
|
||||||
import type { BoxProps } from '@/ui/v2/Box';
|
import type { BoxProps } from '@/ui/v2/Box';
|
||||||
import Box from '@/ui/v2/Box';
|
import Box from '@/ui/v2/Box';
|
||||||
import Tooltip, { useTooltip } from '@/ui/v2/Tooltip';
|
import Tooltip, { useTooltip } from '@/ui/v2/Tooltip';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { DataGridProps } from '@/components/common/DataGrid';
|
import type { DataGridProps } from '@/components/common/DataGrid';
|
||||||
|
import type { DataBrowserGridColumn } from '@/features/database/dataGrid/types/dataBrowser';
|
||||||
import useDataGridConfig from '@/hooks/useDataGridConfig';
|
import useDataGridConfig from '@/hooks/useDataGridConfig';
|
||||||
import type { DataBrowserGridColumn } from '@/types/dataBrowser';
|
|
||||||
import Box from '@/ui/v2/Box';
|
import Box from '@/ui/v2/Box';
|
||||||
import Button from '@/ui/v2/Button';
|
import Button from '@/ui/v2/Button';
|
||||||
import Divider from '@/ui/v2/Divider';
|
import Divider from '@/ui/v2/Divider';
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import AudioPreview from '@/components/icons/AudioPreview';
|
|||||||
import { FileIcon } from '@/components/icons/FileIcon';
|
import { FileIcon } from '@/components/icons/FileIcon';
|
||||||
import PDFPreview from '@/components/icons/PDFPreview';
|
import PDFPreview from '@/components/icons/PDFPreview';
|
||||||
import VideoPreview from '@/components/icons/VideoPreview';
|
import VideoPreview from '@/components/icons/VideoPreview';
|
||||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
import { useAppClient } from '@/features/projects/hooks/useAppClient';
|
||||||
import { useAppClient } from '@/hooks/useAppClient';
|
import { useCurrentWorkspaceAndProject } from '@/features/projects/hooks/useCurrentWorkspaceAndProject';
|
||||||
import { Modal } from '@/ui/Modal';
|
import { Modal } from '@/ui/Modal';
|
||||||
import ActivityIndicator from '@/ui/v2/ActivityIndicator';
|
import ActivityIndicator from '@/ui/v2/ActivityIndicator';
|
||||||
import Box from '@/ui/v2/Box';
|
import Box from '@/ui/v2/Box';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { IconLinkProps } from '@/components/common/IconLink';
|
import type { IconLinkProps } from '@/components/common/IconLink';
|
||||||
import IconLink from '@/components/common/IconLink';
|
import IconLink from '@/components/common/IconLink';
|
||||||
import { Nav } from '@/components/dashboard/Nav';
|
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 type { BoxProps } from '@/ui/v2/Box';
|
||||||
import Box from '@/ui/v2/Box';
|
import Box from '@/ui/v2/Box';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
export * from './FeedbackForm';
|
export * from './FeedbackForm';
|
||||||
export { default } from './FeedbackForm';
|
export { default as FeedbackForm } from './FeedbackForm';
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
export * from './Form';
|
export * from './Form';
|
||||||
export { default } from './Form';
|
export { default as Form, default } from './Form';
|
||||||
|
|||||||
@@ -1,2 +1,5 @@
|
|||||||
export * from './FormActivityIndicator';
|
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 { Breadcrumbs } from '@/components/common/Breadcrumbs';
|
||||||
import FeedbackForm from '@/components/common/FeedbackForm';
|
import { FeedbackForm } from '@/components/common/FeedbackForm';
|
||||||
import LocalAccountMenu from '@/components/common/LocalAccountMenu';
|
import { LocalAccountMenu } from '@/components/common/LocalAccountMenu';
|
||||||
import Logo from '@/components/common/Logo';
|
import { Logo } from '@/components/common/Logo';
|
||||||
import MobileNav from '@/components/common/MobileNav';
|
import { MobileNav } from '@/components/common/MobileNav';
|
||||||
import NavLink from '@/components/common/NavLink';
|
import { NavLink } from '@/components/common/NavLink';
|
||||||
import { AccountMenu } from '@/components/dashboard/AccountMenu';
|
import { AccountMenu } from '@/components/dashboard/AccountMenu';
|
||||||
import useIsPlatform from '@/hooks/common/useIsPlatform';
|
import { useCurrentWorkspaceAndProject } from '@/features/projects/hooks/useCurrentWorkspaceAndProject';
|
||||||
import Box from '@/ui/v2/Box';
|
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 { Dropdown } from '@/ui/v2/Dropdown';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import type { DetailedHTMLProps, HTMLProps, PropsWithoutRef } from 'react';
|
import type { DetailedHTMLProps, HTMLProps, PropsWithoutRef } from 'react';
|
||||||
|
import { useEffect } from 'react';
|
||||||
import { twMerge } from 'tailwind-merge';
|
import { twMerge } from 'tailwind-merge';
|
||||||
|
|
||||||
export interface HeaderProps
|
export interface HeaderProps
|
||||||
@@ -20,6 +24,25 @@ export interface HeaderProps
|
|||||||
export default function Header({ className, ...props }: HeaderProps) {
|
export default function Header({ className, ...props }: HeaderProps) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const isPlatform = useIsPlatform();
|
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 (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@@ -39,6 +62,10 @@ export default function Header({ className, ...props }: HeaderProps) {
|
|||||||
{(router.query.workspaceSlug || router.query.appSlug) && (
|
{(router.query.workspaceSlug || router.query.appSlug) && (
|
||||||
<Breadcrumbs aria-label="Workspace breadcrumbs" />
|
<Breadcrumbs aria-label="Workspace breadcrumbs" />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{isProjectUpdating && (
|
||||||
|
<Chip size="small" label="Updating" color="warning" />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="hidden grid-flow-col items-center gap-2 sm:grid">
|
<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 * 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 { Dropdown } from '@/ui/v2/Dropdown';
|
||||||
import IconButton from '@/ui/v2/IconButton';
|
import { IconButton } from '@/ui/v2/IconButton';
|
||||||
import UserIcon from '@/ui/v2/icons/UserIcon';
|
import { UserIcon } from '@/ui/v2/icons/UserIcon';
|
||||||
import Text from '@/ui/v2/Text';
|
import { Text } from '@/ui/v2/Text';
|
||||||
import getConfig from 'next/config';
|
import getConfig from 'next/config';
|
||||||
|
|
||||||
export default function LocalAccountMenu() {
|
export default function LocalAccountMenu() {
|
||||||
@@ -25,14 +28,38 @@ export default function LocalAccountMenu() {
|
|||||||
|
|
||||||
<Dropdown.Content
|
<Dropdown.Content
|
||||||
PaperProps={{
|
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">
|
<Box className="grid grid-flow-row gap-0.5">
|
||||||
Dashboard Version: {publicRuntimeConfig?.version || 'n/a'}
|
<Text className="font-semibold">Local User</Text>
|
||||||
</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.Content>
|
||||||
</Dropdown.Root>
|
</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 FeedbackForm from '@/components/common/FeedbackForm';
|
import { NavLink } from '@/components/common/NavLink';
|
||||||
import NavLink from '@/components/common/NavLink';
|
import { ThemeSwitcher } from '@/components/common/ThemeSwitcher';
|
||||||
import ThemeSwitcher from '@/components/common/ThemeSwitcher';
|
|
||||||
import { Nav } from '@/components/dashboard/Nav';
|
import { Nav } from '@/components/dashboard/Nav';
|
||||||
import useIsPlatform from '@/hooks/common/useIsPlatform';
|
import { useIsPlatform } from '@/features/projects/hooks/useIsPlatform';
|
||||||
import useProjectRoutes from '@/hooks/common/useProjectRoutes';
|
import { useProjectRoutes } from '@/features/projects/hooks/useProjectRoutes';
|
||||||
import { useNavigationVisible } from '@/hooks/useNavigationVisible';
|
import { useNavigationVisible } from '@/hooks/useNavigationVisible';
|
||||||
import { Modal } from '@/ui/Modal';
|
|
||||||
import type { ButtonProps } from '@/ui/v2/Button';
|
import type { ButtonProps } from '@/ui/v2/Button';
|
||||||
import Button from '@/ui/v2/Button';
|
import { Button } from '@/ui/v2/Button';
|
||||||
import Divider from '@/ui/v2/Divider';
|
import { Divider } from '@/ui/v2/Divider';
|
||||||
import Drawer from '@/ui/v2/Drawer';
|
import { Drawer } from '@/ui/v2/Drawer';
|
||||||
import { Dropdown } from '@/ui/v2/Dropdown';
|
import { Dropdown } from '@/ui/v2/Dropdown';
|
||||||
import MenuIcon from '@/ui/v2/icons/MenuIcon';
|
import { MenuIcon } from '@/ui/v2/icons/MenuIcon';
|
||||||
import XIcon from '@/ui/v2/icons/XIcon';
|
import { XIcon } from '@/ui/v2/icons/XIcon';
|
||||||
import List from '@/ui/v2/List';
|
import { List } from '@/ui/v2/List';
|
||||||
import type { ListItemButtonProps } from '@/ui/v2/ListItem';
|
import type { ListItemButtonProps } from '@/ui/v2/ListItem';
|
||||||
import { ListItem } from '@/ui/v2/ListItem';
|
import { ListItem } from '@/ui/v2/ListItem';
|
||||||
import Text from '@/ui/v2/Text';
|
import { Text } from '@/ui/v2/Text';
|
||||||
import { useApolloClient } from '@apollo/client';
|
import { useApolloClient } from '@apollo/client';
|
||||||
import { useSignOut } from '@nhost/nextjs';
|
import { useSignOut } from '@nhost/nextjs';
|
||||||
import getConfig from 'next/config';
|
import getConfig from 'next/config';
|
||||||
@@ -86,7 +84,6 @@ export default function MobileNav({ className, ...props }: MobileNavProps) {
|
|||||||
const { allRoutes } = useProjectRoutes();
|
const { allRoutes } = useProjectRoutes();
|
||||||
const shouldDisplayNav = useNavigationVisible();
|
const shouldDisplayNav = useNavigationVisible();
|
||||||
const [menuOpen, setMenuOpen] = useState(false);
|
const [menuOpen, setMenuOpen] = useState(false);
|
||||||
const [showChangePasswordModal, setShowChangePasswordModal] = useState(false);
|
|
||||||
const { signOut } = useSignOut();
|
const { signOut } = useSignOut();
|
||||||
const apolloClient = useApolloClient();
|
const apolloClient = useApolloClient();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -94,14 +91,6 @@ export default function MobileNav({ className, ...props }: MobileNavProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Modal
|
|
||||||
showModal={showChangePasswordModal}
|
|
||||||
close={() => setShowChangePasswordModal(false)}
|
|
||||||
>
|
|
||||||
{/** TODO: Make use of `DialogProvider` here or create a separate page. */}
|
|
||||||
<ChangePasswordModal close={() => setShowChangePasswordModal(false)} />
|
|
||||||
</Modal>
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant="borderless"
|
variant="borderless"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
@@ -228,15 +217,14 @@ export default function MobileNav({ className, ...props }: MobileNavProps) {
|
|||||||
<List className="grid grid-flow-row gap-2">
|
<List className="grid grid-flow-row gap-2">
|
||||||
<ListItem.Root>
|
<ListItem.Root>
|
||||||
<ListItem.Button
|
<ListItem.Button
|
||||||
|
component={NavLink}
|
||||||
variant="borderless"
|
variant="borderless"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
className="w-full justify-start border-none px-2 py-2.5 text-[16px]"
|
className="w-full justify-start border-none px-2 py-2.5 text-[16px]"
|
||||||
onClick={() => {
|
href="/account"
|
||||||
setMenuOpen(false);
|
onClick={() => setMenuOpen(false)}
|
||||||
setShowChangePasswordModal(true);
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
Change Password
|
Settings
|
||||||
</ListItem.Button>
|
</ListItem.Button>
|
||||||
</ListItem.Root>
|
</ListItem.Root>
|
||||||
|
|
||||||
@@ -245,7 +233,7 @@ export default function MobileNav({ className, ...props }: MobileNavProps) {
|
|||||||
<ListItem.Root>
|
<ListItem.Root>
|
||||||
<ListItem.Button
|
<ListItem.Button
|
||||||
variant="borderless"
|
variant="borderless"
|
||||||
color="secondary"
|
sx={{ color: 'error.main' }}
|
||||||
className="justify-start border-none px-2 py-2.5 text-[16px]"
|
className="justify-start border-none px-2 py-2.5 text-[16px]"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
setMenuOpen(false);
|
setMenuOpen(false);
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
export * from './MobileNav';
|
export * from './MobileNav';
|
||||||
export { default } from './MobileNav';
|
export { default as MobileNav } from './MobileNav';
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
export * from './NavLink';
|
export * from './NavLink';
|
||||||
export { default } from './NavLink';
|
export { default as NavLink, default } from './NavLink';
|
||||||
|
|||||||
@@ -1,3 +1,2 @@
|
|||||||
export * from './Pagination';
|
export * from './Pagination';
|
||||||
export { default } from './Pagination';
|
export { default as Pagination } from './Pagination';
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +1,5 @@
|
|||||||
export * from './RetryableErrorBoundary';
|
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);
|
onChange?.(event, value);
|
||||||
}}
|
}}
|
||||||
slotProps={{
|
slotProps={{
|
||||||
|
...props?.slotProps,
|
||||||
listbox: { className: 'min-w-0 w-full' },
|
listbox: { className: 'min-w-0 w-full' },
|
||||||
popper: {
|
popper: {
|
||||||
disablePortal: false,
|
disablePortal: false,
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
export * from './ThemeSwitcher';
|
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 * from './AppDeploymentDuration';
|
||||||
export { default } from './AppDeploymentDuration';
|
export {
|
||||||
|
default as AppDeploymentDuration,
|
||||||
|
default,
|
||||||
|
} from './AppDeploymentDuration';
|
||||||
|
|||||||
@@ -1,20 +1,24 @@
|
|||||||
import NavLink from '@/components/common/NavLink';
|
import { NavLink } from '@/components/common/NavLink';
|
||||||
import AppDeploymentDuration from '@/components/deployments/AppDeploymentDuration';
|
import { AppDeploymentDuration } from '@/components/deployments/AppDeploymentDuration';
|
||||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
import { useCurrentWorkspaceAndProject } from '@/features/projects/hooks/useCurrentWorkspaceAndProject';
|
||||||
import { Avatar } from '@/ui/Avatar';
|
import { Avatar } from '@/ui/Avatar';
|
||||||
import type { DeploymentStatus } from '@/ui/StatusCircle';
|
import type { DeploymentStatus } from '@/ui/StatusCircle';
|
||||||
import { StatusCircle } from '@/ui/StatusCircle';
|
import { StatusCircle } from '@/ui/StatusCircle';
|
||||||
import Button from '@/ui/v2/Button';
|
import { Button } from '@/ui/v2/Button';
|
||||||
import Chip from '@/ui/v2/Chip';
|
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 { ListItem } from '@/ui/v2/ListItem';
|
||||||
import Tooltip from '@/ui/v2/Tooltip';
|
import { Tooltip } from '@/ui/v2/Tooltip';
|
||||||
import ArrowCounterclockwiseIcon from '@/ui/v2/icons/ArrowCounterclockwiseIcon';
|
import { getServerError } from '@/utils/settings/getServerError';
|
||||||
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 { getToastStyleProps } from '@/utils/settings/settingsConstants';
|
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 { formatDistanceToNowStrict, parseISO } from 'date-fns';
|
||||||
|
import type { MouseEvent } from 'react';
|
||||||
import { toast } from 'react-hot-toast';
|
import { toast } from 'react-hot-toast';
|
||||||
import { twMerge } from 'tailwind-merge';
|
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;
|
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 (
|
return (
|
||||||
<ListItem.Root>
|
<ListItem.Root>
|
||||||
<ListItem.Button
|
<ListItem.Button
|
||||||
@@ -88,7 +122,7 @@ export default function DeploymentListItem({
|
|||||||
{showRedeploy && (
|
{showRedeploy && (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={
|
title={
|
||||||
!disableRedeploy && !loading
|
disableRedeploy || loading
|
||||||
? 'Deployments cannot be re-triggered when a deployment is in progress.'
|
? 'Deployments cannot be re-triggered when a deployment is in progress.'
|
||||||
: ''
|
: ''
|
||||||
}
|
}
|
||||||
@@ -100,35 +134,7 @@ export default function DeploymentListItem({
|
|||||||
size="small"
|
size="small"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
onClick={async (event) => {
|
onClick={redeployDeployment}
|
||||||
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(),
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
startIcon={
|
startIcon={
|
||||||
<ArrowCounterclockwiseIcon className={twMerge('h-4 w-4')} />
|
<ArrowCounterclockwiseIcon className={twMerge('h-4 w-4')} />
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
export * from './DeploymentListItem';
|
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 { useDialog } from '@/components/common/DialogProvider';
|
||||||
import Form from '@/components/common/Form';
|
import { Form } from '@/components/common/Form';
|
||||||
import type { DialogFormProps } from '@/types/common';
|
import type { DialogFormProps } from '@/types/common';
|
||||||
import Button from '@/ui/v2/Button';
|
import { Button } from '@/ui/v2/Button';
|
||||||
import Input from '@/ui/v2/Input';
|
import { Input } from '@/ui/v2/Input';
|
||||||
|
import { slugifyString } from '@/utils/helpers';
|
||||||
|
import { getServerError } from '@/utils/settings/getServerError';
|
||||||
|
import { getToastStyleProps } from '@/utils/settings/settingsConstants';
|
||||||
import {
|
import {
|
||||||
GetAllWorkspacesAndProjectsDocument,
|
GetAllWorkspacesAndProjectsDocument,
|
||||||
useInsertWorkspaceMutation,
|
useInsertWorkspaceMutation,
|
||||||
useUpdateWorkspaceMutation,
|
useUpdateWorkspaceMutation,
|
||||||
} from '@/utils/__generated__/graphql';
|
} 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 { yupResolver } from '@hookform/resolvers/yup';
|
||||||
import { useUserData } from '@nhost/nextjs';
|
import { useUserData } from '@nhost/nextjs';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
|
import { useIsPlatform } from '@/features/projects/hooks/useIsPlatform';
|
||||||
import {
|
import {
|
||||||
GetAllWorkspacesAndProjectsDocument,
|
GetAllWorkspacesAndProjectsDocument,
|
||||||
GetWorkspaceMemberInvitesToManageDocument,
|
GetWorkspaceMemberInvitesToManageDocument,
|
||||||
useGetWorkspaceMemberInvitesToManageQuery,
|
useGetWorkspaceMemberInvitesToManageQuery,
|
||||||
} from '@/generated/graphql';
|
} from '@/generated/graphql';
|
||||||
import useIsPlatform from '@/hooks/common/useIsPlatform';
|
|
||||||
import { useSubmitState } from '@/hooks/useSubmitState';
|
import { useSubmitState } from '@/hooks/useSubmitState';
|
||||||
import Box from '@/ui/v2/Box';
|
import Box from '@/ui/v2/Box';
|
||||||
import Button from '@/ui/v2/Button';
|
import Button from '@/ui/v2/Button';
|
||||||
import Text from '@/ui/v2/Text';
|
import Text from '@/ui/v2/Text';
|
||||||
import { nhost } from '@/utils/nhost';
|
import { nhost } from '@/utils/nhost';
|
||||||
import { triggerToast } from '@/utils/toast';
|
import { triggerToast } from '@/utils/toast';
|
||||||
import { updateOwnCache } from '@/utils/updateOwnCache';
|
|
||||||
import { useApolloClient } from '@apollo/client';
|
import { useApolloClient } from '@apollo/client';
|
||||||
import { alpha } from '@mui/system';
|
import { alpha } from '@mui/system';
|
||||||
import { useUserData } from '@nhost/nextjs';
|
import { useUserData } from '@nhost/nextjs';
|
||||||
@@ -28,13 +27,18 @@ export function InviteAnnounce() {
|
|||||||
useSubmitState();
|
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?
|
// @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 } =
|
const {
|
||||||
useGetWorkspaceMemberInvitesToManageQuery({
|
data,
|
||||||
variables: {
|
loading,
|
||||||
userId: user?.id,
|
error,
|
||||||
},
|
refetch: refetchInvitations,
|
||||||
skip: !isPlatform || !user,
|
startPolling,
|
||||||
});
|
} = useGetWorkspaceMemberInvitesToManageQuery({
|
||||||
|
variables: {
|
||||||
|
userId: user?.id,
|
||||||
|
},
|
||||||
|
skip: !isPlatform || !user,
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
startPolling(15000);
|
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 router.push(`/${invite.workspace.slug}`);
|
||||||
await refetch();
|
await refetchInvitations();
|
||||||
triggerToast('Workspace invite accepted');
|
triggerToast('Workspace invite accepted');
|
||||||
return setSubmitState({
|
return setSubmitState({
|
||||||
error: null,
|
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 { useInsertFeedbackOneMutation } from '@/generated/graphql';
|
||||||
import { Avatar } from '@/ui/Avatar';
|
import { Avatar } from '@/ui/Avatar';
|
||||||
import Button from '@/ui/v2/Button';
|
import Button from '@/ui/v2/Button';
|
||||||
|
|||||||
@@ -207,19 +207,6 @@ export default function WorkspaceAndProjectList({
|
|||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</Box>
|
</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>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import Header from '@/components/common/Header';
|
import Header from '@/components/common/Header';
|
||||||
import HighlightedText from '@/components/common/HighlightedText';
|
import { HighlightedText } from '@/components/common/HighlightedText';
|
||||||
import RetryableErrorBoundary from '@/components/common/RetryableErrorBoundary';
|
import { RetryableErrorBoundary } from '@/components/common/RetryableErrorBoundary';
|
||||||
import { InviteAnnounce } from '@/components/home/InviteAnnounce';
|
import { InviteAnnounce } from '@/components/home/InviteAnnounce';
|
||||||
import type { BaseLayoutProps } from '@/components/layout/BaseLayout';
|
import type { BaseLayoutProps } from '@/components/layout/BaseLayout';
|
||||||
import BaseLayout from '@/components/layout/BaseLayout';
|
import BaseLayout from '@/components/layout/BaseLayout';
|
||||||
import Container from '@/components/layout/Container';
|
import Container from '@/components/layout/Container';
|
||||||
import useIsHealthy from '@/hooks/common/useIsHealthy';
|
import { useIsHealthy } from '@/features/projects/hooks/useIsHealthy';
|
||||||
import useIsPlatform from '@/hooks/common/useIsPlatform';
|
import { useIsPlatform } from '@/features/projects/hooks/useIsPlatform';
|
||||||
import ActivityIndicator from '@/ui/v2/ActivityIndicator';
|
import ActivityIndicator from '@/ui/v2/ActivityIndicator';
|
||||||
import Link from '@/ui/v2/Link';
|
import Link from '@/ui/v2/Link';
|
||||||
import Text from '@/ui/v2/Text';
|
import Text from '@/ui/v2/Text';
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
export * from './AuthenticatedLayout';
|
export * from './AuthenticatedLayout';
|
||||||
export { default } from './AuthenticatedLayout';
|
export { default as AuthenticatedLayout, default } from './AuthenticatedLayout';
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
export * from './Container';
|
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 { LoadingScreen } from '@/components/common/LoadingScreen';
|
||||||
import type { AuthenticatedLayoutProps } from '@/components/layout/AuthenticatedLayout';
|
import type { AuthenticatedLayoutProps } from '@/components/layout/AuthenticatedLayout';
|
||||||
import AuthenticatedLayout from '@/components/layout/AuthenticatedLayout';
|
import AuthenticatedLayout from '@/components/layout/AuthenticatedLayout';
|
||||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
import { useCurrentWorkspaceAndProject } from '@/features/projects/hooks/useCurrentWorkspaceAndProject';
|
||||||
import useIsPlatform from '@/hooks/common/useIsPlatform';
|
import { useIsPlatform } from '@/features/projects/hooks/useIsPlatform';
|
||||||
import useProjectRoutes from '@/hooks/common/useProjectRoutes';
|
import { useProjectRoutes } from '@/features/projects/hooks/useProjectRoutes';
|
||||||
import { useNavigationVisible } from '@/hooks/useNavigationVisible';
|
import { useNavigationVisible } from '@/hooks/useNavigationVisible';
|
||||||
import useNotFoundRedirect from '@/hooks/useNotFoundRedirect';
|
import useNotFoundRedirect from '@/hooks/useNotFoundRedirect';
|
||||||
import type { BoxProps } from '@/ui/v2/Box';
|
import type { BoxProps } from '@/ui/v2/Box';
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
export * from './ProjectLayout';
|
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 type { BaseLayoutProps } from '@/components/layout/BaseLayout';
|
||||||
import BaseLayout from '@/components/layout/BaseLayout';
|
import BaseLayout from '@/components/layout/BaseLayout';
|
||||||
import Container from '@/components/layout/Container';
|
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 Box from '@/ui/v2/Box';
|
||||||
import ThemeProvider from '@/ui/v2/ThemeProvider';
|
import ThemeProvider from '@/ui/v2/ThemeProvider';
|
||||||
import GlobalStyles from '@mui/material/GlobalStyles';
|
import GlobalStyles from '@mui/material/GlobalStyles';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import LogsDatePicker from '@/components/logs/LogsDatePicker';
|
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 { AvailableLogsServices, LogsCustomInterval } from '@/types/logs';
|
||||||
import type { BoxProps } from '@/ui/v2/Box';
|
import type { BoxProps } from '@/ui/v2/Box';
|
||||||
import Box 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';
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
export * from './OverviewDocumentation';
|
|
||||||
export { default } from './OverviewDocumentation';
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export { default } from './OverviewMetrics';
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user