Added some mobile view work
This commit is contained in:
parent
ed69096ef0
commit
a5bf96437e
@ -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 }}> </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>
|
||||
);
|
||||
|
@ -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> at
|
||||
|
@ -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())
|
||||
|
Loading…
x
Reference in New Issue
Block a user