Files
srbhr--resume-matcher/apps/frontend/components/builder/template-selector.tsx
T
wehub-resource-sync 5bdf4cc89a
Publish Docker Image / publish (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:39:36 +08:00

295 lines
12 KiB
TypeScript

'use client';
import React from 'react';
import { type TemplateType, TEMPLATE_OPTIONS } from '@/lib/types/template-settings';
import { useTranslations } from '@/lib/i18n';
interface TemplateSelectorProps {
value: TemplateType;
onChange: (template: TemplateType) => void;
}
/**
* Template Selector Component
*
* Visual thumbnail buttons for selecting resume templates.
* Swiss design: Square corners, high contrast, monospace labels.
*/
export const TemplateSelector: React.FC<TemplateSelectorProps> = ({ value, onChange }) => {
const { t } = useTranslations();
const templateLabels = {
'swiss-single': {
name: t('builder.formatting.templates.swissSingle.name'),
description: t('builder.formatting.templates.swissSingle.description'),
},
'swiss-two-column': {
name: t('builder.formatting.templates.swissTwoColumn.name'),
description: t('builder.formatting.templates.swissTwoColumn.description'),
},
modern: {
name: t('builder.formatting.templates.modern.name'),
description: t('builder.formatting.templates.modern.description'),
},
'modern-two-column': {
name: t('builder.formatting.templates.modernTwoColumn.name'),
description: t('builder.formatting.templates.modernTwoColumn.description'),
},
latex: {
name: t('builder.formatting.templates.latex.name'),
description: t('builder.formatting.templates.latex.description'),
},
clean: {
name: t('builder.formatting.templates.clean.name'),
description: t('builder.formatting.templates.clean.description'),
},
vivid: {
name: t('builder.formatting.templates.vivid.name'),
description: t('builder.formatting.templates.vivid.description'),
},
};
return (
<div className="flex flex-wrap gap-3">
{TEMPLATE_OPTIONS.map((template) => (
<button
key={template.id}
onClick={() => onChange(template.id)}
className={`group flex flex-col items-center p-3 border-2 transition-all ${
value === template.id
? 'border-blue-700 bg-white shadow-[3px_3px_0px_0px_#1D4ED8]'
: 'border-black bg-white hover:bg-background hover:shadow-sw-sm'
}`}
title={templateLabels[template.id].description}
>
{/* Template Thumbnail */}
<div className="w-16 h-20 mb-2 flex items-center justify-center">
<TemplateThumbnail type={template.id} isActive={value === template.id} />
</div>
{/* Template Name */}
<span
className={`font-mono text-[10px] uppercase tracking-wider font-bold ${
value === template.id ? 'text-blue-700' : 'text-ink-soft'
}`}
>
{templateLabels[template.id].name}
</span>
</button>
))}
</div>
);
};
/**
* Template Thumbnail
*
* Visual representation of each template layout
* Exported for use in FormattingControls
*/
interface TemplateThumbnailProps {
type: TemplateType;
isActive: boolean;
}
export const TemplateThumbnail: React.FC<TemplateThumbnailProps> = ({ type, isActive }) => {
const lineColor = isActive ? 'bg-blue-700' : 'bg-steel-grey';
const borderColor = isActive ? 'border-blue-700' : 'border-steel-grey';
const accentColor = isActive ? 'bg-blue-600' : 'bg-blue-400';
if (type === 'swiss-single') {
// Single column thumbnail
return (
<div className={`w-14 h-18 border ${borderColor} bg-white p-1.5 flex flex-col gap-1`}>
{/* Header */}
<div className={`h-2 ${lineColor} w-full`}></div>
<div className={`h-0.5 ${lineColor} w-3/4`}></div>
{/* Sections */}
<div className="flex-1 space-y-1 mt-1">
<div className={`h-0.5 ${lineColor} w-full`}></div>
<div className={`h-0.5 ${lineColor} w-5/6 opacity-50`}></div>
<div className={`h-0.5 ${lineColor} w-4/6 opacity-50`}></div>
<div className="h-1"></div>
<div className={`h-0.5 ${lineColor} w-full`}></div>
<div className={`h-0.5 ${lineColor} w-5/6 opacity-50`}></div>
<div className={`h-0.5 ${lineColor} w-3/6 opacity-50`}></div>
</div>
</div>
);
}
if (type === 'latex') {
// LaTeX thumbnail - centered name + Title-Case ruled section headers (serif feel)
return (
<div className={`w-14 h-18 border ${borderColor} bg-white p-1.5 flex flex-col gap-1`}>
{/* Centered name + contact */}
<div className="flex flex-col items-center gap-0.5">
<div className={`h-1.5 ${lineColor} w-2/3`}></div>
<div className={`h-0.5 ${lineColor} w-1/2 opacity-60`}></div>
</div>
{/* Sections: each header is a full-width line with a bottom rule */}
<div className="flex-1 space-y-1 mt-1">
<div className={`h-0.5 ${lineColor} w-2/5 border-b ${borderColor} pb-1`}></div>
<div className={`h-0.5 ${lineColor} w-5/6 opacity-50`}></div>
<div className={`h-0.5 ${lineColor} w-4/6 opacity-50`}></div>
<div className="h-0.5"></div>
<div className={`h-0.5 ${lineColor} w-1/3 border-b ${borderColor} pb-1`}></div>
<div className={`h-0.5 ${lineColor} w-5/6 opacity-50`}></div>
</div>
</div>
);
}
if (type === 'clean') {
// Clean thumbnail - centered light name + large understated gray uppercase headers
return (
<div className={`w-14 h-18 border ${borderColor} bg-white p-1.5 flex flex-col gap-1`}>
{/* Centered light name + contact line */}
<div className="flex flex-col items-center gap-0.5">
<div className={`h-1.5 ${lineColor} w-1/2 opacity-70`}></div>
<div className={`h-0.5 ${lineColor} w-2/3 opacity-40`}></div>
</div>
{/* Large gray section headers (taller, lower opacity) + thin rule */}
<div className="flex-1 space-y-1 mt-1">
<div className={`h-1 ${lineColor} w-1/2 opacity-30 border-b ${borderColor}`}></div>
<div className={`h-0.5 ${lineColor} w-5/6 opacity-50`}></div>
<div className={`h-0.5 ${lineColor} w-4/6 opacity-50`}></div>
<div className="h-0.5"></div>
<div className={`h-1 ${lineColor} w-2/5 opacity-30 border-b ${borderColor}`}></div>
<div className={`h-0.5 ${lineColor} w-5/6 opacity-50`}></div>
</div>
</div>
);
}
if (type === 'modern') {
// Modern template thumbnail - with accent color highlights
return (
<div className={`w-14 h-18 border ${borderColor} bg-white p-1.5 flex flex-col gap-1`}>
{/* Header with accent underline */}
<div className="flex flex-col items-center gap-0.5">
<div className={`h-2 ${lineColor} w-3/4`}></div>
<div className={`h-0.5 ${accentColor} w-1/3`}></div>
</div>
{/* Sections with accent headers */}
<div className="flex-1 space-y-1 mt-1">
<div className={`h-0.5 ${accentColor} w-full`}></div>
<div className={`h-0.5 ${lineColor} w-5/6 opacity-50`}></div>
<div className={`h-0.5 ${lineColor} w-4/6 opacity-50`}></div>
<div className="h-0.5"></div>
<div className={`h-0.5 ${accentColor} w-full`}></div>
<div className={`h-0.5 ${lineColor} w-5/6 opacity-50`}></div>
<div className={`h-0.5 ${lineColor} w-3/6 opacity-50`}></div>
</div>
</div>
);
}
if (type === 'modern-two-column') {
// Modern two-column template thumbnail - accent colors + two columns
return (
<div className={`w-14 h-18 border ${borderColor} bg-white p-1.5 flex flex-col gap-1`}>
{/* Header with accent underline */}
<div className="flex flex-col items-center gap-0.5">
<div className={`h-1.5 ${lineColor} w-3/4`}></div>
<div className={`h-0.5 ${accentColor} w-1/3`}></div>
</div>
{/* Two columns */}
<div className="flex-1 flex gap-1 mt-1">
{/* Left column (wider) - with accent headers */}
<div className="w-2/3 space-y-0.5">
<div className={`h-0.5 ${accentColor} w-full`}></div>
<div className={`h-0.5 ${lineColor} w-5/6 opacity-50`}></div>
<div className={`h-0.5 ${lineColor} w-4/6 opacity-50`}></div>
<div className="h-0.5"></div>
<div className={`h-0.5 ${accentColor} w-full`}></div>
<div className={`h-0.5 ${lineColor} w-5/6 opacity-50`}></div>
</div>
{/* Right column (narrower). Active state uses a heavier full
border instead of a left stripe (impeccable BAN 1). */}
<div
className={`w-1/3 border ${isActive ? 'border-blue-600' : 'border-blue-300'} pl-1 space-y-0.5`}
>
<div className={`h-0.5 ${accentColor} w-full`}></div>
<div className={`h-0.5 ${lineColor} w-4/5 opacity-50`}></div>
<div className="h-0.5"></div>
<div className={`h-0.5 ${accentColor} w-full`}></div>
<div className={`h-0.5 ${lineColor} w-3/5 opacity-50`}></div>
</div>
</div>
</div>
);
}
if (type === 'vivid') {
// Vivid thumbnail - two-tone accent name + accent headers + accent arrow bullets
return (
<div className={`w-14 h-18 border ${borderColor} bg-white p-1.5 flex flex-col gap-1`}>
{/* Two-tone name (left-aligned) */}
<div className="flex items-center gap-0.5">
<div className={`h-1.5 ${accentColor} w-1/3`}></div>
<div className={`h-1.5 ${accentColor} w-1/4 opacity-50`}></div>
</div>
<div className={`h-0.5 ${lineColor} w-2/3 opacity-40`}></div>
{/* Two columns (no divider) */}
<div className="flex-1 flex gap-1 mt-0.5">
{/* Left column with arrow ticks */}
<div className="w-2/3 space-y-0.5">
<div className={`h-0.5 ${accentColor} w-1/2`}></div>
<div className="flex items-center gap-0.5">
<div className={`h-0.5 w-0.5 ${accentColor}`}></div>
<div className={`h-0.5 ${lineColor} w-5/6 opacity-50`}></div>
</div>
<div className="flex items-center gap-0.5">
<div className={`h-0.5 w-0.5 ${accentColor}`}></div>
<div className={`h-0.5 ${lineColor} w-4/6 opacity-50`}></div>
</div>
<div className="h-0.5"></div>
<div className={`h-0.5 ${accentColor} w-2/5`}></div>
</div>
{/* Right column (sidebar) */}
<div className="w-1/3 space-y-0.5">
<div className={`h-0.5 ${accentColor} w-full`}></div>
<div className={`h-0.5 ${lineColor} w-4/5 opacity-50`}></div>
<div className="h-0.5"></div>
<div className={`h-0.5 ${accentColor} w-full`}></div>
<div className={`h-0.5 ${lineColor} w-3/5 opacity-50`}></div>
</div>
</div>
</div>
);
}
// Two column thumbnail (swiss-two-column)
return (
<div className={`w-14 h-18 border ${borderColor} bg-white p-1.5 flex flex-col gap-1`}>
{/* Header - centered */}
<div className="flex flex-col items-center gap-0.5">
<div className={`h-1.5 ${lineColor} w-3/4`}></div>
<div className={`h-0.5 ${lineColor} w-1/2 opacity-70`}></div>
</div>
{/* Two columns */}
<div className="flex-1 flex gap-1 mt-1">
{/* Left column (wider) */}
<div className="w-2/3 space-y-0.5">
<div className={`h-0.5 ${lineColor} w-full`}></div>
<div className={`h-0.5 ${lineColor} w-5/6 opacity-50`}></div>
<div className={`h-0.5 ${lineColor} w-4/6 opacity-50`}></div>
<div className="h-0.5"></div>
<div className={`h-0.5 ${lineColor} w-full`}></div>
<div className={`h-0.5 ${lineColor} w-5/6 opacity-50`}></div>
</div>
{/* Right column (narrower) */}
<div className="w-1/3 border-l border-paper-tint pl-1 space-y-0.5">
<div className={`h-0.5 ${lineColor} w-full`}></div>
<div className={`h-0.5 ${lineColor} w-4/5 opacity-50`}></div>
<div className="h-0.5"></div>
<div className={`h-0.5 ${lineColor} w-full`}></div>
<div className={`h-0.5 ${lineColor} w-3/5 opacity-50`}></div>
</div>
</div>
</div>
);
};
export default TemplateSelector;