Add supportsPhoto flag to template registry and conditionally render photo URL field
123 lines
4.8 KiB
TypeScript
123 lines
4.8 KiB
TypeScript
import React from 'react';
|
|
import type { CV } from '../schema/cvSchema';
|
|
import { sanitizeHtml } from '../schema/cvSchema';
|
|
|
|
interface MinimalTemplateProps { cv: CV; className?: string; }
|
|
|
|
// Minimal clean template with generous whitespace and serif headings
|
|
const MinimalTemplate: React.FC<MinimalTemplateProps> = ({ cv, className = '' }) => {
|
|
const { personal, summary, work = [], education = [], skills = [], languages = [], certifications = [] } = cv;
|
|
|
|
return (
|
|
<div className={`bg-white text-gray-900 max-w-3xl mx-auto px-8 py-6 ${className}`} style={{ fontFamily: 'ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial' }}>
|
|
<header className="mb-6">
|
|
<h1 className="text-3xl font-serif tracking-tight">{(personal?.firstName || '')} {(personal?.lastName || '')}</h1>
|
|
<div className="mt-2 text-sm text-gray-600 flex flex-wrap gap-2">
|
|
{personal?.email && <span>{personal.email}</span>}
|
|
{personal?.phone && <span>{personal.phone}</span>}
|
|
{(personal?.city || personal?.country) && (
|
|
<span>
|
|
{personal.city}{personal.city && personal.country ? ', ' : ''}{personal.country}
|
|
</span>
|
|
)}
|
|
</div>
|
|
</header>
|
|
|
|
{summary && (
|
|
<section className="mb-6">
|
|
<h2 className="text-xl font-serif mb-2">Summary</h2>
|
|
<div className="prose max-w-none" dangerouslySetInnerHTML={{ __html: sanitizeHtml(summary) }} />
|
|
</section>
|
|
)}
|
|
|
|
{work.length > 0 && (
|
|
<section className="mb-6">
|
|
<h2 className="text-xl font-serif mb-2">Experience</h2>
|
|
<div className="space-y-4">
|
|
{work.map((job) => (
|
|
<article key={job.id}>
|
|
<div className="flex justify-between items-start">
|
|
<div>
|
|
<h3 className="text-base font-semibold">{job.title}</h3>
|
|
<div className="text-sm text-gray-600">{job.company}</div>
|
|
</div>
|
|
<div className="text-sm text-gray-600 text-right">
|
|
{job.startDate} - {job.endDate || 'Present'}
|
|
{job.location && <div>{job.location}</div>}
|
|
</div>
|
|
</div>
|
|
{job.bullets && job.bullets.length > 0 && (
|
|
<ul className="list-disc pl-6 mt-2">
|
|
{job.bullets.map((b, i) => (
|
|
<li key={i}>{b}</li>
|
|
))}
|
|
</ul>
|
|
)}
|
|
</article>
|
|
))}
|
|
</div>
|
|
</section>
|
|
)}
|
|
|
|
{education.length > 0 && (
|
|
<section className="mb-6">
|
|
<h2 className="text-xl font-serif mb-2">Education</h2>
|
|
<div className="space-y-3">
|
|
{education.map((ed) => (
|
|
<article key={ed.id}>
|
|
<div className="flex justify-between items-start">
|
|
<div>
|
|
<h3 className="text-base font-semibold">{ed.degree}</h3>
|
|
<div className="text-sm text-gray-600">{ed.school}</div>
|
|
</div>
|
|
<div className="text-sm text-gray-600">{ed.startDate}{ed.endDate && <> - {ed.endDate}</>}</div>
|
|
</div>
|
|
</article>
|
|
))}
|
|
</div>
|
|
</section>
|
|
)}
|
|
|
|
{(skills.length > 0 || languages.length > 0 || certifications.length > 0) && (
|
|
<section className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
{skills.length > 0 && (
|
|
<div>
|
|
<h3 className="text-base font-serif mb-2">Skills</h3>
|
|
<div className="flex flex-wrap gap-2">
|
|
{skills.map((s) => (
|
|
<span key={s.id} className="px-2 py-1 rounded bg-gray-100 text-gray-800 text-sm">
|
|
{s.name}{s.level ? ` — ${s.level}` : ''}
|
|
</span>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
{languages.length > 0 && (
|
|
<div>
|
|
<h3 className="text-base font-serif mb-2">Languages</h3>
|
|
<div className="flex flex-wrap gap-2">
|
|
{languages.map((l) => (
|
|
<span key={l.id} className="px-2 py-1 rounded bg-gray-100 text-gray-800 text-sm">
|
|
{l.name}{l.level ? ` — ${l.level}` : ''}
|
|
</span>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
{certifications.length > 0 && (
|
|
<div>
|
|
<h3 className="text-base font-serif mb-2">Certifications</h3>
|
|
<ul className="list-disc pl-6">
|
|
{certifications.map((c) => (
|
|
<li key={c.id}>{c.name}{c.issuer ? ` - ${c.issuer}` : ''}{c.date ? ` (${c.date})` : ''}</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
)}
|
|
</section>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default MinimalTemplate; |