From a5bf96437ef98127e275725e3ff223f8c2fcda4f Mon Sep 17 00:00:00 2001 From: James Ketrenos Date: Wed, 16 Jul 2025 18:41:01 -0700 Subject: [PATCH] Added some mobile view work --- frontend/src/components/ui/ResumePreview.tsx | 26 ++++++----- frontend/src/pages/CandidateChatPage.tsx | 1 + src/backend/routes/candidates.py | 48 ++++++-------------- 3 files changed, 30 insertions(+), 45 deletions(-) diff --git a/frontend/src/components/ui/ResumePreview.tsx b/frontend/src/components/ui/ResumePreview.tsx index 00bda6f..827627f 100644 --- a/frontend/src/components/ui/ResumePreview.tsx +++ b/frontend/src/components/ui/ResumePreview.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Box, Typography, SxProps, Theme } from '@mui/material'; +import { Box, Typography, SxProps, Theme, useMediaQuery, useTheme } from '@mui/material'; import { Email as EmailIcon, Phone as PhoneIcon, @@ -305,9 +305,10 @@ export const resumeStyles: Record = generateResumeStyles(); interface StyledHeaderProps { candidate: Types.Candidate; style: ResumeStyle; + isMobile: boolean; } -const StyledHeader: React.FC = ({ candidate, style }) => { +const StyledHeader: React.FC = ({ candidate, style, isMobile }) => { const phone = parsePhoneNumberFromString(candidate.phone || '', 'US'); return ( @@ -329,7 +330,7 @@ const StyledHeader: React.FC = ({ candidate, style }) => { = ({ candidate, style }) => { // Styled Footer Component interface StyledFooterProps { - candidate: Types.Candidate; - job?: Types.Job; + resume: Types.Resume; style: ResumeStyle; + isMobile: boolean; } -const StyledFooter: React.FC = ({ candidate, job, style }) => { +const StyledFooter: React.FC = ({ resume, style, isMobile }) => { return ( <> = ({ candidate, job, style }) => sx={{ ...style.footerStyle, color: style.color.secondary, + fontSize: isMobile ? '0.6rem' : '1rem', }} > Dive deeper into my qualifications at Backstory... - {candidate?.username - ? `${window.location.protocol}://${window.location.host}/u/${candidate?.username}` - : 'backstory'} + {window.location.protocol}://{window.location.host}/chat/{resume.id}   @@ -470,6 +470,8 @@ export const ResumePreview: React.FC = (props: ResumePreview const currentStyle = resumeStyles[selectedStyle] || resumeStyles.corporate; const job: Types.Job | null = resume.job || null; const candidate: Types.Candidate | null = resume.candidate || null; + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down('md')); if (!resume || !candidate || !job) { return ( @@ -505,7 +507,7 @@ export const ResumePreview: React.FC = (props: ResumePreview }} > {/* Custom Header */} - + {/* Styled Markdown Content */} @@ -523,7 +525,7 @@ export const ResumePreview: React.FC = (props: ResumePreview {/* QR Code Footer */} - {job && } + {job && } ); diff --git a/frontend/src/pages/CandidateChatPage.tsx b/frontend/src/pages/CandidateChatPage.tsx index 71da23d..7bcfba4 100644 --- a/frontend/src/pages/CandidateChatPage.tsx +++ b/frontend/src/pages/CandidateChatPage.tsx @@ -341,6 +341,7 @@ const CandidateChatPage = forwardRef display: 'flex', justifyContent: 'center', p: 1, + fontSize: isMobile ? '0.8rem' : '1rem', }} > {resume.job?.title} at  diff --git a/src/backend/routes/candidates.py b/src/backend/routes/candidates.py index ff26b04..40ccf30 100644 --- a/src/backend/routes/candidates.py +++ b/src/backend/routes/candidates.py @@ -58,6 +58,7 @@ from models import ( RAGDocumentRequest, RagContentMetadata, RagContentResponse, + Resume, SkillAssessment, SkillMatchRequest, SkillStrength, @@ -716,55 +717,36 @@ async def get_candidate_profile_image( status_code=500, content=create_error_response("FETCH_ERROR", "Failed to retrieve profile image") ) -@router.get("/qr-code/{candidate_id}/{job_id}") +@router.get("/qr-code/{resume_id}") async def get_candidate_qr_code( - candidate_id: str = Path(..., description="ID of the candidate"), - job_id: Optional[str] = Path(..., description="ID of the candidate"), + resume_id: str = Path(..., description="ID of the resume"), # current_user = Depends(get_current_user), database: RedisDatabase = Depends(get_database), ): """Get profile image of a candidate by username""" try: - all_candidates_data = await database.get_all_candidates() - candidates_list = [Candidate.model_validate(data) for data in all_candidates_data.values()] - - # Normalize username to lowercase for case-insensitive search - query_lower = candidate_id.lower() - - # Filter by search query - candidates_list = [c for c in candidates_list if query_lower == c.id.lower()] - - if not len(candidates_list): + resume_data = await database.get_resume(resume_id=resume_id) + if not resume_data: + logger.warning(f"⚠️ Resume not found for ID: {resume_id}") + return JSONResponse(status_code=404, content=create_error_response("NOT_FOUND", "Resume not found")) + resume: Resume = Resume.model_validate(resume_data) + candidate_data = await database.get_candidate(resume.candidate_id) + if not candidate_data: + logger.warning(f"⚠️ Candidate not found for resume ID: {resume_id}") return JSONResponse(status_code=404, content=create_error_response("NOT_FOUND", "Candidate not found")) - - candidate = Candidate.model_validate(candidates_list[0]) - - job = None - if job_id: - job_data = await database.get_job(job_id) - if not job_data: - logger.warning(f"⚠️ Job not found for ID: {job_id}") - return JSONResponse( - status_code=404, - content=create_error_response("JOB_NOT_FOUND", f"Job with id '{job_id}' not found"), - ) - job = Job.model_validate(job_data) - - file_name = f"{job.id}.png" if job else "qrcode.png" + candidate: Candidate = Candidate.model_validate(candidate_data) + file_name = f"resume-{resume_id}.png" file_path = pathlib.Path(defines.user_dir) / candidate.username / file_name if not file_path.exists(): import pyqrcode - if job: - qrobj = pyqrcode.create(f"{defines.frontend_url}/job-analysis/{candidate.id}/{job.id}/2") - else: - qrobj = pyqrcode.create(f"{defines.frontend_url}/u/{candidate.id}") + qrobj = pyqrcode.create(f"{defines.frontend_url}/chat/{resume.id}") with open(file_path, "wb") as f: qrobj.png(f, scale=2) return FileResponse( file_path, media_type=f"image/{file_path.suffix[1:]}", # Get extension without dot - filename="qrcode.png", + filename=file_name, ) except Exception as e: logger.error(backstory_traceback.format_exc())