diff --git a/web/src/app/api/resume/download/route.ts b/web/src/app/api/resume/download/route.ts
index 422c440..ed91d25 100644
--- a/web/src/app/api/resume/download/route.ts
+++ b/web/src/app/api/resume/download/route.ts
@@ -166,19 +166,19 @@ export async function GET(req: Request) {
doc.on("error", (err) => reject(err));
});
- // --- Header ---
+ // --- Modern ATS Header ---
doc
- .fontSize(22)
- .fillColor("#111827")
+ .fontSize(24)
+ .fillColor("#0f172a")
.font("Helvetica-Bold")
- .text(name, { align: "center" });
+ .text(name, 40, 40, { align: "left" });
if (headline) {
doc
- .fontSize(12)
+ .fontSize(11)
.fillColor("#2563eb")
.font("Helvetica-Bold")
- .text(headline, { align: "center" });
+ .text(headline.toUpperCase(), { align: "left" });
}
const contactParts = [];
@@ -188,14 +188,14 @@ export async function GET(req: Request) {
if (contactParts.length > 0) {
doc
- .fontSize(9)
- .fillColor("#4b5563")
+ .fontSize(8.5)
+ .fillColor("#64748b")
.font("Helvetica")
- .text(contactParts.join(" | "), { align: "center" });
+ .text(contactParts.join(" | "), { align: "left" });
}
- doc.moveDown(0.8);
- doc.strokeColor("#e5e7eb").lineWidth(1).moveTo(40, doc.y).lineTo(572, doc.y).stroke();
+ doc.moveDown(0.6);
+ doc.strokeColor("#0f172a").lineWidth(1.5).moveTo(40, doc.y).lineTo(572, doc.y).stroke();
doc.moveDown(0.8);
// --- Professional Summary ---
diff --git a/web/src/components/ResumePreview.tsx b/web/src/components/ResumePreview.tsx
index 711d418..4d579d5 100644
--- a/web/src/components/ResumePreview.tsx
+++ b/web/src/components/ResumePreview.tsx
@@ -3,140 +3,339 @@
import React, { useEffect, useState } from "react";
import { Document, Page, Text, View, StyleSheet, PDFDownloadLink } from "@react-pdf/renderer";
-const pdfStyles = StyleSheet.create({
- page: {
- padding: 35,
- fontFamily: "Helvetica",
- fontSize: 9.5,
- color: "#18181b",
- lineHeight: 1.45,
- },
- header: {
- marginBottom: 14,
- borderBottomWidth: 1.5,
- borderBottomColor: "#18181b",
- paddingBottom: 8,
- },
- name: {
- fontSize: 18,
- fontFamily: "Helvetica-Bold",
- color: "#09090b",
- marginBottom: 2,
- textTransform: "uppercase",
- letterSpacing: 0.5,
- },
- title: {
- fontSize: 10.5,
- color: "#3f3f46",
- marginBottom: 4,
- fontFamily: "Helvetica-Bold",
- },
- contact: {
- fontSize: 8.5,
- color: "#52525b",
- },
- section: {
- marginBottom: 12,
- },
- sectionTitle: {
- fontSize: 9.5,
- fontFamily: "Helvetica-Bold",
- color: "#09090b",
- borderBottomWidth: 1,
- 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",
- },
-});
+// Styles for React-PDF export by theme
+const themesPDF = {
+ modern: StyleSheet.create({
+ page: {
+ padding: 38,
+ fontFamily: "Helvetica",
+ fontSize: 9.5,
+ color: "#1e293b",
+ lineHeight: 1.45,
+ },
+ header: {
+ marginBottom: 14,
+ borderBottomWidth: 2,
+ borderBottomColor: "#0f172a",
+ paddingBottom: 10,
+ },
+ name: {
+ fontSize: 22,
+ fontFamily: "Helvetica-Bold",
+ color: "#0f172a",
+ marginBottom: 3,
+ letterSpacing: -0.2,
+ },
+ title: {
+ fontSize: 11,
+ color: "#2563eb",
+ marginBottom: 6,
+ fontFamily: "Helvetica-Bold",
+ textTransform: "uppercase",
+ letterSpacing: 0.8,
+ },
+ contact: {
+ fontSize: 8.5,
+ color: "#64748b",
+ },
+ section: {
+ marginBottom: 13,
+ },
+ sectionTitle: {
+ fontSize: 10,
+ fontFamily: "Helvetica-Bold",
+ color: "#0f172a",
+ borderBottomWidth: 1,
+ borderBottomColor: "#cbd5e1",
+ paddingBottom: 3,
+ marginBottom: 7,
+ textTransform: "uppercase",
+ letterSpacing: 0.8,
+ },
+ jobHeader: {
+ flexDirection: "row",
+ justifyContent: "space-between",
+ alignItems: "baseline",
+ marginBottom: 2,
+ },
+ jobTitle: {
+ fontFamily: "Helvetica-Bold",
+ fontSize: 10,
+ color: "#0f172a",
+ },
+ company: {
+ color: "#2563eb",
+ fontFamily: "Helvetica-Bold",
+ 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 }) => (
-
-
-
- {data.profile?.name || "Applicant Name"}
- {data.profile?.title && {data.profile.title}}
-
- {[data.profile?.email, data.profile?.phone, data.profile?.location].filter(Boolean).join(" | ")}
-
-
+ editorial: StyleSheet.create({
+ page: {
+ padding: 40,
+ fontFamily: "Times-Roman",
+ fontSize: 10,
+ color: "#1c1917",
+ lineHeight: 1.5,
+ },
+ header: {
+ 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 && (
-
- Professional Summary
- {data.profile.summary}
+ minimal: StyleSheet.create({
+ page: {
+ padding: 36,
+ 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 (
+
+
+ {/* Header */}
+
+ {data.profile?.name || "Applicant Name"}
+ {data.profile?.title && {data.profile.title}}
+
+ {[data.profile?.email, data.profile?.phone, data.profile?.location].filter(Boolean).join(" | ")}
+
- )}
- {Array.isArray(data.work) && data.work.length > 0 && (
-
- Professional Experience
- {data.work.map((item: any, i: number) => (
-
-
-
- {item.position || "Position"}{" "}
- {item.company ? — {item.company} : null}
+ {/* Summary */}
+ {data.profile?.summary && (
+
+ Professional Summary
+
+ {data.profile.summary}
+
+
+ )}
+
+ {/* Experience */}
+ {Array.isArray(data.work) && data.work.length > 0 && (
+
+ Professional Experience
+ {data.work.map((item: any, i: number) => (
+
+
+ {item.position || "Position"}
+ {item.company && {item.company}}
+
+ {item.summary && (
+
+ {item.summary}
+
+ )}
+ {Array.isArray(item.highlights) &&
+ item.highlights.map((h: string, hIdx: number) => (
+ • {h}
+ ))}
+
+ ))}
+
+ )}
+
+ {/* Skills */}
+ {Array.isArray(data.skills) && data.skills.length > 0 && (
+
+ Skills & Technical Proficiencies
+
+ {theme === "modern" ? (
+ data.skills.map((s: string, idx: number) => (
+ {s}
+ ))
+ ) : (
+ {data.skills.join(" • ")}
+ )}
+
+
+ )}
+
+ {/* Education */}
+ {Array.isArray(data.education) && data.education.length > 0 && (
+
+ Education & Certifications
+ {data.education.map((edu: any, i: number) => (
+
+
+ {edu.degree || "Degree / Program"}
+ {edu.institution ? ` — ${edu.institution}` : ""}
- {item.summary && {item.summary}}
- {Array.isArray(item.highlights) &&
- item.highlights.map((h: string, hIdx: number) => (
- • {h}
- ))}
-
- ))}
-
- )}
-
- {Array.isArray(data.skills) && data.skills.length > 0 && (
-
- Skills & Competencies
- {data.skills.join(" • ")}
-
- )}
-
- {Array.isArray(data.education) && data.education.length > 0 && (
-
- Education & Certifications
- {data.education.map((edu: any, i: number) => (
-
-
- {edu.degree && edu.institution
- ? `${edu.degree} — ${edu.institution}`
- : edu.degree || edu.institution || "High School / Technical Education"}
-
-
- ))}
-
- )}
-
-
-);
+ ))}
+
+ )}
+
+
+ );
+};
export function ResumePreview({ data }: { data: any }) {
const [isClient, setIsClient] = useState(false);
- const [theme, setTheme] = useState<"classic" | "modern" | "compact">("classic");
+ const [theme, setTheme] = useState<"modern" | "editorial" | "minimal">("modern");
useEffect(() => {
setIsClient(true);
@@ -148,78 +347,147 @@ export function ResumePreview({ data }: { data: any }) {
const education = data.education || [];
return (
-
-
+
+
-
- Live ATS Preview
+
+ ATS Visual Template
{isClient && (
}
- fileName={`${(profile.name || "Resume").replace(/\s+/g, "_")}_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"
+ document={
}
+ fileName={`${(profile.name || "Resume").replace(/\s+/g, "_")}_${theme.toUpperCase()}_ATS.pdf`}
+ 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
)}
+ {/* Styled Responsive Visual Document View */}
-
-
{profile.name || "Your Full Name"}
-
{profile.title || "Target Job Title"}
-
+ {/* Header Block */}
+
+
+ {profile.name || "Your Full Name"}
+
+
+
+ {profile.title || "Target Professional Title"}
+
+
+
{profile.email && {profile.email}}
- {profile.phone && {profile.phone}}
- {profile.location && {profile.location}}
+ {profile.phone && • {profile.phone}}
+ {profile.location && • {profile.location}}
+ {/* Summary Block */}
{profile.summary && (
-
-
- Summary
+
+
+ Professional Summary
-
{profile.summary}
+
{profile.summary}
)}
+ {/* Experience Block */}
{work.length > 0 && (
-
-
+
+
Professional Experience
-
+
+
{work.map((w: any, idx: number) => (
-
-
- {w.position || "Position"}{" "}
- {w.company ? — {w.company} : null}
-
- {w.summary &&
{w.summary}
}
+
+
+ {w.position || "Position"}
+ {w.company && (
+
+ {w.company}
+
+ )}
+
+
+ {w.summary &&
{w.summary}
}
+
{Array.isArray(w.highlights) && w.highlights.length > 0 && (
-
+
{w.highlights.map((h: string, hIdx: number) => (
- {h}
))}
@@ -231,26 +499,61 @@ export function ResumePreview({ data }: { data: any }) {
)}
+ {/* Skills Block */}
{skills.length > 0 && (
-
-
- Skills & Competencies
+
+
+ Skills & Technical Proficiencies
-
{skills.join(" • ")}
+
+ {theme === "modern" ? (
+
+ {skills.map((s: string, idx: number) => (
+
+ {s}
+
+ ))}
+
+ ) : (
+
{skills.join(" • ")}
+ )}
)}
+ {/* Education Block */}
{education.length > 0 && (
-
-
+
+
Education & Certifications
+
{education.map((edu: any, idx: number) => (
-
- {edu.degree || edu.institution || "High School Diploma"}
- {edu.institution && edu.degree ? — {edu.institution} : null}
-
+
+ {edu.degree || "Degree / Program"}
+ {edu.institution && (
+ — {edu.institution}
+ )}
+
))}