diff --git a/.github/workflows/generate-tailored-cvs.yml b/.github/workflows/generate-tailored-cvs.yml new file mode 100644 index 0000000..4a61192 --- /dev/null +++ b/.github/workflows/generate-tailored-cvs.yml @@ -0,0 +1,48 @@ +name: Generate Tailored CVs + +on: + pull_request: + branches: [main] + paths: + - 'lib/cv/**' + - 'app/[locale]/(standalone)/cv/**' + - 'scripts/generate-tailored-cvs.tsx' + - '.github/workflows/generate-tailored-cvs.yml' + - 'public/images/cv/**' + - 'public/images/yotam-programmer.png' + workflow_dispatch: + +permissions: + contents: read + +jobs: + generate: + runs-on: ubuntu-latest + timeout-minutes: 15 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 24 + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Generate tailored CV PDFs + run: pnpm tsx scripts/generate-tailored-cvs.tsx + + - name: Upload tailored CVs + uses: actions/upload-artifact@v4 + with: + name: tailored-cvs + path: artifacts/cv-tailored/*.pdf + if-no-files-found: error + retention-days: 7 diff --git a/docs/cv/TAILORED_CV_GUIDELINES.md b/docs/cv/TAILORED_CV_GUIDELINES.md new file mode 100644 index 0000000..0383dd2 --- /dev/null +++ b/docs/cv/TAILORED_CV_GUIDELINES.md @@ -0,0 +1,331 @@ +# Tailored CV Guidelines + +This document defines how Yotam Faraggi's tailored CV variants should be created. + +The goal is to make each CV highly relevant to a specific role **without changing Yotam's professional identity or making the CV look artificially rewritten to mirror a job description**. + +## Core principle + +> Tailor the emphasis, not the identity. + +A tailored CV should help a recruiter quickly see the parts of Yotam's real experience that matter most for the role. It should not make him appear to become a different kind of engineer for every application. + +--- + +## 1. Professional identity is stable + +Yotam's canonical engineering identity is: + +**Senior Full-Stack Engineer** + +This is the primary title and should remain stable across tailored CVs. + +Do **not** replace it with job-specific identities such as: + +- Senior Backend Engineer +- Senior Frontend Engineer +- AI Engineer +- Platform Engineer +- Integration Engineer + +Those can describe a role being targeted or an area of relevant experience, but they should not replace the canonical title unless Yotam explicitly decides to change his long-term professional identity. + +### Why + +Yotam's career is genuinely full-stack. His experience spans frontend, backend, APIs, integrations, infrastructure, enterprise systems, product delivery, and technical leadership. Calling him a backend engineer for one application and a frontend engineer for another can make the tailoring feel performative rather than credible. + +A backend role does not require pretending that the rest of the full-stack experience does not exist. Instead, the CV should demonstrate enough backend depth that the recruiter can confidently see the fit. + +--- + +## 2. Stable positioning vs. role-specific focus + +Think of the top of the CV as two layers. + +### Stable identity / positioning + +Preferred base: + +**Senior Full-Stack Engineer | Product & AI** + +"Product & AI" is positioning, not a replacement for the engineering identity. It reflects Yotam's product ownership mindset and current AI-assisted product work. + +This wording can be refined over time if Yotam changes his long-term positioning, but it should not be changed simply because a job advertisement uses a different title. + +### Tailored subtitle / focus + +The secondary focus may be tailored, but it must remain **capability-based, broad, and reusable across multiple employers**. + +It should describe a genuine professional area inside Yotam's experience, not the industry, company, or vacancy he is applying to. + +Good examples: + +- Systems, APIs & Integrations +- Distributed Systems & Cloud Platforms +- React, Product UX & Web Performance +- Cloud Platforms, APIs & Integrations +- AI-Assisted Products, Automation & Integrations + +Avoid subtitles such as: + +- Banking Systems, APIs & Integrations +- Healthcare Platforms & Integrations +- Fintech Backend Systems +- E-commerce Engineering for Retail +- N26 Connectivity & Banking Systems + +Even when these are factually supported, they can make the CV look visibly rewritten for one employer. + +### Subtitle test + +Before using a subtitle, ask: + +> **Would this still look natural if the recruiter knew the same CV had been sent to another company with a similar engineering problem?** + +If yes, it is probably a good professional focus. + +If it only makes sense because of the target company's industry or wording, move that relevance into the summary, experience bullets, and skill emphasis instead. + +### Important distinction + +Industry-specific evidence is absolutely allowed and often valuable inside the CV. + +For example, a banking role can and should surface Leumi, high-availability banking services, fintech integrations, and enterprise messaging when relevant. The rule is only that **the top-level subtitle should not advertise that the CV was tailored to banking**. + +The recruiter should discover the fit from the evidence, rather than from a label that announces the tailoring. + +--- + +## 3. What may be tailored + +The following elements are intentionally flexible. + +### Professional summary + +Rewrite the summary for the target role while preserving the same underlying career story. + +The summary should: + +- start from the stable Full-Stack identity +- surface the most relevant domain and technical experience early +- use terminology that is natural for the target role when it accurately describes existing experience +- explain transferable experience when the exact target stack is different +- remain readable and human, not keyword-stuffed + +Example for a backend/platform role: + +Do not say that Yotam "is a backend engineer" simply because the role is backend. + +Instead, explain that he is a senior full-stack engineer whose backend experience includes APIs, integrations, high-availability banking services, event-driven messaging, production systems, Node.js, PostgreSQL, cloud infrastructure, and reliability work. + +### Experience descriptions and bullets + +Bullets may be rewritten or reordered to make relevant work easier to see. + +Allowed: + +- bring relevant responsibilities to the first bullet +- make an implicit but factual technical connection clearer +- emphasize scale, reliability, integrations, architecture, product ownership, performance, security, or domain experience when supported +- reduce detail that is irrelevant to the target role +- use more specific wording when the underlying fact is known + +Not allowed: + +- invent technologies +- invent ownership that did not exist +- imply experience with a target stack solely because it is similar to something Yotam used +- inflate scale, team size, business impact, seniority, or responsibility +- rewrite a role so aggressively that it no longer resembles the actual job + +### Skills + +Skills can be reordered and regrouped so the most relevant proven skills appear first. + +For example, a backend role can begin with APIs, Node.js, system design, PostgreSQL, integrations, messaging, cloud, and reliability before React. + +This is **emphasis**, not concealment. Full-stack skills should remain visible. + +### Section ordering and density + +Sections and individual items can be reordered where the renderer permits it if doing so makes the relevant evidence easier to find. + +Older experience can receive slightly more visibility when it is unusually relevant to the target role, such as Leumi or HOT for banking, integrations, high-availability services, or messaging-heavy backend roles. + +--- + +## 4. What should normally remain unchanged + +Unless there is a factual correction or Yotam explicitly requests a change, preserve: + +- name and contact details +- location and work authorization +- company names +- actual job titles held at each company +- employment dates +- education +- languages +- factual technologies and systems used +- the overall chronological career history +- the canonical **Senior Full-Stack Engineer** identity + +The CV may change the lens through which the experience is presented, but not the underlying career history. + +--- + +## 5. Never mirror the job description mechanically + +A tailored CV should not look like the job advertisement was copied into the candidate profile. + +Avoid: + +- changing the title to exactly match the vacancy when that is not Yotam's real professional identity +- using an industry- or company-specific subtitle that reveals the targeting +- repeating the employer's wording unnaturally +- inserting every keyword from the requirements +- forcing marginal experience into the summary because a keyword appears in the listing +- claiming adjacent technologies as direct experience + +Use the job description as a **relevance map**, not as source material for a new persona. + +A recruiter should come away thinking: + +> "His existing background happens to be very relevant to this role." + +not: + +> "This CV was rewritten to make him look like exactly whatever we advertised." + +--- + +## 6. Handling missing target-stack experience + +When a job asks for technologies Yotam has not used, never manufacture them. + +Instead: + +1. Identify the underlying engineering capability being tested. +2. Find the strongest real analogue in Yotam's experience. +3. Surface that experience clearly. +4. Let the recruiter infer transferability. + +Example: + +If a role asks for Kafka but Yotam's proven event-driven production experience is with JMS, emphasize the JMS/event-driven systems experience. Do not add Kafka. + +If a role asks for AWS but Yotam's production cloud experience is GCP, emphasize cloud architecture and GCP. Do not silently replace GCP with AWS. + +If a role asks for Kotlin/Spring and the demonstrated backend stack is Node.js/TypeScript plus enterprise integration technologies, show the backend/system-design depth without claiming Kotlin/Spring experience. + +--- + +## 7. Role-specific tailoring strategy + +Before writing a variant, identify the 3-5 strongest reasons Yotam is a fit. + +Use those reasons consistently across the summary, experience bullets, and skills. + +### Backend / platform role + +Possible evidence: + +- Node.js / TypeScript backend work +- API design and integrations +- PostgreSQL +- high-availability banking services +- high-throughput services +- JMS and event-driven messaging +- enterprise integration systems +- cloud infrastructure +- Docker / CI/CD +- architecture, reliability, and technical ownership + +Keep React/Next.js visible because they reinforce the full-stack profile rather than weaken it. + +### Frontend-heavy role + +Possible evidence: + +- React / Next.js / TypeScript +- frontend architecture and modernization +- customer-facing product ownership +- performance optimization +- API integration +- design implementation and product quality + +Do not rename Yotam "Senior Frontend Engineer" merely to match the vacancy. + +### Product engineering role + +Possible evidence: + +- end-to-end ownership +- technical scoping +- customer-facing product development +- product and engineering tradeoffs +- integrations +- AI-assisted workflows +- shipping and iteration + +### AI-oriented product role + +Possible evidence: + +- LLM APIs +- AI-assisted product workflows +- automation +- product architecture around AI capabilities +- integrations with external systems + +Do not imply ML research, model training, or specialized ML engineering unless it is actually supported. + +--- + +## 8. File-generation workflow + +Tailored CVs must follow the repository workflow rather than being manually recreated. + +1. Use `CartShift/CartShift-Studio` as the source of truth. +2. Work on the dedicated `cv-tailored-variants` branch. +3. Add or update the tailored variant in the CV variant system. +4. Use the existing React-PDF `CVDocument` renderer, assets, typography, spacing, and layout. +5. Do **not** create a new public website route or change the live CV simply to generate an application-specific version. +6. Generate the PDF using the repository's tailored-CV workflow/script. +7. Inspect every generated page visually for clipping, overflow, awkward page breaks, density, or layout regressions. +8. Keep the result concise, preferably two pages unless there is a strong reason otherwise. +9. Upload the approved PDF to the existing tailored-CV location in Google Drive. + +The code and the generated PDF should remain reproducible from the repository. + +--- + +## 9. Final pre-generation checklist + +Before generating any tailored CV, verify: + +- [ ] The primary professional identity still says **Senior Full-Stack Engineer**. +- [ ] The stable positioning remains **Product & AI** unless Yotam explicitly changes it. +- [ ] The subtitle describes a broad professional capability, not the target company or industry. +- [ ] The CV does not pretend Yotam has become the exact job title in the vacancy. +- [ ] The summary makes the target fit obvious within the first few sentences. +- [ ] The strongest relevant evidence appears early. +- [ ] Every claimed technology and responsibility is factual. +- [ ] Missing technologies were handled through honest transferable experience, not invention. +- [ ] Full-stack breadth is still visible even when emphasizing backend or frontend depth. +- [ ] Existing job titles, companies, dates, education, and languages remain factual. +- [ ] Skills are reordered for relevance rather than stuffed with job-description keywords. +- [ ] The result still sounds like the same person as every other version of the CV. +- [ ] The PDF is generated through the canonical repository workflow. +- [ ] The final PDF is visually inspected before it is used or uploaded. + +--- + +## 10. Decision rule + +When unsure whether a change is appropriate, ask: + +> **Does this make Yotam's real experience easier to understand, or does it make him look more like the job advertisement?** + +If it is the first, the change is probably good tailoring. + +If it is the second, do not make the change. \ No newline at end of file diff --git a/lib/cv/cv-variants.ts b/lib/cv/cv-variants.ts new file mode 100644 index 0000000..c575fb8 --- /dev/null +++ b/lib/cv/cv-variants.ts @@ -0,0 +1,442 @@ +import { + getEnglishCVData, + type CVData, + type CVExperienceItem, + type CVExperienceKey, + type CVSkillGroup, + type CVSkillKey, +} from './cv-data'; + +export const cvVariantIds = [ + 'product-ai', + 'fullstack-healthcare', + 'product-frontend', + 'n26-backend', +] as const; + +export type CVVariantId = (typeof cvVariantIds)[number]; + +export const CANONICAL_CV_IDENTITY = 'Senior Full-Stack Engineer | Product & AI'; + +export interface CVVariantDefinition { + id: CVVariantId; + label: string; + filename: string; + focus: string; +} + +export const cvVariantDefinitions: Record = { + 'product-ai': { + id: 'product-ai', + label: 'Product AI', + filename: 'yotam-faraggi-senior-full-stack-engineer-product-ai-cv.pdf', + focus: 'AI-Assisted Products, Automation & Integrations', + }, + 'fullstack-healthcare': { + id: 'fullstack-healthcare', + label: 'Full-Stack Healthcare', + filename: 'yotam-faraggi-senior-full-stack-engineer-healthcare-cv.pdf', + focus: 'Cloud Platforms, APIs & Integrations', + }, + 'product-frontend': { + id: 'product-frontend', + label: 'Product Frontend', + filename: 'yotam-faraggi-senior-full-stack-engineer-frontend-cv.pdf', + focus: 'React, Product UX & Web Performance', + }, + 'n26-backend': { + id: 'n26-backend', + label: 'N26', + filename: 'yotam-faraggi-senior-full-stack-engineer-n26-cv.pdf', + focus: 'Systems, APIs & Integrations', + }, +}; + +function cloneExperience(experience: CVExperienceItem): CVExperienceItem { + return { + ...experience, + highlights: [...experience.highlights], + }; +} + +function cloneSkill(skill: CVSkillGroup): CVSkillGroup { + return { + ...skill, + items: [...skill.items], + }; +} + +function cloneCVData(): CVData { + const base = getEnglishCVData(); + const experiences = base.experiences.map(cloneExperience); + const byKey = new Map(experiences.map(experience => [experience.key, experience])); + + return { + ...base, + contact: { ...base.contact }, + labels: { ...base.labels }, + sections: { ...base.sections }, + summary: { ...base.summary }, + portfolio: { + ...base.portfolio, + projects: base.portfolio.projects.map(project => ({ + ...project, + signals: [...project.signals], + })), + clients: { ...base.portfolio.clients }, + }, + experiences, + recentExperiences: base.recentExperiences.map(experience => byKey.get(experience.key)!), + earlierExperiences: base.earlierExperiences.map(experience => byKey.get(experience.key)!), + skills: base.skills.map(cloneSkill), + education: base.education.map(entry => ({ ...entry })), + languages: base.languages.map(language => ({ ...language })), + }; +} + +function applyPositioning(cv: CVData, variant: CVVariantId) { + cv.headline = `${CANONICAL_CV_IDENTITY}\n${cvVariantDefinitions[variant].focus}`; +} + +function updateExperience( + cv: CVData, + key: CVExperienceKey, + update: Partial> +) { + const experience = cv.experiences.find(item => item.key === key); + if (!experience) return; + + Object.assign(experience, update); + if (update.highlights) experience.highlights = [...update.highlights]; +} + +function getSkill(cv: CVData, key: CVSkillKey) { + return cv.skills.find(skill => skill.key === key); +} + +function reorderSkills(cv: CVData, order: CVSkillKey[]) { + const index = new Map(order.map((key, position) => [key, position])); + cv.skills.sort((a, b) => (index.get(a.key) ?? 999) - (index.get(b.key) ?? 999)); +} + +function buildProductAIVariant() { + const cv = cloneCVData(); + applyPositioning(cv, 'product-ai'); + cv.summary.text = + 'Senior Full-Stack Engineer with more than 10 years of experience building customer-facing web products and internal systems across e-commerce, healthcare, fintech, and enterprise software. Full-stack across React, Next.js, TypeScript, Node.js, APIs, integrations, cloud infrastructure, and PostgreSQL, with a strong focus on turning practical product needs into reliable software. Recent work includes AI-assisted workflow tools, headless e-commerce, telemedicine, and third-party integrations, with ownership from technical planning through deployment and ongoing improvement.'; + cv.summary.metaDescription = + 'Senior Full-Stack Engineer in Berlin building product-focused web applications, AI-assisted workflows, integrations, and reliable customer-facing software.'; + + updateExperience(cv, 'cartshift', { + highlights: [ + 'Build and ship Shopify storefronts and headless e-commerce experiences using Next.js, React, TypeScript, and modern web tooling.', + 'Develop AI-assisted workflow tools and internal utilities that automate operational tasks.', + 'Take projects from requirements and technical planning through implementation, deployment, and iterative improvement for international clients.', + ], + }); + + const productEngineering = getSkill(cv, 'productEngineering'); + if (productEngineering) { + productEngineering.items = [ + 'Technical scoping', + 'System design', + 'API design', + 'Customer-facing product development', + 'Reliability', + 'Performance optimization', + 'Web application architecture', + ]; + } + + const aiAutomation = getSkill(cv, 'aiAutomation'); + if (aiAutomation) { + aiAutomation.items = [ + 'OpenAI API', + 'Claude API', + 'LangChain', + 'Webhooks', + 'Puppeteer', + 'Playwright', + ]; + } + + reorderSkills(cv, [ + 'productEngineering', + 'aiAutomation', + 'frontendFullStack', + 'cloudData', + 'commerceIntegrations', + 'legacyEnterprise', + ]); + + return cv; +} + +function buildHealthcareVariant() { + const cv = cloneCVData(); + applyPositioning(cv, 'fullstack-healthcare'); + cv.summary.text = + 'Senior Full-Stack Engineer with more than 10 years of experience building web products, integrations, and production systems, including more than four years working on customer-facing healthcare and telemedicine software. Strong across React, Next.js, TypeScript, Node.js, API design, cloud infrastructure, and data systems, with hands-on experience building a HIPAA-compliant telemedicine funnel on Google Cloud Platform. Focused on privacy, reliability, maintainability, and translating operational requirements into dependable software.'; + cv.summary.metaDescription = + 'Senior Full-Stack Engineer in Berlin with healthcare, telemedicine, cloud, API, integration, and privacy-conscious product experience.'; + + updateExperience(cv, 'curalife', { + description: + 'Worked across customer-facing telemedicine and e-commerce systems, integrations, frontend modernization, and cloud infrastructure in a healthcare environment.', + highlights: [ + 'Built and maintained a HIPAA-compliant telemedicine funnel using Next.js, React, TypeScript, and Google Cloud Platform.', + 'Worked across customer-facing telemedicine and e-commerce systems, integrations, frontend modernization, and cloud infrastructure.', + 'Built Shopify and HubSpot integrations supporting customer and operational workflows.', + 'Built and deployed cloud infrastructure with a focus on privacy, reliability, and maintainability.', + ], + }); + + updateExperience(cv, 'cartshift', { + highlights: [ + 'Build full-stack web and e-commerce products using Next.js, React, TypeScript, and modern web tooling.', + 'Develop workflow automation tools and internal utilities for operational tasks.', + 'Handle projects from requirements and technical planning through implementation, deployment, and ongoing improvement.', + ], + }); + + const productEngineering = getSkill(cv, 'productEngineering'); + if (productEngineering) { + productEngineering.category = 'Healthcare & Reliability'; + productEngineering.items = [ + 'HIPAA-compliant telemedicine', + 'Privacy', + 'Reliability', + 'Maintainability', + 'API design', + 'Customer-facing workflows', + 'System design', + ]; + } + + reorderSkills(cv, [ + 'productEngineering', + 'frontendFullStack', + 'commerceIntegrations', + 'cloudData', + 'aiAutomation', + 'legacyEnterprise', + ]); + + return cv; +} + +function buildProductFrontendVariant() { + const cv = cloneCVData(); + applyPositioning(cv, 'product-frontend'); + cv.summary.text = + 'Senior Full-Stack Engineer with more than 10 years of experience building customer-facing web products, with a strong center of gravity in modern frontend development and full-stack delivery. Deep experience with React, Next.js, TypeScript, JavaScript, performance optimization, API integrations, and web application architecture, backed by hands-on work with Node.js, PostgreSQL, Google Cloud Platform, and Vercel. Built telemedicine, e-commerce, and high-traffic web experiences from requirements and technical planning through deployment and continuous improvement.'; + cv.summary.metaDescription = + 'Senior Full-Stack Engineer in Berlin with deep React, Next.js, TypeScript, frontend architecture, API integration, and customer-facing product experience.'; + + updateExperience(cv, 'cartshift', { + highlights: [ + 'Build Shopify storefronts and headless e-commerce experiences using Next.js, React, TypeScript, and modern web tooling.', + 'Take customer-facing projects from requirements and technical planning through implementation, deployment, and ongoing improvement.', + 'Develop AI-assisted workflow tools and internal utilities for operational tasks.', + ], + }); + + updateExperience(cv, 'curalife', { + highlights: [ + 'Led migration work toward a modern frontend stack and improved key customer-facing flows.', + 'Built and maintained a HIPAA-compliant telemedicine funnel using Next.js, React, TypeScript, and Google Cloud Platform.', + 'Built Shopify and HubSpot integrations supporting customer-facing and operational workflows.', + 'Built and deployed cloud infrastructure with a focus on privacy, reliability, and maintainability.', + ], + }); + + const frontend = getSkill(cv, 'frontendFullStack'); + if (frontend) { + frontend.category = 'Frontend & Primary Stack'; + frontend.items = [ + 'React', + 'Next.js', + 'TypeScript', + 'JavaScript', + 'Tailwind CSS', + 'Framer Motion', + 'Node.js', + ]; + } + + const productEngineering = getSkill(cv, 'productEngineering'); + if (productEngineering) { + productEngineering.items = [ + 'Technical scoping', + 'Web application architecture', + 'System design', + 'Customer-facing product development', + 'Performance optimization', + 'Reliability', + 'API design', + ]; + } + + reorderSkills(cv, [ + 'frontendFullStack', + 'productEngineering', + 'commerceIntegrations', + 'cloudData', + 'aiAutomation', + 'legacyEnterprise', + ]); + + return cv; +} + +function buildN26BackendVariant() { + const cv = cloneCVData(); + applyPositioning(cv, 'n26-backend'); + cv.summary.text = + 'Senior Full-Stack Engineer with more than 10 years of experience building production software across banking, fintech, healthcare, and e-commerce. Backend and integration experience includes high-availability banking services, high-throughput web services, event-driven messaging with JMS, REST APIs and third-party integrations, Node.js and TypeScript, PostgreSQL, Docker, CI/CD, and Google Cloud Platform. Combines hands-on system design and delivery with product ownership, taking work from technical scoping through deployment and ongoing improvement.'; + cv.summary.metaDescription = + 'Berlin-based Senior Full-Stack Engineer with banking, fintech, API, integration, cloud, event-driven messaging, and production systems experience.'; + + updateExperience(cv, 'cartshift', { + description: + 'Independent product and web development studio focused on production web applications, integrations, and workflow automation for international clients.', + highlights: [ + 'Build and ship production applications and backend workflows using TypeScript, Node.js, Next.js, third-party APIs, webhooks, and modern web tooling.', + 'Own architecture and integration decisions from requirements and technical planning through deployment and iterative improvement.', + 'Develop AI-assisted workflow tools and automations that integrate LLM APIs with external services and operational processes.', + ], + }); + + updateExperience(cv, 'curalife', { + description: + 'Worked across customer-facing telemedicine and e-commerce systems, backend integrations, cloud infrastructure, and technical leadership.', + highlights: [ + 'Built and maintained a HIPAA-compliant telemedicine funnel using Next.js, React, TypeScript, and Google Cloud Platform, connecting customer-facing flows with backend services.', + 'Designed and integrated third-party healthcare and eligibility APIs supporting dynamic customer and operational workflows.', + 'Built Shopify and HubSpot integrations supporting customer-facing and operational processes.', + 'Led technical modernization and cloud deployment work with a focus on privacy, reliability, and maintainability.', + ], + }); + + updateExperience(cv, 'paragonex', { + description: + 'Built and maintained production integrations and high-traffic web systems for fintech affiliate operations.', + highlights: [ + 'Built integrations between affiliate systems and core fintech platform services.', + 'Developed and optimized high-traffic web applications using Laravel, Sage, and WordPress with a focus on performance and maintainability.', + ], + }); + + updateExperience(cv, 'ecommerce_venture', { + highlights: [ + 'Built and operated direct-to-consumer systems spanning storefronts, payments, fulfillment, analytics, integrations, and day-to-day operations.', + ], + }); + + updateExperience(cv, 'hot', { + highlights: [ + 'Built enterprise integrations and high-throughput web services using Oracle OSB, WebLogic, IBM DataPower, and JMS queues.', + 'Used asynchronous JMS messaging to connect services and operational systems in production environments.', + ], + }); + + updateExperience(cv, 'leumi', { + highlights: [ + 'Developed and maintained high-availability banking integrations using IBM WebSphere ESB, IBM Integration Bus, and IBM DataPower.', + 'Worked on production services connecting banking systems with a focus on reliability and operational continuity.', + ], + }); + + const productEngineering = getSkill(cv, 'productEngineering'); + if (productEngineering) { + productEngineering.category = 'Backend & Product Engineering'; + productEngineering.items = [ + 'System design', + 'API design', + 'Technical scoping', + 'Production systems', + 'Reliability', + 'Performance optimization', + 'End-to-end ownership', + ]; + } + + const cloudData = getSkill(cv, 'cloudData'); + if (cloudData) { + cloudData.category = 'Cloud & Delivery'; + cloudData.items = [ + 'Google Cloud Platform', + 'Docker', + 'GitHub Actions', + 'CI/CD', + 'PostgreSQL', + 'Firebase', + 'Vercel', + ]; + } + + const integrations = getSkill(cv, 'commerceIntegrations'); + if (integrations) { + integrations.category = 'APIs, Integrations & Messaging'; + integrations.items = [ + 'REST APIs', + 'GraphQL', + 'Webhooks', + 'JMS', + 'IBM Integration Bus', + 'Oracle OSB', + 'IBM DataPower', + ]; + } + + const fullStack = getSkill(cv, 'frontendFullStack'); + if (fullStack) { + fullStack.category = 'Backend & Full-Stack'; + fullStack.items = [ + 'Node.js', + 'TypeScript', + 'JavaScript', + 'Next.js', + 'React', + 'PostgreSQL', + ]; + } + + const enterprise = getSkill(cv, 'legacyEnterprise'); + if (enterprise) { + enterprise.category = 'Enterprise & Banking Systems'; + enterprise.items = [ + 'IBM WebSphere ESB', + 'IBM Integration Bus', + 'Oracle OSB', + 'WebLogic', + 'IBM DataPower', + 'JMS', + 'C++', + ]; + } + + reorderSkills(cv, [ + 'productEngineering', + 'cloudData', + 'commerceIntegrations', + 'frontendFullStack', + 'legacyEnterprise', + 'aiAutomation', + ]); + + return cv; +} + +export function getCVDataForVariant(variant: CVVariantId): CVData { + switch (variant) { + case 'product-ai': + return buildProductAIVariant(); + case 'fullstack-healthcare': + return buildHealthcareVariant(); + case 'product-frontend': + return buildProductFrontendVariant(); + case 'n26-backend': + return buildN26BackendVariant(); + } +} \ No newline at end of file diff --git a/scripts/generate-tailored-cvs.tsx b/scripts/generate-tailored-cvs.tsx new file mode 100644 index 0000000..e56a815 --- /dev/null +++ b/scripts/generate-tailored-cvs.tsx @@ -0,0 +1,37 @@ +import React from 'react'; +import { mkdir } from 'node:fs/promises'; +import { join } from 'node:path'; +import { renderToFile } from '@react-pdf/renderer'; +import { CVDocument } from '../app/[locale]/(standalone)/cv/CVDocument'; +import { resolveCvPdfAssets } from '../lib/cv/cv-media'; +import { + cvVariantDefinitions, + cvVariantIds, + getCVDataForVariant, +} from '../lib/cv/cv-variants'; + +async function main() { + const outputDir = join(process.cwd(), 'artifacts', 'cv-tailored'); + await mkdir(outputDir, { recursive: true }); + + const resolvedAssets = await resolveCvPdfAssets(); + + for (const variant of cvVariantIds) { + const definition = cvVariantDefinitions[variant]; + const cv = getCVDataForVariant(variant); + const outputPath = join(outputDir, definition.filename); + const document = React.createElement(CVDocument, { + cv, + resolvedAssets, + }) as Parameters[0]; + + await renderToFile(document, outputPath); + + console.log(`Generated ${definition.label}: ${outputPath}`); + } +} + +main().catch(error => { + console.error(error); + process.exitCode = 1; +});