/* email-store.jsx * Email template store — default templates, CRUD, and render helpers. * All exports are placed on window so other Babel files can access them. */ const RECRUITER_NAME = "Airtm Careers"; const RECRUITER_EMAIL = "people@airtm.io"; const RECRUITER_TITLE = "Talent partner, Airtm"; /* ── Template IDs ─────────────────────────────────────────────────────────── */ function newTemplateId() { return "tpl_" + Math.random().toString(36).slice(2, 10); } /* ── Default templates (one per meaningful stage transition) ─────────────── */ const DEFAULT_TEMPLATES = { tpl_intro_screening: { id: "tpl_intro_screening", name: "Intro screening invite", stageId: "intro_screening", subject: "Let's connect — {{role}} at Airtm", body: `Hi {{firstName}}, Thank you for applying for the {{role}} position at Airtm! Your background caught our attention, and we'd love to learn more about you. We'd like to schedule a brief 30-minute intro call to share more about the role, learn about your experience, and answer any questions you might have. Please feel free to reply to this email with a few times that work for you, or use the scheduling link below. We're excited about the possibility of working together. Looking forward to speaking with you soon! Warm regards, {{recruiter}} {{recruiterTitle}}`, }, tpl_hiring_manager: { id: "tpl_hiring_manager", name: "Hiring manager interview", stageId: "hiring_manager", subject: "Next step: hiring manager interview — {{role}}", body: `Hi {{firstName}}, We really enjoyed our conversation and are excited to move forward! The next step is a conversation with the hiring manager for the {{role}} role. This will be a deeper dive into your experience and vision — typically 45–60 minutes. You'll have the opportunity to hear directly from the team about the challenges they're working on and ask any questions about the role and culture. We'll reach out shortly with scheduling options. If you have any questions in the meantime, don't hesitate to reply here. Thank you for your continued interest in Airtm — we're looking forward to it! Best, {{recruiter}} {{recruiterTitle}}`, }, tpl_technical_test: { id: "tpl_technical_test", name: "Take-home / technical exercise", stageId: "technical_test", subject: "Technical exercise — {{role}} at Airtm", body: `Hi {{firstName}}, Great news! We'd like to move to the next step in our process for the {{role}} role — a take-home technical exercise. The exercise is designed to take approximately 3–4 hours and is intended to reflect the kind of work you'd be doing at Airtm. There are no trick questions — we're looking for clear thinking, good judgment, and clean execution. We'll send the exercise details in a separate email. You'll have 5 business days to complete and submit it. Please don't hesitate to ask if anything is unclear. We're excited to see your work! Best, {{recruiter}} {{recruiterTitle}}`, }, tpl_offer: { id: "tpl_offer", name: "Offer extended", stageId: "offer", subject: "Offer from Airtm — {{role}}", body: `Hi {{firstName}}, On behalf of the entire Airtm team — congratulations! We'd love to officially extend you an offer for the {{role}} position. This has been a fantastic process, and the team is genuinely excited about what you'll bring. We believe you're the right person for this role, and we can't wait to work together. A formal offer letter with all the details — compensation, benefits, and start date — will follow from our People team shortly. Please review it carefully, and don't hesitate to reach out with any questions. We want to make sure you have everything you need to feel confident in your decision. We're rooting for you! Warmly, {{recruiter}} {{recruiterTitle}}`, }, tpl_hired: { id: "tpl_hired", name: "Welcome — offer accepted", stageId: "hired", subject: "Welcome to Airtm, {{firstName}}! 🎉", body: `Hi {{firstName}}, We are absolutely thrilled to welcome you to Airtm! The team is so excited to have you joining us. Over the next few days, you'll receive onboarding instructions from our People team covering your start date, equipment setup, and first-week schedule. In the meantime, feel free to reach out to me with any questions — big or small. We want your first days to feel smooth and welcoming. See you soon! Warmly, {{recruiter}} {{recruiterTitle}}`, }, tpl_rejected: { id: "tpl_rejected", name: "Respectful rejection", stageId: "rejected", subject: "Your application for {{role}} at Airtm", body: `Hi {{firstName}}, Thank you so much for taking the time to apply for the {{role}} role at Airtm and for the conversations we've had throughout this process. After careful consideration, we've decided to move forward with another candidate whose experience more closely aligns with our immediate needs. This was a genuinely difficult decision — you have a strong background, and we appreciate the time and effort you invested. We'll keep your details on file and would love to stay in touch. Please don't hesitate to apply for future openings, and we wish you all the best in your search. Thank you again for your interest in Airtm. Warm regards, {{recruiter}} {{recruiterTitle}}`, }, }; /* ── Default bindings: stageId → templateId ──────────────────────────────── */ const DEFAULT_STAGE_BINDINGS = { intro_screening: "tpl_intro_screening", hiring_manager: "tpl_hiring_manager", technical_test: "tpl_technical_test", offer: "tpl_offer", hired: "tpl_hired", rejected: "tpl_rejected", }; /* ── localStorage CRUD ───────────────────────────────────────────────────── */ const LS_KEY = "airtm_ats_templates_v1"; function loadTemplateStore() { try { const raw = localStorage.getItem(LS_KEY); if (!raw) return null; return JSON.parse(raw); } catch (e) { return null; } } function saveTemplateStore(store) { try { localStorage.setItem(LS_KEY, JSON.stringify(store)); } catch (e) { /* storage quota exceeded — ignore */ } } function resetTemplateStore() { try { localStorage.removeItem(LS_KEY); } catch (e) {} } /* ── Retrieve the effective template for a given stage ───────────────────── */ function getTemplateForStage(stageId) { const store = loadTemplateStore(); const tplId = (store?.bindings || DEFAULT_STAGE_BINDINGS)[stageId]; const tpls = { ...DEFAULT_TEMPLATES, ...(store?.templates || {}) }; return tpls[tplId] || null; } /* ── Template renderer ───────────────────────────────────────────────────── */ /** * Replace {{var}} tokens in template subject/body with values from ctx. * ctx shape: { contact: { firstName, lastName }, posting: { title, location }, stage: { label } } */ function renderEmailTemplate(template, ctx) { const vars = { firstName: ctx.contact?.firstName || "", lastName: ctx.contact?.lastName || "", role: ctx.posting?.title || "", stageName: ctx.stage?.label || "", recruiter: RECRUITER_NAME, recruiterTitle: RECRUITER_TITLE, }; const replace = (str) => (str || "").replace(/\{\{(\w+)\}\}/g, (_, k) => vars[k] !== undefined ? vars[k] : `{{${k}}}`); return { subject: replace(template.subject), body: replace(template.body), }; } /* ── Export to window ────────────────────────────────────────────────────── */ Object.assign(window, { RECRUITER_NAME, RECRUITER_EMAIL, RECRUITER_TITLE, DEFAULT_TEMPLATES, DEFAULT_STAGE_BINDINGS, newTemplateId, loadTemplateStore, saveTemplateStore, resetTemplateStore, getTemplateForStage, renderEmailTemplate, });