Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
a3dc77f
Fixed path routes for group view and artefact editor
JunHammy Aug 11, 2025
3aa6174
Added fetch metadata request for artefact editor
JunHammy Aug 11, 2025
ee52930
Added rough layout for artefact editor
JunHammy Aug 12, 2025
55eed68
Fixed metadataDisplay to set metadata based on response.data.raw.data…
JunHammy Aug 12, 2025
8dba01e
Added back arrow and chevrons for artefact editor
JunHammy Aug 12, 2025
a09a706
Added image rendering for AE
JunHammy Aug 12, 2025
7c5f36c
Fixed sizing and layout in AE
JunHammy Aug 12, 2025
512c63e
added editorCard
JunHammy Aug 12, 2025
f046b57
Added title and button for editCard
JunHammy Aug 12, 2025
8b2d040
Shifted addInfo in metadatadisplay to be before the figure headshot
JunHammy Aug 12, 2025
aa43d21
Added metadata toggle
JunHammy Aug 12, 2025
fd57f37
Added rendering of name and mm transcription using text area
JunHammy Aug 12, 2025
24e157d
Added displaying of all transcriptions
JunHammy Aug 12, 2025
8b80ffa
Added artefact editor action bar
JunHammy Aug 13, 2025
48d1358
Change icon size
JunHammy Aug 13, 2025
27ed57f
Added edit button
JunHammy Aug 13, 2025
2b67068
Added icon to edit button
JunHammy Aug 13, 2025
682cff0
Added feedback toasts and edit button for editor card
JunHammy Aug 13, 2025
b9f42ea
changed informatin toast
JunHammy Aug 13, 2025
3017382
Implemented better and cleaner layout for HF editor card
JunHammy Aug 13, 2025
4f00109
Added remove figure function
JunHammy Aug 13, 2025
f64b261
Added dialog to confirm delete of HF for artefact figure association
JunHammy Aug 13, 2025
e588914
Added highlighted text
JunHammy Aug 13, 2025
b80bd3e
Removed debug console logs
JunHammy Aug 13, 2025
c4f589d
Minor fixes to the Artefact Editor
JunHammy Aug 13, 2025
b832e0a
Fixed variable names and removed field.root tags
JunHammy Aug 13, 2025
ff45a8e
Changed validation for artefact editor action bar
JunHammy Aug 13, 2025
fd85709
Removed debug console log
JunHammy Aug 13, 2025
eeb9cf4
Merge branch 'main' into junhan
Prakhar896 Aug 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions src/components/Catalogue/metadataDisplay.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ function MetadataDisplay({ currentItem, isOpen }) {
}

// Success case
setMetadata(response.data.raw.data);
setMetadata(response.data.raw.data.metadata);
} else {
throw new Error("Unexpected response format");
}
Expand Down Expand Up @@ -245,6 +245,14 @@ function MetadataDisplay({ currentItem, isOpen }) {
<Text fontWeight="bold" mb={1}>Caption:</Text>
<Text mb={6}>{metadata.caption || "N/A"}</Text>

{/* Additional figure metadata, if present */}
{metadata.addInfo && (
<>
<Text fontWeight="bold" mb={1}>Additional Info:</Text>
<Text>{metadata.addInfo}</Text>
</>
)}

{/* Figure headshots (if available) */}
{Array.isArray(metadata.figureIDs) && metadata.figureIDs.length > 0 ? (
<Flex wrap="wrap" gap={4} mb={4}>
Expand Down Expand Up @@ -277,14 +285,6 @@ function MetadataDisplay({ currentItem, isOpen }) {
) : (
<Text mb={2}>N/A</Text>
)}

{/* Additional figure metadata, if present */}
{metadata.addInfo && (
<>
<Text fontWeight="bold" mb={1}>Additional Info:</Text>
<Text>{metadata.addInfo}</Text>
</>
)}
</>
)}
</Box>
Expand Down
2 changes: 1 addition & 1 deletion src/components/Catalogue/transcriptionToggle.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ const TranscriptionToggle = ({ value, onChange }) => {
p="1"
rounded="lg"
boxShadow="sm"
gap="00"
gap="0"
>
<SegmentGroup.Indicator bg="primaryColour" rounded="lg" />
<SegmentGroup.Items items={items} />
Expand Down
222 changes: 222 additions & 0 deletions src/components/DataStudio/artefactEditorActionBar.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
import { ActionBar, Button, Dialog, Portal } from '@chakra-ui/react'
import { isEqual } from 'lodash';
import { useEffect, useState } from 'react'
import ToastWizard from '../toastWizard';
import server, { JSONResponse } from '../../networking';

function ArtefactEditorActionBar({ artefactData, originalArtefactData, setArtefactData, getArtefactData, artefactId, isEditing, setIsEditing, isMMArtefact }) {
const [changesMade, setChangesMade] = useState(false);
const [isConfirmSaveOpen, setIsConfirmSaveOpen] = useState(false);
const [saveLoading, setSaveLoading] = useState(false);

useEffect(() => {
setChangesMade(!isEqual(artefactData, originalArtefactData) && isEditing);
}, [artefactData, originalArtefactData, isEditing])

// Validation functions
const validateField = (fieldName, value) => {
if (value === null || value === undefined) return true;

switch (fieldName) {
case 'name':
return typeof value === 'string' && value.trim().length >= 1 && value.trim().length <= 25;
case 'tradCN':
case 'simplifiedCN':
return typeof value === 'string' && value.trim().length >= 1 && value.trim().length <= 1000;
case 'caption':
return typeof value === 'string' && value.trim().length >= 1 && value.trim().length <= 200;
case 'english':
case 'summary':
return typeof value === 'string' && value.trim().length >= 1 && value.trim().length <= 2000;
case 'addInfo':
return typeof value === 'string' && value.trim().length <= 3000;
default:
return true;
}
};

const validateUpdateDict = (updateDict) => {
const errors = [];

// Common validation
if (!validateField('name', updateDict.name)) {
errors.push('Name must be between 1-25 characters');
}

if (isMMArtefact) {
// MM-type validation
if (updateDict.tradCN && !validateField('tradCN', updateDict.tradCN)) errors.push('Traditional Chinese must be between 1-1000 characters')
if (updateDict.simplifiedCN && !validateField('simplifiedCN', updateDict.simplifiedCN)) errors.push('Simplified Chinese must be between 1-1000 characters')
if (updateDict.english && !validateField('english', updateDict.english)) errors.push('English must be between 1-2000 characters')
if (updateDict.summary && !validateField('summary', updateDict.summary)) errors.push('Summary must be between 1-2000 characters')
} else {
// HF-type validation
if (updateDict.caption && !validateField('caption', updateDict.caption)) errors.push('Caption must be between 1-200 characters')
if (updateDict.addInfo && !validateField('addInfo', updateDict.addInfo)) errors.push('Additional Info must be 3000 characters or less')
}

return errors;
};

const handleCancelChanges = () => {
setArtefactData(originalArtefactData);
ToastWizard.standard("warning", "Changes Reset", "All changes reverted.", 3000);
}

const handleSaveChanges = async () => {
setSaveLoading(true);

var updateDict = {
artefactID: artefactId,
name: artefactData.name || '',
};

// Add fields based on artefact type
if (isMMArtefact) {
// MM-type metadata fields
if (artefactData.metadata.tradCN != originalArtefactData.tradCN) updateDict.tradCN = artefactData.metadata.tradCN
if (artefactData.metadata.simplifiedCN != originalArtefactData.simplifiedCN) updateDict.simplifiedCN = artefactData.metadata.simplifiedCN
if (artefactData.metadata.english != originalArtefactData.english) updateDict.english = artefactData.metadata.english
if (artefactData.metadata.summary != originalArtefactData.summary) updateDict.summary = artefactData.metadata.summary
} else {
// HF-type metadata fields
if (artefactData.metadata.caption != originalArtefactData.caption) updateDict.caption = artefactData.metadata.caption
if (artefactData.metadata.addInfo != originalArtefactData.addInfo) updateDict.addInfo = artefactData.metadata.addInfo
}

// Validate the update dictionary
const validationErrors = validateUpdateDict(updateDict);
if (validationErrors.length > 0) {
setSaveLoading(false);
ToastWizard.standard(
"error",
"Validation Error",
validationErrors.join('. '),
5000
);
return;
}

try {
const response = await server.post('/studio/artefact/update', updateDict);

if (response.data instanceof JSONResponse) {
if (response.data.isErrorStatus()) {
const errObject = {
response: {
data: response.data
}
};
throw new Error(errObject);
}

// Success case - handle both full success (200) and partial success (207)
getArtefactData();
setIsConfirmSaveOpen(false);
setIsEditing(false); // Exit editing mode after successful save

if (response.data.status === 207) {
// Partial success - metadata updated but NER labelling failed
ToastWizard.standard(
"warning",
"Artefact partially updated.",
5000,
true,
handleSaveChanges,
"Retry"
);
} else {
// Full success
ToastWizard.standard("success", "Artefact updated.", response.data.message);
}
} else {
throw new Error("Unexpected response format");
}
} catch (err) {
if (err.response && err.response.data instanceof JSONResponse) {
console.log("Error response in update artefact data request:", err.response.data.fullMessage());
if (err.response.data.userErrorType()) {
ToastWizard.standard(
"error",
"Artefact update failed.",
err.response.data.message,
3000,
true,
handleSaveChanges,
"Retry"
);
} else {
ToastWizard.standard(
"error",
"Artefact update failed.",
"Failed to update your artefact information. Please try again.",
3000,
true,
handleSaveChanges,
"Retry"
);
}
} else {
console.log("Unexpected error in artefact update request:", err);
ToastWizard.standard(
"error",
"Artefact update failed.",
"Failed to update your artefact information. Please try again.",
3000,
true,
handleSaveChanges,
"Retry"
);
}
}

setSaveLoading(false);
}

return (
<ActionBar.Root open={changesMade}>
<Portal>
<ActionBar.Positioner>
<ActionBar.Content>
<Button variant={'outline'} colorPalette={'red'} onClick={handleCancelChanges}>
Reset
</Button>

<Dialog.Root placement="center" open={isConfirmSaveOpen} onOpenChange={(e) => setIsConfirmSaveOpen(e.open)}>
<Dialog.Trigger asChild>
<Button variant={"outline"} colorPalette={'green'} size="sm">
Save Changes
</Button>
</Dialog.Trigger>
<Portal>
<Dialog.Backdrop />
<Dialog.Positioner>
<Dialog.Content>
<Dialog.Header>
<Dialog.Title>Update Artefact</Dialog.Title>
</Dialog.Header>
<Dialog.Body>
<Dialog.Description>
Are you sure you'd like to save these changes? This action cannot be undone.
</Dialog.Description>
</Dialog.Body>
<Dialog.Footer>
<Button variant={'solid'} colorPalette={'red'} onClick={() => setIsConfirmSaveOpen(false)}>
Cancel
</Button>
<Button bgColor={'primaryColour'} color={'white'} onClick={handleSaveChanges} loading={saveLoading} loadingText={'Saving...'}>
Save Changes
</Button>
</Dialog.Footer>
</Dialog.Content>
</Dialog.Positioner>
</Portal>
</Dialog.Root>
</ActionBar.Content>
</ActionBar.Positioner>
</Portal>
</ActionBar.Root>
)
}

export default ArtefactEditorActionBar
Loading