Introduce modern, editorial, and minimal visual themes for live preview and ATS-compliant PDF exports
This commit is contained in:
parent
d9e108ba1c
commit
a8dddccbdb
2 changed files with 485 additions and 182 deletions
|
|
@ -166,19 +166,19 @@ export async function GET(req: Request) {
|
||||||
doc.on("error", (err) => reject(err));
|
doc.on("error", (err) => reject(err));
|
||||||
});
|
});
|
||||||
|
|
||||||
// --- Header ---
|
// --- Modern ATS Header ---
|
||||||
doc
|
doc
|
||||||
.fontSize(22)
|
.fontSize(24)
|
||||||
.fillColor("#111827")
|
.fillColor("#0f172a")
|
||||||
.font("Helvetica-Bold")
|
.font("Helvetica-Bold")
|
||||||
.text(name, { align: "center" });
|
.text(name, 40, 40, { align: "left" });
|
||||||
|
|
||||||
if (headline) {
|
if (headline) {
|
||||||
doc
|
doc
|
||||||
.fontSize(12)
|
.fontSize(11)
|
||||||
.fillColor("#2563eb")
|
.fillColor("#2563eb")
|
||||||
.font("Helvetica-Bold")
|
.font("Helvetica-Bold")
|
||||||
.text(headline, { align: "center" });
|
.text(headline.toUpperCase(), { align: "left" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const contactParts = [];
|
const contactParts = [];
|
||||||
|
|
@ -188,14 +188,14 @@ export async function GET(req: Request) {
|
||||||
|
|
||||||
if (contactParts.length > 0) {
|
if (contactParts.length > 0) {
|
||||||
doc
|
doc
|
||||||
.fontSize(9)
|
.fontSize(8.5)
|
||||||
.fillColor("#4b5563")
|
.fillColor("#64748b")
|
||||||
.font("Helvetica")
|
.font("Helvetica")
|
||||||
.text(contactParts.join(" | "), { align: "center" });
|
.text(contactParts.join(" | "), { align: "left" });
|
||||||
}
|
}
|
||||||
|
|
||||||
doc.moveDown(0.8);
|
doc.moveDown(0.6);
|
||||||
doc.strokeColor("#e5e7eb").lineWidth(1).moveTo(40, doc.y).lineTo(572, doc.y).stroke();
|
doc.strokeColor("#0f172a").lineWidth(1.5).moveTo(40, doc.y).lineTo(572, doc.y).stroke();
|
||||||
doc.moveDown(0.8);
|
doc.moveDown(0.8);
|
||||||
|
|
||||||
// --- Professional Summary ---
|
// --- Professional Summary ---
|
||||||
|
|
|
||||||
|
|
@ -3,140 +3,339 @@
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Document, Page, Text, View, StyleSheet, PDFDownloadLink } from "@react-pdf/renderer";
|
import { Document, Page, Text, View, StyleSheet, PDFDownloadLink } from "@react-pdf/renderer";
|
||||||
|
|
||||||
const pdfStyles = StyleSheet.create({
|
// Styles for React-PDF export by theme
|
||||||
page: {
|
const themesPDF = {
|
||||||
padding: 35,
|
modern: StyleSheet.create({
|
||||||
fontFamily: "Helvetica",
|
page: {
|
||||||
fontSize: 9.5,
|
padding: 38,
|
||||||
color: "#18181b",
|
fontFamily: "Helvetica",
|
||||||
lineHeight: 1.45,
|
fontSize: 9.5,
|
||||||
},
|
color: "#1e293b",
|
||||||
header: {
|
lineHeight: 1.45,
|
||||||
marginBottom: 14,
|
},
|
||||||
borderBottomWidth: 1.5,
|
header: {
|
||||||
borderBottomColor: "#18181b",
|
marginBottom: 14,
|
||||||
paddingBottom: 8,
|
borderBottomWidth: 2,
|
||||||
},
|
borderBottomColor: "#0f172a",
|
||||||
name: {
|
paddingBottom: 10,
|
||||||
fontSize: 18,
|
},
|
||||||
fontFamily: "Helvetica-Bold",
|
name: {
|
||||||
color: "#09090b",
|
fontSize: 22,
|
||||||
marginBottom: 2,
|
fontFamily: "Helvetica-Bold",
|
||||||
textTransform: "uppercase",
|
color: "#0f172a",
|
||||||
letterSpacing: 0.5,
|
marginBottom: 3,
|
||||||
},
|
letterSpacing: -0.2,
|
||||||
title: {
|
},
|
||||||
fontSize: 10.5,
|
title: {
|
||||||
color: "#3f3f46",
|
fontSize: 11,
|
||||||
marginBottom: 4,
|
color: "#2563eb",
|
||||||
fontFamily: "Helvetica-Bold",
|
marginBottom: 6,
|
||||||
},
|
fontFamily: "Helvetica-Bold",
|
||||||
contact: {
|
textTransform: "uppercase",
|
||||||
fontSize: 8.5,
|
letterSpacing: 0.8,
|
||||||
color: "#52525b",
|
},
|
||||||
},
|
contact: {
|
||||||
section: {
|
fontSize: 8.5,
|
||||||
marginBottom: 12,
|
color: "#64748b",
|
||||||
},
|
},
|
||||||
sectionTitle: {
|
section: {
|
||||||
fontSize: 9.5,
|
marginBottom: 13,
|
||||||
fontFamily: "Helvetica-Bold",
|
},
|
||||||
color: "#09090b",
|
sectionTitle: {
|
||||||
borderBottomWidth: 1,
|
fontSize: 10,
|
||||||
borderBottomColor: "#e4e4e7",
|
fontFamily: "Helvetica-Bold",
|
||||||
paddingBottom: 2,
|
color: "#0f172a",
|
||||||
marginBottom: 6,
|
borderBottomWidth: 1,
|
||||||
textTransform: "uppercase",
|
borderBottomColor: "#cbd5e1",
|
||||||
letterSpacing: 0.5,
|
paddingBottom: 3,
|
||||||
},
|
marginBottom: 7,
|
||||||
jobHeader: {
|
textTransform: "uppercase",
|
||||||
flexDirection: "row",
|
letterSpacing: 0.8,
|
||||||
justifyContent: "space-between",
|
},
|
||||||
marginBottom: 2,
|
jobHeader: {
|
||||||
},
|
flexDirection: "row",
|
||||||
jobTitle: {
|
justifyContent: "space-between",
|
||||||
fontFamily: "Helvetica-Bold",
|
alignItems: "baseline",
|
||||||
fontSize: 9.5,
|
marginBottom: 2,
|
||||||
},
|
},
|
||||||
company: {
|
jobTitle: {
|
||||||
color: "#52525b",
|
fontFamily: "Helvetica-Bold",
|
||||||
fontFamily: "Helvetica",
|
fontSize: 10,
|
||||||
},
|
color: "#0f172a",
|
||||||
bullet: {
|
},
|
||||||
marginLeft: 8,
|
company: {
|
||||||
marginBottom: 2,
|
color: "#2563eb",
|
||||||
fontSize: 8.5,
|
fontFamily: "Helvetica-Bold",
|
||||||
color: "#27272a",
|
fontSize: 9.5,
|
||||||
},
|
},
|
||||||
});
|
bullet: {
|
||||||
|
marginLeft: 10,
|
||||||
|
marginBottom: 2.5,
|
||||||
|
fontSize: 9,
|
||||||
|
color: "#334155",
|
||||||
|
lineHeight: 1.4,
|
||||||
|
},
|
||||||
|
skillsRow: {
|
||||||
|
flexDirection: "row",
|
||||||
|
flexWrap: "wrap",
|
||||||
|
gap: 4,
|
||||||
|
},
|
||||||
|
skillBadge: {
|
||||||
|
backgroundColor: "#f1f5f9",
|
||||||
|
borderWidth: 0.5,
|
||||||
|
borderColor: "#cbd5e1",
|
||||||
|
borderRadius: 3,
|
||||||
|
paddingVertical: 2,
|
||||||
|
paddingHorizontal: 6,
|
||||||
|
fontSize: 8,
|
||||||
|
color: "#334155",
|
||||||
|
fontFamily: "Helvetica-Bold",
|
||||||
|
marginBottom: 3,
|
||||||
|
marginRight: 4,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
const ResumePDFDocument = ({ data }: { data: any }) => (
|
editorial: StyleSheet.create({
|
||||||
<Document>
|
page: {
|
||||||
<Page size="A4" style={pdfStyles.page}>
|
padding: 40,
|
||||||
<View style={pdfStyles.header}>
|
fontFamily: "Times-Roman",
|
||||||
<Text style={pdfStyles.name}>{data.profile?.name || "Applicant Name"}</Text>
|
fontSize: 10,
|
||||||
{data.profile?.title && <Text style={pdfStyles.title}>{data.profile.title}</Text>}
|
color: "#1c1917",
|
||||||
<Text style={pdfStyles.contact}>
|
lineHeight: 1.5,
|
||||||
{[data.profile?.email, data.profile?.phone, data.profile?.location].filter(Boolean).join(" | ")}
|
},
|
||||||
</Text>
|
header: {
|
||||||
</View>
|
marginBottom: 16,
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
borderBottomColor: "#1c1917",
|
||||||
|
paddingBottom: 10,
|
||||||
|
textAlign: "center",
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
fontSize: 24,
|
||||||
|
fontFamily: "Times-Bold",
|
||||||
|
color: "#1c1917",
|
||||||
|
marginBottom: 4,
|
||||||
|
letterSpacing: 0.5,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: 11,
|
||||||
|
color: "#78350f",
|
||||||
|
marginBottom: 5,
|
||||||
|
fontFamily: "Times-Italic",
|
||||||
|
},
|
||||||
|
contact: {
|
||||||
|
fontSize: 9,
|
||||||
|
color: "#57534e",
|
||||||
|
},
|
||||||
|
section: {
|
||||||
|
marginBottom: 14,
|
||||||
|
},
|
||||||
|
sectionTitle: {
|
||||||
|
fontSize: 10.5,
|
||||||
|
fontFamily: "Times-Bold",
|
||||||
|
color: "#1c1917",
|
||||||
|
borderBottomWidth: 0.5,
|
||||||
|
borderBottomColor: "#a8a29e",
|
||||||
|
paddingBottom: 2,
|
||||||
|
marginBottom: 7,
|
||||||
|
textTransform: "uppercase",
|
||||||
|
letterSpacing: 1,
|
||||||
|
},
|
||||||
|
jobHeader: {
|
||||||
|
flexDirection: "row",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
marginBottom: 2,
|
||||||
|
},
|
||||||
|
jobTitle: {
|
||||||
|
fontFamily: "Times-Bold",
|
||||||
|
fontSize: 10.5,
|
||||||
|
color: "#1c1917",
|
||||||
|
},
|
||||||
|
company: {
|
||||||
|
color: "#44403c",
|
||||||
|
fontFamily: "Times-Italic",
|
||||||
|
fontSize: 10,
|
||||||
|
},
|
||||||
|
bullet: {
|
||||||
|
marginLeft: 10,
|
||||||
|
marginBottom: 2.5,
|
||||||
|
fontSize: 9.5,
|
||||||
|
color: "#292524",
|
||||||
|
},
|
||||||
|
skillsRow: {
|
||||||
|
flexDirection: "row",
|
||||||
|
flexWrap: "wrap",
|
||||||
|
},
|
||||||
|
skillBadge: {
|
||||||
|
fontSize: 9,
|
||||||
|
color: "#292524",
|
||||||
|
fontFamily: "Times-Roman",
|
||||||
|
marginRight: 6,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
{data.profile?.summary && (
|
minimal: StyleSheet.create({
|
||||||
<View style={pdfStyles.section}>
|
page: {
|
||||||
<Text style={pdfStyles.sectionTitle}>Professional Summary</Text>
|
padding: 36,
|
||||||
<Text style={{ fontSize: 9, color: "#27272a" }}>{data.profile.summary}</Text>
|
fontFamily: "Helvetica",
|
||||||
|
fontSize: 9,
|
||||||
|
color: "#18181b",
|
||||||
|
lineHeight: 1.4,
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
marginBottom: 14,
|
||||||
|
paddingBottom: 8,
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
borderBottomColor: "#e4e4e7",
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
fontSize: 20,
|
||||||
|
fontFamily: "Helvetica-Bold",
|
||||||
|
color: "#09090b",
|
||||||
|
marginBottom: 2,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: 10,
|
||||||
|
color: "#52525b",
|
||||||
|
marginBottom: 4,
|
||||||
|
fontFamily: "Helvetica",
|
||||||
|
},
|
||||||
|
contact: {
|
||||||
|
fontSize: 8.5,
|
||||||
|
color: "#71717a",
|
||||||
|
},
|
||||||
|
section: {
|
||||||
|
marginBottom: 12,
|
||||||
|
},
|
||||||
|
sectionTitle: {
|
||||||
|
fontSize: 9,
|
||||||
|
fontFamily: "Helvetica-Bold",
|
||||||
|
color: "#09090b",
|
||||||
|
borderBottomWidth: 0.5,
|
||||||
|
borderBottomColor: "#e4e4e7",
|
||||||
|
paddingBottom: 2,
|
||||||
|
marginBottom: 6,
|
||||||
|
textTransform: "uppercase",
|
||||||
|
letterSpacing: 0.5,
|
||||||
|
},
|
||||||
|
jobHeader: {
|
||||||
|
flexDirection: "row",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
marginBottom: 2,
|
||||||
|
},
|
||||||
|
jobTitle: {
|
||||||
|
fontFamily: "Helvetica-Bold",
|
||||||
|
fontSize: 9.5,
|
||||||
|
},
|
||||||
|
company: {
|
||||||
|
color: "#52525b",
|
||||||
|
fontFamily: "Helvetica",
|
||||||
|
},
|
||||||
|
bullet: {
|
||||||
|
marginLeft: 8,
|
||||||
|
marginBottom: 2,
|
||||||
|
fontSize: 8.5,
|
||||||
|
color: "#27272a",
|
||||||
|
},
|
||||||
|
skillsRow: {
|
||||||
|
flexDirection: "row",
|
||||||
|
flexWrap: "wrap",
|
||||||
|
},
|
||||||
|
skillBadge: {
|
||||||
|
fontSize: 8.5,
|
||||||
|
color: "#27272a",
|
||||||
|
marginRight: 8,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const ResumePDFDocument = ({ data, theme = "modern" }: { data: any; theme?: "modern" | "editorial" | "minimal" }) => {
|
||||||
|
const styles = themesPDF[theme] || themesPDF.modern;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Document>
|
||||||
|
<Page size="A4" style={styles.page}>
|
||||||
|
{/* Header */}
|
||||||
|
<View style={styles.header}>
|
||||||
|
<Text style={styles.name}>{data.profile?.name || "Applicant Name"}</Text>
|
||||||
|
{data.profile?.title && <Text style={styles.title}>{data.profile.title}</Text>}
|
||||||
|
<Text style={styles.contact}>
|
||||||
|
{[data.profile?.email, data.profile?.phone, data.profile?.location].filter(Boolean).join(" | ")}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
)}
|
|
||||||
|
|
||||||
{Array.isArray(data.work) && data.work.length > 0 && (
|
{/* Summary */}
|
||||||
<View style={pdfStyles.section}>
|
{data.profile?.summary && (
|
||||||
<Text style={pdfStyles.sectionTitle}>Professional Experience</Text>
|
<View style={styles.section}>
|
||||||
{data.work.map((item: any, i: number) => (
|
<Text style={styles.sectionTitle}>Professional Summary</Text>
|
||||||
<View key={i} style={{ marginBottom: 8 }}>
|
<Text style={{ fontSize: theme === "editorial" ? 9.5 : 9, color: theme === "modern" ? "#334155" : "#27272a" }}>
|
||||||
<View style={pdfStyles.jobHeader}>
|
{data.profile.summary}
|
||||||
<Text style={pdfStyles.jobTitle}>
|
</Text>
|
||||||
{item.position || "Position"}{" "}
|
</View>
|
||||||
{item.company ? <Text style={pdfStyles.company}>— {item.company}</Text> : null}
|
)}
|
||||||
|
|
||||||
|
{/* Experience */}
|
||||||
|
{Array.isArray(data.work) && data.work.length > 0 && (
|
||||||
|
<View style={styles.section}>
|
||||||
|
<Text style={styles.sectionTitle}>Professional Experience</Text>
|
||||||
|
{data.work.map((item: any, i: number) => (
|
||||||
|
<View key={i} style={{ marginBottom: 9 }}>
|
||||||
|
<View style={styles.jobHeader}>
|
||||||
|
<Text style={styles.jobTitle}>{item.position || "Position"}</Text>
|
||||||
|
{item.company && <Text style={styles.company}>{item.company}</Text>}
|
||||||
|
</View>
|
||||||
|
{item.summary && (
|
||||||
|
<Text style={{ marginBottom: 3, fontSize: 8.5, color: "#64748b" }}>
|
||||||
|
{item.summary}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
{Array.isArray(item.highlights) &&
|
||||||
|
item.highlights.map((h: string, hIdx: number) => (
|
||||||
|
<Text key={hIdx} style={styles.bullet}>• {h}</Text>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Skills */}
|
||||||
|
{Array.isArray(data.skills) && data.skills.length > 0 && (
|
||||||
|
<View style={styles.section}>
|
||||||
|
<Text style={styles.sectionTitle}>Skills & Technical Proficiencies</Text>
|
||||||
|
<View style={styles.skillsRow}>
|
||||||
|
{theme === "modern" ? (
|
||||||
|
data.skills.map((s: string, idx: number) => (
|
||||||
|
<Text key={idx} style={styles.skillBadge}>{s}</Text>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<Text style={{ fontSize: 9 }}>{data.skills.join(" • ")}</Text>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Education */}
|
||||||
|
{Array.isArray(data.education) && data.education.length > 0 && (
|
||||||
|
<View style={styles.section}>
|
||||||
|
<Text style={styles.sectionTitle}>Education & Certifications</Text>
|
||||||
|
{data.education.map((edu: any, i: number) => (
|
||||||
|
<View key={i} style={{ marginBottom: 4 }}>
|
||||||
|
<Text style={styles.jobTitle}>
|
||||||
|
{edu.degree || "Degree / Program"}
|
||||||
|
{edu.institution ? ` — ${edu.institution}` : ""}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
{item.summary && <Text style={{ marginBottom: 3, fontSize: 8.5 }}>{item.summary}</Text>}
|
))}
|
||||||
{Array.isArray(item.highlights) &&
|
</View>
|
||||||
item.highlights.map((h: string, hIdx: number) => (
|
)}
|
||||||
<Text key={hIdx} style={pdfStyles.bullet}>• {h}</Text>
|
</Page>
|
||||||
))}
|
</Document>
|
||||||
</View>
|
);
|
||||||
))}
|
};
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{Array.isArray(data.skills) && data.skills.length > 0 && (
|
|
||||||
<View style={pdfStyles.section}>
|
|
||||||
<Text style={pdfStyles.sectionTitle}>Skills & Competencies</Text>
|
|
||||||
<Text style={{ fontSize: 9 }}>{data.skills.join(" • ")}</Text>
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{Array.isArray(data.education) && data.education.length > 0 && (
|
|
||||||
<View style={pdfStyles.section}>
|
|
||||||
<Text style={pdfStyles.sectionTitle}>Education & Certifications</Text>
|
|
||||||
{data.education.map((edu: any, i: number) => (
|
|
||||||
<View key={i} style={{ marginBottom: 4 }}>
|
|
||||||
<Text style={pdfStyles.jobTitle}>
|
|
||||||
{edu.degree && edu.institution
|
|
||||||
? `${edu.degree} — ${edu.institution}`
|
|
||||||
: edu.degree || edu.institution || "High School / Technical Education"}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
))}
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
</Page>
|
|
||||||
</Document>
|
|
||||||
);
|
|
||||||
|
|
||||||
export function ResumePreview({ data }: { data: any }) {
|
export function ResumePreview({ data }: { data: any }) {
|
||||||
const [isClient, setIsClient] = useState(false);
|
const [isClient, setIsClient] = useState(false);
|
||||||
const [theme, setTheme] = useState<"classic" | "modern" | "compact">("classic");
|
const [theme, setTheme] = useState<"modern" | "editorial" | "minimal">("modern");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsClient(true);
|
setIsClient(true);
|
||||||
|
|
@ -148,78 +347,147 @@ export function ResumePreview({ data }: { data: any }) {
|
||||||
const education = data.education || [];
|
const education = data.education || [];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-white border border-zinc-200 rounded-md p-5 sticky top-16 shadow-sm">
|
<div className="bg-white border border-stone-300 rounded-sm p-5 sticky top-16 shadow-[2px_2px_0px_#1c1917]">
|
||||||
<div className="flex items-center justify-between pb-3 border-b border-zinc-200 mb-4 flex-wrap gap-2">
|
<div className="flex items-center justify-between pb-3 border-b border-stone-200 mb-4 flex-wrap gap-2">
|
||||||
<div className="flex items-center space-x-3">
|
<div className="flex items-center space-x-3">
|
||||||
<span className="text-xs font-mono font-bold text-zinc-900 uppercase tracking-wider">
|
<span className="text-xs font-mono font-bold text-stone-900 uppercase tracking-wider">
|
||||||
Live ATS Preview
|
ATS Visual Template
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<select
|
<select
|
||||||
value={theme}
|
value={theme}
|
||||||
onChange={(e) => setTheme(e.target.value as any)}
|
onChange={(e) => setTheme(e.target.value as any)}
|
||||||
className="text-[11px] font-mono border border-zinc-200 rounded px-2 py-0.5 bg-zinc-50 text-zinc-800"
|
className="text-xs font-mono border border-stone-300 rounded-none px-2.5 py-1 bg-stone-50 text-stone-900 font-semibold focus:outline-none focus:ring-1 focus:ring-stone-900"
|
||||||
>
|
>
|
||||||
<option value="classic">Theme: Classic ATS</option>
|
<option value="modern">Modern Tech (Navy & Slate Accent)</option>
|
||||||
<option value="modern">Theme: Modern Tech</option>
|
<option value="editorial">Executive Editorial (Warm Serif & Rust)</option>
|
||||||
<option value="compact">Theme: Compact Executive</option>
|
<option value="minimal">Clean Monochrome (Standard ATS)</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{isClient && (
|
{isClient && (
|
||||||
<PDFDownloadLink
|
<PDFDownloadLink
|
||||||
document={<ResumePDFDocument data={data} />}
|
document={<ResumePDFDocument data={data} theme={theme} />}
|
||||||
fileName={`${(profile.name || "Resume").replace(/\s+/g, "_")}_ATS.pdf`}
|
fileName={`${(profile.name || "Resume").replace(/\s+/g, "_")}_${theme.toUpperCase()}_ATS.pdf`}
|
||||||
className="text-xs font-mono font-semibold bg-zinc-900 hover:bg-zinc-800 text-white px-3 py-1 rounded transition-colors inline-block"
|
className="text-xs font-mono font-semibold bg-stone-900 hover:bg-stone-800 text-stone-100 px-3.5 py-1.5 border border-stone-950 shadow-[1px_1px_0px_#000] transition-colors inline-block"
|
||||||
>
|
>
|
||||||
Download ATS PDF
|
Export Styled PDF
|
||||||
</PDFDownloadLink>
|
</PDFDownloadLink>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Styled Responsive Visual Document View */}
|
||||||
<div
|
<div
|
||||||
className={`bg-white border p-6 rounded text-xs space-y-4 max-h-[680px] overflow-y-auto ${
|
className={`bg-white border p-7 text-xs space-y-5 max-h-[680px] overflow-y-auto transition-all ${
|
||||||
theme === "modern"
|
theme === "modern"
|
||||||
? "border-zinc-300 font-sans shadow-inner"
|
? "border-slate-300 font-sans shadow-md"
|
||||||
: theme === "compact"
|
: theme === "editorial"
|
||||||
? "border-zinc-200 font-mono text-[11px]"
|
? "border-amber-200/80 font-serif shadow-md bg-[#fffdfa]"
|
||||||
: "border-zinc-300 font-sans"
|
: "border-zinc-300 font-sans"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="border-b pb-3 border-zinc-300">
|
{/* Header Block */}
|
||||||
<h2 className="text-lg font-bold text-zinc-900 uppercase tracking-tight">{profile.name || "Your Full Name"}</h2>
|
<div
|
||||||
<p className="text-zinc-700 font-bold text-xs mt-0.5">{profile.title || "Target Job Title"}</p>
|
className={`pb-4 ${
|
||||||
<div className="text-zinc-500 mt-1 flex flex-wrap gap-x-3 text-[11px] font-mono">
|
theme === "modern"
|
||||||
|
? "border-b-2 border-slate-900"
|
||||||
|
: theme === "editorial"
|
||||||
|
? "border-b border-stone-800 text-center"
|
||||||
|
: "border-b border-zinc-300"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<h2
|
||||||
|
className={`text-2xl font-bold tracking-tight ${
|
||||||
|
theme === "modern"
|
||||||
|
? "text-slate-950"
|
||||||
|
: theme === "editorial"
|
||||||
|
? "font-serif text-3xl text-stone-950"
|
||||||
|
: "text-zinc-950 uppercase"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{profile.name || "Your Full Name"}
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<p
|
||||||
|
className={`text-xs font-semibold mt-1 uppercase tracking-wider ${
|
||||||
|
theme === "modern"
|
||||||
|
? "text-blue-700 font-mono"
|
||||||
|
: theme === "editorial"
|
||||||
|
? "text-amber-800 italic normal-case text-sm"
|
||||||
|
: "text-zinc-600 font-mono"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{profile.title || "Target Professional Title"}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={`mt-2 flex flex-wrap gap-x-3 gap-y-1 text-[11px] font-mono ${
|
||||||
|
theme === "editorial" ? "justify-center text-stone-600" : "text-slate-600"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
{profile.email && <span>{profile.email}</span>}
|
{profile.email && <span>{profile.email}</span>}
|
||||||
{profile.phone && <span>{profile.phone}</span>}
|
{profile.phone && <span>• {profile.phone}</span>}
|
||||||
{profile.location && <span>{profile.location}</span>}
|
{profile.location && <span>• {profile.location}</span>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Summary Block */}
|
||||||
{profile.summary && (
|
{profile.summary && (
|
||||||
<div>
|
<div className="space-y-1.5">
|
||||||
<h3 className="font-bold text-zinc-900 border-b border-zinc-200 pb-0.5 mb-1 uppercase text-[10px] tracking-wider font-mono">
|
<h3
|
||||||
Summary
|
className={`font-mono text-[10px] font-bold uppercase tracking-wider pb-0.5 border-b ${
|
||||||
|
theme === "modern"
|
||||||
|
? "text-slate-900 border-slate-200"
|
||||||
|
: theme === "editorial"
|
||||||
|
? "text-stone-900 border-stone-300"
|
||||||
|
: "text-zinc-800 border-zinc-200"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
Professional Summary
|
||||||
</h3>
|
</h3>
|
||||||
<p className="leading-relaxed text-zinc-700">{profile.summary}</p>
|
<p className="leading-relaxed text-stone-700 text-xs">{profile.summary}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Experience Block */}
|
||||||
{work.length > 0 && (
|
{work.length > 0 && (
|
||||||
<div>
|
<div className="space-y-2">
|
||||||
<h3 className="font-bold text-zinc-900 border-b border-zinc-200 pb-0.5 mb-2 uppercase text-[10px] tracking-wider font-mono">
|
<h3
|
||||||
|
className={`font-mono text-[10px] font-bold uppercase tracking-wider pb-0.5 border-b ${
|
||||||
|
theme === "modern"
|
||||||
|
? "text-slate-900 border-slate-200"
|
||||||
|
: theme === "editorial"
|
||||||
|
? "text-stone-900 border-stone-300"
|
||||||
|
: "text-zinc-800 border-zinc-200"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
Professional Experience
|
Professional Experience
|
||||||
</h3>
|
</h3>
|
||||||
<div className="space-y-3">
|
|
||||||
|
<div className="space-y-4">
|
||||||
{work.map((w: any, idx: number) => (
|
{work.map((w: any, idx: number) => (
|
||||||
<div key={idx}>
|
<div key={idx} className="space-y-1">
|
||||||
<p className="font-bold text-zinc-900">
|
<div className="flex justify-between items-baseline flex-wrap gap-1">
|
||||||
{w.position || "Position"}{" "}
|
<span className="font-bold text-slate-950 text-sm">{w.position || "Position"}</span>
|
||||||
{w.company ? <span className="text-zinc-600 font-medium">— {w.company}</span> : null}
|
{w.company && (
|
||||||
</p>
|
<span
|
||||||
{w.summary && <p className="text-zinc-700 mt-0.5 text-xs">{w.summary}</p>}
|
className={`text-xs font-semibold ${
|
||||||
|
theme === "modern"
|
||||||
|
? "text-blue-700 font-mono"
|
||||||
|
: theme === "editorial"
|
||||||
|
? "text-amber-900 italic font-serif"
|
||||||
|
: "text-zinc-700 font-mono"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{w.company}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{w.summary && <p className="text-slate-600 italic text-xs mb-1">{w.summary}</p>}
|
||||||
|
|
||||||
{Array.isArray(w.highlights) && w.highlights.length > 0 && (
|
{Array.isArray(w.highlights) && w.highlights.length > 0 && (
|
||||||
<ul className="list-disc list-inside text-zinc-700 mt-1 space-y-0.5">
|
<ul className="list-disc list-outside ml-4 text-slate-700 space-y-1 text-xs leading-relaxed">
|
||||||
{w.highlights.map((h: string, hIdx: number) => (
|
{w.highlights.map((h: string, hIdx: number) => (
|
||||||
<li key={hIdx}>{h}</li>
|
<li key={hIdx}>{h}</li>
|
||||||
))}
|
))}
|
||||||
|
|
@ -231,26 +499,61 @@ export function ResumePreview({ data }: { data: any }) {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Skills Block */}
|
||||||
{skills.length > 0 && (
|
{skills.length > 0 && (
|
||||||
<div>
|
<div className="space-y-2">
|
||||||
<h3 className="font-bold text-zinc-900 border-b border-zinc-200 pb-0.5 mb-1.5 uppercase text-[10px] tracking-wider font-mono">
|
<h3
|
||||||
Skills & Competencies
|
className={`font-mono text-[10px] font-bold uppercase tracking-wider pb-0.5 border-b ${
|
||||||
|
theme === "modern"
|
||||||
|
? "text-slate-900 border-slate-200"
|
||||||
|
: theme === "editorial"
|
||||||
|
? "text-stone-900 border-stone-300"
|
||||||
|
: "text-zinc-800 border-zinc-200"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
Skills & Technical Proficiencies
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-zinc-800 font-mono text-[11px] leading-relaxed">{skills.join(" • ")}</p>
|
|
||||||
|
{theme === "modern" ? (
|
||||||
|
<div className="flex flex-wrap gap-1.5 pt-0.5">
|
||||||
|
{skills.map((s: string, idx: number) => (
|
||||||
|
<span
|
||||||
|
key={idx}
|
||||||
|
className="bg-slate-100 border border-slate-200 text-slate-800 text-[11px] font-mono px-2 py-0.5 rounded-sm font-medium"
|
||||||
|
>
|
||||||
|
{s}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<p className="text-slate-800 text-xs font-mono leading-relaxed">{skills.join(" • ")}</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Education Block */}
|
||||||
{education.length > 0 && (
|
{education.length > 0 && (
|
||||||
<div>
|
<div className="space-y-1.5">
|
||||||
<h3 className="font-bold text-zinc-900 border-b border-zinc-200 pb-0.5 mb-1.5 uppercase text-[10px] tracking-wider font-mono">
|
<h3
|
||||||
|
className={`font-mono text-[10px] font-bold uppercase tracking-wider pb-0.5 border-b ${
|
||||||
|
theme === "modern"
|
||||||
|
? "text-slate-900 border-slate-200"
|
||||||
|
: theme === "editorial"
|
||||||
|
? "text-stone-900 border-stone-300"
|
||||||
|
: "text-zinc-800 border-zinc-200"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
Education & Certifications
|
Education & Certifications
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
{education.map((edu: any, idx: number) => (
|
{education.map((edu: any, idx: number) => (
|
||||||
<p key={idx} className="text-zinc-800">
|
<div key={idx} className="text-xs text-slate-900">
|
||||||
<span className="font-bold">{edu.degree || edu.institution || "High School Diploma"}</span>
|
<span className="font-bold">{edu.degree || "Degree / Program"}</span>
|
||||||
{edu.institution && edu.degree ? <span className="text-zinc-600"> — {edu.institution}</span> : null}
|
{edu.institution && (
|
||||||
</p>
|
<span className="text-slate-600 font-medium"> — {edu.institution}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue