Initial commit: ArchiTools modular dashboard platform

Complete Next.js 16 application with 13 fully implemented modules:
Email Signature, Word XML Generator, Registratura, Dashboard,
Tag Manager, IT Inventory, Address Book, Password Vault,
Mini Utilities, Prompt Generator, Digital Signatures,
Word Templates, and AI Chat.

Includes core platform systems (module registry, feature flags,
storage abstraction, i18n, theming, auth stub, tagging),
16 technical documentation files, Docker deployment config,
and legacy HTML tool reference.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Marius Tarau
2026-02-17 12:50:25 +02:00
commit 4c46e8bcdd
189 changed files with 33780 additions and 0 deletions

View File

@@ -0,0 +1,166 @@
'use client';
import type { CompanyId } from '@/core/auth/types';
import type { SignatureConfig, SignatureColors, SignatureLayout, SignatureVariant } from '../types';
import { COMPANY_BRANDING } 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;
}
const COLOR_PALETTE: Record<string, string> = {
verde: '#22B5AB',
griInchis: '#54504F',
griDeschis: '#A7A9AA',
negru: '#323232',
};
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,
}: SignatureConfiguratorProps) {
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>
<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 */}
<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(COLOR_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>
);
}