Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
0c6efc2
Merge pull request #31 from ArchAIve-Project/main
Prakhar896 Aug 13, 2025
b819874
added more icon for group card item
JunHammy Aug 14, 2025
ebd89a1
Added group item menu
JunHammy Aug 14, 2025
b464c78
added empty component manage associations dialog, enhanced functional…
JunHammy Aug 14, 2025
777eacb
Added basic structure for manageassociationdialog
JunHammy Aug 14, 2025
ad12652
Added fetching association
JunHammy Aug 14, 2025
2f88312
Merge pull request #36 from ArchAIve-Project/prakhar
Prakhar896 Aug 14, 2025
61e5499
Merge pull request #37 from ArchAIve-Project/main
Prakhar896 Aug 14, 2025
2ff0b6b
added association card
JunHammy Aug 14, 2025
bda186a
Added logic for manage associations
JunHammy Aug 14, 2025
49c4128
Linked manage assoc dialog to editor card
JunHammy Aug 14, 2025
b3363be
Updated itemChat
ZyuT0h Aug 14, 2025
aafb785
Minor additions for toast wizards and button colours
JunHammy Aug 14, 2025
3db8bc3
Handled long name text for card item
JunHammy Aug 14, 2025
f3c64ed
Fixed title size for catalogue item view
JunHammy Aug 14, 2025
1a0b818
Enhanced toast message for user error for group view
JunHammy Aug 14, 2025
2055d73
Modified to create msg object immediately added
ZyuT0h Aug 14, 2025
c959867
Merge pull request #38 from ArchAIve-Project/junhan
JunHammy Aug 14, 2025
82cbbae
removed initemchat send button nonexistent righticon attribute
Prakhar896 Aug 15, 2025
034a00c
Merge pull request #39 from ArchAIve-Project/zhengyu
Prakhar896 Aug 15, 2025
9638e21
Merge pull request #42 from ArchAIve-Project/prakhar
Prakhar896 Aug 15, 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
4 changes: 2 additions & 2 deletions src/components/Catalogue/cardComponent.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ const CardComponent = ({ imageSrc, itemTitle, itemDescription, expanded }) => {
alt={itemTitle}
loading={expanded ? "eager" : "lazy"}
onLoad={() => setIsImageLoaded(true)}
height="180px"
height="180px"
width="100%"
/>
</Skeleton>
<Card.Body>
<Skeleton loading={isLoading}>
<Card.Title>{itemTitle}</Card.Title>
<Card.Title lineClamp={1}>{itemTitle}</Card.Title>
</Skeleton>
{expanded && (
<Skeleton loading={isLoading} mt={2}>
Expand Down
62 changes: 31 additions & 31 deletions src/components/Catalogue/catalogueItemView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { IoOpen } from "react-icons/io5";
const MotionBox = motion.create(Box);
const MotionChevron = motion.create(Box);

const CatalogueItemView = ({ isOpen, onClose, title, sectionId, items, setDialogTitle, imageSrc }) => {
const CatalogueItemView = ({ isOpen, onClose, title, sectionId, items, setDialogTitle, imageSrc, artefactID }) => {
const [isNavigating, setIsNavigating] = useState(false);
const [direction, setDirection] = useState(0);
const [isInitialRender, setIsInitialRender] = useState(true);
Expand Down Expand Up @@ -92,19 +92,19 @@ const CatalogueItemView = ({ isOpen, onClose, title, sectionId, items, setDialog
debounceTimeout = setTimeout(() => {
const scrollLeft = scrollRef.current.scrollLeft;
const newIndex = Math.round(scrollLeft / window.innerWidth);

if (newIndex !== currentIndex && items[newIndex]) {
setDialogTitle(items[newIndex].title);
}

// Reset scrolling flag after debounce
isScrolling = false;
}, 100); // Keep the debounce time reasonable (100ms)
};

const scrollEl = scrollRef.current;
scrollEl.addEventListener("scroll", handleScroll);

return () => {
scrollEl.removeEventListener("scroll", handleScroll);
if (debounceTimeout) clearTimeout(debounceTimeout);
Expand Down Expand Up @@ -151,13 +151,13 @@ const CatalogueItemView = ({ isOpen, onClose, title, sectionId, items, setDialog
left: isMobile
? "10px"
: isTablet
? "calc(5vw - 50px)"
: "calc(10vw - 60px)",
? "calc(5vw - 50px)"
: "calc(10vw - 60px)",
right: isMobile
? "10px"
: isTablet
? "calc(5vw - 50px)"
: "calc(10vw - 60px)",
? "calc(5vw - 50px)"
: "calc(10vw - 60px)",
top: "50%",
};

Expand Down Expand Up @@ -356,21 +356,21 @@ const CatalogueItemView = ({ isOpen, onClose, title, sectionId, items, setDialog
gap={2}
>
<Text
fontSize="xl"
fontSize="lg"
fontWeight="bold"
>
{item.title}
</Text>

<Tooltip
showArrow
content="Open in Data Studio"
positioning={{ placement: "right-end" }}
closeDelay={150}
<Tooltip
showArrow
content="Open in Data Studio"
positioning={{ placement: "right-end" }}
closeDelay={150}
openDelay={150}
contentProps={{ css: { "--tooltip-bg": "#12144C" } }}
>
<IoOpen size={25} color="#12144C" onClick={() => navigate(`/studio/${sectionId}/${currentId}`)} cursor={"pointer"}/>
<IoOpen size={25} color="#12144C" onClick={() => navigate(`/studio/${sectionId}/${currentId}`)} cursor={"pointer"} />
</Tooltip>
</Flex>

Expand All @@ -394,14 +394,14 @@ const CatalogueItemView = ({ isOpen, onClose, title, sectionId, items, setDialog

<Box flex={1} mt={4}>
{selectedSegment ===
"metadata" ? (
"metadata" ? (
<MetadataDisplay
currentItem={items[currentIndex]}
currentItem={items[currentIndex]}
isOpen={isOpen}
/>
) : selectedSegment ===
"chat" ? (
<ItemChat />
"chat" ? (
<ItemChat artefactID={artefactID} />
) : null}
</Box>
</Box>
Expand Down Expand Up @@ -491,24 +491,24 @@ const CatalogueItemView = ({ isOpen, onClose, title, sectionId, items, setDialog
>
<Text
fontSize={{
md: "xl",
lg: "2xl",
md: "lg",
lg: "xl",
}}
fontWeight="bold"
mr={4}
>
{title}
</Text>

<Tooltip
showArrow
content="Open in Data Studio"
positioning={{ placement: "right-end" }}
closeDelay={150}
<Tooltip
showArrow
content="Open in Data Studio"
positioning={{ placement: "right-end" }}
closeDelay={150}
openDelay={150}
contentProps={{ css: { "--tooltip-bg": "#12144C" } }}
>
<IoOpen size={25} color="#12144C" onClick={() => navigate(`/studio/${sectionId}/${currentId}`)} cursor={"pointer"}/>
<IoOpen size={25} color="#12144C" onClick={() => navigate(`/studio/${sectionId}/${currentId}`)} cursor={"pointer"} />
</Tooltip>
</Flex>

Expand All @@ -532,14 +532,14 @@ const CatalogueItemView = ({ isOpen, onClose, title, sectionId, items, setDialog

<Box flex={1} mt={4} overflowY={"auto"} scrollbar={"hidden"}>
{selectedSegment ===
"metadata" ? (
"metadata" ? (
<MetadataDisplay
currentItem={items[currentIndex]}
currentItem={items[currentIndex]}
isOpen={isOpen}
/>
) : selectedSegment ===
"chat" ? (
<ItemChat />
"chat" ? (
<ItemChat artefactID={artefactID} />
) : null}
</Box>
</Box>
Expand Down
133 changes: 130 additions & 3 deletions src/components/Catalogue/itemChat.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,132 @@
function ItemChat() {
return <div>ItemChat</div>;
import { useState, useEffect, useRef } from "react";
import { Input, Box, Button, VStack, HStack, Text } from "@chakra-ui/react";
import { IoIosSend } from "react-icons/io";
import server, { JSONResponse } from "../../networking";
import ToastWizard from "../toastWizard";

function ItemChat({ artefactID }) {
const [messages, setMessages] = useState([]);
const [input, setInput] = useState("");
const [loading, setLoading] = useState(false);
const messagesEndRef = useRef(null);

useEffect(() => {
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
}, [messages]);

const sendMessage = async () => {
if (!input.trim() || loading) return;

const userMessage = { role: "user", content: input };
setMessages(prev => [...prev, userMessage]);
setInput("");
setLoading(true);

try {
const response = await server.post('/chatbot/query', {
artefactID,
newPrompt: input,
history: messages
})

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

if (response.data?.raw?.data?.history) {
setMessages(response.data.raw.data.history);
} else if (response.data?.raw?.data?.response) {
setMessages(prev => [...prev,
{ role: "assistant", content: response.data.raw.data.response }
]);
}

} else {
throw new Error("Unexpected response format");
}
} catch (err) {
if (err.response && err.response.data instanceof JSONResponse) {
console.log("Error response in chatbot query:", err.response.data.fullMessage());
if (err.response.data.userErrorType()) {
ToastWizard.standard("error", "Chatbot query failed.", err.response.data.message);
} else {
ToastWizard.standard("error", "Chatbot query failed.", "An unexpected error occurred.");
}
} else {
console.log("Unexpected error in chatbot query:", err);
ToastWizard.standard("error", "Chatbot query failed.", "An unexpected error occurred.");
}
} finally {
setLoading(false);
}
};

const handleKeyDown = (e) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
};

return (
<VStack spacing={4} align="stretch" h="100%">
<Box
flex="1"
overflowY="auto"
border="1px solid"
borderColor="gray.200"
p={4}
borderRadius="md"
bg="white"
>
{messages.length === 0 ? (
<Text color="gray.500" textAlign="center" py={4}>
Let's discover this artefact together !
</Text>
) : (
messages.map((msg, idx) => (
<Box
key={`${idx}-${msg.role}`}
mb={3}
p={3}
bg={msg.role === "user" ? "blue.50" : "green.50"}
borderRadius="md"
>
<Text fontWeight="bold" color={msg.role === "user" ? "blue.600" : "green.600"}>
{msg.role === "user" ? "You: " : "Archivus: "}
</Text>
<Text whiteSpace="pre-wrap">{msg.content}</Text>
</Box>
))
)}
<div ref={messagesEndRef} />
</Box>

<HStack>
<Input
placeholder="Ask Archivus..."
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyDown={handleKeyDown}
disabled={loading}
/>
<Button
onClick={sendMessage}
loading={loading}
variant={"ArchPrimaryAlt"}
px={6}
>
Send
</Button>
</HStack>
</VStack>
);
}

export default ItemChat;
export default ItemChat;
11 changes: 7 additions & 4 deletions src/components/Catalogue/section.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const Section = ({ sectionTitle, sectionId, onItemClick, artefacts = [] }) => {
const [atEnd, setAtEnd] = useState(false);
const [dialogOpen, setDialogOpen] = useState(false);
const [dialogTitle, setDialogTitle] = useState("");
const [dialogArtefactID, setDialogArtefactID] = useState("");

const checkScrollEdges = () => {
const node = scrollRef.current;
Expand All @@ -45,10 +46,11 @@ const Section = ({ sectionTitle, sectionId, onItemClick, artefacts = [] }) => {
});
};

const handleCardClick = (title) => {
setDialogTitle(title);
const handleCardClick = (item) => {
setDialogTitle(item.title);
setDialogArtefactID(item.id);
setDialogOpen(true);
if (onItemClick) onItemClick(title);
if (onItemClick) onItemClick(item.id);
};

useEffect(() => {
Expand Down Expand Up @@ -129,7 +131,7 @@ const Section = ({ sectionTitle, sectionId, onItemClick, artefacts = [] }) => {
flex="0 0 auto"
cursor="pointer"
borderRadius="md"
onClick={() => handleCardClick(item.title)}
onClick={() => handleCardClick(item)}
>
<CardItem
imageSrc={item.imageSrc}
Expand All @@ -144,6 +146,7 @@ const Section = ({ sectionTitle, sectionId, onItemClick, artefacts = [] }) => {
<CatalogueItemView
isOpen={dialogOpen}
onClose={() => setDialogOpen(false)}
artefactID={dialogArtefactID}
title={dialogTitle}
sectionId={sectionId}
items={items}
Expand Down
34 changes: 34 additions & 0 deletions src/components/DataStudio/associationCard.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Card, Box, Text, Flex } from "@chakra-ui/react";
import AssociationSwitch from "./associationSwitch";

function AssociationCard({ associationData, isMember, onToggle }) {
if (!associationData) return null;

return (
<Card.Root direction="row" variant="outline" p={4}>
<Card.Body p={0}>
<Flex flexDirection="row" justifyContent="space-between" gap={4} alignItems="center">
{/* Left: title & description */}
<Box flex="1">
<Text fontWeight="bold" fontSize="lg" mb={1}>
{associationData.name || "Untitled"}
</Text>
<Text fontSize="sm" color="gray.600" lineClamp={1}>
{associationData.description}
</Text>
</Box>

{/* Right: membership switch */}
<Box>
<AssociationSwitch
isMember={isMember}
onToggle={onToggle}
/>
</Box>
</Flex>
</Card.Body>
</Card.Root>
);
}

export default AssociationCard;
Loading