feat(registratura): atomic numbering, reserved slots, audit trail, API endpoints + theme toggle animation

Registratura module:
- Atomic sequence numbering (BTG-2026-IN-00125 format) via PostgreSQL upsert
- Reserved monthly slots (2/company/month) for late registrations
- Append-only audit trail with diff tracking
- REST API: /api/registratura (CRUD), /api/registratura/reserved, /api/registratura/audit
- Auth: NextAuth session + Bearer API key support
- New "intern" direction type with UI support (form, filters, table, detail panel)
- Prisma models: RegistrySequence, RegistryAudit

Theme toggle:
- SVG mask-based sun/moon morph with 360° spin animation
- Inverted logic (sun in dark mode, moon in light mode)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
AI Assistant
2026-03-10 07:54:32 +02:00
parent f94529c380
commit a0dd35a066
15 changed files with 1354 additions and 124 deletions
@@ -2,6 +2,7 @@
import {
ArrowDownToLine,
ArrowRightLeft,
ArrowUpFromLine,
Calendar,
CheckCircle2,
@@ -64,6 +65,12 @@ const DIRECTION_CONFIG = {
class:
"bg-orange-100 text-orange-800 dark:bg-orange-900/40 dark:text-orange-300",
},
intern: {
label: "Intern",
icon: ArrowRightLeft,
class:
"bg-purple-100 text-purple-800 dark:bg-purple-900/40 dark:text-purple-300",
},
} as const;
const STATUS_CONFIG = {
@@ -76,6 +83,10 @@ const STATUS_CONFIG = {
label: "Închis",
class: "bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-400",
},
reserved: {
label: "Rezervat",
class: "bg-amber-100 text-amber-800 dark:bg-amber-900/40 dark:text-amber-300",
},
} as const;
const RESOLUTION_LABELS: Record<string, string> = {
@@ -15,6 +15,7 @@ import {
Globe,
ArrowDownToLine,
ArrowUpFromLine,
ArrowRightLeft,
HardDrive,
FolderOpen,
Link2,
@@ -554,6 +555,19 @@ export function RegistryEntryForm({
<ArrowUpFromLine className="h-4 w-4" />
Ieșit
</button>
<button
type="button"
onClick={() => setDirection("intern")}
className={cn(
"flex flex-1 items-center justify-center gap-1.5 rounded-md px-3 py-2 text-sm font-medium transition-all",
direction === "intern"
? "bg-purple-500 text-white shadow-sm"
: "text-muted-foreground hover:text-foreground hover:bg-background",
)}
>
<ArrowRightLeft className="h-4 w-4" />
Intern
</button>
</div>
</div>
<div>
@@ -41,6 +41,7 @@ export function RegistryFilters({ filters, onUpdate }: RegistryFiltersProps) {
<SelectItem value="all">Toate</SelectItem>
<SelectItem value="intrat">Intrat</SelectItem>
<SelectItem value="iesit">Ieșit</SelectItem>
<SelectItem value="intern">Intern</SelectItem>
</SelectContent>
</Select>
@@ -74,6 +75,7 @@ export function RegistryFilters({ filters, onUpdate }: RegistryFiltersProps) {
<SelectItem value="all">Toate</SelectItem>
<SelectItem value="deschis">Deschis</SelectItem>
<SelectItem value="inchis">Închis</SelectItem>
<SelectItem value="reserved">Rezervat</SelectItem>
</SelectContent>
</Select>
@@ -82,7 +82,7 @@ const COLUMNS: ColumnDef[] = [
id: "direction",
label: "Dir.",
tooltip:
"Direcție: Intrat = primit de la terți, Ieșit = trimis către terți",
"Direcție: Intrat = primit, Ieșit = trimis, Intern = intern",
defaultVisible: true,
},
{
@@ -141,6 +141,7 @@ const STORAGE_KEY = "registratura:visible-columns";
const DIRECTION_LABELS: Record<string, string> = {
intrat: "Intrat",
iesit: "Ieșit",
intern: "Intern",
};
function getDocTypeLabel(type: string): string {
@@ -152,6 +153,7 @@ function getDocTypeLabel(type: string): string {
const STATUS_LABELS: Record<string, string> = {
deschis: "Deschis",
inchis: "Închis",
reserved: "Rezervat",
};
function loadVisibleColumns(): Set<ColumnId> {
@@ -327,7 +329,11 @@ export function RegistryTable({
<td className="px-3 py-2">
<Badge
variant={
entry.direction === "intrat" ? "default" : "secondary"
entry.direction === "intrat"
? "default"
: entry.direction === "intern"
? "outline"
: "secondary"
}
className="text-xs"
>
@@ -424,9 +430,16 @@ export function RegistryTable({
<td className="px-3 py-2">
<Badge
variant={
entry.status === "deschis" ? "default" : "outline"
entry.status === "deschis"
? "default"
: entry.status === "reserved"
? "secondary"
: "outline"
}
className="text-xs"
className={cn(
"text-xs",
entry.status === "reserved" && "border-dashed",
)}
>
{STATUS_LABELS[entry.status]}
</Badge>