Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions packages/client/public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,11 @@
},
"datasetControl": {
"deleteEntry": "Delete Entry",
"deleteDescription": "Are you sure you want to delete this project? Doing so will delete all associated tags",
"deleteEntries": "Delete Entries",
"deleteDescription": "Are you sure you want to delete this entry? Doing so will delete all cooresponding tags",
"addDataset": " Add New Dataset",
"uploadEntries": " Upload Entries"
"uploadEntries": " Upload Entries",
"deleteMultipleEntries": "Are you sure you want to delete selected entries? All cooresponding tags will be deleted"
},
"projectAccess": {
"datasetName": "Dataset Name",
Expand Down
81 changes: 78 additions & 3 deletions packages/client/src/components/DatasetTable.component.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,33 @@
import { DataGrid, GridColDef } from '@mui/x-data-grid';
import { DataGrid, GridColDef, GridRowId, GridActionsCellItem } from '@mui/x-data-grid';
import { useState, useEffect } from 'react';
import { Dataset, Entry } from '../graphql/graphql';
import { useEntryForDatasetLazyQuery } from '../graphql/entry/entry';
import { EntryView } from './EntryView.component';
import { useTranslation } from 'react-i18next';
import { useSnackbar } from '../context/Snackbar.context';
import { Delete } from '@mui/icons-material';
import { IconButton } from '@mui/material';
import { useDeleteEntryMutation } from '../graphql/entry/entry';
import { useConfirmation } from '../context/Confirmation.context';

export interface DatasetTableProps {
dataset: Dataset;
additionalColumns?: GridColDef[];
supportEntryDelete?: boolean;
}

export const DatasetTable: React.FC<DatasetTableProps> = (props) => {
const { t } = useTranslation();
const { pushSnackbarMessage } = useSnackbar();
const [deleteEntryMutation] = useDeleteEntryMutation();
const confirmation = useConfirmation();
const [selectedRows, setSelectedRows] = useState<GridRowId[]>([]);

const defaultColumns: GridColDef[] = [
{
field: 'view',
headerName: t('common.view'),
width: 300,
width: 350,
renderCell: (params) => <EntryView entry={params.row as Entry} width={300} />
},
{
Expand All @@ -30,15 +38,80 @@ export const DatasetTable: React.FC<DatasetTableProps> = (props) => {
}
];

const handleMultiSelectDelete = () => {
confirmation.pushConfirmationRequest({
title: t('components.datasetcontrol.deleteEntries'),
message: `${t('components.datasetControl.deleteMultipleEntries')}:${selectedRows.length}`,
onConfirm: async () => {
await Promise.all(
selectedRows.map((id) => {
return deleteEntryMutation({ variables: { entry: id.toString() } });
})
);
reload();
},
onCancel: () => {}
});
};

const handleDelete = async (id: GridRowId) => {
// Execute delete mutation
confirmation.pushConfirmationRequest({
title: t('components.datasetControl.deleteEntry'),
message: t('components.datasetControl.deleteDescription'),
onConfirm: async () => {
const res = await deleteEntryMutation({ variables: { entry: id.toString() } });
if (res.errors) {
//TODO show error with snackbar
} else if (res.data) {
reload();
}
},
onCancel: () => {}
});
};

const deleteColumn: GridColDef = {
field: 'delete',
type: 'actions',
headerName: t('common.delete'),
width: 120,
maxWidth: 120,
cellClassName: 'delete',
renderHeader: () => {
return (
<IconButton onClick={() => handleMultiSelectDelete()}>
<Delete color={'error'} />
</IconButton>
);
},
getActions: (params) => {
return [
<GridActionsCellItem
icon={<Delete color={'error'} />}
label={t('common.delete')}
onClick={() => handleDelete(params.id)}
/>
];
}
};

const [entries, setEntries] = useState<Entry[]>([]);
const columns = [...defaultColumns, ...(props.additionalColumns ?? [])];
if (props.supportEntryDelete) {
columns.push(deleteColumn);
}

const [entryForDataset, entryForDatasetResult] = useEntryForDatasetLazyQuery();

useEffect(() => {
entryForDataset({ variables: { dataset: props.dataset._id }, fetchPolicy: 'network-only' });
reload();
}, [props.dataset]);

const reload = () => {
entryForDataset({ variables: { dataset: props.dataset._id }, fetchPolicy: 'network-only' });
};

// TODO: Add in logic to re-fetch data when the presigned URL expires
useEffect(() => {
if (entryForDatasetResult.data) {
Expand All @@ -63,6 +136,8 @@ export const DatasetTable: React.FC<DatasetTableProps> = (props) => {
}}
getRowId={(row) => row._id}
pageSizeOptions={[5, 10, 15]}
onRowSelectionModelChange={(ids) => setSelectedRows(ids)}
rowSelectionModel={selectedRows}
checkboxSelection
disableRowSelectionOnClick
/>
Expand Down
9 changes: 7 additions & 2 deletions packages/client/src/components/DatasetsView.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import { GridColDef } from '@mui/x-data-grid';
export interface DatasetsViewProps {
datasets: Dataset[];
additionalColumns?: GridColDef[];
supportEntryDelete?: boolean;
}

// TODO: Implement lazy loading on accordion open to prevent loading all datasets at once
export const DatasetsView: React.FC<DatasetsViewProps> = ({ datasets, additionalColumns }) => {
export const DatasetsView: React.FC<DatasetsViewProps> = ({ datasets, additionalColumns, supportEntryDelete }) => {
return (
<>
{datasets.map((dataset: Dataset) => (
Expand All @@ -23,7 +24,11 @@ export const DatasetsView: React.FC<DatasetsViewProps> = ({ datasets, additional
</AccordionSummary>
<AccordionDetails>
{/* provide new dataset object to allow DatasetTable to refetch entries after entries are updated */}
<DatasetTable dataset={{ ...dataset }} additionalColumns={additionalColumns} />
<DatasetTable
dataset={{ ...dataset }}
additionalColumns={additionalColumns}
supportEntryDelete={supportEntryDelete}
/>
</AccordionDetails>
</Accordion>
))}
Expand Down
47 changes: 1 addition & 46 deletions packages/client/src/pages/datasets/DatasetControls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,15 @@ import { UploadEntries } from '../../components/UploadEntries.component';
import { Dataset } from '../../graphql/graphql';
import { useGetDatasetsLazyQuery } from '../../graphql/dataset/dataset';
import { DatasetsView } from '../../components/DatasetsView.component';
import { GridColDef, GridActionsCellItem, GridRowId } from '@mui/x-data-grid';
import DeleteIcon from '@mui/icons-material/DeleteOutlined';
import { useConfirmation } from '../../context/Confirmation.context';
import { useDeleteEntryMutation } from '../../graphql/entry/entry';
import { useTranslation } from 'react-i18next';

export const DatasetControls: React.FC = () => {
const [add, setAdd] = useState(false);
const [upload, setUpload] = useState(false);
const [datasets, setDatasets] = useState<Dataset[]>([]);
const [getDatasets, getDatasetsResults] = useGetDatasetsLazyQuery();
const [deleteEntryMutation] = useDeleteEntryMutation();
const { t } = useTranslation();

const confirmation = useConfirmation();

useEffect(() => {
getDatasets();
}, []);
Expand Down Expand Up @@ -51,44 +44,6 @@ export const DatasetControls: React.FC = () => {
setUpload((upload) => !upload);
};

const handleDelete = async (id: GridRowId) => {
// Execute delete mutation
confirmation.pushConfirmationRequest({
title: t('components.datasetControl.deleteEntry'),
message: t('components.datasetControl.deleteDescription'),
onConfirm: async () => {
const res = await deleteEntryMutation({ variables: { entry: id.toString() } });
if (res.errors) {
//TODO show error with snackbar
} else if (res.data) {
// force rerender
setDatasets([...datasets]);
}
},
onCancel: () => {}
});
};

const additionalColumns: GridColDef[] = [
{
field: 'delete',
type: 'actions',
headerName: t('common.delete'),
width: 120,
maxWidth: 120,
cellClassName: 'delete',
getActions: (params) => {
return [
<GridActionsCellItem
icon={<DeleteIcon color={'error'} />}
label={t('common.delete')}
onClick={() => handleDelete(params.id)}
/>
];
}
}
];

return (
<>
<Typography variant="h3">{t('menu.datasetControl')}</Typography>
Expand Down Expand Up @@ -116,7 +71,7 @@ export const DatasetControls: React.FC = () => {
</Typography>
</Box>
</Box>
<DatasetsView datasets={datasets} additionalColumns={additionalColumns} />
<DatasetsView datasets={datasets} supportEntryDelete={true} />
</>
);
};