fix(parcel-sync): replace Unicode escapes with actual Romanian diacritics

The \u0103, \u00ee etc. escape sequences were rendering literally in JSX
text nodes instead of displaying ă, î, ț, ș characters.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
AI Assistant
2026-03-24 15:24:11 +02:00
parent d48a2bbf5d
commit 2b8d144924
8 changed files with 167 additions and 167 deletions
@@ -77,11 +77,11 @@ export function ConnectionPill({
)}
<span className="hidden sm:inline">
{connecting
? "Se conecteaz\u0103\u2026"
? "Se conectează…"
: session.connected
? "eTerra"
: session.eterraMaintenance
? "Mentenan\u021b\u0103"
? "Mentenanță"
: connectionError
? "Eroare"
: "Deconectat"}
@@ -128,11 +128,11 @@ export function ConnectionPill({
<AlertTriangle className="h-3.5 w-3.5 text-amber-500 mt-0.5 shrink-0" />
<div>
<p className="font-medium text-amber-700 dark:text-amber-400">
eTerra este \u00een mentenan\u021b\u0103
eTerra este în mentenanță
</p>
<p className="mt-1 text-[11px] text-amber-600/80 dark:text-amber-400/70">
Platforma ANCPI nu este disponibil\u0103 momentan. Conectarea va fi
reactivat\u0103 automat c\u00e2nd serviciul revine online.
Platforma ANCPI nu este disponibilă momentan. Conectarea va fi
reactivată automat când serviciul revine online.
</p>
{session.eterraHealthMessage && (
<p className="mt-1 text-[10px] opacity-60 font-mono">
@@ -149,9 +149,9 @@ export function ConnectionPill({
!connectionError &&
!session.eterraMaintenance && (
<div className="px-3 py-3 text-xs text-muted-foreground">
<p>Conexiunea se face automat c\u00e2nd \u00eencepi s\u0103 scrii un UAT.</p>
<p>Conexiunea se face automat când începi scrii un UAT.</p>
<p className="mt-1 text-[11px] opacity-70">
Creden\u021bialele sunt preluate din configurarea serverului.
Credențialele sunt preluate din configurarea serverului.
</p>
</div>
)}
@@ -162,7 +162,7 @@ export function ConnectionPill({
!session.eterraMaintenance && (
<div className="px-3 py-3 text-xs text-muted-foreground">
<p>
Conexiunea automat\u0103 a e\u0219uat. Verific\u0103 creden\u021bialele din
Conexiunea automată a eșuat. Verifică credențialele din
variabilele de mediu (ETERRA_USERNAME / ETERRA_PASSWORD).
</p>
</div>
@@ -182,7 +182,7 @@ export function ConnectionPill({
{session.activeJobPhase && (
<span className="opacity-70">
{" "}
\u2014 {session.activeJobPhase}
{session.activeJobPhase}
</span>
)}
</p>
@@ -194,14 +194,14 @@ export function ParcelSyncModule() {
...prev,
eterraMaintenance: true,
eterraAvailable: false,
eterraHealthMessage: data.error ?? "eTerra \u00een mentenan\u021b\u0103",
eterraHealthMessage: data.error ?? "eTerra în mentenanță",
}));
autoConnectAttempted.current = false;
} else {
setConnectionError(data.error ?? "Eroare conectare");
}
} catch {
setConnectionError("Eroare re\u021bea");
setConnectionError("Eroare rețea");
}
setConnecting(false);
}, [session.connected, session.eterraMaintenance, connecting, fetchSession]);
@@ -222,7 +222,7 @@ export function ParcelSyncModule() {
setConnectionError(data.error ?? "Nu se poate deconecta");
}
} catch {
setConnectionError("Eroare re\u021bea");
setConnectionError("Eroare rețea");
}
}, []);
@@ -282,7 +282,7 @@ export function ParcelSyncModule() {
<MapPin className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground pointer-events-none" />
<Input
id="uat-search"
placeholder="Selecteaz\u0103 UAT \u2014 scrie nume sau cod SIRUTA\u2026"
placeholder="Selectează UAT — scrie nume sau cod SIRUTA"
value={uatQuery}
onChange={(e) => {
setUatQuery(e.target.value);
@@ -329,7 +329,7 @@ export function ParcelSyncModule() {
<span className="text-muted-foreground">({item.siruta})</span>
{item.county && (
<span className="text-muted-foreground">
{"\u2013 "}
{" "}
<span className="font-medium text-foreground/70">
jud. {item.county}
</span>
@@ -363,7 +363,7 @@ export function ParcelSyncModule() {
<TabsList>
<TabsTrigger value="search" className="gap-1.5">
<Search className="h-4 w-4" />
C\u0103utare Parcele
Căutare Parcele
</TabsTrigger>
<TabsTrigger value="layers" className="gap-1.5">
<Layers className="h-4 w-4" />
@@ -85,7 +85,7 @@ export const normalizeText = (text: string) =>
.trim();
export function formatDate(iso?: string | null) {
if (!iso) return "\u2014";
if (!iso) return "";
return new Date(iso).toLocaleDateString("ro-RO", {
day: "2-digit",
month: "2-digit",
@@ -96,13 +96,13 @@ export function formatDate(iso?: string | null) {
}
export function formatArea(val?: number | null) {
if (val == null) return "\u2014";
if (val == null) return "";
return val.toLocaleString("ro-RO", { maximumFractionDigits: 2 }) + " mp";
}
/** Format ISO date as DD.MM.YYYY (no time) */
export function formatShortDate(iso?: string | null) {
if (!iso) return "\u2014";
if (!iso) return "";
const d = new Date(iso);
const dd = String(d.getDate()).padStart(2, "0");
const mm = String(d.getMonth() + 1).padStart(2, "0");
@@ -110,7 +110,7 @@ export function formatShortDate(iso?: string | null) {
}
export function relativeTime(date: Date | null) {
if (!date) return "niciodat\u0103";
if (!date) return "niciodată";
const mins = Math.floor((Date.now() - date.getTime()) / 60_000);
if (mins < 1) return "acum";
if (mins < 60) return `acum ${mins} min`;
@@ -52,7 +52,7 @@ export function DatabaseTab({
<Card>
<CardContent className="py-12 text-center text-muted-foreground">
<Loader2 className="h-8 w-8 mx-auto mb-3 animate-spin opacity-40" />
<p>Se \u00eencarc\u0103 datele din baza de date\u2026</p>
<p>Se încarcă datele din baza de date</p>
</CardContent>
</Card>
);
@@ -64,9 +64,9 @@ export function DatabaseTab({
<Card>
<CardContent className="py-12 text-center text-muted-foreground">
<Database className="h-10 w-10 mx-auto mb-3 opacity-30" />
<p className="font-medium">Nicio dat\u0103 \u00een baza de date</p>
<p className="font-medium">Nicio dată în baza de date</p>
<p className="text-xs mt-1">
Folose\u0219te tab-ul Export pentru a sincroniza date din eTerra.
Folosește tab-ul Export pentru a sincroniza date din eTerra.
</p>
</CardContent>
</Card>
@@ -81,7 +81,7 @@ export function DatabaseTab({
<div className="flex items-center gap-2">
<Database className="h-4 w-4 text-muted-foreground" />
<span className="text-sm font-medium">
{dbSummary.totalFeatures.toLocaleString("ro-RO")} entit\u0103\u021bi
{dbSummary.totalFeatures.toLocaleString("ro-RO")} entități
</span>
<span className="text-xs text-muted-foreground">
din {dbSummary.totalUats} UAT-uri
@@ -99,7 +99,7 @@ export function DatabaseTab({
) : (
<RefreshCw className="h-3 w-3 mr-1" />
)}
Re\u00eencarc\u0103
Reîncarcă
</Button>
</div>
@@ -169,7 +169,7 @@ export function DatabaseTab({
Dashboard
</Button>
<span className="text-xs text-muted-foreground">
{oldestSync ? relativeTime(oldestSync) : "\u2014"}
{oldestSync ? relativeTime(oldestSync) : ""}
</span>
</span>
</div>
@@ -212,7 +212,7 @@ export function DatabaseTab({
{noGeomTotal > 0 && (
<span className="inline-flex items-center gap-1">
<span className="text-muted-foreground">
F\u0103r\u0103 geom:
Fără geom:
</span>
<span className="font-medium tabular-nums text-amber-600 dark:text-amber-400">
{noGeomTotal.toLocaleString("ro-RO")}
@@ -239,7 +239,7 @@ export function DatabaseTab({
? "border-teal-200 bg-teal-50/50 dark:border-teal-800 dark:bg-teal-950/30"
: "border-muted bg-muted/30",
)}
title={`${label}: ${layer.count} entit\u0103\u021bi${isEnriched ? `, ${layer.enrichedCount} \u00eembog\u0103\u021bite` : ""}${layer.lastSynced ? `, sync: ${new Date(layer.lastSynced).toLocaleDateString("ro-RO")}` : ""}`}
title={`${label}: ${layer.count} entități${isEnriched ? `, ${layer.enrichedCount} îmbogățite` : ""}${layer.lastSynced ? `, sync: ${new Date(layer.lastSynced).toLocaleDateString("ro-RO")}` : ""}`}
>
<span className="truncate max-w-[120px]">
{label}
@@ -244,7 +244,7 @@ export function ExportTab({
phase: "Finalizat",
downloaded: prev.total ?? 100,
total: prev.total ?? 100,
message: `Desc\u0103rcare complet\u0103 \u2014 ${filename}`,
message: `Descărcare completă — ${filename}`,
note: undefined,
}
: null,
@@ -365,7 +365,7 @@ export function ExportTab({
err instanceof DOMException && err.name === "AbortError";
if (isTimeout) {
console.warn(
"[no-geom-scan] Timeout after 2 min \u2014 server eTerra lent",
"[no-geom-scan] Timeout after 2 min server eTerra lent",
);
}
setNoGeomScan({
@@ -469,7 +469,7 @@ export function ExportTab({
} else if (data.status === "done") {
setBgJobId(saved.jobId);
setBgProgress(data);
if (data.phase) setBgPhaseTrail(["Sincronizare complet\u0103"]);
if (data.phase) setBgPhaseTrail(["Sincronizare completă"]);
localStorage.removeItem("parcel-sync:bg-job");
} else {
localStorage.removeItem("parcel-sync:bg-job");
@@ -532,7 +532,7 @@ export function ExportTab({
}
startBgPolling(jid);
} catch (error) {
const msg = error instanceof Error ? error.message : "Eroare re\u021bea";
const msg = error instanceof Error ? error.message : "Eroare rețea";
console.warn("[sync-background]", msg);
}
},
@@ -573,7 +573,7 @@ export function ExportTab({
URL.revokeObjectURL(url);
} catch (error) {
const msg =
error instanceof Error ? error.message : "Eroare desc\u0103rcare";
error instanceof Error ? error.message : "Eroare descărcare";
console.warn("[download-from-db]", msg);
}
setDownloadingFromDb(false);
@@ -597,7 +597,7 @@ export function ExportTab({
<span className="font-medium text-foreground">
{dbTotalFeatures.toLocaleString("ro-RO")}
</span>{" "}
entit\u0103\u021bi \u00een DB din{" "}
entități în DB din{" "}
<span className="font-medium text-foreground">
{dbLayersSummary.length}
</span>{" "}
@@ -664,10 +664,10 @@ export function ExportTab({
)}
<div className="text-left">
<div className="font-semibold">
Descarc\u0103 Terenuri \u0219i Cl\u0103diri
Descarcă Terenuri și Clădiri
</div>
<div className="text-xs opacity-70 font-normal">
Sync + GPKG (din cache dac\u0103 e proasp\u0103t)
Sync + GPKG (din cache dacă e proaspăt)
</div>
</div>
</Button>
@@ -686,7 +686,7 @@ export function ExportTab({
<div className="text-left">
<div className="font-semibold">Magic</div>
<div className="text-xs opacity-70 font-normal">
Sync + \u00eembog\u0103\u021bire (CF, proprietari, adres\u0103) + GPKG + CSV
Sync + îmbogățire (CF, proprietari, adresă) + GPKG + CSV
</div>
</div>
</Button>
@@ -697,12 +697,12 @@ export function ExportTab({
{!session.connected ? (
<>
<Wifi className="h-10 w-10 mx-auto mb-3 opacity-30" />
<p>Conecteaz\u0103-te la eTerra pentru a activa exportul.</p>
<p>Conectează-te la eTerra pentru a activa exportul.</p>
</>
) : (
<>
<MapPin className="h-10 w-10 mx-auto mb-3 opacity-30" />
<p>Selecteaz\u0103 un UAT pentru a activa exportul.</p>
<p>Selectează un UAT pentru a activa exportul.</p>
</>
)}
</CardContent>
@@ -730,10 +730,10 @@ export function ExportTab({
<CardContent className="py-3 px-4">
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<Loader2 className="h-4 w-4 animate-spin text-amber-500" />
Se scaneaz\u0103 lista de imobile din eTerra\u2026 (max 2 min)
Se scanează lista de imobile din eTerra (max 2 min)
</div>
<p className="text-[11px] text-muted-foreground mt-1 ml-6">
Po\u021bi folosi butoanele de mai jos f\u0103r\u0103 s\u0103 a\u0219tep\u021bi scanarea.
Poți folosi butoanele de mai jos fără aștepți scanarea.
</p>
</CardContent>
</Card>
@@ -746,10 +746,10 @@ export function ExportTab({
<CardContent className="py-3 px-4">
<div className="flex items-center gap-2 text-sm text-amber-600 dark:text-amber-400">
<Clock className="h-4 w-4 shrink-0" />
Scanarea a dep\u0103\u0219it 2 minute \u2014 serverul eTerra e lent.
Scanarea a depășit 2 minute serverul eTerra e lent.
</div>
<p className="text-[11px] text-muted-foreground mt-1 ml-6">
Po\u021bi lansa sincronizarea fundal f\u0103r\u0103 rezultate de scanare.
Poți lansa sincronizarea fundal fără rezultate de scanare.
Include no-geom nu va fi disponibil.
</p>
<Button
@@ -759,7 +759,7 @@ export function ExportTab({
onClick={() => void handleNoGeomScan()}
>
<RefreshCw className="h-3 w-3 mr-1" />
Re\u00eencearc\u0103 scanarea
Reîncearcă scanarea
</Button>
</CardContent>
</Card>
@@ -779,7 +779,7 @@ export function ExportTab({
<div className="flex items-center gap-1.5 flex-wrap text-[11px] text-muted-foreground">
<Database className="h-3 w-3 shrink-0" />
<span>
Baza de date local\u0103:{" "}
Baza de date locală:{" "}
<span className="font-medium text-foreground">
{noGeomScan.localDbWithGeom.toLocaleString("ro-RO")}
</span>{" "}
@@ -790,16 +790,16 @@ export function ExportTab({
<span className="font-medium text-amber-600 dark:text-amber-400">
{noGeomScan.localDbNoGeom.toLocaleString("ro-RO")}
</span>{" "}
f\u0103r\u0103 geometrie
fără geometrie
</>
)}
{noGeomScan.localDbEnriched > 0 && (
<>
{" \u00b7 "}
{" · "}
<span className="font-medium text-teal-600 dark:text-teal-400">
{noGeomScan.localDbEnriched.toLocaleString("ro-RO")}
</span>{" "}
\u00eembog\u0103\u021bite
îmbogățite
{staleEnrichment && (
<span className="text-orange-600 dark:text-orange-400">
{" "}
@@ -810,7 +810,7 @@ export function ExportTab({
)}
{noGeomScan.localSyncFresh && (
<span className="text-emerald-600 dark:text-emerald-400 ml-1">
(proasp\u0103t)
(proaspăt)
</span>
)}
</span>
@@ -819,9 +819,9 @@ export function ExportTab({
<div className="flex items-center gap-1.5 text-[11px] text-orange-600 dark:text-orange-400 ml-[18px]">
<AlertTriangle className="h-3 w-3 shrink-0" />
<span>
{staleCount.toLocaleString("ro-RO")} parcele au \u00eembog\u0103\u021bire
veche (lips\u0103 PROPRIETARI_VECHI). Vor fi re-\u00eembog\u0103\u021bite la
urm\u0103torul export Magic.
{staleCount.toLocaleString("ro-RO")} parcele au îmbogățire
veche (lipsă PROPRIETARI_VECHI). Vor fi re-îmbogățite la
următorul export Magic.
</span>
</div>
)}
@@ -832,11 +832,11 @@ export function ExportTab({
const workflowPreview = scanDone && (
<div className="mt-2 ml-7 space-y-0.5">
<p className="text-[11px] font-medium text-muted-foreground">
La ap\u0103sarea Magic, pa\u0219ii vor fi:
La apăsarea Magic, pașii vor fi:
</p>
<ol className="text-[11px] text-muted-foreground list-decimal ml-4 space-y-px">
<li>
{"Sync GIS \u2014 "}
{"Sync GIS "}
<span
className={cn(
"font-medium",
@@ -848,16 +848,16 @@ export function ExportTab({
>
{noGeomScan.localSyncFresh &&
noGeomScan.localDbWithGeom > 0
? "skip (date proaspete \u00een DB)"
: `descarc\u0103 ${noGeomScan.remoteGisCount.toLocaleString("ro-RO")} terenuri` +
? "skip (date proaspete în DB)"
: `descarcă ${noGeomScan.remoteGisCount.toLocaleString("ro-RO")} terenuri` +
(noGeomScan.remoteCladiriCount > 0
? ` + ${noGeomScan.remoteCladiriCount.toLocaleString("ro-RO")} cl\u0103diri`
? ` + ${noGeomScan.remoteCladiriCount.toLocaleString("ro-RO")} clădiri`
: "")}
</span>
</li>
{includeNoGeom && (
<li>
Import parcele f\u0103r\u0103 geometrie \u2014{" "}
Import parcele fără geometrie {" "}
<span className="font-medium text-amber-600 dark:text-amber-400">
{(() => {
const usefulNoGeom =
@@ -878,7 +878,7 @@ export function ExportTab({
</li>
)}
<li>
\u00cembog\u0103\u021bire CF, proprietari, adrese \u2014{" "}
\u00cembogățire CF, proprietari, adrese {" "}
<span className="font-medium text-teal-600 dark:text-teal-400">
{(() => {
// What will be in DB after sync + optional no-geom import:
@@ -900,12 +900,12 @@ export function ExportTab({
totalAfter - noGeomScan.localDbEnrichedComplete;
return remaining > 0
? `~${remaining.toLocaleString("ro-RO")} de procesat (~${Math.ceil((remaining * 0.25) / 60)} min)`
: "deja \u00eembog\u0103\u021bite";
: "deja îmbogățite";
})()}
</span>
</li>
<li>Generare GPKG + CSV</li>
<li>Comprimare ZIP + desc\u0103rcare</li>
<li>Comprimare ZIP + descărcare</li>
</ol>
</div>
);
@@ -939,10 +939,10 @@ export function ExportTab({
"ro-RO",
)}
</span>{" "}
cl\u0103diri
clădiri
</>
)}
{" \u00b7 "}
{" · "}
Lista imobile:{" "}
<span className="font-semibold">
{noGeomScan.totalImmovables.toLocaleString("ro-RO")}
@@ -956,11 +956,11 @@ export function ExportTab({
noGeomScan.remoteGisCount,
).toLocaleString("ro-RO")}
</span>
{" f\u0103r\u0103 geometrie)"}
{" fără geometrie)"}
</p>
<p className="text-[11px] text-muted-foreground mt-0.5">
Cele f\u0103r\u0103 geometrie exist\u0103 \u00een baza de date eTerra dar
nu au contur desenat \u00een layerul GIS.
Cele fără geometrie există în baza de date eTerra dar
nu au contur desenat în layerul GIS.
</p>
{localDbLine}
</div>
@@ -984,7 +984,7 @@ export function ExportTab({
className="h-4 w-4 rounded border-muted-foreground/30 accent-amber-600"
/>
<span className="text-sm font-medium">
Include \u0219i parcelele f\u0103r\u0103 geometrie la export
Include și parcelele fără geometrie la export
</span>
</label>
{/* Quality breakdown of no-geom items */}
@@ -992,7 +992,7 @@ export function ExportTab({
<div className="ml-7 p-2 rounded-md bg-muted/40 space-y-1">
<p className="text-[11px] font-medium text-muted-foreground">
Calitate date (din{" "}
{noGeomScan.noGeomCount.toLocaleString("ro-RO")} f\u0103r\u0103
{noGeomScan.noGeomCount.toLocaleString("ro-RO")} fără
geometrie):
</p>
<div className="grid grid-cols-2 gap-x-4 gap-y-0.5 text-[11px] text-muted-foreground">
@@ -1013,7 +1013,7 @@ export function ExportTab({
</span>
</span>
<span>
Cu nr. cad. pe h\u00e2rtie:{" "}
Cu nr. cad. pe hârtie:{" "}
<span className="font-medium text-foreground">
{noGeomScan.qualityBreakdown.withPaperCad.toLocaleString(
"ro-RO",
@@ -1021,7 +1021,7 @@ export function ExportTab({
</span>
</span>
<span>
Cu suprafa\u021b\u0103:{" "}
Cu suprafață:{" "}
<span className="font-medium text-foreground">
{noGeomScan.qualityBreakdown.withArea.toLocaleString(
"ro-RO",
@@ -1037,7 +1037,7 @@ export function ExportTab({
</span>
</span>
<span>
Cu carte funciar\u0103:{" "}
Cu carte funciară:{" "}
<span className="font-medium text-foreground">
{noGeomScan.qualityBreakdown.withLandbook.toLocaleString(
"ro-RO",
@@ -1056,7 +1056,7 @@ export function ExportTab({
</span>
{noGeomScan.qualityBreakdown.empty > 0 && (
<span>
Filtrate (f\u0103r\u0103 CF/inactive/f\u0103r\u0103 date):{" "}
Filtrate (fără CF/inactive/fără date):{" "}
<span className="font-semibold text-rose-600 dark:text-rose-400">
{noGeomScan.qualityBreakdown.empty.toLocaleString(
"ro-RO",
@@ -1070,9 +1070,9 @@ export function ExportTab({
{includeNoGeom && (
<p className="text-[11px] text-muted-foreground ml-7">
{noGeomScan.qualityBreakdown.empty > 0
? `Din ${noGeomScan.noGeomCount.toLocaleString("ro-RO")} f\u0103r\u0103 geometrie, ~${noGeomScan.qualityBreakdown.useful.toLocaleString("ro-RO")} vor fi importate (imobile electronice cu CF). ${noGeomScan.qualityBreakdown.empty.toLocaleString("ro-RO")} vor fi filtrate (f\u0103r\u0103 carte funciar\u0103, inactive sau f\u0103r\u0103 date).`
: "Vor fi importate \u00een DB \u0219i incluse \u00een CSV + Magic GPKG (coloana HAS_GEOMETRY=0/1)."}{" "}
\u00cen GPKG de baz\u0103 apar doar cele cu geometrie.
? `Din ${noGeomScan.noGeomCount.toLocaleString("ro-RO")} fără geometrie, ~${noGeomScan.qualityBreakdown.useful.toLocaleString("ro-RO")} vor fi importate (imobile electronice cu CF). ${noGeomScan.qualityBreakdown.empty.toLocaleString("ro-RO")} vor fi filtrate (fără carte funciară, inactive sau fără date).`
: "Vor fi importate în DB și incluse în CSV + Magic GPKG (coloana HAS_GEOMETRY=0/1)."}{" "}
\u00cen GPKG de bază apar doar cele cu geometrie.
</p>
)}
{workflowPreview}
@@ -1091,14 +1091,14 @@ export function ExportTab({
<CheckCircle2 className="h-3.5 w-3.5 text-emerald-500" />
Toate cele{" "}
{noGeomScan.totalImmovables.toLocaleString("ro-RO")}{" "}
imobile din eTerra au geometrie \u2014 nimic de importat
imobile din eTerra au geometrie nimic de importat
suplimentar.
{noGeomScan.localDbTotal > 0 && (
<span className="ml-1">
({noGeomScan.localDbTotal.toLocaleString("ro-RO")}{" "}
\u00een DB local
în DB local
{noGeomScan.localDbEnriched > 0 &&
`, ${noGeomScan.localDbEnriched.toLocaleString("ro-RO")} \u00eembog\u0103\u021bite`}
`, ${noGeomScan.localDbEnriched.toLocaleString("ro-RO")} îmbogățite`}
{noGeomScan.localDbEnriched > 0 &&
noGeomScan.localDbEnrichedComplete <
noGeomScan.localDbEnriched && (
@@ -1106,15 +1106,15 @@ export function ExportTab({
{` (${(noGeomScan.localDbEnriched - noGeomScan.localDbEnrichedComplete).toLocaleString("ro-RO")} incomplete)`}
</span>
)}
{noGeomScan.localSyncFresh && ", proasp\u0103t"})
{noGeomScan.localSyncFresh && ", proaspăt"})
</span>
)}
</>
) : (
<>
<AlertTriangle className="h-3.5 w-3.5 text-muted-foreground" />
Nu s-au g\u0103sit imobile \u00een lista eTerra pentru acest
UAT. Verific\u0103 sesiunea eTerra.
Nu s-au găsit imobile în lista eTerra pentru acest
UAT. Verifică sesiunea eTerra.
</>
)}
</div>
@@ -1133,10 +1133,10 @@ export function ExportTab({
<div className="flex items-center gap-2">
<HardDrive className="h-4 w-4 text-muted-foreground" />
<span className="text-sm font-medium">
Procesare fundal &amp; desc\u0103rcare din DB
Procesare fundal &amp; descărcare din DB
</span>
<span className="text-[10px] text-muted-foreground">
\u2014 porne\u0219te sincronizarea, \u00eenchide pagina, descarc\u0103 mai t\u00e2rziu
pornește sincronizarea, închide pagina, descarcă mai târziu
</span>
</div>
@@ -1154,11 +1154,11 @@ export function ExportTab({
className="h-4 w-4 rounded border-muted-foreground/30 accent-amber-600"
/>
<span className="text-xs">
Include \u0219i parcelele f\u0103r\u0103 geometrie
Include și parcelele fără geometrie
</span>
{noGeomScanning && (
<span className="text-[10px] text-muted-foreground">
(scanare \u00een curs\u2026)
(scanare în curs)
</span>
)}
</label>
@@ -1179,17 +1179,17 @@ export function ExportTab({
>
{bgJobId &&
bgProgress?.status === "running" &&
!bgPhaseTrail.some((p) => p.includes("\u00cembog\u0103\u021bire")) ? (
!bgPhaseTrail.some((p) => p.includes("\u00cembogățire")) ? (
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
) : (
<ArrowDownToLine className="mr-2 h-4 w-4" />
)}
<div className="text-left">
<div className="text-xs font-semibold">
Sync fundal \u2014 Baz\u0103
Sync fundal Bază
</div>
<div className="text-[10px] opacity-60 font-normal">
Terenuri + cl\u0103diri \u2192 salveaz\u0103 \u00een DB
Terenuri + clădiri \u2192 salvează în DB
</div>
</div>
</Button>
@@ -1205,17 +1205,17 @@ export function ExportTab({
>
{bgJobId &&
bgProgress?.status === "running" &&
bgPhaseTrail.some((p) => p.includes("\u00cembog\u0103\u021bire")) ? (
bgPhaseTrail.some((p) => p.includes("\u00cembogățire")) ? (
<Loader2 className="mr-2 h-4 w-4 animate-spin text-teal-600" />
) : (
<Sparkles className="mr-2 h-4 w-4 text-teal-600" />
)}
<div className="text-left">
<div className="text-xs font-semibold">
Sync fundal \u2014 Magic
Sync fundal Magic
</div>
<div className="text-[10px] opacity-60 font-normal">
Sync + \u00eembog\u0103\u021bire \u2192 salveaz\u0103 \u00een DB
Sync + îmbogățire \u2192 salvează în DB
</div>
</div>
</Button>
@@ -1239,10 +1239,10 @@ export function ExportTab({
)}
<div className="text-left">
<div className="text-xs font-semibold">
Descarc\u0103 din DB \u2014 Baz\u0103
Descarcă din DB Bază
</div>
<div className="text-[10px] opacity-60 font-normal">
GPKG terenuri + cl\u0103diri (instant, f\u0103r\u0103 eTerra)
GPKG terenuri + clădiri (instant, fără eTerra)
</div>
</div>
</Button>
@@ -1260,7 +1260,7 @@ export function ExportTab({
)}
<div className="text-left">
<div className="text-xs font-semibold">
Descarc\u0103 din DB \u2014 Magic
Descarcă din DB Magic
</div>
<div className="text-[10px] opacity-60 font-normal">
GPKG + CSV + raport calitate (instant)
@@ -1272,8 +1272,8 @@ export function ExportTab({
{!session.connected && dbTotalFeatures === 0 && (
<p className="text-xs text-muted-foreground ml-6">
Conecteaz\u0103-te la eTerra pentru a porni sincronizarea fundal,
sau sincronizeaz\u0103 mai \u00eent\u00e2i date \u00een baza local\u0103.
Conectează-te la eTerra pentru a porni sincronizarea fundal,
sau sincronizează mai întâi date în baza locală.
</p>
)}
</CardContent>
@@ -1302,7 +1302,7 @@ export function ExportTab({
</span>
{bgProgress.status === "running" && (
<span className="text-muted-foreground">
(po\u021bi \u00eenchide pagina)
(poți închide pagina)
</span>
)}
</div>
@@ -1396,7 +1396,7 @@ export function ExportTab({
) : (
<Database className="mr-1.5 h-3 w-3" />
)}
Descarc\u0103 din DB (Magic)
Descarcă din DB (Magic)
</Button>
<Button
variant="ghost"
@@ -1466,7 +1466,7 @@ export function ExportTab({
{exportProgress.phase}
{exportProgress.phaseCurrent != null &&
exportProgress.phaseTotal
? ` \u2014 ${exportProgress.phaseCurrent} / ${exportProgress.phaseTotal}`
? ` ${exportProgress.phaseCurrent} / ${exportProgress.phaseTotal}`
: ""}
</p>
{exportProgress.note && (
@@ -239,7 +239,7 @@ export function LayersTab({
async (layerId: string) => {
if (!siruta || syncingLayer) return;
setSyncingLayer(layerId);
setSyncProgress("Sincronizare pornit\u0103\u2026");
setSyncProgress("Sincronizare pornită…");
try {
const res = await fetch("/api/eterra/sync", {
method: "POST",
@@ -261,13 +261,13 @@ export function LayersTab({
setSyncProgress(`Eroare: ${data.error}`);
} else {
setSyncProgress(
`Finalizat \u2014 ${data.newFeatures ?? 0} noi, ${data.removedFeatures ?? 0} \u0219terse, ${data.totalLocal ?? 0} total local`,
`Finalizat ${data.newFeatures ?? 0} noi, ${data.removedFeatures ?? 0} șterse, ${data.totalLocal ?? 0} total local`,
);
// Refresh sync status
onSyncRefresh();
}
} catch {
setSyncProgress("Eroare re\u021bea");
setSyncProgress("Eroare rețea");
}
// Clear progress after 8s
setTimeout(() => {
@@ -334,7 +334,7 @@ export function LayersTab({
for (const layerId of layerIds) {
setSyncingLayer(layerId);
setSyncProgress(
`Sincronizare ${LAYER_CATALOG.find((l) => l.id === layerId)?.label ?? layerId}\u2026`,
`Sincronizare ${LAYER_CATALOG.find((l) => l.id === layerId)?.label ?? layerId}`,
);
try {
const res = await fetch("/api/eterra/sync", {
@@ -356,7 +356,7 @@ export function LayersTab({
setSyncProgress(`Eroare: ${data.error}`);
}
} catch {
setSyncProgress("Eroare re\u021bea");
setSyncProgress("Eroare rețea");
}
// Remove from queue
syncQueueRef.current = syncQueueRef.current.filter(
@@ -445,7 +445,7 @@ export function LayersTab({
phase: "Finalizat",
downloaded: prev.total ?? 100,
total: prev.total ?? 100,
message: `Desc\u0103rcare complet\u0103 \u2014 ${filename}`,
message: `Descărcare completă — ${filename}`,
note: undefined,
}
: null,
@@ -489,8 +489,8 @@ export function LayersTab({
<Layers className="h-10 w-10 mx-auto mb-3 opacity-30" />
<p>
{!session.connected
? "Conecteaz\u0103-te la eTerra \u0219i selecteaz\u0103 un UAT."
: "Selecteaz\u0103 un UAT pentru a vedea catalogul de layere."}
? "Conectează-te la eTerra și selectează un UAT."
: "Selectează un UAT pentru a vedea catalogul de layere."}
</p>
</CardContent>
</Card>
@@ -504,8 +504,8 @@ export function LayersTab({
<p className="text-xs text-muted-foreground">
{layerCountSiruta === siruta &&
Object.keys(layerCounts).length > 0
? `Num\u0103r features pentru SIRUTA ${siruta}`
: "Apas\u0103 pentru a num\u0103ra features-urile din fiecare layer."}
? `Număr features pentru SIRUTA ${siruta}`
: "Apasă pentru a număra features-urile din fiecare layer."}
</p>
<div className="flex items-center gap-2">
{/* Export from local DB */}
@@ -537,7 +537,7 @@ export function LayersTab({
) : (
<Search className="h-3.5 w-3.5 mr-1.5" />
)}
{countingLayers ? "Se num\u0103r\u0103\u2026" : "Num\u0103r\u0103"}
{countingLayers ? "Se numără…" : "Numără"}
</Button>
</div>
</div>
@@ -712,7 +712,7 @@ export function LayersTab({
void handleSyncLayer(layer.id)
}
className="border-violet-200 dark:border-violet-800"
title="Sincronizeaz\u0103 \u00een baza de date"
title="Sincronizează în baza de date"
>
{isSyncing ? (
<Loader2 className="h-3.5 w-3.5 animate-spin" />
@@ -733,8 +733,8 @@ export function LayersTab({
}
title={
localCount > 0
? "Descarc\u0103 GPKG (din cache dac\u0103 e proasp\u0103t)"
: "Sincronizeaz\u0103 + descarc\u0103 GPKG"
? "Descarcă GPKG (din cache dacă e proaspăt)"
: "Sincronizează + descarcă GPKG"
}
>
{isDownloading ? (
@@ -787,7 +787,7 @@ export function LayersTab({
<p className="text-[11px] font-semibold text-muted-foreground">
SIRUTA {sir}{" "}
<span className="font-normal opacity-70">
\u2014{" "}
{" "}
{new Date(entries[0]!.time).toLocaleTimeString(
"ro-RO",
{ hour: "2-digit", minute: "2-digit" },
@@ -845,7 +845,7 @@ export function LayersTab({
<Database className="h-3.5 w-3.5 mr-1.5" />
)}
{postgisRunning
? "Se activeaza\u2026"
? "Se activeaza"
: "Activeaza compatibilitate QGIS"}
</Button>
</TooltipTrigger>
@@ -871,7 +871,7 @@ export function LayersTab({
<div className="flex items-center gap-2">
<CheckCircle2 className="h-4 w-4 text-emerald-500" />
<span className="text-sm font-medium text-emerald-700 dark:text-emerald-400">
QGIS compatibil \u2014 gata de conectare
QGIS compatibil gata de conectare
</span>
</div>
<div className="rounded bg-muted/50 p-3 text-xs space-y-2">
@@ -946,7 +946,7 @@ export function LayersTab({
si limitelor UAT.
</p>
<p>
Apasa butonul pentru a activa \u2014 creeaza view-uri read-only
Apasa butonul pentru a activa creeaza view-uri read-only
(nu modifica datele, nu afecteaza performanta aplicatiei).
</p>
</div>
@@ -965,7 +965,7 @@ export function LayersTab({
{exportProgress.phase}
{exportProgress.phaseCurrent != null &&
exportProgress.phaseTotal
? ` \u2014 ${exportProgress.phaseCurrent} / ${exportProgress.phaseTotal}`
? ` ${exportProgress.phaseCurrent} / ${exportProgress.phaseTotal}`
: ""}
</p>
</div>
@@ -321,7 +321,7 @@ export function MapTab({ siruta, sirutaValid }: MapTabProps) {
<Card>
<CardContent className="py-12 text-center text-muted-foreground">
<MapIcon className="h-10 w-10 mx-auto mb-3 opacity-30" />
<p>Selecteaz\u0103 un UAT din lista de mai sus</p>
<p>Selectează un UAT din lista de mai sus</p>
</CardContent>
</Card>
);
@@ -332,7 +332,7 @@ export function MapTab({ siruta, sirutaValid }: MapTabProps) {
{boundsLoading && (
<div className="absolute top-3 left-1/2 -translate-x-1/2 z-20 flex items-center gap-2 rounded-full bg-background/90 border px-3 py-1.5 text-xs shadow-sm">
<Loader2 className="h-3 w-3 animate-spin" />
Se \u00eencarc\u0103 zona UAT...
Se încarcă zona UAT...
</div>
)}
@@ -376,7 +376,7 @@ export function MapTab({ siruta, sirutaValid }: MapTabProps) {
<div className="absolute bottom-3 right-3 z-10 rounded-lg bg-background/90 border p-2 text-[10px] space-y-1">
<div className="flex items-center gap-1.5">
<span className="inline-block h-3 w-3 rounded-sm border" style={{ backgroundColor: "rgba(134,239,172,0.25)", borderColor: "#15803d" }} />
F\u0103r\u0103 enrichment
Fără enrichment
</div>
<div className="flex items-center gap-1.5">
<span className="inline-block h-3 w-3 rounded-sm border" style={{ backgroundColor: "rgba(21,128,61,0.25)", borderColor: "#15803d" }} />
@@ -384,11 +384,11 @@ export function MapTab({ siruta, sirutaValid }: MapTabProps) {
</div>
<div className="flex items-center gap-1.5">
<span className="inline-block h-3 w-3 rounded-sm" style={{ border: "2px solid #3b82f6" }} />
Cu cl\u0103dire
Cu clădire
</div>
<div className="flex items-center gap-1.5">
<span className="inline-block h-3 w-3 rounded-sm" style={{ border: "2px solid #ef4444" }} />
Cl\u0103dire f\u0103r\u0103 acte
Clădire fără acte
</div>
</div>
</div>
@@ -123,7 +123,7 @@ export function SearchTab({
setSearchError("");
}
} catch {
setSearchError("Eroare de re\u021bea.");
setSearchError("Eroare de rețea.");
}
setLoadingFeatures(false);
}, [siruta, featuresSearch, workspacePk]);
@@ -178,12 +178,12 @@ export function SearchTab({
if (data.eterraNote) notes.push(data.eterraNote);
setOwnerNote(
notes.length > 0
? `Surse: ${notes.join(" + ")}${data.total ? ` \u00b7 ${data.total} rezultate` : ""}`
? `Surse: ${notes.join(" + ")}${data.total ? ` · ${data.total} rezultate` : ""}`
: "",
);
}
} catch {
setOwnerError("Eroare de re\u021bea.");
setOwnerError("Eroare de rețea.");
}
setOwnerLoading(false);
}, [siruta, ownerSearch, workspacePk]);
@@ -596,7 +596,7 @@ export function SearchTab({
<Card>
<CardContent className="py-12 text-center text-muted-foreground">
<Search className="h-10 w-10 mx-auto mb-3 opacity-30" />
<p>Selecteaz\u0103 un UAT mai sus pentru a c\u0103uta parcele.</p>
<p>Selectează un UAT mai sus pentru a căuta parcele.</p>
</CardContent>
</Card>
);
@@ -640,7 +640,7 @@ export function SearchTab({
<div className="flex gap-3 items-end">
<div className="space-y-1 flex-1">
<Label className="text-xs">
Numere cadastrale (separate prin virgul\u0103 sau Enter)
Numere cadastrale (separate prin virgulă sau Enter)
</Label>
<div className="relative">
<Search className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
@@ -655,8 +655,8 @@ export function SearchTab({
</div>
{!session.connected && (
<p className="text-xs text-muted-foreground">
Necesit\u0103 conexiune eTerra. Folose\u0219te modul Proprietar
pentru a c\u0103uta offline \u00een DB.
Necesită conexiune eTerra. Folosește modul Proprietar
pentru a căuta offline în DB.
</p>
)}
</div>
@@ -673,7 +673,7 @@ export function SearchTab({
) : (
<Search className="mr-2 h-4 w-4" />
)}
Caut\u0103
Caută
</Button>
</div>
)}
@@ -683,7 +683,7 @@ export function SearchTab({
<div className="flex gap-3 items-end">
<div className="space-y-1 flex-1">
<Label className="text-xs">
Nume proprietar (caut\u0103 \u00een DB local + eTerra)
Nume proprietar (caută în DB local + eTerra)
</Label>
<div className="relative">
<User className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
@@ -705,7 +705,7 @@ export function SearchTab({
) : (
<Search className="mr-2 h-4 w-4" />
)}
Caut\u0103
Caută
</Button>
</div>
)}
@@ -729,10 +729,10 @@ export function SearchTab({
<Card>
<CardContent className="py-12 text-center text-muted-foreground">
<Loader2 className="h-10 w-10 mx-auto mb-3 animate-spin opacity-50" />
<p>Se caut\u0103 \u00een eTerra...</p>
<p>Se caută în eTerra...</p>
<p className="text-xs mt-1 opacity-60">
Prima c\u0103utare pe un UAT nou poate dura ~10-30s (se
\u00eencarc\u0103 lista de jude\u021be).
Prima căutare pe un UAT nou poate dura ~10-30s (se
încarcă lista de județe).
</p>
</CardContent>
</Card>
@@ -747,7 +747,7 @@ export function SearchTab({
{searchResults.length > 1 ? "e" : ""}
{searchList.length > 0 && (
<span className="ml-2">
· <strong>{searchList.length}</strong> \u00een list\u0103
· <strong>{searchList.length}</strong> în listă
</span>
)}
</span>
@@ -761,7 +761,7 @@ export function SearchTab({
}}
>
<Plus className="mr-1 h-3.5 w-3.5" />
Adaug\u0103 toate \u00een list\u0103
Adaugă toate în listă
</Button>
)}
<Button
@@ -773,7 +773,7 @@ export function SearchTab({
}
>
<FileDown className="mr-1 h-3.5 w-3.5" />
Descarc\u0103 CSV
Descarcă CSV
</Button>
</div>
</div>
@@ -796,7 +796,7 @@ export function SearchTab({
</h3>
{!p.immovablePk && (
<p className="text-xs text-destructive">
Parcela nu a fost g\u0103sit\u0103 \u00een eTerra.
Parcela nu a fost găsită în eTerra.
</p>
)}
</div>
@@ -805,7 +805,7 @@ export function SearchTab({
size="sm"
variant="ghost"
className="h-7 w-7 p-0"
title="Adaug\u0103 \u00een list\u0103"
title="Adaugă în listă"
onClick={() => addToList(p)}
disabled={!p.immovablePk}
>
@@ -819,7 +819,7 @@ export function SearchTab({
onClick={() => {
const text = [
`Nr. Cad: ${p.nrCad}`,
`Nr. CF: ${p.nrCF || "\u2014"}`,
`Nr. CF: ${p.nrCF || ""}`,
p.nrCFVechi
? `CF vechi: ${p.nrCFVechi}`
: null,
@@ -829,7 +829,7 @@ export function SearchTab({
p.suprafata != null
? `Suprafata: ${p.suprafata.toLocaleString("ro-RO")} mp`
: null,
`Intravilan: ${p.intravilan || "\u2014"}`,
`Intravilan: ${p.intravilan || ""}`,
p.categorieFolosinta
? `Categorie: ${p.categorieFolosinta}`
: null,
@@ -870,7 +870,7 @@ export function SearchTab({
Nr. CF
</span>
<span className="font-medium">
{p.nrCF || "\u2014"}
{p.nrCF || ""}
</span>
</div>
{p.nrCFVechi && (
@@ -885,16 +885,16 @@ export function SearchTab({
<span className="text-xs text-muted-foreground block">
Nr. Topo
</span>
<span>{p.nrTopo || "\u2014"}</span>
<span>{p.nrTopo || ""}</span>
</div>
<div>
<span className="text-xs text-muted-foreground block">
Suprafa\u021b\u0103
Suprafață
</span>
<span className="tabular-nums">
{p.suprafata != null
? formatArea(p.suprafata)
: "\u2014"}
: ""}
</span>
</div>
<div>
@@ -911,13 +911,13 @@ export function SearchTab({
}
className="text-[11px]"
>
{p.intravilan || "\u2014"}
{p.intravilan || ""}
</Badge>
</div>
{p.categorieFolosinta && (
<div className="col-span-2">
<span className="text-xs text-muted-foreground block">
Categorii folosin\u021b\u0103
Categorii folosință
</span>
<span className="text-xs">
{p.categorieFolosinta}
@@ -927,7 +927,7 @@ export function SearchTab({
{p.adresa && (
<div className="col-span-2">
<span className="text-xs text-muted-foreground block">
Adres\u0103
Adresă
</span>
<span>{p.adresa}</span>
</div>
@@ -991,10 +991,10 @@ export function SearchTab({
<Card>
<CardContent className="py-12 text-center text-muted-foreground">
<Search className="h-10 w-10 mx-auto mb-3 opacity-30" />
<p>Introdu un num\u0103r cadastral \u0219i apas\u0103 Caut\u0103.</p>
<p>Introdu un număr cadastral și apasă Caută.</p>
<p className="text-xs mt-1 opacity-60">
Po\u021bi c\u0103uta mai multe parcele simultan, separate prin
virgul\u0103.
Poți căuta mai multe parcele simultan, separate prin
virgulă.
</p>
</CardContent>
</Card>
@@ -1009,9 +1009,9 @@ export function SearchTab({
<Card>
<CardContent className="py-12 text-center text-muted-foreground">
<Loader2 className="h-10 w-10 mx-auto mb-3 animate-spin opacity-50" />
<p>Se caut\u0103 proprietar...</p>
<p>Se caută proprietar...</p>
<p className="text-xs mt-1 opacity-60">
Caut\u0103 mai \u00eent\u00e2i \u00een DB local (date \u00eembog\u0103\u021bite), apoi pe
Caută mai întâi în DB local (date îmbogățite), apoi pe
eTerra.
</p>
</CardContent>
@@ -1036,7 +1036,7 @@ export function SearchTab({
}}
>
<Plus className="mr-1 h-3.5 w-3.5" />
Adaug\u0103 toate \u00een list\u0103
Adaugă toate în listă
</Button>
<Button
size="sm"
@@ -1047,7 +1047,7 @@ export function SearchTab({
}
>
<FileDown className="mr-1 h-3.5 w-3.5" />
Descarc\u0103 CSV
Descarcă CSV
</Button>
</div>
</div>
@@ -1075,7 +1075,7 @@ export function SearchTab({
size="sm"
variant="ghost"
className="h-7 w-7 p-0"
title="Adaug\u0103 \u00een list\u0103"
title="Adaugă în listă"
onClick={() =>
addToList(ownerResultToParcelDetail(r))
}
@@ -1128,7 +1128,7 @@ export function SearchTab({
{r.suprafata && (
<div>
<span className="text-xs text-muted-foreground block">
Suprafa\u021b\u0103
Suprafață
</span>
<span className="tabular-nums">
{typeof r.suprafata === "number"
@@ -1159,7 +1159,7 @@ export function SearchTab({
{r.categorieFolosinta && (
<div className="col-span-2">
<span className="text-xs text-muted-foreground block">
Categorii folosin\u021b\u0103
Categorii folosință
</span>
<span className="text-xs">
{r.categorieFolosinta}
@@ -1169,7 +1169,7 @@ export function SearchTab({
{r.adresa && (
<div className="col-span-2">
<span className="text-xs text-muted-foreground block">
Adres\u0103
Adresă
</span>
<span>{r.adresa}</span>
</div>
@@ -1206,12 +1206,12 @@ export function SearchTab({
<Card>
<CardContent className="py-12 text-center text-muted-foreground">
<User className="h-10 w-10 mx-auto mb-3 opacity-30" />
<p>Introdu numele proprietarului \u0219i apas\u0103 Caut\u0103.</p>
<p>Introdu numele proprietarului și apasă Caută.</p>
<p className="text-xs mt-1 opacity-60">
Caut\u0103 \u00een datele \u00eembog\u0103\u021bite (DB local) \u0219i pe eTerra.
Caută în datele îmbogățite (DB local) și pe eTerra.
<br />
Pentru rezultate complete, lanseaz\u0103 &quot;Sync fundal \u2014
Magic&quot; \u00een tab-ul Export.
Pentru rezultate complete, lansează &quot;Sync fundal
Magic&quot; în tab-ul Export.
</p>
</CardContent>
</Card>
@@ -1377,15 +1377,15 @@ export function SearchTab({
{p.nrCad}
</td>
<td className="px-3 py-2 text-xs">
{p.nrCF || "\u2014"}
{p.nrCF || ""}
</td>
<td className="px-3 py-2 text-right hidden sm:table-cell tabular-nums text-xs">
{p.suprafata != null
? formatArea(p.suprafata)
: "\u2014"}
: ""}
</td>
<td className="px-3 py-2 hidden md:table-cell text-xs truncate max-w-[300px]">
{p.proprietari || "\u2014"}
{p.proprietari || ""}
</td>
<td className="px-3 py-2 text-center">
<TooltipProvider>