diff --git a/TEMPLATE_AUDIT_REPORT.md b/TEMPLATE_AUDIT_REPORT.md new file mode 100644 index 0000000..d1e35ad --- /dev/null +++ b/TEMPLATE_AUDIT_REPORT.md @@ -0,0 +1,276 @@ +# Template Audit Report + +Generated: 2025-07-23T10:25:30.877Z + +Total templates found: 27 + +## AcademicTemplate.tsx +- **Export Name:** AcademicTemplate +- **Has TemplateProps:** ✅ +- **Has Correct Export:** ✅ +- **Has Default Export:** ❌ + +✅ No issues found + +--- + +## BoldTemplate.tsx +- **Export Name:** BoldTemplate +- **Has TemplateProps:** ✅ +- **Has Correct Export:** ✅ +- **Has Default Export:** ❌ + +✅ No issues found + +--- + +## CleanMinimalistTemplate.tsx +- **Export Name:** CleanMinimalistTemplate +- **Has TemplateProps:** ✅ +- **Has Correct Export:** ✅ +- **Has Default Export:** ❌ + +✅ No issues found + +--- + +## CleanProfessionalTemplate.tsx +- **Export Name:** CleanProfessionalTemplate +- **Has TemplateProps:** ✅ +- **Has Correct Export:** ✅ +- **Has Default Export:** ❌ + +✅ No issues found + +--- + +## CorporateTemplate.tsx +- **Export Name:** CorporateTemplate +- **Has TemplateProps:** ✅ +- **Has Correct Export:** ✅ +- **Has Default Export:** ❌ + +✅ No issues found + +--- + +## CreativeColorBlocksTemplate.tsx +- **Export Name:** CreativeColorBlocksTemplate +- **Has TemplateProps:** ✅ +- **Has Correct Export:** ✅ +- **Has Default Export:** ❌ + +✅ No issues found + +--- + +## CreativeTemplate.tsx +- **Export Name:** CreativeTemplate +- **Has TemplateProps:** ✅ +- **Has Correct Export:** ✅ +- **Has Default Export:** ❌ + +✅ No issues found + +--- + +## ElegantTemplate.tsx +- **Export Name:** ElegantTemplate +- **Has TemplateProps:** ✅ +- **Has Correct Export:** ✅ +- **Has Default Export:** ❌ + +✅ No issues found + +--- + +## ExecutiveTemplate.tsx +- **Export Name:** ExecutiveTemplate +- **Has TemplateProps:** ✅ +- **Has Correct Export:** ✅ +- **Has Default Export:** ❌ + +✅ No issues found + +--- + +## MinimalistATS.tsx +- **Export Name:** MinimalistATS +- **Has TemplateProps:** ✅ +- **Has Correct Export:** ✅ +- **Has Default Export:** ❌ + +✅ No issues found + +--- + +## MinimalistProfessionalTemplate.tsx +- **Export Name:** MinimalistProfessionalTemplate +- **Has TemplateProps:** ✅ +- **Has Correct Export:** ✅ +- **Has Default Export:** ❌ + +✅ No issues found + +--- + +## MinimalistSidebarTemplate.tsx +- **Export Name:** MinimalistSidebarTemplate +- **Has TemplateProps:** ✅ +- **Has Correct Export:** ✅ +- **Has Default Export:** ❌ + +✅ No issues found + +--- + +## MinimalistTemplate.tsx +- **Export Name:** MinimalistTemplate +- **Has TemplateProps:** ✅ +- **Has Correct Export:** ✅ +- **Has Default Export:** ❌ + +✅ No issues found + +--- + +## MinimalTemplate.tsx +- **Export Name:** MinimalTemplate +- **Has TemplateProps:** ✅ +- **Has Correct Export:** ✅ +- **Has Default Export:** ❌ + +✅ No issues found + +--- + +## ModernDarkSidebarTemplate.tsx +- **Export Name:** ModernDarkSidebarTemplate +- **Has TemplateProps:** ✅ +- **Has Correct Export:** ✅ +- **Has Default Export:** ❌ + +✅ No issues found + +--- + +## ModernExecutiveTemplate.tsx +- **Export Name:** ModernExecutiveTemplate +- **Has TemplateProps:** ✅ +- **Has Correct Export:** ✅ +- **Has Default Export:** ❌ + +✅ No issues found + +--- + +## ModernProfessionalTemplate.tsx +- **Export Name:** ModernProfessionalTemplate +- **Has TemplateProps:** ✅ +- **Has Correct Export:** ✅ +- **Has Default Export:** ❌ + +✅ No issues found + +--- + +## ModernSidebarTemplate.tsx +- **Export Name:** ModernSidebarTemplate +- **Has TemplateProps:** ✅ +- **Has Correct Export:** ✅ +- **Has Default Export:** ❌ + +✅ No issues found + +--- + +## ModernTemplate.tsx +- **Export Name:** ModernTemplate +- **Has TemplateProps:** ✅ +- **Has Correct Export:** ✅ +- **Has Default Export:** ❌ + +✅ No issues found + +--- + +## ModernTimelineTemplate.tsx +- **Export Name:** ModernTimelineTemplate +- **Has TemplateProps:** ✅ +- **Has Correct Export:** ✅ +- **Has Default Export:** ❌ + +✅ No issues found + +--- + +## ModernTwoColumn.tsx +- **Export Name:** ModernTwoColumn +- **Has TemplateProps:** ✅ +- **Has Correct Export:** ✅ +- **Has Default Export:** ❌ + +✅ No issues found + +--- + +## ProfessionalBlueTemplate.tsx +- **Export Name:** ProfessionalBlueTemplate +- **Has TemplateProps:** ✅ +- **Has Correct Export:** ✅ +- **Has Default Export:** ❌ + +✅ No issues found + +--- + +## ProfessionalSidebarTemplate.tsx +- **Export Name:** ProfessionalSidebarTemplate +- **Has TemplateProps:** ✅ +- **Has Correct Export:** ✅ +- **Has Default Export:** ❌ + +✅ No issues found + +--- + +## ProfessionalSummaryTemplate.tsx +- **Export Name:** ProfessionalSummaryTemplate +- **Has TemplateProps:** ✅ +- **Has Correct Export:** ✅ +- **Has Default Export:** ❌ + +✅ No issues found + +--- + +## ProfessionalTemplate.tsx +- **Export Name:** ProfessionalTemplate +- **Has TemplateProps:** ✅ +- **Has Correct Export:** ✅ +- **Has Default Export:** ❌ + +✅ No issues found + +--- + +## TechTemplate.tsx +- **Export Name:** TechTemplate +- **Has TemplateProps:** ✅ +- **Has Correct Export:** ✅ +- **Has Default Export:** ❌ + +✅ No issues found + +--- + +## TimelineTemplate.tsx +- **Export Name:** TimelineTemplate +- **Has TemplateProps:** ✅ +- **Has Correct Export:** ✅ +- **Has Default Export:** ❌ + +✅ No issues found + +--- + diff --git a/backup/templates/AcademicTemplate.tsx b/backup/templates/AcademicTemplate.tsx new file mode 100644 index 0000000..0f959ea --- /dev/null +++ b/backup/templates/AcademicTemplate.tsx @@ -0,0 +1,383 @@ +import { ResumeData } from '@/types/resume'; +import { Mail, Phone, MapPin, Globe, Linkedin, Github, BookOpen, GraduationCap, Award, Briefcase, FileText, Book } from 'lucide-react'; + +interface TemplateProps { + data: ResumeData; +} + +export function AcademicTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications, projects, sections, customization } = data; + + const visibleSections = sections.filter(section => section.isVisible).sort((a, b) => a.order - b.order); + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + const formatUrl = (url: string) => { + if (!url) return ''; + return url.replace(/^https?:\/\/(www\.)?/, '').replace(/\/$/, ''); + }; + + const renderSection = (section: typeof sections[0]) => { + switch (section.type) { + case 'personalInfo': + return ( +
+
+

+ {personalInfo.fullName || 'Your Name'} +

+

+ {personalInfo.title || 'Researcher / Academic'} +

+ +
+ {personalInfo.email && ( + + + {personalInfo.email} + + )} + {personalInfo.phone && ( +
+ + {personalInfo.phone} +
+ )} + {personalInfo.location && ( +
+ + {personalInfo.location} +
+ )} + {personalInfo.website && ( + + + Personal Website + + )} + {personalInfo.linkedin && ( + + + LinkedIn + + )} + {personalInfo.github && ( + + + GitHub + + )} +
+
+
+ ); + + case 'summary': + if (!summary) return null; + return ( +
+

+ + Research Profile +

+

+ {summary} +

+
+ ); + + case 'education': + if (education.length === 0) return null; + return ( +
+

+ + Education +

+
+ {education.map((edu) => ( +
+
+
+

+ {edu.degree} +

+

+ {edu.school} + {edu.location && `, ${edu.location}`} +

+
+ + {formatDate(edu.graduationDate)} + +
+ {edu.gpa && ( +

+ GPA: {edu.gpa} +

+ )} + {edu.relevantCourses && edu.relevantCourses.length > 0 && ( +
+

+ Relevant Coursework: +

+
+ {edu.relevantCourses.map((course, i) => ( + + {course} + + ))} +
+
+ )} + {edu.description && ( +
+

+ {edu.description} +

+
+ )} +
+ ))} +
+
+ ); + + case 'experience': + if (experience.length === 0) return null; + return ( +
+

+ + {experience.some(exp => exp.company.toLowerCase().includes('research') || + exp.jobTitle.toLowerCase().includes('research')) + ? 'Research Experience' + : 'Professional Experience'} +

+
+ {experience.map((exp) => ( +
+
+
+

+ {exp.jobTitle} +

+

+ {exp.company} + {exp.location && ` • ${exp.location}`} +

+
+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+
    + {exp.description.filter(desc => desc.trim()).map((desc, index) => ( +
  • + + {desc} +
  • + ))} +
+
+ ))} +
+
+ ); + + case 'publications': + // Publications would be part of the projects array with type 'publication' + const publications = projects.filter(proj => proj.type === 'publication'); + if (publications.length === 0) return null; + + return ( +
+

+ + Publications +

+
+ {publications.map((pub) => ( +
+

+ {pub.name} +

+

+ {pub.description} +

+ {pub.technologies && pub.technologies.length > 0 && ( +
+ Keywords:{' '} + + {pub.technologies.join(', ')} + +
+ )} + {pub.url && ( + + View Publication + + )} +
+ ))} +
+
+ ); + + case 'projects': + // Filter out publications as they're handled separately + const nonPublicationProjects = projects.filter(proj => proj.type !== 'publication'); + if (nonPublicationProjects.length === 0) return null; + + return ( +
+

+ + Research Projects +

+
+ {nonPublicationProjects.map((project) => ( +
+
+

+ {project.name} + {project.url && ( + + (View Project) + + )} +

+ {project.startDate && ( + + {formatDate(project.startDate)} + {project.endDate ? ` - ${formatDate(project.endDate)}` : ' - Present'} + + )} +
+

+ {project.description} +

+ {project.technologies && project.technologies.length > 0 && ( +
+

+ Technologies/Methods: +

+
+ {project.technologies.map((tech, i) => ( + + {tech} + + ))} +
+
+ )} +
+ ))} +
+
+ ); + + case 'certifications': + if (certifications.length === 0) return null; + return ( +
+

+ + Certifications & Training +

+
+ {certifications.map((cert) => ( +
+

+ {cert.name} +

+

+ {cert.issuer} +

+

+ {formatDate(cert.date)} + {cert.expirationDate && ` - ${formatDate(cert.expirationDate)}`} +

+
+ ))} +
+
+ ); + + case 'skills': + if (skills.length === 0) return null; + return ( +
+

+ + Research Skills & Expertise +

+
+ {skills.map((skill) => ( +
+
+ + {skill.name} + + + {skill.level} + +
+
+
+
+
+ ))} +
+
+ ); + + default: + return null; + } + }; + + return ( +
+ {visibleSections.map(section => renderSection(section))} +
+ ); +} diff --git a/backup/templates/BoldTemplate.tsx b/backup/templates/BoldTemplate.tsx new file mode 100644 index 0000000..b4feeaf --- /dev/null +++ b/backup/templates/BoldTemplate.tsx @@ -0,0 +1,298 @@ +import { ResumeData } from '@/types/resume'; +import { Mail, Phone, MapPin, Globe, Linkedin, Github, Briefcase, GraduationCap, Code, Award, User } from 'lucide-react'; + +interface TemplateProps { + data: ResumeData; +} + +export function BoldTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications, projects, sections } = data; + const visibleSections = sections.filter(section => section.isVisible).sort((a, b) => a.order - b.order); + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + const formatUrl = (url: string) => { + if (!url) return ''; + return url.replace(/^https?:\/\/(www\.)?/, '').replace(/\/$/, ''); + }; + + const renderSection = (section: typeof sections[0]) => { + switch (section.type) { + case 'personalInfo': + return ( +
+
+
+

+ {personalInfo.fullName || 'Your Name'} +

+

+ {personalInfo.title || 'Professional Title'} +

+
+ +
+ {personalInfo.email && ( +
+ + {personalInfo.email} +
+ )} + {personalInfo.phone && ( +
+ + {personalInfo.phone} +
+ )} + {personalInfo.location && ( +
+ + {personalInfo.location} +
+ )} + {personalInfo.website && ( +
+ + {formatUrl(personalInfo.website)} +
+ )} + {personalInfo.linkedin && ( +
+ + linkedin.com/in/... +
+ )} + {personalInfo.github && ( +
+ + github.com/... +
+ )} +
+
+ + {summary && ( +
+

+ + PROFILE +

+

+ {summary} +

+
+ )} +
+ ); + + case 'experience': + if (experience.length === 0) return null; + return ( +
+

+ + EXPERIENCE +

+
+ {experience.map((exp) => ( +
+
+

+ {exp.jobTitle} +

+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+

+ {exp.company} + {exp.location && ` • ${exp.location}`} +

+
    + {exp.description.filter(desc => desc.trim()).map((desc, index) => ( +
  • + + {desc} +
  • + ))} +
+
+ ))} +
+
+ ); + + case 'education': + if (education.length === 0) return null; + return ( +
+

+ + EDUCATION +

+
+ {education.map((edu) => ( +
+
+

+ {edu.degree} +

+ + {formatDate(edu.graduationDate)} + +
+

+ {edu.school} + {edu.location && `, ${edu.location}`} +

+ {edu.gpa && ( +

+ GPA: {edu.gpa} +

+ )} + {edu.achievements && edu.achievements.length > 0 && ( +
    + {edu.achievements.map((achievement, idx) => ( +
  • + + {achievement} +
  • + ))} +
+ )} +
+ ))} +
+
+ ); + + case 'skills': + if (skills.length === 0) return null; + return ( +
+

+ + SKILLS & EXPERTISE +

+
+ {skills.map((skill) => ( +
+
+ + {skill.name} + + + {skill.level} + +
+
+
+
+
+ ))} +
+
+ ); + + case 'projects': + if (projects.length === 0) return null; + return ( +
+

+ + PROJECTS +

+
+ {projects.map((project) => ( +
+
+

+ {project.name} + {project.url && ( + + (View Project) + + )} +

+ {project.startDate && ( + + {formatDate(project.startDate)} + {project.endDate ? ` - ${formatDate(project.endDate)}` : ' - Present'} + + )} +
+

+ {project.description} +

+ {project.technologies && project.technologies.length > 0 && ( +
+ {project.technologies.map((tech, i) => ( + + {tech} + + ))} +
+ )} +
+ ))} +
+
+ ); + + case 'certifications': + if (certifications.length === 0) return null; + return ( +
+

+ + CERTIFICATIONS +

+
+ {certifications.map((cert) => ( +
+

+ {cert.name} +

+

+ {cert.issuer} +

+

+ {formatDate(cert.date)} + {cert.expirationDate && ` - ${formatDate(cert.expirationDate)}`} +

+
+ ))} +
+
+ ); + + default: + return null; + } + }; + + return ( +
+ {visibleSections.map(section => renderSection(section))} +
+ ); +} diff --git a/backup/templates/CleanMinimalistTemplate.tsx b/backup/templates/CleanMinimalistTemplate.tsx new file mode 100644 index 0000000..d15310a --- /dev/null +++ b/backup/templates/CleanMinimalistTemplate.tsx @@ -0,0 +1,198 @@ +import { Mail, Phone, MapPin, Linkedin, Globe } from 'lucide-react'; +import { ResumeData } from '@/types/resume'; + +type TemplateProps = { + data: ResumeData; +}; + +export function CleanMinimalistTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications } = data; + const accentColor = data.customization.accentColor || '#2563eb'; + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + return ( +
+ {/* Header */} +
+

+ {personalInfo.fullName} +

+ {personalInfo.title && ( +

+ {personalInfo.title} +

+ )} +
+ {personalInfo.email && ( + + + {personalInfo.email} + + )} + {personalInfo.phone && ( + + + {personalInfo.phone} + + )} + {personalInfo.location && ( + + + {personalInfo.location} + + )} + {personalInfo.linkedin && ( + + + LinkedIn + + )} + {personalInfo.website && ( + + + {personalInfo.website.replace(/^https?:\/\//, '')} + + )} +
+
+ + {/* Summary */} + {summary && ( +
+

+ SUMMARY +

+

+ {summary} +

+
+ )} + +
+ {/* Left Column */} +
+ {/* Experience */} + {experience.length > 0 && ( +
+

+ PROFESSIONAL EXPERIENCE +

+
+ {experience.map((exp) => ( +
+
+

{exp.jobTitle}

+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+

+ {exp.company} + {exp.location && `, ${exp.location}`} +

+
    + {exp.description.map((desc, idx) => ( +
  • + + {desc} +
  • + ))} +
+
+ ))} +
+
+ )} + + {/* Education */} + {education.length > 0 && ( +
+

+ EDUCATION +

+
+ {education.map((edu) => ( +
+

{edu.degree}

+

+ {edu.school} + {edu.location && `, ${edu.location}`} +

+
+ {edu.fieldOfStudy} + {formatDate(edu.graduationDate)} +
+ {edu.gpa && ( +

+ GPA: {edu.gpa} +

+ )} +
+ ))} +
+
+ )} +
+ + {/* Right Column */} +
+ {/* Skills */} + {skills.length > 0 && ( +
+

+ SKILLS +

+
+ {skills.map((skill) => ( +
+
+ {skill.name} + {skill.level} +
+
+
+
+
+ ))} +
+
+ )} + + {/* Certifications */} + {certifications.length > 0 && ( +
+

+ CERTIFICATIONS +

+
+ {certifications.map((cert) => ( +
+

{cert.name}

+

{cert.issuer}

+

+ {formatDate(cert.date)} + {cert.expirationDate && ` • Expires: ${formatDate(cert.expirationDate)}`} +

+
+ ))} +
+
+ )} +
+
+
+ ); +} diff --git a/backup/templates/CleanProfessionalTemplate.tsx b/backup/templates/CleanProfessionalTemplate.tsx new file mode 100644 index 0000000..0fce631 --- /dev/null +++ b/backup/templates/CleanProfessionalTemplate.tsx @@ -0,0 +1,200 @@ +import { Mail, Phone, MapPin, Globe, Linkedin, Github, Briefcase, GraduationCap, Code, Award } from 'lucide-react'; +import { TemplateProps } from './types'; + +export function CleanProfessionalTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications, projects, sections, customization } = data; + const visibleSections = sections.filter(section => section.isVisible).sort((a, b) => a.order - b.order); + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + const renderSection = (section: typeof sections[0]) => { + switch (section.type) { + case 'personalInfo': + return ( +
+
+

+ {personalInfo.fullName} +

+ {personalInfo.title && ( +

+ {personalInfo.title} +

+ )} +
+ {personalInfo.email && ( + + + {personalInfo.email} + + )} + {personalInfo.phone && ( + + + {personalInfo.phone} + + )} + {personalInfo.location && ( + + + {personalInfo.location} + + )} + {personalInfo.website && ( + + + {personalInfo.website.replace(/^https?:\/\//, '')} + + )} +
+
+
+ ); + + case 'summary': + if (!summary) return null; + return ( +
+

+ Professional Summary +

+

{summary}

+
+ ); + + case 'experience': + if (experience.length === 0) return null; + return ( +
+

+ + Professional Experience +

+
+ {experience.map((exp) => ( +
+
+
+

+ {exp.jobTitle} +

+

+ {exp.company} • {exp.location} +

+
+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+
    + {exp.description.map((desc, idx) => ( +
  • + + {desc} +
  • + ))} +
+
+ ))} +
+
+ ); + + case 'education': + if (education.length === 0) return null; + return ( +
+

+ + Education +

+
+ {education.map((edu) => ( +
+

+ {edu.degree} +

+

+ {edu.school} • {edu.location} +

+

+ {formatDate(edu.graduationDate)} + {edu.gpa && ` • GPA: ${edu.gpa}`} +

+
+ ))} +
+
+ ); + + case 'skills': + if (skills.length === 0) return null; + return ( +
+

+ + Skills +

+
+ {skills.map((skill) => ( +
+
+ {skill.name} + {skill.level} +
+
+
+
+
+ ))} +
+
+ ); + + case 'certifications': + if (certifications.length === 0) return null; + return ( +
+

+ + Certifications +

+
+ {certifications.map((cert) => ( +
+

+ {cert.name} +

+

+ {cert.issuer} • {formatDate(cert.date)} + {cert.expirationDate && ` • Expires: ${formatDate(cert.expirationDate)}`} +

+
+ ))} +
+
+ ); + + default: + return null; + } + }; + + return ( +
+ {visibleSections.map((section) => renderSection(section))} +
+ ); +} diff --git a/backup/templates/CorporateTemplate.tsx b/backup/templates/CorporateTemplate.tsx new file mode 100644 index 0000000..c54df7f --- /dev/null +++ b/backup/templates/CorporateTemplate.tsx @@ -0,0 +1,319 @@ +import { ResumeData } from '@/types/resume'; +import { Mail, Phone, MapPin, Globe, Linkedin, Github, Briefcase, GraduationCap, Code, Award, BookOpen, User } from 'lucide-react'; + +interface TemplateProps { + data: ResumeData; +} + +export function CorporateTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications, projects, sections, customization } = data; + + const visibleSections = sections.filter(section => section.isVisible).sort((a, b) => a.order - b.order); + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + const formatUrl = (url: string) => { + if (!url) return ''; + return url.replace(/^https?:\/\/(www\.)?/, '').replace(/\/$/, ''); + }; + + const renderSection = (section: typeof sections[0]) => { + switch (section.type) { + case 'personalInfo': + return ( +
+
+

+ {personalInfo.fullName || 'Your Name'} +

+

+ {personalInfo.title || 'Professional Title'} +

+ +
+ {personalInfo.email && ( + + + {personalInfo.email} + + )} + {personalInfo.phone && ( +
+ + {personalInfo.phone} +
+ )} + {personalInfo.location && ( +
+ + {personalInfo.location} +
+ )} + {personalInfo.website && ( + + + {formatUrl(personalInfo.website)} + + )} + {personalInfo.linkedin && ( + + + {formatUrl(personalInfo.linkedin).split('/').pop()} + + )} + {personalInfo.github && ( + + + {formatUrl(personalInfo.github).split('/').pop()} + + )} +
+
+
+ ); + + case 'summary': + if (!summary) return null; + return ( +
+

+ + Professional Summary +

+

+ {summary} +

+
+ ); + + case 'experience': + if (experience.length === 0) return null; + return ( +
+

+ + Professional Experience +

+
+ {experience.map((exp) => ( +
+
+
+

+ {exp.jobTitle} +

+

+ {exp.company} + {exp.location && ` • ${exp.location}`} +

+
+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+
    + {exp.description.filter(desc => desc.trim()).map((desc, index) => ( +
  • + + {desc} +
  • + ))} +
+
+ ))} +
+
+ ); + + case 'education': + if (education.length === 0) return null; + return ( +
+

+ + Education +

+
+ {education.map((edu) => ( +
+
+
+

+ {edu.degree} +

+

+ {edu.school} + {edu.location && `, ${edu.location}`} +

+ {edu.gpa && ( +

+ GPA: {edu.gpa} +

+ )} +
+ + {formatDate(edu.graduationDate)} + +
+ {edu.relevantCourses && edu.relevantCourses.length > 0 && ( +
+

+ Relevant Coursework: +

+
+ {edu.relevantCourses.map((course, i) => ( + + {course} + + ))} +
+
+ )} +
+ ))} +
+
+ ); + + case 'skills': + if (skills.length === 0) return null; + return ( +
+

+ + Skills & Expertise +

+
+ {skills.map((skill) => ( +
+
+ + {skill.name} + + + {skill.level} + +
+
+
+
+
+ ))} +
+
+ ); + + case 'certifications': + if (certifications.length === 0) return null; + return ( +
+

+ + Certifications +

+
+ {certifications.map((cert) => ( +
+

+ {cert.name} +

+

+ {cert.issuer} +

+

+ {formatDate(cert.date)} + {cert.expirationDate && ` - ${formatDate(cert.expirationDate)}`} +

+
+ ))} +
+
+ ); + + case 'projects': + if (projects.length === 0) return null; + return ( +
+

+ + Projects +

+
+ {projects.map((project) => ( +
+
+

+ {project.name} + {project.url && ( + + (View Project) + + )} +

+ {project.startDate && ( + + {formatDate(project.startDate)} + {project.endDate ? ` - ${formatDate(project.endDate)}` : ' - Present'} + + )} +
+

+ {project.description} +

+ {project.technologies && project.technologies.length > 0 && ( +
+ {project.technologies.map((tech, i) => ( + + {tech} + + ))} +
+ )} +
+ ))} +
+
+ ); + + default: + return null; + } + }; + + return ( +
+ {visibleSections.map(section => renderSection(section))} +
+ ); +} diff --git a/backup/templates/CreativeColorBlocksTemplate.tsx b/backup/templates/CreativeColorBlocksTemplate.tsx new file mode 100644 index 0000000..c7ec765 --- /dev/null +++ b/backup/templates/CreativeColorBlocksTemplate.tsx @@ -0,0 +1,220 @@ +import { ResumeData } from '@/types/resume'; +import { Briefcase, GraduationCap, User, Code, Mail, Phone, MapPin, Globe } from 'lucide-react'; + +interface TemplateProps { + data: ResumeData; +} + +export function CreativeColorBlocksTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, projects } = data; + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + // Color variants for different sections + const colorVariants = { + primary: 'bg-blue-600 text-white', + secondary: 'bg-blue-100 text-blue-900', + accent: 'bg-yellow-400 text-gray-900', + light: 'bg-gray-50 text-gray-800', + dark: 'bg-gray-800 text-white', + }; + + return ( +
+ {/* Header with color block */} +
+
+
+ {personalInfo.profileImage && ( +
+ {personalInfo.fullName +
+ )} +
+

{personalInfo.fullName || 'Your Name'}

+

{personalInfo.title || 'Professional Title'}

+
+
+
+
+ +
+
+ {/* Left Column */} +
+ {/* Contact */} +
+

CONTACT

+
+ {personalInfo.email && ( +
+ + {personalInfo.email} +
+ )} + {personalInfo.phone && ( +
+ + {personalInfo.phone} +
+ )} + {personalInfo.location && ( +
+ + {personalInfo.location} +
+ )} + {personalInfo.website && ( + + )} +
+
+ + {/* Skills */} + {skills.length > 0 && ( +
+

+ + SKILLS +

+
+ {skills.map((skill) => ( +
+
+ {skill.name} + {skill.level} +
+
+
+
+
+ ))} +
+
+ )} + + {/* Education */} + {education.length > 0 && ( +
+

+ + EDUCATION +

+
+ {education.map((edu) => ( +
+

{edu.degree}

+

+ {edu.school} + {edu.location && `, ${edu.location}`} +

+ {edu.graduationDate && ( +

+ {formatDate(edu.graduationDate)} + {edu.gpa && ` • GPA: ${edu.gpa}`} +

+ )} +
+ ))} +
+
+ )} +
+ + {/* Right Column */} +
+ {/* Summary */} + {summary && ( +
+

+ + ABOUT ME +

+

{summary}

+
+ )} + + {/* Experience */} + {experience.length > 0 && ( +
+

+ + EXPERIENCE +

+
+ {experience.map((exp) => ( +
+
+

{exp.jobTitle}

+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+

+ {exp.company} + {exp.location && ` • ${exp.location}`} +

+
    + {exp.description.filter(desc => desc.trim()).map((desc, index) => ( +
  • + + {desc} +
  • + ))} +
+
+ ))} +
+
+ )} + + {/* Projects */} + {projects && projects.length > 0 && ( +
+

PROJECTS

+
+ {projects.map((project) => ( +
+

{project.name}

+ {project.url && ( + + {project.url.replace(/^https?:\/\//, '')} + + )} +

{project.description}

+
+ ))} +
+
+ )} +
+
+
+
+ ); +} diff --git a/backup/templates/CreativeTemplate.tsx b/backup/templates/CreativeTemplate.tsx new file mode 100644 index 0000000..37bcbec --- /dev/null +++ b/backup/templates/CreativeTemplate.tsx @@ -0,0 +1,361 @@ +import React from 'react'; +import { ResumeData } from '@/types/resume'; +import { Mail, Phone, MapPin, Globe, Linkedin, Github } from 'lucide-react'; + +interface TemplateProps { + data: ResumeData; +} + +export function CreativeTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications, projects, references, sections, customization } = data; + + const visibleSections = sections.filter(section => section.isVisible).sort((a, b) => a.order - b.order); + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + const formatUrl = (url: string) => { + if (!url) return ''; + // Remove protocol and www for cleaner display + return url.replace(/^https?:\/\/(www\.)?/, ''); + }; + + const leftSections = visibleSections.filter(section => + ['personalInfo', 'skills', 'certifications', 'references'].includes(section.type) + ); + + const rightSections = visibleSections.filter(section => + ['summary', 'experience', 'education', 'projects'].includes(section.type) + ); + + const renderLeftSection = (section: typeof sections[0]) => { + switch (section.type) { + case 'personalInfo': + return ( +
+ {customization.showProfileImage && personalInfo.profileImage && ( + Profile + )} +

+ {personalInfo.fullName || 'Your Name'} +

+ +
+ {personalInfo.email && ( +
+ + {personalInfo.email} +
+ )} + {personalInfo.phone && ( +
+ + {personalInfo.phone} +
+ )} + {personalInfo.location && ( +
+ + {personalInfo.location} +
+ )} + {personalInfo.website && ( +
+ + {formatUrl(personalInfo.website)} +
+ )} + {personalInfo.linkedin && ( +
+ + {formatUrl(personalInfo.linkedin)} +
+ )} + {personalInfo.github && ( +
+ + {formatUrl(personalInfo.github)} +
+ )} +
+
+ ); + + case 'skills': + if (skills.length === 0) return null; + return ( +
+

+ Skills +

+
+ {skills.map((skill) => ( +
+
+ {skill.name} + {skill.level} +
+
+
+
+
+ ))} +
+
+ ); + + case 'certifications': + if (certifications.length === 0) return null; + return ( +
+

+ Certifications +

+
+ {certifications.map((cert) => ( +
+

+ {cert.name} +

+

+ {cert.issuer} +

+

+ {formatDate(cert.date)} + {cert.expirationDate && ` - ${formatDate(cert.expirationDate)}`} +

+
+ ))} +
+
+ ); + + case 'references': + if (references.length === 0) return null; + return ( +
+

+ References +

+
+ {references.map((ref) => ( +
+

+ {ref.name} +

+

+ {ref.title} +

+

+ {ref.company} +

+

+ {ref.relationship} +

+

+ {ref.email} • {ref.phone} +

+
+ ))} +
+
+ ); + + default: + return null; + } + }; + + const renderRightSection = (section: typeof sections[0]) => { + switch (section.type) { + case 'summary': + if (!summary) return null; + return ( +
+

+ Professional Summary +

+

+ {summary} +

+
+ ); + + case 'experience': + if (experience.length === 0) return null; + return ( +
+

+ Work Experience +

+
+ {experience.map((exp, index) => ( +
+
+ {index < experience.length - 1 && ( +
+ )} + +
+
+

+ {exp.jobTitle} +

+

+ {exp.company}{exp.location && `, ${exp.location}`} +

+
+
+ {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} +
+
+
    + {exp.description.filter(desc => desc.trim()).map((desc, descIndex) => ( +
  • {desc}
  • + ))} +
+
+ ))} +
+
+ ); + + case 'education': + if (education.length === 0) return null; + return ( +
+

+ Education +

+
+ {education.map((edu, index) => ( +
+
+ {index < education.length - 1 && ( +
+ )} + +
+
+

+ {edu.degree} +

+

+ {edu.school}{edu.location && `, ${edu.location}`} +

+ {edu.gpa && ( +

+ GPA: {edu.gpa} +

+ )} +
+
+ {formatDate(edu.graduationDate)} +
+
+
+ ))} +
+
+ ); + + case 'projects': + if (projects.length === 0) return null; + return ( +
+

+ Projects +

+
+ {projects.map((project, index) => ( +
+
+ {index < projects.length - 1 && ( +
+ )} + +
+

+ {project.name} +

+
+ {formatDate(project.startDate)} + {project.endDate && ` - ${formatDate(project.endDate)}`} +
+
+

+ {project.description} +

+ {project.technologies.length > 0 && ( +
+ {project.technologies.map((tech, techIndex) => ( + + {tech} + + ))} +
+ )} + {project.url && ( +

+ {formatUrl(project.url)} +

+ )} +
+ ))} +
+
+ ); + + default: + return null; + } + }; + + return ( +
+ {/* Left Sidebar */} +
+ {leftSections.map(renderLeftSection)} +
+ + {/* Right Content */} +
+ {rightSections.map(renderRightSection)} +
+
+ ); +} \ No newline at end of file diff --git a/backup/templates/ElegantTemplate.tsx b/backup/templates/ElegantTemplate.tsx new file mode 100644 index 0000000..c2dcbc4 --- /dev/null +++ b/backup/templates/ElegantTemplate.tsx @@ -0,0 +1,281 @@ +import { ResumeData } from '@/types/resume'; +import { Mail, Phone, MapPin, Globe, Linkedin, Github, Briefcase, GraduationCap, Code, Award, User } from 'lucide-react'; + +interface TemplateProps { + data: ResumeData; +} + +export function ElegantTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications, projects, sections } = data; + const visibleSections = sections.filter(section => section.isVisible).sort((a, b) => a.order - b.order); + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + const formatUrl = (url: string) => { + if (!url) return ''; + return url.replace(/^https?:\/\/(www\.)?/, '').replace(/\/$/, ''); + }; + + const renderSection = (section: typeof sections[0]) => { + switch (section.type) { + case 'personalInfo': + return ( +
+
+
+
+

+ {personalInfo.fullName || 'Your Name'} +

+
+

+ {personalInfo.title || 'Professional Title'} +

+
+ +
+ {personalInfo.email && ( +
+ + {personalInfo.email} +
+ )} + {personalInfo.phone && ( +
+ + {personalInfo.phone} +
+ )} + {personalInfo.location && ( +
+ + {personalInfo.location} +
+ )} + {personalInfo.website && ( +
+ + {formatUrl(personalInfo.website)} +
+ )} + {personalInfo.linkedin && ( +
+ + linkedin.com/in/... +
+ )} +
+
+ + {summary && ( +
+

+ + Profile +

+

+ {summary} +

+
+ )} +
+
+ ); + + case 'experience': + if (experience.length === 0) return null; + return ( +
+

+ + Professional Experience +

+
+ {experience.map((exp) => ( +
+
+

+ {exp.jobTitle} +

+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+

+ {exp.company} + {exp.location && ` • ${exp.location}`} +

+
    + {exp.description.filter(desc => desc.trim()).map((desc, index) => ( +
  • + + {desc} +
  • + ))} +
+
+ ))} +
+
+ ); + + case 'education': + if (education.length === 0) return null; + return ( +
+

+ + Education +

+
+ {education.map((edu) => ( +
+
+

+ {edu.degree} +

+ + {formatDate(edu.graduationDate)} + +
+

+ {edu.school} + {edu.location && `, ${edu.location}`} + {edu.gpa && ` • GPA: ${edu.gpa}`} +

+
+ ))} +
+
+ ); + + case 'skills': + if (skills.length === 0) return null; + return ( +
+

+ + Skills & Expertise +

+
+ {skills.map((skill) => ( +
+
+ + {skill.name} + + + {skill.level} + +
+
+
+
+
+ ))} +
+
+ ); + + case 'projects': + if (projects.length === 0) return null; + return ( +
+

+ + Projects +

+
+ {projects.map((project) => ( +
+
+

+ {project.name} + {project.url && ( + + (View Project) + + )} +

+ {project.startDate && ( + + {formatDate(project.startDate)} + {project.endDate ? ` - ${formatDate(project.endDate)}` : ' - Present'} + + )} +
+

+ {project.description} +

+ {project.technologies && project.technologies.length > 0 && ( +
+ {project.technologies.map((tech, i) => ( + + {tech} + + ))} +
+ )} +
+ ))} +
+
+ ); + + case 'certifications': + if (certifications.length === 0) return null; + return ( +
+

+ + Certifications +

+
+ {certifications.map((cert) => ( +
+

+ {cert.name} +

+

+ {cert.issuer} +

+

+ {formatDate(cert.date)} + {cert.expirationDate && ` - ${formatDate(cert.expirationDate)}`} +

+
+ ))} +
+
+ ); + + default: + return null; + } + }; + + return ( +
+ {visibleSections.map(section => renderSection(section))} +
+ ); +} diff --git a/backup/templates/ExecutiveTemplate.tsx b/backup/templates/ExecutiveTemplate.tsx new file mode 100644 index 0000000..5309599 --- /dev/null +++ b/backup/templates/ExecutiveTemplate.tsx @@ -0,0 +1,284 @@ +import React from 'react'; +import { ResumeData } from '@/types/resume'; +import { Mail, Phone, MapPin, Globe, Linkedin, Github, Award, Briefcase, GraduationCap, Code, BookOpen } from 'lucide-react'; + +interface TemplateProps { + data: ResumeData; +} + +export function ExecutiveTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications, projects, references, sections, customization } = data; + + const visibleSections = sections.filter(section => section.isVisible).sort((a, b) => a.order - b.order); + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + const formatUrl = (url: string) => { + if (!url) return ''; + return url.replace(/^https?:\/\/(www\.)?/, '').replace(/\/$/, ''); + }; + + const renderSection = (section: typeof sections[0]) => { + switch (section.type) { + case 'personalInfo': + return ( +
+
+

+ {personalInfo.fullName || 'Your Name'} +

+ {personalInfo.jobTitle && ( +

+ {personalInfo.jobTitle} +

+ )} +
+ +
+ {personalInfo.email && ( +
+ + {personalInfo.email} +
+ )} + {personalInfo.phone && ( +
+ + {personalInfo.phone} +
+ )} + {personalInfo.location && ( +
+ + {personalInfo.location} +
+ )} + {personalInfo.website && ( +
+ + {formatUrl(personalInfo.website)} +
+ )} + {personalInfo.linkedin && ( +
+ + {formatUrl(personalInfo.linkedin)} +
+ )} + {personalInfo.github && ( +
+ + {formatUrl(personalInfo.github)} +
+ )} +
+
+ ); + + case 'summary': + if (!summary) return null; + return ( +
+
+
+

+ Executive Profile +

+
+

+ {summary} +

+
+ ); + + case 'experience': + if (experience.length === 0) return null; + return ( +
+
+ +

+ Professional Experience +

+
+
+ {experience.map((exp) => ( +
+
+
+

+ {exp.jobTitle} +

+

+ {exp.company}{exp.location && ` • ${exp.location}`} +

+
+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+
    + {exp.description.filter(desc => desc.trim()).map((desc, index) => ( +
  • + + {desc} +
  • + ))} +
+
+ ))} +
+
+ ); + + case 'education': + if (education.length === 0) return null; + return ( +
+
+ +

+ Education +

+
+
+ {education.map((edu) => ( +
+
+
+

+ {edu.degree} +

+

+ {edu.school}{edu.location && `, ${edu.location}`} +

+
+ + {formatDate(edu.graduationDate)} + +
+ {edu.gpa && ( +

+ GPA: {edu.gpa} +

+ )} +
+ ))} +
+
+ ); + + case 'skills': + if (skills.length === 0) return null; + return ( +
+
+ +

+ Core Competencies +

+
+
+ {skills.map((skill) => ( +
+ + + {skill.name}{skill.level ? ` (${skill.level})` : ''} + +
+ ))} +
+
+ ); + + case 'certifications': + if (certifications.length === 0) return null; + return ( +
+
+ +

+ Certifications +

+
+
+ {certifications.map((cert) => ( +
+
+

+ {cert.name} +

+ + {cert.date && formatDate(cert.date)} + +
+ {cert.issuer && ( +

+ {cert.issuer} +

+ )} +
+ ))} +
+
+ ); + + case 'projects': + if (projects.length === 0) return null; + return ( +
+
+ +

+ Key Projects +

+
+
+ {projects.map((project) => ( +
+
+

+ {project.name} +

+ {project.url && ( + + View Project + + )} +
+ {project.technologies && project.technologies.length > 0 && ( +

+ Technologies: {project.technologies.join(', ')} +

+ )} +

+ {project.description} +

+
+ ))} +
+
+ ); + + default: + return null; + } + }; + + return ( +
+ {visibleSections.map(section => renderSection(section))} +
+ ); +} diff --git a/backup/templates/MinimalTemplate.tsx b/backup/templates/MinimalTemplate.tsx new file mode 100644 index 0000000..cb9c50e --- /dev/null +++ b/backup/templates/MinimalTemplate.tsx @@ -0,0 +1,319 @@ +import React from 'react'; +import { ResumeData } from '@/types/resume'; +import { Mail, Phone, MapPin, Globe, Linkedin, Github } from 'lucide-react'; + +interface TemplateProps { + data: ResumeData; +} + +export function MinimalTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications, projects, references, sections, customization } = data; + + const visibleSections = sections.filter(section => section.isVisible).sort((a, b) => a.order - b.order); + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + const formatUrl = (url: string) => { + if (!url) return ''; + // Remove protocol and www for cleaner display + return url.replace(/^https?:\/\/(www\.)?/, ''); + }; + + const renderSection = (section: typeof sections[0]) => { + switch (section.type) { + case 'personalInfo': + return ( +
+
+ {customization.showProfileImage && personalInfo.profileImage && ( + Profile + )} +

+ {personalInfo.fullName || 'Your Name'} +

+ +
+ {personalInfo.email && ( +
+ + {personalInfo.email} +
+ )} + {personalInfo.phone && ( +
+ + {personalInfo.phone} +
+ )} + {personalInfo.location && ( +
+ + {personalInfo.location} +
+ )} +
+ + {(personalInfo.website || personalInfo.linkedin || personalInfo.github) && ( +
+ {personalInfo.website && ( +
+ + {formatUrl(personalInfo.website)} +
+ )} + {personalInfo.linkedin && ( +
+ + {formatUrl(personalInfo.linkedin)} +
+ )} + {personalInfo.github && ( +
+ + {formatUrl(personalInfo.github)} +
+ )} +
+ )} +
+
+ ); + + case 'summary': + if (!summary) return null; + return ( +
+

+ Professional Summary +

+

+ {summary} +

+
+ ); + + case 'experience': + if (experience.length === 0) return null; + return ( +
+

+ Work Experience +

+
+ {experience.map((exp) => ( +
+
+
+

+ {exp.jobTitle} +

+

+ {exp.company}{exp.location && `, ${exp.location}`} +

+
+
+ {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} +
+
+
    + {exp.description.filter(desc => desc.trim()).map((desc, index) => ( +
  • {desc}
  • + ))} +
+
+ ))} +
+
+ ); + + case 'education': + if (education.length === 0) return null; + return ( +
+

+ Education +

+
+ {education.map((edu) => ( +
+
+
+

+ {edu.degree} +

+

+ {edu.school}{edu.location && `, ${edu.location}`} +

+ {edu.gpa && ( +

+ GPA: {edu.gpa} +

+ )} +
+
+ {formatDate(edu.graduationDate)} +
+
+
+ ))} +
+
+ ); + + case 'skills': + if (skills.length === 0) return null; + return ( +
+

+ Skills +

+
+ {skills.map((skill) => ( +
+ {skill.name} + + {skill.level} + +
+ ))} +
+
+ ); + + case 'certifications': + if (certifications.length === 0) return null; + return ( +
+

+ Certifications +

+
+ {certifications.map((cert) => ( +
+
+

+ {cert.name} +

+

+ {cert.issuer} +

+
+
+ {formatDate(cert.date)} + {cert.expirationDate && ` - ${formatDate(cert.expirationDate)}`} +
+
+ ))} +
+
+ ); + + case 'projects': + if (projects.length === 0) return null; + return ( +
+

+ Projects +

+
+ {projects.map((project) => ( +
+
+

+ {project.name} +

+
+ {formatDate(project.startDate)} + {project.endDate && ` - ${formatDate(project.endDate)}`} +
+
+

+ {project.description} +

+ {project.technologies.length > 0 && ( +
+ {project.technologies.map((tech, index) => ( + + {tech} + + ))} +
+ )} + {project.url && ( +

+ {formatUrl(project.url)} +

+ )} +
+ ))} +
+
+ ); + + case 'references': + if (references.length === 0) return null; + return ( +
+

+ Professional References +

+
+ {references.map((ref) => ( +
+
+
+

+ {ref.name} +

+

+ {ref.title} at {ref.company} +

+

+ {ref.relationship} +

+
+
+
{ref.email}
+
{ref.phone}
+
+
+
+ ))} +
+
+ ); + + default: + return null; + } + }; + + return ( +
+ {visibleSections.map(renderSection)} +
+ ); +} \ No newline at end of file diff --git a/backup/templates/MinimalistATS.tsx b/backup/templates/MinimalistATS.tsx new file mode 100644 index 0000000..8247c71 --- /dev/null +++ b/backup/templates/MinimalistATS.tsx @@ -0,0 +1,126 @@ +import { Mail, Phone, MapPin, Globe, Linkedin, Github, Briefcase, GraduationCap, Code, Award } from 'lucide-react'; +import { ResumeData } from '@/types/resume'; + +type TemplateProps = { + data: ResumeData; +}; + +export function MinimalistATS({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications } = data; + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short' }); + }; + + return ( +
+ {/* Header */} +
+

{personalInfo.fullName}

+ {personalInfo.title &&

{personalInfo.title}

} +
+ {personalInfo.email && {personalInfo.email}} + {personalInfo.phone && {personalInfo.phone}} + {personalInfo.location && {personalInfo.location}} + {personalInfo.linkedin && linkedin.com/in/username} +
+
+ + {/* Summary */} + {summary && ( +
+

SUMMARY

+

{summary}

+
+ )} + + {/* Experience */} + {experience.length > 0 && ( +
+

EXPERIENCE

+
+ {experience.map((exp) => ( +
+
+

{exp.jobTitle}

+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+

+ {exp.company} + {exp.location && ` • ${exp.location}`} +

+
    + {exp.description.map((desc, idx) => ( +
  • + + {desc} +
  • + ))} +
+
+ ))} +
+
+ )} + + {/* Education */} + {education.length > 0 && ( +
+

EDUCATION

+
+ {education.map((edu) => ( +
+
+

{edu.degree}

+ + {formatDate(edu.graduationDate)} + +
+

+ {edu.school} + {edu.location && `, ${edu.location}`} + {edu.gpa && ` • GPA: ${edu.gpa}`} +

+
+ ))} +
+
+ )} + + {/* Skills */} + {skills.length > 0 && ( +
+

SKILLS

+
+ {skills.map((skill) => ( + + {skill.name} + + ))} +
+
+ )} + + {/* Certifications */} + {certifications.length > 0 && ( +
+

CERTIFICATIONS

+
+ {certifications.map((cert) => ( +
+

{cert.name}

+

+ {cert.issuer} • {formatDate(cert.date)} +

+
+ ))} +
+
+ )} +
+ ); +} diff --git a/backup/templates/MinimalistProfessionalTemplate.tsx b/backup/templates/MinimalistProfessionalTemplate.tsx new file mode 100644 index 0000000..72fa78e --- /dev/null +++ b/backup/templates/MinimalistProfessionalTemplate.tsx @@ -0,0 +1,307 @@ +import { ResumeData } from '@/types/resume'; +import { Mail, Phone, MapPin, Globe, Linkedin, Github, Briefcase, GraduationCap, Code, Award, User } from 'lucide-react'; + +interface TemplateProps { + data: ResumeData; +} + +export function MinimalistProfessionalTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications, projects, sections, customization } = data; + + const visibleSections = sections.filter(section => section.isVisible).sort((a, b) => a.order - b.order); + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + const formatUrl = (url: string) => { + if (!url) return ''; + return url.replace(/^https?:\/\/(www\.)?/, '').replace(/\/$/, ''); + }; + + const renderSection = (section: typeof sections[0]) => { + switch (section.type) { + case 'personalInfo': + return ( +
+
+

+ {personalInfo.fullName || 'Your Name'} +

+
+

+ {personalInfo.title || 'Professional Title'} +

+ +
+ {personalInfo.email && ( + + {personalInfo.email} + + )} + {personalInfo.phone && ( + {personalInfo.phone} + )} + {personalInfo.location && ( + {personalInfo.location} + )} + {personalInfo.website && ( + + {formatUrl(personalInfo.website)} + + )} + {personalInfo.linkedin && ( + + LinkedIn + + )} +
+
+
+ ); + + case 'summary': + if (!summary) return null; + return ( +
+
+

+ Profile +

+

+ {summary} +

+
+
+ ); + + case 'experience': + if (experience.length === 0) return null; + return ( +
+
+

+ Experience +

+
+ {experience.map((exp) => ( +
+
+

+ {exp.jobTitle} +

+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+

+ {exp.company} + {exp.location && ` • ${exp.location}`} +

+
    + {exp.description.filter(desc => desc.trim()).map((desc, index) => ( +
  • + + {desc} +
  • + ))} +
+
+ ))} +
+
+
+ ); + + case 'education': + if (education.length === 0) return null; + return ( +
+
+

+ Education +

+
+ {education.map((edu) => ( +
+
+

+ {edu.degree} +

+ + {formatDate(edu.graduationDate)} + +
+

+ {edu.school} + {edu.location && `, ${edu.location}`} + {edu.gpa && ` • GPA: ${edu.gpa}`} +

+ {edu.relevantCourses && edu.relevantCourses.length > 0 && ( +
+

+ Relevant Coursework: +

+
+ {edu.relevantCourses.map((course, i) => ( + + {course} + + ))} +
+
+ )} +
+ ))} +
+
+
+ ); + + case 'skills': + if (skills.length === 0) return null; + return ( +
+
+

+ Skills +

+
+ {skills.map((skill) => ( + + {skill.name} + + ))} +
+
+
+ ); + + case 'projects': + if (projects.length === 0) return null; + return ( +
+
+

+ Projects +

+
+ {projects.map((project) => ( +
+
+

+ {project.name} + {project.url && ( + + (View Project) + + )} +

+ {project.startDate && ( + + {formatDate(project.startDate)} + {project.endDate ? ` - ${formatDate(project.endDate)}` : ' - Present'} + + )} +
+

+ {project.description} +

+ {project.technologies && project.technologies.length > 0 && ( +
+ {project.technologies.map((tech, i) => ( + + {tech} + + ))} +
+ )} +
+ ))} +
+
+
+ ); + + case 'certifications': + if (certifications.length === 0) return null; + return ( +
+
+

+ Certifications +

+
+ {certifications.map((cert) => ( +
+

+ {cert.name} +

+

+ {cert.issuer} + {cert.date && ` • ${formatDate(cert.date)}`} + {cert.expirationDate && ` - ${formatDate(cert.expirationDate)}`} +

+
+ ))} +
+
+
+ ); + + default: + return null; + } + }; + + return ( +
+ {visibleSections.map(section => renderSection(section))} +
+ ); +} diff --git a/backup/templates/MinimalistSidebarTemplate.tsx b/backup/templates/MinimalistSidebarTemplate.tsx new file mode 100644 index 0000000..29b7ff7 --- /dev/null +++ b/backup/templates/MinimalistSidebarTemplate.tsx @@ -0,0 +1,166 @@ +import { ResumeData } from '@/types/resume'; +import { Mail, Phone, MapPin, Globe, Briefcase, GraduationCap, User } from 'lucide-react'; + +interface TemplateProps { + data: ResumeData; +} + +export function MinimalistSidebarTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills } = data; + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + const renderSidebar = () => ( +
+ {personalInfo.profileImage && ( +
+ {personalInfo.fullName +
+ )} + +
+
+

CONTACT

+
+ {personalInfo.email && ( +
+ + {personalInfo.email} +
+ )} + {personalInfo.phone && ( +
+ + {personalInfo.phone} +
+ )} + {personalInfo.location && ( +
+ + {personalInfo.location} +
+ )} + {personalInfo.website && ( +
+ + {personalInfo.website.replace(/^https?:\/\//, '')} +
+ )} +
+
+ + {skills.length > 0 && ( +
+

SKILLS

+
+ {skills.map((skill) => ( +
+
+ {skill.name} + + {skill.level === 'Expert' ? '★★★★★' : + skill.level === 'Advanced' ? '★★★★☆' : + skill.level === 'Intermediate' ? '★★★☆☆' : '★★☆☆☆'} + +
+
+ ))} +
+
+ )} +
+
+ ); + + const renderMainContent = () => ( +
+

+ {personalInfo.fullName || 'Your Name'} +

+

+ {personalInfo.title || 'Professional Title'} +

+ + {summary && ( +
+

+ + PROFILE +

+

{summary}

+
+ )} + + {experience.length > 0 && ( +
+

+ + EXPERIENCE +

+
+ {experience.map((exp) => ( +
+
+

{exp.jobTitle}

+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+

+ {exp.company} + {exp.location && ` • ${exp.location}`} +

+
    + {exp.description.filter(desc => desc.trim()).map((desc, index) => ( +
  • {desc}
  • + ))} +
+
+ ))} +
+
+ )} + + {education.length > 0 && ( +
+

+ + EDUCATION +

+
+ {education.map((edu) => ( +
+
+

{edu.degree}

+ + {formatDate(edu.graduationDate)} + +
+

+ {edu.school} + {edu.location && `, ${edu.location}`} + {edu.gpa && ` • GPA: ${edu.gpa}`} +

+
+ ))} +
+
+ )} +
+ ); + + return ( +
+ {renderSidebar()} + {renderMainContent()} +
+ ); +} diff --git a/backup/templates/MinimalistTemplate.tsx b/backup/templates/MinimalistTemplate.tsx new file mode 100644 index 0000000..5efe0cd --- /dev/null +++ b/backup/templates/MinimalistTemplate.tsx @@ -0,0 +1,261 @@ +import { ResumeData } from '@/types/resume'; +import { Mail, Phone, MapPin, Globe, Linkedin, Github } from 'lucide-react'; + +interface TemplateProps { + data: ResumeData; +} + +export function MinimalistTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications, projects, sections, customization } = data; + + const visibleSections = sections.filter(section => section.isVisible).sort((a, b) => a.order - b.order); + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + const formatUrl = (url: string) => { + if (!url) return ''; + return url.replace(/^https?:\/\/(www\.)?/, '').replace(/\/$/, ''); + }; + + const renderSection = (section: typeof sections[0]) => { + switch (section.type) { + case 'personalInfo': + return ( +
+

+ {personalInfo.fullName || 'Your Name'} +

+ {personalInfo.title && ( +

+ {personalInfo.title} +

+ )} + +
+ {personalInfo.email && ( +
+ + {personalInfo.email} +
+ )} + {personalInfo.phone && ( +
+ + {personalInfo.phone} +
+ )} + {personalInfo.location && ( +
+ + {personalInfo.location} +
+ )} + {personalInfo.website && ( +
+ + {formatUrl(personalInfo.website)} +
+ )} + {personalInfo.linkedin && ( +
+ + {formatUrl(personalInfo.linkedin)} +
+ )} + {personalInfo.github && ( +
+ + {formatUrl(personalInfo.github)} +
+ )} +
+
+
+ ); + + case 'summary': + if (!summary) return null; + return ( +
+

+ About +

+

+ {summary} +

+
+ ); + + case 'experience': + if (experience.length === 0) return null; + return ( +
+

+ Experience +

+
+ {experience.map((exp) => ( +
+
+

+ {exp.jobTitle} +

+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+

+ {exp.company}{exp.location && ` • ${exp.location}`} +

+
    + {exp.description.filter(desc => desc.trim()).map((desc, index) => ( +
  • + + {desc} +
  • + ))} +
+
+ ))} +
+
+ ); + + case 'education': + if (education.length === 0) return null; + return ( +
+

+ Education +

+
+ {education.map((edu) => ( +
+
+

+ {edu.degree} +

+ + {formatDate(edu.graduationDate)} + +
+

+ {edu.school}{edu.location && ` • ${edu.location}`} +

+ {edu.gpa && ( +

+ GPA: {edu.gpa} +

+ )} +
+ ))} +
+
+ ); + + case 'skills': + if (skills.length === 0) return null; + return ( +
+

+ Skills +

+
+ {skills.map((skill) => ( + + {skill.name} + + ))} +
+
+ ); + + case 'certifications': + if (certifications.length === 0) return null; + return ( +
+

+ Certifications +

+
+ {certifications.map((cert) => ( +
+

+ {cert.name} +

+

+ {cert.issuer} + {cert.date && ` • ${formatDate(cert.date)}`} +

+
+ ))} +
+
+ ); + + case 'projects': + if (projects.length === 0) return null; + return ( +
+

+ Projects +

+
+ {projects.map((project) => ( +
+
+

+ {project.name} +

+ {project.url && ( + + View Project + + )} +
+ {project.technologies && project.technologies.length > 0 && ( +

+ {project.technologies.join(' • ')} +

+ )} +

+ {project.description} +

+
+ ))} +
+
+ ); + + default: + return null; + } + }; + + return ( +
+ {visibleSections.map(section => renderSection(section))} +
+ ); +} diff --git a/backup/templates/ModernDarkSidebarTemplate.tsx b/backup/templates/ModernDarkSidebarTemplate.tsx new file mode 100644 index 0000000..267bd3f --- /dev/null +++ b/backup/templates/ModernDarkSidebarTemplate.tsx @@ -0,0 +1,192 @@ +import { ResumeData } from '@/types/resume'; +import { Mail, Phone, MapPin, Globe, Briefcase, GraduationCap, User } from 'lucide-react'; + +interface TemplateProps { + data: ResumeData; +} + +export function ModernDarkSidebarTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications } = data; + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + const renderSidebar = () => ( +
+ {personalInfo.profileImage && ( +
+ {personalInfo.fullName +
+ )} + +
+
+

CONTACT

+
+ {personalInfo.email && ( +
+ + {personalInfo.email} +
+ )} + {personalInfo.phone && ( +
+ + {personalInfo.phone} +
+ )} + {personalInfo.location && ( +
+ + {personalInfo.location} +
+ )} + {personalInfo.website && ( + + )} +
+
+ + {skills.length > 0 && ( +
+

SKILLS

+
+ {skills.map((skill) => ( +
+
+ {skill.name} + + {skill.level === 'Expert' ? '★★★★★' : + skill.level === 'Advanced' ? '★★★★☆' : + skill.level === 'Intermediate' ? '★★★☆☆' : '★★☆☆☆'} + +
+
+ ))} +
+
+ )} + + {certifications && certifications.length > 0 && ( +
+

CERTIFICATIONS

+
+ {certifications.map((cert) => ( +
+
{cert.name}
+
{cert.issuer}
+ {cert.date && ( +
+ {new Date(cert.date).toLocaleDateString('en-US', { year: 'numeric', month: 'short' })} +
+ )} +
+ ))} +
+
+ )} +
+
+ ); + + const renderMainContent = () => ( +
+
+

+ {personalInfo.fullName || 'Your Name'} +

+

+ {personalInfo.title || 'Professional Title'} +

+
+ + {summary && ( +
+

+ + ABOUT ME +

+

{summary}

+
+ )} + + {experience.length > 0 && ( +
+

+ + WORK EXPERIENCE +

+
+ {experience.map((exp) => ( +
+
+

{exp.jobTitle}

+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+

+ {exp.company} + {exp.location && ` • ${exp.location}`} +

+
    + {exp.description.filter(desc => desc.trim()).map((desc, index) => ( +
  • + + {desc} +
  • + ))} +
+
+ ))} +
+
+ )} + + {education.length > 0 && ( +
+

+ + EDUCATION +

+
+ {education.map((edu) => ( +
+
+

{edu.degree}

+ + {formatDate(edu.graduationDate)} + +
+

+ {edu.school} + {edu.location && `, ${edu.location}`} + {edu.gpa && ` • GPA: ${edu.gpa}`} +

+
+ ))} +
+
+ )} +
+ ); + + return ( +
+ {renderSidebar()} + {renderMainContent()} +
+ ); +} diff --git a/backup/templates/ModernExecutiveTemplate.tsx b/backup/templates/ModernExecutiveTemplate.tsx new file mode 100644 index 0000000..ade357e --- /dev/null +++ b/backup/templates/ModernExecutiveTemplate.tsx @@ -0,0 +1,220 @@ +import { Mail, Phone, MapPin, Linkedin, Globe, Briefcase, GraduationCap, Award, Code } from 'lucide-react'; +import { ResumeData } from '@/types/resume'; + +type TemplateProps = { + data: ResumeData; +}; + +export function ModernExecutiveTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications } = data; + const accentColor = data.customization.accentColor || '#2563eb'; + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + return ( +
+ {/* Sidebar */} +
+ {/* Profile Image */} + {personalInfo.profileImage && ( +
+ {personalInfo.fullName} +
+ )} + + {/* Contact */} +
+

+ Contact +

+
    + {personalInfo.email && ( +
  • + + {personalInfo.email} +
  • + )} + {personalInfo.phone && ( +
  • + + {personalInfo.phone} +
  • + )} + {personalInfo.location && ( +
  • + + {personalInfo.location} +
  • + )} + {personalInfo.linkedin && ( +
  • + + LinkedIn +
  • + )} + {personalInfo.website && ( +
  • + + {personalInfo.website.replace(/^https?:\/\//, '')} +
  • + )} +
+
+ + {/* Skills */} + {skills.length > 0 && ( +
+

+ Skills +

+
+ {skills.map((skill) => ( +
+
+ {skill.name} + {skill.level} +
+
+
+
+
+ ))} +
+
+ )} + + {/* Certifications */} + {certifications.length > 0 && ( +
+

+ Certifications +

+
+ {certifications.map((cert) => ( +
+

{cert.name}

+

{cert.issuer}

+

+ {formatDate(cert.date)} + {cert.expirationDate && ` • Expires: ${formatDate(cert.expirationDate)}`} +

+
+ ))} +
+
+ )} +
+ + {/* Main Content */} +
+ {/* Header */} +
+

+ {personalInfo.fullName} +

+ {personalInfo.title && ( +

+ {personalInfo.title} +

+ )} +
+ + {/* Summary */} + {summary && ( +
+

+ Professional Summary +

+

+ {summary} +

+
+ )} + + {/* Experience */} + {experience.length > 0 && ( +
+

+ + Work Experience +

+
+ {experience.map((exp) => ( +
+
+
+

{exp.jobTitle}

+
+

+ {exp.company} + {exp.location && ` • ${exp.location}`} +

+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+
+
    + {exp.description.map((desc, idx) => ( +
  • + + {desc} +
  • + ))} +
+
+ ))} +
+
+ )} + + {/* Education */} + {education.length > 0 && ( +
+

+ + Education +

+
+ {education.map((edu) => ( +
+
+

{edu.degree}

+

+ {edu.school} + {edu.location && `, ${edu.location}`} +

+
+ {edu.fieldOfStudy} + {formatDate(edu.graduationDate)} +
+ {edu.gpa && ( +

+ GPA: {edu.gpa} +

+ )} +
+ ))} +
+
+ )} +
+
+ ); +} diff --git a/backup/templates/ModernProfessionalTemplate.tsx b/backup/templates/ModernProfessionalTemplate.tsx new file mode 100644 index 0000000..bf58969 --- /dev/null +++ b/backup/templates/ModernProfessionalTemplate.tsx @@ -0,0 +1,366 @@ +import { ResumeData } from '@/types/resume'; +import { Mail, Phone, MapPin, Globe, Linkedin, Github, Briefcase, GraduationCap, Code, Award, User, BookOpen } from 'lucide-react'; + +interface TemplateProps { + data: ResumeData; +} + +export function ModernProfessionalTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications, projects, sections, customization } = data; + + const visibleSections = sections.filter(section => section.isVisible).sort((a, b) => a.order - b.order); + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + const formatUrl = (url: string) => { + if (!url) return ''; + return url.replace(/^https?:\/\/(www\.)?/, '').replace(/\/$/, ''); + }; + + const renderSection = (section: typeof sections[0]) => { + switch (section.type) { + case 'personalInfo': + return ( +
+
+

+ {personalInfo.fullName || 'Your Name'} +

+

+ {personalInfo.title || 'Professional Title'} +

+ +
+ +
+ {personalInfo.email && ( + + + {personalInfo.email} + + )} + {personalInfo.phone && ( +
+ + {personalInfo.phone} +
+ )} + {personalInfo.location && ( +
+ + {personalInfo.location} +
+ )} + {personalInfo.website && ( + + + Website + + )} + {personalInfo.linkedin && ( + + + LinkedIn + + )} + {personalInfo.github && ( + + + GitHub + + )} +
+
+
+ ); + + case 'summary': + if (!summary) return null; + return ( +
+

+ + Professional Profile +

+

+ {summary} +

+
+ ); + + case 'experience': + if (experience.length === 0) return null; + return ( +
+

+ + Professional Experience +

+
+ {experience.map((exp) => ( +
+
+
+

+ {exp.jobTitle} +

+

+ {exp.company} + {exp.location && ` • ${exp.location}`} +

+
+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+
    + {exp.description.filter(desc => desc.trim()).map((desc, index) => ( +
  • + + {desc} +
  • + ))} +
+
+ ))} +
+
+ ); + + case 'education': + if (education.length === 0) return null; + return ( +
+

+ + Education +

+
+ {education.map((edu) => ( +
+
+
+

+ {edu.degree} +

+

+ {edu.school} + {edu.location && `, ${edu.location}`} +

+
+ + {formatDate(edu.graduationDate)} + +
+ {edu.gpa && ( +

+ GPA: {edu.gpa} +

+ )} + {edu.relevantCourses && edu.relevantCourses.length > 0 && ( +
+

+ Relevant Coursework: +

+
+ {edu.relevantCourses.map((course, i) => ( + + {course} + + ))} +
+
+ )} +
+ ))} +
+
+ ); + + case 'skills': + if (skills.length === 0) return null; + return ( +
+

+ + Skills & Expertise +

+
+ {skills.map((skill) => ( +
+
+ + {skill.name} + + + {skill.level} + +
+
+
+
+
+ ))} +
+
+ ); + + case 'projects': + if (projects.length === 0) return null; + return ( +
+

+ + Projects +

+
+ {projects.map((project) => ( +
+
+

+ {project.name} + {project.url && ( + + (View Project) + + )} +

+ {project.startDate && ( + + {formatDate(project.startDate)} + {project.endDate ? ` - ${formatDate(project.endDate)}` : ' - Present'} + + )} +
+

+ {project.description} +

+ {project.technologies && project.technologies.length > 0 && ( +
+
+ {project.technologies.map((tech, i) => ( + + {tech} + + ))} +
+
+ )} +
+ ))} +
+
+ ); + + case 'certifications': + if (certifications.length === 0) return null; + return ( +
+

+ + Certifications +

+
+ {certifications.map((cert) => ( +
+

+ {cert.name} +

+

+ {cert.issuer} +

+

+ {formatDate(cert.date)} + {cert.expirationDate && ` - ${formatDate(cert.expirationDate)}`} +

+
+ ))} +
+
+ ); + + default: + return null; + } + }; + + return ( +
+ {visibleSections.map(section => renderSection(section))} +
+ ); +} diff --git a/backup/templates/ModernSidebarTemplate.tsx b/backup/templates/ModernSidebarTemplate.tsx new file mode 100644 index 0000000..25759f3 --- /dev/null +++ b/backup/templates/ModernSidebarTemplate.tsx @@ -0,0 +1,266 @@ +import { Mail, Phone, MapPin, Globe, Linkedin, Github, Briefcase, GraduationCap, Code, Award, User, BookOpen } from 'lucide-react'; +import { ResumeData } from '@/types/resume'; + +type TemplateProps = { + data: ResumeData; +}; + +export function ModernSidebarTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications, projects, sections, customization } = data; + const visibleSections = sections.filter(section => section.isVisible).sort((a, b) => a.order - b.order); + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + const renderSidebar = () => ( +
+ {personalInfo.profileImage && ( +
+ {personalInfo.fullName} +
+ )} + +
+
+

CONTACT

+
+ {personalInfo.email && ( + + )} + {personalInfo.phone && ( + + )} + {personalInfo.location && ( +
+ + {personalInfo.location} +
+ )} + {personalInfo.website && ( + + )} + {personalInfo.linkedin && ( + + )} + {personalInfo.github && ( + + )} +
+
+ + {skills.length > 0 && ( +
+

SKILLS

+
+ {skills.map((skill) => ( +
+
+ {skill.name} + + {skill.level === 'Expert' ? '★★★★★' : + skill.level === 'Advanced' ? '★★★★☆' : + skill.level === 'Intermediate' ? '★★★☆☆' : '★★☆☆☆'} + +
+
+ ))} +
+
+ )} + + {certifications.length > 0 && ( +
+

CERTIFICATIONS

+
+ {certifications.map((cert) => ( +
+

{cert.name}

+

{cert.issuer}

+

+ {formatDate(cert.date)} + {cert.expirationDate && ` • Expires: ${formatDate(cert.expirationDate)}`} +

+
+ ))} +
+
+ )} +
+
+ ); + + const renderMainContent = () => ( +
+
+

+ {personalInfo.fullName} +

+ {personalInfo.title && ( +

+ {personalInfo.title} +

+ )} +
+ + {summary && ( +
+

+ + PROFILE +

+

{summary}

+
+ )} + + {experience.length > 0 && ( +
+

+ + EXPERIENCE +

+
+ {experience.map((exp) => ( +
+
+
+

+ {exp.jobTitle} +

+

+ {exp.company} + {exp.location && ` • ${exp.location}`} +

+
+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+
    + {exp.description.map((desc, idx) => ( +
  • + + {desc} +
  • + ))} +
+
+ ))} +
+
+ )} + + {education.length > 0 && ( +
+

+ + EDUCATION +

+
+ {education.map((edu) => ( +
+
+
+

+ {edu.degree} +

+

+ {edu.school} + {edu.location && `, ${edu.location}`} +

+
+ + {formatDate(edu.graduationDate)} + +
+ {edu.gpa && ( +

+ GPA: {edu.gpa} +

+ )} +
+ ))} +
+
+ )} + + {projects.length > 0 && ( +
+

+ + PROJECTS +

+
+ {projects.map((project) => ( +
+
+

+ {project.name} +

+ + {formatDate(project.startDate)} + {project.endDate && ` - ${formatDate(project.endDate)}`} + +
+

+ {project.description} +

+ {project.technologies.length > 0 && ( +
+ {project.technologies.map((tech, idx) => ( + + {tech} + + ))} +
+ )} +
+ ))} +
+
+ )} +
+ ); + + return ( +
+ {renderSidebar()} + {renderMainContent()} +
+ ); +} diff --git a/backup/templates/ModernTemplate.tsx b/backup/templates/ModernTemplate.tsx new file mode 100644 index 0000000..b338344 --- /dev/null +++ b/backup/templates/ModernTemplate.tsx @@ -0,0 +1,339 @@ +import React from 'react'; +import { ResumeData } from '@/types/resume'; +import { Mail, Phone, MapPin, Globe, Linkedin, Github } from 'lucide-react'; + +interface TemplateProps { + data: ResumeData; +} + +export function ModernTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications, projects, references, sections, customization } = data; + + const visibleSections = sections.filter(section => section.isVisible).sort((a, b) => a.order - b.order); + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + const formatUrl = (url: string) => { + if (!url) return ''; + // Remove protocol and www for cleaner display + return url.replace(/^https?:\/\/(www\.)?/, ''); + }; + + const renderSection = (section: typeof sections[0]) => { + switch (section.type) { + case 'personalInfo': + return ( +
+
+ {customization.showProfileImage && personalInfo.profileImage && ( + Profile + )} +
+

+ {personalInfo.fullName || 'Your Name'} +

+ +
+ {personalInfo.email && ( +
+ + {personalInfo.email} +
+ )} + {personalInfo.phone && ( +
+ + {personalInfo.phone} +
+ )} + {personalInfo.location && ( +
+ + {personalInfo.location} +
+ )} + {personalInfo.website && ( +
+ + {formatUrl(personalInfo.website)} +
+ )} + {personalInfo.linkedin && ( +
+ + {formatUrl(personalInfo.linkedin)} +
+ )} + {personalInfo.github && ( +
+ + {formatUrl(personalInfo.github)} +
+ )} +
+
+
+
+ ); + + case 'summary': + if (!summary) return null; + return ( +
+
+
+

+ Professional Summary +

+
+

+ {summary} +

+
+ ); + + case 'experience': + if (experience.length === 0) return null; + return ( +
+
+
+

+ Work Experience +

+
+
+ {experience.map((exp) => ( +
+
+
+
+

+ {exp.jobTitle} +

+

+ {exp.company}{exp.location && `, ${exp.location}`} +

+
+
+ {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} +
+
+
    + {exp.description.filter(desc => desc.trim()).map((desc, index) => ( +
  • {desc}
  • + ))} +
+
+ ))} +
+
+ ); + + case 'education': + if (education.length === 0) return null; + return ( +
+
+
+

+ Education +

+
+
+ {education.map((edu) => ( +
+
+
+
+

+ {edu.degree} +

+

+ {edu.school}{edu.location && `, ${edu.location}`} +

+ {edu.gpa && ( +

+ GPA: {edu.gpa} +

+ )} +
+
+ {formatDate(edu.graduationDate)} +
+
+
+ ))} +
+
+ ); + + case 'skills': + if (skills.length === 0) return null; + return ( +
+
+
+

+ Skills +

+
+
+ {skills.map((skill) => ( +
+ {skill.name} + + {skill.level} + +
+ ))} +
+
+ ); + + case 'certifications': + if (certifications.length === 0) return null; + return ( +
+
+
+

+ Certifications +

+
+
+ {certifications.map((cert) => ( +
+
+

+ {cert.name} +

+

+ {cert.issuer} +

+
+
+ {formatDate(cert.date)} + {cert.expirationDate && ` - ${formatDate(cert.expirationDate)}`} +
+
+ ))} +
+
+ ); + + case 'projects': + if (projects.length === 0) return null; + return ( +
+
+
+

+ Projects +

+
+
+ {projects.map((project) => ( +
+
+

+ {project.name} +

+
+ {formatDate(project.startDate)} + {project.endDate && ` - ${formatDate(project.endDate)}`} +
+
+

+ {project.description} +

+ {project.technologies.length > 0 && ( +
+ {project.technologies.map((tech, index) => ( + + {tech} + + ))} +
+ )} + {project.url && ( +

+ {formatUrl(project.url)} +

+ )} +
+ ))} +
+
+ ); + + case 'references': + if (references.length === 0) return null; + return ( +
+
+
+

+ Professional References +

+
+
+ {references.map((ref) => ( +
+
+
+

+ {ref.name} +

+

+ {ref.title} at {ref.company} +

+

+ {ref.relationship} +

+
+
+
{ref.email}
+
{ref.phone}
+
+
+
+ ))} +
+
+ ); + + default: + return null; + } + }; + + return ( +
+ {visibleSections.map(renderSection)} +
+ ); +} \ No newline at end of file diff --git a/backup/templates/ModernTimelineTemplate.tsx b/backup/templates/ModernTimelineTemplate.tsx new file mode 100644 index 0000000..6f3528d --- /dev/null +++ b/backup/templates/ModernTimelineTemplate.tsx @@ -0,0 +1,207 @@ +import { Mail, Phone, MapPin, Linkedin, Globe, Briefcase, GraduationCap, Award } from 'lucide-react'; +import { ResumeData } from '@/types/resume'; + +type TemplateProps = { + data: ResumeData; +}; + +export function ModernTimelineTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications } = data; + const accentColor = data.customization.accentColor || '#3b82f6'; + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + return ( +
+ {/* Header */} +
+

+ {personalInfo.fullName} +

+ {personalInfo.title && ( +

+ {personalInfo.title} +

+ )} +
+ {personalInfo.email && ( + + + {personalInfo.email} + + )} + {personalInfo.phone && ( + + + {personalInfo.phone} + + )} + {personalInfo.location && ( + + + {personalInfo.location} + + )} +
+
+ + {/* Summary */} + {summary && ( +
+

About Me

+

{summary}

+
+ )} + + {/* Timeline Section */} +
+ {/* Timeline Line */} +
+ +
+ {/* Experience */} + {experience.length > 0 && ( +
+

+ + Work Experience +

+
+ {experience.map((exp, idx) => ( +
+
+
+
+

{exp.jobTitle}

+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+

+ {exp.company} + {exp.location && ` • ${exp.location}`} +

+
    + {exp.description.map((desc, idx) => ( +
  • + + {desc} +
  • + ))} +
+
+
+ ))} +
+
+ )} + + {/* Education */} + {education.length > 0 && ( +
+

+ + Education +

+
+ {education.map((edu) => ( +
+
+
+
+

{edu.degree}

+ + {formatDate(edu.graduationDate)} + +
+

+ {edu.school} + {edu.location && `, ${edu.location}`} +

+ {edu.fieldOfStudy && ( +

+ {edu.fieldOfStudy} +

+ )} + {edu.gpa && ( +

+ GPA: {edu.gpa} +

+ )} +
+
+ ))} +
+
+ )} +
+
+ + {/* Skills & Certifications */} +
+ {/* Skills */} + {skills.length > 0 && ( +
+

+ + Key Skills +

+
+ {skills.map((skill) => ( + + {skill.name} + + ))} +
+
+ )} + + {/* Certifications */} + {certifications.length > 0 && ( +
+

+ + Certifications +

+
+ {certifications.map((cert) => ( +
+

{cert.name}

+

+ {cert.issuer} • {formatDate(cert.date)} +

+
+ ))} +
+
+ )} +
+
+ ); +} diff --git a/backup/templates/ModernTwoColumn.tsx b/backup/templates/ModernTwoColumn.tsx new file mode 100644 index 0000000..effc367 --- /dev/null +++ b/backup/templates/ModernTwoColumn.tsx @@ -0,0 +1,205 @@ +import { Mail, Phone, MapPin, Globe, Linkedin, Github, Briefcase, GraduationCap, Code, Award } from 'lucide-react'; +import { ResumeData } from '@/types/resume'; + +type TemplateProps = { + data: ResumeData; +}; + +export function ModernTwoColumn({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications } = data; + const accentColor = data.customization.accentColor || '#3b82f6'; + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short' }); + }; + + return ( +
+ {/* Left Column */} +
+ {/* Profile */} +
+
+ {personalInfo.profileImage ? ( + {personalInfo.fullName} + ) : ( +
+ + {personalInfo.fullName.split(' ').map(n => n[0]).join('')} + +
+ )} +
+

{personalInfo.fullName}

+ {personalInfo.title && ( +

{personalInfo.title}

+ )} +
+ + {/* Contact */} +
+

Contact

+
    + {personalInfo.email && ( +
  • + + {personalInfo.email} +
  • + )} + {personalInfo.phone && ( +
  • + + {personalInfo.phone} +
  • + )} + {personalInfo.location && ( +
  • + + {personalInfo.location} +
  • + )} + {personalInfo.linkedin && ( +
  • + + LinkedIn +
  • + )} +
+
+ + {/* Skills */} + {skills.length > 0 && ( +
+

Skills

+
+ {skills.map((skill) => ( +
+
+ {skill.name} + {skill.level} +
+
+
+
+
+ ))} +
+
+ )} +
+ + {/* Right Column */} +
+ {/* Summary */} + {summary && ( +
+

+ About Me +

+

{summary}

+
+ )} + + {/* Experience */} + {experience.length > 0 && ( +
+

+ Work Experience +

+
+ {experience.map((exp) => ( +
+
+
+

{exp.jobTitle}

+

+ {exp.company} + {exp.location && ` • ${exp.location}`} +

+
+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+
    + {exp.description.map((desc, idx) => ( +
  • + + {desc} +
  • + ))} +
+
+ ))} +
+
+ )} + + {/* Education */} + {education.length > 0 && ( +
+

+ Education +

+
+ {education.map((edu) => ( +
+
+
+

{edu.degree}

+

+ {edu.school} + {edu.location && `, ${edu.location}`} +

+
+ + {formatDate(edu.graduationDate)} + +
+ {edu.gpa && ( +

+ GPA: {edu.gpa} +

+ )} +
+ ))} +
+
+ )} + + {/* Certifications */} + {certifications.length > 0 && ( +
+

+ Certifications +

+
+ {certifications.map((cert) => ( +
+

{cert.name}

+

+ {cert.issuer} • {formatDate(cert.date)} +

+
+ ))} +
+
+ )} +
+
+ ); +} diff --git a/backup/templates/ProfessionalBlueTemplate.tsx b/backup/templates/ProfessionalBlueTemplate.tsx new file mode 100644 index 0000000..a961f1c --- /dev/null +++ b/backup/templates/ProfessionalBlueTemplate.tsx @@ -0,0 +1,150 @@ +import { ResumeData } from '@/types/resume'; +import { Briefcase, GraduationCap, Code, User } from 'lucide-react'; + +interface TemplateProps { + data: ResumeData; +} + +export function ProfessionalBlueTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, sections } = data; + const visibleSections = sections.filter(section => section.isVisible).sort((a, b) => a.order - b.order); + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + const renderSection = (section: typeof sections[0]) => { + switch (section.type) { + case 'personalInfo': + return ( +
+
+ {personalInfo.profileImage && ( +
+ {personalInfo.fullName +
+ )} +
+

{personalInfo.fullName || 'Your Name'}

+

{personalInfo.title || 'Professional Title'}

+
+
+
+ ); + + case 'summary': + if (!summary) return null; + return ( +
+

+ + PROFILE +

+

{summary}

+
+ ); + + case 'experience': + if (experience.length === 0) return null; + return ( +
+

+ + WORK EXPERIENCE +

+
+ {experience.map((exp) => ( +
+
+

{exp.jobTitle}

+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+

+ {exp.company} + {exp.location && ` • ${exp.location}`} +

+
+ ))} +
+
+ ); + + case 'education': + if (education.length === 0) return null; + return ( +
+

+ + EDUCATION +

+
+ {education.map((edu) => ( +
+
+

{edu.degree}

+ + {formatDate(edu.graduationDate)} + +
+

+ {edu.school} + {edu.location && `, ${edu.location}`} + {edu.gpa && ` • GPA: ${edu.gpa}`} +

+
+ ))} +
+
+ ); + + case 'skills': + if (skills.length === 0) return null; + return ( +
+

+ + SKILLS +

+
+ {skills.map((skill) => ( +
+
+ + {skill.name} + +
+
+
+
+
+ ))} +
+
+ ); + + default: + return null; + } + }; + + return ( +
+ {visibleSections.map(section => renderSection(section))} +
+ ); +} diff --git a/backup/templates/ProfessionalSidebarTemplate.tsx b/backup/templates/ProfessionalSidebarTemplate.tsx new file mode 100644 index 0000000..d91b338 --- /dev/null +++ b/backup/templates/ProfessionalSidebarTemplate.tsx @@ -0,0 +1,175 @@ +import { Mail, Phone, MapPin, Linkedin, Globe, Briefcase, GraduationCap, Award } from 'lucide-react'; +import { ResumeData } from '@/types/resume'; + +type TemplateProps = { + data: ResumeData; +}; + +export function ProfessionalSidebarTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications } = data; + const accentColor = data.customization.accentColor || '#2563eb'; + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + return ( +
+ {/* Sidebar */} +
+ {/* Profile */} +
+ {personalInfo.profileImage ? ( +
+ {personalInfo.fullName} +
+ ) : ( +
+ + {personalInfo.fullName.split(' ').map(n => n[0]).join('')} + +
+ )} +

{personalInfo.fullName}

+ {personalInfo.title &&

{personalInfo.title}

} +
+ + {/* Contact */} +
+

Contact

+
    + {personalInfo.email && ( +
  • + + {personalInfo.email} +
  • + )} + {personalInfo.phone && ( +
  • + + {personalInfo.phone} +
  • + )} + {personalInfo.location && ( +
  • + + {personalInfo.location} +
  • + )} + {personalInfo.linkedin && ( +
  • + + LinkedIn +
  • + )} +
+
+ + {/* Skills */} + {skills.length > 0 && ( +
+

Skills

+
+ {skills.map((skill) => ( +
+
+ {skill.name} + {skill.level} +
+
+
+
+
+ ))} +
+
+ )} +
+ + {/* Main Content */} +
+ {/* Summary */} + {summary && ( +
+

+ Professional Summary +

+

{summary}

+
+ )} + + {/* Experience */} + {experience.length > 0 && ( +
+

+ + Work Experience +

+
+ {experience.map((exp) => ( +
+
+

{exp.jobTitle}

+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+

+ {exp.company} + {exp.location && ` • ${exp.location}`} +

+
    + {exp.description.map((desc, idx) => ( +
  • + + {desc} +
  • + ))} +
+
+ ))} +
+
+ )} + + {/* Education */} + {education.length > 0 && ( +
+

+ + Education +

+
+ {education.map((edu) => ( +
+

{edu.degree}

+

+ {edu.school} + {edu.location && `, ${edu.location}`} +

+
+ {edu.fieldOfStudy} + {formatDate(edu.graduationDate)} +
+ {edu.gpa && ( +

GPA: {edu.gpa}

+ )} +
+ ))} +
+
+ )} +
+
+ ); +} diff --git a/backup/templates/ProfessionalSummaryTemplate.tsx b/backup/templates/ProfessionalSummaryTemplate.tsx new file mode 100644 index 0000000..58f1bc7 --- /dev/null +++ b/backup/templates/ProfessionalSummaryTemplate.tsx @@ -0,0 +1,189 @@ +import { ResumeData } from '@/types/resume'; +import { Briefcase, GraduationCap, User, Code, Mail, Phone, MapPin, Globe } from 'lucide-react'; + +interface TemplateProps { + data: ResumeData; +} + +export function ProfessionalSummaryTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications } = data; + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + return ( +
+ {/* Header */} +
+

{personalInfo.fullName || 'Your Name'}

+

{personalInfo.title || 'Professional Title'}

+ +
+ {personalInfo.email && ( +
+ + {personalInfo.email} +
+ )} + {personalInfo.phone && ( +
+ + {personalInfo.phone} +
+ )} + {personalInfo.location && ( +
+ + {personalInfo.location} +
+ )} + {personalInfo.website && ( + + )} +
+
+ + {/* Summary */} + {summary && ( +
+

+ + PROFESSIONAL SUMMARY +

+

{summary}

+
+ )} + +
+ {/* Left Column */} +
+ {/* Experience */} + {experience.length > 0 && ( +
+

+ + WORK EXPERIENCE +

+
+ {experience.map((exp) => ( +
+
+

{exp.jobTitle}

+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+

+ {exp.company} + {exp.location && ` • ${exp.location}`} +

+
    + {exp.description.filter(desc => desc.trim()).map((desc, index) => ( +
  • + + {desc} +
  • + ))} +
+
+ ))} +
+
+ )} + + {/* Education */} + {education.length > 0 && ( +
+

+ + EDUCATION +

+
+ {education.map((edu) => ( +
+
+

{edu.degree}

+ + {formatDate(edu.graduationDate)} + +
+

+ {edu.school} + {edu.location && `, ${edu.location}`} + {edu.gpa && ` • GPA: ${edu.gpa}`} +

+
+ ))} +
+
+ )} +
+ + {/* Right Column */} +
+ {/* Skills */} + {skills.length > 0 && ( +
+

+ + SKILLS +

+
+ {skills.map((skill) => ( +
+
+ + {skill.name} + + + {skill.level} + +
+
+
+
+
+ ))} +
+
+ )} + + {/* Certifications */} + {certifications && certifications.length > 0 && ( +
+

CERTIFICATIONS

+
+ {certifications.map((cert) => ( +
+
{cert.name}
+
{cert.issuer}
+ {cert.date && ( +
+ {new Date(cert.date).toLocaleDateString('en-US', { year: 'numeric', month: 'short' })} +
+ )} +
+ ))} +
+
+ )} +
+
+
+ ); +} diff --git a/backup/templates/ProfessionalTemplate.tsx b/backup/templates/ProfessionalTemplate.tsx new file mode 100644 index 0000000..d87c90e --- /dev/null +++ b/backup/templates/ProfessionalTemplate.tsx @@ -0,0 +1,147 @@ +import React from 'react'; +import { ResumeData } from '@/types/resume'; +import { Mail, Phone, MapPin, Globe, Linkedin, Github } from 'lucide-react'; + +interface TemplateProps { + data: ResumeData; +} + +export function ProfessionalTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications, projects, references, sections, customization } = data; + + const visibleSections = sections.filter(section => section.isVisible).sort((a, b) => a.order - b.order); + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + const formatUrl = (url: string) => { + if (!url) return ''; + return url.replace(/^https?:\/\/(www\.)?/, '').replace(/\/$/, ''); + }; + + const renderSection = (section: typeof sections[0]) => { + switch (section.type) { + case 'personalInfo': + return ( +
+
+

+ {personalInfo.fullName || 'Your Name'} +

+ {personalInfo.jobTitle && ( +

+ {personalInfo.jobTitle} +

+ )} +
+ +
+ {personalInfo.email && ( +
+ + {personalInfo.email} +
+ )} + {personalInfo.phone && ( +
+ + {personalInfo.phone} +
+ )} + {personalInfo.location && ( +
+ + {personalInfo.location} +
+ )} + {personalInfo.website && ( +
+ + {formatUrl(personalInfo.website)} +
+ )} + {personalInfo.linkedin && ( +
+ + {formatUrl(personalInfo.linkedin)} +
+ )} + {personalInfo.github && ( +
+ + {formatUrl(personalInfo.github)} +
+ )} +
+
+ ); + + case 'summary': + if (!summary) return null; + return ( +
+
+
+

+ Professional Summary +

+
+

+ {summary} +

+
+ ); + + case 'experience': + if (experience.length === 0) return null; + return ( +
+
+
+

+ Professional Experience +

+
+
+ {experience.map((exp) => ( +
+
+

+ {exp.jobTitle} +

+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+
+ + {exp.company}{exp.location && ` • ${exp.location}`} + +
+
    + {exp.description.filter(desc => desc.trim()).map((desc, index) => ( +
  • {desc}
  • + ))} +
+
+ ))} +
+
+ ); + + // Continue with other section types... + default: + return null; + } + }; + + return ( +
+ {visibleSections.map(section => renderSection(section))} +
+ ); +} diff --git a/backup/templates/TechTemplate.tsx b/backup/templates/TechTemplate.tsx new file mode 100644 index 0000000..cd658f7 --- /dev/null +++ b/backup/templates/TechTemplate.tsx @@ -0,0 +1,321 @@ +import { ResumeData } from '@/types/resume'; +import { Mail, Phone, MapPin, Globe, Linkedin, Github, Code, Cpu, BookOpen, Award, Briefcase, GraduationCap } from 'lucide-react'; + +interface TemplateProps { + data: ResumeData; +} + +export function TechTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications, projects, sections, customization } = data; + + const visibleSections = sections.filter(section => section.isVisible).sort((a, b) => a.order - b.order); + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + const formatUrl = (url: string) => { + if (!url) return ''; + return url.replace(/^https?:\/\/(www\.)?/, '').replace(/\/$/, ''); + }; + + const renderSection = (section: typeof sections[0]) => { + switch (section.type) { + case 'personalInfo': + return ( +
+
+
+

+ {personalInfo.fullName || 'Your Name'} +

+

+ {personalInfo.title || 'Software Engineer'} +

+
+
+ {personalInfo.email && ( + + + {personalInfo.email} + + )} + {personalInfo.phone && ( +
+ + {personalInfo.phone} +
+ )} + {personalInfo.location && ( +
+ + {personalInfo.location} +
+ )} + {personalInfo.linkedin && ( + + + LinkedIn + + )} + {personalInfo.github && ( + + + GitHub + + )} + {personalInfo.website && ( + + + Website + + )} +
+
+
+ ); + + case 'summary': + if (!summary) return null; + return ( +
+

+ + Technical Profile +

+

+ {summary} +

+
+ ); + + case 'skills': + if (skills.length === 0) return null; + return ( +
+

+ + Technical Skills +

+
+ {skills.map((skill) => ( +
+
+ + {skill.name} + + + {skill.level} + +
+
+
+
+
+ ))} +
+
+ ); + + case 'experience': + if (experience.length === 0) return null; + return ( +
+

+ + Professional Experience +

+
+ {experience.map((exp) => ( +
+
+
+

+ {exp.jobTitle} +

+

+ {exp.company} + {exp.location && ` • ${exp.location}`} +

+
+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+
    + {exp.description.filter(desc => desc.trim()).map((desc, index) => ( +
  • + + {desc} +
  • + ))} +
+
+ ))} +
+
+ ); + + case 'projects': + if (projects.length === 0) return null; + return ( +
+

+ + Projects +

+
+ {projects.map((project) => ( +
+
+

+ {project.name} + {project.url && ( + + (View Project) + + )} +

+ {project.startDate && ( + + {formatDate(project.startDate)} + {project.endDate ? ` - ${formatDate(project.endDate)}` : ' - Present'} + + )} +
+

+ {project.description} +

+ {project.technologies && project.technologies.length > 0 && ( +
+ {project.technologies.map((tech, i) => ( + + {tech} + + ))} +
+ )} +
+ ))} +
+
+ ); + + case 'education': + if (education.length === 0) return null; + return ( +
+

+ + Education +

+
+ {education.map((edu) => ( +
+
+
+

+ {edu.degree} +

+

+ {edu.school} + {edu.location && `, ${edu.location}`} +

+ {edu.gpa && ( +

+ GPA: {edu.gpa} +

+ )} +
+ + {formatDate(edu.graduationDate)} + +
+ {edu.relevantCourses && edu.relevantCourses.length > 0 && ( +
+

+ Relevant Coursework: +

+
+ {edu.relevantCourses.map((course, i) => ( + + {course} + + ))} +
+
+ )} +
+ ))} +
+
+ ); + + case 'certifications': + if (certifications.length === 0) return null; + return ( +
+

+ + Certifications +

+
+ {certifications.map((cert) => ( +
+

+ {cert.name} +

+

+ {cert.issuer} +

+

+ {formatDate(cert.date)} + {cert.expirationDate && ` - ${formatDate(cert.expirationDate)}`} +

+
+ ))} +
+
+ ); + + default: + return null; + } + }; + + return ( +
+ {visibleSections.map(section => renderSection(section))} +
+ ); +} diff --git a/backup/templates/TimelineTemplate.tsx b/backup/templates/TimelineTemplate.tsx new file mode 100644 index 0000000..30b9a1f --- /dev/null +++ b/backup/templates/TimelineTemplate.tsx @@ -0,0 +1,183 @@ +import { Mail, Phone, MapPin, Globe, Linkedin, Github, Briefcase, GraduationCap, Code, Award } from 'lucide-react'; +import { ResumeData } from '@/types/resume'; + +type TemplateProps = { + data: ResumeData; +}; + +export function TimelineTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications } = data; + const accentColor = data.customization.accentColor || '#3b82f6'; + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short' }); + }; + + return ( +
+ {/* Header */} +
+

{personalInfo.fullName}

+ {personalInfo.title && ( +

{personalInfo.title}

+ )} +
+ {personalInfo.email && {personalInfo.email}} + {personalInfo.phone && {personalInfo.phone}} + {personalInfo.location && {personalInfo.location}} +
+
+ + {/* Summary */} + {summary && ( +
+

Professional Summary

+

{summary}

+
+ )} + + {/* Timeline Section */} +
+ {/* Timeline Line */} +
+ +
+ {/* Experience */} + {experience.length > 0 && ( +
+

+ + Work Experience +

+
+ {experience.map((exp, idx) => ( +
+
+
+
+

{exp.jobTitle}

+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+

+ {exp.company} + {exp.location && ` • ${exp.location}`} +

+
    + {exp.description.map((desc, idx) => ( +
  • + + {desc} +
  • + ))} +
+
+
+ ))} +
+
+ )} + + {/* Education */} + {education.length > 0 && ( +
+

+ + Education +

+
+ {education.map((edu) => ( +
+
+
+
+

{edu.degree}

+ + {formatDate(edu.graduationDate)} + +
+

+ {edu.school} + {edu.location && `, ${edu.location}`} +

+ {edu.gpa && ( +

+ GPA: {edu.gpa} +

+ )} +
+
+ ))} +
+
+ )} +
+
+ + {/* Skills & Certifications */} +
+ {/* Skills */} + {skills.length > 0 && ( +
+

+ + Skills +

+
+ {skills.map((skill) => ( + + {skill.name} + + ))} +
+
+ )} + + {/* Certifications */} + {certifications.length > 0 && ( +
+

+ + Certifications +

+
+ {certifications.map((cert) => ( +
+

{cert.name}

+

+ {cert.issuer} • {formatDate(cert.date)} +

+
+ ))} +
+
+ )} +
+
+ ); +} diff --git a/backup/templates/types.ts b/backup/templates/types.ts new file mode 100644 index 0000000..6f36d51 --- /dev/null +++ b/backup/templates/types.ts @@ -0,0 +1,58 @@ +import { ResumeData } from '@/types/resume'; + +export interface TemplateProps { + data: ResumeData; +} + +export interface TemplateSectionProps { + title: string; + className?: string; + children: React.ReactNode; +} + +export interface ExperienceItemProps { + jobTitle: string; + company: string; + location?: string; + startDate: string; + endDate: string; + isCurrentJob: boolean; + description: string[]; + className?: string; +} + +export interface EducationItemProps { + degree: string; + school: string; + location?: string; + graduationDate: string; + fieldOfStudy?: string; + gpa?: string; + className?: string; +} + +export interface SkillItemProps { + name: string; + level: 'Beginner' | 'Intermediate' | 'Advanced' | 'Expert'; + className?: string; +} + +export interface CertificationItemProps { + name: string; + issuer: string; + date: string; + expirationDate?: string; + className?: string; +} + +export interface TemplateStyles { + fontFamily?: string; + primaryColor?: string; + secondaryColor?: string; + textColor?: string; + backgroundColor?: string; + spacing?: { + section?: string; + item?: string; + }; +} diff --git a/docs/TEMPLATE_INTEGRATION_PLAN.md b/docs/TEMPLATE_INTEGRATION_PLAN.md new file mode 100644 index 0000000..708591c --- /dev/null +++ b/docs/TEMPLATE_INTEGRATION_PLAN.md @@ -0,0 +1,88 @@ +# Resume Template Integration Plan + +## Phase 1: Template Integration + +### 1.1 Audit Existing Templates +- [ ] Create a complete inventory of all 27 template files +- [ ] Verify each template implements the correct `TemplateProps` interface +- [ ] Ensure consistent export naming conventions + +### 1.2 Create Template Registry +- [ ] Create a new file `templateRegistry.ts` to manage all templates +- [ ] Import and register all 27 templates with metadata (name, category, preview image) +- [ ] Implement a template selection component + +### 1.3 Update Resume Preview +- [ ] Modify the preview component to use the selected template +- [ ] Implement template switching functionality +- [ ] Add loading states for template changes + +## Phase 2: UI/UX Improvements + +### 2.1 Style Section Redesign +- [ ] Update the Style tab to display all 27 templates +- [ ] Implement a responsive grid layout for template selection +- [ ] Add search and filter functionality for templates +- [ ] Include template preview thumbnails + +### 2.2 Template Previews +- [ ] Generate or create preview images for each template +- [ ] Implement hover effects and selection states +- [ ] Add template details on hover + +## Phase 3: Testing & Optimization + +### 3.1 ATS Compatibility Testing +- [ ] Test each template with ATS simulation tools +- [ ] Ensure proper semantic HTML structure +- [ ] Verify text extraction works correctly + +### 3.2 Print Testing +- [ ] Test print output for each template +- [ ] Ensure proper page breaks and margins +- [ ] Verify color contrast for printed versions + +### 3.3 Performance Optimization +- [ ] Implement code splitting for template loading +- [ ] Optimize template components for performance +- [ ] Add lazy loading for template previews + +## Phase 4: Documentation + +### 4.1 Update README.md +- [ ] Add a section about the 27 available templates +- [ ] Include template previews in the documentation +- [ ] Document template features and best use cases + +### 4.2 Developer Documentation +- [ ] Document how to add new templates +- [ ] Create template development guidelines +- [ ] Document template props and requirements + +## Phase 5: Final Review + +### 5.1 Code Review +- [ ] Review all template implementations +- [ ] Ensure consistent styling and behavior +- [ ] Verify all templates work with all features + +### 5.2 User Testing +- [ ] Test template selection and switching +- [ ] Verify all templates render correctly +- [ ] Test on different screen sizes + +## Implementation Notes: +1. **Template Organization**: + - Group templates by category (Minimalist, Professional, Creative, etc.) + - Use consistent naming conventions + - Maintain a clean file structure + +2. **Performance Considerations**: + - Lazy load template components + - Optimize preview generation + - Cache template previews + +3. **Accessibility**: + - Ensure all templates meet WCAG guidelines + - Test with screen readers + - Verify keyboard navigation diff --git a/scripts/audit-templates.js b/scripts/audit-templates.js new file mode 100644 index 0000000..2283a70 --- /dev/null +++ b/scripts/audit-templates.js @@ -0,0 +1,54 @@ +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const templatesDir = path.join(__dirname, '..', 'src', 'components', 'resume-builder', 'templates'); +const outputFile = path.join(__dirname, '..', 'TEMPLATE_AUDIT_REPORT.md'); + +// Get all template files +const templateFiles = fs.readdirSync(templatesDir).filter(file => file.endsWith('.tsx')); + +// Initialize report +let report = '# Template Audit Report\n\n'; +report += `Generated: ${new Date().toISOString()}\n\n`; +report += `Total templates found: ${templateFiles.length}\n\n`; + +// Check each template file +templateFiles.forEach((file, index) => { + const filePath = path.join(templatesDir, file); + const content = fs.readFileSync(filePath, 'utf8'); + + // Basic checks + const hasTemplateProps = content.includes('interface TemplateProps') || content.includes('type TemplateProps'); + const hasExport = content.includes(`export function ${file.replace('.tsx', '')}`); + const hasDefaultExport = content.includes('export default'); + + // Add to report + report += `## ${file}\n`; + report += `- **Export Name:** ${file.replace('.tsx', '')}\n`; + report += `- **Has TemplateProps:** ${hasTemplateProps ? '✅' : '❌'}\n`; + report += `- **Has Correct Export:** ${hasExport ? '✅' : '❌'}\n`; + report += `- **Has Default Export:** ${hasDefaultExport ? '✅' : '❌'}\n`; + + // Check for common issues + const issues = []; + if (!hasTemplateProps) issues.push('Missing TemplateProps interface/type'); + if (!hasExport) issues.push(`Missing export for ${file.replace('.tsx', '')}`); + + if (issues.length > 0) { + report += '\n**Issues:**\n'; + issues.forEach(issue => { + report += `- ❌ ${issue}\n`; + }); + } else { + report += '\n✅ No issues found\n'; + } + + report += '\n---\n\n'; +}); + +// Save the report +fs.writeFileSync(outputFile, report); +console.log(`Audit complete. Report saved to: ${outputFile}`); diff --git a/scripts/audit-templates.ts b/scripts/audit-templates.ts new file mode 100644 index 0000000..dce6a32 --- /dev/null +++ b/scripts/audit-templates.ts @@ -0,0 +1,115 @@ +import fs from 'fs'; +import path from 'path'; + +const templatesDir = path.join(process.cwd(), 'src/components/resume-builder/templates'); +const templateFiles = fs.readdirSync(templatesDir).filter(file => file.endsWith('.tsx')); + +interface TemplateAuditResult { + filename: string; + hasTemplateProps: boolean; + exportName: string; + hasDefaultExport: boolean; + issues: string[]; +} + +const auditResults: TemplateAuditResult[] = []; + +// Check each template file +templateFiles.forEach(filename => { + const filePath = path.join(templatesDir, filename); + const content = fs.readFileSync(filePath, 'utf8'); + + const result: TemplateAuditResult = { + filename, + hasTemplateProps: content.includes('interface TemplateProps') || content.includes('type TemplateProps'), + exportName: '', + hasDefaultExport: content.includes('export default'), + issues: [] + }; + + // Extract export name + const exportMatch = content.match(/export\s+(?:function|const|class)\s+(\w+)/); + if (exportMatch) { + result.exportName = exportMatch[1]; + } else { + result.issues.push('No named export found'); + } + + // Check for common issues + if (!result.hasTemplateProps) { + result.issues.push('Missing TemplateProps interface/type'); + } + + // Check if export name matches filename + const expectedExportName = filename.replace('.tsx', ''); + if (result.exportName && result.exportName !== expectedExportName) { + result.issues.push(`Export name (${result.exportName}) doesn't match filename (${expectedExportName})`); + } + + auditResults.push(result); +}); + +// Generate report +console.log('## Template Audit Report'); +console.log(`Generated: ${new Date().toISOString()}`); +console.log(`Templates Audited: ${auditResults.length}\n`); + +// Summary +const templatesWithIssues = auditResults.filter(t => t.issues.length > 0); +console.log('### Summary'); +console.log(`- Total Templates: ${auditResults.length}`); +console.log(`- Templates with issues: ${templatesWithIssues.length}`); +console.log(`- Templates without TemplateProps: ${auditResults.filter(t => !t.hasTemplateProps).length}`); +console.log(`- Templates with default export: ${auditResults.filter(t => t.hasDefaultExport).length}\n`); + +// Detailed report +if (templatesWithIssues.length > 0) { + console.log('### Templates with Issues'); + templatesWithIssues.forEach(template => { + console.log(`\n#### ${template.filename}`); + if (template.exportName) { + console.log(`- Export: ${template.exportName}`); + } + template.issues.forEach(issue => { + console.log(`- ❌ ${issue}`); + }); + }); +} + +// All templates list +console.log('\n### All Templates'); +console.log('| Filename | Export Name | Issues |'); +console.log('|----------|-------------|--------|'); +auditResults.forEach(template => { + const status = template.issues.length === 0 ? '✅' : '❌'; + console.log(`| ${template.filename} | ${template.exportName || 'N/A'} | ${template.issues.length} issue(s) |`); +}); + +// Save report to file +const reportPath = path.join(process.cwd(), 'TEMPLATE_AUDIT_REPORT.md'); +const reportStream = fs.createWriteStream(reportPath); + +reportStream.write('# Template Audit Report\n'); +reportStream.write(`Generated: ${new Date().toISOString()}\n\n`); + +// Summary +auditResults.forEach(template => { + reportStream.write(`## ${template.filename}\n`); + reportStream.write(`- Export: ${template.exportName || 'N/A'}\n`); + reportStream.write(`- Has TemplateProps: ${template.hasTemplateProps ? '✅' : '❌'}\n`); + reportStream.write(`- Has Default Export: ${template.hasDefaultExport ? '✅' : '❌'}\n`); + + if (template.issues.length > 0) { + reportStream.write('### Issues\n'); + template.issues.forEach(issue => { + reportStream.write(`- ❌ ${issue}\n`); + }); + } else { + reportStream.write('✅ No issues found\n'); + } + reportStream.write('\n'); +}); + +reportStream.end(); + +console.log(`\nAudit complete. Detailed report saved to: ${reportPath}`); diff --git a/scripts/rename-templates.js b/scripts/rename-templates.js new file mode 100644 index 0000000..beaf0bb --- /dev/null +++ b/scripts/rename-templates.js @@ -0,0 +1,158 @@ +const fs = require('fs'); +const path = require('path'); +const { promisify } = require('util'); + +const readdir = promisify(fs.readdir); +const rename = promisify(fs.rename); +const stat = promisify(fs.stat); + +// Template mapping: old name -> new name +const templateMapping = { + // Minimal Style (MIN-XX-Name) + 'MinimalTemplate.tsx': 'MIN-01-Minimal.tsx', + 'MinimalistTemplate.tsx': 'MIN-02-Minimalist.tsx', + 'MinimalistATS.tsx': 'MIN-03-ATS.tsx', + 'MinimalistSidebarTemplate.tsx': 'MIN-04-Sidebar.tsx', + 'CleanMinimalistTemplate.tsx': 'MIN-05-Clean.tsx', + + // Professional Style (PRO-XX-Name) + 'ProfessionalTemplate.tsx': 'PRO-01-Professional.tsx', + 'ProfessionalSidebarTemplate.tsx': 'PRO-02-Sidebar.tsx', + 'ProfessionalBlueTemplate.tsx': 'PRO-03-Blue.tsx', + 'ProfessionalSummaryTemplate.tsx': 'PRO-04-Summary.tsx', + 'MinimalistProfessionalTemplate.tsx': 'PRO-05-Minimalist.tsx', + 'ModernProfessionalTemplate.tsx': 'PRO-06-Modern.tsx', + 'CleanProfessionalTemplate.tsx': 'PRO-07-Clean.tsx', + + // Creative Style (CRE-XX-Name) + 'CreativeTemplate.tsx': 'CRE-01-Creative.tsx', + 'CreativeColorBlocksTemplate.tsx': 'CRE-02-ColorBlocks.tsx', + 'ModernTemplate.tsx': 'CRE-03-Modern.tsx', + 'ModernSidebarTemplate.tsx': 'CRE-04-ModernSidebar.tsx', + 'ModernDarkSidebarTemplate.tsx': 'CRE-05-DarkSidebar.tsx', + + // Executive Style (EXE-XX-Name) + 'ExecutiveTemplate.tsx': 'EXE-01-Executive.tsx', + 'ModernExecutiveTemplate.tsx': 'EXE-02-Modern.tsx', + 'CorporateTemplate.tsx': 'EXE-03-Corporate.tsx', + + // Other Style (OTH-XX-Name) + 'AcademicTemplate.tsx': 'OTH-01-Academic.tsx', + 'BoldTemplate.tsx': 'OTH-02-Bold.tsx', + 'ElegantTemplate.tsx': 'OTH-03-Elegant.tsx', + 'TechTemplate.tsx': 'OTH-04-Tech.tsx', + 'TimelineTemplate.tsx': 'OTH-05-Timeline.tsx', + 'ModernTimelineTemplate.tsx': 'OTH-06-ModernTimeline.tsx', + 'ModernTwoColumn.tsx': 'OTH-07-TwoColumn.tsx' +}; + +// ID mapping for template registry +const idMapping = { + // Minimal Style + 'minimal': 'min-01-minimal', + 'minimalist': 'min-02-minimalist', + 'minimalist-ats': 'min-03-ats', + 'minimalist-sidebar': 'min-04-sidebar', + 'clean-minimalist': 'min-05-clean', + + // Professional Style + 'professional': 'pro-01-professional', + 'professional-sidebar': 'pro-02-sidebar', + 'professional-blue': 'pro-03-blue', + 'professional-summary': 'pro-04-summary', + 'minimalist-professional': 'pro-05-minimalist', + 'modern-professional': 'pro-06-modern', + 'clean-professional': 'pro-07-clean', + + // Creative Style + 'creative': 'cre-01-creative', + 'creative-color-blocks': 'cre-02-color-blocks', + 'modern': 'cre-03-modern', + 'modern-sidebar': 'cre-04-modern-sidebar', + 'modern-dark-sidebar': 'cre-05-dark-sidebar', + + // Executive Style + 'executive': 'exe-01-executive', + 'modern-executive': 'exe-02-modern', + 'corporate': 'exe-03-corporate', + + // Other Style + 'academic': 'oth-01-academic', + 'bold': 'oth-02-bold', + 'elegant': 'oth-03-elegant', + 'tech': 'oth-04-tech', + 'timeline': 'oth-05-timeline', + 'modern-timeline': 'oth-06-modern-timeline', + 'modern-two-column': 'oth-07-two-column' +}; + +async function updateTemplateRegistry() { + const registryPath = path.join(__dirname, '../src/components/resume-builder/templateRegistry.ts'); + let registryContent = await fs.promises.readFile(registryPath, 'utf8'); + + // Update imports + for (const [oldName, newName] of Object.entries(templateMapping)) { + const oldImportName = oldName.replace('.tsx', ''); + const newImportName = newName.replace('.tsx', ''); + + // Update imports + const importRegex = new RegExp(`import\('\..\/templates\/${oldName.replace('.', '\.')}'\)`, 'g'); + registryContent = registryContent.replace(importRegex, `import('..\/templates\/${newName}')`); + + // Update component names in the registry + const componentRegex = new RegExp(`component: templates\.${oldImportName}`, 'g'); + registryContent = registryContent.replace(componentRegex, `component: templates.${newImportName}`); + } + + // Update template IDs in the registry + for (const [oldId, newId] of Object.entries(idMapping)) { + const idRegex = new RegExp(`id: '${oldId}'`, 'g'); + registryContent = registryContent.replace(idRegex, `id: '${newId}'`); + } + + // Write the updated registry back to disk + await fs.promises.writeFile(registryPath, registryContent, 'utf8'); + console.log('Template registry updated successfully'); +} + +async function renameTemplateFiles() { + const templatesDir = path.join(__dirname, '../src/components/resume-builder/templates'); + const files = await readdir(templatesDir); + + for (const file of files) { + if (templateMapping[file]) { + const oldPath = path.join(templatesDir, file); + const newPath = path.join(templatesDir, templateMapping[file]); + + try { + await rename(oldPath, newPath); + console.log(`Renamed: ${file} -> ${templateMapping[file]}`); + } catch (err) { + console.error(`Error renaming ${file}:`, err); + } + } + } +} + +async function main() { + try { + console.log('Starting template renaming process...'); + + // First rename all the files + console.log('\nRenaming template files...'); + await renameTemplateFiles(); + + // Then update the template registry + console.log('\nUpdating template registry...'); + await updateTemplateRegistry(); + + console.log('\nTemplate renaming completed successfully!'); + console.log('Please review the changes and run tests to ensure everything works as expected.'); + + } catch (error) { + console.error('Error during template renaming:', error); + process.exit(1); + } +} + +main(); diff --git a/src/components/resume-builder/TemplatePreview.tsx b/src/components/resume-builder/TemplatePreview.tsx new file mode 100644 index 0000000..053d8b2 --- /dev/null +++ b/src/components/resume-builder/TemplatePreview.tsx @@ -0,0 +1,359 @@ +import { useEffect, useState, useMemo } from 'react'; +import { getTemplateById } from './templateRegistry'; +import { TemplateCategory, TemplateMeta } from './types'; +import { TemplateProps } from './templates/types'; +import { AlertCircle, FileQuestion, Check } from 'lucide-react'; +import { cn } from '@/lib/utils'; + +// Define a type for the template component +type TemplateComponent = React.LazyExoticComponent>; + +type TemplateLayout = 'one-column' | 'two-column' | 'sidebar' | 'creative'; + +// Define the base SVG function with proper escaping +const baseSvg = (content: string, colors: TemplateColors) => { + const { background } = colors; + try { + return ` + + + ${content} + + `; + } catch (error) { + console.error('Error generating SVG:', error); + return ` + + + Preview Error + + `; + } +}; + +interface TemplatePreviewProps { + templateId: string; + className?: string; + isSelected?: boolean; + onClick?: (e: React.MouseEvent | React.KeyboardEvent) => void; +} + +// Use TemplateMeta directly since we don't need to modify it + +interface TemplateColors { + primary: string; + secondary: string; + background: string; + text: string; + muted: string; +} + +const getTemplateLayout = (templateId: string): TemplateLayout => { + if (templateId.includes('sidebar')) return 'sidebar'; + if (templateId.includes('modern') || templateId.includes('creative')) return 'creative'; + if (templateId.includes('minimalist')) return 'one-column'; + return 'two-column'; +}; + +const getCategoryColors = (category: TemplateCategory): TemplateColors => ({ + [TemplateCategory.PROFESSIONAL]: { + primary: '#1e40af', + secondary: '#93c5fd', + background: '#ffffff', + text: '#111827', + muted: '#6b7280' + }, + [TemplateCategory.MODERN]: { + primary: '#7c3aed', + secondary: '#c4b5fd', + background: '#f9fafb', + text: '#1f2937', + muted: '#6b7280' + }, + [TemplateCategory.MINIMALIST]: { + primary: '#059669', + secondary: '#6ee7b7', + background: '#ffffff', + text: '#1f2937', + muted: '#6b7280' + }, + [TemplateCategory.CREATIVE]: { + primary: '#c026d3', + secondary: '#f0abfc', + background: '#fdf4ff', + text: '#1f2937', + muted: '#6b7280' + }, + [TemplateCategory.SPECIALIZED]: { + primary: '#d97706', + secondary: '#fcd34d', + background: '#fffbeb', + text: '#1f2937', + muted: '#6b7280' + }, +})[category] || { + primary: '#1e40af', + secondary: '#93c5fd', + background: '#ffffff', + text: '#111827', + muted: '#6b7280' +}; + +const generateSvgPreview = (layout: TemplateLayout, colors: TemplateColors): string => { + if (!colors) { + colors = { + primary: '#1e40af', + secondary: '#93c5fd', + background: '#ffffff', + text: '#111827', + muted: '#6b7280' + }; + } + const { primary, secondary, text, muted } = colors; + + switch (layout) { + case 'one-column': + return baseSvg(` + + + + {/* Contact Info */} + + + + + {/* Work Experience */} + + + + + {/* Skills */} + + + + `, colors); + + case 'sidebar': + return baseSvg(` + {/* Sidebar */} + + + + + {/* Main Content */} + + + + {/* Experience */} + + + `, colors); + + case 'creative': + return baseSvg(` + {/* Decorative Elements */} + + + + {/* Header */} + + + + {/* Content Blocks */} + + + `, colors); + + default: // two-column + return baseSvg(` + {/* Left Column */} + + + + {/* Right Column */} + + + + {/* Experience */} + + + `, colors); + } +}; + +// Helper function to safely get template metadata +const useTemplate = (templateId: string): TemplateMeta | null => { + return useMemo(() => { + try { + const t = getTemplateById(templateId); + if (!t) { + console.warn(`Template not found: ${templateId}, falling back to 'minimalist'`); + return getTemplateById('minimalist'); + } + return t; + } catch (err) { + console.error('Error loading template:', err); + return null; + } + }, [templateId]); +}; + +export default function TemplatePreview({ + templateId, + className = '', + isSelected = false, + onClick +}: TemplatePreviewProps) { + const [previewImage, setPreviewImage] = useState(null); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(null); + + // Get template metadata safely + const template = useTemplate(templateId); + + // Load preview image or generate SVG preview + useEffect(() => { + // This variable will hold the URL if we create one in this effect run. + let objectUrl: string | null = null; + + const loadPreview = async () => { + if (!template) { + setError('Template not found'); + setIsLoading(false); + return; + } + + setIsLoading(true); + setError(null); + + try { + if (template.previewImage) { + // If template has a preview image, use it + setPreviewImage(template.previewImage); + } else { + // Otherwise generate an SVG preview + const layout = getTemplateLayout(template.id); + const category = template.category || TemplateCategory.PROFESSIONAL; + const colors = getCategoryColors(category); + + const svgContent = generateSvgPreview(layout, colors); + if (!svgContent) { + throw new Error('Failed to generate SVG content'); + } + const svgBlob = new Blob([svgContent], { type: 'image/svg+xml' }); + objectUrl = URL.createObjectURL(svgBlob); + setPreviewImage(objectUrl); + } + } catch (err) { + console.error('Error generating preview:', err); + setError('Failed to generate preview'); + // Fallback to a simple SVG + const fallbackSvg = ` + + + + + + `; + const svgBlob = new Blob([fallbackSvg], { type: 'image/svg+xml' }); + objectUrl = URL.createObjectURL(svgBlob); + setPreviewImage(objectUrl); + } finally { + setIsLoading(false); + } + }; + + loadPreview(); + + // Clean up object URL to prevent memory leaks + return () => { + if (objectUrl) { + URL.revokeObjectURL(objectUrl); + } + }; + }, [template]); + + if (isLoading) { + return ( +
+
+
+
+
+
+ ); + } + + if (error) { + return ( +
+
+ + Failed to load preview +
+

{error}

+
+ ); + } + + if (!previewImage) { + return ( +
+
+ +

No preview available

+
+
+ ); + } + + return ( +
) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + if (onClick) { + onClick(e); + } + } + }} + > +
+ {`${template?.name} { + setError('Failed to load preview image'); + setPreviewImage(null); + }} + /> +
+
+

+ {template?.name || 'Untitled Template'} +

+ {template?.category && ( + + {template.category} + + )} +
+ {isSelected && ( +
+ +
+ )} +
+ ); +} diff --git a/src/components/resume-builder/TemplateSelector.tsx b/src/components/resume-builder/TemplateSelector.tsx new file mode 100644 index 0000000..beac202 --- /dev/null +++ b/src/components/resume-builder/TemplateSelector.tsx @@ -0,0 +1,215 @@ +import { useState, Suspense, lazy } from 'react'; +import { Card, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; +import { Input } from '@/components/ui/input'; +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; +import { Skeleton } from '@/components/ui/skeleton'; +import { templateRegistry } from './templateRegistry'; +import { TemplateCategory } from './types'; +import { + LayoutGrid, + Briefcase, + Sparkles, + RectangleHorizontal, + Palette, + Target, + ShieldCheck, + Check, +} from 'lucide-react'; +import { cn } from '@/lib/utils'; + +// Lazy load the template preview components +const TemplatePreview = lazy(() => import('./TemplatePreview')); + +// Type for template item +interface TemplateItem { + id: string; + name: string; + description: string; + category: TemplateCategory; + atsFriendly?: boolean; + tags?: string[]; +} + +interface TemplateSelectorProps { + onSelectTemplate: (templateId: string) => void; + selectedTemplateId?: string; +} + +const categories = [ + { value: 'all', name: 'All', icon: }, + { value: TemplateCategory.PROFESSIONAL, name: 'Professional', icon: }, + { value: TemplateCategory.MODERN, name: 'Modern', icon: }, + { value: TemplateCategory.MINIMALIST, name: 'Minimalist', icon: }, + { value: TemplateCategory.CREATIVE, name: 'Creative', icon: }, + { value: TemplateCategory.SPECIALIZED, name: 'Specialized', icon: }, + { value: 'ats', name: 'ATS Friendly', icon: }, +]; + +export function TemplateSelector({ onSelectTemplate, selectedTemplateId }: TemplateSelectorProps) { + const [searchQuery, setSearchQuery] = useState(''); + const [selectedCategory, setSelectedCategory] = useState('all'); + + // Filter templates based on search query and selected category + const filteredTemplates = templateRegistry.filter((template) => { + const matchesSearch = template.name.toLowerCase().includes(searchQuery.toLowerCase()) || + template.description.toLowerCase().includes(searchQuery.toLowerCase()) || + template.tags?.some((tag: string) => tag.toLowerCase().includes(searchQuery.toLowerCase())); + + const matchesCategory = selectedCategory === 'all' || + template.category === selectedCategory || + (selectedCategory === 'ats' && template.atsFriendly); + + return matchesSearch && matchesCategory; + }); + + return ( +
+
+
+

Choose a Template

+

+ Select a template that best fits your needs. You can change this later. +

+
+ +
+
+ setSearchQuery(e.target.value)} + className="w-full" + /> +
+
+ + setSelectedCategory(value)} + > + + {categories.map((cat) => ( + + + + + {cat.icon} + + +

{cat.name}

+
+
+ ))} +
+ + + {filteredTemplates.length === 0 ? ( +
+

No templates found matching your criteria.

+ +
+ ) : ( +
+ {filteredTemplates.map((template: TemplateItem) => { + const isSelected = selectedTemplateId === template.id; + return ( + + + +
onSelectTemplate(template.id)} + onKeyDown={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + onSelectTemplate(template.id); + } + }} + className={cn( + 'group relative cursor-pointer overflow-hidden rounded-lg border-2 transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2', + isSelected ? 'border-primary ring-2 ring-primary/20' : 'border-transparent hover:border-primary/50' + )} + > +
+ }> + + +
+ {isSelected && ( +
+ +
+ )} +
+
+ +
+
+ }> + + +
+
+

{template.name}

+

{template.description}

+
+
+ + {template.category} + + {template.atsFriendly && ( + + + ATS Friendly + + )} + {template.tags?.map((tag: string) => ( + + {tag} + + ))} +
+
+
+
+
+ ); + })} +
+ )} +
+
+
+
+ ); +} diff --git a/src/components/resume-builder/templateRegistry.ts b/src/components/resume-builder/templateRegistry.ts new file mode 100644 index 0000000..19f1cc3 --- /dev/null +++ b/src/components/resume-builder/templateRegistry.ts @@ -0,0 +1,294 @@ +import { lazy, LazyExoticComponent } from 'react'; +import { TemplateMeta, TemplateCategory } from './types'; +import { TemplateProps } from './templates/types'; + +// Define the type for our template components +type TemplateComponent = (props: TemplateProps) => JSX.Element; +type LazyTemplate = LazyExoticComponent; + +// Helper function to create lazy-loaded template components with proper typing +const createLazyTemplate = ( + importFn: () => Promise, + exportName: keyof T & string +): LazyTemplate => { + return lazy(async () => { + const module = await importFn(); + return { default: module[exportName] }; + }); +}; + +// Lazy load all templates with proper typing +const templates = { + AcademicTemplate: createLazyTemplate( + () => import('./templates/AcademicTemplate'), + 'AcademicTemplate' + ), + BoldTemplate: createLazyTemplate( + () => import('./templates/BoldTemplate'), + 'BoldTemplate' + ), + CleanMinimalistTemplate: createLazyTemplate( + () => import('./templates/CleanMinimalistTemplate'), + 'CleanMinimalistTemplate' + ), + CleanProfessionalTemplate: createLazyTemplate( + () => import('./templates/CleanProfessionalTemplate'), + 'CleanProfessionalTemplate' + ), + CorporateTemplate: createLazyTemplate( + () => import('./templates/CorporateTemplate'), + 'CorporateTemplate' + ), + CreativeColorBlocksTemplate: createLazyTemplate( + () => import('./templates/CreativeColorBlocksTemplate'), + 'CreativeColorBlocksTemplate' + ), + CreativeTemplate: createLazyTemplate( + () => import('./templates/CreativeTemplate'), + 'CreativeTemplate' + ), + ElegantTemplate: createLazyTemplate( + () => import('./templates/ElegantTemplate'), + 'ElegantTemplate' + ), + ExecutiveTemplate: createLazyTemplate( + () => import('./templates/ExecutiveTemplate'), + 'ExecutiveTemplate' + ), + // MinimalTemplate: createLazyTemplate( + // () => import('./templates/MinimalTemplate'), + // 'MinimalTemplate' + // ), + MinimalistATS: createLazyTemplate( + () => import('./templates/MinimalistATS'), + 'MinimalistATS' + ), + MinimalistProfessionalTemplate: createLazyTemplate( + () => import('./templates/MinimalistProfessionalTemplate'), + 'MinimalistProfessionalTemplate' + ), + MinimalistSidebarTemplate: createLazyTemplate( + () => import('./templates/MinimalistSidebarTemplate'), + 'MinimalistSidebarTemplate' + ), + MinimalistTemplate: createLazyTemplate( + () => import('./templates/MinimalistTemplate'), + 'MinimalistTemplate' + ), + ModernDarkSidebarTemplate: createLazyTemplate( + () => import('./templates/ModernDarkSidebarTemplate'), + 'ModernDarkSidebarTemplate' + ), + ModernExecutiveTemplate: createLazyTemplate( + () => import('./templates/ModernExecutiveTemplate'), + 'ModernExecutiveTemplate' + ), + ModernProfessionalTemplate: createLazyTemplate( + () => import('./templates/ModernProfessionalTemplate'), + 'ModernProfessionalTemplate' + ), + ModernSidebarTemplate: createLazyTemplate( + () => import('./templates/ModernSidebarTemplate'), + 'ModernSidebarTemplate' + ), + ModernTemplate: createLazyTemplate( + () => import('./templates/ModernTemplate'), + 'ModernTemplate' + ), + ModernTimelineTemplate: createLazyTemplate( + () => import('./templates/ModernTimelineTemplate'), + 'ModernTimelineTemplate' + ), + ModernTwoColumn: createLazyTemplate( + () => import('./templates/ModernTwoColumn'), + 'ModernTwoColumn' + ), + ProfessionalBlueTemplate: createLazyTemplate( + () => import('./templates/ProfessionalBlueTemplate'), + 'ProfessionalBlueTemplate' + ), + ProfessionalSidebarTemplate: createLazyTemplate( + () => import('./templates/ProfessionalSidebarTemplate'), + 'ProfessionalSidebarTemplate' + ), + ProfessionalSummaryTemplate: createLazyTemplate( + () => import('./templates/ProfessionalSummaryTemplate'), + 'ProfessionalSummaryTemplate' + ), + ProfessionalTemplate: createLazyTemplate( + () => import('./templates/ProfessionalTemplate'), + 'ProfessionalTemplate' + ), + TechTemplate: createLazyTemplate( + () => import('./templates/TechTemplate'), + 'TechTemplate' + ), + TimelineTemplate: createLazyTemplate( + () => import('./templates/TimelineTemplate'), + 'TimelineTemplate' + ), +}; + +// Template metadata with descriptions and categories +export const templateRegistry: TemplateMeta[] = [ + { + id: 'clean-professional', + name: 'Clean Professional', + description: 'A clean and professional resume template with a balanced layout.', + category: TemplateCategory.PROFESSIONAL, + component: templates.CleanProfessionalTemplate, + tags: ['professional', 'clean', 'ats-friendly'], + atsFriendly: true, + lastUpdated: '2025-07-23', + }, + { + id: 'modern-executive', + name: 'Modern Executive', + description: 'A modern executive resume with a sidebar for key information.', + category: TemplateCategory.MODERN, + component: templates.ModernExecutiveTemplate, + tags: ['modern', 'executive', 'sidebar'], + atsFriendly: true, + lastUpdated: '2025-07-23', + }, + { + id: 'minimalist-ats', + name: 'Minimalist ATS', + description: 'Optimized for Applicant Tracking Systems with clean typography and structure.', + category: TemplateCategory.MINIMALIST, + component: templates.MinimalistATS, + tags: ['minimalist', 'ats-optimized', 'clean'], + atsFriendly: true, + lastUpdated: '2025-07-23', + }, + { + id: 'creative-color-blocks', + name: 'Creative Color Blocks', + description: 'A creative template with color blocks for a modern, eye-catching design.', + category: TemplateCategory.CREATIVE, + component: templates.CreativeColorBlocksTemplate, + tags: ['creative', 'colorful', 'modern'], + atsFriendly: false, + lastUpdated: '2025-07-23', + }, + // Add more templates with their metadata + { + id: 'academic', + name: 'Academic', + description: 'Designed for academic and research positions with emphasis on publications.', + category: TemplateCategory.SPECIALIZED, + component: templates.AcademicTemplate, + tags: ['academic', 'research', 'publications'], + atsFriendly: true, + lastUpdated: '2025-07-23', + }, + { + id: 'bold', + name: 'Bold', + description: 'A bold and confident design that makes a strong impression.', + category: TemplateCategory.CREATIVE, + component: templates.BoldTemplate, + tags: ['bold', 'modern', 'creative'], + atsFriendly: false, + lastUpdated: '2025-07-23', + }, + { + id: 'clean-minimalist', + name: 'Clean Minimalist', + description: 'A minimalist design focused on content with plenty of white space.', + category: TemplateCategory.MINIMALIST, + component: templates.CleanMinimalistTemplate, + tags: ['minimalist', 'clean', 'simple'], + atsFriendly: true, + lastUpdated: '2025-07-23', + }, + { + id: 'corporate', + name: 'Corporate', + description: 'A traditional corporate resume with a professional layout.', + category: TemplateCategory.PROFESSIONAL, + component: templates.CorporateTemplate, + tags: ['corporate', 'professional', 'traditional'], + atsFriendly: true, + lastUpdated: '2025-07-23', + }, + { + id: 'minimalist', + name: 'Minimalist', + description: 'A clean and minimalist resume template with a focus on readability.', + category: TemplateCategory.MINIMALIST, + component: templates.MinimalistTemplate, + tags: ['minimalist', 'clean', 'simple'], + atsFriendly: true, + lastUpdated: '2025-07-23', + }, + { + id: 'modern-timeline', + name: 'Modern Timeline', + description: 'A modern design with a timeline layout for work experience.', + category: TemplateCategory.MODERN, + component: templates.ModernTimelineTemplate, + tags: ['modern', 'timeline', 'creative'], + atsFriendly: true, + lastUpdated: '2025-07-23', + }, + { + id: 'professional-sidebar', + name: 'Professional Sidebar', + description: 'Professional layout with a sidebar for key information.', + category: TemplateCategory.PROFESSIONAL, + component: templates.ProfessionalSidebarTemplate, + tags: ['professional', 'sidebar', 'modern'], + atsFriendly: true, + lastUpdated: '2025-07-23', + }, +]; + +// Helper functions +export const getTemplateById = (id: string) => { + return templateRegistry.find(template => template.id === id); +}; + +export const getTemplatesByCategory = (category: TemplateCategory) => { + return templateRegistry.filter(template => template.category === category); +}; + +export const getTemplatesByTag = (tag: string) => { + return templateRegistry.filter(template => + template.tags?.includes(tag.toLowerCase()) + ); +}; + +export const getATSFriendlyTemplates = () => { + return templateRegistry.filter(template => template.atsFriendly); +}; + +// Get the lazy-loaded component for a template by ID +export const getTemplateComponent = (templateId: string) => { + // Normalize the template ID to match the registry format (kebab-case to PascalCase) + const normalizedId = templateId + .toLowerCase() + .split('-') + .map(word => word.charAt(0).toUpperCase() + word.slice(1)) + .join('') + 'Template'; + + // Try to find the exact match first + if (normalizedId in templates) { + return templates[normalizedId as keyof typeof templates]; + } + + // Fallback to case-insensitive search + const componentKey = Object.keys(templates).find( + key => key.toLowerCase() === normalizedId.toLowerCase() + ); + + if (componentKey && componentKey in templates) { + return templates[componentKey as keyof typeof templates]; + } + + // If still not found, log a warning and return MinimalTemplate as fallback + console.warn(`Template component for ID "${templateId}" not found, falling back to MinimalTemplate`); + return templates.MinimalTemplate || templates.CleanProfessionalTemplate; +}; + +export default templateRegistry; diff --git a/src/components/resume-builder/templates/AcademicTemplate.tsx b/src/components/resume-builder/templates/AcademicTemplate.tsx new file mode 100644 index 0000000..9a90a10 --- /dev/null +++ b/src/components/resume-builder/templates/AcademicTemplate.tsx @@ -0,0 +1,364 @@ +import { Mail, Phone, MapPin, Globe, Linkedin, Github, Book, GraduationCap, Award, Briefcase, FileText } from 'lucide-react'; +import { TemplateProps } from './types'; + +// Default values for customization +const defaultCustomization = { + fontFamily: 'Arial, sans-serif', + accentColor: '#2563eb', + showProfileImage: true +}; + +export function AcademicTemplate({ data }: TemplateProps) { + // Destructure with default values to prevent undefined errors + const { + personalInfo = { fullName: '', email: '', phone: '', location: '' }, + summary = '', + experience = [], + education = [], + skills = [], + certifications = [], + projects = [], + customization = defaultCustomization + } = data || {}; + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + const renderSection = (section: { type: string; id: string }) => { + switch (section.type) { + case 'personalInfo': + return ( +
+
+

+ {personalInfo.fullName || 'Your Name'} +

+

+ {personalInfo.title || 'Researcher / Academic'} +

+ +
+ {personalInfo.email && ( + + + {personalInfo.email} + + )} + {personalInfo.phone && ( +
+ + {personalInfo.phone} +
+ )} + {personalInfo.location && ( +
+ + {personalInfo.location} +
+ )} + {personalInfo.website && ( + + + Personal Website + + )} + {personalInfo.linkedin && ( + + + LinkedIn + + )} + {personalInfo.github && ( + + + GitHub + + )} +
+
+
+ ); + + case 'summary': + if (!summary) return null; + return ( +
+

+ + Research Profile +

+

+ {summary} +

+
+ ); + + case 'education': + if (education.length === 0) return null; + return ( +
+

+ + Education +

+
+ {education.map((edu) => ( +
+
+
+ +
+
+

+ {edu.degree} +

+

+ {edu.school} +

+
+
+ {edu.gpa && ( +

+ GPA: {edu.gpa} +

+ )} + {edu.relevantCourses && edu.relevantCourses.length > 0 && ( +
+

+ Relevant Coursework: +

+
+ {edu.relevantCourses.map((course, i) => ( + + {course} + + ))} +
+
+ )} +
+ ))} +
+
+ ); + + case 'experience': + if (experience.length === 0) return null; + return ( +
+

+ + {experience.some(exp => exp.company.toLowerCase().includes('research') || + exp.jobTitle.toLowerCase().includes('research')) + ? 'Research Experience' + : 'Professional Experience'} +

+
+ {experience.map((exp) => ( +
+
+
+

+ {exp.jobTitle} +

+

+ {exp.company} + {exp.location && ` • ${exp.location}`} +

+
+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+
    + {exp.description.filter(desc => desc.trim()).map((desc, index) => ( +
  • + + {desc} +
  • + ))} +
+
+ ))} +
+
+ ); + +case 'publications': { + if (projects.length === 0) return null; + + return ( +
+

+ + Publications +

+
+ {projects.map((pub) => ( +
+

+ {pub.name} +

+

+ {pub.description} +

+ {pub.url && ( + + View Publication + + )} +
+ ))} +
+
+ ); + } + + case 'projects': + // Filter out publications as they're handled separately + // Show all projects since we don't have a type filter anymore + if (projects.length === 0) return null; + + return ( +
+

+ + Research Projects +

+
+ {projects.map((project) => ( +
+
+

+ {project.name} + {project.url && ( + + (View Project) + + )} +

+ {project.startDate && ( + + {formatDate(project.startDate)} + {project.endDate ? ` - ${formatDate(project.endDate)}` : ' - Present'} + + )} +
+

+ {project.description} +

+ {project.technologies && project.technologies.length > 0 && ( +
+

+ Technologies: +

+

+ {project.technologies.join(', ')} +

+
+ )} +
+ ))} +
+
+ ); + + case 'certifications': + if (certifications.length === 0) return null; + return ( +
+

+ + Certifications & Training +

+
+ {certifications.map((cert) => ( +
+

+ {cert.name} +

+

+ {cert.issuer} +

+

+ {formatDate(cert.date)} + {cert.expirationDate && ` - ${formatDate(cert.expirationDate)}`} +

+
+ ))} +
+
+ ); + + case 'skills': + if (skills.length === 0) return null; + return ( +
+

+ + Research Skills & Expertise +

+
+ {skills.map((skill) => ( +
+
+ + {skill.name} + + + {skill.level} + +
+
+
+
+
+ ))} +
+
+ ); + + default: + return null; + } + }; + + return ( +
+ {data.sections.map((section) => renderSection(section))} +
+ ); +} diff --git a/src/components/resume-builder/templates/BoldTemplate.tsx b/src/components/resume-builder/templates/BoldTemplate.tsx new file mode 100644 index 0000000..b4feeaf --- /dev/null +++ b/src/components/resume-builder/templates/BoldTemplate.tsx @@ -0,0 +1,298 @@ +import { ResumeData } from '@/types/resume'; +import { Mail, Phone, MapPin, Globe, Linkedin, Github, Briefcase, GraduationCap, Code, Award, User } from 'lucide-react'; + +interface TemplateProps { + data: ResumeData; +} + +export function BoldTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications, projects, sections } = data; + const visibleSections = sections.filter(section => section.isVisible).sort((a, b) => a.order - b.order); + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + const formatUrl = (url: string) => { + if (!url) return ''; + return url.replace(/^https?:\/\/(www\.)?/, '').replace(/\/$/, ''); + }; + + const renderSection = (section: typeof sections[0]) => { + switch (section.type) { + case 'personalInfo': + return ( +
+
+
+

+ {personalInfo.fullName || 'Your Name'} +

+

+ {personalInfo.title || 'Professional Title'} +

+
+ +
+ {personalInfo.email && ( +
+ + {personalInfo.email} +
+ )} + {personalInfo.phone && ( +
+ + {personalInfo.phone} +
+ )} + {personalInfo.location && ( +
+ + {personalInfo.location} +
+ )} + {personalInfo.website && ( +
+ + {formatUrl(personalInfo.website)} +
+ )} + {personalInfo.linkedin && ( +
+ + linkedin.com/in/... +
+ )} + {personalInfo.github && ( +
+ + github.com/... +
+ )} +
+
+ + {summary && ( +
+

+ + PROFILE +

+

+ {summary} +

+
+ )} +
+ ); + + case 'experience': + if (experience.length === 0) return null; + return ( +
+

+ + EXPERIENCE +

+
+ {experience.map((exp) => ( +
+
+

+ {exp.jobTitle} +

+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+

+ {exp.company} + {exp.location && ` • ${exp.location}`} +

+
    + {exp.description.filter(desc => desc.trim()).map((desc, index) => ( +
  • + + {desc} +
  • + ))} +
+
+ ))} +
+
+ ); + + case 'education': + if (education.length === 0) return null; + return ( +
+

+ + EDUCATION +

+
+ {education.map((edu) => ( +
+
+

+ {edu.degree} +

+ + {formatDate(edu.graduationDate)} + +
+

+ {edu.school} + {edu.location && `, ${edu.location}`} +

+ {edu.gpa && ( +

+ GPA: {edu.gpa} +

+ )} + {edu.achievements && edu.achievements.length > 0 && ( +
    + {edu.achievements.map((achievement, idx) => ( +
  • + + {achievement} +
  • + ))} +
+ )} +
+ ))} +
+
+ ); + + case 'skills': + if (skills.length === 0) return null; + return ( +
+

+ + SKILLS & EXPERTISE +

+
+ {skills.map((skill) => ( +
+
+ + {skill.name} + + + {skill.level} + +
+
+
+
+
+ ))} +
+
+ ); + + case 'projects': + if (projects.length === 0) return null; + return ( +
+

+ + PROJECTS +

+
+ {projects.map((project) => ( +
+
+

+ {project.name} + {project.url && ( + + (View Project) + + )} +

+ {project.startDate && ( + + {formatDate(project.startDate)} + {project.endDate ? ` - ${formatDate(project.endDate)}` : ' - Present'} + + )} +
+

+ {project.description} +

+ {project.technologies && project.technologies.length > 0 && ( +
+ {project.technologies.map((tech, i) => ( + + {tech} + + ))} +
+ )} +
+ ))} +
+
+ ); + + case 'certifications': + if (certifications.length === 0) return null; + return ( +
+

+ + CERTIFICATIONS +

+
+ {certifications.map((cert) => ( +
+

+ {cert.name} +

+

+ {cert.issuer} +

+

+ {formatDate(cert.date)} + {cert.expirationDate && ` - ${formatDate(cert.expirationDate)}`} +

+
+ ))} +
+
+ ); + + default: + return null; + } + }; + + return ( +
+ {visibleSections.map(section => renderSection(section))} +
+ ); +} diff --git a/src/components/resume-builder/templates/CleanMinimalistTemplate.tsx b/src/components/resume-builder/templates/CleanMinimalistTemplate.tsx new file mode 100644 index 0000000..d15310a --- /dev/null +++ b/src/components/resume-builder/templates/CleanMinimalistTemplate.tsx @@ -0,0 +1,198 @@ +import { Mail, Phone, MapPin, Linkedin, Globe } from 'lucide-react'; +import { ResumeData } from '@/types/resume'; + +type TemplateProps = { + data: ResumeData; +}; + +export function CleanMinimalistTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications } = data; + const accentColor = data.customization.accentColor || '#2563eb'; + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + return ( +
+ {/* Header */} +
+

+ {personalInfo.fullName} +

+ {personalInfo.title && ( +

+ {personalInfo.title} +

+ )} +
+ {personalInfo.email && ( + + + {personalInfo.email} + + )} + {personalInfo.phone && ( + + + {personalInfo.phone} + + )} + {personalInfo.location && ( + + + {personalInfo.location} + + )} + {personalInfo.linkedin && ( + + + LinkedIn + + )} + {personalInfo.website && ( + + + {personalInfo.website.replace(/^https?:\/\//, '')} + + )} +
+
+ + {/* Summary */} + {summary && ( +
+

+ SUMMARY +

+

+ {summary} +

+
+ )} + +
+ {/* Left Column */} +
+ {/* Experience */} + {experience.length > 0 && ( +
+

+ PROFESSIONAL EXPERIENCE +

+
+ {experience.map((exp) => ( +
+
+

{exp.jobTitle}

+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+

+ {exp.company} + {exp.location && `, ${exp.location}`} +

+
    + {exp.description.map((desc, idx) => ( +
  • + + {desc} +
  • + ))} +
+
+ ))} +
+
+ )} + + {/* Education */} + {education.length > 0 && ( +
+

+ EDUCATION +

+
+ {education.map((edu) => ( +
+

{edu.degree}

+

+ {edu.school} + {edu.location && `, ${edu.location}`} +

+
+ {edu.fieldOfStudy} + {formatDate(edu.graduationDate)} +
+ {edu.gpa && ( +

+ GPA: {edu.gpa} +

+ )} +
+ ))} +
+
+ )} +
+ + {/* Right Column */} +
+ {/* Skills */} + {skills.length > 0 && ( +
+

+ SKILLS +

+
+ {skills.map((skill) => ( +
+
+ {skill.name} + {skill.level} +
+
+
+
+
+ ))} +
+
+ )} + + {/* Certifications */} + {certifications.length > 0 && ( +
+

+ CERTIFICATIONS +

+
+ {certifications.map((cert) => ( +
+

{cert.name}

+

{cert.issuer}

+

+ {formatDate(cert.date)} + {cert.expirationDate && ` • Expires: ${formatDate(cert.expirationDate)}`} +

+
+ ))} +
+
+ )} +
+
+
+ ); +} diff --git a/src/components/resume-builder/templates/CleanProfessionalTemplate.tsx b/src/components/resume-builder/templates/CleanProfessionalTemplate.tsx new file mode 100644 index 0000000..0fce631 --- /dev/null +++ b/src/components/resume-builder/templates/CleanProfessionalTemplate.tsx @@ -0,0 +1,200 @@ +import { Mail, Phone, MapPin, Globe, Linkedin, Github, Briefcase, GraduationCap, Code, Award } from 'lucide-react'; +import { TemplateProps } from './types'; + +export function CleanProfessionalTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications, projects, sections, customization } = data; + const visibleSections = sections.filter(section => section.isVisible).sort((a, b) => a.order - b.order); + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + const renderSection = (section: typeof sections[0]) => { + switch (section.type) { + case 'personalInfo': + return ( +
+
+

+ {personalInfo.fullName} +

+ {personalInfo.title && ( +

+ {personalInfo.title} +

+ )} +
+ {personalInfo.email && ( + + + {personalInfo.email} + + )} + {personalInfo.phone && ( + + + {personalInfo.phone} + + )} + {personalInfo.location && ( + + + {personalInfo.location} + + )} + {personalInfo.website && ( + + + {personalInfo.website.replace(/^https?:\/\//, '')} + + )} +
+
+
+ ); + + case 'summary': + if (!summary) return null; + return ( +
+

+ Professional Summary +

+

{summary}

+
+ ); + + case 'experience': + if (experience.length === 0) return null; + return ( +
+

+ + Professional Experience +

+
+ {experience.map((exp) => ( +
+
+
+

+ {exp.jobTitle} +

+

+ {exp.company} • {exp.location} +

+
+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+
    + {exp.description.map((desc, idx) => ( +
  • + + {desc} +
  • + ))} +
+
+ ))} +
+
+ ); + + case 'education': + if (education.length === 0) return null; + return ( +
+

+ + Education +

+
+ {education.map((edu) => ( +
+

+ {edu.degree} +

+

+ {edu.school} • {edu.location} +

+

+ {formatDate(edu.graduationDate)} + {edu.gpa && ` • GPA: ${edu.gpa}`} +

+
+ ))} +
+
+ ); + + case 'skills': + if (skills.length === 0) return null; + return ( +
+

+ + Skills +

+
+ {skills.map((skill) => ( +
+
+ {skill.name} + {skill.level} +
+
+
+
+
+ ))} +
+
+ ); + + case 'certifications': + if (certifications.length === 0) return null; + return ( +
+

+ + Certifications +

+
+ {certifications.map((cert) => ( +
+

+ {cert.name} +

+

+ {cert.issuer} • {formatDate(cert.date)} + {cert.expirationDate && ` • Expires: ${formatDate(cert.expirationDate)}`} +

+
+ ))} +
+
+ ); + + default: + return null; + } + }; + + return ( +
+ {visibleSections.map((section) => renderSection(section))} +
+ ); +} diff --git a/src/components/resume-builder/templates/CorporateTemplate.tsx b/src/components/resume-builder/templates/CorporateTemplate.tsx new file mode 100644 index 0000000..c54df7f --- /dev/null +++ b/src/components/resume-builder/templates/CorporateTemplate.tsx @@ -0,0 +1,319 @@ +import { ResumeData } from '@/types/resume'; +import { Mail, Phone, MapPin, Globe, Linkedin, Github, Briefcase, GraduationCap, Code, Award, BookOpen, User } from 'lucide-react'; + +interface TemplateProps { + data: ResumeData; +} + +export function CorporateTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications, projects, sections, customization } = data; + + const visibleSections = sections.filter(section => section.isVisible).sort((a, b) => a.order - b.order); + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + const formatUrl = (url: string) => { + if (!url) return ''; + return url.replace(/^https?:\/\/(www\.)?/, '').replace(/\/$/, ''); + }; + + const renderSection = (section: typeof sections[0]) => { + switch (section.type) { + case 'personalInfo': + return ( +
+
+

+ {personalInfo.fullName || 'Your Name'} +

+

+ {personalInfo.title || 'Professional Title'} +

+ +
+ {personalInfo.email && ( + + + {personalInfo.email} + + )} + {personalInfo.phone && ( +
+ + {personalInfo.phone} +
+ )} + {personalInfo.location && ( +
+ + {personalInfo.location} +
+ )} + {personalInfo.website && ( + + + {formatUrl(personalInfo.website)} + + )} + {personalInfo.linkedin && ( + + + {formatUrl(personalInfo.linkedin).split('/').pop()} + + )} + {personalInfo.github && ( + + + {formatUrl(personalInfo.github).split('/').pop()} + + )} +
+
+
+ ); + + case 'summary': + if (!summary) return null; + return ( +
+

+ + Professional Summary +

+

+ {summary} +

+
+ ); + + case 'experience': + if (experience.length === 0) return null; + return ( +
+

+ + Professional Experience +

+
+ {experience.map((exp) => ( +
+
+
+

+ {exp.jobTitle} +

+

+ {exp.company} + {exp.location && ` • ${exp.location}`} +

+
+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+
    + {exp.description.filter(desc => desc.trim()).map((desc, index) => ( +
  • + + {desc} +
  • + ))} +
+
+ ))} +
+
+ ); + + case 'education': + if (education.length === 0) return null; + return ( +
+

+ + Education +

+
+ {education.map((edu) => ( +
+
+
+

+ {edu.degree} +

+

+ {edu.school} + {edu.location && `, ${edu.location}`} +

+ {edu.gpa && ( +

+ GPA: {edu.gpa} +

+ )} +
+ + {formatDate(edu.graduationDate)} + +
+ {edu.relevantCourses && edu.relevantCourses.length > 0 && ( +
+

+ Relevant Coursework: +

+
+ {edu.relevantCourses.map((course, i) => ( + + {course} + + ))} +
+
+ )} +
+ ))} +
+
+ ); + + case 'skills': + if (skills.length === 0) return null; + return ( +
+

+ + Skills & Expertise +

+
+ {skills.map((skill) => ( +
+
+ + {skill.name} + + + {skill.level} + +
+
+
+
+
+ ))} +
+
+ ); + + case 'certifications': + if (certifications.length === 0) return null; + return ( +
+

+ + Certifications +

+
+ {certifications.map((cert) => ( +
+

+ {cert.name} +

+

+ {cert.issuer} +

+

+ {formatDate(cert.date)} + {cert.expirationDate && ` - ${formatDate(cert.expirationDate)}`} +

+
+ ))} +
+
+ ); + + case 'projects': + if (projects.length === 0) return null; + return ( +
+

+ + Projects +

+
+ {projects.map((project) => ( +
+
+

+ {project.name} + {project.url && ( + + (View Project) + + )} +

+ {project.startDate && ( + + {formatDate(project.startDate)} + {project.endDate ? ` - ${formatDate(project.endDate)}` : ' - Present'} + + )} +
+

+ {project.description} +

+ {project.technologies && project.technologies.length > 0 && ( +
+ {project.technologies.map((tech, i) => ( + + {tech} + + ))} +
+ )} +
+ ))} +
+
+ ); + + default: + return null; + } + }; + + return ( +
+ {visibleSections.map(section => renderSection(section))} +
+ ); +} diff --git a/src/components/resume-builder/templates/CreativeColorBlocksTemplate.tsx b/src/components/resume-builder/templates/CreativeColorBlocksTemplate.tsx new file mode 100644 index 0000000..c7ec765 --- /dev/null +++ b/src/components/resume-builder/templates/CreativeColorBlocksTemplate.tsx @@ -0,0 +1,220 @@ +import { ResumeData } from '@/types/resume'; +import { Briefcase, GraduationCap, User, Code, Mail, Phone, MapPin, Globe } from 'lucide-react'; + +interface TemplateProps { + data: ResumeData; +} + +export function CreativeColorBlocksTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, projects } = data; + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + // Color variants for different sections + const colorVariants = { + primary: 'bg-blue-600 text-white', + secondary: 'bg-blue-100 text-blue-900', + accent: 'bg-yellow-400 text-gray-900', + light: 'bg-gray-50 text-gray-800', + dark: 'bg-gray-800 text-white', + }; + + return ( +
+ {/* Header with color block */} +
+
+
+ {personalInfo.profileImage && ( +
+ {personalInfo.fullName +
+ )} +
+

{personalInfo.fullName || 'Your Name'}

+

{personalInfo.title || 'Professional Title'}

+
+
+
+
+ +
+
+ {/* Left Column */} +
+ {/* Contact */} +
+

CONTACT

+
+ {personalInfo.email && ( +
+ + {personalInfo.email} +
+ )} + {personalInfo.phone && ( +
+ + {personalInfo.phone} +
+ )} + {personalInfo.location && ( +
+ + {personalInfo.location} +
+ )} + {personalInfo.website && ( + + )} +
+
+ + {/* Skills */} + {skills.length > 0 && ( +
+

+ + SKILLS +

+
+ {skills.map((skill) => ( +
+
+ {skill.name} + {skill.level} +
+
+
+
+
+ ))} +
+
+ )} + + {/* Education */} + {education.length > 0 && ( +
+

+ + EDUCATION +

+
+ {education.map((edu) => ( +
+

{edu.degree}

+

+ {edu.school} + {edu.location && `, ${edu.location}`} +

+ {edu.graduationDate && ( +

+ {formatDate(edu.graduationDate)} + {edu.gpa && ` • GPA: ${edu.gpa}`} +

+ )} +
+ ))} +
+
+ )} +
+ + {/* Right Column */} +
+ {/* Summary */} + {summary && ( +
+

+ + ABOUT ME +

+

{summary}

+
+ )} + + {/* Experience */} + {experience.length > 0 && ( +
+

+ + EXPERIENCE +

+
+ {experience.map((exp) => ( +
+
+

{exp.jobTitle}

+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+

+ {exp.company} + {exp.location && ` • ${exp.location}`} +

+
    + {exp.description.filter(desc => desc.trim()).map((desc, index) => ( +
  • + + {desc} +
  • + ))} +
+
+ ))} +
+
+ )} + + {/* Projects */} + {projects && projects.length > 0 && ( +
+

PROJECTS

+
+ {projects.map((project) => ( +
+

{project.name}

+ {project.url && ( + + {project.url.replace(/^https?:\/\//, '')} + + )} +

{project.description}

+
+ ))} +
+
+ )} +
+
+
+
+ ); +} diff --git a/src/components/resume-builder/templates/ElegantTemplate.tsx b/src/components/resume-builder/templates/ElegantTemplate.tsx new file mode 100644 index 0000000..c2dcbc4 --- /dev/null +++ b/src/components/resume-builder/templates/ElegantTemplate.tsx @@ -0,0 +1,281 @@ +import { ResumeData } from '@/types/resume'; +import { Mail, Phone, MapPin, Globe, Linkedin, Github, Briefcase, GraduationCap, Code, Award, User } from 'lucide-react'; + +interface TemplateProps { + data: ResumeData; +} + +export function ElegantTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications, projects, sections } = data; + const visibleSections = sections.filter(section => section.isVisible).sort((a, b) => a.order - b.order); + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + const formatUrl = (url: string) => { + if (!url) return ''; + return url.replace(/^https?:\/\/(www\.)?/, '').replace(/\/$/, ''); + }; + + const renderSection = (section: typeof sections[0]) => { + switch (section.type) { + case 'personalInfo': + return ( +
+
+
+
+

+ {personalInfo.fullName || 'Your Name'} +

+
+

+ {personalInfo.title || 'Professional Title'} +

+
+ +
+ {personalInfo.email && ( +
+ + {personalInfo.email} +
+ )} + {personalInfo.phone && ( +
+ + {personalInfo.phone} +
+ )} + {personalInfo.location && ( +
+ + {personalInfo.location} +
+ )} + {personalInfo.website && ( +
+ + {formatUrl(personalInfo.website)} +
+ )} + {personalInfo.linkedin && ( +
+ + linkedin.com/in/... +
+ )} +
+
+ + {summary && ( +
+

+ + Profile +

+

+ {summary} +

+
+ )} +
+
+ ); + + case 'experience': + if (experience.length === 0) return null; + return ( +
+

+ + Professional Experience +

+
+ {experience.map((exp) => ( +
+
+

+ {exp.jobTitle} +

+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+

+ {exp.company} + {exp.location && ` • ${exp.location}`} +

+
    + {exp.description.filter(desc => desc.trim()).map((desc, index) => ( +
  • + + {desc} +
  • + ))} +
+
+ ))} +
+
+ ); + + case 'education': + if (education.length === 0) return null; + return ( +
+

+ + Education +

+
+ {education.map((edu) => ( +
+
+

+ {edu.degree} +

+ + {formatDate(edu.graduationDate)} + +
+

+ {edu.school} + {edu.location && `, ${edu.location}`} + {edu.gpa && ` • GPA: ${edu.gpa}`} +

+
+ ))} +
+
+ ); + + case 'skills': + if (skills.length === 0) return null; + return ( +
+

+ + Skills & Expertise +

+
+ {skills.map((skill) => ( +
+
+ + {skill.name} + + + {skill.level} + +
+
+
+
+
+ ))} +
+
+ ); + + case 'projects': + if (projects.length === 0) return null; + return ( +
+

+ + Projects +

+
+ {projects.map((project) => ( +
+
+

+ {project.name} + {project.url && ( + + (View Project) + + )} +

+ {project.startDate && ( + + {formatDate(project.startDate)} + {project.endDate ? ` - ${formatDate(project.endDate)}` : ' - Present'} + + )} +
+

+ {project.description} +

+ {project.technologies && project.technologies.length > 0 && ( +
+ {project.technologies.map((tech, i) => ( + + {tech} + + ))} +
+ )} +
+ ))} +
+
+ ); + + case 'certifications': + if (certifications.length === 0) return null; + return ( +
+

+ + Certifications +

+
+ {certifications.map((cert) => ( +
+

+ {cert.name} +

+

+ {cert.issuer} +

+

+ {formatDate(cert.date)} + {cert.expirationDate && ` - ${formatDate(cert.expirationDate)}`} +

+
+ ))} +
+
+ ); + + default: + return null; + } + }; + + return ( +
+ {visibleSections.map(section => renderSection(section))} +
+ ); +} diff --git a/src/components/resume-builder/templates/ExecutiveTemplate.tsx b/src/components/resume-builder/templates/ExecutiveTemplate.tsx new file mode 100644 index 0000000..5309599 --- /dev/null +++ b/src/components/resume-builder/templates/ExecutiveTemplate.tsx @@ -0,0 +1,284 @@ +import React from 'react'; +import { ResumeData } from '@/types/resume'; +import { Mail, Phone, MapPin, Globe, Linkedin, Github, Award, Briefcase, GraduationCap, Code, BookOpen } from 'lucide-react'; + +interface TemplateProps { + data: ResumeData; +} + +export function ExecutiveTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications, projects, references, sections, customization } = data; + + const visibleSections = sections.filter(section => section.isVisible).sort((a, b) => a.order - b.order); + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + const formatUrl = (url: string) => { + if (!url) return ''; + return url.replace(/^https?:\/\/(www\.)?/, '').replace(/\/$/, ''); + }; + + const renderSection = (section: typeof sections[0]) => { + switch (section.type) { + case 'personalInfo': + return ( +
+
+

+ {personalInfo.fullName || 'Your Name'} +

+ {personalInfo.jobTitle && ( +

+ {personalInfo.jobTitle} +

+ )} +
+ +
+ {personalInfo.email && ( +
+ + {personalInfo.email} +
+ )} + {personalInfo.phone && ( +
+ + {personalInfo.phone} +
+ )} + {personalInfo.location && ( +
+ + {personalInfo.location} +
+ )} + {personalInfo.website && ( +
+ + {formatUrl(personalInfo.website)} +
+ )} + {personalInfo.linkedin && ( +
+ + {formatUrl(personalInfo.linkedin)} +
+ )} + {personalInfo.github && ( +
+ + {formatUrl(personalInfo.github)} +
+ )} +
+
+ ); + + case 'summary': + if (!summary) return null; + return ( +
+
+
+

+ Executive Profile +

+
+

+ {summary} +

+
+ ); + + case 'experience': + if (experience.length === 0) return null; + return ( +
+
+ +

+ Professional Experience +

+
+
+ {experience.map((exp) => ( +
+
+
+

+ {exp.jobTitle} +

+

+ {exp.company}{exp.location && ` • ${exp.location}`} +

+
+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+
    + {exp.description.filter(desc => desc.trim()).map((desc, index) => ( +
  • + + {desc} +
  • + ))} +
+
+ ))} +
+
+ ); + + case 'education': + if (education.length === 0) return null; + return ( +
+
+ +

+ Education +

+
+
+ {education.map((edu) => ( +
+
+
+

+ {edu.degree} +

+

+ {edu.school}{edu.location && `, ${edu.location}`} +

+
+ + {formatDate(edu.graduationDate)} + +
+ {edu.gpa && ( +

+ GPA: {edu.gpa} +

+ )} +
+ ))} +
+
+ ); + + case 'skills': + if (skills.length === 0) return null; + return ( +
+
+ +

+ Core Competencies +

+
+
+ {skills.map((skill) => ( +
+ + + {skill.name}{skill.level ? ` (${skill.level})` : ''} + +
+ ))} +
+
+ ); + + case 'certifications': + if (certifications.length === 0) return null; + return ( +
+
+ +

+ Certifications +

+
+
+ {certifications.map((cert) => ( +
+
+

+ {cert.name} +

+ + {cert.date && formatDate(cert.date)} + +
+ {cert.issuer && ( +

+ {cert.issuer} +

+ )} +
+ ))} +
+
+ ); + + case 'projects': + if (projects.length === 0) return null; + return ( +
+
+ +

+ Key Projects +

+
+
+ {projects.map((project) => ( +
+
+

+ {project.name} +

+ {project.url && ( + + View Project + + )} +
+ {project.technologies && project.technologies.length > 0 && ( +

+ Technologies: {project.technologies.join(', ')} +

+ )} +

+ {project.description} +

+
+ ))} +
+
+ ); + + default: + return null; + } + }; + + return ( +
+ {visibleSections.map(section => renderSection(section))} +
+ ); +} diff --git a/src/components/resume-builder/templates/MinimalistATS.tsx b/src/components/resume-builder/templates/MinimalistATS.tsx new file mode 100644 index 0000000..8247c71 --- /dev/null +++ b/src/components/resume-builder/templates/MinimalistATS.tsx @@ -0,0 +1,126 @@ +import { Mail, Phone, MapPin, Globe, Linkedin, Github, Briefcase, GraduationCap, Code, Award } from 'lucide-react'; +import { ResumeData } from '@/types/resume'; + +type TemplateProps = { + data: ResumeData; +}; + +export function MinimalistATS({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications } = data; + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short' }); + }; + + return ( +
+ {/* Header */} +
+

{personalInfo.fullName}

+ {personalInfo.title &&

{personalInfo.title}

} +
+ {personalInfo.email && {personalInfo.email}} + {personalInfo.phone && {personalInfo.phone}} + {personalInfo.location && {personalInfo.location}} + {personalInfo.linkedin && linkedin.com/in/username} +
+
+ + {/* Summary */} + {summary && ( +
+

SUMMARY

+

{summary}

+
+ )} + + {/* Experience */} + {experience.length > 0 && ( +
+

EXPERIENCE

+
+ {experience.map((exp) => ( +
+
+

{exp.jobTitle}

+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+

+ {exp.company} + {exp.location && ` • ${exp.location}`} +

+
    + {exp.description.map((desc, idx) => ( +
  • + + {desc} +
  • + ))} +
+
+ ))} +
+
+ )} + + {/* Education */} + {education.length > 0 && ( +
+

EDUCATION

+
+ {education.map((edu) => ( +
+
+

{edu.degree}

+ + {formatDate(edu.graduationDate)} + +
+

+ {edu.school} + {edu.location && `, ${edu.location}`} + {edu.gpa && ` • GPA: ${edu.gpa}`} +

+
+ ))} +
+
+ )} + + {/* Skills */} + {skills.length > 0 && ( +
+

SKILLS

+
+ {skills.map((skill) => ( + + {skill.name} + + ))} +
+
+ )} + + {/* Certifications */} + {certifications.length > 0 && ( +
+

CERTIFICATIONS

+
+ {certifications.map((cert) => ( +
+

{cert.name}

+

+ {cert.issuer} • {formatDate(cert.date)} +

+
+ ))} +
+
+ )} +
+ ); +} diff --git a/src/components/resume-builder/templates/MinimalistProfessionalTemplate.tsx b/src/components/resume-builder/templates/MinimalistProfessionalTemplate.tsx new file mode 100644 index 0000000..72fa78e --- /dev/null +++ b/src/components/resume-builder/templates/MinimalistProfessionalTemplate.tsx @@ -0,0 +1,307 @@ +import { ResumeData } from '@/types/resume'; +import { Mail, Phone, MapPin, Globe, Linkedin, Github, Briefcase, GraduationCap, Code, Award, User } from 'lucide-react'; + +interface TemplateProps { + data: ResumeData; +} + +export function MinimalistProfessionalTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications, projects, sections, customization } = data; + + const visibleSections = sections.filter(section => section.isVisible).sort((a, b) => a.order - b.order); + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + const formatUrl = (url: string) => { + if (!url) return ''; + return url.replace(/^https?:\/\/(www\.)?/, '').replace(/\/$/, ''); + }; + + const renderSection = (section: typeof sections[0]) => { + switch (section.type) { + case 'personalInfo': + return ( +
+
+

+ {personalInfo.fullName || 'Your Name'} +

+
+

+ {personalInfo.title || 'Professional Title'} +

+ +
+ {personalInfo.email && ( + + {personalInfo.email} + + )} + {personalInfo.phone && ( + {personalInfo.phone} + )} + {personalInfo.location && ( + {personalInfo.location} + )} + {personalInfo.website && ( + + {formatUrl(personalInfo.website)} + + )} + {personalInfo.linkedin && ( + + LinkedIn + + )} +
+
+
+ ); + + case 'summary': + if (!summary) return null; + return ( +
+
+

+ Profile +

+

+ {summary} +

+
+
+ ); + + case 'experience': + if (experience.length === 0) return null; + return ( +
+
+

+ Experience +

+
+ {experience.map((exp) => ( +
+
+

+ {exp.jobTitle} +

+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+

+ {exp.company} + {exp.location && ` • ${exp.location}`} +

+
    + {exp.description.filter(desc => desc.trim()).map((desc, index) => ( +
  • + + {desc} +
  • + ))} +
+
+ ))} +
+
+
+ ); + + case 'education': + if (education.length === 0) return null; + return ( +
+
+

+ Education +

+
+ {education.map((edu) => ( +
+
+

+ {edu.degree} +

+ + {formatDate(edu.graduationDate)} + +
+

+ {edu.school} + {edu.location && `, ${edu.location}`} + {edu.gpa && ` • GPA: ${edu.gpa}`} +

+ {edu.relevantCourses && edu.relevantCourses.length > 0 && ( +
+

+ Relevant Coursework: +

+
+ {edu.relevantCourses.map((course, i) => ( + + {course} + + ))} +
+
+ )} +
+ ))} +
+
+
+ ); + + case 'skills': + if (skills.length === 0) return null; + return ( +
+
+

+ Skills +

+
+ {skills.map((skill) => ( + + {skill.name} + + ))} +
+
+
+ ); + + case 'projects': + if (projects.length === 0) return null; + return ( +
+
+

+ Projects +

+
+ {projects.map((project) => ( +
+
+

+ {project.name} + {project.url && ( + + (View Project) + + )} +

+ {project.startDate && ( + + {formatDate(project.startDate)} + {project.endDate ? ` - ${formatDate(project.endDate)}` : ' - Present'} + + )} +
+

+ {project.description} +

+ {project.technologies && project.technologies.length > 0 && ( +
+ {project.technologies.map((tech, i) => ( + + {tech} + + ))} +
+ )} +
+ ))} +
+
+
+ ); + + case 'certifications': + if (certifications.length === 0) return null; + return ( +
+
+

+ Certifications +

+
+ {certifications.map((cert) => ( +
+

+ {cert.name} +

+

+ {cert.issuer} + {cert.date && ` • ${formatDate(cert.date)}`} + {cert.expirationDate && ` - ${formatDate(cert.expirationDate)}`} +

+
+ ))} +
+
+
+ ); + + default: + return null; + } + }; + + return ( +
+ {visibleSections.map(section => renderSection(section))} +
+ ); +} diff --git a/src/components/resume-builder/templates/MinimalistSidebarTemplate.tsx b/src/components/resume-builder/templates/MinimalistSidebarTemplate.tsx new file mode 100644 index 0000000..29b7ff7 --- /dev/null +++ b/src/components/resume-builder/templates/MinimalistSidebarTemplate.tsx @@ -0,0 +1,166 @@ +import { ResumeData } from '@/types/resume'; +import { Mail, Phone, MapPin, Globe, Briefcase, GraduationCap, User } from 'lucide-react'; + +interface TemplateProps { + data: ResumeData; +} + +export function MinimalistSidebarTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills } = data; + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + const renderSidebar = () => ( +
+ {personalInfo.profileImage && ( +
+ {personalInfo.fullName +
+ )} + +
+
+

CONTACT

+
+ {personalInfo.email && ( +
+ + {personalInfo.email} +
+ )} + {personalInfo.phone && ( +
+ + {personalInfo.phone} +
+ )} + {personalInfo.location && ( +
+ + {personalInfo.location} +
+ )} + {personalInfo.website && ( +
+ + {personalInfo.website.replace(/^https?:\/\//, '')} +
+ )} +
+
+ + {skills.length > 0 && ( +
+

SKILLS

+
+ {skills.map((skill) => ( +
+
+ {skill.name} + + {skill.level === 'Expert' ? '★★★★★' : + skill.level === 'Advanced' ? '★★★★☆' : + skill.level === 'Intermediate' ? '★★★☆☆' : '★★☆☆☆'} + +
+
+ ))} +
+
+ )} +
+
+ ); + + const renderMainContent = () => ( +
+

+ {personalInfo.fullName || 'Your Name'} +

+

+ {personalInfo.title || 'Professional Title'} +

+ + {summary && ( +
+

+ + PROFILE +

+

{summary}

+
+ )} + + {experience.length > 0 && ( +
+

+ + EXPERIENCE +

+
+ {experience.map((exp) => ( +
+
+

{exp.jobTitle}

+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+

+ {exp.company} + {exp.location && ` • ${exp.location}`} +

+
    + {exp.description.filter(desc => desc.trim()).map((desc, index) => ( +
  • {desc}
  • + ))} +
+
+ ))} +
+
+ )} + + {education.length > 0 && ( +
+

+ + EDUCATION +

+
+ {education.map((edu) => ( +
+
+

{edu.degree}

+ + {formatDate(edu.graduationDate)} + +
+

+ {edu.school} + {edu.location && `, ${edu.location}`} + {edu.gpa && ` • GPA: ${edu.gpa}`} +

+
+ ))} +
+
+ )} +
+ ); + + return ( +
+ {renderSidebar()} + {renderMainContent()} +
+ ); +} diff --git a/src/components/resume-builder/templates/MinimalistTemplate.tsx b/src/components/resume-builder/templates/MinimalistTemplate.tsx new file mode 100644 index 0000000..5efe0cd --- /dev/null +++ b/src/components/resume-builder/templates/MinimalistTemplate.tsx @@ -0,0 +1,261 @@ +import { ResumeData } from '@/types/resume'; +import { Mail, Phone, MapPin, Globe, Linkedin, Github } from 'lucide-react'; + +interface TemplateProps { + data: ResumeData; +} + +export function MinimalistTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications, projects, sections, customization } = data; + + const visibleSections = sections.filter(section => section.isVisible).sort((a, b) => a.order - b.order); + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + const formatUrl = (url: string) => { + if (!url) return ''; + return url.replace(/^https?:\/\/(www\.)?/, '').replace(/\/$/, ''); + }; + + const renderSection = (section: typeof sections[0]) => { + switch (section.type) { + case 'personalInfo': + return ( +
+

+ {personalInfo.fullName || 'Your Name'} +

+ {personalInfo.title && ( +

+ {personalInfo.title} +

+ )} + +
+ {personalInfo.email && ( +
+ + {personalInfo.email} +
+ )} + {personalInfo.phone && ( +
+ + {personalInfo.phone} +
+ )} + {personalInfo.location && ( +
+ + {personalInfo.location} +
+ )} + {personalInfo.website && ( +
+ + {formatUrl(personalInfo.website)} +
+ )} + {personalInfo.linkedin && ( +
+ + {formatUrl(personalInfo.linkedin)} +
+ )} + {personalInfo.github && ( +
+ + {formatUrl(personalInfo.github)} +
+ )} +
+
+
+ ); + + case 'summary': + if (!summary) return null; + return ( +
+

+ About +

+

+ {summary} +

+
+ ); + + case 'experience': + if (experience.length === 0) return null; + return ( +
+

+ Experience +

+
+ {experience.map((exp) => ( +
+
+

+ {exp.jobTitle} +

+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+

+ {exp.company}{exp.location && ` • ${exp.location}`} +

+
    + {exp.description.filter(desc => desc.trim()).map((desc, index) => ( +
  • + + {desc} +
  • + ))} +
+
+ ))} +
+
+ ); + + case 'education': + if (education.length === 0) return null; + return ( +
+

+ Education +

+
+ {education.map((edu) => ( +
+
+

+ {edu.degree} +

+ + {formatDate(edu.graduationDate)} + +
+

+ {edu.school}{edu.location && ` • ${edu.location}`} +

+ {edu.gpa && ( +

+ GPA: {edu.gpa} +

+ )} +
+ ))} +
+
+ ); + + case 'skills': + if (skills.length === 0) return null; + return ( +
+

+ Skills +

+
+ {skills.map((skill) => ( + + {skill.name} + + ))} +
+
+ ); + + case 'certifications': + if (certifications.length === 0) return null; + return ( +
+

+ Certifications +

+
+ {certifications.map((cert) => ( +
+

+ {cert.name} +

+

+ {cert.issuer} + {cert.date && ` • ${formatDate(cert.date)}`} +

+
+ ))} +
+
+ ); + + case 'projects': + if (projects.length === 0) return null; + return ( +
+

+ Projects +

+
+ {projects.map((project) => ( +
+
+

+ {project.name} +

+ {project.url && ( + + View Project + + )} +
+ {project.technologies && project.technologies.length > 0 && ( +

+ {project.technologies.join(' • ')} +

+ )} +

+ {project.description} +

+
+ ))} +
+
+ ); + + default: + return null; + } + }; + + return ( +
+ {visibleSections.map(section => renderSection(section))} +
+ ); +} diff --git a/src/components/resume-builder/templates/ModernDarkSidebarTemplate.tsx b/src/components/resume-builder/templates/ModernDarkSidebarTemplate.tsx new file mode 100644 index 0000000..267bd3f --- /dev/null +++ b/src/components/resume-builder/templates/ModernDarkSidebarTemplate.tsx @@ -0,0 +1,192 @@ +import { ResumeData } from '@/types/resume'; +import { Mail, Phone, MapPin, Globe, Briefcase, GraduationCap, User } from 'lucide-react'; + +interface TemplateProps { + data: ResumeData; +} + +export function ModernDarkSidebarTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications } = data; + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + const renderSidebar = () => ( +
+ {personalInfo.profileImage && ( +
+ {personalInfo.fullName +
+ )} + +
+
+

CONTACT

+
+ {personalInfo.email && ( +
+ + {personalInfo.email} +
+ )} + {personalInfo.phone && ( +
+ + {personalInfo.phone} +
+ )} + {personalInfo.location && ( +
+ + {personalInfo.location} +
+ )} + {personalInfo.website && ( + + )} +
+
+ + {skills.length > 0 && ( +
+

SKILLS

+
+ {skills.map((skill) => ( +
+
+ {skill.name} + + {skill.level === 'Expert' ? '★★★★★' : + skill.level === 'Advanced' ? '★★★★☆' : + skill.level === 'Intermediate' ? '★★★☆☆' : '★★☆☆☆'} + +
+
+ ))} +
+
+ )} + + {certifications && certifications.length > 0 && ( +
+

CERTIFICATIONS

+
+ {certifications.map((cert) => ( +
+
{cert.name}
+
{cert.issuer}
+ {cert.date && ( +
+ {new Date(cert.date).toLocaleDateString('en-US', { year: 'numeric', month: 'short' })} +
+ )} +
+ ))} +
+
+ )} +
+
+ ); + + const renderMainContent = () => ( +
+
+

+ {personalInfo.fullName || 'Your Name'} +

+

+ {personalInfo.title || 'Professional Title'} +

+
+ + {summary && ( +
+

+ + ABOUT ME +

+

{summary}

+
+ )} + + {experience.length > 0 && ( +
+

+ + WORK EXPERIENCE +

+
+ {experience.map((exp) => ( +
+
+

{exp.jobTitle}

+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+

+ {exp.company} + {exp.location && ` • ${exp.location}`} +

+
    + {exp.description.filter(desc => desc.trim()).map((desc, index) => ( +
  • + + {desc} +
  • + ))} +
+
+ ))} +
+
+ )} + + {education.length > 0 && ( +
+

+ + EDUCATION +

+
+ {education.map((edu) => ( +
+
+

{edu.degree}

+ + {formatDate(edu.graduationDate)} + +
+

+ {edu.school} + {edu.location && `, ${edu.location}`} + {edu.gpa && ` • GPA: ${edu.gpa}`} +

+
+ ))} +
+
+ )} +
+ ); + + return ( +
+ {renderSidebar()} + {renderMainContent()} +
+ ); +} diff --git a/src/components/resume-builder/templates/ModernExecutiveTemplate.tsx b/src/components/resume-builder/templates/ModernExecutiveTemplate.tsx new file mode 100644 index 0000000..ade357e --- /dev/null +++ b/src/components/resume-builder/templates/ModernExecutiveTemplate.tsx @@ -0,0 +1,220 @@ +import { Mail, Phone, MapPin, Linkedin, Globe, Briefcase, GraduationCap, Award, Code } from 'lucide-react'; +import { ResumeData } from '@/types/resume'; + +type TemplateProps = { + data: ResumeData; +}; + +export function ModernExecutiveTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications } = data; + const accentColor = data.customization.accentColor || '#2563eb'; + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + return ( +
+ {/* Sidebar */} +
+ {/* Profile Image */} + {personalInfo.profileImage && ( +
+ {personalInfo.fullName} +
+ )} + + {/* Contact */} +
+

+ Contact +

+
    + {personalInfo.email && ( +
  • + + {personalInfo.email} +
  • + )} + {personalInfo.phone && ( +
  • + + {personalInfo.phone} +
  • + )} + {personalInfo.location && ( +
  • + + {personalInfo.location} +
  • + )} + {personalInfo.linkedin && ( +
  • + + LinkedIn +
  • + )} + {personalInfo.website && ( +
  • + + {personalInfo.website.replace(/^https?:\/\//, '')} +
  • + )} +
+
+ + {/* Skills */} + {skills.length > 0 && ( +
+

+ Skills +

+
+ {skills.map((skill) => ( +
+
+ {skill.name} + {skill.level} +
+
+
+
+
+ ))} +
+
+ )} + + {/* Certifications */} + {certifications.length > 0 && ( +
+

+ Certifications +

+
+ {certifications.map((cert) => ( +
+

{cert.name}

+

{cert.issuer}

+

+ {formatDate(cert.date)} + {cert.expirationDate && ` • Expires: ${formatDate(cert.expirationDate)}`} +

+
+ ))} +
+
+ )} +
+ + {/* Main Content */} +
+ {/* Header */} +
+

+ {personalInfo.fullName} +

+ {personalInfo.title && ( +

+ {personalInfo.title} +

+ )} +
+ + {/* Summary */} + {summary && ( +
+

+ Professional Summary +

+

+ {summary} +

+
+ )} + + {/* Experience */} + {experience.length > 0 && ( +
+

+ + Work Experience +

+
+ {experience.map((exp) => ( +
+
+
+

{exp.jobTitle}

+
+

+ {exp.company} + {exp.location && ` • ${exp.location}`} +

+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+
+
    + {exp.description.map((desc, idx) => ( +
  • + + {desc} +
  • + ))} +
+
+ ))} +
+
+ )} + + {/* Education */} + {education.length > 0 && ( +
+

+ + Education +

+
+ {education.map((edu) => ( +
+
+

{edu.degree}

+

+ {edu.school} + {edu.location && `, ${edu.location}`} +

+
+ {edu.fieldOfStudy} + {formatDate(edu.graduationDate)} +
+ {edu.gpa && ( +

+ GPA: {edu.gpa} +

+ )} +
+ ))} +
+
+ )} +
+
+ ); +} diff --git a/src/components/resume-builder/templates/ModernProfessionalTemplate.tsx b/src/components/resume-builder/templates/ModernProfessionalTemplate.tsx new file mode 100644 index 0000000..bf58969 --- /dev/null +++ b/src/components/resume-builder/templates/ModernProfessionalTemplate.tsx @@ -0,0 +1,366 @@ +import { ResumeData } from '@/types/resume'; +import { Mail, Phone, MapPin, Globe, Linkedin, Github, Briefcase, GraduationCap, Code, Award, User, BookOpen } from 'lucide-react'; + +interface TemplateProps { + data: ResumeData; +} + +export function ModernProfessionalTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications, projects, sections, customization } = data; + + const visibleSections = sections.filter(section => section.isVisible).sort((a, b) => a.order - b.order); + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + const formatUrl = (url: string) => { + if (!url) return ''; + return url.replace(/^https?:\/\/(www\.)?/, '').replace(/\/$/, ''); + }; + + const renderSection = (section: typeof sections[0]) => { + switch (section.type) { + case 'personalInfo': + return ( +
+
+

+ {personalInfo.fullName || 'Your Name'} +

+

+ {personalInfo.title || 'Professional Title'} +

+ +
+ +
+ {personalInfo.email && ( + + + {personalInfo.email} + + )} + {personalInfo.phone && ( +
+ + {personalInfo.phone} +
+ )} + {personalInfo.location && ( +
+ + {personalInfo.location} +
+ )} + {personalInfo.website && ( + + + Website + + )} + {personalInfo.linkedin && ( + + + LinkedIn + + )} + {personalInfo.github && ( + + + GitHub + + )} +
+
+
+ ); + + case 'summary': + if (!summary) return null; + return ( +
+

+ + Professional Profile +

+

+ {summary} +

+
+ ); + + case 'experience': + if (experience.length === 0) return null; + return ( +
+

+ + Professional Experience +

+
+ {experience.map((exp) => ( +
+
+
+

+ {exp.jobTitle} +

+

+ {exp.company} + {exp.location && ` • ${exp.location}`} +

+
+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+
    + {exp.description.filter(desc => desc.trim()).map((desc, index) => ( +
  • + + {desc} +
  • + ))} +
+
+ ))} +
+
+ ); + + case 'education': + if (education.length === 0) return null; + return ( +
+

+ + Education +

+
+ {education.map((edu) => ( +
+
+
+

+ {edu.degree} +

+

+ {edu.school} + {edu.location && `, ${edu.location}`} +

+
+ + {formatDate(edu.graduationDate)} + +
+ {edu.gpa && ( +

+ GPA: {edu.gpa} +

+ )} + {edu.relevantCourses && edu.relevantCourses.length > 0 && ( +
+

+ Relevant Coursework: +

+
+ {edu.relevantCourses.map((course, i) => ( + + {course} + + ))} +
+
+ )} +
+ ))} +
+
+ ); + + case 'skills': + if (skills.length === 0) return null; + return ( +
+

+ + Skills & Expertise +

+
+ {skills.map((skill) => ( +
+
+ + {skill.name} + + + {skill.level} + +
+
+
+
+
+ ))} +
+
+ ); + + case 'projects': + if (projects.length === 0) return null; + return ( +
+

+ + Projects +

+
+ {projects.map((project) => ( +
+
+

+ {project.name} + {project.url && ( + + (View Project) + + )} +

+ {project.startDate && ( + + {formatDate(project.startDate)} + {project.endDate ? ` - ${formatDate(project.endDate)}` : ' - Present'} + + )} +
+

+ {project.description} +

+ {project.technologies && project.technologies.length > 0 && ( +
+
+ {project.technologies.map((tech, i) => ( + + {tech} + + ))} +
+
+ )} +
+ ))} +
+
+ ); + + case 'certifications': + if (certifications.length === 0) return null; + return ( +
+

+ + Certifications +

+
+ {certifications.map((cert) => ( +
+

+ {cert.name} +

+

+ {cert.issuer} +

+

+ {formatDate(cert.date)} + {cert.expirationDate && ` - ${formatDate(cert.expirationDate)}`} +

+
+ ))} +
+
+ ); + + default: + return null; + } + }; + + return ( +
+ {visibleSections.map(section => renderSection(section))} +
+ ); +} diff --git a/src/components/resume-builder/templates/ModernSidebarTemplate.tsx b/src/components/resume-builder/templates/ModernSidebarTemplate.tsx new file mode 100644 index 0000000..25759f3 --- /dev/null +++ b/src/components/resume-builder/templates/ModernSidebarTemplate.tsx @@ -0,0 +1,266 @@ +import { Mail, Phone, MapPin, Globe, Linkedin, Github, Briefcase, GraduationCap, Code, Award, User, BookOpen } from 'lucide-react'; +import { ResumeData } from '@/types/resume'; + +type TemplateProps = { + data: ResumeData; +}; + +export function ModernSidebarTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications, projects, sections, customization } = data; + const visibleSections = sections.filter(section => section.isVisible).sort((a, b) => a.order - b.order); + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + const renderSidebar = () => ( +
+ {personalInfo.profileImage && ( +
+ {personalInfo.fullName} +
+ )} + +
+
+

CONTACT

+
+ {personalInfo.email && ( + + )} + {personalInfo.phone && ( + + )} + {personalInfo.location && ( +
+ + {personalInfo.location} +
+ )} + {personalInfo.website && ( + + )} + {personalInfo.linkedin && ( + + )} + {personalInfo.github && ( + + )} +
+
+ + {skills.length > 0 && ( +
+

SKILLS

+
+ {skills.map((skill) => ( +
+
+ {skill.name} + + {skill.level === 'Expert' ? '★★★★★' : + skill.level === 'Advanced' ? '★★★★☆' : + skill.level === 'Intermediate' ? '★★★☆☆' : '★★☆☆☆'} + +
+
+ ))} +
+
+ )} + + {certifications.length > 0 && ( +
+

CERTIFICATIONS

+
+ {certifications.map((cert) => ( +
+

{cert.name}

+

{cert.issuer}

+

+ {formatDate(cert.date)} + {cert.expirationDate && ` • Expires: ${formatDate(cert.expirationDate)}`} +

+
+ ))} +
+
+ )} +
+
+ ); + + const renderMainContent = () => ( +
+
+

+ {personalInfo.fullName} +

+ {personalInfo.title && ( +

+ {personalInfo.title} +

+ )} +
+ + {summary && ( +
+

+ + PROFILE +

+

{summary}

+
+ )} + + {experience.length > 0 && ( +
+

+ + EXPERIENCE +

+
+ {experience.map((exp) => ( +
+
+
+

+ {exp.jobTitle} +

+

+ {exp.company} + {exp.location && ` • ${exp.location}`} +

+
+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+
    + {exp.description.map((desc, idx) => ( +
  • + + {desc} +
  • + ))} +
+
+ ))} +
+
+ )} + + {education.length > 0 && ( +
+

+ + EDUCATION +

+
+ {education.map((edu) => ( +
+
+
+

+ {edu.degree} +

+

+ {edu.school} + {edu.location && `, ${edu.location}`} +

+
+ + {formatDate(edu.graduationDate)} + +
+ {edu.gpa && ( +

+ GPA: {edu.gpa} +

+ )} +
+ ))} +
+
+ )} + + {projects.length > 0 && ( +
+

+ + PROJECTS +

+
+ {projects.map((project) => ( +
+
+

+ {project.name} +

+ + {formatDate(project.startDate)} + {project.endDate && ` - ${formatDate(project.endDate)}`} + +
+

+ {project.description} +

+ {project.technologies.length > 0 && ( +
+ {project.technologies.map((tech, idx) => ( + + {tech} + + ))} +
+ )} +
+ ))} +
+
+ )} +
+ ); + + return ( +
+ {renderSidebar()} + {renderMainContent()} +
+ ); +} diff --git a/src/components/resume-builder/templates/ModernTimelineTemplate.tsx b/src/components/resume-builder/templates/ModernTimelineTemplate.tsx new file mode 100644 index 0000000..6f3528d --- /dev/null +++ b/src/components/resume-builder/templates/ModernTimelineTemplate.tsx @@ -0,0 +1,207 @@ +import { Mail, Phone, MapPin, Linkedin, Globe, Briefcase, GraduationCap, Award } from 'lucide-react'; +import { ResumeData } from '@/types/resume'; + +type TemplateProps = { + data: ResumeData; +}; + +export function ModernTimelineTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications } = data; + const accentColor = data.customization.accentColor || '#3b82f6'; + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + return ( +
+ {/* Header */} +
+

+ {personalInfo.fullName} +

+ {personalInfo.title && ( +

+ {personalInfo.title} +

+ )} +
+ {personalInfo.email && ( + + + {personalInfo.email} + + )} + {personalInfo.phone && ( + + + {personalInfo.phone} + + )} + {personalInfo.location && ( + + + {personalInfo.location} + + )} +
+
+ + {/* Summary */} + {summary && ( +
+

About Me

+

{summary}

+
+ )} + + {/* Timeline Section */} +
+ {/* Timeline Line */} +
+ +
+ {/* Experience */} + {experience.length > 0 && ( +
+

+ + Work Experience +

+
+ {experience.map((exp, idx) => ( +
+
+
+
+

{exp.jobTitle}

+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+

+ {exp.company} + {exp.location && ` • ${exp.location}`} +

+
    + {exp.description.map((desc, idx) => ( +
  • + + {desc} +
  • + ))} +
+
+
+ ))} +
+
+ )} + + {/* Education */} + {education.length > 0 && ( +
+

+ + Education +

+
+ {education.map((edu) => ( +
+
+
+
+

{edu.degree}

+ + {formatDate(edu.graduationDate)} + +
+

+ {edu.school} + {edu.location && `, ${edu.location}`} +

+ {edu.fieldOfStudy && ( +

+ {edu.fieldOfStudy} +

+ )} + {edu.gpa && ( +

+ GPA: {edu.gpa} +

+ )} +
+
+ ))} +
+
+ )} +
+
+ + {/* Skills & Certifications */} +
+ {/* Skills */} + {skills.length > 0 && ( +
+

+ + Key Skills +

+
+ {skills.map((skill) => ( + + {skill.name} + + ))} +
+
+ )} + + {/* Certifications */} + {certifications.length > 0 && ( +
+

+ + Certifications +

+
+ {certifications.map((cert) => ( +
+

{cert.name}

+

+ {cert.issuer} • {formatDate(cert.date)} +

+
+ ))} +
+
+ )} +
+
+ ); +} diff --git a/src/components/resume-builder/templates/ModernTwoColumn.tsx b/src/components/resume-builder/templates/ModernTwoColumn.tsx new file mode 100644 index 0000000..effc367 --- /dev/null +++ b/src/components/resume-builder/templates/ModernTwoColumn.tsx @@ -0,0 +1,205 @@ +import { Mail, Phone, MapPin, Globe, Linkedin, Github, Briefcase, GraduationCap, Code, Award } from 'lucide-react'; +import { ResumeData } from '@/types/resume'; + +type TemplateProps = { + data: ResumeData; +}; + +export function ModernTwoColumn({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications } = data; + const accentColor = data.customization.accentColor || '#3b82f6'; + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short' }); + }; + + return ( +
+ {/* Left Column */} +
+ {/* Profile */} +
+
+ {personalInfo.profileImage ? ( + {personalInfo.fullName} + ) : ( +
+ + {personalInfo.fullName.split(' ').map(n => n[0]).join('')} + +
+ )} +
+

{personalInfo.fullName}

+ {personalInfo.title && ( +

{personalInfo.title}

+ )} +
+ + {/* Contact */} +
+

Contact

+
    + {personalInfo.email && ( +
  • + + {personalInfo.email} +
  • + )} + {personalInfo.phone && ( +
  • + + {personalInfo.phone} +
  • + )} + {personalInfo.location && ( +
  • + + {personalInfo.location} +
  • + )} + {personalInfo.linkedin && ( +
  • + + LinkedIn +
  • + )} +
+
+ + {/* Skills */} + {skills.length > 0 && ( +
+

Skills

+
+ {skills.map((skill) => ( +
+
+ {skill.name} + {skill.level} +
+
+
+
+
+ ))} +
+
+ )} +
+ + {/* Right Column */} +
+ {/* Summary */} + {summary && ( +
+

+ About Me +

+

{summary}

+
+ )} + + {/* Experience */} + {experience.length > 0 && ( +
+

+ Work Experience +

+
+ {experience.map((exp) => ( +
+
+
+

{exp.jobTitle}

+

+ {exp.company} + {exp.location && ` • ${exp.location}`} +

+
+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+
    + {exp.description.map((desc, idx) => ( +
  • + + {desc} +
  • + ))} +
+
+ ))} +
+
+ )} + + {/* Education */} + {education.length > 0 && ( +
+

+ Education +

+
+ {education.map((edu) => ( +
+
+
+

{edu.degree}

+

+ {edu.school} + {edu.location && `, ${edu.location}`} +

+
+ + {formatDate(edu.graduationDate)} + +
+ {edu.gpa && ( +

+ GPA: {edu.gpa} +

+ )} +
+ ))} +
+
+ )} + + {/* Certifications */} + {certifications.length > 0 && ( +
+

+ Certifications +

+
+ {certifications.map((cert) => ( +
+

{cert.name}

+

+ {cert.issuer} • {formatDate(cert.date)} +

+
+ ))} +
+
+ )} +
+
+ ); +} diff --git a/src/components/resume-builder/templates/ProfessionalBlueTemplate.tsx b/src/components/resume-builder/templates/ProfessionalBlueTemplate.tsx new file mode 100644 index 0000000..a961f1c --- /dev/null +++ b/src/components/resume-builder/templates/ProfessionalBlueTemplate.tsx @@ -0,0 +1,150 @@ +import { ResumeData } from '@/types/resume'; +import { Briefcase, GraduationCap, Code, User } from 'lucide-react'; + +interface TemplateProps { + data: ResumeData; +} + +export function ProfessionalBlueTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, sections } = data; + const visibleSections = sections.filter(section => section.isVisible).sort((a, b) => a.order - b.order); + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + const renderSection = (section: typeof sections[0]) => { + switch (section.type) { + case 'personalInfo': + return ( +
+
+ {personalInfo.profileImage && ( +
+ {personalInfo.fullName +
+ )} +
+

{personalInfo.fullName || 'Your Name'}

+

{personalInfo.title || 'Professional Title'}

+
+
+
+ ); + + case 'summary': + if (!summary) return null; + return ( +
+

+ + PROFILE +

+

{summary}

+
+ ); + + case 'experience': + if (experience.length === 0) return null; + return ( +
+

+ + WORK EXPERIENCE +

+
+ {experience.map((exp) => ( +
+
+

{exp.jobTitle}

+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+

+ {exp.company} + {exp.location && ` • ${exp.location}`} +

+
+ ))} +
+
+ ); + + case 'education': + if (education.length === 0) return null; + return ( +
+

+ + EDUCATION +

+
+ {education.map((edu) => ( +
+
+

{edu.degree}

+ + {formatDate(edu.graduationDate)} + +
+

+ {edu.school} + {edu.location && `, ${edu.location}`} + {edu.gpa && ` • GPA: ${edu.gpa}`} +

+
+ ))} +
+
+ ); + + case 'skills': + if (skills.length === 0) return null; + return ( +
+

+ + SKILLS +

+
+ {skills.map((skill) => ( +
+
+ + {skill.name} + +
+
+
+
+
+ ))} +
+
+ ); + + default: + return null; + } + }; + + return ( +
+ {visibleSections.map(section => renderSection(section))} +
+ ); +} diff --git a/src/components/resume-builder/templates/ProfessionalSidebarTemplate.tsx b/src/components/resume-builder/templates/ProfessionalSidebarTemplate.tsx new file mode 100644 index 0000000..0f3eeec --- /dev/null +++ b/src/components/resume-builder/templates/ProfessionalSidebarTemplate.tsx @@ -0,0 +1,175 @@ +import { Mail, Phone, MapPin, Linkedin, Briefcase, GraduationCap } from 'lucide-react'; +import { ResumeData } from '@/types/resume'; + +type TemplateProps = { + data: ResumeData; +}; + +export function ProfessionalSidebarTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications } = data; + const accentColor = data.customization.accentColor || '#2563eb'; + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + return ( +
+ {/* Sidebar */} +
+ {/* Profile */} +
+ {personalInfo.profileImage ? ( +
+ {personalInfo.fullName} +
+ ) : ( +
+ + {personalInfo.fullName.split(' ').map(n => n[0]).join('')} + +
+ )} +

{personalInfo.fullName}

+ {personalInfo.title &&

{personalInfo.title}

} +
+ + {/* Contact */} +
+

Contact

+
    + {personalInfo.email && ( +
  • + + {personalInfo.email} +
  • + )} + {personalInfo.phone && ( +
  • + + {personalInfo.phone} +
  • + )} + {personalInfo.location && ( +
  • + + {personalInfo.location} +
  • + )} + {personalInfo.linkedin && ( +
  • + + LinkedIn +
  • + )} +
+
+ + {/* Skills */} + {skills.length > 0 && ( +
+

Skills

+
+ {skills.map((skill) => ( +
+
+ {skill.name} + {skill.level} +
+
+
+
+
+ ))} +
+
+ )} +
+ + {/* Main Content */} +
+ {/* Summary */} + {summary && ( +
+

+ Professional Summary +

+

{summary}

+
+ )} + + {/* Experience */} + {experience.length > 0 && ( +
+

+ + Work Experience +

+
+ {experience.map((exp) => ( +
+
+

{exp.jobTitle}

+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+

+ {exp.company} + {exp.location && ` • ${exp.location}`} +

+
    + {exp.description.map((desc, idx) => ( +
  • + + {desc} +
  • + ))} +
+
+ ))} +
+
+ )} + + {/* Education */} + {education.length > 0 && ( +
+

+ + Education +

+
+ {education.map((edu) => ( +
+

{edu.degree}

+

+ {edu.school} + {edu.location && `, ${edu.location}`} +

+
+ {edu.relevantCourses ? edu.relevantCourses.join(', ') : ''} + {formatDate(edu.graduationDate)} +
+ {edu.gpa && ( +

GPA: {edu.gpa}

+ )} +
+ ))} +
+
+ )} +
+
+ ); +} diff --git a/src/components/resume-builder/templates/ProfessionalSummaryTemplate.tsx b/src/components/resume-builder/templates/ProfessionalSummaryTemplate.tsx new file mode 100644 index 0000000..58f1bc7 --- /dev/null +++ b/src/components/resume-builder/templates/ProfessionalSummaryTemplate.tsx @@ -0,0 +1,189 @@ +import { ResumeData } from '@/types/resume'; +import { Briefcase, GraduationCap, User, Code, Mail, Phone, MapPin, Globe } from 'lucide-react'; + +interface TemplateProps { + data: ResumeData; +} + +export function ProfessionalSummaryTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications } = data; + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + return ( +
+ {/* Header */} +
+

{personalInfo.fullName || 'Your Name'}

+

{personalInfo.title || 'Professional Title'}

+ +
+ {personalInfo.email && ( +
+ + {personalInfo.email} +
+ )} + {personalInfo.phone && ( +
+ + {personalInfo.phone} +
+ )} + {personalInfo.location && ( +
+ + {personalInfo.location} +
+ )} + {personalInfo.website && ( + + )} +
+
+ + {/* Summary */} + {summary && ( +
+

+ + PROFESSIONAL SUMMARY +

+

{summary}

+
+ )} + +
+ {/* Left Column */} +
+ {/* Experience */} + {experience.length > 0 && ( +
+

+ + WORK EXPERIENCE +

+
+ {experience.map((exp) => ( +
+
+

{exp.jobTitle}

+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+

+ {exp.company} + {exp.location && ` • ${exp.location}`} +

+
    + {exp.description.filter(desc => desc.trim()).map((desc, index) => ( +
  • + + {desc} +
  • + ))} +
+
+ ))} +
+
+ )} + + {/* Education */} + {education.length > 0 && ( +
+

+ + EDUCATION +

+
+ {education.map((edu) => ( +
+
+

{edu.degree}

+ + {formatDate(edu.graduationDate)} + +
+

+ {edu.school} + {edu.location && `, ${edu.location}`} + {edu.gpa && ` • GPA: ${edu.gpa}`} +

+
+ ))} +
+
+ )} +
+ + {/* Right Column */} +
+ {/* Skills */} + {skills.length > 0 && ( +
+

+ + SKILLS +

+
+ {skills.map((skill) => ( +
+
+ + {skill.name} + + + {skill.level} + +
+
+
+
+
+ ))} +
+
+ )} + + {/* Certifications */} + {certifications && certifications.length > 0 && ( +
+

CERTIFICATIONS

+
+ {certifications.map((cert) => ( +
+
{cert.name}
+
{cert.issuer}
+ {cert.date && ( +
+ {new Date(cert.date).toLocaleDateString('en-US', { year: 'numeric', month: 'short' })} +
+ )} +
+ ))} +
+
+ )} +
+
+
+ ); +} diff --git a/src/components/resume-builder/templates/ProfessionalTemplate.tsx b/src/components/resume-builder/templates/ProfessionalTemplate.tsx new file mode 100644 index 0000000..d87c90e --- /dev/null +++ b/src/components/resume-builder/templates/ProfessionalTemplate.tsx @@ -0,0 +1,147 @@ +import React from 'react'; +import { ResumeData } from '@/types/resume'; +import { Mail, Phone, MapPin, Globe, Linkedin, Github } from 'lucide-react'; + +interface TemplateProps { + data: ResumeData; +} + +export function ProfessionalTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications, projects, references, sections, customization } = data; + + const visibleSections = sections.filter(section => section.isVisible).sort((a, b) => a.order - b.order); + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + const formatUrl = (url: string) => { + if (!url) return ''; + return url.replace(/^https?:\/\/(www\.)?/, '').replace(/\/$/, ''); + }; + + const renderSection = (section: typeof sections[0]) => { + switch (section.type) { + case 'personalInfo': + return ( +
+
+

+ {personalInfo.fullName || 'Your Name'} +

+ {personalInfo.jobTitle && ( +

+ {personalInfo.jobTitle} +

+ )} +
+ +
+ {personalInfo.email && ( +
+ + {personalInfo.email} +
+ )} + {personalInfo.phone && ( +
+ + {personalInfo.phone} +
+ )} + {personalInfo.location && ( +
+ + {personalInfo.location} +
+ )} + {personalInfo.website && ( +
+ + {formatUrl(personalInfo.website)} +
+ )} + {personalInfo.linkedin && ( +
+ + {formatUrl(personalInfo.linkedin)} +
+ )} + {personalInfo.github && ( +
+ + {formatUrl(personalInfo.github)} +
+ )} +
+
+ ); + + case 'summary': + if (!summary) return null; + return ( +
+
+
+

+ Professional Summary +

+
+

+ {summary} +

+
+ ); + + case 'experience': + if (experience.length === 0) return null; + return ( +
+
+
+

+ Professional Experience +

+
+
+ {experience.map((exp) => ( +
+
+

+ {exp.jobTitle} +

+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+
+ + {exp.company}{exp.location && ` • ${exp.location}`} + +
+
    + {exp.description.filter(desc => desc.trim()).map((desc, index) => ( +
  • {desc}
  • + ))} +
+
+ ))} +
+
+ ); + + // Continue with other section types... + default: + return null; + } + }; + + return ( +
+ {visibleSections.map(section => renderSection(section))} +
+ ); +} diff --git a/src/components/resume-builder/templates/TechTemplate.tsx b/src/components/resume-builder/templates/TechTemplate.tsx new file mode 100644 index 0000000..cd658f7 --- /dev/null +++ b/src/components/resume-builder/templates/TechTemplate.tsx @@ -0,0 +1,321 @@ +import { ResumeData } from '@/types/resume'; +import { Mail, Phone, MapPin, Globe, Linkedin, Github, Code, Cpu, BookOpen, Award, Briefcase, GraduationCap } from 'lucide-react'; + +interface TemplateProps { + data: ResumeData; +} + +export function TechTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications, projects, sections, customization } = data; + + const visibleSections = sections.filter(section => section.isVisible).sort((a, b) => a.order - b.order); + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); + }; + + const formatUrl = (url: string) => { + if (!url) return ''; + return url.replace(/^https?:\/\/(www\.)?/, '').replace(/\/$/, ''); + }; + + const renderSection = (section: typeof sections[0]) => { + switch (section.type) { + case 'personalInfo': + return ( +
+
+
+

+ {personalInfo.fullName || 'Your Name'} +

+

+ {personalInfo.title || 'Software Engineer'} +

+
+
+ {personalInfo.email && ( + + + {personalInfo.email} + + )} + {personalInfo.phone && ( +
+ + {personalInfo.phone} +
+ )} + {personalInfo.location && ( +
+ + {personalInfo.location} +
+ )} + {personalInfo.linkedin && ( + + + LinkedIn + + )} + {personalInfo.github && ( + + + GitHub + + )} + {personalInfo.website && ( + + + Website + + )} +
+
+
+ ); + + case 'summary': + if (!summary) return null; + return ( +
+

+ + Technical Profile +

+

+ {summary} +

+
+ ); + + case 'skills': + if (skills.length === 0) return null; + return ( +
+

+ + Technical Skills +

+
+ {skills.map((skill) => ( +
+
+ + {skill.name} + + + {skill.level} + +
+
+
+
+
+ ))} +
+
+ ); + + case 'experience': + if (experience.length === 0) return null; + return ( +
+

+ + Professional Experience +

+
+ {experience.map((exp) => ( +
+
+
+

+ {exp.jobTitle} +

+

+ {exp.company} + {exp.location && ` • ${exp.location}`} +

+
+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+
    + {exp.description.filter(desc => desc.trim()).map((desc, index) => ( +
  • + + {desc} +
  • + ))} +
+
+ ))} +
+
+ ); + + case 'projects': + if (projects.length === 0) return null; + return ( +
+

+ + Projects +

+
+ {projects.map((project) => ( +
+
+

+ {project.name} + {project.url && ( + + (View Project) + + )} +

+ {project.startDate && ( + + {formatDate(project.startDate)} + {project.endDate ? ` - ${formatDate(project.endDate)}` : ' - Present'} + + )} +
+

+ {project.description} +

+ {project.technologies && project.technologies.length > 0 && ( +
+ {project.technologies.map((tech, i) => ( + + {tech} + + ))} +
+ )} +
+ ))} +
+
+ ); + + case 'education': + if (education.length === 0) return null; + return ( +
+

+ + Education +

+
+ {education.map((edu) => ( +
+
+
+

+ {edu.degree} +

+

+ {edu.school} + {edu.location && `, ${edu.location}`} +

+ {edu.gpa && ( +

+ GPA: {edu.gpa} +

+ )} +
+ + {formatDate(edu.graduationDate)} + +
+ {edu.relevantCourses && edu.relevantCourses.length > 0 && ( +
+

+ Relevant Coursework: +

+
+ {edu.relevantCourses.map((course, i) => ( + + {course} + + ))} +
+
+ )} +
+ ))} +
+
+ ); + + case 'certifications': + if (certifications.length === 0) return null; + return ( +
+

+ + Certifications +

+
+ {certifications.map((cert) => ( +
+

+ {cert.name} +

+

+ {cert.issuer} +

+

+ {formatDate(cert.date)} + {cert.expirationDate && ` - ${formatDate(cert.expirationDate)}`} +

+
+ ))} +
+
+ ); + + default: + return null; + } + }; + + return ( +
+ {visibleSections.map(section => renderSection(section))} +
+ ); +} diff --git a/src/components/resume-builder/templates/TimelineTemplate.tsx b/src/components/resume-builder/templates/TimelineTemplate.tsx new file mode 100644 index 0000000..30b9a1f --- /dev/null +++ b/src/components/resume-builder/templates/TimelineTemplate.tsx @@ -0,0 +1,183 @@ +import { Mail, Phone, MapPin, Globe, Linkedin, Github, Briefcase, GraduationCap, Code, Award } from 'lucide-react'; +import { ResumeData } from '@/types/resume'; + +type TemplateProps = { + data: ResumeData; +}; + +export function TimelineTemplate({ data }: TemplateProps) { + const { personalInfo, summary, experience, education, skills, certifications } = data; + const accentColor = data.customization.accentColor || '#3b82f6'; + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr + '-01'); + return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short' }); + }; + + return ( +
+ {/* Header */} +
+

{personalInfo.fullName}

+ {personalInfo.title && ( +

{personalInfo.title}

+ )} +
+ {personalInfo.email && {personalInfo.email}} + {personalInfo.phone && {personalInfo.phone}} + {personalInfo.location && {personalInfo.location}} +
+
+ + {/* Summary */} + {summary && ( +
+

Professional Summary

+

{summary}

+
+ )} + + {/* Timeline Section */} +
+ {/* Timeline Line */} +
+ +
+ {/* Experience */} + {experience.length > 0 && ( +
+

+ + Work Experience +

+
+ {experience.map((exp, idx) => ( +
+
+
+
+

{exp.jobTitle}

+ + {formatDate(exp.startDate)} - {exp.isCurrentJob ? 'Present' : formatDate(exp.endDate)} + +
+

+ {exp.company} + {exp.location && ` • ${exp.location}`} +

+
    + {exp.description.map((desc, idx) => ( +
  • + + {desc} +
  • + ))} +
+
+
+ ))} +
+
+ )} + + {/* Education */} + {education.length > 0 && ( +
+

+ + Education +

+
+ {education.map((edu) => ( +
+
+
+
+

{edu.degree}

+ + {formatDate(edu.graduationDate)} + +
+

+ {edu.school} + {edu.location && `, ${edu.location}`} +

+ {edu.gpa && ( +

+ GPA: {edu.gpa} +

+ )} +
+
+ ))} +
+
+ )} +
+
+ + {/* Skills & Certifications */} +
+ {/* Skills */} + {skills.length > 0 && ( +
+

+ + Skills +

+
+ {skills.map((skill) => ( + + {skill.name} + + ))} +
+
+ )} + + {/* Certifications */} + {certifications.length > 0 && ( +
+

+ + Certifications +

+
+ {certifications.map((cert) => ( +
+

{cert.name}

+

+ {cert.issuer} • {formatDate(cert.date)} +

+
+ ))} +
+
+ )} +
+
+ ); +} diff --git a/src/components/resume-builder/templates/types.ts b/src/components/resume-builder/templates/types.ts new file mode 100644 index 0000000..6f36d51 --- /dev/null +++ b/src/components/resume-builder/templates/types.ts @@ -0,0 +1,58 @@ +import { ResumeData } from '@/types/resume'; + +export interface TemplateProps { + data: ResumeData; +} + +export interface TemplateSectionProps { + title: string; + className?: string; + children: React.ReactNode; +} + +export interface ExperienceItemProps { + jobTitle: string; + company: string; + location?: string; + startDate: string; + endDate: string; + isCurrentJob: boolean; + description: string[]; + className?: string; +} + +export interface EducationItemProps { + degree: string; + school: string; + location?: string; + graduationDate: string; + fieldOfStudy?: string; + gpa?: string; + className?: string; +} + +export interface SkillItemProps { + name: string; + level: 'Beginner' | 'Intermediate' | 'Advanced' | 'Expert'; + className?: string; +} + +export interface CertificationItemProps { + name: string; + issuer: string; + date: string; + expirationDate?: string; + className?: string; +} + +export interface TemplateStyles { + fontFamily?: string; + primaryColor?: string; + secondaryColor?: string; + textColor?: string; + backgroundColor?: string; + spacing?: { + section?: string; + item?: string; + }; +} diff --git a/src/components/resume-builder/types.ts b/src/components/resume-builder/types.ts new file mode 100644 index 0000000..1506996 --- /dev/null +++ b/src/components/resume-builder/types.ts @@ -0,0 +1,21 @@ +import { TemplateProps as TemplateComponentProps } from './templates/types'; + +export interface TemplateMeta { + id: string; + name: string; + description: string; + category: TemplateCategory; + previewImage?: string; + component: React.LazyExoticComponent<(props: TemplateComponentProps) => JSX.Element>; + tags?: string[]; + atsFriendly: boolean; + lastUpdated: string; +} + +export enum TemplateCategory { + PROFESSIONAL = 'Professional', + MODERN = 'Modern', + MINIMALIST = 'Minimalist', + CREATIVE = 'Creative', + SPECIALIZED = 'Specialized', +}