feat: add Carbone tags help - view in-app modal and download as .docx

This commit is contained in:
root
2026-07-16 09:19:06 +00:00
parent 49a6081c92
commit 6fa8c58179
5 changed files with 522 additions and 0 deletions

View File

@@ -473,6 +473,128 @@ router.get('/master-template', async (req, res) => {
}
});
// GET /api/carbone/help-docx - Download tags reference as .docx
router.get('/help-docx', (req, res) => {
const helpPath = path.join(TEMPLATES_DIR, 'HELP_TAGS_REFERENCE.docx');
if (fs.existsSync(helpPath)) {
res.download(helpPath, 'Carbone_Tags_Reference.docx');
} else {
res.status(404).json({ error: 'Help file not found' });
}
});
// GET /api/carbone/help - Get tags reference as JSON (for in-app viewing)
router.get('/help', (req, res) => {
res.json({
sections: [
{
title: '1. Personal Details',
type: 'simple',
tags: [
{ tag: '{d.fullName}', desc: 'Full name (first + last)' },
{ tag: '{d.firstName}', desc: 'First name only' },
{ tag: '{d.lastName}', desc: 'Last name only' },
{ tag: '{d.email}', desc: 'Email address' },
{ tag: '{d.phone}', desc: 'Phone number' },
{ tag: '{d.address}', desc: 'Physical address' },
{ tag: '{d.linkedin}', desc: 'LinkedIn profile URL' },
{ tag: '{d.github}', desc: 'GitHub profile URL' },
{ tag: '{d.website}', desc: 'Personal website' },
{ tag: '{d.summary}', desc: 'Professional summary paragraph' },
{ tag: '{d.generationDate}', desc: 'Date the CV was generated' }
]
},
{
title: '2. Work Experience (Loop)',
type: 'loop',
note: 'Carbone auto-detects loops from [i]. Section repeats for each job.',
tags: [
{ tag: '{d.experience[i].position}', desc: 'Job title' },
{ tag: '{d.experience[i].company}', desc: 'Company name' },
{ tag: '{d.experience[i].location}', desc: 'Work location' },
{ tag: '{d.experience[i].startDate}', desc: 'Start date (Mon YYYY)' },
{ tag: '{d.experience[i].endDate}', desc: 'End date or "Present"' },
{ tag: '{d.experience[i].duration}', desc: 'Calculated duration (e.g., 4.5 years)' },
{ tag: '{d.experience[i].description}', desc: 'Job description' },
{ tag: '{d.experience[i].skillsUsed}', desc: 'Comma-separated skills used' },
{ tag: '{d.experience[i].achievements[j]}', desc: 'Achievement bullet (sub-loop)' }
]
},
{
title: '3. Skills',
type: 'loop',
tags: [
{ tag: '{d.skills[i].name}', desc: 'Skill name' },
{ tag: '{d.skills[i].category}', desc: 'Skill category' },
{ tag: '{d.skills[i].proficiency}', desc: 'Proficiency level' },
{ tag: '{d.skills[i].years}', desc: 'Years of experience (dynamic)' },
{ tag: '{d.skillsByCategory[i].category}', desc: 'Category name (grouped)' },
{ tag: '{d.skillsByCategory[i].skills[j].name}', desc: 'Skill in category (grouped)' },
{ tag: '{d.skillsByCategory[i].skills[j].years}', desc: 'Years in category (grouped)' }
]
},
{
title: '4. Education (Loop)',
type: 'loop',
tags: [
{ tag: '{d.education[i].degree}', desc: 'Degree name' },
{ tag: '{d.education[i].field}', desc: 'Field of study' },
{ tag: '{d.education[i].institution}', desc: 'University/institution' },
{ tag: '{d.education[i].startDate}', desc: 'Start date' },
{ tag: '{d.education[i].endDate}', desc: 'End date' },
{ tag: '{d.education[i].grade}', desc: 'Grade/result' }
]
},
{
title: '5. Certifications (Loop)',
type: 'loop',
tags: [
{ tag: '{d.certifications[i].name}', desc: 'Certification name' },
{ tag: '{d.certifications[i].issuer}', desc: 'Issuing organization' },
{ tag: '{d.certifications[i].date}', desc: 'Issue date' },
{ tag: '{d.certifications[i].expiryDate}', desc: 'Expiry date (if any)' }
]
},
{
title: '6. Conditionals (Show/Hide)',
type: 'simple',
note: 'Show content only when a field has a value (or is empty).',
tags: [
{ tag: '{d.field:showBegin}...{d.field:showEnd}', desc: 'Show if NOT empty' },
{ tag: '{d.field:hideBegin}...{d.field:hideEnd}', desc: 'Show if empty' },
{ tag: '{d.field:ifEM(replacement text)}', desc: 'Show text if field is empty' },
{ tag: '{d.field:ifNEM(replacement text)}', desc: 'Show text if field is not empty' },
{ tag: '{d.field:ifEQ(value):showBegin}...{d.field:showEnd}', desc: 'Show if equals value' },
{ tag: '{d.field:ifContain(text):showBegin}...{d.field:showEnd}', desc: 'Show if contains text' },
{ tag: '{d.field:ifGT(5):showBegin}...{d.field:showEnd}', desc: 'Show if greater than value' }
]
},
{
title: '7. Formatting',
type: 'simple',
tags: [
{ tag: '{d.field:formatD(YYYY-MM-DD)}', desc: 'Format date: 2022-01-01' },
{ tag: '{d.field:formatD(MMM YYYY)}', desc: 'Format date: Jan 2022' },
{ tag: '{d.field:formatN(0.0)}', desc: 'Format number with 1 decimal' },
{ tag: '{d.field:upperCase}', desc: 'Convert to UPPERCASE' },
{ tag: '{d.field:lowerCase}', desc: 'Convert to lowercase' },
{ tag: '{d.field:ucWords}', desc: 'Capitalize Each Word' },
{ tag: '{d.field:substr(0, 100)}', desc: 'First 100 characters' }
]
},
{
title: '8. Array Operations',
type: 'simple',
tags: [
{ tag: '{d.skills:arrayJoin(, )}', desc: 'Join all items with separator' },
{ tag: '{d.skills:arrayMap(name):arrayJoin(, )}', desc: 'Extract field and join' },
{ tag: '{d.experience:count}', desc: 'Count items in array' }
]
}
]
});
});
// POST /api/carbone/templates/upload - Upload a custom .docx template
router.post('/templates/upload', upload.single('template'), (req, res) => {
if (!req.file) {