diff --git a/api/src/columns/columns.service.ts b/api/src/columns/columns.service.ts index a279c834..8c32d8a5 100644 --- a/api/src/columns/columns.service.ts +++ b/api/src/columns/columns.service.ts @@ -163,10 +163,31 @@ export class ColumnsService { if (!datetimeSummary?.datetimeSummary) { throw new NotFoundException('Datetime summary NOT FOUND!'); } + + let datetimeDataArray: { value: Date | null }[]; + // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access + if (colSeries.dtype.toString() === pl.Datetime('us').toString()) { + datetimeDataArray = dataArray.map((entry) => { + return { + value: entry.value === undefined || entry.value === null ? new Date(Math.floor(entry.value / 1000)) : null + }; + }); + // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access + } else if (colSeries.dtype.toString() === pl.Datetime('ns').toString()) { + datetimeDataArray = dataArray.map((entry) => { + return { + value: + entry.value === undefined || entry.value === null ? new Date(Math.floor(entry.value / 1000000)) : null + }; + }); + } else { + datetimeDataArray = dataArray; + } + await this.columnModel.create({ data: { dataPermission: 'MANAGER', - datetimeData: dataArray, + datetimeData: datetimeDataArray, kind: 'DATETIME', name: colSeries.name, nullable: colSeries.nullCount() !== 0, @@ -703,7 +724,7 @@ export class ColumnsService { data: { datetimeColumnValidation: { max: new Date(), - min: '1970-01-01' + min: new Date() }, datetimeData: data.toArray().map((entry: Date) => { return { value: entry }; @@ -869,10 +890,13 @@ export class ColumnsService { case 'DATETIME': return { count: currSeries.len() - currSeries.nullCount(), - datetimeSummary: { - max: new Date(currSeries.max() * 24 * 3600 * 1000), - min: new Date(currSeries.min() * 24 * 3600 * 1000) - }, + datetimeSummary: + currSeries.len === currSeries.nullCount + ? null + : { + max: new Date(Math.floor(currSeries.cast(pl.Datetime('ns'), true).max() / 1000000)), + min: new Date(Math.floor(currSeries.cast(pl.Datetime('ns'), true).min() / 1000000)) + }, nullCount: currSeries.nullCount() }; case 'ENUM': diff --git a/api/src/datasets/datasets.service.ts b/api/src/datasets/datasets.service.ts index 1a8ba598..691a4301 100644 --- a/api/src/datasets/datasets.service.ts +++ b/api/src/datasets/datasets.service.ts @@ -181,6 +181,11 @@ export class DatasetsService { // Add a job to the file-upload queue let dataset; + const primaryKeysArray = createTabularDatasetDto.primaryKeys + ? Array.isArray(createTabularDatasetDto.primaryKeys) + ? createTabularDatasetDto.primaryKeys + : [createTabularDatasetDto.primaryKeys] + : []; if (typeof file !== 'string') { // Resolve once from configuration or env await fs.promises.mkdir(this.uploadsDir, { recursive: true }); @@ -210,7 +215,7 @@ export class DatasetsService { typeof createTabularDatasetDto.isJSON === 'string' ? createTabularDatasetDto.isJSON.toLowerCase() === 'true' : Boolean(createTabularDatasetDto.isJSON), - primaryKeys: createTabularDatasetDto.primaryKeys ?? undefined + primaryKeys: primaryKeysArray }); } else { dataset = await this.datasetModel.create({ @@ -234,7 +239,7 @@ export class DatasetsService { typeof createTabularDatasetDto.isJSON === 'string' ? createTabularDatasetDto.isJSON.toLowerCase() === 'true' : Boolean(createTabularDatasetDto.isJSON), - primaryKeys: createTabularDatasetDto.primaryKeys, + primaryKeys: primaryKeysArray, uploadedString: file }); } @@ -515,7 +520,8 @@ export class DatasetsService { where: { managerIds: { has: currentUserId - } + }, + status: 'Success' } }); } diff --git a/api/src/projects/projects.controller.ts b/api/src/projects/projects.controller.ts index 43fda310..35635a4b 100644 --- a/api/src/projects/projects.controller.ts +++ b/api/src/projects/projects.controller.ts @@ -24,12 +24,12 @@ export class ProjectsController { } @ApiOperation({ summary: 'Add a User to a Project' }) - @Post('add-user/:id') + @Post('add-user/:projectId/:userEmailToAdd') @RouteAccess({ role: 'STANDARD' }) addUserToProject( @CurrentUser('id') currentUserId: string, - @Param('id') projectId: string, - @Body('newUserEmail') newUserEmail: string + @Param('projectId') projectId: string, + @Param('userEmailToAdd') newUserEmail: string ) { return this.projectsService.addUser(currentUserId, projectId, newUserEmail); } diff --git a/api/src/setup/setup.service.ts b/api/src/setup/setup.service.ts index 042a729b..b8d3bb8b 100644 --- a/api/src/setup/setup.service.ts +++ b/api/src/setup/setup.service.ts @@ -73,7 +73,7 @@ export class SetupService { await this.datasetsService.createDataset( { ...createDemoDatasetData, - primaryKeys: [...createDemoDatasetData.primaryKeys] + primaryKeys: createDemoDatasetData.primaryKeys }, await fs.readFile(path.resolve(import.meta.dirname, 'resources', 'demo-dataset.csv'), 'utf-8'), demoUser.id diff --git a/api/src/tabular-data/tabular-data.service.ts b/api/src/tabular-data/tabular-data.service.ts index 66fc5c3b..547025de 100644 --- a/api/src/tabular-data/tabular-data.service.ts +++ b/api/src/tabular-data/tabular-data.service.ts @@ -341,7 +341,7 @@ export class TabularDataService { if (columnIdsModifyData.has(col._id.$oid)) { rows[i][col.name] = 'Hidden'; } else { - rows[i][col.name] = entry.value.$date ? new Date(entry.value.$date).toDateString() : null; + rows[i][col.name] = entry.value ? new Date(entry.value.$date!).toDateString() : null; } }); diff --git a/core/src/datasets.ts b/core/src/datasets.ts index bceda946..0b8c5d8a 100644 --- a/core/src/datasets.ts +++ b/core/src/datasets.ts @@ -23,7 +23,7 @@ const $CreateDataset = z.object({ license: $DatasetLicenses, name: z.string(), permission: $PermissionLevel, - primaryKeys: z.string().array() + primaryKeys: z.union([z.string(), z.string().array()]).optional() }); type $CreateDataset = z.infer; diff --git a/web/src/components/Layout/DesktopSidebar.tsx b/web/src/components/Layout/DesktopSidebar.tsx index c2d01095..8b626611 100644 --- a/web/src/components/Layout/DesktopSidebar.tsx +++ b/web/src/components/Layout/DesktopSidebar.tsx @@ -1,5 +1,5 @@ import { LanguageToggle, ThemeToggle, Tooltip } from '@douglasneuroinformatics/libui/components'; -import { Link, useNavigate } from '@tanstack/react-router'; +import { useNavigate } from '@tanstack/react-router'; import { Logo } from '@/components'; @@ -17,9 +17,9 @@ export const DesktopSidebar = ({ isLogIn, navigation }: DesktopSidebarProps) => return (
- +
- +

- + {tabularDataset.isManager && ( <> diff --git a/web/src/features/dataset/components/ManagerCard.tsx b/web/src/features/dataset/components/ManagerCard.tsx index 9e23d730..32fb40ad 100644 --- a/web/src/features/dataset/components/ManagerCard.tsx +++ b/web/src/features/dataset/components/ManagerCard.tsx @@ -40,7 +40,13 @@ const ManagerCard = ({ datasetId, isManager, managerId }: ManagerCardProps) => { }); void navigate({ to: `/portal/datasets` }); }) - .catch(console.error); + .catch((error) => { + console.error(error); + addNotification({ + message: t('removeDatasetManagerFailure'), + type: 'error' + }); + }); }; return ( diff --git a/web/src/features/dataset/pages/CreateDatasetPage.tsx b/web/src/features/dataset/pages/CreateDatasetPage.tsx index 9ff4aa6b..5d2f731f 100644 --- a/web/src/features/dataset/pages/CreateDatasetPage.tsx +++ b/web/src/features/dataset/pages/CreateDatasetPage.tsx @@ -14,20 +14,45 @@ import { z } from 'zod/v4'; import { LoadingFallback } from '@/components'; import { useDebounceLicensesFilter } from '@/hooks/useDebounceLicensesFilter'; -const $CreateDatasetFormValidation = z.object({ - description: z.string().optional(), - datasetType: z.enum(['BASE', 'TABULAR']), - license: $DatasetLicenses, - name: z.string().min(1), - hasPrimaryKeys: z.boolean(), - primaryKeys: z.array( - z.object({ - key: z.string() - }) - ), - isOpenSource: z.boolean().optional(), - searchLicenseString: z.string().optional() -}); +const $CreateDatasetFormValidation = z + .object({ + description: z.string().optional(), + datasetType: z.enum(['BASE', 'TABULAR']), + license: $DatasetLicenses, + name: z.string().min(1), + hasPrimaryKeys: z.boolean().optional(), + primaryKeys: z + .array( + z.object({ + key: z.string() + }) + ) + .optional(), + isOpenSource: z.boolean().optional(), + searchLicenseString: z.string().optional() + }) + .check((ctx) => { + if (ctx.value.datasetType === 'TABULAR' && ctx.value.hasPrimaryKeys === undefined) { + ctx.issues.push({ + code: 'custom', + input: ctx.value.hasPrimaryKeys, + message: 'hasPrimaryKeys cannot be undefined for tabular dataset.' + }); + } + + if ( + ctx.value.datasetType === 'TABULAR' && + ctx.value.hasPrimaryKeys === true && + ctx.value.primaryKeys === undefined + ) { + ctx.issues.push({ + code: 'custom', + input: ctx.value.primaryKeys, + message: 'Must enter at least 1 primary key because hasPrimaryKeys is set to true.' + }); + } + }); +// .refine((val)=> val.datasetType === "TABULAR" && val.hasPrimaryKeys === false && val.primaryKeys === undefined, {error: "Must enter at least 1 primary key because hasPrimaryKeys is set to true."}); type CreateDatasetFormData = z.infer; @@ -55,15 +80,14 @@ const CreateDatasetPage = () => { requestFormData.append('license', String(formData?.license)); requestFormData.append('name', formData.name); requestFormData.append('description', formData.description ?? ''); - formData.primaryKeys.forEach((entry) => requestFormData.append('primaryKeys', entry.key)); + formData.primaryKeys?.forEach((entry) => requestFormData.append('primaryKeys', entry.key)); requestFormData.append('isJSON', 'false'); requestFormData.append('isReadyToShare', 'false'); requestFormData.append('permission', 'MANAGER'); - if (!file) { - return; + if (file) { + requestFormData.append('file', file); } - requestFormData.append('file', file); await axios.post('/v1/datasets/create', requestFormData, { headers: { @@ -141,7 +165,11 @@ const CreateDatasetPage = () => { kind: 'boolean', label: 'Do you want to add primary keys to your dataset?', // description: "A set of primary keys can uniquely identify an entry of your dataset. If you skip this step, an automatically generated id column will be added to the beginning of your tabular dataset.", - variant: 'radio' + variant: 'radio', + options: { + false: 'No', + true: 'Yes' + } } : null; } @@ -174,7 +202,11 @@ const CreateDatasetPage = () => { isOpenSource: { kind: 'boolean', label: 'Is License Open Source', - variant: 'radio' + variant: 'radio', + options: { + false: 'No', + true: 'Yes' + } }, searchLicenseString: { kind: 'string', diff --git a/web/src/features/dataset/pages/EditDatasetInfoPage.tsx b/web/src/features/dataset/pages/EditDatasetInfoPage.tsx index 25b44c01..83ce4c7e 100644 --- a/web/src/features/dataset/pages/EditDatasetInfoPage.tsx +++ b/web/src/features/dataset/pages/EditDatasetInfoPage.tsx @@ -47,7 +47,13 @@ const EditDatasetInfoPage = () => { addNotification({ message: 'Dataset Information Updated!', type: 'success' }); void navigate({ to: `/portal/datasets/${params.datasetId}` }); }) - .catch(console.error); + .catch((error) => { + console.error(error); + addNotification({ + message: t('editDatasetInfoFailure'), + type: 'error' + }); + }); }, [params.datasetId] ); diff --git a/web/src/features/dataset/pages/ManageDatasetManagersPage.tsx b/web/src/features/dataset/pages/ManageDatasetManagersPage.tsx index 380cda16..c8d47843 100644 --- a/web/src/features/dataset/pages/ManageDatasetManagersPage.tsx +++ b/web/src/features/dataset/pages/ManageDatasetManagersPage.tsx @@ -25,7 +25,13 @@ const ManageDatasetManagersPage = () => { }); void navigate({ to: `/portal/datasets/${datasetId}` }); }) - .catch(console.error); + .catch((error) => { + console.error(error); + addNotification({ + message: t('addDatasetManagerFailure'), + type: 'error' + }); + }); }; return ( diff --git a/web/src/features/dataset/pages/ViewOneDatasetPage.tsx b/web/src/features/dataset/pages/ViewOneDatasetPage.tsx index b1d896be..c25d812f 100644 --- a/web/src/features/dataset/pages/ViewOneDatasetPage.tsx +++ b/web/src/features/dataset/pages/ViewOneDatasetPage.tsx @@ -24,6 +24,7 @@ type ViewOneDatasetPageProps = { dataset: $TabularDataset; downloadDataUrl: string; downloadMetaDataUrl: string; + queryKey: string; rowPagination: $DatasetViewPagination; }; @@ -32,7 +33,8 @@ const ViewOneDatasetPage = ({ downloadDataUrl, downloadMetaDataUrl, columnPagination, - rowPagination + rowPagination, + queryKey }: ViewOneDatasetPageProps) => { const { t } = useTranslation('common'); const navigate = useNavigate(); @@ -65,7 +67,13 @@ const ViewOneDatasetPage = ({ .then((response) => { void download(filename, response.data); }) - .catch(console.error); + .catch((error) => { + console.error(error); + addNotification({ + message: t('downloadDatasetDataFailure'), + type: 'error' + }); + }); }; const handleMetaDataDownload = (format: 'CSV' | 'TSV', data: $TabularDataset) => { @@ -75,7 +83,13 @@ const ViewOneDatasetPage = ({ .then((response) => { void download(filename, response.data); }) - .catch(console.error); + .catch((error) => { + console.error(error); + addNotification({ + message: t('downloadDatasetMetadataFailure'), + type: 'error' + }); + }); }; const handleSetReadyToShare = useDestructiveAction((datasetId: string) => { @@ -88,7 +102,13 @@ const ViewOneDatasetPage = ({ }); void navigate({ to: '/portal/datasets' }); }) - .catch(console.error); + .catch((error) => { + console.error(error); + addNotification({ + message: t('setDatasetSharableFailure'), + type: 'error' + }); + }); }); // if (!dataset) { @@ -187,7 +207,7 @@ const ViewOneDatasetPage = ({ totalNumberOfItems={dataset.totalNumberOfColumns} /> - + { switch (column.kind) { case 'DATETIME': return Object.entries(column.datetimeSummary) - .map(([key, value]) => `${key}: ${value.toISOString()}`) + .map(([key, value]) => `${key}: ${new Date(value).toISOString()}`) .join(', '); case 'ENUM': return column.enumSummary.distribution.map((entry) => `${entry['']}: ${entry.count}`).join(', '); diff --git a/web/src/features/projects/components/UserCard.tsx b/web/src/features/projects/components/UserCard.tsx index 3b95ad99..d7ab7e96 100644 --- a/web/src/features/projects/components/UserCard.tsx +++ b/web/src/features/projects/components/UserCard.tsx @@ -40,7 +40,13 @@ const UserCard = ({ projectId, userId, userNumber }: UserCardProps) => { }); void navigate({ to: `/portal/projects/${projectId}` }); }) - .catch(console.error); + .catch((error) => { + console.error(error); + addNotification({ + message: t('removeProjectUserFailure'), + type: 'error' + }); + }); }; return ( @@ -50,13 +56,13 @@ const UserCard = ({ projectId, userId, userNumber }: UserCardProps) => {
  • - {t('userFirstName')}: {user.firstName} + {t('userFirstName')} {user.firstName}
  • - {t('userLastName')}: {user.lastName} + {t('userLastName')} {user.lastName}
  • - {t('userEmail')}: {user.email} + {t('userEmail')} {user.email}
diff --git a/web/src/features/projects/pages/AddProjectDatasetPage.tsx b/web/src/features/projects/pages/AddProjectDatasetPage.tsx index fa7f535d..60be10b1 100644 --- a/web/src/features/projects/pages/AddProjectDatasetPage.tsx +++ b/web/src/features/projects/pages/AddProjectDatasetPage.tsx @@ -2,7 +2,7 @@ import { useEffect, useState } from 'react'; import type { $DatasetCardProps } from '@databank/core'; import { Card } from '@douglasneuroinformatics/libui/components'; -import { useTranslation } from '@douglasneuroinformatics/libui/hooks'; +import { useNotificationsStore, useTranslation } from '@douglasneuroinformatics/libui/hooks'; import { getRouteApi } from '@tanstack/react-router'; import axios from 'axios'; @@ -13,6 +13,7 @@ const AddProjectDatasetPage = () => { const [datasetsInfoArray, setDatasetsInfoArray] = useState<$DatasetCardProps[] | null>(null); const route = getRouteApi('/portal/projects/add-dataset/$projectId'); const params = route.useParams(); + const addNotification = useNotificationsStore((state) => state.addNotification); useEffect(() => { axios @@ -20,7 +21,13 @@ const AddProjectDatasetPage = () => { .then((response) => { setDatasetsInfoArray(response.data); }) - .catch(console.error); + .catch((error) => { + console.error(error); + addNotification({ + message: t('fetchDatasetFailure'), + type: 'error' + }); + }); }, []); return ( diff --git a/web/src/features/projects/pages/EditProjectInfoPage.tsx b/web/src/features/projects/pages/EditProjectInfoPage.tsx index 2947e624..63a36340 100644 --- a/web/src/features/projects/pages/EditProjectInfoPage.tsx +++ b/web/src/features/projects/pages/EditProjectInfoPage.tsx @@ -34,7 +34,13 @@ const EditProjectInfoPage = () => { addNotification({ message: 'Project Information Updated!', type: 'success' }); void navigate({ to: `/portal/projects/${params.projectId}` }); }) - .catch(console.error); + .catch((error) => { + console.error(error); + addNotification({ + message: t('editProjectInfoFailure'), + type: 'error' + }); + }); }, [params.projectId] ); diff --git a/web/src/features/projects/pages/ManageProjectUsersPage.tsx b/web/src/features/projects/pages/ManageProjectUsersPage.tsx index 7b431234..9931fdbf 100644 --- a/web/src/features/projects/pages/ManageProjectUsersPage.tsx +++ b/web/src/features/projects/pages/ManageProjectUsersPage.tsx @@ -14,19 +14,23 @@ const ManageProjectUsersPage = () => { const addNotification = useNotificationsStore((state) => state.addNotification); const navigate = useNavigate(); - const addManager = (userEmailToAdd: string) => { + const addProjectUser = (userEmailToAdd: string) => { axios - .post(`/v1/projects/add-user/${projectId}`, { - newUserEmail: userEmailToAdd - }) + .post(`/v1/projects/add-user/${projectId}/${encodeURIComponent(userEmailToAdd)}`) .then(() => { addNotification({ message: `User with Email ${userEmailToAdd} has been added to the current project`, type: 'success' }); - void navigate({ to: `/portal/project/${projectId}` }); + void navigate({ to: `/portal/projects/${projectId}` }); }) - .catch(console.error); + .catch((error) => { + console.error(error); + addNotification({ + message: t('addProjectUserFailure'), + type: 'error' + }); + }); }; return ( @@ -35,16 +39,16 @@ const ManageProjectUsersPage = () => { content={{ newManagerEmail: { kind: 'string', - label: t('newManagerEmail'), + label: t('newProjectUserEmail'), variant: 'input' } }} - submitBtnLabel={t('addManager')} + submitBtnLabel={t('addUser')} validationSchema={z.object({ - newManagerEmail: z.string().email() + newManagerEmail: z.email() })} onSubmit={(data) => { - addManager(data.newManagerEmail); + addProjectUser(data.newManagerEmail); }} />
    diff --git a/web/src/features/projects/pages/SelectProjectDatasetColumnsPage.tsx b/web/src/features/projects/pages/SelectProjectDatasetColumnsPage.tsx index 46592d0f..b03941f0 100644 --- a/web/src/features/projects/pages/SelectProjectDatasetColumnsPage.tsx +++ b/web/src/features/projects/pages/SelectProjectDatasetColumnsPage.tsx @@ -1,6 +1,7 @@ import { useEffect, useState } from 'react'; import { $ProjectColumnSummary, $ProjectDatasetConfigStep } from '@databank/core'; +import { useNotificationsStore, useTranslation } from '@douglasneuroinformatics/libui/hooks'; import axios from 'axios'; import { LoadingFallback } from '@/components'; @@ -24,6 +25,8 @@ export const SelectProjectDatasetColumnsPage = ({ setStep }: SelectProjectDatasetColumnsPagePros) => { const [data, setData] = useState<$ProjectColumnSummary[]>([]); + const addNotification = useNotificationsStore((state) => state.addNotification); + const { t } = useTranslation('common'); useEffect(() => { axios @@ -31,7 +34,13 @@ export const SelectProjectDatasetColumnsPage = ({ .then((response) => { setData(response.data); }) - .catch(console.error); + .catch((error) => { + console.error(error); + addNotification({ + message: t('fetchProjectDatasetSummaryFailure'), + type: 'error' + }); + }); }, [datasetId]); return ( diff --git a/web/src/features/projects/pages/ViewOneProjectDatasetPage.tsx b/web/src/features/projects/pages/ViewOneProjectDatasetPage.tsx index 759a8992..0abed354 100644 --- a/web/src/features/projects/pages/ViewOneProjectDatasetPage.tsx +++ b/web/src/features/projects/pages/ViewOneProjectDatasetPage.tsx @@ -49,7 +49,13 @@ const ViewOneProjectDatasetPage = () => { .then((response) => { setDataset(response.data); }) - .catch(console.error); + .catch((error) => { + console.error(error); + addNotification({ + message: t('fetchProjectDatasetFailure'), + type: 'error' + }); + }); }; void fetchDataset(); }, [columnPaginationDto, rowPaginationDto]); @@ -66,7 +72,13 @@ const ViewOneProjectDatasetPage = () => { }); void navigate({ to: `/portal/projects/${params.projectId}` }); }) - .catch(console.error); + .catch((error) => { + console.error(error); + addNotification({ + message: t('deleteProjectDatasetFailure'), + type: 'error' + }); + }); }); const handleDataDownload = async (format: 'CSV' | 'TSV', data: $TabularDataset) => { diff --git a/web/src/features/projects/pages/ViewOneProjectPage.tsx b/web/src/features/projects/pages/ViewOneProjectPage.tsx index 2bda620e..523ef241 100644 --- a/web/src/features/projects/pages/ViewOneProjectPage.tsx +++ b/web/src/features/projects/pages/ViewOneProjectPage.tsx @@ -16,7 +16,7 @@ type Project = { datasets: string[]; description: string; expiry: Date; - externalId: string; + externalId?: string; id: string; name: string; updatedAt: Date; @@ -44,7 +44,13 @@ const ViewOneProjectPage = () => { }); void navigate({ to: '/portal/projects' }); }) - .catch(console.error); + .catch((error) => { + console.error(error); + addNotification({ + message: t('deleteProjectFailure'), + type: 'error' + }); + }); }); useEffect(() => { @@ -53,21 +59,39 @@ const ViewOneProjectPage = () => { .then((response) => { setProject(response.data); }) - .catch(console.error); + .catch((error) => { + console.error(error); + addNotification({ + message: t('fetchProjectFailure'), + type: 'error' + }); + }); axios .get(`/v1/projects/is-manager/${projectId}`) .then((response) => { setIsManager(response.data); }) - .catch(console.error); + .catch((error) => { + console.error(error); + addNotification({ + message: t('isProjectManagerFailure'), + type: 'error' + }); + }); axios .get<$DatasetCardProps[]>(`/v1/projects/datasets/${projectId}`) .then((response) => { setDatasetsInfoArray(response.data); }) - .catch(console.error); + .catch((error) => { + console.error(error); + addNotification({ + message: t('fetchProjectDatasetsFailure'), + type: 'error' + }); + }); }, [projectId]); return ( diff --git a/web/src/routes/portal/datasets/$datasetId.tsx b/web/src/routes/portal/datasets/$datasetId.tsx index d22013d4..fe40b743 100644 --- a/web/src/routes/portal/datasets/$datasetId.tsx +++ b/web/src/routes/portal/datasets/$datasetId.tsx @@ -14,10 +14,18 @@ const $ViewOneDatasetPageSearchParams = z.object({ rowPagination: $DatasetViewPagination.default({ currentPage: 1, itemsPerPage: 10 }) }); -const getViewDatasetQueryOptions = ( +const getDatasetQueryKey = ( datasetId: string, columnPagination: $DatasetViewPagination, rowPagination: $DatasetViewPagination +) => + `dataset-query-${datasetId}-colPage-${columnPagination.currentPage}-colItems-${columnPagination.itemsPerPage}-rowPage-${rowPagination.currentPage}-rowItems-${rowPagination.itemsPerPage}`; + +const getViewDatasetQueryOptions = ( + datasetId: string, + columnPagination: $DatasetViewPagination, + rowPagination: $DatasetViewPagination, + queryKey: string ) => { const dataQueryUrl = `/v1/datasets/${datasetId}`; return queryOptions({ @@ -28,9 +36,7 @@ const getViewDatasetQueryOptions = ( }); return $TabularDataset.parse(response.data); }, - queryKey: [ - `dataset-query-${datasetId}-colPage-${columnPagination.currentPage}-colItems-${columnPagination.itemsPerPage}-rowPage-${rowPagination.currentPage}-rowItems-${rowPagination.itemsPerPage}` - ] + queryKey: [queryKey] }); }; @@ -38,7 +44,12 @@ export const Route = createFileRoute('/portal/datasets/$datasetId')({ validateSearch: zodValidator($ViewOneDatasetPageSearchParams), loaderDeps: ({ search: { columnPagination, rowPagination } }) => ({ columnPagination, rowPagination }), loader: async ({ deps: { columnPagination, rowPagination }, params }) => { - const viewOneDatasetOptions = getViewDatasetQueryOptions(params.datasetId, columnPagination, rowPagination); + const viewOneDatasetOptions = getViewDatasetQueryOptions( + params.datasetId, + columnPagination, + rowPagination, + getDatasetQueryKey(params.datasetId, columnPagination, rowPagination) + ); await queryClient.ensureQueryData(viewOneDatasetOptions); }, component: () => { @@ -46,7 +57,14 @@ export const Route = createFileRoute('/portal/datasets/$datasetId')({ const params = useParams({ from: '/portal/datasets/$datasetId' }); const downloadDataUrl = `/v1/datasets/download-data/`; const downloadMetaDataUrl = `/v1/datasets/download-metadata/`; - const viewOneDatasetOptions = getViewDatasetQueryOptions(params.datasetId, columnPagination, rowPagination); + const queryKey = getDatasetQueryKey(params.datasetId, columnPagination, rowPagination); + + const viewOneDatasetOptions = getViewDatasetQueryOptions( + params.datasetId, + columnPagination, + rowPagination, + queryKey + ); const datasetQuery = useSuspenseQuery(viewOneDatasetOptions); const dataset = datasetQuery.data; @@ -57,6 +75,7 @@ export const Route = createFileRoute('/portal/datasets/$datasetId')({ dataset={dataset} downloadDataUrl={downloadDataUrl} downloadMetaDataUrl={downloadMetaDataUrl} + queryKey={queryKey} rowPagination={rowPagination} /> ); diff --git a/web/src/translations/common.json b/web/src/translations/common.json index cd71ebe0..46e8e1ae 100644 --- a/web/src/translations/common.json +++ b/web/src/translations/common.json @@ -64,9 +64,13 @@ "fr": "Gérer les gestionnaires de bases de données" }, "newManagerEmail": { - "en": "New Manage Email: ", + "en": "New Manager Email: ", "fr": "Nouvel e-mail du gestionnaire :" }, + "newProjectUserEmail": { + "en": "New Project User Email: ", + "fr": "Courriel du nouvel utilisateur du projet : " + }, "managerFirstName": { "en": "Manager First Name: ", "fr": "Prénom du gestionnaire :" @@ -363,6 +367,94 @@ "en": "Error: unable to created dataset", "fr": "Erreur : impossible de créer la base de données" }, + "fetchDatasetFailure": { + "en": "Error: unable to find dataset owned by the current user", + "fr": "Erreur : impossible de trouver un jeu de données appartenant à l'utilisateur actuel" + }, + "fetchProjectDatasetFailure": { + "en": "Error: unable to fetch the current project dataset", + "fr": "Erreur : impossible de récupérer le jeu de données du projet actuel" + }, + "deleteProjectDatasetFailure": { + "en": "Error: unable to delete the current project dataset", + "fr": "Erreur : impossible de supprimer le jeu de données du projet actuel" + }, + "editDatasetInfoFailure": { + "en": "Error: unable to edit dataset information", + "fr": "Erreur : impossible de modifier les informations du jeu de données." + }, + "fetchProjectDatasetSummaryFailure": { + "en": "Error: unable to fetch the summary for the current project dataset.", + "fr": "Erreur : impossible de récupérer le résumé du jeu de données du projet actuel." + }, + "editProjectInfoFailure": { + "en": "Error: unable to edit project information", + "fr": "Erreur : impossible de modifier les informations du projet." + }, + "removeDatasetManagerFailure": { + "en": "Error: unable to remove dataset manager", + "fr": "Erreur : impossible de supprimer le gestionnaire du jeu de données" + }, + "addDatasetManagerFailure": { + "en": "Error: unable to add dataset manager. Please make sure you enter the correct email address.", + "fr": "Erreur : impossible d'ajouter le gestionnaire du jeu de données. Veuillez vous assurer que vous avez saisi la bonne adresse courriel." + }, + "addProjectUserFailure": { + "en": "Error: unable to add project user. Please make sure you enter the correct email address.", + "fr": "Erreur : impossible d'ajouter l'utilisateur au projet. Veuillez vous assurer que vous avez saisi la bonne adresse courriel." + }, + "removeProjectUserFailure": { + "en": "Error: unable to remove project user", + "fr": "Erreur : impossible de supprimer l'utilisateur du projet" + }, + "downloadDatasetDataFailure": { + "en": "Error: unable to download dataset data.", + "fr": "Erreur : impossible de télécharger les données du jeu de données." + }, + "downloadDatasetMetadataFailure": { + "en": "Error: unable to download dataset metadata.", + "fr": "Erreur : impossible de télécharger les métadonnées du jeu de données." + }, + "setDatasetSharableFailure": { + "en": "Error: unable to change dataset sharing settings.", + "fr": "Erreur : impossible de modifier les paramètres de partage du jeu de données." + }, + "deleteProjectFailure": { + "en": "Error: unable to delete the project.", + "fr": "Erreur : impossible de supprimer le projet." + }, + "fetchProjectFailure": { + "en": "Error: unable to fetch project details.", + "fr": "Erreur : impossible de récupérer les détails du projet." + }, + "isProjectManagerFailure": { + "en": "Error: unable to verify project manager status.", + "fr": "Erreur : impossible de vérifier le statut de gestionnaire du projet." + }, + "fetchProjectDatasetsFailure": { + "en": "Error: unable to fetch datasets for the project.", + "fr": "Erreur : impossible de récupérer les jeux de données du projet." + }, + "setColumnMetadataPermissionFailure": { + "en": "Error: unable to set column metadata permissions.", + "fr": "Erreur : impossible de définir les permissions des métadonnées de la colonne." + }, + "setColumnDataPermissionFailure": { + "en": "Error: unable to set column data permissions.", + "fr": "Erreur : impossible de définir les permissions des données de la colonne." + }, + "toggleColumnNullableFailure": { + "en": "Error: unable to change column nullability.", + "fr": "Erreur : impossible de modifier la possibilité de valeur nulle de la colonne." + }, + "changeColumnDataTypeFailure": { + "en": "Error: unable to change column data type.", + "fr": "Erreur : impossible de modifier le type de données de la colonne." + }, + "deleteColumnFailure": { + "en": "Error: unable to delete column.", + "fr": "Erreur : impossible de supprimer la colonne." + }, "unexpectedValue": { "en": "Unexpected Value", "fr": "Valeur inattendue"