Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
199 changes: 199 additions & 0 deletions src/components/Catalogue/createGroup.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
import { useState } from "react";
import { Button, CloseButton, Dialog, Portal, Field, Input, Text, Box, useBreakpointValue } from "@chakra-ui/react";
import { Tooltip } from "../../components/ui/tooltip"
import { HiInformationCircle, HiPlus } from "react-icons/hi2";
import server, { JSONResponse } from "../../networking";
import ToastWizard from "../toastWizard";
import GroupTypeToggle from "./groupTypeToggle";

function CreateGroup({ fetchCatalogue }) {
const [groupType, setGroupType] = useState("book");
const [title, setTitle] = useState("");
const [subtitle, setSubtitle] = useState("");
const [isCreating, setIsCreating] = useState(false);
const [isOpen, setIsOpen] = useState(false);
const isMobile = useBreakpointValue({ base: true, md: false });

const validateForm = () => {
const errors = [];

if (!title.trim()) {
errors.push("Title is required");
} else if (title.trim().length > 25) {
errors.push("Title must be 25 characters or less");
}

if (!subtitle.trim()) {
errors.push("Subtitle is required");
} else if (subtitle.trim().length > 100) {
errors.push("Subtitle must be 100 characters or less");
}

return errors;
};

const handleCreateGroup = async () => {
const validationErrors = validateForm();
if (validationErrors.length > 0) {
ToastWizard.standard(
"error",
"All fields must be filled!",
validationErrors.join(". ")
);
return;
}

setIsCreating(true);
try {
const payload = {
title: title.trim(),
subtitle: subtitle.trim(),
type: groupType,
};

const response = await server.post("/studio/group/create", payload);

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

// Success case
ToastWizard.standard(
"success",
"Group created successfully"
);

setTitle("");
setSubtitle("");
setGroupType("book");
setIsOpen(false);
fetchCatalogue()
} else {
throw new Error("Unexpected response format");
}
} catch (err) {
if (err.response && err.response.data instanceof JSONResponse) {
console.log("Error response in login request:", err.response.data.fullMessage());
if (err.response.data.userErrorType()) {
ToastWizard.standard("error", "Group creation failed", err.response.data.message);
} else {
ToastWizard.standard("error", "Group creation failed", "Something went wrong while creating your group. Please try again later.", 3000, true, handleCreateGroup, 'Retry');
}
} else {
console.log("Unexpected error in login request:", err);
ToastWizard.standard("error", "Group creation failed", "Something went wrong while creating your group. Please try again later.", 3000, true, handleCreateGroup, 'Retry');
}
setIsCreating(false);
}
};

const isTitleValid = title.trim().length > 0 && title.trim().length <= 25;
const isSubtitleValid = subtitle.trim().length > 0 && subtitle.trim().length <= 100;
const isFormValid = isTitleValid && isSubtitleValid;

return (
<Dialog.Root
placement="center"
open={isOpen}
onOpenChange={(e) => setIsOpen(e.open)}
>
<Dialog.Trigger asChild>
<Button variant="ArchPrimary" colorScheme="blue">
<HiPlus /> {!isMobile ? "Create Group" : ""}
</Button>
</Dialog.Trigger>
<Portal>
<Dialog.Backdrop />
<Dialog.Positioner>
<Dialog.Content>
<Dialog.Header>
<Dialog.Title display={"flex"} flexDir={"row"} alignItems={"center"}>
<Text mr={2}>Create New Group</Text>
<Tooltip
showArrow
content='Books contain Meeting Minutes, Category contain artefacts with Human Figures'
positioning={{ placement: "right" }}
closeDelay={150}
openDelay={150}
contentProps={{ css: { "--tooltip-bg": "#12144C" } }}
>
<HiInformationCircle size={20} />
</Tooltip>
</Dialog.Title>
<Dialog.CloseTrigger asChild>
<CloseButton size="sm" />
</Dialog.CloseTrigger>
</Dialog.Header>
<Dialog.Body>
<Box display={"flex"} flexDirection={"column"} alignItems={"center"}>
<GroupTypeToggle
value={groupType}
onChange={setGroupType}
/>
</Box>

<Field.Root w="100%">
<Field.Label>
{groupType === "book"
? "Book Title"
: "Category Name"}
</Field.Label>
<Input
placeholder={`Enter ${groupType} title`}
value={title}
onChange={(e) => setTitle(e.target.value)}
maxLength={25}
/>
<Text fontSize="xs" color="gray.500" mt={1}>
{title.length}/25 characters
</Text>
</Field.Root>

<Field.Root mt={6} w="100%">
<Field.Label>
{groupType === "book"
? "Subtitle"
: "Description"}
</Field.Label>
<Input
placeholder={`Enter ${groupType} details`}
value={subtitle}
onChange={(e) =>
setSubtitle(e.target.value)
}
maxLength={100}
/>
<Text fontSize="xs" color="gray.500" mt={1}>
{subtitle.length}/100 characters
</Text>
</Field.Root>
</Dialog.Body>
<Dialog.Footer>
<Dialog.ActionTrigger asChild>
<Button variant="outline">Cancel</Button>
</Dialog.ActionTrigger>
<Button
variant="ArchPrimary"
disabled={!isFormValid}
onClick={handleCreateGroup}
loading={isCreating}
loadingText="Creating..."
>
Create
{groupType === "book" ? " Book" : " Category"}
</Button>
</Dialog.Footer>
</Dialog.Content>
</Dialog.Positioner>
</Portal>
</Dialog.Root>
);
}

export default CreateGroup;
73 changes: 73 additions & 0 deletions src/components/Catalogue/groupTypeToggle.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { HStack, SegmentGroup, Text, useBreakpointValue } from "@chakra-ui/react";
import { LuBook, LuTag } from "react-icons/lu";

const GroupTypeToggle = ({ value, onChange, size = "md" }) => {
const isMobile = useBreakpointValue({ base: true, md: false });

const items = [
{
value: "book",
label: (
<HStack spacing={2} justify="center" color={value === "book" ? "white" : "black"}>
<LuBook color={value === "book" ? "white" : "black"} />
<Text fontWeight={value === "book" ? "bold" : "normal"}>
Book
</Text>
</HStack>
),
},
{
value: "category",
label: (
<HStack spacing={2} justify="center" color={value === "category" ? "white" : "black"}>
<LuTag color={value === "category" ? "white" : "black"} />
<Text fontWeight={value === "category" ? "bold" : "normal"}>
Category
</Text>
</HStack>
),
},
];

const segmentProps = {
sm: {
p: "1",
rounded: "lg",
width: "180px"
},
md: {
p: "1",
rounded: "lg",
width: "210px"
},
lg: {
p: "1.5",
rounded: "lg",
width: "240px"
}
};

const currentProps = segmentProps[size] || segmentProps.md;

return (
<SegmentGroup.Root
value={value}
onValueChange={({ value }) => onChange(value)}
size={size}
bg="gray.50"
p={currentProps.p}
rounded={currentProps.rounded}
boxShadow="md"
gap="1"
mb={6}
>
<SegmentGroup.Indicator bg="primaryColour" />
<SegmentGroup.Items
w={currentProps.width}
items={items}
/>
</SegmentGroup.Root>
);
};

export default GroupTypeToggle;
15 changes: 13 additions & 2 deletions src/components/Catalogue/metadataDisplay.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,11 @@ function MetadataDisplay({ currentItem, isOpen }) {
throw errObject;
}

// Success case
setMetadata(response.data.raw.data.metadata);
setMetadata({
...response.data.raw.data.metadata,
figureInfo: response.data.raw.figureInfo || {}
});

} else {
throw new Error("Unexpected response format");
}
Expand Down Expand Up @@ -279,6 +282,14 @@ function MetadataDisplay({ currentItem, isOpen }) {
}
loading="eager"
/>
<Text
mt={2}
fontSize={10}
lineClamp={1}
title={metadata.figureInfo[id]}
>
{metadata.figureInfo[id] || "No Label"}
</Text>
</Box>
))}
</Flex>
Expand Down
Loading