Pruned out React warnings

This commit is contained in:
James Ketr 2025-06-09 18:09:02 -07:00
parent 149ce9e9b3
commit 1531a05de0
6 changed files with 31 additions and 464 deletions

View File

@ -8,31 +8,23 @@ import {
BarChart as BarChartIcon,
Settings as SettingsIcon,
Work as WorkIcon,
Info as InfoIcon,
Person as PersonIcon,
Business as BusinessIcon,
Quiz as QuizIcon,
Analytics as AnalyticsIcon,
Search as SearchIcon,
Bookmark as BookmarkIcon,
History as HistoryIcon,
QuestionAnswer as QuestionAnswerIcon,
AttachMoney as AttachMoneyIcon,
BubbleChart,
InsertEmoticon,
BubbleChart,
} from '@mui/icons-material';
import FaceRetouchingNaturalIcon from '@mui/icons-material/FaceRetouchingNatural';
import LibraryBooksIcon from '@mui/icons-material/LibraryBooks';
import PersonSearchIcon from '@mui/icons-material/PersonSearch';
import { BackstoryLogo } from 'components/ui/BackstoryLogo';
import { HomePage } from 'pages/HomePage';
import { CandidateChatPage } from 'pages/CandidateChatPage';
import { ResumeBuilderPage } from 'pages/ResumeBuilderPage';
import { DocsPage } from 'pages/DocsPage';
import { CreateProfilePage } from 'pages/CreateProfilePage';
import { VectorVisualizerPage } from 'pages/VectorVisualizerPage';
import { BetaPage } from 'pages/BetaPage';
import { CandidateListingPage } from 'pages/FindCandidatePage';
import { JobAnalysisPage } from 'pages/JobAnalysisPage';
import { GenerateCandidate } from 'pages/GenerateCandidate';
import { Settings } from 'pages/candidate/Settings';
@ -42,11 +34,8 @@ import { EmailVerificationPage } from 'components/EmailVerificationComponents';
import { Box, Typography } from '@mui/material';
import { NavigationConfig, NavigationItem } from 'types/navigation';
import { CandidateProfile } from 'pages/candidate/Profile';
import { JobPicker } from 'components/ui/JobPicker';
import { DocumentManager } from 'components/DocumentManager';
import { VectorVisualizer } from 'components/VectorVisualizer';
import { ComingSoon } from 'components/ui/ComingSoon';
import { Beta } from 'components/ui/Beta';
// Beta page components for placeholder routes
const SearchPage = () => (<BetaPage><Typography variant="h4">Search</Typography></BetaPage>);
@ -68,16 +57,14 @@ export const navigationConfig: NavigationConfig = {
{ id: 'candidate-qa-setup', label: 'Q&A Setup', icon: <QuizIcon />, path: '/candidate/qa-setup', component: <BetaPage><Box>Candidate q&a setup page</Box></BetaPage>, userTypes: ['candidate'] },
{ id: 'candidate-analytics', label: 'Analytics', icon: <AnalyticsIcon />, path: '/candidate/analytics', component: <BetaPage><Box>Candidate analytics page</Box></BetaPage>, userTypes: ['candidate'] },
{ id: 'candidate-job-analysis', label: 'Job Analysis', path: '/candidate/job-analysis', icon: <WorkIcon />, component: <JobAnalysisPage />, userTypes: ['candidate'], },
{ id: 'candidate-resumes', label: 'Resumes', icon: <DescriptionIcon />, path: '/candidate/resumes', component: <BetaPage><Box>Candidate resumes page</Box></BetaPage>, userTypes: ['candidate'] },
{ id: 'candidate-resume-builder', label: 'Resume Builder', path: '/candidate/resume-builder', icon: <DescriptionIcon />, component: <ResumeBuilderPage />, userTypes: ['candidate'], },
{ id: 'candidate-resumes', label: 'Resumes', icon: <DescriptionIcon />, path: '/candidate/resumes', component: <BetaPage><Box>Candidate resumes page</Box></BetaPage>, userTypes: ['candidate'] },
{ id: 'candidate-content', label: 'Content', icon: <BubbleChart />, path: '/candidate/content', component: <Box sx={{ display: "flex", width: "100%", flexDirection: "column" }}><VectorVisualizer /><DocumentManager /></Box>, userTypes: ['candidate'] },
{ id: 'candidate-settings', label: 'Settings', path: '/candidate/settings', icon: <SettingsIcon />, component: <Settings />, userTypes: ['candidate'], },
],
},
{
id: 'employer-menu', label: 'Employer Tools', icon: <BusinessIcon />, userTypes: ['employer'], children: [
{ id: 'employer-job-analysis', label: 'Job Analysis', path: '/employer/job-analysis', icon: <WorkIcon />, component: <JobAnalysisPage />, userTypes: ['employer'], },
{ id: 'employer-resume-builder', label: 'Resume Builder', path: '/employer/resume-builder', icon: <DescriptionIcon />, component: <ResumeBuilderPage />, userTypes: ['employer'], },
{ id: 'employer-job-analysis', label: 'Job Analysis', path: '/employer/job-analysis', icon: <WorkIcon />, component: <JobAnalysisPage />, userTypes: ['employer'], },
{ id: 'employer-knowledge-explorer', label: 'Knowledge Explorer', path: '/employer/knowledge-explorer', icon: <WorkIcon />, component: <VectorVisualizerPage />, userTypes: ['employer'], },
{ id: 'employer-search', label: 'Search', path: '/employer/search', icon: <SearchIcon />, component: <SearchPage />, userTypes: ['employer'], },
{ id: 'employer-saved', label: 'Saved', path: '/employer/saved', icon: <BookmarkIcon />, component: <SavedPage />, userTypes: ['employer'], },

View File

@ -1,6 +0,0 @@
.ResumeBuilder .JsonViewScrollable {
min-height: unset !important;
max-height: 30rem !important;
border: 1px solid orange;
overflow-x: auto !important;
}

View File

@ -1,385 +0,0 @@
import React, { useState, useCallback, useRef } from 'react';
import {
Tabs,
Tab,
Box,
} from '@mui/material';
import { SxProps } from '@mui/material';
import { BackstoryQuery } from 'components/BackstoryQuery';
import { Conversation } from 'components/Conversation';
import { BackstoryPageProps } from 'components/BackstoryTab';
import { ChatQuery, ChatMessage } from "types/types";
import './ResumeBuilderPage.css';
import { useAppState } from 'hooks/GlobalContext';
/**
* ResumeBuilder component
*
* A responsive component that displays job descriptions, generated resumes and fact checks
* with different layouts for mobile and desktop views.
*/
const ResumeBuilderPage: React.FC<BackstoryPageProps> = (props: BackstoryPageProps) => {
// State for editing job description
const [hasJobDescription, setHasJobDescription] = useState<boolean>(false);
const [hasResume, setHasResume] = useState<boolean>(false);
const [hasFacts, setHasFacts] = useState<boolean>(false);
const jobConversationRef = useRef<any>(null);
const resumeConversationRef = useRef<any>(null);
const factsConversationRef = useRef<any>(null);
const [activeTab, setActiveTab] = useState<number>(0);
/**
* Handle tab change for mobile view
*/
const handleTabChange = (_event: React.SyntheticEvent, newValue: number): void => {
setActiveTab(newValue);
};
const handleJobQuery = (query: ChatQuery) => {
console.log(`handleJobQuery: ${query.prompt} -- `, jobConversationRef.current ? ' sending' : 'no handler');
jobConversationRef.current?.submitQuery(query);
};
const handleResumeQuery = (query: ChatQuery) => {
console.log(`handleResumeQuery: ${query.prompt} -- `, resumeConversationRef.current ? ' sending' : 'no handler');
resumeConversationRef.current?.submitQuery(query);
};
const handleFactsQuery = (query: ChatQuery) => {
console.log(`handleFactsQuery: ${query.prompt} -- `, factsConversationRef.current ? ' sending' : 'no handler');
factsConversationRef.current?.submitQuery(query);
};
const filterJobDescriptionMessages = useCallback((messages: ChatMessage[]): ChatMessage[] => {
if (messages === undefined || messages.length === 0) {
return [];
}
if (messages.length > 0) {
// messages[0].role = 'content';
// messages[0].title = 'Job Description';
// messages[0].disableCopy = false;
// messages[0].expandable = true;
}
if (-1 !== messages.findIndex(m => m.status === 'done')) { // || (m.actions && m.actions.includes("resume_generated")))) {
setHasResume(true);
setHasFacts(true);
}
return messages;
if (messages.length > 1) {
setHasResume(true);
setHasFacts(true);
}
if (messages.length > 3) {
// messages[2] is Show job requirements
// messages[3].role = 'job-requirements';
// messages[3].title = 'Job Requirements';
// messages[3].disableCopy = false;
// messages[3].expanded = false;
// messages[3].expandable = true;
}
/* Filter out the 2nd and 3rd (0-based) */
const filtered = messages;//.filter((m, i) => i !== 1 && i !== 2);
console.warn("Set filtering back on");
return filtered;
}, [setHasResume, setHasFacts]);
const filterResumeMessages = useCallback((messages: ChatMessage[]): ChatMessage[] => {
if (messages === undefined || messages.length === 0) {
return [];
}
return messages;
if (messages.length > 1) {
// messages[0] is Show Qualifications
// messages[1].role = 'qualifications';
// messages[1].title = 'Candidate qualifications';
// messages[1].disableCopy = false;
// messages[1].expanded = false;
// messages[1].expandable = true;
}
if (messages.length > 3) {
// messages[2] is Show Resume
// messages[3].role = 'resume';
// messages[3].title = 'Generated Resume';
// messages[3].disableCopy = false;
// messages[3].expanded = true;
// messages[3].expandable = true;
}
/* Filter out the 1st and 3rd messages (0-based) */
const filtered = messages.filter((m, i) => i !== 0 && i !== 2);
return filtered;
}, []);
const filterFactsMessages = useCallback((messages: ChatMessage[]): ChatMessage[] => {
if (messages === undefined || messages.length === 0) {
return [];
}
if (messages.length > 1) {
// messages[0] is Show verification
// messages[1].role = 'fact-check';
// messages[1].title = 'Fact Check';
// messages[1].disableCopy = false;
// messages[1].expanded = true;
// messages[1].expandable = true;
}
/* Filter out the 1st (0-based) */
const filtered = messages.filter((m, i) => i !== 0);
return filtered;
}, []);
const jobResponse = useCallback(async (message: ChatMessage) => {
// if (message.actions && message.actions.includes("job_description")) {
// if (jobConversationRef.current) {
// await jobConversationRef.current.fetchHistory();
// }
// }
// if (message.actions && message.actions.includes("resume_generated")) {
// if (resumeConversationRef.current) {
// await resumeConversationRef.current.fetchHistory();
// }
// setHasResume(true);
// setActiveTab(1); // Switch to Resume tab
// }
// if (message.actions && message.actions.includes("facts_checked")) {
// if (factsConversationRef.current) {
// await factsConversationRef.current.fetchHistory();
// }
// setHasFacts(true);
// }
}, [setHasFacts, setHasResume, setActiveTab]);
const resumeResponse = useCallback((message: ChatMessage): void => {
console.log('onResumeResponse', message);
setHasFacts(true);
}, [setHasFacts]);
const factsResponse = useCallback((message: ChatMessage): 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]);
return (<Box>Not re-implmented yet</Box>);
// const renderJobDescriptionView = useCallback((sx?: SxProps) => {
// console.log('renderJobDescriptionView');
// const jobDescriptionQuestions = [
// <Box sx={{ display: "flex", flexDirection: "column" }}>
// <BackstoryQuery query={{ prompt: "What are the key skills necessary for this position?", tunables: { enableTools: false } }} submitQuery={handleJobQuery} />
// <BackstoryQuery query={{ prompt: "How much should this position pay (accounting for inflation)?", tunables: { enableTools: false } }} submitQuery={handleJobQuery} />
// </Box>,
// ];
// const jobDescriptionPreamble: ChatMessage[] = [{
// role: 'info',
// content: `Once you paste a job description and press **Generate Resume**, Backstory will perform the following actions:
// 1. **Job Analysis**: LLM extracts requirements from '\`Job Description\`' to generate a list of desired '\`Skills\`'.
// 2. **Candidate Analysis**: LLM determines candidate qualifications by performing skill assessments.
// For each '\`Skill\`' from **Job Analysis** phase:
// 1. **RAG**: Retrieval Augmented Generation collection is queried for context related content for each '\`Skill\`'.
// 2. **Evidence Creation**: LLM is queried to generate supporting evidence of '\`Skill\`' from the '\`RAG\`' and '\`Candidate Resume\`'.
// 3. **Resume Generation**: LLM is provided the output from the **Candidate Analysis:Evidence Creation** phase and asked to generate a professional resume.
// See [About > Resume Generation Architecture](/about/resume-generation) for more details.
// `,
// disableCopy: true
// }];
// if (!hasJobDescription) {
// return <Conversation
// ref={jobConversationRef}
// {...{
// type: "job_description",
// actionLabel: "Generate Resume",
// preamble: jobDescriptionPreamble,
// hidePreamble: true,
// placeholder: "Paste a job description, then click Generate...",
// multiline: true,
// resetLabel: `job description${hasFacts ? ", resume, and fact check" : hasResume ? " and resume" : ""}`,
// messageFilter: filterJobDescriptionMessages,
// resetAction: resetJobDescription,
// onResponse: jobResponse,
// sessionId,
// setSnack,
// submitQuery,
// sx,
// }}
// />
// } else {
// return <Conversation
// ref={jobConversationRef}
// {...{
// type: "job_description",
// actionLabel: "Send",
// placeholder: "Ask a question about this job description...",
// resetLabel: `job description${hasFacts ? ", resume, and fact check" : hasResume ? " and resume" : ""}`,
// messageFilter: filterJobDescriptionMessages,
// defaultPrompts: jobDescriptionQuestions,
// resetAction: resetJobDescription,
// onResponse: jobResponse,
// sessionId,
// setSnack,
// submitQuery,
// sx,
// }}
// />
// }
// }, [filterJobDescriptionMessages, hasJobDescription, sessionId, setSnack, jobResponse, resetJobDescription, hasFacts, hasResume, submitQuery]);
// /**
// * Renders the resume view with loading indicator
// */
// const renderResumeView = useCallback((sx?: SxProps) => {
// const resumeQuestions = [
// <Box sx={{ display: "flex", flexDirection: "column" }}>
// <BackstoryQuery query={{ prompt: "Is this resume a good fit for the provided job description?", tunables: { enableTools: false } }} submitQuery={handleResumeQuery} />
// <BackstoryQuery query={{ prompt: "Provide a more concise resume.", tunables: { enableTools: false } }} submitQuery={handleResumeQuery} />
// </Box>,
// ];
// if (!hasFacts) {
// return <Conversation
// ref={resumeConversationRef}
// {...{
// type: "resume",
// actionLabel: "Fact Check",
// defaultQuery: "Fact check the resume.",
// resetLabel: `job description${hasFacts ? ", resume, and fact check" : hasResume ? " and resume" : ""}`,
// messageFilter: filterResumeMessages,
// onResponse: resumeResponse,
// resetAction: resetResume,
// sessionId,
// setSnack,
// submitQuery,
// sx,
// }}
// />
// } else {
// return <Conversation
// ref={resumeConversationRef}
// {...{
// type: "resume",
// actionLabel: "Send",
// placeholder: "Ask a question about this job resume...",
// resetLabel: `job description${hasFacts ? ", resume, and fact check" : hasResume ? " and resume" : ""}`,
// messageFilter: filterResumeMessages,
// onResponse: resumeResponse,
// resetAction: resetResume,
// sessionId,
// setSnack,
// defaultPrompts: resumeQuestions,
// submitQuery,
// sx,
// }}
// />
// }
// }, [filterResumeMessages, hasFacts, sessionId, setSnack, resumeResponse, resetResume, hasResume, submitQuery]);
// /**
// * Renders the fact check view
// */
// const renderFactCheckView = useCallback((sx?: SxProps) => {
// const factsQuestions = [
// <Box sx={{ display: "flex", flexDirection: "column" }}>
// <BackstoryQuery query={{ prompt: "Rewrite the resume to address any discrepancies.", tunables: { enableTools: false } }} submitQuery={handleFactsQuery} />
// </Box>,
// ];
// return <Conversation
// ref={factsConversationRef}
// {...{
// type: "fact_check",
// actionLabel: "Send",
// placeholder: "Ask a question about any discrepencies...",
// resetLabel: `job description${hasFacts ? ", resume, and fact check" : hasResume ? " and resume" : ""}`,
// messageFilter: filterFactsMessages,
// defaultPrompts: factsQuestions,
// resetAction: resetFacts,
// onResponse: factsResponse,
// sessionId,
// submitQuery,
// setSnack,
// sx,
// }}
// />
// }, [ sessionId, setSnack, factsResponse, filterFactsMessages, resetFacts, hasResume, hasFacts, submitQuery]);
// return (
// <Box className="ResumeBuilder"
// sx={{
// p: 0,
// m: 0,
// display: "flex",
// flexGrow: 1,
// margin: "0 auto",
// overflow: "hidden",
// backgroundColor: "#F5F5F5",
// flexDirection: "column",
// maxWidth: "1024px",
// }}
// >
// {/* Tabs */}
// <Tabs
// value={activeTab}
// onChange={handleTabChange}
// variant="fullWidth"
// sx={{ bgcolor: 'background.paper' }}
// >
// <Tab value={0} label="Job Description" />
// {hasResume && <Tab value={1} label="Resume" />}
// {hasFacts && <Tab value={2} label="Fact Check" />}
// </Tabs>
// {/* Document display area */}
// <Box sx={{
// display: 'flex', flexDirection: 'column', flexGrow: 1, p: 0, width: "100%", ...sx,
// overflow: "hidden"
// }}>
// <Box sx={{ display: activeTab === 0 ? "flex" : "none" }}>{renderJobDescriptionView(/*{ height: "calc(100% - 72px - 48px)" }*/)}</Box>
// <Box sx={{ display: activeTab === 1 ? "flex" : "none" }}>{renderResumeView(/*{ height: "calc(100% - 72px - 48px)" }*/)}</Box>
// <Box sx={{ display: activeTab === 2 ? "flex" : "none" }}>{renderFactCheckView(/*{ height: "calc(100% - 72px - 48px)" }*/)}</Box>
// </Box>
// </Box>
// );
};
export {
ResumeBuilderPage
};

View File

@ -19,20 +19,20 @@ import { useAppState } from 'hooks/GlobalContext';
import { useAuth } from 'hooks/AuthContext';
import * as Types from 'types/types';
interface ServerTunables {
system_prompt: string,
tools: Tool[],
rags: Tool[]
};
// interface ServerTunables {
// system_prompt: string,
// tools: Tool[],
// rags: Tool[]
// };
type Tool = {
type: string,
enabled: boolean
name: string,
description: string,
parameters?: any,
returns?: any
};
// type Tool = {
// type: string,
// enabled: boolean
// name: string,
// description: string,
// parameters?: any,
// returns?: any
// };
const SystemInfoComponent: React.FC<{ systemInfo: Types.SystemInfo | undefined }> = ({ systemInfo }) => {
const [systemElements, setSystemElements] = useState<ReactElement[]>([]);
@ -77,13 +77,13 @@ const SystemInfoComponent: React.FC<{ systemInfo: Types.SystemInfo | undefined }
const Settings = (props: BackstoryPageProps) => {
const { apiClient } = useAuth();
const { setSnack } = useAppState();
const [editSystemPrompt, setEditSystemPrompt] = useState<string>("");
// const [editSystemPrompt, setEditSystemPrompt] = useState<string>("");
const [systemInfo, setSystemInfo] = useState<Types.SystemInfo | undefined>(undefined);
const [tools, setTools] = useState<Tool[]>([]);
const [rags, setRags] = useState<Tool[]>([]);
const [systemPrompt, setSystemPrompt] = useState<string>("");
const [messageHistoryLength, setMessageHistoryLength] = useState<number>(5);
const [serverTunables, setServerTunables] = useState<ServerTunables | undefined>(undefined);
// const [tools, setTools] = useState<Tool[]>([]);
// const [rags, setRags] = useState<Tool[]>([]);
// const [systemPrompt, setSystemPrompt] = useState<string>("");
// const [messageHistoryLength, setMessageHistoryLength] = useState<number>(5);
// const [serverTunables, setServerTunables] = useState<ServerTunables | undefined>(undefined);
// useEffect(() => {
// if (serverTunables === undefined || systemPrompt === serverTunables.system_prompt || !systemPrompt.trim()) {
@ -179,14 +179,14 @@ const Settings = (props: BackstoryPageProps) => {
fetchSystemInfo();
}, [systemInfo, setSystemInfo, setSnack])
}, [systemInfo, setSystemInfo, setSnack, apiClient]);
useEffect(() => {
if (!systemPrompt) {
return;
}
setEditSystemPrompt(systemPrompt.trim());
}, [systemPrompt, setEditSystemPrompt]);
// useEffect(() => {
// if (!systemPrompt) {
// return;
// }
// setEditSystemPrompt(systemPrompt.trim());
// }, [systemPrompt, setEditSystemPrompt]);
// const toggleRag = async (tool: Tool) => {
// tool.enabled = !tool.enabled

View File

@ -1,4 +1,4 @@
import React, { useEffect, useState } from "react";
import React, { useEffect } from "react";
import { useParams, useNavigate } from "react-router-dom";
import { Box } from "@mui/material";
@ -37,7 +37,7 @@ const CandidateRoute: React.FC<CandidateRouteProps> = (props: CandidateRouteProp
}
getCandidate(username);
}, [selectedCandidate, username, selectedCandidate, navigate, setSnack, apiClient]);
}, [setSelectedCandidate, selectedCandidate, username, navigate, setSnack, apiClient]);
if (selectedCandidate?.username !== username) {
return (<Box>

View File

@ -1210,35 +1210,6 @@ class ApiClient {
return this.handleApiResponseWithConversion<T>(response);
}
/**
* Retry mechanism for rate-limited requests
*/
private async retryWithBackoff<T>(
requestFn: () => Promise<Response>,
maxRetries: number = 3
): Promise<T> {
let lastError: Error;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
const response = await requestFn();
return await this.handleApiResponseWithRateLimit<T>(response);
} catch (error) {
lastError = error as Error;
if (error instanceof RateLimitError && attempt < maxRetries) {
const delayMs = Math.min(error.retryAfterSeconds * 1000, 60000); // Max 1 minute
console.warn(`Rate limited, retrying in ${delayMs}ms (attempt ${attempt + 1}/${maxRetries + 1})`);
await new Promise(resolve => setTimeout(resolve, delayMs));
continue;
}
throw error;
}
}
throw lastError!;
}
async resetChatSession(id: string): Promise<{ success: boolean; message: string }> {
const response = await fetch(`${this.baseUrl}/chat/sessions/${id}/reset`, {
method: 'PATCH',