From dc9acdd7cc549aa21b685c71c18553ddf4fc6153 Mon Sep 17 00:00:00 2001 From: Connor Yoh Date: Wed, 10 Sep 2025 19:05:30 +0100 Subject: [PATCH] Show history --- .../public/locales/en-GB/translation.json | 5 +- .../fileManager/FileHistoryGroup.tsx | 68 +++++++++++++++++++ .../components/fileManager/FileListArea.tsx | 44 +++++++----- .../components/fileManager/FileListItem.tsx | 49 +++++++------ frontend/src/contexts/FileManagerContext.tsx | 24 ++----- 5 files changed, 132 insertions(+), 58 deletions(-) create mode 100644 frontend/src/components/fileManager/FileHistoryGroup.tsx diff --git a/frontend/public/locales/en-GB/translation.json b/frontend/public/locales/en-GB/translation.json index 7792c5c16..f7bfc2d3f 100644 --- a/frontend/public/locales/en-GB/translation.json +++ b/frontend/public/locales/en-GB/translation.json @@ -2147,11 +2147,12 @@ "supportMessage": "Powered by browser database storage for unlimited capacity", "noFileSelected": "No files selected", "showHistory": "Show History", - "hideHistory": "Hide History", + "hideHistory": "Hide History", + "fileHistory": "FileHistory", "loadingHistory": "Loading History...", "lastModified": "Last Modified", "toolChain": "Tools Applied", - "addToRecents": "Add to Recents", + "restore": "Restore", "searchFiles": "Search files...", "recent": "Recent", "localFiles": "Local Files", diff --git a/frontend/src/components/fileManager/FileHistoryGroup.tsx b/frontend/src/components/fileManager/FileHistoryGroup.tsx new file mode 100644 index 000000000..328e502a3 --- /dev/null +++ b/frontend/src/components/fileManager/FileHistoryGroup.tsx @@ -0,0 +1,68 @@ +import React from 'react'; +import { Box, Text, Collapse, Group } from '@mantine/core'; +import { useTranslation } from 'react-i18next'; +import { StirlingFileStub } from '../../types/fileContext'; +import FileListItem from './FileListItem'; + +interface FileHistoryGroupProps { + leafFile: StirlingFileStub; + historyFiles: StirlingFileStub[]; + isExpanded: boolean; + onDownloadSingle: (file: StirlingFileStub) => void; + onFileDoubleClick: (file: StirlingFileStub) => void; + onFileRemove: (index: number) => void; + isFileSupported: (fileName: string) => boolean; +} + +const FileHistoryGroup: React.FC = ({ + leafFile, + historyFiles, + isExpanded, + onDownloadSingle, + onFileDoubleClick, + onFileRemove, + isFileSupported, +}) => { + const { t } = useTranslation(); + + // Sort history files by version number (oldest first, excluding the current leaf file) + const sortedHistory = historyFiles + .filter(file => file.id !== leafFile.id) // Exclude the leaf file itself + .sort((a, b) => (a.versionNumber || 1) - (b.versionNumber || 1)); + + if (!isExpanded || sortedHistory.length === 0) { + return null; + } + + return ( + + + + + {t('fileManager.fileHistory', 'File History')} ({sortedHistory.length}) + + + + + {sortedHistory.map((historyFile, index) => ( + {}} // No selection for history files + onRemove={() => onFileRemove(index)} // Pass through remove handler + onDownload={() => onDownloadSingle(historyFile)} + onDoubleClick={() => onFileDoubleClick(historyFile)} + isHistoryFile={true} // This enables "Add to Recents" in menu + isLatestVersion={false} // History files are never latest + // onAddToRecents is accessed from context by FileListItem + /> + ))} + + + + ); +}; + +export default FileHistoryGroup; diff --git a/frontend/src/components/fileManager/FileListArea.tsx b/frontend/src/components/fileManager/FileListArea.tsx index ab447a50c..c3cc22929 100644 --- a/frontend/src/components/fileManager/FileListArea.tsx +++ b/frontend/src/components/fileManager/FileListArea.tsx @@ -4,6 +4,7 @@ import CloudIcon from '@mui/icons-material/Cloud'; import HistoryIcon from '@mui/icons-material/History'; import { useTranslation } from 'react-i18next'; import FileListItem from './FileListItem'; +import FileHistoryGroup from './FileHistoryGroup'; import { useFileManagerContext } from '../../contexts/FileManagerContext'; interface FileListAreaProps { @@ -22,6 +23,7 @@ const FileListArea: React.FC = ({ selectedFilesSet, fileGroups, expandedFileIds, + loadedHistoryFiles, onFileSelect, onFileRemove, onFileDoubleClick, @@ -53,24 +55,34 @@ const FileListArea: React.FC = ({ ) : ( filteredFiles.map((file, index) => { - // Determine if this is a history file based on whether it's in the recent files or loaded as history - const isLeafFile = recentFiles.some(rf => rf.id === file.id); - const isHistoryFile = !isLeafFile; // If not in recent files, it's a loaded history file - const isLatestVersion = isLeafFile; // Only leaf files (from recent files) are latest versions + // All files in filteredFiles are now leaf files only + const historyFiles = loadedHistoryFiles.get(file.id) || []; + const isExpanded = expandedFileIds.has(file.id); return ( - onFileSelect(file, index, shiftKey)} - onRemove={() => onFileRemove(index)} - onDownload={() => onDownloadSingle(file)} - onDoubleClick={() => onFileDoubleClick(file)} - isHistoryFile={isHistoryFile} - isLatestVersion={isLatestVersion} - /> + + onFileSelect(file, index, shiftKey)} + onRemove={() => onFileRemove(index)} + onDownload={() => onDownloadSingle(file)} + onDoubleClick={() => onFileDoubleClick(file)} + isHistoryFile={false} // All files here are leaf files + isLatestVersion={true} // All files here are the latest versions + /> + + + ); }) )} diff --git a/frontend/src/components/fileManager/FileListItem.tsx b/frontend/src/components/fileManager/FileListItem.tsx index 3ad138f9e..bf11ce261 100644 --- a/frontend/src/components/fileManager/FileListItem.tsx +++ b/frontend/src/components/fileManager/FileListItem.tsx @@ -5,6 +5,7 @@ import DeleteIcon from '@mui/icons-material/Delete'; import DownloadIcon from '@mui/icons-material/Download'; import AddIcon from '@mui/icons-material/Add'; import HistoryIcon from '@mui/icons-material/History'; +import RestoreIcon from '@mui/icons-material/Restore'; import { useTranslation } from 'react-i18next'; import { getFileSize, getFileDate } from '../../utils/fileUtils'; import { StirlingFileStub } from '../../types/fileContext'; @@ -54,8 +55,10 @@ const FileListItem: React.FC = ({ = ({ paddingLeft: isHistoryFile ? '2rem' : '0.75rem', // Indent history files borderLeft: isHistoryFile ? '3px solid var(--mantine-color-blue-4)' : 'none' // Visual indicator for history }} - onClick={(e) => onSelect(e.shiftKey)} + onClick={isHistoryFile ? undefined : (e) => onSelect(e.shiftKey)} onDoubleClick={onDoubleClick} onMouseEnter={() => setIsHovered(true)} onMouseLeave={() => setIsHovered(false)} > - - {/* Checkbox for all files */} - {}} // Handled by parent onClick - size="sm" - pl="sm" - pr="xs" - styles={{ - input: { - cursor: 'pointer' - } - }} - /> - + {!isHistoryFile && ( + + {/* Checkbox for regular files only */} + {}} // Handled by parent onClick + size="sm" + pl="sm" + pr="xs" + styles={{ + input: { + cursor: 'pointer' + } + }} + /> + + )} {file.name} - 1 ? "blue" : "gray"}> + v{currentVersion} @@ -172,17 +177,17 @@ const FileListItem: React.FC = ({ )} - {/* Add to Recents option for history files */} + {/* Restore option for history files */} {isHistoryFile && ( <> } + leftSection={} onClick={(e) => { e.stopPropagation(); onAddToRecents(file); }} > - {t('fileManager.addToRecents', 'Add to Recents')} + {t('fileManager.restore', 'Restore')} diff --git a/frontend/src/contexts/FileManagerContext.tsx b/frontend/src/contexts/FileManagerContext.tsx index e514593a7..3cca922dd 100644 --- a/frontend/src/contexts/FileManagerContext.tsx +++ b/frontend/src/contexts/FileManagerContext.tsx @@ -17,6 +17,7 @@ interface FileManagerContextValue { selectedFilesSet: Set; expandedFileIds: Set; fileGroups: Map; + loadedHistoryFiles: Map; // Handlers onSourceChange: (source: 'recent' | 'local' | 'drive') => void; @@ -103,24 +104,9 @@ export const FileManagerProvider: React.FC = ({ const displayFiles = useMemo(() => { if (!recentFiles || recentFiles.length === 0) return []; - const expandedFiles = []; - - // Since we now only load leaf files, iterate through recent files directly - for (const leafFile of recentFiles) { - // Add the leaf file (main file shown in list) - expandedFiles.push(leafFile); - - // If expanded, add the loaded history files - if (expandedFileIds.has(leafFile.id)) { - const historyFiles = loadedHistoryFiles.get(leafFile.id) || []; - // Sort history files by version number (oldest first) - const sortedHistory = historyFiles.sort((a, b) => (a.versionNumber || 1) - (b.versionNumber || 1)); - expandedFiles.push(...sortedHistory); - } - } - - return expandedFiles; - }, [recentFiles, expandedFileIds, loadedHistoryFiles]); + // Only return leaf files - history files will be handled by separate components + return recentFiles; + }, [recentFiles]); const selectedFiles = selectedFileIds.length === 0 ? [] : displayFiles.filter(file => selectedFilesSet.has(file.id)); @@ -533,6 +519,7 @@ export const FileManagerProvider: React.FC = ({ selectedFilesSet, expandedFileIds, fileGroups, + loadedHistoryFiles, // Handlers onSourceChange: handleSourceChange, @@ -564,6 +551,7 @@ export const FileManagerProvider: React.FC = ({ fileInputRef, expandedFileIds, fileGroups, + loadedHistoryFiles, handleSourceChange, handleLocalFileClick, handleFileSelect,