feat(parcel-sync): store UATs in PostgreSQL, eliminate repeated eTerra calls

- GisUat table now includes workspacePk column (created via raw SQL)
- GET /api/eterra/uats serves from PostgreSQL  instant, no eTerra login needed
- POST /api/eterra/uats triggers sync check: compares county count with DB,
  only does full eTerra fetch if data differs or DB is empty
- Frontend loads UATs from DB on mount (fast), falls back to uat.json if empty
- On eTerra connect, fires POST to sync-check; if data changed, reloads from DB
- Workspace cache populated from DB on GET for search route performance
This commit is contained in:
AI Assistant
2026-03-06 20:56:12 +02:00
parent d948e5c1cf
commit ec5a866673
3 changed files with 165 additions and 55 deletions
@@ -325,11 +325,31 @@ export function ParcelSyncModule() {
}, []);
useEffect(() => {
// Load static UAT data as fallback
fetch("/uat.json")
// Load UATs from local DB (fast — no eTerra needed)
fetch("/api/eterra/uats")
.then((res) => res.json())
.then((data: UatEntry[]) => setUatData(data))
.catch(() => {});
.then(
(data: {
uats?: UatEntry[];
}) => {
if (data.uats && data.uats.length > 0) {
setUatData(data.uats);
} else {
// DB empty — fall back to static uat.json (no county/workspace)
fetch("/uat.json")
.then((res) => res.json())
.then((fallback: UatEntry[]) => setUatData(fallback))
.catch(() => {});
}
},
)
.catch(() => {
// API failed — fall back to static uat.json
fetch("/uat.json")
.then((res) => res.json())
.then((fallback: UatEntry[]) => setUatData(fallback))
.catch(() => {});
});
// Check existing server session on mount
void fetchSession();
@@ -342,33 +362,34 @@ export function ParcelSyncModule() {
}, [fetchSession]);
/* ════════════════════════════════════════════════════════════ */
/* Fetch enriched UAT list (with county + workspace) when */
/* connected to eTerra. Falls back to static uat.json. */
/* Sync UATs from eTerra → DB on connect (lightweight check) */
/* ════════════════════════════════════════════════════════════ */
useEffect(() => {
if (!session.connected || enrichedUatsFetched.current) return;
enrichedUatsFetched.current = true;
fetch("/api/eterra/uats")
// POST triggers sync check — only does full fetch if data changed
fetch("/api/eterra/uats", { method: "POST" })
.then((res) => res.json())
.then(
(data: {
uats?: {
siruta: string;
name: string;
county: string;
workspacePk: number;
}[];
}) => {
if (data.uats && data.uats.length > 0) {
setUatData(data.uats);
(data: { synced?: boolean }) => {
if (data.synced) {
// Data changed — reload from DB
fetch("/api/eterra/uats")
.then((res) => res.json())
.then(
(fresh: { uats?: UatEntry[] }) => {
if (fresh.uats && fresh.uats.length > 0) {
setUatData(fresh.uats);
}
},
)
.catch(() => {});
}
},
)
.catch(() => {
// Keep static uat.json data
});
.catch(() => {});
}, [session.connected]);
/* ════════════════════════════════════════════════════════════ */