'use client'; import { useState } from 'react'; import { Spinner } from './spinner'; import { useSession } from 'next-auth/react'; type ResumeJSON = Partial<{ header: Partial<{ name: string; title: string; contact: Partial<{ email: string; phone: string; location: string; links: string[] }> }>; summary: string; skills: Partial<{ categories: Array> }>; experience: Array>; projects: Array>; education: Array>; certifications: Array>; links: Array>; }>; interface ResumeViewProps { resume: ResumeJSON | null | undefined; roleName?: string; badgeId?: string; isPublic?: boolean; showDownloadButton?: boolean; } function asText(value: unknown): string { if (value === undefined || value === null) return ''; if (typeof value === 'string') return value; return String(value); } function hasText(value: unknown): boolean { return asText(value).trim().length > 0; } function hasAnyText(...values: unknown[]): boolean { return values.some(hasText); } const Row = ({ label, value }: { label: string; value?: string }) => { if (!value) return null; return (
{label}
{value}
); }; export default function ResumeView({ resume, roleName, badgeId, isPublic, showDownloadButton }: ResumeViewProps) { const [isDownloading, setIsDownloading] = useState(false); const { data: session } = useSession(); const header = resume?.header || {}; const contact = header?.contact || {} as any; const skills = resume?.skills || {} as any; const experience = Array.isArray(resume?.experience) ? (resume?.experience as any[]) : []; const projects = Array.isArray(resume?.projects) ? (resume?.projects as any[]) : []; const education = Array.isArray(resume?.education) ? (resume?.education as any[]) : []; const certifications = Array.isArray(resume?.certifications) ? (resume?.certifications as any[]) : []; const links = Array.isArray(resume?.links) ? (resume?.links as any[]) : []; const canDownload = !!(showDownloadButton && badgeId); // Determine which sections have meaningful content and filter empty entries const filteredSkillCategories = Array.isArray(skills?.categories) ? (skills.categories as any[]).filter((cat: any) => Array.isArray(cat?.items) && cat.items.some(hasText)) : []; const hasSkillsSection = filteredSkillCategories.length > 0; const filteredExperience = experience.filter((exp: any) => hasAnyText(exp?.title, exp?.company, exp?.startDate, exp?.endDate) || (Array.isArray(exp?.highlights) && exp.highlights.some(hasText)) || (Array.isArray(exp?.technologies) && exp.technologies.some(hasText)) ); const hasExperienceSection = filteredExperience.length > 0; const filteredProjects = projects.filter((p: any) => hasAnyText(p?.name, p?.description, p?.impact, p?.link) || (Array.isArray(p?.technologies) && p.technologies.some(hasText)) ); const hasProjectsSection = filteredProjects.length > 0; const filteredEducation = education.filter((e: any) => hasAnyText(e?.degree, e?.institution, e?.graduationDate)); const hasEducationSection = filteredEducation.length > 0; const filteredCertifications = certifications.filter((c: any) => hasAnyText(c?.name, c?.issuer, c?.date)); const hasCertificationsSection = filteredCertifications.length > 0; const filteredLinks = links.filter((l: any) => hasAnyText(l?.label, l?.url)); const hasLinksSection = filteredLinks.length > 0; const handleDownloadResume = async () => { if (!badgeId) return; try { setIsDownloading(true); const apiGatewayUrl = process.env.NEXT_PUBLIC_API_GATEWAY_URL; if (!apiGatewayUrl) throw new Error('API not configured'); const usePublic = !!isPublic; const idToken = (session as any)?.id_token; const path = usePublic ? `/share/badge/${badgeId}/resume` : `/user/assessment/${badgeId}/resume`; const res = await fetch(`${apiGatewayUrl}${path}`, { method: 'GET', headers: (!usePublic && idToken) ? { 'Authorization': `Bearer ${idToken}` } : {} }); const data = await res.json().catch(() => ({})); if (!res.ok) throw new Error(data?.error || 'Unable to fetch Resume'); const url = data?.url; if (!url) throw new Error('Missing URL'); if (typeof window !== 'undefined') window.location.href = url; } catch (e) { // eslint-disable-next-line no-console console.error('[ResumeView] Failed to download resume', e); } finally { setIsDownloading(false); } }; return (
{asText(header?.name) || 'Resume'}
{asText(header?.title)}
{roleName ? (
Role: {roleName}
) : null}
{[asText(contact?.location), asText(contact?.email), asText(contact?.phone)].filter(Boolean).join(' • ')}
{canDownload ? ( ) : null}
{asText(resume?.summary) ? (
Summary
{asText(resume?.summary)}
) : null} {hasSkillsSection ? (
Skills
{filteredSkillCategories.map((cat: any, idx: number) => ( ))}
) : null} {hasExperienceSection ? (
Experience
{filteredExperience.map((exp: any, idx: number) => (
{asText(exp?.title)}{asText(exp?.company) ? ` — ${asText(exp?.company)}` : ''}
{(asText(exp?.startDate) || asText(exp?.endDate)) ? `${asText(exp?.startDate)} – ${asText(exp?.endDate) || 'Present'}` : ''}
{Array.isArray(exp?.highlights) && exp.highlights.filter(hasText).length > 0 ? (
    {exp.highlights.filter(hasText).map((h: any, i: number) => (
  • {asText(h)}
  • ))}
) : null} {Array.isArray(exp?.technologies) && exp.technologies.filter(hasText).length > 0 ? (
Technologies: {exp.technologies.filter(hasText).join(', ')}
) : null}
))}
) : null} {hasProjectsSection ? (
Projects
{filteredProjects.map((p: any, idx: number) => (
{asText(p?.name)}
{asText(p?.description) ?
{asText(p?.description)}
: null} {asText(p?.impact) ?
Impact: {asText(p?.impact)}
: null} {Array.isArray(p?.technologies) && p.technologies.filter(hasText).length > 0 ? (
Technologies: {p.technologies.filter(hasText).join(', ')}
) : null} {asText(p?.link) ?
{asText(p?.link)}
: null}
))}
) : null} {hasEducationSection ? (
Education
{filteredEducation.map((e: any, idx: number) => (
{`${asText(e?.degree)} — ${asText(e?.institution)}${asText(e?.graduationDate) ? ` (${asText(e?.graduationDate)})` : ''}`}
))}
) : null} {hasCertificationsSection ? (
Certifications
{filteredCertifications.map((c: any, idx: number) => (
{`${asText(c?.name)} — ${asText(c?.issuer)}${asText(c?.date) ? ` (${asText(c?.date)})` : ''}`}
))}
) : null} {hasLinksSection ? (
Links
{filteredLinks.map((l: any, idx: number) => (
{`${asText(l?.label)}: ${asText(l?.url)}`}
))}
) : null}
); }