Added some mobile view work

This commit is contained in:
James Ketr 2025-07-16 18:41:01 -07:00
parent ed69096ef0
commit a5bf96437e
3 changed files with 30 additions and 45 deletions

View File

@ -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<string, ResumeStyle> = generateResumeStyles();
interface StyledHeaderProps {
candidate: Types.Candidate;
style: ResumeStyle;
isMobile: boolean;
}
const StyledHeader: React.FC<StyledHeaderProps> = ({ candidate, style }) => {
const StyledHeader: React.FC<StyledHeaderProps> = ({ candidate, style, isMobile }) => {
const phone = parsePhoneNumberFromString(candidate.phone || '', 'US');
return (
@ -329,7 +330,7 @@ const StyledHeader: React.FC<StyledHeaderProps> = ({ candidate, style }) => {
<Box
sx={{
display: 'flex',
flexDirection: 'row',
flexDirection: isMobile ? 'column' : 'row',
alignItems: 'flex-start',
gap: 1,
}}
@ -426,12 +427,12 @@ const StyledHeader: React.FC<StyledHeaderProps> = ({ candidate, style }) => {
// Styled Footer Component
interface StyledFooterProps {
candidate: Types.Candidate;
job?: Types.Job;
resume: Types.Resume;
style: ResumeStyle;
isMobile: boolean;
}
const StyledFooter: React.FC<StyledFooterProps> = ({ candidate, job, style }) => {
const StyledFooter: React.FC<StyledFooterProps> = ({ resume, style, isMobile }) => {
return (
<>
<Box
@ -439,19 +440,18 @@ const StyledFooter: React.FC<StyledFooterProps> = ({ candidate, job, style }) =>
sx={{
...style.footerStyle,
color: style.color.secondary,
fontSize: isMobile ? '0.6rem' : '1rem',
}}
>
Dive deeper into my qualifications at Backstory...
<Box
component="img"
src={`/api/1.0/candidates/qr-code/${candidate.id || ''}/${(job && job.id) || ''}`}
src={`/api/1.0/candidates/qr-code/${resume.id}`}
alt="QR Code"
className="qr-code"
sx={{ display: 'flex', mt: 1, mb: 1 }}
/>
{candidate?.username
? `${window.location.protocol}://${window.location.host}/u/${candidate?.username}`
: 'backstory'}
{window.location.protocol}://{window.location.host}/chat/{resume.id}
</Box>
<Box sx={{ pb: 2 }}>&nbsp;</Box>
</>
@ -470,6 +470,8 @@ export const ResumePreview: React.FC<ResumePreviewProps> = (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<ResumePreviewProps> = (props: ResumePreview
}}
>
{/* Custom Header */}
<StyledHeader candidate={candidate} style={currentStyle} />
<StyledHeader isMobile={isMobile} candidate={candidate} style={currentStyle} />
{/* Styled Markdown Content */}
<Box sx={currentStyle.markdownStyle}>
@ -523,7 +525,7 @@ export const ResumePreview: React.FC<ResumePreviewProps> = (props: ResumePreview
</Box>
{/* QR Code Footer */}
{job && <StyledFooter candidate={candidate} job={job} style={currentStyle} />}
{job && <StyledFooter isMobile={isMobile} resume={resume} style={currentStyle} />}
</Box>
</Box>
);

View File

@ -341,6 +341,7 @@ const CandidateChatPage = forwardRef<ConversationHandle, CandidateChatPageProps>
display: 'flex',
justifyContent: 'center',
p: 1,
fontSize: isMobile ? '0.8rem' : '1rem',
}}
>
<strong>{resume.job?.title}</strong>&nbsp;at&nbsp;

View File

@ -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())