From 0b00fac7a9ef912097d6262d74e09cde85c6b466 Mon Sep 17 00:00:00 2001 From: tombch Date: Wed, 27 May 2026 13:22:46 +0200 Subject: [PATCH 1/6] Populate preexisting files for revision on EditPage --- website/src/components/Edit/EditPage.tsx | 19 ++++++++++---- .../components/Submission/DataUploadForm.tsx | 3 +++ .../FileUpload/FolderUploadComponent.tsx | 18 ++++++++++++- website/src/types/backend.ts | 25 ++++++++++--------- 4 files changed, 47 insertions(+), 18 deletions(-) diff --git a/website/src/components/Edit/EditPage.tsx b/website/src/components/Edit/EditPage.tsx index 175af25d50..074a524725 100644 --- a/website/src/components/Edit/EditPage.tsx +++ b/website/src/components/Edit/EditPage.tsx @@ -51,14 +51,18 @@ const InnerEditPage: FC = ({ groupedInputFields, submissionDataTypes, }) => { + const isCreatingRevision = dataToEdit.status === approvedForReleaseStatus; + const extraFilesEnabled = submissionDataTypes.files?.enabled ?? false; + const [editableMetadata, setEditableMetadata] = useState(EditableMetadata.fromInitialData(dataToEdit)); const [editableSequences, setEditableSequences] = useState( EditableSequences.fromInitialData(dataToEdit, submissionDataTypes.maxSequencesPerEntry), ); - const [fileMapping, setFileMapping] = useState(undefined); - - const isCreatingRevision = dataToEdit.status === approvedForReleaseStatus; - const extraFilesEnabled = submissionDataTypes.files?.enabled ?? false; + const [fileMapping, setFileMapping] = useState(() => + extraFilesEnabled && dataToEdit.originalData.files + ? { [dataToEdit.submissionId]: dataToEdit.originalData.files } + : undefined, + ); const { mutate: submitRevision, isPending: isRevisionPending } = useSubmitRevision( organism, @@ -116,12 +120,16 @@ const InnerEditPage: FC = ({ fileMapping: fileMappingWithSubmissionId, }); } else { + const fileMappingForEdit = + extraFilesEnabled && fileMapping !== undefined ? Object.values(fileMapping)[0] : null; + submitEdit({ accession: dataToEdit.accession, version: dataToEdit.version, data: { metadata: editableMetadata.getMetadataRecord(), unalignedNucleotideSequences: editableSequences.getSequenceRecord(), + files: fileMappingForEdit, }, }); } @@ -158,7 +166,7 @@ const InnerEditPage: FC = ({ /> )} - {isCreatingRevision && extraFilesEnabled && ( + {extraFilesEnabled && (
= ({ inputMode='form' groupId={dataToEdit.groupId} fileCategories={submissionDataTypes.files?.categories ?? []} + fileMapping={fileMapping} setFileMapping={setFileMapping} onError={(msg) => toast.error(msg, { position: 'top-center', autoClose: false })} /> diff --git a/website/src/components/Submission/DataUploadForm.tsx b/website/src/components/Submission/DataUploadForm.tsx index 4542dd34c6..9bbe5edaf3 100644 --- a/website/src/components/Submission/DataUploadForm.tsx +++ b/website/src/components/Submission/DataUploadForm.tsx @@ -282,6 +282,7 @@ export const ExtraFilesUpload = ({ inputMode, groupId, fileCategories, + fileMapping, setFileMapping, onError, }: { @@ -290,6 +291,7 @@ export const ExtraFilesUpload = ({ inputMode: InputMode; groupId: number; fileCategories: FileCategory[]; + fileMapping?: FilesBySubmissionId | undefined; setFileMapping: Dispatch>; onError: (message: string) => void; }) => { @@ -313,6 +315,7 @@ export const ExtraFilesUpload = ({ clientConfig={clientConfig} groupId={groupId} onError={onError} + fileMapping={fileMapping} setFileMapping={setFileMapping} /> ))} diff --git a/website/src/components/Submission/FileUpload/FolderUploadComponent.tsx b/website/src/components/Submission/FileUpload/FolderUploadComponent.tsx index 9e578908f7..d7183312b2 100644 --- a/website/src/components/Submission/FileUpload/FolderUploadComponent.tsx +++ b/website/src/components/Submission/FileUpload/FolderUploadComponent.tsx @@ -81,6 +81,7 @@ type FolderUploadComponentProps = { accessToken: string; clientConfig: ClientConfig; groupId: number; + fileMapping: FilesBySubmissionId | undefined; setFileMapping: Dispatch>; onError: (message: string) => void; }; @@ -91,11 +92,26 @@ export const FolderUploadComponent: FC = ({ accessToken, clientConfig, groupId, + fileMapping, setFileMapping, onError, }) => { const isClient = useClientFlag(); - const [fileUploadState, setFileUploadState] = useState(undefined); + const [fileUploadState, setFileUploadState] = useState(() => { + if (fileMapping === undefined) return undefined; + + const preExistingFiles: Record = {}; + + Object.entries(fileMapping).forEach(([submissionId, categories]) => { + preExistingFiles[submissionId] = categories[fileCategory.name].map((file) => ({ + type: 'uploaded', + fileId: file.fileId, + name: file.name, + size: 0, + })); + }); + return { type: 'uploadCompleted', files: preExistingFiles }; + }); const [isDragging, setIsDragging] = useState(false); const backendClient = new BackendClient(clientConfig.backendUrl); diff --git a/website/src/types/backend.ts b/website/src/types/backend.ts index db984b39cb..51c05a4bb2 100644 --- a/website/src/types/backend.ts +++ b/website/src/types/backend.ts @@ -172,11 +172,24 @@ export const submissionIdMapping = accessionVersion.merge( ); export type SubmissionIdMapping = z.infer; +const filesByCategory = z.record( + z.array( + z.object({ + fileId: z.string(), + name: z.string(), + }), + ), +); + +export const filesBySubmissionId = z.record(filesByCategory); +export type FilesBySubmissionId = z.infer; + export const editedSequenceEntryData = accessionVersion.merge( z.object({ data: z.object({ metadata: unprocessedMetadataRecord, unalignedNucleotideSequences: z.record(z.string()), + files: filesByCategory.nullable(), }), }), ); @@ -203,18 +216,6 @@ export const unprocessedData = accessionVersion.merge( ); export type UnprocessedData = z.infer; -const filesByCategory = z.record( - z.array( - z.object({ - fileId: z.string(), - name: z.string(), - }), - ), -); - -export const filesBySubmissionId = z.record(filesByCategory); -export type FilesBySubmissionId = z.infer; - export const sequenceEntryToEdit = accessionVersion.merge( z.object({ status: sequenceEntryStatusNames, From 703d072e0b15de5a882f4a5271944f8891f70755 Mon Sep 17 00:00:00 2001 From: tombch Date: Mon, 15 Jun 2026 14:10:08 +0200 Subject: [PATCH 2/6] Discard individual files in form submission/revision --- website/src/components/Edit/EditPage.tsx | 9 +++--- .../FileUpload/FolderUploadComponent.tsx | 28 +++++++++++++++++-- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/website/src/components/Edit/EditPage.tsx b/website/src/components/Edit/EditPage.tsx index 7f3cda1601..3a81246de0 100644 --- a/website/src/components/Edit/EditPage.tsx +++ b/website/src/components/Edit/EditPage.tsx @@ -52,16 +52,15 @@ const InnerEditPage: FC = ({ groupedInputFields, submissionDataTypes, }) => { - const isCreatingRevision = dataToEdit.status === approvedForReleaseStatus; - const extraFilesEnabled = submissionDataTypes.files?.enabled ?? false; - const [editableMetadata, setEditableMetadata] = useState(EditableMetadata.fromInitialData(dataToEdit)); const [editableSequences, setEditableSequences] = useState( EditableSequences.fromInitialData(dataToEdit, submissionDataTypes.maxSequencesPerEntry), ); + const isCreatingRevision = dataToEdit.status === approvedForReleaseStatus; + const extraFilesEnabled = submissionDataTypes.files?.enabled ?? false; const [fileMapping, setFileMapping] = useState(() => - extraFilesEnabled && dataToEdit.originalData.files - ? { [dataToEdit.submissionId]: dataToEdit.originalData.files } + extraFilesEnabled && dataToEdit.submittedData.files + ? { [dataToEdit.submissionId]: dataToEdit.submittedData.files } : undefined, ); diff --git a/website/src/components/Submission/FileUpload/FolderUploadComponent.tsx b/website/src/components/Submission/FileUpload/FolderUploadComponent.tsx index ee2b4d5673..819f0a1c52 100644 --- a/website/src/components/Submission/FileUpload/FolderUploadComponent.tsx +++ b/website/src/components/Submission/FileUpload/FolderUploadComponent.tsx @@ -318,6 +318,19 @@ export const FolderUploadComponent: FC = ({ } }; + const handleDiscardFile = (submissionId: string, file: SingleFileUpload) => { + setFileUploadState((state) => { + if (state?.type === 'uploadCompleted') { + const remaining = state.files[submissionId].filter((f) => f.name !== file.name); + if (remaining.length === 0) return undefined; + return produce(state, (draft) => { + draft.files[submissionId] = remaining; + }); + } + return state; + }); + }; + return fileUploadState === undefined || fileUploadState.type === 'awaitingUrls' ? (
= ({

Files

{inputMode === 'form' ? Object.values(fileUploadState.files)[0].map((file) => ( - +
+
+ +
+ +
)) : Object.entries(fileUploadState.files).flatMap(([submissionId, files]) => [

@@ -406,7 +430,7 @@ export const FolderUploadComponent: FC = ({ data-testid={`discard_${fileCategory.name}`} className='text-xs break-words text-gray-700 py-1.5 px-4 border border-gray-300 rounded-md hover:bg-gray-50' > - Discard files + Discard all files

); From 5eb90118a470a60826762b824db5fe073a08518e Mon Sep 17 00:00:00 2001 From: tombch Date: Mon, 15 Jun 2026 14:34:17 +0200 Subject: [PATCH 3/6] Revert edit page changes --- website/src/components/Edit/EditPage.tsx | 6 +---- .../FileUpload/FolderUploadComponent.spec.tsx | 1 + website/src/types/backend.ts | 25 +++++++++---------- 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/website/src/components/Edit/EditPage.tsx b/website/src/components/Edit/EditPage.tsx index 3a81246de0..a11064a647 100644 --- a/website/src/components/Edit/EditPage.tsx +++ b/website/src/components/Edit/EditPage.tsx @@ -120,16 +120,12 @@ const InnerEditPage: FC = ({ fileMapping: fileMappingWithSubmissionId, }); } else { - const fileMappingForEdit = - extraFilesEnabled && fileMapping !== undefined ? Object.values(fileMapping)[0] : null; - submitEdit({ accession: dataToEdit.accession, version: dataToEdit.version, data: { metadata: editableMetadata.getMetadataRecord(), unalignedNucleotideSequences: editableSequences.getSequenceRecord(), - files: fileMappingForEdit, }, }); } @@ -166,7 +162,7 @@ const InnerEditPage: FC = ({ />
)} - {extraFilesEnabled && ( + {isCreatingRevision && extraFilesEnabled && (
; -const filesByCategory = z.record( - z.array( - z.object({ - fileId: z.string(), - name: z.string(), - }), - ), -); - -export const filesBySubmissionId = z.record(filesByCategory); -export type FilesBySubmissionId = z.infer; - export const editedSequenceEntryData = accessionVersion.merge( z.object({ data: z.object({ metadata: submittedMetadataRecord, unalignedNucleotideSequences: z.record(z.string()), - files: filesByCategory.nullable(), }), }), ); @@ -216,6 +203,18 @@ export const unprocessedData = accessionVersion.merge( ); export type UnprocessedData = z.infer; +const filesByCategory = z.record( + z.array( + z.object({ + fileId: z.string(), + name: z.string(), + }), + ), +); + +export const filesBySubmissionId = z.record(filesByCategory); +export type FilesBySubmissionId = z.infer; + export const sequenceEntryToEdit = accessionVersion.merge( z.object({ status: sequenceEntryStatusNames, From d18bbef08b4e573b5a9d6c2205c9c8b838e1f10b Mon Sep 17 00:00:00 2001 From: tombch Date: Mon, 15 Jun 2026 15:18:11 +0200 Subject: [PATCH 4/6] Add previous upload state --- .../FileUpload/FolderUploadComponent.tsx | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/website/src/components/Submission/FileUpload/FolderUploadComponent.tsx b/website/src/components/Submission/FileUpload/FolderUploadComponent.tsx index 819f0a1c52..4f79454541 100644 --- a/website/src/components/Submission/FileUpload/FolderUploadComponent.tsx +++ b/website/src/components/Submission/FileUpload/FolderUploadComponent.tsx @@ -31,7 +31,7 @@ type AwaitingUrlState = { >; }; -type SingleFileUpload = Pending | Uploaded | Error; +type SingleFileUpload = Pending | Uploaded | PreviousUpload | Error; type UploadInProgressState = { type: 'uploadInProgress'; @@ -40,12 +40,12 @@ type UploadInProgressState = { type UploadCompleted = { type: 'uploadCompleted'; - files: Record; + files: Record; }; type FileUploadState = AwaitingUrlState | UploadInProgressState | UploadCompleted; -type UploadStatus = 'pending' | 'uploaded' | 'error'; +type UploadStatus = 'pending' | 'uploaded' | 'previousUpload' | 'error'; type Pending = { type: 'pending'; @@ -68,6 +68,12 @@ type Uploaded = { size: number; }; +type PreviousUpload = { + type: 'previousUpload'; + fileId: string; + name: string; +}; + type Error = { type: 'error'; name: string; @@ -99,18 +105,16 @@ export const FolderUploadComponent: FC = ({ const isClient = useClientFlag(); const [fileUploadState, setFileUploadState] = useState(() => { if (fileMapping === undefined) return undefined; - - const preExistingFiles: Record = {}; + const previousUploadFiles: Record = {}; Object.entries(fileMapping).forEach(([submissionId, categories]) => { - preExistingFiles[submissionId] = categories[fileCategory.name].map((file) => ({ - type: 'uploaded', + previousUploadFiles[submissionId] = categories[fileCategory.name].map((file) => ({ + type: 'previousUpload', fileId: file.fileId, name: file.name, - size: 0, })); }); - return { type: 'uploadCompleted', files: preExistingFiles }; + return { type: 'uploadCompleted', files: previousUploadFiles }; }); const [isDragging, setIsDragging] = useState(false); @@ -450,7 +454,11 @@ const FileListItem: FC = ({ file }) => {
{file.name} - ({formatFileSize(file.size)}) + {file.type === 'previousUpload' ? ( + (previous upload) + ) : ( + ({formatFileSize(file.size)}) + )} {showProgress && {percentage}%}
{/* Status icon */} @@ -474,6 +482,7 @@ const getStatusIcon = (status: UploadStatus) => { switch (status) { case 'pending': return ; + case 'previousUpload': case 'uploaded': return ; case 'error': From f2f91ff99d896270f00566e9f633b827270e1ef5 Mon Sep 17 00:00:00 2001 From: tombch Date: Mon, 15 Jun 2026 15:53:47 +0200 Subject: [PATCH 5/6] Locked file mapping prefill to revision only for now, and fixed bugs with discard files and accessing default files --- website/src/components/Edit/EditPage.tsx | 2 +- .../Submission/FileUpload/FolderUploadComponent.tsx | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/website/src/components/Edit/EditPage.tsx b/website/src/components/Edit/EditPage.tsx index a11064a647..71278c17d2 100644 --- a/website/src/components/Edit/EditPage.tsx +++ b/website/src/components/Edit/EditPage.tsx @@ -59,7 +59,7 @@ const InnerEditPage: FC = ({ const isCreatingRevision = dataToEdit.status === approvedForReleaseStatus; const extraFilesEnabled = submissionDataTypes.files?.enabled ?? false; const [fileMapping, setFileMapping] = useState(() => - extraFilesEnabled && dataToEdit.submittedData.files + isCreatingRevision && extraFilesEnabled && dataToEdit.submittedData.files ? { [dataToEdit.submissionId]: dataToEdit.submittedData.files } : undefined, ); diff --git a/website/src/components/Submission/FileUpload/FolderUploadComponent.tsx b/website/src/components/Submission/FileUpload/FolderUploadComponent.tsx index 4f79454541..f1704258c3 100644 --- a/website/src/components/Submission/FileUpload/FolderUploadComponent.tsx +++ b/website/src/components/Submission/FileUpload/FolderUploadComponent.tsx @@ -108,7 +108,8 @@ export const FolderUploadComponent: FC = ({ const previousUploadFiles: Record = {}; Object.entries(fileMapping).forEach(([submissionId, categories]) => { - previousUploadFiles[submissionId] = categories[fileCategory.name].map((file) => ({ + const fileCategoryFiles = categories[fileCategory.name] ?? []; + previousUploadFiles[submissionId] = fileCategoryFiles.map((file) => ({ type: 'previousUpload', fileId: file.fileId, name: file.name, @@ -411,7 +412,7 @@ export const FolderUploadComponent: FC = ({