feat: external status monitor for registratura (Primaria Cluj-Napoca)

- Add ExternalStatusTracking types + ExternalDocStatus semantic states
- Authority catalog with Primaria Cluj-Napoca (POST scraper + HTML parser)
- Status check service: batch + single entry, change detection via hash
- API routes: cron-triggered batch (/api/registratura/status-check) +
  user-triggered single (/api/registratura/status-check/single)
- Add "status-change" notification type with instant email on change
- Table badge: Radio icon color-coded by status (amber/blue/green/red)
- Detail panel: full monitoring section with status, history, manual check
- Auto-detection: prompt when recipient matches known authority
- Activation dialog: configure petitioner name + confirm registration data

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
AI Assistant
2026-03-11 14:42:21 +02:00
parent 1c51236c31
commit d7bd1a7f5d
10 changed files with 1201 additions and 6 deletions
@@ -16,6 +16,7 @@ import {
Check,
ArrowDownLeft,
ArrowUpRight,
Radio,
} from "lucide-react";
import { Button } from "@/shared/components/ui/button";
import { Badge } from "@/shared/components/ui/badge";
@@ -34,7 +35,7 @@ import {
DropdownMenuTrigger,
} from "@/shared/components/ui/dropdown-menu";
import type { RegistryEntry } from "../types";
import { DEFAULT_DOC_TYPE_LABELS } from "../types";
import { DEFAULT_DOC_TYPE_LABELS, EXTERNAL_STATUS_LABELS } from "../types";
import { getOverdueDays } from "../services/registry-service";
import { cn } from "@/shared/lib/utils";
@@ -383,6 +384,31 @@ export function RegistryTable({
{(entry.trackedDeadlines ?? []).length}
</Badge>
)}
{entry.externalStatusTracking?.active && (
<Tooltip>
<TooltipTrigger asChild>
<Badge
variant="outline"
className={`text-[10px] px-1 py-0 ${
entry.externalStatusTracking.semanticStatus === "solutionat"
? "border-green-400 text-green-600"
: entry.externalStatusTracking.semanticStatus === "trimis"
? "border-blue-400 text-blue-600"
: entry.externalStatusTracking.semanticStatus === "in-operare"
? "border-amber-400 text-amber-600"
: entry.externalStatusTracking.semanticStatus === "respins"
? "border-red-400 text-red-600"
: "border-muted-foreground"
}`}
>
<Radio className="mr-0.5 inline h-2.5 w-2.5" />
</Badge>
</TooltipTrigger>
<TooltipContent>
Status extern: {EXTERNAL_STATUS_LABELS[entry.externalStatusTracking.semanticStatus]}
</TooltipContent>
</Tooltip>
)}
</span>
</td>
)}