Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
56 changes: 28 additions & 28 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 @@ -362,15 +362,15 @@ const CatalogueItemView = ({ isOpen, onClose, title, sectionId, items, setDialog
{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 @@ -500,15 +500,15 @@ const CatalogueItemView = ({ isOpen, onClose, title, sectionId, items, setDialog
{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