From dc7c457b7b049155d73e1e7290745c61698a46a1 Mon Sep 17 00:00:00 2001 From: cbolles Date: Fri, 15 Mar 2024 11:28:19 -0400 Subject: [PATCH 1/4] Have entry deletion be a flag passed to the dataset view --- .../src/components/DatasetTable.component.tsx | 57 ++++++++++++++++++- .../src/components/DatasetsView.component.tsx | 5 +- .../src/pages/datasets/DatasetControls.tsx | 47 +-------------- 3 files changed, 59 insertions(+), 50 deletions(-) diff --git a/packages/client/src/components/DatasetTable.component.tsx b/packages/client/src/components/DatasetTable.component.tsx index c21dbb2d..164e842e 100644 --- a/packages/client/src/components/DatasetTable.component.tsx +++ b/packages/client/src/components/DatasetTable.component.tsx @@ -1,25 +1,32 @@ -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 = (props) => { const { t } = useTranslation(); const { pushSnackbarMessage } = useSnackbar(); + const [deleteEntryMutation] = useDeleteEntryMutation(); + const confirmation = useConfirmation(); const defaultColumns: GridColDef[] = [ { field: 'view', headerName: t('common.view'), - width: 300, + width: 350, renderCell: (params) => }, { @@ -30,8 +37,53 @@ export const DatasetTable: React.FC = (props) => { } ]; + const handleMultiSelectDelete = () => { + + }; + + 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) { + // TODO: Force rerendering + } + }, + onCancel: () => {} + }); + }; + + const deleteColumn: GridColDef = { + field: 'delete', + type: 'actions', + headerName: t('common.delete'), + width: 120, + maxWidth: 120, + cellClassName: 'delete', + renderHeader: () => { + return handleMultiSelectDelete()}>; + }, + getActions: (params) => { + return [ + } + label={t('common.delete')} + onClick={() => handleDelete(params.id)} + /> + ]; + } + }; + const [entries, setEntries] = useState([]); const columns = [...defaultColumns, ...(props.additionalColumns ?? [])]; + if (props.supportEntryDelete) { + columns.push(deleteColumn); + } const [entryForDataset, entryForDatasetResult] = useEntryForDatasetLazyQuery(); @@ -63,6 +115,7 @@ export const DatasetTable: React.FC = (props) => { }} getRowId={(row) => row._id} pageSizeOptions={[5, 10, 15]} + onRowSelectionModelChange={(ids) => props.onSelectChange(ids)} checkboxSelection disableRowSelectionOnClick /> diff --git a/packages/client/src/components/DatasetsView.component.tsx b/packages/client/src/components/DatasetsView.component.tsx index 6c38aab9..60bfa284 100644 --- a/packages/client/src/components/DatasetsView.component.tsx +++ b/packages/client/src/components/DatasetsView.component.tsx @@ -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 = ({ datasets, additionalColumns }) => { +export const DatasetsView: React.FC = ({ datasets, additionalColumns, supportEntryDelete }) => { return ( <> {datasets.map((dataset: Dataset) => ( @@ -23,7 +24,7 @@ export const DatasetsView: React.FC = ({ datasets, additional {/* provide new dataset object to allow DatasetTable to refetch entries after entries are updated */} - + ))} diff --git a/packages/client/src/pages/datasets/DatasetControls.tsx b/packages/client/src/pages/datasets/DatasetControls.tsx index a66f93c5..a9d8310e 100644 --- a/packages/client/src/pages/datasets/DatasetControls.tsx +++ b/packages/client/src/pages/datasets/DatasetControls.tsx @@ -6,10 +6,6 @@ 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 = () => { @@ -17,11 +13,8 @@ export const DatasetControls: React.FC = () => { const [upload, setUpload] = useState(false); const [datasets, setDatasets] = useState([]); const [getDatasets, getDatasetsResults] = useGetDatasetsLazyQuery(); - const [deleteEntryMutation] = useDeleteEntryMutation(); const { t } = useTranslation(); - const confirmation = useConfirmation(); - useEffect(() => { getDatasets(); }, []); @@ -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 [ - } - label={t('common.delete')} - onClick={() => handleDelete(params.id)} - /> - ]; - } - } - ]; - return ( <> {t('menu.datasetControl')} @@ -116,7 +71,7 @@ export const DatasetControls: React.FC = () => { - + ); }; From 1862b9df4547c9ea46d05f08ea937256cfbc6a9d Mon Sep 17 00:00:00 2001 From: cbolles Date: Fri, 15 Mar 2024 11:52:18 -0400 Subject: [PATCH 2/4] Add logic around multi-deletion --- .../client/public/locales/en/translation.json | 6 ++++-- .../src/components/DatasetTable.component.tsx | 15 +++++++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/packages/client/public/locales/en/translation.json b/packages/client/public/locales/en/translation.json index 6ccfbc20..9c310852 100644 --- a/packages/client/public/locales/en/translation.json +++ b/packages/client/public/locales/en/translation.json @@ -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", diff --git a/packages/client/src/components/DatasetTable.component.tsx b/packages/client/src/components/DatasetTable.component.tsx index 164e842e..fd72c6a3 100644 --- a/packages/client/src/components/DatasetTable.component.tsx +++ b/packages/client/src/components/DatasetTable.component.tsx @@ -21,6 +21,7 @@ export const DatasetTable: React.FC = (props) => { const { pushSnackbarMessage } = useSnackbar(); const [deleteEntryMutation] = useDeleteEntryMutation(); const confirmation = useConfirmation(); + const [selectedRows, setSelectedRows] = useState([]); const defaultColumns: GridColDef[] = [ { @@ -38,7 +39,16 @@ export const DatasetTable: React.FC = (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( )} }); + })) + }, + onCancel: () => {} + }); }; const handleDelete = async (id: GridRowId) => { @@ -115,7 +125,8 @@ export const DatasetTable: React.FC = (props) => { }} getRowId={(row) => row._id} pageSizeOptions={[5, 10, 15]} - onRowSelectionModelChange={(ids) => props.onSelectChange(ids)} + onRowSelectionModelChange={(ids) => setSelectedRows(ids)} + rowSelectionModel={selectedRows} checkboxSelection disableRowSelectionOnClick /> From 287fb7f092dee258ea329d29123aff24a9fedb04 Mon Sep 17 00:00:00 2001 From: cbolles Date: Fri, 15 Mar 2024 12:01:56 -0400 Subject: [PATCH 3/4] Working bulk delete --- packages/client/public/locales/en/translation.json | 2 +- .../client/src/components/DatasetTable.component.tsx | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/client/public/locales/en/translation.json b/packages/client/public/locales/en/translation.json index 9c310852..b53aa97e 100644 --- a/packages/client/public/locales/en/translation.json +++ b/packages/client/public/locales/en/translation.json @@ -76,7 +76,7 @@ "deleteDescription": "Are you sure you want to delete this entry? Doing so will delete all cooresponding tags", "addDataset": " Add New Dataset", "uploadEntries": " Upload Entries", - "deleteMultipleEntries": "Are you sure you want to delete selected entries? All cooresponding tags will be deleted: " + "deleteMultipleEntries": "Are you sure you want to delete selected entries? All cooresponding tags will be deleted" }, "projectAccess": { "datasetName": "Dataset Name", diff --git a/packages/client/src/components/DatasetTable.component.tsx b/packages/client/src/components/DatasetTable.component.tsx index fd72c6a3..7c0da177 100644 --- a/packages/client/src/components/DatasetTable.component.tsx +++ b/packages/client/src/components/DatasetTable.component.tsx @@ -45,7 +45,8 @@ export const DatasetTable: React.FC = (props) => { onConfirm: async () => { await Promise.all(selectedRows.map((id) => { return deleteEntryMutation({ variables: { entry: id.toString( )} }); - })) + })); + reload(); }, onCancel: () => {} }); @@ -61,7 +62,7 @@ export const DatasetTable: React.FC = (props) => { if (res.errors) { //TODO show error with snackbar } else if (res.data) { - // TODO: Force rerendering + reload(); } }, onCancel: () => {} @@ -98,9 +99,13 @@ export const DatasetTable: React.FC = (props) => { 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) { From cdda8297bd85407f6525ef12ce2ebe9ebe68cdfb Mon Sep 17 00:00:00 2001 From: cbolles Date: Fri, 15 Mar 2024 12:04:50 -0400 Subject: [PATCH 4/4] Fix formatting --- .../src/components/DatasetTable.component.tsx | 16 +++++++++++----- .../src/components/DatasetsView.component.tsx | 6 +++++- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/packages/client/src/components/DatasetTable.component.tsx b/packages/client/src/components/DatasetTable.component.tsx index 7c0da177..bea15293 100644 --- a/packages/client/src/components/DatasetTable.component.tsx +++ b/packages/client/src/components/DatasetTable.component.tsx @@ -43,9 +43,11 @@ export const DatasetTable: React.FC = (props) => { 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( )} }); - })); + await Promise.all( + selectedRows.map((id) => { + return deleteEntryMutation({ variables: { entry: id.toString() } }); + }) + ); reload(); }, onCancel: () => {} @@ -77,7 +79,11 @@ export const DatasetTable: React.FC = (props) => { maxWidth: 120, cellClassName: 'delete', renderHeader: () => { - return handleMultiSelectDelete()}>; + return ( + handleMultiSelectDelete()}> + + + ); }, getActions: (params) => { return [ @@ -104,7 +110,7 @@ export const DatasetTable: React.FC = (props) => { 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(() => { diff --git a/packages/client/src/components/DatasetsView.component.tsx b/packages/client/src/components/DatasetsView.component.tsx index 60bfa284..6ea022f5 100644 --- a/packages/client/src/components/DatasetsView.component.tsx +++ b/packages/client/src/components/DatasetsView.component.tsx @@ -24,7 +24,11 @@ export const DatasetsView: React.FC = ({ datasets, additional {/* provide new dataset object to allow DatasetTable to refetch entries after entries are updated */} - + ))}