diff --git a/src/features/collections/components/CollectionTab.tsx b/src/features/collections/components/CollectionTab.tsx index 5caa65e..2707398 100644 --- a/src/features/collections/components/CollectionTab.tsx +++ b/src/features/collections/components/CollectionTab.tsx @@ -395,6 +395,7 @@ const CollectionTab: React.FC = ({ const [jsonEditData, setJsonEditData] = useState(''); const [jsonHasChanges, setJsonHasChanges] = useState(false); const [createDialogOpen, setCreateDialogOpen] = useState(false); + const [duplicateDialogOpen, setDuplicateDialogOpen] = useState(false); const [newDocId, setNewDocId] = useState(''); const [newDocData, setNewDocData] = useState('{}'); const [columnWidths, setColumnWidths] = useState>({}); @@ -703,6 +704,45 @@ const CollectionTab: React.FC = ({ } }, [newDocId, newDocData, dispatch, project, collectionPath, firestoreDatabaseId, showMessage, showError]); + // Duplicate the single selected table document using the create flow. + const handleDuplicateDocument = useCallback(() => { + if (selectedRows.length !== 1) return; + const source = documents.find((doc) => doc.id === selectedRows[0]); + if (!source) return; + + const existingIds = new Set(documents.map((doc) => doc.id)); + const baseId = `${source.id}_copy`; + let duplicateId = baseId; + let suffix = 2; + while (existingIds.has(duplicateId)) duplicateId = `${baseId}_${suffix++}`; + + setNewDocId(duplicateId); + setNewDocData(JSON.stringify(source.data, null, 2)); + setDuplicateDialogOpen(true); + }, [documents, selectedRows]); + + const handleDuplicateCreate = useCallback(async () => { + try { + JSON.parse(newDocData); + const createdId = await dispatch( + addDocument({ + project, + collection: collectionPath, + docId: newDocId, + docData: newDocData, + firestoreDatabaseId, + }), + ).unwrap(); + setDuplicateDialogOpen(false); + setNewDocId(''); + setNewDocData('{}'); + setSelectedRows([]); + showMessage?.(`Duplicated document as ${createdId || newDocId}`, 'success'); + } catch (error: unknown) { + showError(error); + } + }, [newDocData, newDocId, dispatch, project, collectionPath, firestoreDatabaseId, showMessage, showError]); + // Delete Selected Documents Handler const handleDeleteSelected = useCallback(async () => { if (selectedRows.length === 0) return; @@ -923,6 +963,7 @@ const CollectionTab: React.FC = ({ onImport={handleImportDocuments} onAdd={() => setCreateDialogOpen(true)} onDelete={() => setDeleteDialogOpen(true)} + onDuplicate={handleDuplicateDocument} allFields={allFields} visibleFields={visibleFields} hiddenColumns={hiddenColumns} @@ -1009,6 +1050,19 @@ const CollectionTab: React.FC = ({ onCreate={handleCreateDocument} /> + {/* Duplicate Document Dialog */} + setDuplicateDialogOpen(false)} + newDocId={newDocId} + setNewDocId={setNewDocId} + newDocData={newDocData} + setNewDocData={setNewDocData} + onCreate={handleDuplicateCreate} + title="Duplicate Document" + actionLabel="Duplicate" + /> + {/* Add Field Dialog (Tree view) */} void; onCreate: () => void; + title?: string; + actionLabel?: string; } const CreateDocumentDialog: React.FC = ({ @@ -19,10 +21,13 @@ const CreateDocumentDialog: React.FC = ({ newDocData, setNewDocData, onCreate, + title = 'Create Document', + actionLabel = 'Create', }) => { return ( Create Document + {title} = ({ diff --git a/src/features/collections/components/ViewTabs.tsx b/src/features/collections/components/ViewTabs.tsx index de6741c..22916fd 100644 --- a/src/features/collections/components/ViewTabs.tsx +++ b/src/features/collections/components/ViewTabs.tsx @@ -10,6 +10,7 @@ import { FileUpload as ImportIcon, ViewColumn as ColumnsIcon, Delete as DeleteIcon, + ContentCopy as DuplicateIcon, } from '@mui/icons-material'; interface ViewTabsProps { @@ -21,6 +22,7 @@ interface ViewTabsProps { onImport: () => void; onAdd: () => void; onDelete: () => void; + onDuplicate: () => void; allFields: string[]; visibleFields: string[]; hiddenColumns: Record; @@ -37,6 +39,7 @@ const ViewTabs: React.FC = ({ onImport, onAdd, onDelete, + onDuplicate, allFields, visibleFields, hiddenColumns, @@ -167,6 +170,13 @@ const ViewTabs: React.FC = ({ )} + {selectedRowsCount === 1 && ( + + + + + + )}