253 lines
9.8 KiB
TypeScript
253 lines
9.8 KiB
TypeScript
'use client';
|
|
|
|
import type { CompanyId } from '@/core/auth/types';
|
|
import type { SignatureConfig, SignatureColors, SignatureLayout, SignatureVariant } from '../types';
|
|
import { COMPANY_BRANDING, BELETAGE_ADDRESSES, US_ADDRESSES, SDT_ADDRESSES } from '../services/company-branding';
|
|
import { Input } from '@/shared/components/ui/input';
|
|
import { Label } from '@/shared/components/ui/label';
|
|
import { Switch } from '@/shared/components/ui/switch';
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/shared/components/ui/select';
|
|
import { Separator } from '@/shared/components/ui/separator';
|
|
import { cn } from '@/shared/lib/utils';
|
|
|
|
interface SignatureConfiguratorProps {
|
|
config: SignatureConfig;
|
|
onUpdateField: <K extends keyof SignatureConfig>(key: K, value: SignatureConfig[K]) => void;
|
|
onUpdateColor: (key: keyof SignatureColors, value: string) => void;
|
|
onUpdateLayout: (key: keyof SignatureLayout, value: number) => void;
|
|
onSetVariant: (variant: SignatureVariant) => void;
|
|
onSetCompany: (company: CompanyId) => void;
|
|
onSetAddress?: (address: string[]) => void;
|
|
}
|
|
|
|
/** Color palette per company */
|
|
const COMPANY_PALETTES: Record<CompanyId, Record<string, string>> = {
|
|
beletage: {
|
|
verde: '#22B5AB',
|
|
griInchis: '#54504F',
|
|
griDeschis: '#A7A9AA',
|
|
negru: '#323232',
|
|
},
|
|
'urban-switch': {
|
|
indigo: '#6366f1',
|
|
violet: '#4F46E5',
|
|
griInchis: '#2D2D2D',
|
|
griDeschis: '#6B7280',
|
|
albastru: '#3B82F6',
|
|
negru: '#1F2937',
|
|
},
|
|
'studii-de-teren': {
|
|
amber: '#f59e0b',
|
|
portocaliu: '#D97706',
|
|
griInchis: '#2D2D2D',
|
|
griDeschis: '#6B7280',
|
|
maro: '#92400E',
|
|
negru: '#1F2937',
|
|
},
|
|
group: {
|
|
gri: '#64748b',
|
|
griInchis: '#334155',
|
|
griDeschis: '#94a3b8',
|
|
negru: '#1e293b',
|
|
},
|
|
};
|
|
|
|
const COLOR_LABELS: Record<keyof SignatureColors, string> = {
|
|
prefix: 'Titulatură',
|
|
name: 'Nume',
|
|
title: 'Funcție',
|
|
address: 'Adresă',
|
|
phone: 'Telefon',
|
|
website: 'Website',
|
|
motto: 'Motto',
|
|
};
|
|
|
|
const LAYOUT_CONTROLS: { key: keyof SignatureLayout; label: string; min: number; max: number }[] = [
|
|
{ key: 'greenLineWidth', label: 'Lungime linie accent', min: 50, max: 300 },
|
|
{ key: 'sectionSpacing', label: 'Spațiere secțiuni', min: 0, max: 30 },
|
|
{ key: 'logoSpacing', label: 'Spațiere logo', min: 0, max: 30 },
|
|
{ key: 'titleSpacing', label: 'Spațiere funcție', min: 0, max: 20 },
|
|
{ key: 'gutterWidth', label: 'Aliniere contact', min: 0, max: 150 },
|
|
{ key: 'iconTextSpacing', label: 'Spațiu icon-text', min: -10, max: 30 },
|
|
{ key: 'iconVerticalOffset', label: 'Aliniere verticală iconițe', min: -10, max: 10 },
|
|
{ key: 'mottoSpacing', label: 'Spațiere motto', min: 0, max: 20 },
|
|
];
|
|
|
|
export function SignatureConfigurator({
|
|
config, onUpdateField, onUpdateColor, onUpdateLayout, onSetVariant, onSetCompany, onSetAddress,
|
|
}: SignatureConfiguratorProps) {
|
|
const palette = COMPANY_PALETTES[config.company];
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
{/* Company selector */}
|
|
<div>
|
|
<Label>Companie</Label>
|
|
<Select value={config.company} onValueChange={(v) => onSetCompany(v as CompanyId)}>
|
|
<SelectTrigger className="mt-1">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
{Object.values(COMPANY_BRANDING).map((b) => (
|
|
<SelectItem key={b.id} value={b.id}>{b.name}</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
|
|
{/* Address selector (for Beletage) */}
|
|
{config.company === 'beletage' && onSetAddress && (
|
|
<div>
|
|
<Label>Adresă birou</Label>
|
|
<Select
|
|
value={!config.addressOverride || BELETAGE_ADDRESSES.unirii.join('|') === config.addressOverride.join('|') ? 'unirii' : 'christescu'}
|
|
onValueChange={(v) => {
|
|
const key = v as keyof typeof BELETAGE_ADDRESSES;
|
|
onSetAddress(BELETAGE_ADDRESSES[key]);
|
|
}}
|
|
>
|
|
<SelectTrigger className="mt-1"><SelectValue /></SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="unirii">Str. Unirii, nr. 3</SelectItem>
|
|
<SelectItem value="christescu">Str. G-ral Eremia Grigorescu, nr. 21</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
)}
|
|
|
|
{/* Address selector (for Urban Switch) */}
|
|
{config.company === 'urban-switch' && onSetAddress && (
|
|
<div>
|
|
<Label>Adresă birou</Label>
|
|
<Select
|
|
value={!config.addressOverride || US_ADDRESSES.unirii.join('|') === config.addressOverride.join('|') ? 'unirii' : 'unirii'}
|
|
onValueChange={(v) => {
|
|
const key = v as keyof typeof US_ADDRESSES;
|
|
onSetAddress(US_ADDRESSES[key]);
|
|
}}
|
|
>
|
|
<SelectTrigger className="mt-1"><SelectValue /></SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="unirii">Str. Unirii, nr. 3</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
)}
|
|
|
|
{/* Address selector (for Studii de Teren) */}
|
|
{config.company === 'studii-de-teren' && onSetAddress && (
|
|
<div>
|
|
<Label>Adresă birou</Label>
|
|
<Select
|
|
value={!config.addressOverride || SDT_ADDRESSES.unirii.join('|') === config.addressOverride.join('|') ? 'unirii' : 'unirii'}
|
|
onValueChange={(v) => {
|
|
const key = v as keyof typeof SDT_ADDRESSES;
|
|
onSetAddress(SDT_ADDRESSES[key]);
|
|
}}
|
|
>
|
|
<SelectTrigger className="mt-1"><SelectValue /></SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="unirii">Str. Unirii, nr. 3</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
)}
|
|
|
|
<Separator />
|
|
|
|
{/* Personal data */}
|
|
<div className="space-y-3">
|
|
<h3 className="text-sm font-semibold">Date personale</h3>
|
|
<div>
|
|
<Label htmlFor="sig-prefix">Titulatură (prefix)</Label>
|
|
<Input id="sig-prefix" value={config.prefix} onChange={(e) => onUpdateField('prefix', e.target.value)} className="mt-1" />
|
|
</div>
|
|
<div>
|
|
<Label htmlFor="sig-name">Nume și Prenume</Label>
|
|
<Input id="sig-name" value={config.name} onChange={(e) => onUpdateField('name', e.target.value)} className="mt-1" />
|
|
</div>
|
|
<div>
|
|
<Label htmlFor="sig-title">Funcția</Label>
|
|
<Input id="sig-title" value={config.title} onChange={(e) => onUpdateField('title', e.target.value)} className="mt-1" />
|
|
</div>
|
|
<div>
|
|
<Label htmlFor="sig-phone">Telefon (format 07xxxxxxxx)</Label>
|
|
<Input id="sig-phone" type="tel" value={config.phone} onChange={(e) => onUpdateField('phone', e.target.value)} className="mt-1" />
|
|
</div>
|
|
</div>
|
|
|
|
<Separator />
|
|
|
|
{/* Variant */}
|
|
<div className="space-y-3">
|
|
<h3 className="text-sm font-semibold">Variantă</h3>
|
|
<Select value={config.variant} onValueChange={(v) => onSetVariant(v as SignatureVariant)}>
|
|
<SelectTrigger>
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="full">Completă (logo + adresă + motto)</SelectItem>
|
|
<SelectItem value="reply">Simplă (fără logo/adresă)</SelectItem>
|
|
<SelectItem value="minimal">Super-simplă (doar nume/telefon)</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
<div className="flex items-center gap-2">
|
|
<Switch checked={config.useSvg} onCheckedChange={(v) => onUpdateField('useSvg', v)} id="svg-toggle" />
|
|
<Label htmlFor="svg-toggle" className="cursor-pointer text-sm">Imagini SVG (calitate maximă)</Label>
|
|
</div>
|
|
</div>
|
|
|
|
<Separator />
|
|
|
|
{/* Colors — company-specific palette */}
|
|
<div className="space-y-3">
|
|
<h3 className="text-sm font-semibold">Culori text</h3>
|
|
{(Object.keys(COLOR_LABELS) as (keyof SignatureColors)[]).map((colorKey) => (
|
|
<div key={colorKey} className="flex items-center justify-between">
|
|
<span className="text-sm text-muted-foreground">{COLOR_LABELS[colorKey]}</span>
|
|
<div className="flex gap-1.5">
|
|
{Object.values(palette).map((color) => (
|
|
<button
|
|
key={color}
|
|
type="button"
|
|
onClick={() => onUpdateColor(colorKey, color)}
|
|
className={cn(
|
|
'h-6 w-6 rounded-full border-2 transition-all',
|
|
config.colors[colorKey] === color
|
|
? 'border-primary scale-110 ring-2 ring-primary/30'
|
|
: 'border-transparent hover:scale-105'
|
|
)}
|
|
style={{ backgroundColor: color }}
|
|
/>
|
|
))}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
<Separator />
|
|
|
|
{/* Layout sliders */}
|
|
<div className="space-y-3">
|
|
<h3 className="text-sm font-semibold">Stil & Aranjare</h3>
|
|
{LAYOUT_CONTROLS.map(({ key, label, min, max }) => (
|
|
<div key={key}>
|
|
<div className="flex justify-between text-sm">
|
|
<Label>{label}</Label>
|
|
<span className="text-muted-foreground">{config.layout[key]}px</span>
|
|
</div>
|
|
<input
|
|
type="range"
|
|
min={min}
|
|
max={max}
|
|
value={config.layout[key]}
|
|
onChange={(e) => onUpdateLayout(key, parseInt(e.target.value, 10))}
|
|
className="mt-1 w-full accent-primary"
|
|
/>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|