From a4cdfef620a2009d05bd7995cfe3d362736693ab Mon Sep 17 00:00:00 2001 From: Prakhar Trivedi Date: Thu, 14 Aug 2025 16:27:15 +0800 Subject: [PATCH 1/9] updated sidebar to have upload link --- src/components/Sidebar.jsx | 14 ++++++++------ src/pages/DataImport.jsx | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/components/Sidebar.jsx b/src/components/Sidebar.jsx index fc42c5f..a041d3a 100644 --- a/src/components/Sidebar.jsx +++ b/src/components/Sidebar.jsx @@ -1,14 +1,14 @@ import { Badge, Box, Button, CloseButton, Drawer, Image, Portal, Span, Text, VStack } from '@chakra-ui/react' -import React, { useState } from 'react' +import { useState } from 'react' import { useSelector } from 'react-redux' import logoVisual from '../assets/logoVisual.svg'; import { useLocation, useNavigate } from 'react-router-dom'; -import { CiGrid32 } from 'react-icons/ci'; import { GrCatalogOption } from 'react-icons/gr'; -import { MdAdminPanelSettings, MdOutlinePublic } from 'react-icons/md'; +import { MdAdminPanelSettings } from 'react-icons/md'; import { FaPeopleGroup } from 'react-icons/fa6'; import { CgProfile } from 'react-icons/cg'; import { TbLogin2 } from 'react-icons/tb'; +import { FiUploadCloud } from 'react-icons/fi'; function Sidebar({ isOpen, onOpenChange }) { const navigate = useNavigate(); @@ -45,13 +45,15 @@ function Sidebar({ isOpen, onOpenChange }) { - } - {username && ( + + - )} + } {superuser === true && ( + @@ -43,7 +42,6 @@ function ImportMenuDialog({ pendingBatches, fetchBatches }) { b.id === targetBatchID) || null} setBatchID={(id) => setTargetBatchID(id)} - onClose={handleClose} fetchBatches={fetchBatches} onBackToPending={() => setView("pendingList")} />} @@ -57,18 +55,14 @@ function ImportMenuDialog({ pendingBatches, fetchBatches }) { - Pending Batches + {pendingBatches.length === 0 ? "No pending batches": "Pending Batches"} - {pendingBatches.length === 0 ? ( - - No pending batches. - - ) : ( + {pendingBatches.length > 0 && ( {pendingBatches.map((batch) => ( a.stage === "processed").length; const unprocessed = artefacts.filter(a => a.stage === "unprocessed").length; + const confirmed = artefacts.filter(a => a.stage === "confirmed").length; + const integrated = artefacts.filter(a => a.stage === "integrated").length; const total = processed + unprocessed return { id, ...batch, - artefactSummary: { processed, total }, + artefactSummary: { processed, total, confirmed, integrated }, }; }); @@ -133,7 +135,6 @@ function DataImport() { setBatches(sorted); setBatchNumberMap(batchNumberMap); setPendingBatches(pendingBatches); - } else { throw new Error("Unexpected response format"); } @@ -272,18 +273,7 @@ function DataImport() { filtered.map((batch, idx) => ( console.log("Clicked batch", batch.id)} + batchData={batch} /> )) From 399bfb7f3b5d28bda257c323dc239da4ce6a7424 Mon Sep 17 00:00:00 2001 From: Prakhar Trivedi Date: Thu, 14 Aug 2025 17:19:28 +0800 Subject: [PATCH 3/9] simplified stages --- src/pages/DataImport.jsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/pages/DataImport.jsx b/src/pages/DataImport.jsx index 0f8377b..736b01e 100644 --- a/src/pages/DataImport.jsx +++ b/src/pages/DataImport.jsx @@ -12,8 +12,7 @@ import CentredSpinner from "../components/CentredSpinner.jsx"; const stageCollection = createListCollection({ items: [ { label: "Pending", value: "upload_pending" }, - { label: "Unprocessed", value: "unprocessed" }, - { label: "Processed", value: "processed" }, + { label: "Processing", value: "unprocessed" }, { label: "Vetting", value: "vetting" }, { label: "Integration", value: "integration" }, { label: "Completed", value: "completed" }, @@ -65,12 +64,11 @@ function DataImport() { const { loaded } = useSelector(state => state.auth); - const stages = ["upload_pending", "unprocessed", "processed", "vetting", "integration", "completed"]; + const stages = ["upload_pending", "unprocessed", "vetting", "integration", "completed"]; const stageLabels = { upload_pending: "Pending", - unprocessed: "Unprocessed", - processed: "Processed", + unprocessed: "Processing", vetting: "Vetting", integration: "Integration", completed: "Completed", From 54a9a81fcdae2a37170fb528332628d2132f393b Mon Sep 17 00:00:00 2001 From: Prakhar Trivedi Date: Thu, 14 Aug 2025 18:22:50 +0800 Subject: [PATCH 4/9] reworked batch card rendering, added confirmBatch request --- src/components/DataImport/BatchCard.jsx | 202 ++++++++++++++++++------ src/pages/DataImport.jsx | 6 +- 2 files changed, 156 insertions(+), 52 deletions(-) diff --git a/src/components/DataImport/BatchCard.jsx b/src/components/DataImport/BatchCard.jsx index 71d8451..e8cec4d 100644 --- a/src/components/DataImport/BatchCard.jsx +++ b/src/components/DataImport/BatchCard.jsx @@ -1,11 +1,28 @@ import { HStack, VStack, Text, Image, Button, Card, Progress, useBreakpointValue, Flex, Box } from "@chakra-ui/react"; +import { useEffect, useState } from "react"; +import server, { JSONResponse } from '../../networking'; +import ToastWizard from "../toastWizard"; -function BatchCard({ batchData }) { - // const status = isCancelled - // ? { label: "Cancelled", color: "red.500" } - // : isProcessing - // ? { label: "Processing", color: "blue.500" } - // : { label: "Done", color: "green.500" }; +function BatchCard({ batchData, formatTimestamp, fetchBatches }) { + const statusColours = { + "upload_pending": "yellow.500", + "processing": "blue.500", + "vetting": "orange.500", + "integration": "purple.500", + "completed": "green.500", + "cancelled": "red.500" + } + + const [content, setContent] = useState({ + "displayMessage": null, + "timestamp": formatTimestamp(batchData.created), + "progress": null, + "name": batchData.name || "No name", + "statusColour": null, + "statusText": null, + "buttonText": null, + "buttonLoadingText": '' + }); const isMobile = useBreakpointValue({ base: true, md: false }); const imageWidth = useBreakpointValue({ base: "80px", md: "120px" }); @@ -14,10 +31,100 @@ function BatchCard({ batchData }) { const buttonMinWidth = useBreakpointValue({ base: "80px", md: "100px" }); const infoMaxWidth = useBreakpointValue({ base: "180px", md: "400px" }); const fontSize = useBreakpointValue({ base: "sm", md: "md" }); + const [requestInProgress, setRequestInProgress] = useState(false); + + const handleButtonClick = async () => { + if (content.buttonText === "Confirm Batch") { + setRequestInProgress(true); + + try { + const response = await server.post('/dataImport/confirm', { + batchID: batchData.id + }) + + if (response.data instanceof JSONResponse) { + if (response.data.isErrorStatus()) { + const errObject = { + response: { + data: response.data + } + }; + throw new Error(errObject); + } + + // Success case + if (fetchBatches) { + fetchBatches(); + } + ToastWizard.standard("success", `Processing for '${batchData.name || 'Unknown'}' started`, "Batch was confirmed successfully. No more artefacts can be added."); + } else { + throw new Error("Unexpected response format"); + } + } catch (err) { + if (err.response && err.response.data instanceof JSONResponse) { + console.log("Error response in batch confirmation request:", err.response.data.fullMessage()); + if (err.response.data.userErrorType()) { + ToastWizard.standard("error", "Batch confirmation failed.", err.response.data.message); + } else { + ToastWizard.standard("error", "Something went wrong", "Couldn't confirm batch. Please try again.") + } + } else { + console.log("Unexpected error in batch confirmation request:", err); + ToastWizard.standard("error", "Something went wrong", "Couldn't confirm batch. Please try again.") + } + } finally { + setRequestInProgress(false); + } + return; + } + } - console.log(batchData) + useEffect(() => { + if (batchData) { + var newContent = { ...content }; + if (batchData.stage === "upload_pending") { + newContent.displayMessage = `${batchData.artefactSummary.total || 0} Uploaded (Upload more if you wish)` + // newContent.progress = Math.round((batchData.artefactSummary.unprocessed || 0) / (batchData.artefactSummary.total || 1) * 100); + newContent.progress = null; + newContent.statusColour = statusColours.upload_pending; + newContent.statusText = "Pending"; + newContent.buttonText = "Confirm Batch"; + newContent.buttonLoadingText = "Confirming..."; + } else if (batchData.stage === "unprocessed") { + newContent.displayMessage = `${batchData.artefactSummary.processed || 0}/${batchData.artefactSummary.total || 0} Processed` + newContent.progress = Math.round((batchData.artefactSummary.processed || 0) / (batchData.artefactSummary.total || 1) * 100); + newContent.statusColour = statusColours.processing; + newContent.statusText = "Processing"; + newContent.buttonText = null; + newContent.buttonLoadingText = ''; + } else if (batchData.stage === "vetting") { + newContent.displayMessage = `${batchData.artefactSummary.confirmed || 0}/${batchData.artefactSummary.total || 0} Confirmed` + newContent.progress = Math.round((batchData.artefactSummary.confirmed || 0) / (batchData.artefactSummary.total || 1) * 100); + newContent.statusColour = statusColours.vetting; + newContent.statusText = "Vetting"; + newContent.buttonText = batchData.artefactSummary.confirmed == batchData.artefactSummary.total ? 'Start Integration' : 'Continue Vetting'; + newContent.buttonLoadingText = 'Continue Vetting'; + } else if (batchData.stage === "integration") { + newContent.displayMessage = `${batchData.artefactSummary.integrated || 0}/${batchData.artefactSummary.total || 0} Integrated` + newContent.progress = Math.round((batchData.artefactSummary.integrated || 0) / (batchData.artefactSummary.total || 1) * 100); + newContent.statusColour = statusColours.integration; + newContent.statusText = "Integrating"; + newContent.buttonText = null; + newContent.buttonLoadingText = ''; + } else if (batchData.stage === "completed") { + newContent.displayMessage = `${batchData.artefactSummary.integrated || 0}/${batchData.artefactSummary.total || 0} Completed` + newContent.progress = Math.round((batchData.artefactSummary.integrated || 0) / (batchData.artefactSummary.total || 1) * 100); + newContent.statusColour = statusColours.completed; + newContent.statusText = "Completed"; + newContent.buttonText = null; + newContent.buttonLoadingText = ''; + } - return

WIP

+ setContent(newContent); + } + }, [batchData]); + + console.log(content); return ( @@ -26,56 +133,52 @@ function BatchCard({ batchData }) { - {batchName} + {content.name} - {displayMessage} + {content.displayMessage} - {timestamp} + {content.timestamp} - - {status.label} + + {content.statusText} - - + + - {progress}% + {content.progress}% - + {content.buttonText && + + } ) : ( {/* 1. Thumbnail */} - {batchName} + {content.name} - {displayMessage} | {timestamp} + {content.displayMessage || "No message"} | {content.timestamp || "Unknown Timestamp"}
{/* 3. Progress */} - - + + - {progress}% + {content.progress}% @@ -122,23 +219,28 @@ function BatchCard({ batchData }) { {/* 4. Status */} - {status.label} + {content.statusText || "Unknown"} {/* 5. Button */} - + {content.buttonText && + + } )} diff --git a/src/pages/DataImport.jsx b/src/pages/DataImport.jsx index 736b01e..dff2c0e 100644 --- a/src/pages/DataImport.jsx +++ b/src/pages/DataImport.jsx @@ -107,12 +107,12 @@ function DataImport() { const unprocessed = artefacts.filter(a => a.stage === "unprocessed").length; const confirmed = artefacts.filter(a => a.stage === "confirmed").length; const integrated = artefacts.filter(a => a.stage === "integrated").length; - const total = processed + unprocessed + const total = artefacts.length; return { id, ...batch, - artefactSummary: { processed, total, confirmed, integrated }, + artefactSummary: { processed, total, confirmed, integrated, unprocessed }, }; }); @@ -272,6 +272,8 @@ function DataImport() { )) From 3b4d00cc0e1d360f777b7f90b18ed9af51d2d03d Mon Sep 17 00:00:00 2001 From: Prakhar Trivedi Date: Thu, 14 Aug 2025 18:31:24 +0800 Subject: [PATCH 5/9] added action for continue vetting --- src/components/DataImport/BatchCard.jsx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/components/DataImport/BatchCard.jsx b/src/components/DataImport/BatchCard.jsx index e8cec4d..d90a81b 100644 --- a/src/components/DataImport/BatchCard.jsx +++ b/src/components/DataImport/BatchCard.jsx @@ -2,8 +2,11 @@ import { HStack, VStack, Text, Image, Button, Card, Progress, useBreakpointValue import { useEffect, useState } from "react"; import server, { JSONResponse } from '../../networking'; import ToastWizard from "../toastWizard"; +import { useNavigate } from "react-router-dom"; function BatchCard({ batchData, formatTimestamp, fetchBatches }) { + const navigate = useNavigate(); + const statusColours = { "upload_pending": "yellow.500", "processing": "blue.500", @@ -76,9 +79,21 @@ function BatchCard({ batchData, formatTimestamp, fetchBatches }) { setRequestInProgress(false); } return; + } else if (content.buttonText === "Continue Vetting") { + for (const artefactID in batchData.artefacts) { + if (batchData.artefacts[artefactID].stage !== "confirmed") { + navigate(`/studio/${batchData.id}/${artefactID}`); + return; + } + } + + ToastWizard.standard("info", "Refresh page", "All artefacts in this batch have already been confirmed."); + return; } } + console.log(batchData) + useEffect(() => { if (batchData) { var newContent = { ...content }; From 146e51369f18c44974fb765a29aa99c4d2b9fe52 Mon Sep 17 00:00:00 2001 From: Prakhar Trivedi Date: Thu, 14 Aug 2025 18:38:32 +0800 Subject: [PATCH 6/9] added start integration click handling --- src/components/DataImport/BatchCard.jsx | 45 +++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/src/components/DataImport/BatchCard.jsx b/src/components/DataImport/BatchCard.jsx index d90a81b..55a31ee 100644 --- a/src/components/DataImport/BatchCard.jsx +++ b/src/components/DataImport/BatchCard.jsx @@ -89,6 +89,47 @@ function BatchCard({ batchData, formatTimestamp, fetchBatches }) { ToastWizard.standard("info", "Refresh page", "All artefacts in this batch have already been confirmed."); return; + } else if (content.buttonText === "Start Integration") { + setRequestInProgress(true); + + try { + const response = await server.post('/dataImport/integration/start', { + batchID: batchData.id + }) + + if (response.data instanceof JSONResponse) { + if (response.data.isErrorStatus()) { + const errObject = { + response: { + data: response.data + } + }; + throw new Error(errObject); + } + + // Success case + if (fetchBatches) { + fetchBatches(); + } + ToastWizard.standard("success", `Integration for '${batchData.name || 'Unknown'}' started`, "Batch is now being integrated. This may take some time."); + } else { + throw new Error("Unexpected response format"); + } + } catch (err) { + if (err.response && err.response.data instanceof JSONResponse) { + console.log("Error response in batch start integration request:", err.response.data.fullMessage()); + if (err.response.data.userErrorType()) { + ToastWizard.standard("error", "Integration request failed.", err.response.data.message); + } else { + ToastWizard.standard("error", "Something went wrong", "Couldn't start integration. Please try again.") + } + } else { + console.log("Unexpected error in batch start integration request:", err); + ToastWizard.standard("error", "Something went wrong", "Couldn't start integration. Please try again."); + } + } finally { + setRequestInProgress(false); + } } } @@ -148,7 +189,7 @@ function BatchCard({ batchData, formatTimestamp, fetchBatches }) { Date: Thu, 14 Aug 2025 18:44:40 +0800 Subject: [PATCH 7/9] added handling for view details --- src/components/DataImport/BatchCard.jsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/DataImport/BatchCard.jsx b/src/components/DataImport/BatchCard.jsx index 55a31ee..d459017 100644 --- a/src/components/DataImport/BatchCard.jsx +++ b/src/components/DataImport/BatchCard.jsx @@ -130,6 +130,10 @@ function BatchCard({ batchData, formatTimestamp, fetchBatches }) { } finally { setRequestInProgress(false); } + } else if (content.buttonText === "View Details") { + navigate(`/studio/${batchData.id}`); + } else { + ToastWizard.standard("info", "No action available", "This batch is in a state that does not require any further action at this time."); } } @@ -172,7 +176,7 @@ function BatchCard({ batchData, formatTimestamp, fetchBatches }) { newContent.progress = Math.round((batchData.artefactSummary.integrated || 0) / (batchData.artefactSummary.total || 1) * 100); newContent.statusColour = statusColours.completed; newContent.statusText = "Completed"; - newContent.buttonText = null; + newContent.buttonText = "View Details"; newContent.buttonLoadingText = ''; } From 853db4612d666146d7aff9ddd5356c7a5e0fb734 Mon Sep 17 00:00:00 2001 From: Prakhar Trivedi Date: Thu, 14 Aug 2025 18:45:54 +0800 Subject: [PATCH 8/9] removed raw query param from batch preview link --- src/components/DataImport/BatchCard.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/DataImport/BatchCard.jsx b/src/components/DataImport/BatchCard.jsx index d459017..720b246 100644 --- a/src/components/DataImport/BatchCard.jsx +++ b/src/components/DataImport/BatchCard.jsx @@ -193,7 +193,7 @@ function BatchCard({ batchData, formatTimestamp, fetchBatches }) { Date: Fri, 15 Aug 2025 11:18:50 +0800 Subject: [PATCH 9/9] fixed loading text at the point where starting integration is available --- src/components/DataImport/BatchCard.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/DataImport/BatchCard.jsx b/src/components/DataImport/BatchCard.jsx index 720b246..e9121a9 100644 --- a/src/components/DataImport/BatchCard.jsx +++ b/src/components/DataImport/BatchCard.jsx @@ -163,7 +163,7 @@ function BatchCard({ batchData, formatTimestamp, fetchBatches }) { newContent.statusColour = statusColours.vetting; newContent.statusText = "Vetting"; newContent.buttonText = batchData.artefactSummary.confirmed == batchData.artefactSummary.total ? 'Start Integration' : 'Continue Vetting'; - newContent.buttonLoadingText = 'Continue Vetting'; + newContent.buttonLoadingText = 'Starting...'; } else if (batchData.stage === "integration") { newContent.displayMessage = `${batchData.artefactSummary.integrated || 0}/${batchData.artefactSummary.total || 0} Integrated` newContent.progress = Math.round((batchData.artefactSummary.integrated || 0) / (batchData.artefactSummary.total || 1) * 100);