Compare commits
16 Commits
@nhost/rea
...
@nhost/nex
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2e3357b7a3 | ||
|
|
4385524311 | ||
|
|
9e404c8fc9 | ||
|
|
f8e6b615dd | ||
|
|
ac4aa01ec9 | ||
|
|
e515e71c8b | ||
|
|
1246e0024a | ||
|
|
81cc9b3810 | ||
|
|
5c6ff6efc8 | ||
|
|
1956ed23f8 | ||
|
|
af34015dbe | ||
|
|
88919a3d99 | ||
|
|
ab26a57d05 | ||
|
|
f1052a8826 | ||
|
|
30daa4146e | ||
|
|
7537237465 |
2
.github/workflows/ci.yaml
vendored
2
.github/workflows/ci.yaml
vendored
@@ -163,7 +163,7 @@ jobs:
|
||||
# * Run this step only if the previous step failed, and Playwright generated a report
|
||||
- name: Upload Playwright Report
|
||||
if: ${{ failure() && hashFiles(format('{0}/playwright-report/**', matrix.package.path)) != ''}}
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: playwright-${{ steps.file-name.outputs.fileName }}
|
||||
path: ${{format('{0}/playwright-report/**', matrix.package.path)}}
|
||||
|
||||
@@ -1,5 +1,27 @@
|
||||
# @nhost/dashboard
|
||||
|
||||
## 2.16.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- f8e6b61: fix: can add rule groups in table permissions
|
||||
- 9e404c8: fix: not redirect to 404 page if using local Nhost backend
|
||||
- ac4aa01: fix: can delete column in database page
|
||||
- 4385524: fix: update url to check service health in local dashboard
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/react-apollo@16.0.1
|
||||
- @nhost/nextjs@2.2.2
|
||||
|
||||
## 2.15.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- f1052a8: fix: improve stability of the dashboard when pausing projects
|
||||
- 30daa41: fix: update links to docs in overview page
|
||||
- 7537237: feat: add image preview toggle in storage
|
||||
|
||||
## 2.14.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/dashboard",
|
||||
"version": "2.14.0",
|
||||
"version": "2.16.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
DialogTitle,
|
||||
} from '@/components/ui/v3/dialog';
|
||||
|
||||
import { LoadingScreen } from '@/components/presentational/LoadingScreen';
|
||||
import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
|
||||
import {
|
||||
Form,
|
||||
@@ -53,8 +54,8 @@ export default function TransferProjectDialog({
|
||||
}: TransferProjectDialogProps) {
|
||||
const { push } = useRouter();
|
||||
const currentUserId = useUserId();
|
||||
const { project } = useProject();
|
||||
const { orgs, currentOrg } = useOrgs();
|
||||
const { project, loading: projectLoading } = useProject();
|
||||
const { orgs, currentOrg, loading: orgsLoading } = useOrgs();
|
||||
const [transferProject] = useBillingTransferAppMutation();
|
||||
|
||||
const form = useForm<z.infer<typeof transferProjectFormSchema>>({
|
||||
@@ -96,6 +97,10 @@ export default function TransferProjectDialog({
|
||||
member.user.id === userId,
|
||||
);
|
||||
|
||||
if (projectLoading || orgsLoading) {
|
||||
return <LoadingScreen />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={open}
|
||||
|
||||
@@ -61,7 +61,7 @@ export default function AISettings() {
|
||||
const [updateConfig] = useUpdateConfigMutation({
|
||||
...(!isPlatform ? { client: localMimirClient } : {}),
|
||||
});
|
||||
const { project } = useProject();
|
||||
const { project, loading: loadingProject } = useProject();
|
||||
|
||||
const [aiServiceEnabled, setAIServiceEnabled] = useState(true);
|
||||
|
||||
@@ -73,9 +73,10 @@ export default function AISettings() {
|
||||
error: errorGettingAiSettings,
|
||||
} = useGetAiSettingsQuery({
|
||||
variables: {
|
||||
appId: project.id,
|
||||
appId: project?.id,
|
||||
},
|
||||
...(!isPlatform ? { client: localMimirClient } : {}),
|
||||
skip: !project?.id,
|
||||
});
|
||||
|
||||
const { data: graphiteVersionsData, loading: loadingGraphiteVersionsData } =
|
||||
@@ -192,11 +193,11 @@ export default function AISettings() {
|
||||
}
|
||||
};
|
||||
|
||||
if (loadingAiSettings || loadingGraphiteVersionsData) {
|
||||
if (loadingProject || loadingAiSettings || loadingGraphiteVersionsData) {
|
||||
return (
|
||||
<ActivityIndicator
|
||||
delay={1000}
|
||||
label="Loading Postgres version..."
|
||||
label="Loading AI settings..."
|
||||
className="justify-center"
|
||||
/>
|
||||
);
|
||||
@@ -269,7 +270,7 @@ export default function AISettings() {
|
||||
|
||||
return (
|
||||
<Box className="space-y-4" sx={{ backgroundColor: 'background.default' }}>
|
||||
<Box className="flex flex-row items-center justify-between p-4 rounded-lg border-1">
|
||||
<Box className="flex flex-row items-center justify-between rounded-lg border-1 p-4">
|
||||
<Text className="text-lg font-semibold">Enable AI service</Text>
|
||||
<Switch
|
||||
checked={aiServiceEnabled}
|
||||
@@ -298,7 +299,7 @@ export default function AISettings() {
|
||||
<Tooltip title="Version of the service to use.">
|
||||
<InfoIcon
|
||||
aria-label="Info"
|
||||
className="w-4 h-4"
|
||||
className="h-4 w-4"
|
||||
color="primary"
|
||||
/>
|
||||
</Tooltip>
|
||||
@@ -353,7 +354,7 @@ export default function AISettings() {
|
||||
<Tooltip title="Used to validate requests between postgres and the AI service. The AI service will also include the header X-Graphite-Webhook-Secret with this value set when calling external webhooks so the source of the request can be validated.">
|
||||
<InfoIcon
|
||||
aria-label="Info"
|
||||
className="w-4 h-4"
|
||||
className="h-4 w-4"
|
||||
color="primary"
|
||||
/>
|
||||
</Tooltip>
|
||||
@@ -377,7 +378,7 @@ export default function AISettings() {
|
||||
<Tooltip title="Dedicated resources allocated for the service.">
|
||||
<InfoIcon
|
||||
aria-label="Info"
|
||||
className="w-4 h-4"
|
||||
className="h-4 w-4"
|
||||
color="primary"
|
||||
/>
|
||||
</Tooltip>
|
||||
@@ -417,7 +418,7 @@ export default function AISettings() {
|
||||
<Tooltip title="Key to use for authenticating API requests to OpenAI">
|
||||
<InfoIcon
|
||||
aria-label="Info"
|
||||
className="w-4 h-4"
|
||||
className="h-4 w-4"
|
||||
color="primary"
|
||||
/>
|
||||
</Tooltip>
|
||||
@@ -440,7 +441,7 @@ export default function AISettings() {
|
||||
<Tooltip title="Optional. OpenAI organization to use.">
|
||||
<InfoIcon
|
||||
aria-label="Info"
|
||||
className="w-4 h-4"
|
||||
className="h-4 w-4"
|
||||
color="primary"
|
||||
/>
|
||||
</Tooltip>
|
||||
@@ -468,7 +469,7 @@ export default function AISettings() {
|
||||
<Tooltip title="How often to run the job that keeps embeddings up to date.">
|
||||
<InfoIcon
|
||||
aria-label="Info"
|
||||
className="w-4 h-4"
|
||||
className="h-4 w-4"
|
||||
color="primary"
|
||||
/>
|
||||
</Tooltip>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ContactUs } from '@/components/common/ContactUs';
|
||||
import { LoadingScreen } from '@/components/presentational/LoadingScreen';
|
||||
import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
|
||||
import { Button } from '@/components/ui/v2/Button';
|
||||
import { Dropdown } from '@/components/ui/v2/Dropdown';
|
||||
@@ -6,7 +7,7 @@ import { Text } from '@/components/ui/v2/Text';
|
||||
import { useProject } from '@/features/orgs/projects/hooks/useProject';
|
||||
import { useInterval } from '@/hooks/useInterval';
|
||||
import { getRelativeDateByApplicationState } from '@/utils/helpers';
|
||||
import { useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
export interface AppLoaderProps {
|
||||
/**
|
||||
@@ -33,25 +34,31 @@ export default function AppLoader({
|
||||
date,
|
||||
restoring,
|
||||
}: AppLoaderProps) {
|
||||
const { project } = useProject();
|
||||
const { project, loading } = useProject();
|
||||
const [timeElapsed, setTimeElapsed] = useState<number>(0);
|
||||
|
||||
let timeElapsedSinceEventCreation: number;
|
||||
useEffect(() => {
|
||||
if (!project || loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (date) {
|
||||
timeElapsedSinceEventCreation = getRelativeDateByApplicationState(date);
|
||||
} else if (unpause) {
|
||||
timeElapsedSinceEventCreation = getRelativeDateByApplicationState(
|
||||
project.appStates[0].createdAt,
|
||||
);
|
||||
} else {
|
||||
timeElapsedSinceEventCreation = getRelativeDateByApplicationState(
|
||||
project.createdAt,
|
||||
);
|
||||
}
|
||||
let timeElapsedSinceEventCreation: number;
|
||||
|
||||
const [timeElapsed, setTimeElapsed] = useState(timeElapsedSinceEventCreation);
|
||||
if (date) {
|
||||
timeElapsedSinceEventCreation = getRelativeDateByApplicationState(date);
|
||||
} else if (unpause) {
|
||||
timeElapsedSinceEventCreation = getRelativeDateByApplicationState(
|
||||
project.appStates[0].createdAt,
|
||||
);
|
||||
} else {
|
||||
timeElapsedSinceEventCreation = getRelativeDateByApplicationState(
|
||||
project.createdAt,
|
||||
);
|
||||
}
|
||||
|
||||
setTimeElapsed(timeElapsedSinceEventCreation);
|
||||
}, [project, date, unpause, loading]);
|
||||
|
||||
// Would be also valuable to check the appCreatedTime so this doesn't ever appear if not created under a time limit. @GC
|
||||
useInterval(
|
||||
() => {
|
||||
setTimeElapsed(timeElapsed + 1);
|
||||
@@ -59,6 +66,10 @@ export default function AppLoader({
|
||||
startLoader ? 1000 : null,
|
||||
);
|
||||
|
||||
if (loading) {
|
||||
return <LoadingScreen />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="grid grid-flow-row gap-2">
|
||||
<div className="grid grid-flow-row gap-1">
|
||||
@@ -86,9 +97,9 @@ export default function AppLoader({
|
||||
<ActivityIndicator className="mx-auto" />
|
||||
|
||||
{timeElapsed > 180 && (
|
||||
<Dropdown.Root className="flex flex-col mx-auto">
|
||||
<Dropdown.Root className="mx-auto flex flex-col">
|
||||
<Dropdown.Trigger
|
||||
className="flex mx-auto font-medium"
|
||||
className="mx-auto flex font-medium"
|
||||
hideChevron
|
||||
asChild
|
||||
>
|
||||
|
||||
@@ -13,12 +13,12 @@ export default function useIsHealthy() {
|
||||
const appUrl = generateAppServiceUrl(
|
||||
project?.subdomain,
|
||||
project?.region,
|
||||
'auth',
|
||||
'hasura',
|
||||
);
|
||||
|
||||
const { failureCount, status } = useQuery(
|
||||
['/healthz'],
|
||||
() => fetch(`${appUrl}/healthz`),
|
||||
['/v1/version'],
|
||||
() => fetch(`${appUrl}/v1/version`),
|
||||
{
|
||||
enabled: !isPlatform && !!project,
|
||||
retry: true,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { ProjectFragment } from '@/utils/__generated__/graphql';
|
||||
import {
|
||||
getAuthServiceUrl,
|
||||
getDatabaseServiceUrl,
|
||||
@@ -7,7 +8,6 @@ import {
|
||||
getStorageServiceUrl,
|
||||
isPlatform,
|
||||
} from '@/utils/env';
|
||||
import type { ProjectFragment } from '@/utils/__generated__/graphql';
|
||||
|
||||
export type NhostService =
|
||||
| 'auth'
|
||||
@@ -18,21 +18,6 @@ export type NhostService =
|
||||
| 'hasura'
|
||||
| 'grafana';
|
||||
|
||||
/**
|
||||
* The default slugs that are used when running the dashboard locally. These
|
||||
* values are used both in local mode and when running the dashboard locally
|
||||
* against the remote (either staging or production) backend.
|
||||
*/
|
||||
export const defaultLocalBackendSlugs: Record<NhostService, string> = {
|
||||
auth: '/v1/auth',
|
||||
db: '',
|
||||
graphql: '/v1/graphql',
|
||||
functions: '/v1/functions',
|
||||
storage: '/v1/files',
|
||||
hasura: '',
|
||||
grafana: '',
|
||||
};
|
||||
|
||||
/**
|
||||
* The default slugs that are used when running the dashboard against the
|
||||
* remote (either staging or production) backend in a cloud environment.
|
||||
|
||||
@@ -34,7 +34,11 @@ export default function AuthDomain() {
|
||||
const localMimirClient = useLocalMimirClient();
|
||||
const [isVerified, setIsVerified] = useState(false);
|
||||
|
||||
const { project, refetch: refetchProject } = useProject();
|
||||
const {
|
||||
project,
|
||||
refetch: refetchProject,
|
||||
loading: loadingProject,
|
||||
} = useProject();
|
||||
|
||||
const [updateConfig] = useUpdateConfigMutation({
|
||||
...(!isPlatform ? { client: localMimirClient } : {}),
|
||||
@@ -48,7 +52,7 @@ export default function AuthDomain() {
|
||||
|
||||
const { data, loading, error } = useGetAuthenticationSettingsQuery({
|
||||
variables: {
|
||||
appId: project.id,
|
||||
appId: project?.id,
|
||||
},
|
||||
...(!isPlatform ? { client: localMimirClient } : {}),
|
||||
});
|
||||
@@ -62,7 +66,7 @@ export default function AuthDomain() {
|
||||
}
|
||||
}, [data, loading, form, initialValue]);
|
||||
|
||||
if (loading) {
|
||||
if (loadingProject || loading) {
|
||||
return (
|
||||
<ActivityIndicator
|
||||
delay={1000}
|
||||
@@ -147,7 +151,7 @@ export default function AuthDomain() {
|
||||
loading: formState.isSubmitting,
|
||||
},
|
||||
}}
|
||||
className="grid grid-flow-row px-4 gap-x-4 gap-y-4 lg:grid-cols-5"
|
||||
className="grid grid-flow-row gap-x-4 gap-y-4 px-4 lg:grid-cols-5"
|
||||
>
|
||||
<Input
|
||||
{...register('auth_fqdn')}
|
||||
|
||||
@@ -14,10 +14,14 @@ const validationSchema = Yup.object({
|
||||
export type DatabaseDomainFormValues = Yup.InferType<typeof validationSchema>;
|
||||
|
||||
export default function DatabaseDomain() {
|
||||
const { project } = useProject();
|
||||
const { project, loading } = useProject();
|
||||
|
||||
const [dbFQDN, setDbFQDN] = useState('');
|
||||
|
||||
if (loading) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const postgresHost = generateAppServiceUrl(
|
||||
project.subdomain,
|
||||
project.region,
|
||||
@@ -36,7 +40,7 @@ export default function DatabaseDomain() {
|
||||
className: 'hidden',
|
||||
},
|
||||
}}
|
||||
className="grid grid-flow-row px-4 gap-x-4 gap-y-4 lg:grid-cols-5"
|
||||
className="grid grid-flow-row gap-x-4 gap-y-4 px-4 lg:grid-cols-5"
|
||||
>
|
||||
<Input
|
||||
id="database_fqdn"
|
||||
|
||||
@@ -33,7 +33,11 @@ export default function HasuraDomain() {
|
||||
const localMimirClient = useLocalMimirClient();
|
||||
const [isVerified, setIsVerified] = useState(false);
|
||||
|
||||
const { project, refetch: refetchProject } = useProject();
|
||||
const {
|
||||
project,
|
||||
refetch: refetchProject,
|
||||
loading: loadingProject,
|
||||
} = useProject();
|
||||
|
||||
const [updateConfig] = useUpdateConfigMutation({
|
||||
...(!isPlatform ? { client: localMimirClient } : {}),
|
||||
@@ -47,7 +51,7 @@ export default function HasuraDomain() {
|
||||
|
||||
const { data, loading, error } = useGetHasuraSettingsQuery({
|
||||
variables: {
|
||||
appId: project.id,
|
||||
appId: project?.id,
|
||||
},
|
||||
...(!isPlatform ? { client: localMimirClient } : {}),
|
||||
});
|
||||
@@ -61,7 +65,7 @@ export default function HasuraDomain() {
|
||||
}
|
||||
}, [data, loading, form, initialValue]);
|
||||
|
||||
if (loading) {
|
||||
if (loadingProject || loading) {
|
||||
return (
|
||||
<ActivityIndicator
|
||||
delay={0}
|
||||
@@ -148,7 +152,7 @@ export default function HasuraDomain() {
|
||||
loading: formState.isSubmitting,
|
||||
},
|
||||
}}
|
||||
className="grid grid-flow-row px-4 gap-x-4 gap-y-4 lg:grid-cols-5"
|
||||
className="grid grid-flow-row gap-x-4 gap-y-4 px-4 lg:grid-cols-5"
|
||||
>
|
||||
<Input
|
||||
{...register('hasura_fqdn')}
|
||||
|
||||
@@ -35,7 +35,11 @@ export default function ServerlessFunctionsDomain() {
|
||||
const { maintenanceActive } = useUI();
|
||||
const localMimirClient = useLocalMimirClient();
|
||||
const [isVerified, setIsVerified] = useState(false);
|
||||
const { project, refetch: refetchProject } = useProject();
|
||||
const {
|
||||
project,
|
||||
refetch: refetchProject,
|
||||
loading: loadingProject,
|
||||
} = useProject();
|
||||
|
||||
const [updateConfig] = useUpdateConfigMutation({
|
||||
...(!isPlatform ? { client: localMimirClient } : {}),
|
||||
@@ -49,7 +53,7 @@ export default function ServerlessFunctionsDomain() {
|
||||
|
||||
const { data, loading, error } = useGetServerlessFunctionsSettingsQuery({
|
||||
variables: {
|
||||
appId: project.id,
|
||||
appId: project?.id,
|
||||
},
|
||||
...(!isPlatform ? { client: localMimirClient } : {}),
|
||||
});
|
||||
@@ -63,7 +67,7 @@ export default function ServerlessFunctionsDomain() {
|
||||
}
|
||||
}, [data, loading, form, initialValue]);
|
||||
|
||||
if (loading) {
|
||||
if (loadingProject || loading) {
|
||||
return (
|
||||
<ActivityIndicator
|
||||
delay={1000}
|
||||
@@ -151,7 +155,7 @@ export default function ServerlessFunctionsDomain() {
|
||||
loading: formState.isSubmitting,
|
||||
},
|
||||
}}
|
||||
className="grid grid-flow-row px-4 gap-x-4 gap-y-4 lg:grid-cols-5"
|
||||
className="grid grid-flow-row gap-x-4 gap-y-4 px-4 lg:grid-cols-5"
|
||||
>
|
||||
<Input
|
||||
{...register('functions_fqdn')}
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
import { useDialog } from '@/components/common/DialogProvider';
|
||||
import { useDataGridConfig } from '@/components/dataGrid/DataGridConfigProvider';
|
||||
import type { DataGridPaginationProps } from '@/components/dataGrid/DataGridPagination';
|
||||
import { DataGridPagination } from '@/components/dataGrid/DataGridPagination';
|
||||
import type { BoxProps } from '@/components/ui/v2/Box';
|
||||
import { Box } from '@/components/ui/v2/Box';
|
||||
import { Button } from '@/components/ui/v2/Button';
|
||||
@@ -13,6 +10,9 @@ import { RowIcon } from '@/components/ui/v2/icons/RowIcon';
|
||||
import { useDeleteRecordMutation } from '@/features/orgs/projects/database/dataGrid/hooks/useDeleteRecordMutation';
|
||||
import type { DataBrowserGridColumn } from '@/features/orgs/projects/database/dataGrid/types/dataBrowser';
|
||||
import { useProject } from '@/features/orgs/projects/hooks/useProject';
|
||||
import { useDataGridConfig } from '@/features/orgs/projects/storage/dataGrid/components/DataGridConfigProvider';
|
||||
import type { DataGridPaginationProps } from '@/features/orgs/projects/storage/dataGrid/components/DataGridPagination';
|
||||
import { DataGridPagination } from '@/features/orgs/projects/storage/dataGrid/components/DataGridPagination';
|
||||
import { triggerToast } from '@/utils/toast';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { useState } from 'react';
|
||||
@@ -120,7 +120,7 @@ export default function DataBrowserGridControls({
|
||||
)}
|
||||
>
|
||||
{numberOfSelectedRows > 0 && (
|
||||
<div className="grid items-center grid-flow-col gap-2 place-content-start">
|
||||
<div className="grid grid-flow-col place-content-start items-center gap-2">
|
||||
<Chip
|
||||
size="small"
|
||||
color="info"
|
||||
@@ -161,7 +161,7 @@ export default function DataBrowserGridControls({
|
||||
)}
|
||||
|
||||
{numberOfSelectedRows === 0 && (
|
||||
<div className="grid items-center grid-flow-col col-span-6 gap-2">
|
||||
<div className="col-span-6 grid grid-flow-col items-center gap-2">
|
||||
{columns.length > 0 && (
|
||||
<DataGridPagination
|
||||
className={twMerge(
|
||||
@@ -177,7 +177,7 @@ export default function DataBrowserGridControls({
|
||||
<Dropdown.Root>
|
||||
<Dropdown.Trigger asChild hideChevron>
|
||||
<Button
|
||||
startIcon={<PlusIcon className="w-4 h-4" />}
|
||||
startIcon={<PlusIcon className="h-4 w-4" />}
|
||||
size="small"
|
||||
>
|
||||
Insert
|
||||
|
||||
@@ -18,21 +18,7 @@ const ruleSchema = Yup.object().shape({
|
||||
});
|
||||
|
||||
const ruleGroupSchema = Yup.object().shape({
|
||||
operator: Yup.string().test(
|
||||
'operator',
|
||||
'Please select an operator.',
|
||||
(selectedOperator, ctx) => {
|
||||
// `from` is part of the Yup API, but it's not typed.
|
||||
// @ts-ignore
|
||||
const [, { value }] = ctx.from;
|
||||
|
||||
if (Object.keys(value.filter).length > 0 && !selectedOperator) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
),
|
||||
operator: Yup.string().required('Please select an operator.'),
|
||||
rules: Yup.array().of(ruleSchema),
|
||||
groups: Yup.array().of(Yup.lazy(() => ruleGroupSchema) as any),
|
||||
});
|
||||
|
||||
@@ -5,12 +5,12 @@ import { Button } from '@/components/ui/v2/Button';
|
||||
import { PlusIcon } from '@/components/ui/v2/icons/PlusIcon';
|
||||
import { Link } from '@/components/ui/v2/Link';
|
||||
import { Text } from '@/components/ui/v2/Text';
|
||||
import { generateAppServiceUrl } from '@/features/orgs/projects/common/utils/generateAppServiceUrl';
|
||||
import type {
|
||||
Rule,
|
||||
RuleGroup,
|
||||
} from '@/features/orgs/projects/database/dataGrid/types/dataBrowser';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
||||
import { generateAppServiceUrl } from '@/features/projects/common/utils/generateAppServiceUrl';
|
||||
import { useProject } from '@/features/orgs/projects/hooks/useProject';
|
||||
import { useMemo } from 'react';
|
||||
import { useFieldArray, useFormContext } from 'react-hook-form';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
@@ -68,7 +68,7 @@ export default function RuleGroupEditor({
|
||||
sx,
|
||||
...props
|
||||
}: RuleGroupEditorProps) {
|
||||
const { currentProject } = useCurrentWorkspaceAndProject();
|
||||
const { project } = useProject();
|
||||
const form = useFormContext();
|
||||
|
||||
const { control, getValues } = form;
|
||||
@@ -185,13 +185,13 @@ export default function RuleGroupEditor({
|
||||
<Text>
|
||||
This rule group contains one or more objects (e.g: _exists) that
|
||||
are not supported by our dashboard yet.{' '}
|
||||
{currentProject && (
|
||||
{project && (
|
||||
<span>
|
||||
Please{' '}
|
||||
<Link
|
||||
href={`${generateAppServiceUrl(
|
||||
currentProject.subdomain,
|
||||
currentProject.region,
|
||||
project.subdomain,
|
||||
project.region,
|
||||
'hasura',
|
||||
)}/console/data/default/schema/${schema}/tables/${table}/permissions`}
|
||||
underline="hover"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
||||
import { useIsPlatform } from '@/features/projects/common/hooks/useIsPlatform';
|
||||
import { useIsPlatform } from '@/features/orgs/projects/common/hooks/useIsPlatform';
|
||||
import { useProject } from '@/features/orgs/projects/hooks/useProject';
|
||||
import { generateAppServiceUrl } from '@/features/projects/common/utils/generateAppServiceUrl';
|
||||
import { getHasuraAdminSecret } from '@/utils/env';
|
||||
import type { MutationOptions } from '@tanstack/react-query';
|
||||
@@ -39,10 +39,10 @@ export default function useDeleteColumnMutation({
|
||||
const {
|
||||
query: { dataSourceSlug, schemaSlug, tableSlug },
|
||||
} = useRouter();
|
||||
const { currentProject } = useCurrentWorkspaceAndProject();
|
||||
const { project } = useProject();
|
||||
const appUrl = generateAppServiceUrl(
|
||||
currentProject?.subdomain,
|
||||
currentProject?.region,
|
||||
project?.subdomain,
|
||||
project?.region,
|
||||
'hasura',
|
||||
);
|
||||
const mutationFn = isPlatform ? deleteColumn : deleteColumnMigration;
|
||||
@@ -55,7 +55,7 @@ export default function useDeleteColumnMutation({
|
||||
adminSecret:
|
||||
process.env.NEXT_PUBLIC_ENV === 'dev'
|
||||
? getHasuraAdminSecret()
|
||||
: customAdminSecret || currentProject?.config?.hasura.adminSecret,
|
||||
: customAdminSecret || project?.config?.hasura.adminSecret,
|
||||
dataSource: customDataSource || (dataSourceSlug as string),
|
||||
schema: customSchema || (schemaSlug as string),
|
||||
table: customTable || (tableSlug as string),
|
||||
|
||||
@@ -13,8 +13,8 @@ import { Tooltip } from '@/components/ui/v2/Tooltip';
|
||||
import { useProject } from '@/features/orgs/projects/hooks/useProject';
|
||||
import { LogsRangeSelector } from '@/features/orgs/projects/logs/components/LogsRangeSelector';
|
||||
import { AvailableLogsService } from '@/features/orgs/projects/logs/utils/constants/services';
|
||||
import { MINUTES_TO_DECREASE_FROM_CURRENT_DATE } from '@/utils/constants/common';
|
||||
import { useGetServiceLabelValuesQuery } from '@/utils/__generated__/graphql';
|
||||
import { MINUTES_TO_DECREASE_FROM_CURRENT_DATE } from '@/utils/constants/common';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import { subMinutes } from 'date-fns';
|
||||
import { useEffect, useState } from 'react';
|
||||
@@ -55,7 +55,7 @@ export default function LogsHeader({
|
||||
|
||||
const { data, loading: loadingServiceLabelValues } =
|
||||
useGetServiceLabelValuesQuery({
|
||||
variables: { appID: project.id },
|
||||
variables: { appID: project?.id },
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -121,7 +121,7 @@ export default function LogsHeader({
|
||||
<Box className="flex flex-row space-x-2">
|
||||
<ControlledSelect
|
||||
{...register('service')}
|
||||
className="w-full text-sm font-normal min-w-fit"
|
||||
className="w-full min-w-fit text-sm font-normal"
|
||||
placeholder="All Services"
|
||||
aria-label="Select service"
|
||||
hideEmptyHelperText
|
||||
@@ -165,12 +165,12 @@ export default function LogsHeader({
|
||||
},
|
||||
}}
|
||||
title={
|
||||
<div className="p-2 space-y-4">
|
||||
<div className="space-y-4 p-2">
|
||||
<h2>Here are some useful regular expressions:</h2>
|
||||
<ul className="pl-3 space-y-2 list-disc">
|
||||
<ul className="list-disc space-y-2 pl-3">
|
||||
<li>
|
||||
use
|
||||
<code className="px-1 py-px mx-1 rounded-md bg-slate-500 text-slate-100">
|
||||
<code className="mx-1 rounded-md bg-slate-500 px-1 py-px text-slate-100">
|
||||
(?i)error
|
||||
</code>
|
||||
to search for lines with the word <b>error</b> (case
|
||||
@@ -178,7 +178,7 @@ export default function LogsHeader({
|
||||
</li>
|
||||
<li>
|
||||
use
|
||||
<code className="px-1 py-px mx-1 rounded-md bg-slate-500 text-slate-100">
|
||||
<code className="mx-1 rounded-md bg-slate-500 px-1 py-px text-slate-100">
|
||||
error
|
||||
</code>
|
||||
to search for lines with the word <b>error</b> (case
|
||||
@@ -186,7 +186,7 @@ export default function LogsHeader({
|
||||
</li>
|
||||
<li>
|
||||
use
|
||||
<code className="px-1 py-px mx-1 rounded-md bg-slate-500 text-slate-100">
|
||||
<code className="mx-1 rounded-md bg-slate-500 px-1 py-px text-slate-100">
|
||||
/metadata.*error
|
||||
</code>
|
||||
to search for errors in hasura's metadata endpoint
|
||||
@@ -208,10 +208,10 @@ export default function LogsHeader({
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Box className="ml-2 rounded-full cursor-pointer">
|
||||
<Box className="ml-2 cursor-pointer rounded-full">
|
||||
<InfoIcon
|
||||
aria-label="Info"
|
||||
className="w-5 h-5"
|
||||
className="h-5 w-5"
|
||||
color="info"
|
||||
/>
|
||||
</Box>
|
||||
@@ -224,7 +224,7 @@ export default function LogsHeader({
|
||||
className="h-10"
|
||||
startIcon={
|
||||
loading ? (
|
||||
<ActivityIndicator className="w-4 h-4" />
|
||||
<ActivityIndicator className="h-4 w-4" />
|
||||
) : (
|
||||
<SearchIcon />
|
||||
)
|
||||
|
||||
@@ -33,15 +33,24 @@ export default function MetricsSettings() {
|
||||
const isPlatform = useIsPlatform();
|
||||
const { maintenanceActive } = useUI();
|
||||
const localMimirClient = useLocalMimirClient();
|
||||
const { project, refetch: refetchProject } = useProject();
|
||||
const {
|
||||
project,
|
||||
refetch: refetchProject,
|
||||
loading: loadingProject,
|
||||
} = useProject();
|
||||
const [updateConfig] = useUpdateConfigMutation({
|
||||
refetchQueries: [GetObservabilitySettingsDocument],
|
||||
...(!isPlatform ? { client: localMimirClient } : {}),
|
||||
});
|
||||
|
||||
const { data, loading, error } = useGetObservabilitySettingsQuery({
|
||||
const {
|
||||
data,
|
||||
loading: loadingObservabilitySettings,
|
||||
error,
|
||||
} = useGetObservabilitySettingsQuery({
|
||||
variables: { appId: project?.id },
|
||||
...(!isPlatform ? { client: localMimirClient } : {}),
|
||||
skip: !project?.id,
|
||||
});
|
||||
|
||||
const { enabled: alertingEnabled } =
|
||||
@@ -59,14 +68,14 @@ export default function MetricsSettings() {
|
||||
const alerting = watch('enabled');
|
||||
|
||||
useEffect(() => {
|
||||
if (!loading) {
|
||||
if (!loadingObservabilitySettings) {
|
||||
alertingForm.reset({
|
||||
enabled: alertingEnabled,
|
||||
});
|
||||
}
|
||||
}, [loading, alertingEnabled, alertingForm]);
|
||||
}, [loadingObservabilitySettings, alertingEnabled, alertingForm]);
|
||||
|
||||
if (loading) {
|
||||
if (loadingProject || loadingObservabilitySettings) {
|
||||
return (
|
||||
<ActivityIndicator
|
||||
delay={1000}
|
||||
@@ -124,7 +133,7 @@ export default function MetricsSettings() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="grid max-w-5xl grid-flow-row bg-transparent gap-y-6">
|
||||
<div className="grid max-w-5xl grid-flow-row gap-y-6 bg-transparent">
|
||||
<FormProvider {...alertingForm}>
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<SettingsContainer
|
||||
|
||||
@@ -10,28 +10,28 @@ const features: CardProps[] = [
|
||||
description: 'Learn how to use Postgres with Nhost',
|
||||
icon: <DatabaseIcon className="h-8 w-8" sx={{ color: 'text.secondary' }} />,
|
||||
disableIconBackground: true,
|
||||
link: 'https://docs.nhost.io/platform/database',
|
||||
link: 'https://docs.nhost.io/product/database',
|
||||
},
|
||||
{
|
||||
title: 'GraphQL API',
|
||||
description: 'Learn how to interact with the GraphQL API',
|
||||
icon: <GraphQLIcon className="h-8 w-8" sx={{ color: 'text.secondary' }} />,
|
||||
disableIconBackground: true,
|
||||
link: 'https://docs.nhost.io/platform/graphql',
|
||||
link: 'https://docs.nhost.io/product/graphql',
|
||||
},
|
||||
{
|
||||
title: 'Authentication',
|
||||
description: 'Learn how to authenticate users with Nhost',
|
||||
icon: <UserIcon className="h-8 w-8" sx={{ color: 'text.secondary' }} />,
|
||||
disableIconBackground: true,
|
||||
link: 'https://docs.nhost.io/platform/authentication',
|
||||
link: 'https://docs.nhost.io/product/authentication',
|
||||
},
|
||||
{
|
||||
title: 'Storage',
|
||||
description: 'Learn how to use Storage with Nhost',
|
||||
icon: <StorageIcon className="h-8 w-8" sx={{ color: 'text.secondary' }} />,
|
||||
disableIconBackground: true,
|
||||
link: 'https://docs.nhost.io/platform/storage',
|
||||
link: 'https://docs.nhost.io/product/storage',
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ export default function useRunServices() {
|
||||
refetch: refetchPlatformServices,
|
||||
} = useGetRunServicesQuery({
|
||||
variables: {
|
||||
appID: project.id,
|
||||
appID: project?.id,
|
||||
resolve: false,
|
||||
limit: limit.current,
|
||||
offset,
|
||||
@@ -59,7 +59,7 @@ export default function useRunServices() {
|
||||
data: localServicesData,
|
||||
refetch: refetchLocalServices,
|
||||
} = useGetLocalRunServiceConfigsQuery({
|
||||
variables: { appID: project.id as any, resolve: false },
|
||||
variables: { appID: project?.id as any, resolve: false },
|
||||
skip: isPlatform,
|
||||
client: localMimirClient,
|
||||
});
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import type { UseDataGridOptions } from '@/components/dataGrid/DataGrid/useDataGrid';
|
||||
import { DataGridBody } from '@/components/dataGrid/DataGridBody';
|
||||
import { DataGridConfigProvider } from '@/components/dataGrid/DataGridConfigProvider';
|
||||
import { DataGridFrame } from '@/components/dataGrid/DataGridFrame';
|
||||
import type { DataGridHeaderProps } from '@/components/dataGrid/DataGridHeader';
|
||||
import { DataGridHeader } from '@/components/dataGrid/DataGridHeader';
|
||||
import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
|
||||
import { Box } from '@/components/ui/v2/Box';
|
||||
import { DataBrowserEmptyState } from '@/features/database/dataGrid/components/DataBrowserEmptyState';
|
||||
import type { DataBrowserGridColumn } from '@/features/database/dataGrid/types/dataBrowser';
|
||||
import { DataBrowserEmptyState } from '@/features/orgs/projects/database/dataGrid/components/DataBrowserEmptyState';
|
||||
import type { DataBrowserGridColumn } from '@/features/orgs/projects/database/dataGrid/types/dataBrowser';
|
||||
import type { UseDataGridOptions } from '@/features/orgs/projects/storage/dataGrid/components/DataGrid/useDataGrid';
|
||||
import { DataGridBody } from '@/features/orgs/projects/storage/dataGrid/components/DataGridBody';
|
||||
import { DataGridConfigProvider } from '@/features/orgs/projects/storage/dataGrid/components/DataGridConfigProvider';
|
||||
import { DataGridFrame } from '@/features/orgs/projects/storage/dataGrid/components/DataGridFrame';
|
||||
import type { DataGridHeaderProps } from '@/features/orgs/projects/storage/dataGrid/components/DataGridHeader';
|
||||
import { DataGridHeader } from '@/features/orgs/projects/storage/dataGrid/components/DataGridHeader';
|
||||
import type { ForwardedRef } from 'react';
|
||||
import { forwardRef, useEffect, useRef } from 'react';
|
||||
import mergeRefs from 'react-merge-refs';
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import type { DataGridProps } from '@/components/dataGrid/DataGrid';
|
||||
import { DataGridCell } from '@/components/dataGrid/DataGridCell';
|
||||
import { useDataGridConfig } from '@/components/dataGrid/DataGridConfigProvider';
|
||||
import type { BoxProps } from '@/components/ui/v2/Box';
|
||||
import { Box } from '@/components/ui/v2/Box';
|
||||
import { Button } from '@/components/ui/v2/Button';
|
||||
import { PlusIcon } from '@/components/ui/v2/icons/PlusIcon';
|
||||
import type { DataBrowserGridColumn } from '@/features/database/dataGrid/types/dataBrowser';
|
||||
import type { DataBrowserGridColumn } from '@/features/orgs/projects/database/dataGrid/types/dataBrowser';
|
||||
import type { DataGridProps } from '@/features/orgs/projects/storage/dataGrid/components/DataGrid/DataGrid';
|
||||
import { DataGridCell } from '@/features/orgs/projects/storage/dataGrid/components/DataGridCell';
|
||||
import { useDataGridConfig } from '@/features/orgs/projects/storage/dataGrid/components/DataGridConfigProvider';
|
||||
import type { DetailedHTMLProps, HTMLProps, KeyboardEvent } from 'react';
|
||||
import { Fragment, useMemo, useRef } from 'react';
|
||||
import type { Row } from 'react-table';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { CommonDataGridCellProps } from '@/components/dataGrid/DataGridCell';
|
||||
import { useDataGridCell } from '@/components/dataGrid/DataGridCell';
|
||||
import { ReadOnlyToggle } from '@/components/presentational/ReadOnlyToggle';
|
||||
import { Dropdown } from '@/components/ui/v2/Dropdown';
|
||||
import type { KeyboardEvent as ReactKeyboardEvent, MouseEvent } from 'react';
|
||||
import type { CommonDataGridCellProps } from '@/features/orgs/projects/storage/dataGrid/components/DataGridCell';
|
||||
import { useDataGridCell } from '@/features/orgs/projects/storage/dataGrid/components/DataGridCell';
|
||||
import type { MouseEvent, KeyboardEvent as ReactKeyboardEvent } from 'react';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
export type DataGridBooleanCellProps<TData extends object> =
|
||||
|
||||
@@ -6,7 +6,7 @@ import type {
|
||||
ColumnType,
|
||||
DataBrowserGridCell,
|
||||
DataBrowserGridCellProps,
|
||||
} from '@/features/database/dataGrid/types/dataBrowser';
|
||||
} from '@/features/orgs/projects/database/dataGrid/types/dataBrowser';
|
||||
import { triggerToast } from '@/utils/toast';
|
||||
import type {
|
||||
FocusEvent,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { UseDataGridReturn } from '@/components/dataGrid/DataGrid';
|
||||
import type { UseDataGridReturn } from '@/features/orgs/projects/storage/dataGrid/components/DataGrid/useDataGrid';
|
||||
import { createContext } from 'react';
|
||||
|
||||
const DataGridConfigContext = createContext<Partial<UseDataGridReturn>>(null);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { UseDataGridReturn } from '@/components/dataGrid/DataGrid';
|
||||
import type { UseDataGridReturn } from '@/features/orgs/projects/storage/dataGrid/components/DataGrid/useDataGrid';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import DataGridConfigContext from './DataGridConfigContext';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { UseDataGridReturn } from '@/components/dataGrid/DataGrid';
|
||||
import type { UseDataGridReturn } from '@/features/orgs/projects/storage/dataGrid/components/DataGrid';
|
||||
import { useContext } from 'react';
|
||||
import DataGridConfigContext from './DataGridConfigContext';
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { CommonDataGridCellProps } from '@/components/dataGrid/DataGridCell';
|
||||
import { useDataGridCell } from '@/components/dataGrid/DataGridCell';
|
||||
import { Input, inputClasses } from '@/components/ui/v2/Input';
|
||||
import type { TextProps } from '@/components/ui/v2/Text';
|
||||
import { Text } from '@/components/ui/v2/Text';
|
||||
import type { CommonDataGridCellProps } from '@/features/orgs/projects/storage/dataGrid/components/DataGridCell';
|
||||
import { useDataGridCell } from '@/features/orgs/projects/storage/dataGrid/components/DataGridCell';
|
||||
import { getDateComponents } from '@/utils/getDateComponents';
|
||||
import type { ChangeEvent, KeyboardEvent } from 'react';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { CommonDataGridCellProps } from '@/components/dataGrid/DataGridCell';
|
||||
import { useDataGridCell } from '@/components/dataGrid/DataGridCell';
|
||||
import { Input, inputClasses } from '@/components/ui/v2/Input';
|
||||
import { Text } from '@/components/ui/v2/Text';
|
||||
import type { CommonDataGridCellProps } from '@/features/orgs/projects/storage/dataGrid/components/DataGridCell';
|
||||
import { useDataGridCell } from '@/features/orgs/projects/storage/dataGrid/components/DataGridCell';
|
||||
import type { ChangeEvent, KeyboardEvent } from 'react';
|
||||
|
||||
export type DataGridDecimalCellProps<TData extends object> =
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useDataGridConfig } from '@/components/dataGrid/DataGridConfigProvider';
|
||||
import { useDataGridConfig } from '@/features/orgs/projects/storage/dataGrid/components/DataGridConfigProvider';
|
||||
import clsx from 'clsx';
|
||||
import type { DetailedHTMLProps, HTMLProps } from 'react';
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import type { DataGridProps } from '@/components/dataGrid/DataGrid';
|
||||
import { useDataGridConfig } from '@/components/dataGrid/DataGridConfigProvider';
|
||||
import { Box } from '@/components/ui/v2/Box';
|
||||
import { Button } from '@/components/ui/v2/Button';
|
||||
import { Divider } from '@/components/ui/v2/Divider';
|
||||
@@ -9,7 +7,10 @@ import { ArrowUpIcon } from '@/components/ui/v2/icons/ArrowUpIcon';
|
||||
import { PencilIcon } from '@/components/ui/v2/icons/PencilIcon';
|
||||
import { PlusIcon } from '@/components/ui/v2/icons/PlusIcon';
|
||||
import { TrashIcon } from '@/components/ui/v2/icons/TrashIcon';
|
||||
import type { DataBrowserGridColumn } from '@/features/database/dataGrid/types/dataBrowser';
|
||||
import type { DataBrowserGridColumn } from '@/features/orgs/projects/database/dataGrid/types/dataBrowser';
|
||||
import type { DataGridProps } from '@/features/orgs/projects/storage/dataGrid/components/DataGrid';
|
||||
import { useDataGridConfig } from '@/features/orgs/projects/storage/dataGrid/components/DataGridConfigProvider';
|
||||
import { DataGridHeaderButton } from '@/features/orgs/projects/storage/dataGrid/components/DataGridHeaderButton';
|
||||
import type { DetailedHTMLProps, HTMLProps } from 'react';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
@@ -54,7 +55,7 @@ export default function DataGridHeader<T extends object>({
|
||||
componentsProps,
|
||||
...props
|
||||
}: DataGridHeaderProps<T>) {
|
||||
const { flatHeaders, allowSort, allowResize } = useDataGridConfig<T>();
|
||||
const { flatHeaders } = useDataGridConfig<T>();
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -96,53 +97,11 @@ export default function DataGridHeader<T extends object>({
|
||||
}}
|
||||
key={column.id}
|
||||
>
|
||||
{column.id === 'selection' ? (
|
||||
<span
|
||||
{...headerProps}
|
||||
className="relative grid w-full grid-flow-col items-center justify-between p-2"
|
||||
>
|
||||
{column.render('Header')}
|
||||
</span>
|
||||
) : (
|
||||
<Dropdown.Trigger
|
||||
className={twMerge(
|
||||
'focus:outline-none motion-safe:transition-colors',
|
||||
)}
|
||||
disabled={
|
||||
column.isDisabled || (column.disableSortBy && !onRemoveColumn)
|
||||
}
|
||||
hideChevron
|
||||
>
|
||||
<span
|
||||
{...headerProps}
|
||||
className="relative grid w-full grid-flow-col items-center justify-between p-2"
|
||||
>
|
||||
{column.render('Header')}
|
||||
|
||||
{allowSort && (
|
||||
<Box component="span" sx={{ color: 'text.primary' }}>
|
||||
{column.isSorted && !column.isSortedDesc && (
|
||||
<ArrowUpIcon className="h-3 w-3" />
|
||||
)}
|
||||
|
||||
{column.isSorted && column.isSortedDesc && (
|
||||
<ArrowDownIcon className="h-3 w-3" />
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
</span>
|
||||
|
||||
{allowResize && !column.disableResizing && (
|
||||
<span
|
||||
{...column.getResizerProps({
|
||||
onClick: (event: Event) => event.stopPropagation(),
|
||||
})}
|
||||
className="absolute -right-0.5 bottom-0 top-0 z-10 h-full w-1.5 group-hover:bg-slate-900 group-hover:bg-opacity-20 group-active:bg-slate-900 group-active:bg-opacity-20 motion-safe:transition-colors"
|
||||
/>
|
||||
)}
|
||||
</Dropdown.Trigger>
|
||||
)}
|
||||
|
||||
<DataGridHeaderButton
|
||||
column={column}
|
||||
headerProps={headerProps}
|
||||
onRemoveColumn={onRemoveColumn}
|
||||
/>
|
||||
<Dropdown.Content
|
||||
menu
|
||||
PaperProps={{ className: 'w-52 mt-1' }}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
import { Box } from '@/components/ui/v2/Box';
|
||||
import { Dropdown } from '@/components/ui/v2/Dropdown';
|
||||
import { ArrowDownIcon } from '@/components/ui/v2/icons/ArrowDownIcon';
|
||||
import { ArrowUpIcon } from '@/components/ui/v2/icons/ArrowUpIcon';
|
||||
import type { DataBrowserGridColumn } from '@/features/orgs/projects/database/dataGrid/types/dataBrowser';
|
||||
import { useDataGridConfig } from '@/features/orgs/projects/storage/dataGrid/components/DataGridConfigProvider';
|
||||
import type { TableHeaderProps } from 'react-table';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
interface DataGridHeaderButtonProps<T extends object> {
|
||||
column: DataBrowserGridColumn<T>;
|
||||
headerProps: TableHeaderProps;
|
||||
onRemoveColumn: (column: DataBrowserGridColumn<T>) => void;
|
||||
}
|
||||
|
||||
export default function DataGridHeaderButton<T extends object>({
|
||||
column,
|
||||
headerProps,
|
||||
onRemoveColumn,
|
||||
}: DataGridHeaderButtonProps<T>) {
|
||||
const { allowSort, allowResize } = useDataGridConfig();
|
||||
|
||||
if (column.id === 'selection') {
|
||||
return (
|
||||
<span
|
||||
{...headerProps}
|
||||
className="relative grid w-full grid-flow-col items-center justify-between p-2"
|
||||
>
|
||||
{column.render('Header')}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
if (column.id === 'preview') {
|
||||
return (
|
||||
<div className="focus:outline-none motion-safe:transition-colors">
|
||||
{column.render('Header')}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Dropdown.Trigger
|
||||
className={twMerge('focus:outline-none motion-safe:transition-colors')}
|
||||
disabled={column.isDisabled || (column.disableSortBy && !onRemoveColumn)}
|
||||
hideChevron
|
||||
>
|
||||
<span
|
||||
{...headerProps}
|
||||
className="relative grid w-full grid-flow-col items-center justify-between p-2"
|
||||
>
|
||||
{column.render('Header')}
|
||||
|
||||
{allowSort && (
|
||||
<Box component="span" sx={{ color: 'text.primary' }}>
|
||||
{column.isSorted && !column.isSortedDesc && (
|
||||
<ArrowUpIcon className="h-3 w-3" />
|
||||
)}
|
||||
|
||||
{column.isSorted && column.isSortedDesc && (
|
||||
<ArrowDownIcon className="h-3 w-3" />
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
</span>
|
||||
|
||||
{allowResize && !column.disableResizing && (
|
||||
<span
|
||||
{...column.getResizerProps({
|
||||
onClick: (event: Event) => event.stopPropagation(),
|
||||
})}
|
||||
className="absolute -right-0.5 bottom-0 top-0 z-10 h-full w-1.5 group-hover:bg-slate-900 group-hover:bg-opacity-20 group-active:bg-slate-900 group-active:bg-opacity-20 motion-safe:transition-colors"
|
||||
/>
|
||||
)}
|
||||
</Dropdown.Trigger>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default as DataGridHeaderButton } from './DataGridHeaderButton';
|
||||
@@ -1,7 +1,9 @@
|
||||
import type { CommonDataGridCellProps } from '@/components/dataGrid/DataGridCell';
|
||||
import { useDataGridCell } from '@/components/dataGrid/DataGridCell';
|
||||
import { Input, inputClasses } from '@/components/ui/v2/Input';
|
||||
import { Text } from '@/components/ui/v2/Text';
|
||||
import {
|
||||
useDataGridCell,
|
||||
type CommonDataGridCellProps,
|
||||
} from '@/features/orgs/projects/storage/dataGrid/components/DataGridCell';
|
||||
import type { ChangeEvent, KeyboardEvent } from 'react';
|
||||
|
||||
export type DataGridIntegerCellProps<TData extends object> =
|
||||
|
||||
@@ -9,6 +9,8 @@ import { VideoPreviewIcon } from '@/components/ui/v2/icons/VideoPreviewIcon';
|
||||
import { XIcon } from '@/components/ui/v2/icons/XIcon';
|
||||
import { useAppClient } from '@/features/orgs/projects/hooks/useAppClient';
|
||||
import { useProject } from '@/features/orgs/projects/hooks/useProject';
|
||||
import { usePreviewToggle } from '@/features/orgs/projects/storage/dataGrid/hooks/usePreviewToggle';
|
||||
import { useSSRLocalStorage } from '@/hooks/useSSRLocalStorage';
|
||||
import clsx from 'clsx';
|
||||
import type { ReactNode } from 'react';
|
||||
import { useEffect, useReducer, useState } from 'react';
|
||||
@@ -46,11 +48,16 @@ function useBlob({
|
||||
const [objectUrl, setObjectUrl] = useState<string>();
|
||||
const [error, setError] = useState<Error>();
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [preview] = useSSRLocalStorage('preview', true);
|
||||
|
||||
// This side-effect fetches the blob of the file from the server and sets the
|
||||
// relevant `objectUrl` state. Abort controller is reponsible for cancelling
|
||||
// the fetch if the component is unmounted.
|
||||
useEffect(() => {
|
||||
if (!preview) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const abortController = new AbortController();
|
||||
|
||||
async function generateOptimizedObjectUrl() {
|
||||
@@ -104,7 +111,7 @@ function useBlob({
|
||||
generateObjectUrl();
|
||||
|
||||
return () => abortController.abort();
|
||||
}, [blob, fetchBlob, objectUrl, mimeType]);
|
||||
}, [blob, fetchBlob, objectUrl, mimeType, preview]);
|
||||
|
||||
return { objectUrl, error, loading };
|
||||
}
|
||||
@@ -168,8 +175,13 @@ export default function DataGridPreviewCell<TData extends object>({
|
||||
}: DataGridPreviewCellProps<TData>) {
|
||||
const { project } = useProject();
|
||||
const appClient = useAppClient();
|
||||
const { objectUrl, loading, error } = useBlob({ fetchBlob, blob, mimeType });
|
||||
const { objectUrl, loading, error } = useBlob({
|
||||
fetchBlob,
|
||||
blob,
|
||||
mimeType,
|
||||
});
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
const { previewEnabled } = usePreviewToggle();
|
||||
|
||||
const [
|
||||
{ loading: previewLoading, error: previewError, data: previewUrl },
|
||||
@@ -365,7 +377,7 @@ export default function DataGridPreviewCell<TData extends object>({
|
||||
</Modal>
|
||||
|
||||
<div className="flex h-full w-full justify-center">
|
||||
{previewableImages.includes(mimeType) && objectUrl && (
|
||||
{previewEnabled && previewableImages.includes(mimeType) && objectUrl ? (
|
||||
<button
|
||||
type="button"
|
||||
aria-label={alt}
|
||||
@@ -381,9 +393,11 @@ export default function DataGridPreviewCell<TData extends object>({
|
||||
/>
|
||||
</picture>
|
||||
</button>
|
||||
)}
|
||||
) : null}
|
||||
|
||||
{(!previewableImages.includes(mimeType) || !objectUrl) && (
|
||||
{(!previewableImages.includes(mimeType) ||
|
||||
!objectUrl ||
|
||||
!previewEnabled) && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleOpenPreview}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import type { CommonDataGridCellProps } from '@/components/dataGrid/DataGridCell';
|
||||
import { useDataGridCell } from '@/components/dataGrid/DataGridCell';
|
||||
import { Button } from '@/components/ui/v2/Button';
|
||||
import { CopyIcon } from '@/components/ui/v2/icons/CopyIcon';
|
||||
import { Input, inputClasses } from '@/components/ui/v2/Input';
|
||||
import { Text } from '@/components/ui/v2/Text';
|
||||
import {
|
||||
useDataGridCell,
|
||||
type CommonDataGridCellProps,
|
||||
} from '@/features/orgs/projects/storage/dataGrid/components/DataGridCell';
|
||||
import { copy } from '@/utils/copy';
|
||||
import type { ChangeEvent, KeyboardEvent, Ref } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
@@ -10,6 +10,7 @@ import { FilePreviewIcon } from '@/components/ui/v2/icons/FilePreviewIcon';
|
||||
import { useAppClient } from '@/features/orgs/projects/hooks/useAppClient';
|
||||
import { useProject } from '@/features/orgs/projects/hooks/useProject';
|
||||
import { FilesDataGridControls } from '@/features/orgs/projects/storage/dataGrid/components/FilesDataGridControls';
|
||||
import { PreviewHeader } from '@/features/orgs/projects/storage/dataGrid/components/PreviewHeader';
|
||||
import { useBuckets } from '@/features/orgs/projects/storage/dataGrid/hooks/useBuckets';
|
||||
import { useFiles } from '@/features/orgs/projects/storage/dataGrid/hooks/useFiles';
|
||||
import { useFilesAggregate } from '@/features/orgs/projects/storage/dataGrid/hooks/useFilesAggregate';
|
||||
@@ -112,7 +113,8 @@ export default function FilesDataGrid(props: FilesDataGridProps) {
|
||||
const memoizedColumns: Column<StoredFile>[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
Header: 'Preview',
|
||||
id: 'preview',
|
||||
Header: PreviewHeader,
|
||||
accessor: 'preview',
|
||||
Cell: (cellProps) =>
|
||||
DataGridPreviewCell({
|
||||
@@ -121,8 +123,8 @@ export default function FilesDataGrid(props: FilesDataGridProps) {
|
||||
<FilePreviewIcon className="h-5 w-5 fill-current" />
|
||||
),
|
||||
}),
|
||||
minWidth: 80,
|
||||
width: 80,
|
||||
minWidth: 120,
|
||||
width: 120,
|
||||
disableSortBy: true,
|
||||
disableResizing: true,
|
||||
},
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
import { useDialog } from '@/components/common/DialogProvider';
|
||||
import { useDataGridConfig } from '@/components/dataGrid/DataGridConfigProvider';
|
||||
import type { DataGridPaginationProps } from '@/components/dataGrid/DataGridPagination';
|
||||
import { DataGridPagination } from '@/components/dataGrid/DataGridPagination';
|
||||
import type { BoxProps } from '@/components/ui/v2/Box';
|
||||
import { Box } from '@/components/ui/v2/Box';
|
||||
import { Button } from '@/components/ui/v2/Button';
|
||||
@@ -10,6 +7,9 @@ import type { InputProps } from '@/components/ui/v2/Input';
|
||||
import { Input } from '@/components/ui/v2/Input';
|
||||
import { useAppClient } from '@/features/orgs/projects/hooks/useAppClient';
|
||||
import { useProject } from '@/features/orgs/projects/hooks/useProject';
|
||||
import { useDataGridConfig } from '@/features/orgs/projects/storage/dataGrid/components/DataGridConfigProvider';
|
||||
import type { DataGridPaginationProps } from '@/features/orgs/projects/storage/dataGrid/components/DataGridPagination';
|
||||
import { DataGridPagination } from '@/features/orgs/projects/storage/dataGrid/components/DataGridPagination';
|
||||
import type { FileUploadButtonProps } from '@/features/orgs/projects/storage/dataGrid/components/FileUploadButton';
|
||||
import { FileUploadButton } from '@/features/orgs/projects/storage/dataGrid/components/FileUploadButton';
|
||||
import type { Files } from '@/utils/__generated__/graphql';
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Switch } from '@/components/ui/v2/Switch';
|
||||
import { usePreviewToggle } from '@/features/orgs/projects/storage/dataGrid/hooks/usePreviewToggle';
|
||||
import { type ChangeEvent } from 'react';
|
||||
|
||||
export default function PreviewHeader() {
|
||||
const { previewEnabled, setPreviewEnabled } = usePreviewToggle();
|
||||
|
||||
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
setPreviewEnabled(e.target.checked);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-row items-center gap-2 p-2">
|
||||
Preview
|
||||
<Switch
|
||||
className="self-center"
|
||||
checked={previewEnabled}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default as PreviewHeader } from './PreviewHeader';
|
||||
@@ -0,0 +1 @@
|
||||
export { default as usePreviewToggle } from './usePreviewToggle';
|
||||
@@ -0,0 +1,29 @@
|
||||
import { useProject } from '@/features/orgs/projects/hooks/useProject';
|
||||
import { useSSRLocalStorage } from '@/hooks/useSSRLocalStorage';
|
||||
|
||||
// Store {projectId: previewEnabled, projectId2: previewEnabled, ...}
|
||||
type PreviewLocalStorage = {
|
||||
[key: string]: boolean | undefined;
|
||||
};
|
||||
|
||||
export default function usePreviewToggle() {
|
||||
const [preview, setPreview] = useSSRLocalStorage<PreviewLocalStorage>(
|
||||
'preview',
|
||||
{},
|
||||
);
|
||||
const { project } = useProject();
|
||||
|
||||
// Default to previewEnabled true if not set
|
||||
const previewEnabled = preview[project?.id] ?? true;
|
||||
|
||||
const setPreviewEnabled = (value: boolean) => {
|
||||
const newPreview = { ...preview };
|
||||
newPreview[project?.id] = value;
|
||||
setPreview(newPreview);
|
||||
};
|
||||
|
||||
return {
|
||||
previewEnabled,
|
||||
setPreviewEnabled,
|
||||
};
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
export { default as useIsHealthy } from './useIsHealthy';
|
||||
@@ -1,31 +0,0 @@
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
||||
import { useIsPlatform } from '@/features/projects/common/hooks/useIsPlatform';
|
||||
import { generateAppServiceUrl } from '@/features/projects/common/utils/generateAppServiceUrl';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
/**
|
||||
* Returns whether or not the app is healthy.
|
||||
*/
|
||||
export default function useIsHealthy() {
|
||||
const isPlatform = useIsPlatform();
|
||||
const { currentProject } = useCurrentWorkspaceAndProject();
|
||||
|
||||
const appUrl = generateAppServiceUrl(
|
||||
currentProject?.subdomain,
|
||||
currentProject?.region,
|
||||
'auth',
|
||||
);
|
||||
|
||||
const { failureCount, status } = useQuery(
|
||||
['/healthz'],
|
||||
() => fetch(`${appUrl}/healthz`),
|
||||
{
|
||||
enabled: !isPlatform && !!currentProject,
|
||||
retry: true,
|
||||
retryDelay: 5000,
|
||||
cacheTime: 0,
|
||||
},
|
||||
);
|
||||
|
||||
return isPlatform || (status === 'success' && failureCount === 0);
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useIsPlatform } from '@/features/orgs/projects/common/hooks/useIsPlatform';
|
||||
import { useCurrentOrg } from '@/features/orgs/projects/hooks/useCurrentOrg';
|
||||
import { useProject } from '@/features/orgs/projects/hooks/useProject';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
||||
@@ -5,7 +6,9 @@ import { useRouter } from 'next/router';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
/**
|
||||
* Redirects to 404 page if either currentWorkspace/currentProject resolves to undefined.
|
||||
* Redirects to 404 page if either currentWorkspace/currentProject resolves to undefined
|
||||
* or if the current pathname is not a valid organization/project.
|
||||
* Not applicable if running dashboard with local Nhost backend.
|
||||
*/
|
||||
export default function useNotFoundRedirect() {
|
||||
const router = useRouter();
|
||||
@@ -21,6 +24,7 @@ export default function useNotFoundRedirect() {
|
||||
} = router;
|
||||
|
||||
const { project, loading: projectLoading } = useProject();
|
||||
const isPlatform = useIsPlatform();
|
||||
const { org, loading: orgLoading } = useCurrentOrg();
|
||||
|
||||
const { subdomain: projectSubdomain } = project || {};
|
||||
@@ -49,6 +53,8 @@ export default function useNotFoundRedirect() {
|
||||
router.pathname === '/run-one-click-install' ||
|
||||
router.pathname.includes('/orgs/_') ||
|
||||
router.pathname.includes('/orgs/_/projects/_') ||
|
||||
(!isPlatform &&
|
||||
router.pathname.includes('/orgs/[orgSlug]/projects/[appSubdomain]')) ||
|
||||
(urlOrgSlug === currentOrgSlug && !urlAppSubdomain) ||
|
||||
(urlOrgSlug === currentOrgSlug && urlAppSubdomain === projectSubdomain) ||
|
||||
// If we are on a valid workspace and project, we don't want to redirect to 404
|
||||
@@ -75,5 +81,6 @@ export default function useNotFoundRedirect() {
|
||||
projectSubdomain,
|
||||
urlWorkspaceSlug,
|
||||
urlOrgSlug,
|
||||
isPlatform,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import type { ReactElement } from 'react';
|
||||
|
||||
function BackupsContent() {
|
||||
return (
|
||||
<div className="grid w-full grid-flow-row gap-6 mt-6">
|
||||
<div className="mt-6 grid w-full grid-flow-row gap-6">
|
||||
<div>
|
||||
<Text className="font-medium">Database</Text>
|
||||
<Text color="secondary">
|
||||
@@ -27,12 +27,13 @@ function BackupsContent() {
|
||||
|
||||
export default function BackupsPage() {
|
||||
const { currentOrg: org, loading } = useOrgs();
|
||||
const isPlanFree = org.plan.isFree;
|
||||
|
||||
if (loading) {
|
||||
return <ActivityIndicator label="Loading project..." delay={1000} />;
|
||||
}
|
||||
|
||||
const isPlanFree = org.plan.isFree;
|
||||
|
||||
if (isPlanFree) {
|
||||
return (
|
||||
<Container
|
||||
@@ -44,20 +45,19 @@ export default function BackupsPage() {
|
||||
description=""
|
||||
/>
|
||||
</Container>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<Container className="grid max-w-5xl grid-flow-row bg-transparent gap-y-6">
|
||||
<div className="grid justify-between grid-flow-col gap-2">
|
||||
<Container className="grid max-w-5xl grid-flow-row gap-y-6 bg-transparent">
|
||||
<div className="grid grid-flow-col justify-between gap-2">
|
||||
<Text className="text-2xl font-medium" variant="h1">
|
||||
Backups
|
||||
</Text>
|
||||
|
||||
<Chip
|
||||
color={org?.plan.isFree ? 'default' : 'success'}
|
||||
label={org?.plan.isFree ? 'Off' : 'Live'}
|
||||
color={isPlanFree ? 'default' : 'success'}
|
||||
label={isPlanFree ? 'Off' : 'Live'}
|
||||
size="small"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -8,11 +8,11 @@ import {
|
||||
} from '@/features/orgs/projects/logs/components/LogsHeader';
|
||||
import { AvailableLogsService } from '@/features/orgs/projects/logs/utils/constants/services';
|
||||
import { useRemoteApplicationGQLClientWithSubscriptions } from '@/hooks/useRemoteApplicationGQLClientWithSubscriptions';
|
||||
import { MINUTES_TO_DECREASE_FROM_CURRENT_DATE } from '@/utils/constants/common';
|
||||
import {
|
||||
GetLogsSubscriptionDocument,
|
||||
useGetProjectLogsQuery,
|
||||
} from '@/utils/__generated__/graphql';
|
||||
import { MINUTES_TO_DECREASE_FROM_CURRENT_DATE } from '@/utils/constants/common';
|
||||
import { subMinutes } from 'date-fns';
|
||||
import {
|
||||
useCallback,
|
||||
@@ -45,7 +45,7 @@ export default function LogsPage() {
|
||||
|
||||
const { data, error, subscribeToMore, client, loading, refetch } =
|
||||
useGetProjectLogsQuery({
|
||||
variables: { appID: project.id, ...filters },
|
||||
variables: { appID: project?.id, ...filters },
|
||||
client: clientWithSplit,
|
||||
fetchPolicy: 'cache-and-network',
|
||||
notifyOnNetworkStatusChange: true,
|
||||
@@ -56,7 +56,7 @@ export default function LogsPage() {
|
||||
subscribeToMore({
|
||||
document: GetLogsSubscriptionDocument,
|
||||
variables: {
|
||||
appID: project.id,
|
||||
appID: project?.id,
|
||||
service: filters.service,
|
||||
from: filters.from,
|
||||
regexFilter: filters.regexFilter,
|
||||
@@ -98,7 +98,7 @@ export default function LogsPage() {
|
||||
};
|
||||
},
|
||||
}),
|
||||
[subscribeToMore, project.id, filters],
|
||||
[subscribeToMore, project?.id, filters],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -133,7 +133,7 @@ export default function LogsPage() {
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col w-full h-full">
|
||||
<div className="flex h-full w-full flex-col">
|
||||
<RetryableErrorBoundary>
|
||||
<LogsHeader
|
||||
loading={loading}
|
||||
|
||||
@@ -22,18 +22,18 @@ import { useLocalMimirClient } from '@/features/orgs/projects/hooks/useLocalMimi
|
||||
import { useProject } from '@/features/orgs/projects/hooks/useProject';
|
||||
|
||||
export default function SettingsAuthenticationPage() {
|
||||
const { project } = useProject();
|
||||
const { project, loading: loadingProject } = useProject();
|
||||
const isPlatform = useIsPlatform();
|
||||
const localMimirClient = useLocalMimirClient();
|
||||
|
||||
const { data, loading, error } = useGetAuthenticationSettingsQuery({
|
||||
variables: { appId: project?.id },
|
||||
fetchPolicy: 'cache-and-network',
|
||||
skip: !project,
|
||||
skip: !project?.id,
|
||||
...(!isPlatform ? { client: localMimirClient } : {}),
|
||||
});
|
||||
|
||||
if (!data && loading) {
|
||||
if (!data || loadingProject || loading) {
|
||||
return (
|
||||
<ActivityIndicator
|
||||
delay={1000}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { UpgradeToProBanner } from '@/components/common/UpgradeToProBanner';
|
||||
import { Container } from '@/components/layout/Container';
|
||||
import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
|
||||
import { Box } from '@/components/ui/v2/Box';
|
||||
import { ArrowSquareOutIcon } from '@/components/ui/v2/icons/ArrowSquareOutIcon';
|
||||
import { Link } from '@/components/ui/v2/Link';
|
||||
@@ -15,7 +16,11 @@ import { useCurrentOrg } from '@/features/orgs/projects/hooks/useCurrentOrg';
|
||||
import { type ReactElement } from 'react';
|
||||
|
||||
export default function CustomDomains() {
|
||||
const { org } = useCurrentOrg();
|
||||
const { org, loading: loadingOrg } = useCurrentOrg();
|
||||
|
||||
if (loadingOrg) {
|
||||
return <ActivityIndicator delay={1000} label="Loading project..." />;
|
||||
}
|
||||
|
||||
if (org?.plan?.isFree) {
|
||||
return (
|
||||
|
||||
@@ -15,15 +15,15 @@ import type { ReactElement } from 'react';
|
||||
export default function DatabaseSettingsPage() {
|
||||
const isPlatform = useIsPlatform();
|
||||
const localMimirClient = useLocalMimirClient();
|
||||
const { project } = useProject();
|
||||
const { project, loading: loadingProject } = useProject();
|
||||
|
||||
const { loading, error } = useGetPostgresSettingsQuery({
|
||||
variables: { appId: project?.id },
|
||||
skip: !project,
|
||||
skip: !project?.id,
|
||||
...(!isPlatform ? { client: localMimirClient } : {}),
|
||||
});
|
||||
|
||||
if (loading) {
|
||||
if (loadingProject || loading) {
|
||||
return (
|
||||
<ActivityIndicator
|
||||
delay={1000}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useUI } from '@/components/common/UIProvider';
|
||||
import { Form } from '@/components/form/Form';
|
||||
import { Container } from '@/components/layout/Container';
|
||||
import { SettingsContainer } from '@/components/layout/SettingsContainer';
|
||||
import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
|
||||
import { LoadingScreen } from '@/components/presentational/LoadingScreen';
|
||||
import { Alert } from '@/components/ui/v2/Alert';
|
||||
import { Input } from '@/components/ui/v2/Input';
|
||||
import { Link } from '@/components/ui/v2/Link';
|
||||
@@ -29,7 +29,7 @@ import { ApplicationStatus } from '@/types/application';
|
||||
import { slugifyString } from '@/utils/helpers';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useMemo, type ReactElement } from 'react';
|
||||
import { useEffect, useMemo, type ReactElement } from 'react';
|
||||
import { FormProvider, useForm } from 'react-hook-form';
|
||||
import * as Yup from 'yup';
|
||||
|
||||
@@ -96,6 +96,14 @@ export default function SettingsGeneralPage() {
|
||||
|
||||
const { register, formState } = form;
|
||||
|
||||
useEffect(() => {
|
||||
if (!loading) {
|
||||
form.reset({
|
||||
name: project?.name,
|
||||
});
|
||||
}
|
||||
}, [loading, project?.name, form]);
|
||||
|
||||
async function handleProjectNameChange(data: ProjectNameValidationSchema) {
|
||||
const newProjectSlug = slugifyString(data.name);
|
||||
|
||||
@@ -186,7 +194,7 @@ export default function SettingsGeneralPage() {
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return <ActivityIndicator label="Loading project..." />;
|
||||
return <LoadingScreen />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Container } from '@/components/layout/Container';
|
||||
import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
|
||||
import { LoadingScreen } from '@/components/presentational/LoadingScreen';
|
||||
import { ProjectLayout } from '@/features/orgs/layout/ProjectLayout';
|
||||
import { SettingsLayout } from '@/features/orgs/layout/SettingsLayout';
|
||||
import { useIsPlatform } from '@/features/orgs/projects/common/hooks/useIsPlatform';
|
||||
@@ -12,21 +12,17 @@ import type { ReactElement } from 'react';
|
||||
export default function MetricsSettingsPage() {
|
||||
const isPlatform = useIsPlatform();
|
||||
const localMimirClient = useLocalMimirClient();
|
||||
const { project } = useProject();
|
||||
const { project, loading: loadingProject } = useProject();
|
||||
|
||||
const { loading, error } = useGetObservabilitySettingsQuery({
|
||||
variables: { appId: project?.id },
|
||||
...(!isPlatform ? { client: localMimirClient } : {}),
|
||||
});
|
||||
const { loading: loadingObservabilitySettings, error } =
|
||||
useGetObservabilitySettingsQuery({
|
||||
variables: { appId: project?.id },
|
||||
...(!isPlatform ? { client: localMimirClient } : {}),
|
||||
skip: !project?.id,
|
||||
});
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<ActivityIndicator
|
||||
delay={1000}
|
||||
label="Loading Observability settings..."
|
||||
className="justify-center"
|
||||
/>
|
||||
);
|
||||
if (loadingProject || loadingObservabilitySettings) {
|
||||
return <LoadingScreen />;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
@@ -35,7 +31,7 @@ export default function MetricsSettingsPage() {
|
||||
|
||||
return (
|
||||
<Container
|
||||
className="grid max-w-5xl grid-flow-row bg-transparent gap-y-6"
|
||||
className="grid max-w-5xl grid-flow-row gap-y-6 bg-transparent"
|
||||
rootClassName="bg-transparent"
|
||||
>
|
||||
<MetricsSettings />
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# @nhost/docs
|
||||
|
||||
## 2.27.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 81cc9b3: chore: add missing images to permissions API
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- af34015: chore: add note about encryption at rest
|
||||
- 1956ed2: chore: added pgmq extension to postgres docs
|
||||
- 88919a3: chore: added support for nodejs22 to functions
|
||||
|
||||
## 2.26.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -113,22 +113,22 @@ GraphQL requests from unauthenticated users resolve permissions using the `publi
|
||||
|
||||
## Insert Permissions
|
||||
|
||||

|
||||

|
||||
|
||||
Here is a popular approach for insert permission for authenticated users.
|
||||
|
||||
1. At the top of the page, click **"insert"** on the **"user"** role.
|
||||
1. Select **"Without any checks"**.
|
||||
1. Select the columns you want to allow users to insert. In our example, we only mark `title`, because that's the only column that should be inserted by the user. The `id` is automatically generated by the database and `user_id` is set using a column preset.
|
||||
1. Select the columns you want to allow users to insert. In our example, we do not mark `id` nor `user_id`, because they should not be inserted by the user. The `id` is automatically generated by the database and `user_id` is set using a column preset.
|
||||
1. Under **Column presets**, set `user_id` to `x-hasura-user-id`. This way, every new record's `user_id` value is set to the ID of the user making the request.
|
||||
|
||||
Now, authenticated users are allowed to insert posts. Users are allowed to add a title when inserting a post. The post's `id` is automatically generated by the database and the `user_id` is automatically set to the user's id using the `user_id = x-hasura-user-id` column preset.
|
||||
Now, authenticated users are allowed to insert todos. Users are allowed to add a title when inserting a todo. The todo's `id` is automatically generated by the database and the `user_id` is automatically set to the user's id using the `user_id = x-hasura-user-id` column preset.
|
||||
|
||||
## Select, Update and Delete Permissions
|
||||
|
||||
Select, update, and delete permissions usually follow the same pattern. Here's an example of how to add select permissions:
|
||||
|
||||

|
||||

|
||||
|
||||
One of the most common permission requirements is that authenticated users should only be able to read their own data. This is how to do that:
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@ In the table below you can find a list of available extensions with Nhost Postgr
|
||||
| pg_trgm | 1.6 | text similarity measurement and index searching based on trigrams |
|
||||
| pg_visibility | 1.2 | examine the visibility map (VM) and page-level visibility info |
|
||||
| pgcrypto | 1.3 | cryptographic functions |
|
||||
| pgmq | 1.4.5 | A lightweight message queue. Like AWS SQS and RSMQ but on Postgres. |
|
||||
| pgrowlocks | 1.2 | show row-level locking information |
|
||||
| pgstattuple | 1.5 | show tuple-level statistics |
|
||||
| plpgsql | 1.0 | PL/pgSQL procedural language |
|
||||
@@ -147,6 +148,30 @@ DROP EXTENSION ip4r;
|
||||
|
||||
- [GitHub](https://github.com/RhodiumToad/ip4r)
|
||||
|
||||
## pgmq
|
||||
|
||||
A lightweight message queue. Like AWS SQS and RSMQ but on Postgres.
|
||||
|
||||
### Managing
|
||||
|
||||
To install the extension you can create a migration with the following contents:
|
||||
|
||||
```sql SQL
|
||||
SET ROLE postgres;
|
||||
CREATE EXTENSION pgmq;
|
||||
```
|
||||
|
||||
To uninstall it, you can use the following migration:
|
||||
|
||||
```sql SQL
|
||||
SET ROLE postgres;
|
||||
DROP EXTENSION pgmq;
|
||||
```
|
||||
|
||||
### Resources
|
||||
|
||||
- [GitHub](https://github.com/tembo-io/pgmq)
|
||||
|
||||
## postgis
|
||||
|
||||
PostGIS extends the capabilities of the PostgreSQL relational database by adding support storing, indexing and querying geographic data.
|
||||
|
||||
@@ -10,6 +10,7 @@ The following runtimes are supported:
|
||||
|
||||
- [Node.js 18](https://nodejs.org)
|
||||
- [Node.js 20](https://nodejs.org)
|
||||
- [Node.js 22](https://nodejs.org)
|
||||
|
||||
To select your preferred runtime ensure the following configuration is present in your `nhost.toml` file:
|
||||
|
||||
@@ -27,6 +28,14 @@ version = 18
|
||||
```toml
|
||||
[functions.node]
|
||||
version = 20
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="Node.js 22">
|
||||
|
||||
```toml
|
||||
[functions.node]
|
||||
version = 22
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/docs",
|
||||
"version": "2.26.0",
|
||||
"version": "2.27.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "mintlify dev"
|
||||
|
||||
@@ -97,3 +97,11 @@ This borrowing of resources is convenient in case of short and unexpected bursts
|
||||
## Disk Performance
|
||||
|
||||
By default disks are provisioned with a capacity for 3000 IOPS and 125 Mbps of throughput. If you need higher performance don't hesitate to contact us.
|
||||
|
||||
## Encryption at Rest
|
||||
|
||||
All files uploaded to the [storage](/product/storage) service are encrypted at rest using AES-256 encryption. Similarly, any volumes provisioned for your [database](/product/database) and [Run services](/product/run) are also encrypted using AES-256.
|
||||
|
||||
<Warning>
|
||||
Only volumes provisioned after December 2024 are encrypted with AES-256. You can verify your volume's encryption status by navigating to your project's settings -> Database -> Storage capacity. If your volume is not encrypted, you'll see a warning message indicating this. To enable encryption on your volume, you can pause and then unpause your project. This action will provision new volumes with encryption enabled.
|
||||
</Warning>
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @nhost-examples/cli
|
||||
|
||||
## 0.3.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/nhost-js@3.2.4
|
||||
|
||||
## 0.3.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost-examples/cli",
|
||||
"version": "0.3.16",
|
||||
"version": "0.3.17",
|
||||
"main": "src/index.mjs",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @nhost-examples/codegen-react-apollo
|
||||
|
||||
## 0.4.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/react@3.9.1
|
||||
- @nhost/react-apollo@16.0.1
|
||||
|
||||
## 0.4.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost-examples/codegen-react-apollo",
|
||||
"version": "0.4.17",
|
||||
"version": "0.4.18",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"codegen": "graphql-codegen",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @nhost-examples/codegen-react-query
|
||||
|
||||
## 0.4.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/react@3.9.1
|
||||
|
||||
## 0.4.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost-examples/codegen-react-query",
|
||||
"version": "0.4.17",
|
||||
"version": "0.4.18",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"codegen": "graphql-codegen",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @nhost-examples/react-urql
|
||||
|
||||
## 0.3.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/react@3.9.1
|
||||
- @nhost/react-urql@13.0.1
|
||||
|
||||
## 0.3.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@nhost-examples/codegen-react-urql",
|
||||
"private": true,
|
||||
"version": "0.3.17",
|
||||
"version": "0.3.18",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @nhost-examples/multi-tenant-one-to-many
|
||||
|
||||
## 2.2.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/nhost-js@3.2.4
|
||||
|
||||
## 2.2.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@nhost-examples/multi-tenant-one-to-many",
|
||||
"private": true,
|
||||
"version": "2.2.17",
|
||||
"version": "2.2.18",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {},
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# @nhost-examples/nextjs
|
||||
|
||||
## 0.4.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/react@3.9.1
|
||||
- @nhost/react-apollo@16.0.1
|
||||
- @nhost/nextjs@2.2.2
|
||||
|
||||
## 0.4.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost-examples/nextjs",
|
||||
"version": "0.4.1",
|
||||
"version": "0.4.2",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @nhost-examples/node-storage
|
||||
|
||||
## 0.2.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/nhost-js@3.2.4
|
||||
|
||||
## 0.2.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost-examples/node-storage",
|
||||
"version": "0.2.16",
|
||||
"version": "0.2.17",
|
||||
"private": true,
|
||||
"description": "This is an example of how to use the Storage with Node.js",
|
||||
"main": "src/index.mjs",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @nhost-examples/nextjs-server-components
|
||||
|
||||
## 0.5.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/nhost-js@3.2.4
|
||||
|
||||
## 0.5.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost-examples/nextjs-server-components",
|
||||
"version": "0.5.1",
|
||||
"version": "0.5.2",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @nhost-examples/react-apollo
|
||||
|
||||
## 1.2.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/react@3.9.1
|
||||
- @nhost/react-apollo@16.0.1
|
||||
|
||||
## 1.2.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost-examples/react-apollo",
|
||||
"version": "1.2.0",
|
||||
"version": "1.2.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @nhost-examples/react-gqty
|
||||
|
||||
## 1.2.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/react@3.9.1
|
||||
|
||||
## 1.2.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@nhost-examples/react-gqty",
|
||||
"private": true,
|
||||
"version": "1.2.17",
|
||||
"version": "1.2.18",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @nhost-examples/react-native
|
||||
|
||||
## 0.1.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/react@3.9.1
|
||||
- @nhost/react-apollo@16.0.1
|
||||
|
||||
## 0.1.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost-examples/react-native",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.3",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"android": "react-native run-android",
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# @nhost-examples/vue-apollo
|
||||
|
||||
## 0.8.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/nhost-js@3.2.4
|
||||
- @nhost/apollo@8.0.4
|
||||
- @nhost/vue@2.9.1
|
||||
|
||||
## 0.8.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@nhost-examples/vue-apollo",
|
||||
"private": true,
|
||||
"version": "0.8.0",
|
||||
"version": "0.8.1",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @nhost-examples/vue-quickstart
|
||||
|
||||
## 0.2.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/apollo@8.0.4
|
||||
- @nhost/vue@2.9.1
|
||||
|
||||
## 0.2.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost-examples/vue-quickstart",
|
||||
"version": "0.2.17",
|
||||
"version": "0.2.18",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "vite build",
|
||||
|
||||
24
flake.lock
generated
24
flake.lock
generated
@@ -5,11 +5,11 @@
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1710146030,
|
||||
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -20,11 +20,11 @@
|
||||
},
|
||||
"nix-filter": {
|
||||
"locked": {
|
||||
"lastModified": 1710156097,
|
||||
"narHash": "sha256-1Wvk8UP7PXdf8bCCaEoMnOT1qe5/Duqgj+rL8sRQsSM=",
|
||||
"lastModified": 1731533336,
|
||||
"narHash": "sha256-oRam5PS1vcrr5UPgALW0eo1m/5/pls27Z/pabHNy2Ms=",
|
||||
"owner": "numtide",
|
||||
"repo": "nix-filter",
|
||||
"rev": "3342559a24e85fc164b295c3444e8a139924675b",
|
||||
"rev": "f7653272fd234696ae94229839a99b73c9ab7de0",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -40,11 +40,11 @@
|
||||
"nixpkgs": "nixpkgs"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1718635840,
|
||||
"narHash": "sha256-yAc2I1Y05hzAMI8atcxg3g7eXehsJIifTl/BOWIvD3o=",
|
||||
"lastModified": 1736258395,
|
||||
"narHash": "sha256-G55pFLtWxy8pzR5k/UOXBSgHoU8oO263YyYkxbaWzXM=",
|
||||
"owner": "nhost",
|
||||
"repo": "nixops",
|
||||
"rev": "9ea5d933111bcfb1e2ff881e98dd2c48d1b80965",
|
||||
"rev": "52d9d8ff772a58b2cedac9f2f8a89517a86b35a4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -55,11 +55,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1716715802,
|
||||
"narHash": "sha256-usk0vE7VlxPX8jOavrtpOqphdfqEQpf9lgedlY/r66c=",
|
||||
"lastModified": 1732238832,
|
||||
"narHash": "sha256-sQxuJm8rHY20xq6Ah+GwIUkF95tWjGRd1X8xF+Pkk38=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "e2dd4e18cc1c7314e24154331bae07df76eb582f",
|
||||
"rev": "8edf06bea5bcbee082df1b7369ff973b91618b8d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
pname = "node_modules";
|
||||
|
||||
nativeBuildInputs = with pkgs; [
|
||||
nodePackages.pnpm
|
||||
pnpm_9
|
||||
cacert
|
||||
nodejs
|
||||
];
|
||||
@@ -131,7 +131,7 @@
|
||||
buildInputs = with pkgs; [
|
||||
nhost-cli
|
||||
nodejs
|
||||
nodePackages.pnpm
|
||||
pnpm_9
|
||||
go
|
||||
golangci-lint
|
||||
] ++ buildInputs ++ nativeBuildInputs;
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @nhost/apollo
|
||||
|
||||
## 8.0.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/nhost-js@3.2.4
|
||||
|
||||
## 8.0.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/apollo",
|
||||
"version": "8.0.3",
|
||||
"version": "8.0.4",
|
||||
"description": "Nhost Apollo Client library",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @nhost/react-apollo
|
||||
|
||||
## 16.0.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/apollo@8.0.4
|
||||
- @nhost/react@3.9.1
|
||||
|
||||
## 16.0.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/react-apollo",
|
||||
"version": "16.0.0",
|
||||
"version": "16.0.1",
|
||||
"description": "Nhost React Apollo client",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @nhost/react-urql
|
||||
|
||||
## 13.0.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/react@3.9.1
|
||||
|
||||
## 13.0.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/react-urql",
|
||||
"version": "13.0.0",
|
||||
"version": "13.0.1",
|
||||
"description": "Nhost React URQL client",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
(final: prev: rec {
|
||||
nodejs = final.nodejs-18_x;
|
||||
nodejs = final.nodejs_20;
|
||||
nodePackages = nodejs.pkgs;
|
||||
nhost-cli = final.callPackage ./nhost-cli.nix { inherit final; };
|
||||
})
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @nhost/hasura-storage-js
|
||||
|
||||
## 2.7.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 5c6ff6e: fix: correct StorageErrorPayload TypeScript typing
|
||||
|
||||
## 2.6.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/hasura-storage-js",
|
||||
"version": "2.6.0",
|
||||
"version": "2.7.0",
|
||||
"description": "Hasura-storage client",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
|
||||
@@ -92,13 +92,14 @@ export const fetchUpload = async (
|
||||
|
||||
xhr.onload = () => {
|
||||
if (xhr.status < 200 || xhr.status >= 300) {
|
||||
const error: StorageErrorPayload = {
|
||||
error: xhr.response?.error?.message ?? xhr.response?.error ?? xhr.response,
|
||||
message: xhr.response?.error?.message ?? xhr.response,
|
||||
status: xhr.status
|
||||
}
|
||||
return resolve({
|
||||
fileMetadata: null,
|
||||
error: {
|
||||
error: xhr.response?.error ?? xhr.response,
|
||||
message: xhr.response?.error?.message ?? xhr.response,
|
||||
status: xhr.status
|
||||
}
|
||||
error
|
||||
})
|
||||
}
|
||||
return resolve({ fileMetadata: xhr.response, error: null })
|
||||
@@ -106,9 +107,14 @@ export const fetchUpload = async (
|
||||
|
||||
xhr.onerror = () => {
|
||||
// only triggers if the request couldn't be made at all e.g. network error
|
||||
const error: StorageErrorPayload = {
|
||||
error: xhr.statusText,
|
||||
message: xhr.statusText,
|
||||
status: xhr.status
|
||||
}
|
||||
return resolve({
|
||||
fileMetadata: null,
|
||||
error: { error: xhr.statusText, message: xhr.statusText, status: xhr.status }
|
||||
error
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @nhost/nextjs
|
||||
|
||||
## 2.2.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/react@3.9.1
|
||||
|
||||
## 2.2.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/nextjs",
|
||||
"version": "2.2.1",
|
||||
"version": "2.2.2",
|
||||
"description": "Nhost NextJS library",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @nhost/nhost-js
|
||||
|
||||
## 3.2.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5c6ff6e]
|
||||
- @nhost/hasura-storage-js@2.7.0
|
||||
|
||||
## 3.2.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/nhost-js",
|
||||
"version": "3.2.3",
|
||||
"version": "3.2.4",
|
||||
"description": "Nhost JavaScript SDK",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user