import React, { useState, useCallback, useRef } from 'react'; import { Tabs, Tab, Box, } from '@mui/material'; import { SxProps, Theme } from '@mui/material'; import { ChatQuery } from './ChatQuery'; import { MessageList, MessageData } from './Message'; import { SetSnackType } from './Snack'; import { Conversation } from './Conversation'; interface ResumeBuilderProps { connectionBase: string, sessionId: string | undefined, setSnack: SetSnackType, sx?: SxProps; }; /** * ResumeBuilder component * * A responsive component that displays job descriptions, generated resumes and fact checks * with different layouts for mobile and desktop views. */ const ResumeBuilder: React.FC = ({ sx, connectionBase, sessionId, setSnack }) => { // State for editing job description const [hasJobDescription, setHasJobDescription] = useState(false); const [hasResume, setHasResume] = useState(false); const [hasFacts, setHasFacts] = useState(false); const jobConversationRef = useRef(null); const resumeConversationRef = useRef(null); const factsConversationRef = useRef(null); const [activeTab, setActiveTab] = useState(0); /** * Handle tab change for mobile view */ const handleTabChange = (_event: React.SyntheticEvent, newValue: number): void => { setActiveTab(newValue); }; const handleJobQuery = (query: string) => { console.log(`handleJobQuery: ${query} -- `, jobConversationRef.current ? ' sending' : 'no handler'); jobConversationRef.current?.submitQuery(query); }; const handleResumeQuery = (query: string) => { console.log(`handleResumeQuery: ${query} -- `, resumeConversationRef.current ? ' sending' : 'no handler'); resumeConversationRef.current?.submitQuery(query); }; const handleFactsQuery = (query: string) => { console.log(`handleFactsQuery: ${query} -- `, factsConversationRef.current ? ' sending' : 'no handler'); factsConversationRef.current?.submitQuery(query); }; const filterJobDescriptionMessages = useCallback((messages: MessageList): MessageList => { if (messages === undefined || messages.length === 0) { return []; } console.log("filterJobDescriptionMessages disabled", messages) if (messages.length > 1) { setHasResume(true); } messages[0].role = 'content'; messages[0].title = 'Job Description'; messages[0].disableCopy = false; return messages; // let reduced = messages.filter((m, i) => { // const keep = (m.metadata?.origin || m.origin || "no origin") === 'job_description'; // if ((m.metadata?.origin || m.origin || "no origin") === 'resume') { // setHasResume(true); // } // // if (!keep) { // // console.log(`filterJobDescriptionMessages: ${i + 1} filtered:`, m); // // } else { // // console.log(`filterJobDescriptionMessages: ${i + 1}:`, m); // // } // return keep; // }); // /* If Resume hasn't occurred yet and there is still more than one message, // * resume has been generated. */ // if (!hasResume && reduced.length > 1) { // setHasResume(true); // } // if (reduced.length > 0) { // // First message is always 'content' // reduced[0].title = 'Job Description'; // reduced[0].role = 'content'; // setHasJobDescription(true); // } // /* Filter out any messages which the server injected for state management */ // reduced = reduced.filter(m => m.display !== "hide"); // return reduced; }, [setHasResume/*, setHasJobDescription, hasResume*/]); const filterResumeMessages = useCallback((messages: MessageList): MessageList => { if (messages === undefined || messages.length === 0) { return []; } console.log("filterResumeMessages disabled") if (messages.length > 3) { setHasFacts(true); } return messages; // let reduced = messages.filter((m, i) => { // const keep = (m.metadata?.origin || m.origin || "no origin") === 'resume'; // if ((m.metadata?.origin || m.origin || "no origin") === 'fact_check') { // setHasFacts(true); // } // if (!keep) { // console.log(`filterResumeMessages: ${i + 1} filtered:`, m); // } else { // console.log(`filterResumeMessages: ${i + 1}:`, m); // } // return keep; // }); // /* If there is more than one message, it is user: "...JOB_DESCRIPTION...", assistant: "...RESUME..." // * which means a resume has been generated. */ // if (reduced.length > 1) { // /* Remove the assistant message from the UI */ // if (reduced[0].role === "user") { // reduced.splice(0, 1); // } // } // /* If Fact Check hasn't occurred yet and there is still more than one message, // * facts have have been generated. */ // if (!hasFacts && reduced.length > 1) { // setHasFacts(true); // } // /* Filter out any messages which the server injected for state management */ // reduced = reduced.filter(m => m.display !== "hide"); // /* If there are any messages, there is a resume */ // if (reduced.length > 0) { // // First message is always 'content' // reduced[0].title = 'Resume'; // reduced[0].role = 'content'; // setHasResume(true); // } // return reduced; }, [/*setHasResume, hasFacts,*/ setHasFacts]); const filterFactsMessages = useCallback((messages: MessageList): MessageList => { if (messages === undefined || messages.length === 0) { return []; } console.log("filterFactsMessages disabled") return messages; // messages.forEach((m, i) => console.log(`filterFactsMessages: ${i + 1}:`, m)) // const reduced = messages.filter(m => { // return (m.metadata?.origin || m.origin || "no origin") === 'fact_check'; // }); // /* If there is more than one message, it is user: "Fact check this resume...", assistant: "...FACT CHECK..." // * which means facts have been generated. */ // if (reduced.length > 1) { // /* Remove the user message from the UI */ // if (reduced[0].role === "user") { // reduced.splice(0, 1); // } // // First message is always 'content' // reduced[0].title = 'Fact Check'; // reduced[0].role = 'content'; // setHasFacts(true); // } // return reduced; }, [/*setHasFacts*/]); const jobResponse = useCallback(async (message: MessageData) => { console.log('onJobResponse', message); if (message.actions && message.actions.includes("resume_generated")) { await resumeConversationRef.current.fetchHistory(); setHasResume(true); setActiveTab(1); // Switch to Resume tab } if (message.actions && message.actions.includes("facts_checked")) { await factsConversationRef.current.fetchHistory(); setHasFacts(true); } }, [setHasFacts, setHasResume, setActiveTab]); const resumeResponse = useCallback((message: MessageData): void => { console.log('onResumeResponse', message); setHasFacts(true); }, [setHasFacts]); const factsResponse = useCallback((message: MessageData): void => { console.log('onFactsResponse', message); }, []); const resetJobDescription = useCallback(() => { setHasJobDescription(false); setHasResume(false); setHasFacts(false); }, [setHasJobDescription, setHasResume, setHasFacts]); const resetResume = useCallback(() => { setHasResume(false); setHasFacts(false); }, [setHasResume, setHasFacts]); const resetFacts = useCallback(() => { setHasFacts(false); }, [setHasFacts]); const renderJobDescriptionView = useCallback((sx: SxProps) => { console.log('renderJobDescriptionView'); const jobDescriptionQuestions = [ , ]; if (!hasJobDescription) { return } else { return } }, [connectionBase, filterJobDescriptionMessages, hasJobDescription, sessionId, setSnack, jobResponse, resetJobDescription, hasFacts, hasResume]); /** * Renders the resume view with loading indicator */ const renderResumeView = useCallback((sx: SxProps) => { const resumeQuestions = [ , ]; if (!hasFacts) { return } else { return } }, [connectionBase, filterResumeMessages, hasFacts, sessionId, setSnack, resumeResponse, resetResume, hasResume]); /** * Renders the fact check view */ const renderFactCheckView = useCallback((sx: SxProps) => { const factsQuestions = [ , ]; return }, [connectionBase, sessionId, setSnack, factsResponse, filterFactsMessages, resetFacts, hasResume, hasFacts]); return ( {/* Tabs */} {hasResume && } {hasFacts && } {/* Document display area */} {renderJobDescriptionView({ height: "calc(100vh - 72px - 48px)" })} {renderResumeView({ height: "calc(100vh - 72px - 48px)" })} {renderFactCheckView({ height: "calc(100vh - 72px - 48px)" })} ); }; export type { ResumeBuilderProps }; export { ResumeBuilder };