From ec6b9c7f9bbba1bc718873ec42ba8529684e002f Mon Sep 17 00:00:00 2001 From: mimikwkk <113390005+mimikwkk@users.noreply.github.com> Date: Tue, 28 May 2024 03:01:31 -0700 Subject: [PATCH 01/18] Correct the responses.json format so it matches the structure required for actions.py --- src/app/api/chat/main.py | 2 +- src/app/api/chat/responses.json | 7 + src/app/chat/hooks/use-chat-handler.tsx | 219 +++++++++++++++++++----- 3 files changed, 188 insertions(+), 40 deletions(-) create mode 100644 src/app/api/chat/responses.json diff --git a/src/app/api/chat/main.py b/src/app/api/chat/main.py index e6b962c..ed493aa 100644 --- a/src/app/api/chat/main.py +++ b/src/app/api/chat/main.py @@ -42,7 +42,7 @@ def submit_responses(): responses = data.get('responses', {}) with open('responses.json', 'w') as file: - json.dump(responses, file) + json.dump(responses, file, indent=4) return jsonify({"status": "Success", "message": "Data saved successfully"}) diff --git a/src/app/api/chat/responses.json b/src/app/api/chat/responses.json new file mode 100644 index 0000000..3ee3a01 --- /dev/null +++ b/src/app/api/chat/responses.json @@ -0,0 +1,7 @@ +{ + "Enter up to three questions that guide your family\u2019s decision making.": "three questions", + "What are your family values?": "v a l u e", + "What is a statement or commitment that your family lives by?": "statement of work", + "What statement defines your family's vision?": "glasses", + "What is your family's impact statement?": "pow wow" +} \ No newline at end of file diff --git a/src/app/chat/hooks/use-chat-handler.tsx b/src/app/chat/hooks/use-chat-handler.tsx index 6ce34e6..e5e66a5 100644 --- a/src/app/chat/hooks/use-chat-handler.tsx +++ b/src/app/chat/hooks/use-chat-handler.tsx @@ -224,13 +224,157 @@ // }; // }; +// import { useState, useCallback, useContext } from 'react'; +// import { ChatbotUIContext } from '@/app/chat/context'; +// import { ChatMessage } from '@/app/chat/types/types'; +// import { v4 as uuidv4 } from 'uuid'; + +// const questions = [ +// "Enter up to three questions that guide your family’s decision making.", +// "What are your family values?", +// "What is a statement or commitment that your family lives by?", +// "What statement defines your family's vision?", +// "What is your family's impact statement?", +// ]; + +// export const useChatHandler = () => { +// const { chatMessages, setChatMessages } = useContext(ChatbotUIContext); +// const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0); +// const [isGenerating, setIsGenerating] = useState(false); +// const [questionsCompleted, setQuestionsCompleted] = useState(false); +// const [responses, setResponses] = useState([]); + +// const createChatBotMessage = useCallback((text, messageType): ChatMessage => ({ +// id: uuidv4(), +// type: messageType, +// message: text +// }), []); + +// const addMessage = useCallback((message: ChatMessage) => { +// setChatMessages(prevMessages => [...prevMessages, message]); +// }, [setChatMessages]); + +// const handleResponse = (response, questionIndex) => { +// const newResponses = [...responses]; +// newResponses[questionIndex] = { question: questions[questionIndex], answer: response }; +// setResponses(newResponses); +// }; + +// const handleQuestions = useCallback(() => { +// if (currentQuestionIndex < questions.length) { +// addMessage(createChatBotMessage(questions[currentQuestionIndex], 'bot')); +// setCurrentQuestionIndex(current => current + 1); +// } else { +// addMessage(createChatBotMessage("You may type exit to finish creating your charter.", 'bot')); +// setQuestionsCompleted(true); // Mark the completion of questions +// } +// }, [addMessage, createChatBotMessage, currentQuestionIndex]); + +// const submitResponses = useCallback(async () => { +// const responsesObject = responses.reduce((acc, curr) => { +// if (curr) { +// acc[curr.question] = curr.answer; +// } +// return acc; +// }, {}); + +// questions.forEach((question, index) => { +// const userResponse = chatMessages.find(msg => msg.isResponse && msg.questionIndex === index); +// if (userResponse) { +// handleResponse(userResponse.message, index); +// } +// }); + + +// try { +// const response = await fetch('http://localhost:8000/api/chat/submit_responses', { +// method: 'POST', +// headers: { 'Content-Type': 'application/json' }, +// body: JSON.stringify({ responses }), +// credentials: 'include' +// }); +// if (!response.ok) { +// throw new Error(`HTTP error ${response.status}`); +// } +// const result = await response.json(); +// console.log("Responses submitted successfully:", result); +// } catch (error) { +// console.error("Failed to submit responses:", error); +// } +// }, [chatMessages]); + +// const sendMessageToAPI = useCallback(async (message) => { +// try { +// const response = await fetch('http://localhost:8000/api/chat/send_message', { +// method: 'POST', +// headers: { 'Content-Type': 'application/json' }, +// body: JSON.stringify({ message }), +// credentials: 'include' +// }); +// if (!response.ok) throw new Error(`HTTP error ${response.status}`); +// const { answer } = await response.json(); +// return answer; +// } catch (error) { +// console.error("Error during fetch or parsing:", error); +// return "Sorry, there was an error processing your message."; +// } +// }, []); + +// const handleSendMessage = useCallback(async (messageText: string) => { +// setIsGenerating(true); +// const normalizedText = messageText.trim().toLowerCase(); +// try { +// addMessage(createChatBotMessage(messageText, 'user')); + +// // Start the questionnaire if "start" is input and it's the first interaction. +// if (normalizedText === "start" && currentQuestionIndex === 0) { +// handleQuestions(); +// } else if (normalizedText === "exit" && questionsCompleted) { +// // Allow exiting only if the questionnaire has been completed. +// await submitResponses(); +// setChatMessages([]); +// setCurrentQuestionIndex(0); +// setQuestionsCompleted(false); +// addMessage(createChatBotMessage("Thank you, I have recorded your responses. Goodbye!", 'bot')); +// } else { +// // Handle general inquiries or continue with questions if the session has started. +// if (currentQuestionIndex > 0 && currentQuestionIndex <= questions.length) { +// handleQuestions(); +// } else { +// // If no questionnaire is active or needed, handle as a general inquiry. +// if (questionsCompleted || currentQuestionIndex === 0) { +// const apiResponse = await sendMessageToAPI(messageText); +// addMessage(createChatBotMessage(apiResponse, 'bot')); +// } else { +// // Prompt to continue or start the questionnaire if not yet started. +// addMessage(createChatBotMessage("Please type 'start' to begin the questionnaire, or ask anything else.", 'bot')); +// } +// } +// } +// } finally { +// setIsGenerating(false); +// } +// }, [addMessage, handleQuestions, createChatBotMessage, currentQuestionIndex, setChatMessages, submitResponses, sendMessageToAPI, questionsCompleted]); + +// const handleNewChat = useCallback(() => { +// setChatMessages([]); +// setCurrentQuestionIndex(0); +// setQuestionsCompleted(false); // Reset the question completion status +// }, [setChatMessages]); + +// return { +// chatMessages, +// handleSendMessage, +// handleNewChat, +// isGenerating, +// }; +// }; import { useState, useCallback, useContext } from 'react'; import { ChatbotUIContext } from '@/app/chat/context'; -import { ChatMessage } from '@/app/chat/types/types'; import { v4 as uuidv4 } from 'uuid'; const questions = [ - "Enter up to three questions that guide your family’s decision making.", + "Enter up to three questions that guide your family's decision making.", "What are your family values?", "What is a statement or commitment that your family lives by?", "What statement defines your family's vision?", @@ -242,36 +386,38 @@ export const useChatHandler = () => { const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0); const [isGenerating, setIsGenerating] = useState(false); const [questionsCompleted, setQuestionsCompleted] = useState(false); + const [responses, setResponses] = useState({}); - const createChatBotMessage = useCallback((text, messageType): ChatMessage => ({ - id: uuidv4(), - type: messageType, - message: text - }), []); + // Helper function to create chat messages + const createChatBotMessage = useCallback((text, messageType) => { + const id = uuidv4(); + return { id, type: messageType, message: text }; + }, []); - const addMessage = useCallback((message: ChatMessage) => { + // Function to add messages to the chat + const addMessage = useCallback((message) => { setChatMessages(prevMessages => [...prevMessages, message]); }, [setChatMessages]); + // Function to handle the current question const handleQuestions = useCallback(() => { if (currentQuestionIndex < questions.length) { - addMessage(createChatBotMessage(questions[currentQuestionIndex], 'bot')); + const message = createChatBotMessage(questions[currentQuestionIndex], 'bot'); + addMessage(message); setCurrentQuestionIndex(current => current + 1); } else { addMessage(createChatBotMessage("You may type exit to finish creating your charter.", 'bot')); - setQuestionsCompleted(true); // Mark the completion of questions + setQuestionsCompleted(true); } }, [addMessage, createChatBotMessage, currentQuestionIndex]); + // Function to submit all responses const submitResponses = useCallback(async () => { - // Logic to submit responses to the backend - // After submitting, allow API interactions - const userResponses = chatMessages.filter(msg => msg.type === 'user').map(msg => msg.message); try { const response = await fetch('http://localhost:8000/api/chat/submit_responses', { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ responses: userResponses }), + body: JSON.stringify({ responses }), credentials: 'include' }); if (!response.ok) { @@ -282,8 +428,9 @@ export const useChatHandler = () => { } catch (error) { console.error("Failed to submit responses:", error); } - }, [chatMessages]); + }, [responses]); + // Function to send a message to the API const sendMessageToAPI = useCallback(async (message) => { try { const response = await fetch('http://localhost:8000/api/chat/send_message', { @@ -293,54 +440,48 @@ export const useChatHandler = () => { credentials: 'include' }); if (!response.ok) throw new Error(`HTTP error ${response.status}`); - const { answer } = await response.json(); - return answer; + const result = await response.json(); + return result.answer; } catch (error) { console.error("Error during fetch or parsing:", error); return "Sorry, there was an error processing your message."; } }, []); - const handleSendMessage = useCallback(async (messageText: string) => { + // Main function to handle sending messages + const handleSendMessage = useCallback(async (messageText) => { setIsGenerating(true); const normalizedText = messageText.trim().toLowerCase(); try { - addMessage(createChatBotMessage(messageText, 'user')); - - // Start the questionnaire if "start" is input and it's the first interaction. + const message = createChatBotMessage(messageText, 'user'); + addMessage(message); + if (normalizedText === "start" && currentQuestionIndex === 0) { handleQuestions(); } else if (normalizedText === "exit" && questionsCompleted) { - // Allow exiting only if the questionnaire has been completed. - await submitResponses(); + await submitResponses(); setChatMessages([]); setCurrentQuestionIndex(0); setQuestionsCompleted(false); addMessage(createChatBotMessage("Thank you, I have recorded your responses. Goodbye!", 'bot')); + } else if (currentQuestionIndex > 0 && currentQuestionIndex <= questions.length) { + responses[questions[currentQuestionIndex - 1]] = messageText; + handleQuestions(); } else { - // Handle general inquiries or continue with questions if the session has started. - if (currentQuestionIndex > 0 && currentQuestionIndex <= questions.length) { - handleQuestions(); - } else { - // If no questionnaire is active or needed, handle as a general inquiry. - if (questionsCompleted || currentQuestionIndex === 0) { - const apiResponse = await sendMessageToAPI(messageText); - addMessage(createChatBotMessage(apiResponse, 'bot')); - } else { - // Prompt to continue or start the questionnaire if not yet started. - addMessage(createChatBotMessage("Please type 'start' to begin the questionnaire, or ask anything else.", 'bot')); - } - } + const apiResponse = await sendMessageToAPI(messageText); + addMessage(createChatBotMessage(apiResponse, 'bot')); } } finally { setIsGenerating(false); } - }, [addMessage, handleQuestions, createChatBotMessage, currentQuestionIndex, setChatMessages, submitResponses, sendMessageToAPI, questionsCompleted]); - + }, [addMessage, createChatBotMessage, setChatMessages, handleQuestions, sendMessageToAPI, submitResponses, currentQuestionIndex, questionsCompleted, responses]); + + // Function to reset the chat for a new session const handleNewChat = useCallback(() => { setChatMessages([]); setCurrentQuestionIndex(0); - setQuestionsCompleted(false); // Reset the question completion status + setQuestionsCompleted(false); + setResponses({}); }, [setChatMessages]); return { From 61ee9752d18d59eae17f119fd1f5ea6796b05e5d Mon Sep 17 00:00:00 2001 From: Daniel Okonov Date: Tue, 28 May 2024 04:41:42 -0700 Subject: [PATCH 02/18] Adjusted UI for mobile and desktop --- package-lock.json | 240 +++++++++++++-------------- src/app/chat/chat-input.tsx | 6 + src/app/chat/chat-messages.tsx | 29 +++- src/app/chat/page.tsx | 13 +- src/app/chat/styles.css | 44 ++++- src/app/model/update_vector_store.py | 5 +- 6 files changed, 196 insertions(+), 141 deletions(-) diff --git a/package-lock.json b/package-lock.json index a740c0b..2918389 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1049,6 +1049,126 @@ "node": ">= 10" } }, + "node_modules/@next/swc-darwin-x64": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.3.tgz", + "integrity": "sha512-6adp7waE6P1TYFSXpY366xwsOnEXM+y1kgRpjSRVI2CBDOcbRjsJ67Z6EgKIqWIue52d2q/Mx8g9MszARj8IEA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.3.tgz", + "integrity": "sha512-cuzCE/1G0ZSnTAHJPUT1rPgQx1w5tzSX7POXSLaS7w2nIUJUD+e25QoXD/hMfxbsT9rslEXugWypJMILBj/QsA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.3.tgz", + "integrity": "sha512-0D4/oMM2Y9Ta3nGuCcQN8jjJjmDPYpHX9OJzqk42NZGJocU2MqhBq5tWkJrUQOQY9N+In9xOdymzapM09GeiZw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.3.tgz", + "integrity": "sha512-ENPiNnBNDInBLyUU5ii8PMQh+4XLr4pG51tOp6aJ9xqFQ2iRI6IH0Ds2yJkAzNV1CfyagcyzPfROMViS2wOZ9w==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.3.tgz", + "integrity": "sha512-BTAbq0LnCbF5MtoM7I/9UeUu/8ZBY0i8SFjUMCbPDOLv+un67e2JgyN4pmgfXBwy/I+RHu8q+k+MCkDN6P9ViQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.3.tgz", + "integrity": "sha512-AEHIw/dhAMLNFJFJIJIyOFDzrzI5bAjI9J26gbO5xhAKHYTZ9Or04BesFPXiAYXDNdrwTP2dQceYA4dL1geu8A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-ia32-msvc": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.3.tgz", + "integrity": "sha512-vga40n1q6aYb0CLrM+eEmisfKCR45ixQYXuBXxOOmmoV8sYST9k7E3US32FsY+CkkF7NtzdcebiFT4CHuMSyZw==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.3.tgz", + "integrity": "sha512-Q1/zm43RWynxrO7lW4ehciQVj+5ePBhOK+/K2P7pLFX3JaJ/IZVC69SHidrmZSOkqz7ECIOhhy7XhAFG4JYyHA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -13628,126 +13748,6 @@ "type": "github", "url": "https://github.com/sponsors/wooorm" } - }, - "node_modules/@next/swc-darwin-x64": { - "version": "14.2.3", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.3.tgz", - "integrity": "sha512-6adp7waE6P1TYFSXpY366xwsOnEXM+y1kgRpjSRVI2CBDOcbRjsJ67Z6EgKIqWIue52d2q/Mx8g9MszARj8IEA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-gnu": { - "version": "14.2.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.3.tgz", - "integrity": "sha512-cuzCE/1G0ZSnTAHJPUT1rPgQx1w5tzSX7POXSLaS7w2nIUJUD+e25QoXD/hMfxbsT9rslEXugWypJMILBj/QsA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-musl": { - "version": "14.2.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.3.tgz", - "integrity": "sha512-0D4/oMM2Y9Ta3nGuCcQN8jjJjmDPYpHX9OJzqk42NZGJocU2MqhBq5tWkJrUQOQY9N+In9xOdymzapM09GeiZw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-gnu": { - "version": "14.2.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.3.tgz", - "integrity": "sha512-ENPiNnBNDInBLyUU5ii8PMQh+4XLr4pG51tOp6aJ9xqFQ2iRI6IH0Ds2yJkAzNV1CfyagcyzPfROMViS2wOZ9w==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-musl": { - "version": "14.2.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.3.tgz", - "integrity": "sha512-BTAbq0LnCbF5MtoM7I/9UeUu/8ZBY0i8SFjUMCbPDOLv+un67e2JgyN4pmgfXBwy/I+RHu8q+k+MCkDN6P9ViQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-arm64-msvc": { - "version": "14.2.3", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.3.tgz", - "integrity": "sha512-AEHIw/dhAMLNFJFJIJIyOFDzrzI5bAjI9J26gbO5xhAKHYTZ9Or04BesFPXiAYXDNdrwTP2dQceYA4dL1geu8A==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-ia32-msvc": { - "version": "14.2.3", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.3.tgz", - "integrity": "sha512-vga40n1q6aYb0CLrM+eEmisfKCR45ixQYXuBXxOOmmoV8sYST9k7E3US32FsY+CkkF7NtzdcebiFT4CHuMSyZw==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-x64-msvc": { - "version": "14.2.3", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.3.tgz", - "integrity": "sha512-Q1/zm43RWynxrO7lW4ehciQVj+5ePBhOK+/K2P7pLFX3JaJ/IZVC69SHidrmZSOkqz7ECIOhhy7XhAFG4JYyHA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } } } } diff --git a/src/app/chat/chat-input.tsx b/src/app/chat/chat-input.tsx index 632fa5e..0608581 100644 --- a/src/app/chat/chat-input.tsx +++ b/src/app/chat/chat-input.tsx @@ -159,6 +159,9 @@ // ); // }; +//###################################################################################################### +// WORKING VERSION + import { FC, useState, KeyboardEvent, useRef } from 'react'; import { useTranslation } from 'react-i18next'; import { useChatHandler } from '@/app/chat/hooks/use-chat-handler'; @@ -244,3 +247,6 @@ export const ChatInput: FC = () => { ); }; + +// WORKING VERSION ^^ +//###################################################################################################### diff --git a/src/app/chat/chat-messages.tsx b/src/app/chat/chat-messages.tsx index d45f8d7..fe07c06 100644 --- a/src/app/chat/chat-messages.tsx +++ b/src/app/chat/chat-messages.tsx @@ -1,27 +1,40 @@ - import React, { useEffect, useRef } from 'react'; -import { useChatbotUI } from '@/app/chat/context'; +import { useChatbotUI } from '@/app/chat/context'; +import { AiOutlineOpenAI } from "react-icons/ai"; import './styles.css'; export const ChatMessages = () => { - // const { chatMessages } = useContext(ChatbotUIContext); const { chatMessages } = useChatbotUI(); const messagesEndRef = useRef(null); useEffect(() => { - messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); - console.log("Updated messages:", chatMessages) + if (messagesEndRef.current) { + messagesEndRef.current.scrollIntoView({ behavior: "smooth" }); + } }, [chatMessages]); return (
{chatMessages.map((msg, index) => (
-
- {msg.message} -
+ {msg.type === 'bot' && ( +
+
+ +
+
+ {msg.message} +
+
+ )} + {msg.type !== 'bot' && ( +
+ {msg.message} +
+ )}
))} +
); }; diff --git a/src/app/chat/page.tsx b/src/app/chat/page.tsx index 397677b..4ed1440 100644 --- a/src/app/chat/page.tsx +++ b/src/app/chat/page.tsx @@ -20,17 +20,24 @@ export default function ChatPage() { useEffect(() => { console.log("Chat messages changed:", chatMessages.map(m => m.message)); // Confirm messages are present - + const lastMessageText = chatMessages[chatMessages.length - 1]?.message?.toLowerCase() ?? ""; - + console.log("Last message text:", lastMessageText); // Should show the actual last message or an empty string - + if (!chatStarted && lastMessageText === "start") { console.log("Start conditions met. Starting chat."); setChatStarted(true); } }, [chatMessages, chatStarted]); // Keep chatStarted to avoid unnecessary re-renders when it changes + // const handleInputSubmit = (messageText) => { + // if (messageText.trim().toLowerCase() === "start" && !chatStarted) { + // setChatStarted(true); + // } + // handleSendMessage(messageText); + // }; + console.log("Rendering ChatPage, Chat Started:", chatStarted); return ( diff --git a/src/app/chat/styles.css b/src/app/chat/styles.css index 9659118..30fd708 100644 --- a/src/app/chat/styles.css +++ b/src/app/chat/styles.css @@ -2,12 +2,15 @@ .chat-container { display: flex; flex-direction: column; - max-width: 800px; /* Adjusted from a fixed pixel width to a percentage */ + width: 100%; + max-width: 800px; + /* Adjusted from a fixed pixel width to a percentage */ margin: auto; padding: 25px; - /* background: #f9f9f9; */ + /* background: #f9f9f9; */ height: 500px; - overflow-y: scroll; + /* Use a viewport height to make it more responsive */ + overflow-y: auto; /* border: 1px solid #ccc; Optional: adds a border for better visibility */ } @@ -16,28 +19,53 @@ margin-bottom: 10px; padding: 8px 15px; border-radius: 10px; - max-width: 100%; /* Allow messages to take full width of the container */ + max-width: 100%; + /* Allow messages to take full width of the container */ word-wrap: break-word; } +.bot-content { + display: flex; + align-items: center; + /* Center items vertically within the bot content */ +} + /* Bot message styling */ .bot-message { - align-self: flex-start; + align-self: flex-end; /* background-color: #e0e0e0; */ - width: auto; /* Ensure messages can grow as needed */ + width: auto; + /* Ensure messages can grow as needed */ } .bot-text { color: #333; } +.bot-icon { + display: flex; + align-items: center; + justify-content: center; + width: 40px; + /* Adjust size as needed */ + height: 40px; + /* Adjust size as needed */ + border-radius: 50%; + /* Makes the icon circular */ + background-color: white; + /* Optional: background color for the icon */ + margin-right: 20px; + /* Space between the icon and the message */ +} + /* User message styling */ .user-message { align-self: flex-end; background-color: #474a4d; - width: auto; /* Ensure messages can grow as needed */ + width: auto; + /* Ensure messages can grow as needed */ } .user-text { color: white; -} +} \ No newline at end of file diff --git a/src/app/model/update_vector_store.py b/src/app/model/update_vector_store.py index fcb0fcf..e6ce0c6 100644 --- a/src/app/model/update_vector_store.py +++ b/src/app/model/update_vector_store.py @@ -55,7 +55,8 @@ def update_vector_store(pdf_dir, vector_store_dir): print(f"An error occurred: {e}") # Made a change to the code below to make the path relative if __name__ == "__main__": - pdf_dir = "src/app/model/books/" - vector_store_dir = "src/app/model/vector_store/" + + pdf_dir = "C:/Users/danie/Documents/BCIT/Semester4/ProjectsPracticum2/Project2/business-charter/src/app/model/books/" + vector_store_dir = "C:/Users/danie/Documents/BCIT/Semester4/ProjectsPracticum2/Project2/business-charter/src/app/model/vector_store/" update_vector_store(pdf_dir, vector_store_dir) From 690b38d5bd3780a6edcc1c1d33b93d2f175946a3 Mon Sep 17 00:00:00 2001 From: mimikwkk <113390005+mimikwkk@users.noreply.github.com> Date: Tue, 28 May 2024 04:44:42 -0700 Subject: [PATCH 03/18] Remove unused content --- src/app/chat/types/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/chat/types/types.ts b/src/app/chat/types/types.ts index 58c2a53..8c111cd 100644 --- a/src/app/chat/types/types.ts +++ b/src/app/chat/types/types.ts @@ -19,7 +19,7 @@ export interface MessageProps { isLast: boolean; onStartEdit?: () => void; onCancelEdit?: () => void; - onSubmitEdit?: (content: string) => void; + onSubmitEdit?: () => void; } export interface ChatMessage { From b8ac8d7a157e0a61f92108c483855b96bd7c682d Mon Sep 17 00:00:00 2001 From: mimikwkk <113390005+mimikwkk@users.noreply.github.com> Date: Tue, 28 May 2024 05:02:39 -0700 Subject: [PATCH 04/18] Integrate db manipulation into api endpoint --- src/app/api/chat/main.py | 128 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 120 insertions(+), 8 deletions(-) diff --git a/src/app/api/chat/main.py b/src/app/api/chat/main.py index ed493aa..51c1783 100644 --- a/src/app/api/chat/main.py +++ b/src/app/api/chat/main.py @@ -1,24 +1,38 @@ +import os import sys -print("Using Python interpreter at:", sys.executable) from flask import Flask, request, jsonify from flask_cors import CORS from pathlib import Path -import json import logging +from supabase import create_client, Client +from dotenv import load_dotenv + logging.basicConfig(level=logging.DEBUG) # Add the parent directory to the system path to import model sys.path.append(str(Path(__file__).resolve().parents[2] / 'model')) from model import process_message # Import process_message from model.py +# Initialize Flask app with CORS app = Flask(__name__) CORS(app, resources={r"/api/*": { - "origins": "http://localhost:3000", # Adjust this as necessary + "origins": "http://localhost:3000", "methods": ["GET", "POST"], "allow_headers": ["Content-Type", "Authorization"], "supports_credentials": True }}) +# Load environment variables from .env.local file +env_path = '.env.local' +load_dotenv(env_path) + +# Setup Supabase client +supabase_url = os.getenv('NEXT_PUBLIC_SUPABASE_URL') +supabase_key = os.getenv('NEXT_PUBLIC_SUPABASE_ANON_KEY') +if not supabase_url or not supabase_key: + raise EnvironmentError(f"Failed to load environment variables from {env_path}. Please check your .env.local file.") +supabase: Client = create_client(supabase_url, supabase_key) + @app.route('/api/chat/send_message', methods=['POST']) def chat(): app.logger.debug("Received request for /api/chat/send_message") @@ -39,12 +53,110 @@ def chat(): @app.route('/api/chat/submit_responses', methods=['POST']) def submit_responses(): data = request.json + user_id = data.get('userId') responses = data.get('responses', {}) - - with open('responses.json', 'w') as file: - json.dump(responses, file, indent=4) - return jsonify({"status": "Success", "message": "Data saved successfully"}) + if not user_id: + return jsonify({"error": "User ID is missing"}), 400 + + # Process responses after saving + result = add_entry_to_supabase(responses, user_id) + app.logger.info(f"Insertion result: {result}") + return jsonify(result) + +def revalidate_path(path: str): + pass + +def add_entry_to_supabase(entries, user_id): + # try: + # with open(user_id_file_path, 'r') as file: + # user_id = file.read().strip() + # if not user_id: + # raise ValueError("User ID is empty.") + # print(f"User ID: {user_id}") + # except Exception as e: + # return {'message': f"Error reading user ID file: {e}"} + + # # Insert the question with the user_id into decision_tree + try: + app.logger.debug(f"Attempting to insert: {entries}") + response_question = supabase.table('decision_tree').insert({'question': entries['question'], 'user_id': user_id}).execute() + + if 'error' in response_question.data: + raise ValueError(f"Error inserting question: {response_question.data['error']['message']}") + else: + app.logger.debug(f"Successfully inserted question: {response_question.data}") + except Exception as e: + return {'message': f"Error inserting question: {e}"} + + # Insert the family value with the user_id into family_values + try: + response_value = supabase.table('family_values').insert({ + 'title': 'Created by VidereAI', + 'description': entries['family_value'], + 'user_id': user_id + }).execute() + print(response_value) + if 'error' in response_value.data: + raise ValueError(f"Error inserting family value: {response_value.data['error']['message']}") + except Exception as e: + return {'message': f"Error inserting family value: {e}"} + + # Insert the family code statement with the user_id into family_code + try: + response_statement = supabase.table('family_code').insert({ + 'statement': entries['family_statement'], + 'user_id': user_id + }).execute() + print(response_statement) + if 'error' in response_statement.data: + raise ValueError(f"Error inserting family statement: {response_statement.data['error']['message']}") + except Exception as e: + return {'message': f"Error inserting family statement: {e}"} + + # Insert the family vision statement with the user_id into family_vision + try: + response_vision = supabase.table('family_vision').insert({ + 'statement': entries['family_vision'], + 'user_id': user_id + }).execute() + print(response_vision) + if 'error' in response_vision.data: + raise ValueError(f"Error inserting family vision: {response_vision.data['error']['message']}") + except Exception as e: + return {'message': f"Error inserting family vision: {e}"} + + # Insert the impact statement with the user_id into philanthropy_impact_statements + try: + response_impact = supabase.table('philanthropy_impact_statements').insert({ + 'statement': entries['impact_statement'], + 'user_id': user_id + }).execute() + print(response_impact) + if 'error' in response_impact.data: + raise ValueError(f"Error inserting impact statement: {response_impact.data['error']['message']}") + except Exception as e: + return {'message': f"Error inserting impact statement: {e}"} + + revalidate_path('/decision-tree') + revalidate_path('/family-values') + revalidate_path('/family-code') + revalidate_path('/family-vision') + revalidate_path('/philanthropy') + return {'message': 'Question, family value, family statement, family vision, and impact statement added successfully'} if __name__ == '__main__': - app.run(host='0.0.0.0', port=8000, debug=True) \ No newline at end of file + app.run(host='0.0.0.0', port=8000, debug=True) + json_file_path = 'src/app/api/chat/responses.json' + keys = { + 'question': "Enter up to three questions that guide your family's decision making.", + 'family_value': "What are your family values?", + 'family_statement': "What is a statement or commitment that your family lives by?", + 'family_vision': "What statement defines your family's vision?", + 'impact_statement': "What is your family's impact statement?" + } + try: + result = add_entry_to_supabase(json_file_path, keys) + print(result) + except Exception as e: + print(f"An error occurred: {e}") From 50fd24fe2f93cae7bff16a5516903549ec33aa1f Mon Sep 17 00:00:00 2001 From: mimikwkk <113390005+mimikwkk@users.noreply.github.com> Date: Tue, 28 May 2024 05:02:54 -0700 Subject: [PATCH 05/18] Integrate db manipulation into api call in front end --- src/app/chat/hooks/use-chat-handler.tsx | 401 ++---------------------- 1 file changed, 27 insertions(+), 374 deletions(-) diff --git a/src/app/chat/hooks/use-chat-handler.tsx b/src/app/chat/hooks/use-chat-handler.tsx index e5e66a5..f229a38 100644 --- a/src/app/chat/hooks/use-chat-handler.tsx +++ b/src/app/chat/hooks/use-chat-handler.tsx @@ -1,377 +1,9 @@ -// import { useState, useCallback, useContext, useEffect } from 'react'; -// import { ChatbotUIContext } from '@/app/chat/context'; -// import { ChatMessage } from '@/app/chat/types/types'; - -// const questions = [ -// "Enter up to three questions that guide your family’s decision making.", -// "What are your family values?", -// "What is a statement or commitment that your family lives by?", -// "What statement defines your family's vision?", -// "What is your family's impact statement?", -// ]; - -// export const useChatHandler = () => { -// const { chatMessages, setChatMessages } = useContext(ChatbotUIContext); -// const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0); -// const [isGenerating, setIsGenerating] = useState(false); - -// const createChatBotMessage = useCallback((text, messageType): ChatMessage => { -// return { -// id: Date.now().toString(), -// type: messageType, -// message: text -// }; -// }, []); - -// // Initialize chat with a welcome message -// useEffect(() => { -// const welcomeMessage = createChatBotMessage("Welcome to Videre Chatbot!", 'bot'); -// setChatMessages([welcomeMessage]); -// }, [createChatBotMessage, setChatMessages]); - -// const sendMessageToAPI = useCallback(async (message) => { -// if (['start', 'exit'].includes(message.toLowerCase())) { -// console.log("Control message received, not sending to API:", message); -// return; -// } -// try { -// const response = await fetch('http://localhost:8000/api/chat', { -// method: 'POST', -// headers: { 'Content-Type': 'application/json' }, -// body: JSON.stringify({ message }), -// }); -// if (!response.ok) throw new Error(`HTTP error ${response.status}`); -// const { answer } = await response.json(); -// return answer; -// } catch (error) { -// console.error("Error during fetch or parsing:", error); -// } -// }, []); - -// const handleGeneralMessage = useCallback(async (message) => { -// const response = await sendMessageToAPI(message); -// if(response) { -// const botMessage = createChatBotMessage(response, 'bot'); -// setChatMessages(prev => [...prev, botMessage]); -// } -// }, [sendMessageToAPI, createChatBotMessage, setChatMessages]); - -// const handleStart = useCallback(() => { -// if (currentQuestionIndex < questions.length) { -// const message = createChatBotMessage(questions[currentQuestionIndex], 'bot'); -// setChatMessages(prevMessages => [...prevMessages, message]); -// setCurrentQuestionIndex(current => current + 1); -// } -// }, [createChatBotMessage, currentQuestionIndex, setChatMessages]); - - -// const handleExit = useCallback(() => { -// const exitMessage = createChatBotMessage("Goodbye!", "bot"); -// setChatMessages(prev => [...prev, exitMessage]); -// }, [createChatBotMessage, setChatMessages]); - -// const handleSendMessage = useCallback(async (messageText: string) => { -// setIsGenerating(true); -// try { -// const message: ChatMessage = createChatBotMessage(messageText, 'user'); -// setChatMessages(prev => { -// console.log("Adding message:", message); // Log to confirm message is being added -// return [...prev, message]; -// }); - -// if (messageText.trim().toLowerCase() === "start" && currentQuestionIndex === 0) { -// handleStart(); -// } else if (messageText.trim().toLowerCase() === "exit") { -// handleExit(); -// } else { -// await handleGeneralMessage(messageText); -// } -// } catch (error) { -// console.error("Failed to handle message:", error); -// } finally { -// setIsGenerating(false); -// } -// }, [createChatBotMessage, handleStart, handleExit, handleGeneralMessage, setChatMessages, currentQuestionIndex]); - -// const handleNewChat = useCallback(() => { -// setChatMessages([]); // Clearing existing messages for new chat session -// setCurrentQuestionIndex(0); // Reset question index -// }, [setChatMessages]); - -// return { -// chatMessages, -// handleSendMessage, -// handleNewChat, -// handleStart, -// isGenerating, -// }; -// }; - - -// import { useState, useCallback, useContext } from 'react'; -// import { ChatbotUIContext } from '@/app/chat/context'; -// import { ChatMessage } from '@/app/chat/types/types'; -// import { v4 as uuidv4 } from 'uuid'; - -// const questions = [ -// "Enter up to three questions that guide your family’s decision making.", -// "What are your family values?", -// "What is a statement or commitment that your family lives by?", -// "What statement defines your family's vision?", -// "What is your family's impact statement?", -// ]; - -// export const useChatHandler = () => { -// const { chatMessages, setChatMessages } = useContext(ChatbotUIContext); -// const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0); -// const [isGenerating, setIsGenerating] = useState(false); - -// const createChatBotMessage = useCallback((text, messageType): ChatMessage => ({ -// id: uuidv4(), -// type: messageType, -// message: text -// }), []); - -// const addMessage = useCallback((message: ChatMessage) => { -// setChatMessages(prevMessages => [...prevMessages, message]); -// }, [setChatMessages]); - -// const handleQuestions = useCallback(() => { -// console.log("Current Index Before Update:", currentQuestionIndex); -// if (currentQuestionIndex < questions.length) { -// addMessage(createChatBotMessage(questions[currentQuestionIndex], 'bot')); -// setCurrentQuestionIndex(current => { -// console.log("Updating Index From:", current, "To:", current + 1); -// return current + 1; -// }); -// } else { -// addMessage(createChatBotMessage("You may type exit to finish creating your charter.", 'bot')); -// } -// }, [addMessage, createChatBotMessage, currentQuestionIndex]); - -// const submitResponses = useCallback(async () => { -// // Filter messages to only include those from the user -// const userResponses = chatMessages.filter(msg => msg.type === 'user').map(msg => msg.message); - -// // Send the user responses to the backend -// try { -// const response = await fetch('http://localhost:8000/api/chat/submit_responses', { -// method: 'POST', -// headers: { 'Content-Type': 'application/json' }, -// body: JSON.stringify({ responses: userResponses }), -// credentials: 'include' -// }); -// if (!response.ok) { -// throw new Error(`HTTP error ${response.status}`); -// } -// const result = await response.json(); -// console.log("Responses submitted successfully:", result); -// } catch (error) { -// console.error("Failed to submit responses:", error); -// } -// }, [chatMessages]); - -// const sendMessageToAPI = useCallback(async (message) => { -// try { -// const response = await fetch('http://localhost:8000/api/chat/send_message', { -// method: 'POST', -// headers: { 'Content-Type': 'application/json' }, -// body: JSON.stringify({ message }), -// credentials: 'include' -// }); -// if (!response.ok) throw new Error(`HTTP error ${response.status}`); -// const { answer } = await response.json(); -// return answer; -// } catch (error) { -// console.error("Error during fetch or parsing:", error); -// return "Sorry, there was an error processing your message."; -// } -// }, []); - -// const handleSendMessage = useCallback(async(messageText: string) => { -// setIsGenerating(true); -// const normalizedText = messageText.trim().toLowerCase(); -// try { -// addMessage(createChatBotMessage(messageText, 'user')); - -// if (normalizedText === "start" && currentQuestionIndex === 0) { -// handleQuestions(); -// } else if (normalizedText === "exit") { -// await submitResponses(); -// setChatMessages([]); -// setCurrentQuestionIndex(0); -// addMessage(createChatBotMessage("Thank you, I have recorded your responses. Goodbye!", 'bot')); -// } else { -// const apiResponse = await sendMessageToAPI(messageText); -// addMessage(createChatBotMessage(apiResponse, 'bot')); -// handleQuestions(); -// } -// } finally { -// setIsGenerating(false); -// } -// }, [addMessage, handleQuestions, createChatBotMessage, currentQuestionIndex, setChatMessages, submitResponses, sendMessageToAPI]); - -// const handleNewChat = useCallback(() => { -// setChatMessages([]); // Clearing existing messages for new chat session -// setCurrentQuestionIndex(0); // Reset question index -// }, [setChatMessages]); - -// return { -// chatMessages, -// handleSendMessage, -// handleNewChat, -// isGenerating, -// }; -// }; - -// import { useState, useCallback, useContext } from 'react'; -// import { ChatbotUIContext } from '@/app/chat/context'; -// import { ChatMessage } from '@/app/chat/types/types'; -// import { v4 as uuidv4 } from 'uuid'; - -// const questions = [ -// "Enter up to three questions that guide your family’s decision making.", -// "What are your family values?", -// "What is a statement or commitment that your family lives by?", -// "What statement defines your family's vision?", -// "What is your family's impact statement?", -// ]; - -// export const useChatHandler = () => { -// const { chatMessages, setChatMessages } = useContext(ChatbotUIContext); -// const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0); -// const [isGenerating, setIsGenerating] = useState(false); -// const [questionsCompleted, setQuestionsCompleted] = useState(false); -// const [responses, setResponses] = useState([]); - -// const createChatBotMessage = useCallback((text, messageType): ChatMessage => ({ -// id: uuidv4(), -// type: messageType, -// message: text -// }), []); - -// const addMessage = useCallback((message: ChatMessage) => { -// setChatMessages(prevMessages => [...prevMessages, message]); -// }, [setChatMessages]); - -// const handleResponse = (response, questionIndex) => { -// const newResponses = [...responses]; -// newResponses[questionIndex] = { question: questions[questionIndex], answer: response }; -// setResponses(newResponses); -// }; - -// const handleQuestions = useCallback(() => { -// if (currentQuestionIndex < questions.length) { -// addMessage(createChatBotMessage(questions[currentQuestionIndex], 'bot')); -// setCurrentQuestionIndex(current => current + 1); -// } else { -// addMessage(createChatBotMessage("You may type exit to finish creating your charter.", 'bot')); -// setQuestionsCompleted(true); // Mark the completion of questions -// } -// }, [addMessage, createChatBotMessage, currentQuestionIndex]); - -// const submitResponses = useCallback(async () => { -// const responsesObject = responses.reduce((acc, curr) => { -// if (curr) { -// acc[curr.question] = curr.answer; -// } -// return acc; -// }, {}); - -// questions.forEach((question, index) => { -// const userResponse = chatMessages.find(msg => msg.isResponse && msg.questionIndex === index); -// if (userResponse) { -// handleResponse(userResponse.message, index); -// } -// }); - - -// try { -// const response = await fetch('http://localhost:8000/api/chat/submit_responses', { -// method: 'POST', -// headers: { 'Content-Type': 'application/json' }, -// body: JSON.stringify({ responses }), -// credentials: 'include' -// }); -// if (!response.ok) { -// throw new Error(`HTTP error ${response.status}`); -// } -// const result = await response.json(); -// console.log("Responses submitted successfully:", result); -// } catch (error) { -// console.error("Failed to submit responses:", error); -// } -// }, [chatMessages]); - -// const sendMessageToAPI = useCallback(async (message) => { -// try { -// const response = await fetch('http://localhost:8000/api/chat/send_message', { -// method: 'POST', -// headers: { 'Content-Type': 'application/json' }, -// body: JSON.stringify({ message }), -// credentials: 'include' -// }); -// if (!response.ok) throw new Error(`HTTP error ${response.status}`); -// const { answer } = await response.json(); -// return answer; -// } catch (error) { -// console.error("Error during fetch or parsing:", error); -// return "Sorry, there was an error processing your message."; -// } -// }, []); - -// const handleSendMessage = useCallback(async (messageText: string) => { -// setIsGenerating(true); -// const normalizedText = messageText.trim().toLowerCase(); -// try { -// addMessage(createChatBotMessage(messageText, 'user')); - -// // Start the questionnaire if "start" is input and it's the first interaction. -// if (normalizedText === "start" && currentQuestionIndex === 0) { -// handleQuestions(); -// } else if (normalizedText === "exit" && questionsCompleted) { -// // Allow exiting only if the questionnaire has been completed. -// await submitResponses(); -// setChatMessages([]); -// setCurrentQuestionIndex(0); -// setQuestionsCompleted(false); -// addMessage(createChatBotMessage("Thank you, I have recorded your responses. Goodbye!", 'bot')); -// } else { -// // Handle general inquiries or continue with questions if the session has started. -// if (currentQuestionIndex > 0 && currentQuestionIndex <= questions.length) { -// handleQuestions(); -// } else { -// // If no questionnaire is active or needed, handle as a general inquiry. -// if (questionsCompleted || currentQuestionIndex === 0) { -// const apiResponse = await sendMessageToAPI(messageText); -// addMessage(createChatBotMessage(apiResponse, 'bot')); -// } else { -// // Prompt to continue or start the questionnaire if not yet started. -// addMessage(createChatBotMessage("Please type 'start' to begin the questionnaire, or ask anything else.", 'bot')); -// } -// } -// } -// } finally { -// setIsGenerating(false); -// } -// }, [addMessage, handleQuestions, createChatBotMessage, currentQuestionIndex, setChatMessages, submitResponses, sendMessageToAPI, questionsCompleted]); - -// const handleNewChat = useCallback(() => { -// setChatMessages([]); -// setCurrentQuestionIndex(0); -// setQuestionsCompleted(false); // Reset the question completion status -// }, [setChatMessages]); - -// return { -// chatMessages, -// handleSendMessage, -// handleNewChat, -// isGenerating, -// }; -// }; -import { useState, useCallback, useContext } from 'react'; +import { useState, useCallback, useContext, useEffect } from 'react'; import { ChatbotUIContext } from '@/app/chat/context'; import { v4 as uuidv4 } from 'uuid'; +import { createClient } from '@/lib/supabase/client'; + +const supabase = createClient(); const questions = [ "Enter up to three questions that guide your family's decision making.", @@ -387,6 +19,22 @@ export const useChatHandler = () => { const [isGenerating, setIsGenerating] = useState(false); const [questionsCompleted, setQuestionsCompleted] = useState(false); const [responses, setResponses] = useState({}); + const [userId, setUserId] = useState(null); + + useEffect(() => { + const getSession = async () => { + const { data, error } = await supabase.auth.getSession(); + if (data?.session?.user) { + setUserId(data.session.user.id); + console.log('User data:', data.session.user.id); + } else { + setUserId(null); // Ensure to set to null if there is no user + console.error('No user data:', error); + } + }; + + getSession(); + }, []); // Helper function to create chat messages const createChatBotMessage = useCallback((text, messageType) => { @@ -413,11 +61,16 @@ export const useChatHandler = () => { // Function to submit all responses const submitResponses = useCallback(async () => { + if (!userId) { + console.error("User is not logged in."); + return; + } + try { const response = await fetch('http://localhost:8000/api/chat/submit_responses', { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ responses }), + body: JSON.stringify({ responses, userId }), credentials: 'include' }); if (!response.ok) { @@ -428,7 +81,7 @@ export const useChatHandler = () => { } catch (error) { console.error("Failed to submit responses:", error); } - }, [responses]); + }, [responses, userId]); // Function to send a message to the API const sendMessageToAPI = useCallback(async (message) => { From d2d27d056707257d815fe4c663a2c81b4177b26a Mon Sep 17 00:00:00 2001 From: mimikwkk <113390005+mimikwkk@users.noreply.github.com> Date: Tue, 28 May 2024 05:38:36 -0700 Subject: [PATCH 06/18] Refactor code to streamline and fix bugs --- src/app/api/chat/main.py | 118 +++++++++++++++------------------------ 1 file changed, 46 insertions(+), 72 deletions(-) diff --git a/src/app/api/chat/main.py b/src/app/api/chat/main.py index 51c1783..e6691d5 100644 --- a/src/app/api/chat/main.py +++ b/src/app/api/chat/main.py @@ -59,6 +59,16 @@ def submit_responses(): if not user_id: return jsonify({"error": "User ID is missing"}), 400 + entries = { + 'question': responses.get("What questions help guide your family's decision-making?"), + 'family_value': responses.get("What are your family values?"), + 'family_statement': responses.get("What is a statement or commitment that your family lives by?"), + 'family_vision': responses.get("What statement defines your family's vision?"), + 'impact_statement': responses.get("What is your family's impact statement?") + } + + print("Entries to insert:", entries) # Debug print + # Process responses after saving result = add_entry_to_supabase(responses, user_id) app.logger.info(f"Insertion result: {result}") @@ -68,82 +78,46 @@ def revalidate_path(path: str): pass def add_entry_to_supabase(entries, user_id): - # try: - # with open(user_id_file_path, 'r') as file: - # user_id = file.read().strip() - # if not user_id: - # raise ValueError("User ID is empty.") - # print(f"User ID: {user_id}") - # except Exception as e: - # return {'message': f"Error reading user ID file: {e}"} - - # # Insert the question with the user_id into decision_tree - try: - app.logger.debug(f"Attempting to insert: {entries}") - response_question = supabase.table('decision_tree').insert({'question': entries['question'], 'user_id': user_id}).execute() - - if 'error' in response_question.data: - raise ValueError(f"Error inserting question: {response_question.data['error']['message']}") - else: - app.logger.debug(f"Successfully inserted question: {response_question.data}") - except Exception as e: - return {'message': f"Error inserting question: {e}"} + entry_map = { + "Enter up to three questions that guide your family's decision making.": { + 'table': 'decision_tree', + 'data': lambda entry: {'question': entry, 'user_id': user_id} + }, + "What are your family values?": { + 'table': 'family_values', + 'data': lambda entry: {'description': entry, 'user_id': user_id, 'title': 'Created by VidereAI'} + }, + "What is a statement or commitment that your family lives by?": { + 'table': 'family_code', + 'data': lambda entry: {'statement': entry, 'user_id': user_id} + }, + "What statement defines your family's vision?": { + 'table': 'family_vision', + 'data': lambda entry: {'statement': entry, 'user_id': user_id} + }, + "What is your family's impact statement?": { + 'table': 'philanthropy_impact_statements', + 'data': lambda entry: {'statement': entry, 'user_id': user_id} + } + } - # Insert the family value with the user_id into family_values - try: - response_value = supabase.table('family_values').insert({ - 'title': 'Created by VidereAI', - 'description': entries['family_value'], - 'user_id': user_id - }).execute() - print(response_value) - if 'error' in response_value.data: - raise ValueError(f"Error inserting family value: {response_value.data['error']['message']}") - except Exception as e: - return {'message': f"Error inserting family value: {e}"} + for key, entry_info in entry_map.items(): + if key in entries: + try: + app.logger.debug(f"Attempting to insert into {entry_info['table']}: {entries[key]}") + response = supabase.table(entry_info['table']).insert(entry_info['data'](entries[key])).execute() - # Insert the family code statement with the user_id into family_code - try: - response_statement = supabase.table('family_code').insert({ - 'statement': entries['family_statement'], - 'user_id': user_id - }).execute() - print(response_statement) - if 'error' in response_statement.data: - raise ValueError(f"Error inserting family statement: {response_statement.data['error']['message']}") - except Exception as e: - return {'message': f"Error inserting family statement: {e}"} + if 'error' in response.data: + raise ValueError(f"Error inserting into {entry_info['table']}: {response.data['error']['message']}") + else: + app.logger.debug(f"Successfully inserted into {entry_info['table']}: {response.data}") - # Insert the family vision statement with the user_id into family_vision - try: - response_vision = supabase.table('family_vision').insert({ - 'statement': entries['family_vision'], - 'user_id': user_id - }).execute() - print(response_vision) - if 'error' in response_vision.data: - raise ValueError(f"Error inserting family vision: {response_vision.data['error']['message']}") - except Exception as e: - return {'message': f"Error inserting family vision: {e}"} + except Exception as e: + app.logger.error(f"Exception while inserting into {entry_info['table']}: {e}") + return {'message': f"Error inserting into {entry_info['table']}: {e}"} + + return {'message': 'All entries added successfully'} - # Insert the impact statement with the user_id into philanthropy_impact_statements - try: - response_impact = supabase.table('philanthropy_impact_statements').insert({ - 'statement': entries['impact_statement'], - 'user_id': user_id - }).execute() - print(response_impact) - if 'error' in response_impact.data: - raise ValueError(f"Error inserting impact statement: {response_impact.data['error']['message']}") - except Exception as e: - return {'message': f"Error inserting impact statement: {e}"} - - revalidate_path('/decision-tree') - revalidate_path('/family-values') - revalidate_path('/family-code') - revalidate_path('/family-vision') - revalidate_path('/philanthropy') - return {'message': 'Question, family value, family statement, family vision, and impact statement added successfully'} if __name__ == '__main__': app.run(host='0.0.0.0', port=8000, debug=True) From aebf4ba682714f8a47300a92f25ff5f31f0e313a Mon Sep 17 00:00:00 2001 From: mimikwkk <113390005+mimikwkk@users.noreply.github.com> Date: Tue, 28 May 2024 05:43:32 -0700 Subject: [PATCH 07/18] Remove mobile chatbot from navbar --- src/components/navbar.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/navbar.tsx b/src/components/navbar.tsx index d89e820..92c2c94 100644 --- a/src/components/navbar.tsx +++ b/src/components/navbar.tsx @@ -32,7 +32,6 @@ const familySublinks = [ { href: '/family-charter', label: 'Family Charter' }, { href: '/pdf', label: 'Print PDF' }, { href: '/video', label: 'Videos' }, - { href: '/chatbot', label: 'ChatBot Assistant'}, { href: '/chat', label: 'Chat' } ]; From 2dbfdd73492d64b88f07200904ca7d3648dbc5b3 Mon Sep 17 00:00:00 2001 From: mimikwkk <113390005+mimikwkk@users.noreply.github.com> Date: Tue, 28 May 2024 05:44:59 -0700 Subject: [PATCH 08/18] Remove unexpected argument --- src/app/chat/message.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/chat/message.tsx b/src/app/chat/message.tsx index 8f76b72..16ef149 100644 --- a/src/app/chat/message.tsx +++ b/src/app/chat/message.tsx @@ -13,7 +13,7 @@ export const Message: React.FC = ({ const handleEditSubmit = () => { if (editContent !== undefined) { - onSubmitEdit?.(editContent); + onSubmitEdit?.(); } onCancelEdit?.(); }; From 31a9e89d3dcfc5c101e49f743d9c2281d9ce4076 Mon Sep 17 00:00:00 2001 From: mimikwkk <113390005+mimikwkk@users.noreply.github.com> Date: Tue, 28 May 2024 05:54:05 -0700 Subject: [PATCH 09/18] Change chatbot prompts --- src/app/chat/chat-input.tsx | 2 +- src/app/chat/hooks/use-chat-handler.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/chat/chat-input.tsx b/src/app/chat/chat-input.tsx index 632fa5e..edc5dab 100644 --- a/src/app/chat/chat-input.tsx +++ b/src/app/chat/chat-input.tsx @@ -213,7 +213,7 @@ export const ChatInput: FC = () => { { addMessage(message); setCurrentQuestionIndex(current => current + 1); } else { - addMessage(createChatBotMessage("You may type exit to finish creating your charter.", 'bot')); + addMessage(createChatBotMessage("Please type exit to finish creating your charter.", 'bot')); setQuestionsCompleted(true); } }, [addMessage, createChatBotMessage, currentQuestionIndex]); From a410e9916136729aeafd52dc762ea73f81d35e76 Mon Sep 17 00:00:00 2001 From: mimikwkk <113390005+mimikwkk@users.noreply.github.com> Date: Tue, 28 May 2024 05:54:16 -0700 Subject: [PATCH 10/18] Remove unused json file --- src/app/api/chat/responses.json | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 src/app/api/chat/responses.json diff --git a/src/app/api/chat/responses.json b/src/app/api/chat/responses.json deleted file mode 100644 index 3ee3a01..0000000 --- a/src/app/api/chat/responses.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "Enter up to three questions that guide your family\u2019s decision making.": "three questions", - "What are your family values?": "v a l u e", - "What is a statement or commitment that your family lives by?": "statement of work", - "What statement defines your family's vision?": "glasses", - "What is your family's impact statement?": "pow wow" -} \ No newline at end of file From 322282ec50df1146f495285a144fea1d29cfaa6b Mon Sep 17 00:00:00 2001 From: mimikwkk <113390005+mimikwkk@users.noreply.github.com> Date: Tue, 28 May 2024 05:57:40 -0700 Subject: [PATCH 11/18] Delete unused user_id.txt --- user_id.txt | 1 - 1 file changed, 1 deletion(-) delete mode 100644 user_id.txt diff --git a/user_id.txt b/user_id.txt deleted file mode 100644 index 6089551..0000000 --- a/user_id.txt +++ /dev/null @@ -1 +0,0 @@ -64dfda1c-de02-48df-af23-8d2567890d2a \ No newline at end of file From fe879296d2094a0a0b2a2786d0ec29fac31ea3c3 Mon Sep 17 00:00:00 2001 From: mimikwkk <113390005+mimikwkk@users.noreply.github.com> Date: Tue, 28 May 2024 06:03:11 -0700 Subject: [PATCH 12/18] Remove unused code --- src/app/api/chat/main.py | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/src/app/api/chat/main.py b/src/app/api/chat/main.py index e6691d5..a4f2bd0 100644 --- a/src/app/api/chat/main.py +++ b/src/app/api/chat/main.py @@ -121,16 +121,4 @@ def add_entry_to_supabase(entries, user_id): if __name__ == '__main__': app.run(host='0.0.0.0', port=8000, debug=True) - json_file_path = 'src/app/api/chat/responses.json' - keys = { - 'question': "Enter up to three questions that guide your family's decision making.", - 'family_value': "What are your family values?", - 'family_statement': "What is a statement or commitment that your family lives by?", - 'family_vision': "What statement defines your family's vision?", - 'impact_statement': "What is your family's impact statement?" - } - try: - result = add_entry_to_supabase(json_file_path, keys) - print(result) - except Exception as e: - print(f"An error occurred: {e}") + From f6aa3d93670b64e2bebfa5bbeacf3d3c47888761 Mon Sep 17 00:00:00 2001 From: mimikwkk <113390005+mimikwkk@users.noreply.github.com> Date: Tue, 28 May 2024 06:04:33 -0700 Subject: [PATCH 13/18] Split up main.py to vercel serverless functions --- src/app/api/chat/send_message.py | 56 +++++++++++++++++ src/app/api/chat/submit_response.py | 97 +++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 src/app/api/chat/send_message.py create mode 100644 src/app/api/chat/submit_response.py diff --git a/src/app/api/chat/send_message.py b/src/app/api/chat/send_message.py new file mode 100644 index 0000000..dc9e807 --- /dev/null +++ b/src/app/api/chat/send_message.py @@ -0,0 +1,56 @@ +import os +import sys +from flask import Flask, request, jsonify +from flask_cors import CORS +from pathlib import Path +import logging +from supabase import create_client, Client +from dotenv import load_dotenv +from model import process_message # Ensure this import is correct for your structure + +logging.basicConfig(level=logging.DEBUG) + +# Initialize Flask app with CORS +app = Flask(__name__) +CORS(app, resources={r"/api/*": { + "origins": "http://localhost:3000", + "methods": ["GET", "POST"], + "allow_headers": ["Content-Type", "Authorization"], + "supports_credentials": True +}}) + +# Load environment variables from .env.local file +env_path = '.env.local' +load_dotenv(env_path) + +# Setup Supabase client +supabase_url = os.getenv('NEXT_PUBLIC_SUPABASE_URL') +supabase_key = os.getenv('NEXT_PUBLIC_SUPABASE_ANON_KEY') +if not supabase_url or not supabase_key: + raise EnvironmentError(f"Failed to load environment variables from {env_path}. Please check your .env.local file.") +supabase: Client = create_client(supabase_url, supabase_key) + +@app.route('/api/chat/send_message', methods=['POST']) +def chat(): + app.logger.debug("Received request for /api/chat/send_message") + + data = request.json + user_message = data.get('message', '') + + if not user_message: + app.logger.error("No message provided in the request") + return jsonify({"error": "No message provided"}), 400 + + response_message = process_message(user_message) + + app.logger.debug("Processed message: %s", response_message) + + return jsonify({"answer": response_message}) + +# Export the Flask app as the entry point +if __name__ == "__main__": + from werkzeug.serving import run_simple + run_simple('localhost', 8000, app) +else: + # Vercel will call the app directly + app = app diff --git a/src/app/api/chat/submit_response.py b/src/app/api/chat/submit_response.py new file mode 100644 index 0000000..42be686 --- /dev/null +++ b/src/app/api/chat/submit_response.py @@ -0,0 +1,97 @@ +import os +from flask import Flask, request, jsonify +from flask_cors import CORS +from supabase import create_client, Client +from dotenv import load_dotenv + +app = Flask(__name__) +CORS(app, resources={r"/api/*": { + "origins": "http://localhost:3000", + "methods": ["GET", "POST"], + "allow_headers": ["Content-Type", "Authorization"], + "supports_credentials": True +}}) + +# Load environment variables from .env.local file +env_path = '.env.local' +load_dotenv(env_path) + +# Setup Supabase client +supabase_url = os.getenv('NEXT_PUBLIC_SUPABASE_URL') +supabase_key = os.getenv('NEXT_PUBLIC_SUPABASE_ANON_KEY') +if not supabase_url or not supabase_key: + raise EnvironmentError(f"Failed to load environment variables from {env_path}. Please check your .env.local file.") +supabase: Client = create_client(supabase_url, supabase_key) + +@app.route('/api/chat/submit_responses', methods=['POST']) +def submit_responses(): + data = request.json + user_id = data.get('userId') + responses = data.get('responses', {}) + + if not user_id: + return jsonify({"error": "User ID is missing"}), 400 + + entries = { + 'question': responses.get("What questions help guide your family's decision-making?"), + 'family_value': responses.get("What are your family values?"), + 'family_statement': responses.get("What is a statement or commitment that your family lives by?"), + 'family_vision': responses.get("What statement defines your family's vision?"), + 'impact_statement': responses.get("What is your family's impact statement?") + } + + print("Entries to insert:", entries) # Debug print + + # Process responses after saving + result = add_entry_to_supabase(entries, user_id) + app.logger.info(f"Insertion result: {result}") + return jsonify(result) + +def add_entry_to_supabase(entries, user_id): + entry_map = { + "What questions help guide your family's decision-making?": { + 'table': 'decision_tree', + 'data': lambda entry: {'question': entry, 'user_id': user_id} + }, + "What are your family values?": { + 'table': 'family_values', + 'data': lambda entry: {'description': entry, 'user_id': user_id, 'title': 'Created by VidereAI'} + }, + "What is a statement or commitment that your family lives by?": { + 'table': 'family_code', + 'data': lambda entry: {'statement': entry, 'user_id': user_id} + }, + "What statement defines your family's vision?": { + 'table': 'family_vision', + 'data': lambda entry: {'statement': entry, 'user_id': user_id} + }, + "What is your family's impact statement?": { + 'table': 'philanthropy_impact_statements', + 'data': lambda entry: {'statement': entry, 'user_id': user_id} + } + } + + for key, entry_info in entry_map.items(): + if key in entries: + try: + app.logger.debug(f"Attempting to insert into {entry_info['table']}: {entries[key]}") + response = supabase.table(entry_info['table']).insert(entry_info['data'](entries[key])).execute() + + if 'error' in response.data: + raise ValueError(f"Error inserting into {entry_info['table']}: {response.data['error']['message']}") + else: + app.logger.debug(f"Successfully inserted into {entry_info['table']}: {response.data}") + + except Exception as e: + app.logger.error(f"Exception while inserting into {entry_info['table']}: {e}") + return {'message': f"Error inserting into {entry_info['table']}: {e}"} + + return {'message': 'All entries added successfully'} + +# Export the Flask app as the entry point +if __name__ == "__main__": + from werkzeug.serving import run_simple + run_simple('localhost', 8000, app) +else: + # Vercel will call the app directly + app = app From 0bb323f45438ed5c6ebecf03e75598df099e91f2 Mon Sep 17 00:00:00 2001 From: mimikwkk <113390005+mimikwkk@users.noreply.github.com> Date: Tue, 28 May 2024 06:04:57 -0700 Subject: [PATCH 14/18] Add vercel.json --- vercel.json | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 vercel.json diff --git a/vercel.json b/vercel.json new file mode 100644 index 0000000..c827604 --- /dev/null +++ b/vercel.json @@ -0,0 +1,28 @@ +{ + "version": 2, + "builds": [ + { + "src": "api/**/*.py", + "use": "@vercel/python" + }, + { + "src": "src/**/*", + "use": "@vercel/static-build", + "config": { "distDir": "build" } + } +], +"routes": [ + { + "src": "/api/chat/send_message", + "dest": "/api/chat/send_message.py" + }, + { + "src": "/api/chat/submit_responses", + "dest": "/api/chat/submit_responses.py" + }, + { + "src": "/(.*)", + "dest": "/src/$1" + } +] +} From b21cdd41796b849b9ec015ea34b251092450e766 Mon Sep 17 00:00:00 2001 From: Daniel Okonov Date: Tue, 28 May 2024 21:06:53 -0700 Subject: [PATCH 15/18] Modified chat container sizing and bot message position --- src/app/chat/styles.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/chat/styles.css b/src/app/chat/styles.css index 30fd708..28a09ab 100644 --- a/src/app/chat/styles.css +++ b/src/app/chat/styles.css @@ -3,7 +3,7 @@ display: flex; flex-direction: column; width: 100%; - max-width: 800px; + max-width: 1150px; /* Adjusted from a fixed pixel width to a percentage */ margin: auto; padding: 25px; @@ -32,7 +32,7 @@ /* Bot message styling */ .bot-message { - align-self: flex-end; + align-self: flex-start; /* background-color: #e0e0e0; */ width: auto; /* Ensure messages can grow as needed */ From 938bb563336433cc4c1c953778ea5fdc0a57e4bc Mon Sep 17 00:00:00 2001 From: Daniel Okonov Date: Tue, 28 May 2024 22:03:43 -0700 Subject: [PATCH 16/18] Testing authentication for chat --- src/app/api/chat/ServerChatPage.tsx | 13 ++++++ src/app/chat/ChatPage.tsx | 39 +++++++++++++++++ src/app/chat/page.tsx | 67 +++++++++++++++++------------ 3 files changed, 92 insertions(+), 27 deletions(-) create mode 100644 src/app/api/chat/ServerChatPage.tsx create mode 100644 src/app/chat/ChatPage.tsx diff --git a/src/app/api/chat/ServerChatPage.tsx b/src/app/api/chat/ServerChatPage.tsx new file mode 100644 index 0000000..e74efd3 --- /dev/null +++ b/src/app/api/chat/ServerChatPage.tsx @@ -0,0 +1,13 @@ +// src/app/api/chat/ServerChatPage.tsx +import AuthenticatedRoute from '../../(auth)/authenticated-route'; +import ChatPage from '../../chat/ChatPage'; + +const ServerChatPage = async () => { + return ( + + + + ); +}; + +export default ServerChatPage; diff --git a/src/app/chat/ChatPage.tsx b/src/app/chat/ChatPage.tsx new file mode 100644 index 0000000..49fc5a3 --- /dev/null +++ b/src/app/chat/ChatPage.tsx @@ -0,0 +1,39 @@ +// src/app/api/chat/ChatPage.tsx +"use client"; + +import { useState, useContext, useEffect } from 'react'; +import { ChatbotUIContext, ChatbotUIProvider } from '@/app/chat/context'; +import { useChatHandler } from '@/app/chat/hooks/use-chat-handler'; +import { ChatUI } from '@/app/chat/chat-ui'; +import useHotkey from '@/lib/hooks/use-hotkey'; + +const ChatPage = () => { + const { chatMessages } = useContext(ChatbotUIContext); + const { handleNewChat } = useChatHandler(); + const [chatStarted, setChatStarted] = useState(false); + + useHotkey("o", handleNewChat); + + useEffect(() => { + console.log("Chat messages changed:", chatMessages.map(m => m.message)); + + const lastMessageText = chatMessages[chatMessages.length - 1]?.message?.toLowerCase() ?? ""; + + console.log("Last message text:", lastMessageText); + + if (!chatStarted && lastMessageText === "start") { + console.log("Start conditions met. Starting chat."); + setChatStarted(true); + } + }, [chatMessages, chatStarted]); + + console.log("Rendering ChatPage, Chat Started:", chatStarted); + + return ( + + + + ); +}; + +export default ChatPage; diff --git a/src/app/chat/page.tsx b/src/app/chat/page.tsx index c70b91e..9ab5c8f 100644 --- a/src/app/chat/page.tsx +++ b/src/app/chat/page.tsx @@ -1,35 +1,35 @@ -"use client"; +// "use client"; -import { useState, useContext, useEffect } from 'react'; -import { ChatbotUIContext, ChatbotUIProvider } from '@/app/chat/context'; -import { useChatHandler } from '@/app/chat/hooks/use-chat-handler'; +// import { useState, useContext, useEffect } from 'react'; +// import { ChatbotUIContext, ChatbotUIProvider } from '@/app/chat/context'; +// import { useChatHandler } from '@/app/chat/hooks/use-chat-handler'; // import { ChatInput } from "@/app/chat/chat-input"; -import { ChatUI } from "@/app/chat/chat-ui"; +// import { ChatUI } from "@/app/chat/chat-ui"; // import { Brand } from "@/components/ui/brand"; -import useHotkey from "@/lib/hooks/use-hotkey"; +// import useHotkey from "@/lib/hooks/use-hotkey"; // import { useTheme } from "next-themes"; -export default function ChatPage() { - const { chatMessages } = useContext(ChatbotUIContext); - const { handleNewChat } = useChatHandler(); +// export default function ChatPage() { +// const { chatMessages } = useContext(ChatbotUIContext); +// const { handleNewChat } = useChatHandler(); // const { theme } = useTheme(); - const [chatStarted, setChatStarted] = useState(false); + // const [chatStarted, setChatStarted] = useState(false); - useHotkey("o", handleNewChat); + // useHotkey("o", handleNewChat); - useEffect(() => { - console.log("Chat messages changed:", chatMessages.map(m => m.message)); // Confirm messages are present + // useEffect(() => { + // console.log("Chat messages changed:", chatMessages.map(m => m.message)); // Confirm messages are present - const lastMessageText = chatMessages[chatMessages.length - 1]?.message?.toLowerCase() ?? ""; + // const lastMessageText = chatMessages[chatMessages.length - 1]?.message?.toLowerCase() ?? ""; - console.log("Last message text:", lastMessageText); // Should show the actual last message or an empty string + // console.log("Last message text:", lastMessageText); // Should show the actual last message or an empty string - if (!chatStarted && lastMessageText === "start") { - console.log("Start conditions met. Starting chat."); - setChatStarted(true); - } - }, [chatMessages, chatStarted]); // Keep chatStarted to avoid unnecessary re-renders when it changes + // if (!chatStarted && lastMessageText === "start") { + // console.log("Start conditions met. Starting chat."); + // setChatStarted(true); + // } + // }, [chatMessages, chatStarted]); // Keep chatStarted to avoid unnecessary re-renders when it changes // const handleInputSubmit = (messageText) => { // if (messageText.trim().toLowerCase() === "start" && !chatStarted) { @@ -38,7 +38,7 @@ export default function ChatPage() { // handleSendMessage(messageText); // }; - console.log("Rendering ChatPage, Chat Started:", chatStarted); + // console.log("Rendering ChatPage, Chat Started:", chatStarted); // return ( // @@ -61,11 +61,24 @@ export default function ChatPage() { // )} // // ); - return ( - +// return ( +// - +// + +// +// ); +// } + + +// pages/chat/index.tsx +import dynamic from 'next/dynamic'; + +// const ServerChatPage = dynamic(() => import('@/app/api/chat/ServerChatPage'), { ssr: false }); +const ServerChatPage = dynamic(() => import('@/app/api/chat/ServerChatPage'), {ssr: false}) + +const Chat = () => { + return ; +}; - - ); -} +export default Chat; From fd206c6776e0693da65718d725c12b9817d200a0 Mon Sep 17 00:00:00 2001 From: mimikwkk <113390005+mimikwkk@users.noreply.github.com> Date: Tue, 28 May 2024 22:49:59 -0700 Subject: [PATCH 17/18] Add family charter page --- src/app/api/chat/main.py | 1 - src/app/chat/types/types.ts | 13 +++++ src/app/family-charter/charter.ts | 34 +++++++++++++ src/app/family-charter/loader.ts | 10 ---- src/app/family-charter/page.tsx | 82 +++++++++++++++++++++---------- src/app/family-charter/types.ts | 8 --- 6 files changed, 103 insertions(+), 45 deletions(-) create mode 100644 src/app/family-charter/charter.ts delete mode 100644 src/app/family-charter/loader.ts delete mode 100644 src/app/family-charter/types.ts diff --git a/src/app/api/chat/main.py b/src/app/api/chat/main.py index a4f2bd0..eb97b8a 100644 --- a/src/app/api/chat/main.py +++ b/src/app/api/chat/main.py @@ -118,7 +118,6 @@ def add_entry_to_supabase(entries, user_id): return {'message': 'All entries added successfully'} - if __name__ == '__main__': app.run(host='0.0.0.0', port=8000, debug=True) diff --git a/src/app/chat/types/types.ts b/src/app/chat/types/types.ts index 8c111cd..ecfa853 100644 --- a/src/app/chat/types/types.ts +++ b/src/app/chat/types/types.ts @@ -62,3 +62,16 @@ export interface MessageImage { alt: string; } +export interface SupabaseError { + message: string; +} + +export interface CharterEntry { + question?: string; + description?: string; + statement?: string; +} + +export interface CharterData { +[key: string]: CharterEntry; +} \ No newline at end of file diff --git a/src/app/family-charter/charter.ts b/src/app/family-charter/charter.ts new file mode 100644 index 0000000..92a8069 --- /dev/null +++ b/src/app/family-charter/charter.ts @@ -0,0 +1,34 @@ +import { createClient } from '@/lib/supabase/client'; +import { SupabaseError } from '@/app/chat/types/types'; + +const supabase = createClient(); + +export async function fetchLatestCharterEntries(userId: string) { + const tables = [ + 'decision_tree', + 'family_values', + 'family_code', + 'family_vision', + 'philanthropy_impact_statements' + ]; + const results: { [key: string]: any } = {}; + + try { + for (const table of tables) { + const { data, error } = await supabase + .from(table) + .select('*') + .eq('user_id', userId) + .order('created_at', { ascending: false }) + .limit(1); // Fetch only the most recent entry + + if (error) throw new Error((error as SupabaseError).message); // Type assertion here + if (data && data.length > 0) { + results[table] = data[0]; // Store the latest entry from each table + } + } + return { data: results, error: null }; + } catch (error: any) { + return { data: null, error: error.message || "An unknown error occurred" }; + } +} diff --git a/src/app/family-charter/loader.ts b/src/app/family-charter/loader.ts deleted file mode 100644 index d008e95..0000000 --- a/src/app/family-charter/loader.ts +++ /dev/null @@ -1,10 +0,0 @@ -import fs from 'fs'; -import path from 'path'; -import { CharterData } from './types'; - -export const loader = async () => { - const jsonFilePath = path.join(process.cwd(), 'public', 'family_charter.json'); - const jsonData = JSON.parse(fs.readFileSync(jsonFilePath, 'utf8')) as CharterData; - - return jsonData.items; -}; \ No newline at end of file diff --git a/src/app/family-charter/page.tsx b/src/app/family-charter/page.tsx index 47b3d5a..e60b243 100644 --- a/src/app/family-charter/page.tsx +++ b/src/app/family-charter/page.tsx @@ -1,38 +1,68 @@ 'use client'; import React, { useState, useEffect } from 'react'; -import { CharterItem } from './types'; -// import AuthenticatedRoute from '../(auth)/authenticated-route'; +import { createClient } from '@/lib/supabase/client'; +import { fetchLatestCharterEntries } from './charter'; +import { CharterData } from '@/app/chat/types/types'; -const FamilyCharter = () => { - const [charterData, setCharterData] = useState([]); +const supabase = createClient(); +const FamilyCharter = () => { + const [charterData, setCharterData] = useState(null); + const [loading, setLoading] = useState(true); + const [userId, setUserId] = useState(null); + const [error, setError] = useState(null); + useEffect(() => { - fetch('/family_charter.json') // Assuming the JSON file is in the public directory - .then(response => response.json()) - .then(data => { - setCharterData(data.items); // Adjust according to JSON structure - }) - .catch(error => console.error('Failed to load data', error)); + const getSession = async () => { + const { data, error } = await supabase.auth.getSession(); + if (data?.session?.user) { + setUserId(data.session.user.id); + } else { + setError(error ? error.message : "Failed to retrieve user session"); + setLoading(false); + } + }; + + getSession(); }, []); + useEffect(() => { + if (userId) { + fetchLatestCharterEntries(userId).then(({ data, error }) => { + if (error) { + setError(error); + } else { + setCharterData(data); + } + setLoading(false); + }); + } + }, [userId]); // Fetch charter entries when userId is set + + if (loading) { + return
Loading...
; + } + + if (error) { + return
Error: {error}
; + } + return ( - // -
-

Family Charter

- {charterData.length > 0 ? ( - charterData.map((item, index) => ( -
-

{item.title}

-

{item.description}

-
- )) - ) : ( -

No data found

- )} -
- //
+
+

Family Charter

+ {charterData && Object.keys(charterData).length ? ( + Object.entries(charterData).map(([key, item]) => ( +
+

{key.replace(/_/g, ' ').toUpperCase()}

+

{item.question || item.description || item.statement}

+
+ )) + ) : ( +

No charter information found.

+ )} +
); }; -export default FamilyCharter; +export default FamilyCharter; \ No newline at end of file diff --git a/src/app/family-charter/types.ts b/src/app/family-charter/types.ts deleted file mode 100644 index 2552551..0000000 --- a/src/app/family-charter/types.ts +++ /dev/null @@ -1,8 +0,0 @@ -export interface CharterItem { - title: string; - description: string; -} - -export interface CharterData { - items: CharterItem[]; -} \ No newline at end of file From ee794c74f49768b499ae98ec482b77636674d36d Mon Sep 17 00:00:00 2001 From: Daniel Okonov Date: Wed, 29 May 2024 00:40:53 -0700 Subject: [PATCH 18/18] Updated UI for charter --- package-lock.json | 184 ++++++++++++++++++++++++++++++ package.json | 2 + src/app/family-charter/page.tsx | 64 +++++++++-- src/app/family-charter/styles.css | 38 ++++++ 4 files changed, 281 insertions(+), 7 deletions(-) create mode 100644 src/app/family-charter/styles.css diff --git a/package-lock.json b/package-lock.json index 15c6904..57ad496 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,6 +49,7 @@ "@xenova/transformers": "^2.17.1", "ai": "^3.1.8", "axios": "^1.7.2", + "bootstrap": "^5.3.3", "chart.js": "^4.4.2", "class-variance-authority": "^0.7.0", "clsx": "^2.1.0", @@ -70,6 +71,7 @@ "next-themes": "^0.3.0", "openai": "^4.29.1", "react": "^18", + "react-bootstrap": "^2.10.2", "react-chartjs-2": "^5.2.0", "react-chatbot-kit": "^2.2.2", "react-day-picker": "^8.10.0", @@ -1213,6 +1215,15 @@ "node": ">=14" } }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", @@ -2602,6 +2613,20 @@ "@babel/runtime": "^7.13.10" } }, + "node_modules/@react-aria/ssr": { + "version": "3.9.4", + "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.4.tgz", + "integrity": "sha512-4jmAigVq409qcJvQyuorsmBR4+9r3+JEC60wC+Y0MZV0HCtTmm8D9guYXlJMdx0SSkgj0hHAyFm/HvPNFofCoQ==", + "dependencies": { + "@swc/helpers": "^0.5.0" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0" + } + }, "node_modules/@reactflow/background": { "version": "11.3.13", "resolved": "https://registry.npmjs.org/@reactflow/background/-/background-11.3.13.tgz", @@ -2698,6 +2723,45 @@ "react-dom": ">=17" } }, + "node_modules/@restart/hooks": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.16.tgz", + "integrity": "sha512-f7aCv7c+nU/3mF7NWLtVVr0Ra80RqsO89hO72r+Y/nvQr5+q0UFGkocElTH6MJApvReVh6JHUFYn2cw1WdHF3w==", + "dependencies": { + "dequal": "^2.0.3" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@restart/ui": { + "version": "1.6.9", + "resolved": "https://registry.npmjs.org/@restart/ui/-/ui-1.6.9.tgz", + "integrity": "sha512-mUbygUsJcRurjZCt1f77gg4DpheD1D+Sc7J3JjAkysUj7t8m4EBJVOqWC9788Qtbc69cJ+HlJc6jBguKwS8Mcw==", + "dependencies": { + "@babel/runtime": "^7.21.0", + "@popperjs/core": "^2.11.6", + "@react-aria/ssr": "^3.5.0", + "@restart/hooks": "^0.4.9", + "@types/warning": "^3.0.0", + "dequal": "^2.0.3", + "dom-helpers": "^5.2.0", + "uncontrollable": "^8.0.1", + "warning": "^4.0.3" + }, + "peerDependencies": { + "react": ">=16.14.0", + "react-dom": ">=16.14.0" + } + }, + "node_modules/@restart/ui/node_modules/uncontrollable": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-8.0.4.tgz", + "integrity": "sha512-ulRWYWHvscPFc0QQXvyJjY6LIXU56f0h8pQFvhxiKk5V1fcI8gp9Ht9leVAhrVjzqMw0BgjspBINx9r6oyJUvQ==", + "peerDependencies": { + "react": ">=16.14.0" + } + }, "node_modules/@rollup/rollup-darwin-arm64": { "version": "4.17.2", "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.17.2.tgz", @@ -3371,6 +3435,14 @@ "@types/react": "*" } }, + "node_modules/@types/react-transition-group": { + "version": "4.4.10", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz", + "integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==", + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/retry": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", @@ -3386,6 +3458,11 @@ "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==" }, + "node_modules/@types/warning": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.3.tgz", + "integrity": "sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q==" + }, "node_modules/@types/ws": { "version": "8.5.10", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", @@ -4317,6 +4394,24 @@ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==" }, + "node_modules/bootstrap": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz", + "integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "peerDependencies": { + "@popperjs/core": "^2.11.8" + } + }, "node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -4607,6 +4702,11 @@ "resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.5.tgz", "integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==" }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" + }, "node_modules/cli-cursor": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", @@ -5360,6 +5460,15 @@ "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", "peer": true }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/duck": { "version": "0.1.12", "resolved": "https://registry.npmjs.org/duck/-/duck-0.1.12.tgz", @@ -11008,6 +11117,23 @@ "react-is": "^16.13.1" } }, + "node_modules/prop-types-extra": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.1.tgz", + "integrity": "sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==", + "dependencies": { + "react-is": "^16.3.2", + "warning": "^4.0.0" + }, + "peerDependencies": { + "react": ">=0.14.0" + } + }, + "node_modules/prop-types-extra/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, "node_modules/prop-types/node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -11139,6 +11265,35 @@ "node": ">=0.10.0" } }, + "node_modules/react-bootstrap": { + "version": "2.10.2", + "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.10.2.tgz", + "integrity": "sha512-UvB7mRqQjivdZNxJNEA2yOQRB7L9N43nBnKc33K47+cH90/ujmnMwatTCwQLu83gLhrzAl8fsa6Lqig/KLghaA==", + "dependencies": { + "@babel/runtime": "^7.22.5", + "@restart/hooks": "^0.4.9", + "@restart/ui": "^1.6.8", + "@types/react-transition-group": "^4.4.6", + "classnames": "^2.3.2", + "dom-helpers": "^5.2.1", + "invariant": "^2.2.4", + "prop-types": "^15.8.1", + "prop-types-extra": "^1.1.0", + "react-transition-group": "^4.4.5", + "uncontrollable": "^7.2.1", + "warning": "^4.0.3" + }, + "peerDependencies": { + "@types/react": ">=16.14.8", + "react": ">=16.14.0", + "react-dom": ">=16.14.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/react-chartjs-2": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.2.0.tgz", @@ -11408,6 +11563,21 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, "node_modules/react-webcam": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/react-webcam/-/react-webcam-7.2.0.tgz", @@ -13011,6 +13181,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/uncontrollable": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz", + "integrity": "sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==", + "dependencies": { + "@babel/runtime": "^7.6.3", + "@types/react": ">=16.9.11", + "invariant": "^2.2.4", + "react-lifecycles-compat": "^3.0.4" + }, + "peerDependencies": { + "react": ">=15.0.0" + } + }, "node_modules/underscore": { "version": "1.13.6", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", diff --git a/package.json b/package.json index e77037b..b681b3f 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "@xenova/transformers": "^2.17.1", "ai": "^3.1.8", "axios": "^1.7.2", + "bootstrap": "^5.3.3", "chart.js": "^4.4.2", "class-variance-authority": "^0.7.0", "clsx": "^2.1.0", @@ -75,6 +76,7 @@ "next-themes": "^0.3.0", "openai": "^4.29.1", "react": "^18", + "react-bootstrap": "^2.10.2", "react-chartjs-2": "^5.2.0", "react-chatbot-kit": "^2.2.2", "react-day-picker": "^8.10.0", diff --git a/src/app/family-charter/page.tsx b/src/app/family-charter/page.tsx index e60b243..3ae5c52 100644 --- a/src/app/family-charter/page.tsx +++ b/src/app/family-charter/page.tsx @@ -4,6 +4,8 @@ import React, { useState, useEffect } from 'react'; import { createClient } from '@/lib/supabase/client'; import { fetchLatestCharterEntries } from './charter'; import { CharterData } from '@/app/chat/types/types'; +import Badge from 'react-bootstrap/Badge'; +import './styles.css' const supabase = createClient(); @@ -49,18 +51,66 @@ const FamilyCharter = () => { } return ( -
-

Family Charter

- {charterData && Object.keys(charterData).length ? ( + //
+ //

Family Charter

+ // {charterData && Object.keys(charterData).length ? ( + // Object.entries(charterData).map(([key, item]) => ( + //
+ //

{key.replace(/_/g, ' ').toUpperCase()}

+ //

{item.question || item.description || item.statement}

+ //
+ // )) + // ) : ( + //

No charter information found.

+ // )} + //
+ +
+ Family Charter + {charterData && Object.keys(charterData).length? ( Object.entries(charterData).map(([key, item]) => ( -
-

{key.replace(/_/g, ' ').toUpperCase()}

-

{item.question || item.description || item.statement}

-
+

+ + {key.replace(/_/g, ' ').toUpperCase()} + +

+ {item.question || item.description || item.statement} +

+

)) ) : (

No charter information found.

)} +{/* + +

+ + Decision Tree + +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +

+

+

+ + Family Values + +

+

+ + Family Values + +

+

+ + Family Values + +

+

+ + Family Values + +

*/}
); }; diff --git a/src/app/family-charter/styles.css b/src/app/family-charter/styles.css new file mode 100644 index 0000000..244fe1e --- /dev/null +++ b/src/app/family-charter/styles.css @@ -0,0 +1,38 @@ +/* styles.css */ +.title-badge { + display: flex; + justify-content: center; + margin-top: 20px; + margin-bottom: 20px; + font-size: 16px; + padding: 10px; + border-radius: 12px; +} + +.attribute-badges { + margin-bottom: 25px; + margin-top: 25px; + font-size: 16px; + background: #000; + border-radius: 12px; + padding: 10px; + color: white +} + +.charter-container { + width: 100%; + max-width: 1300px; + height: 500px; + margin-top: 20px; + padding-left: 5%; + box-sizing: border-box; +} + +.space-between-attributes { + margin-top: 60px; + margin-bottom: 30px; +} + +.space-between-text { + margin-top: 20px; +} \ No newline at end of file