Layer catalog now has 3 actions per layer:
- Sync: downloads from eTerra, stores in PostgreSQL (GisFeature table),
incremental — only new OBJECTIDs fetched, removed ones deleted
- GPKG: direct download from eTerra (existing behavior)
- Local export: generates GPKG from local DB (no eTerra needed)
New features:
- /api/eterra/export-local endpoint — builds GPKG from DB, ZIP for multi-layer
- /api/eterra/sync now uses session-based auth (no credentials in request)
- Category headers show both remote + local feature counts
- Each layer shows local DB count (violet badge) + last sync timestamp
- 'Export local' button in action bar when any layer has local data
- Sync progress message with auto-dismiss
DB schema already had GisFeature + GisSyncRun tables from prior work.
- CSV export: all fields properly quoted to prevent column misalignment
when values contain commas (e.g. nrTopo with multiple topo numbers)
- Layer catalog: 'Numara toate' button fetches feature count per layer
via /api/eterra/layers/summary (now supports session auth)
- Feature counts displayed as badges on each layer and category total
- 'Drumul de azi' section: persists today's layer counts in localStorage
grouped by SIRUTA with timestamps
- Always build from structured fields first (street, postalNo, building, locality)
- Fall back to addressDescription ONLY when no structured fields exist
- Support multiple addresses per immovable (joined with |)
- Deduplicate identical addresses
- Handle addressDescription as last-resort fallback
- Address: use street.dictionaryItem.name (Strada/Alee/etc) + street.name,
postalNo as house number, buildingEntryNo/FloorNo/UnitNo/SectionNo
for apartment details, locality.name, county.name
- Area+intravilan: fetch from /api/immovable/details/parcels/list (direct
endpoint with area, intravilan, useCategory) before trying immApps
- Owners: remove strikethrough, use smaller neutral font (text-[11px]
text-muted-foreground/80), rename label to 'Proprietari anteriori'
- Area: use measuredArea/legalArea from immovable list and documentation
(actual fields from eTerra API, not area/areaValue which don't exist)
- Owners: detect radiated via nodeStatus === -1 on ancestor I (inscription)
nodes. Walk up parentId tree from P (person) I A C.
nodeStatus: -1=radiated, 0=active, 2=pending
- Remove debug logging (data structure now understood)
- Address: handle street/locality/county as objects (extract .name)
Fixes 'Str. [object Object], Feleacu' 'Str. X, Feleacu'
- Suprafata: fallback to total area from folosinte endpoint when
immovable list and documentation APIs return null
- Owners: use tree traversal (nodeId/parentNodeId) to detect radiated
inscriptions. Walk up parent chain to check radiationDate/cancelled/
isActive/closed/status on ancestor inscription nodes.
- Enhanced logging: first/last 3 partTwoRegs entries + node types
for debugging owner structure in Dozzle
- Proprietari split into proprietariActuali + proprietariVechi (radiati)
based on cancelled/isActive/radiat/status/radiationDate fields
- UI shows owners separated: actuali bold, vechi strikethrough
- CSV export has separate PROPRIETARI_ACTUALI / PROPRIETARI_VECHI columns
- Address: use addressDescription directly when present (>3 chars)
- Add county to address fallback
- Try area/areaValue/areaMP/suprafata fields for surface
- Debug logging: log immovable item keys + partTwoRegs sample on first search
- resolveWorkspace: use listLayer() instead of listLayerByWhere() with
hardcoded field names. Auto-discovers admin field (ADMIN_UNIT_ID/SIRUTA)
from ArcGIS layer metadata via buildWhere().
- resolveWorkspace: persist WORKSPACE_ID to DB on first resolution for
fast subsequent lookups.
- UATs POST: seed from uat.json (correct SIRUTA codes) instead of eTerra
nomenclature API (nomenPk != SIRUTA, county nomenPk != WORKSPACE_ID).
- Remove eTerra nomenclature dependency from UATs endpoint.
- Fix activeJobs Set iteration error on container restart.
- Remove unused enrichedUatsFetched ref.
- Add fetchNomenByPk() to EterraClient single nomen entry lookup
- resolveWorkspace() now tries fast path first: direct nomen lookup for
SIRUTA walk parentNomenPk chain to find COUNTY (1-3 calls vs 42+)
- Falls back to full county scan only if direct lookup fails
- Search route: DB lookup as middle fallback between workspacePk and resolve
- Debug logging to trace workspace resolution on production
- Fix: try all possible UAT identifier fields (nomenPk, siruta, code, pk)
- 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
- New /api/eterra/uats endpoint fetches all counties + UATs from eTerra,
caches server-side for 1 hour, returns enriched data with county name
and workspacePk for each UAT
- When eTerra is connected, auto-fetches enriched UAT list (replaces
static uat.json fallback) shows 'FELEACU (57582), CLUJ' format
- UAT autocomplete now searches both UAT name and county name
- Selected UAT stores workspacePk in state, passes it directly to
/api/eterra/search eliminates slow per-search county resolution
- Search route accepts optional workspacePk, falls back to resolveWorkspace()
- Dropdown shows UAT name, SIRUTA code, and county prominently
- Increased autocomplete results from 8 to 12 items
Search tab now uses eTerra application API (same as the web UI):
- POST /api/eterra/search queries /api/immovable/list with exact
identifierDetails filter + /api/documentation/data for full details
- Returns: nr cad, nr CF, CF vechi, nr topo, suprafata, intravilan,
categorii folosinta, adresa, proprietari, solicitant
- Automatic workspace (county) resolution from SIRUTA with cache
- Support for multiple cadastral numbers (comma separated)
UI changes:
- Detail cards instead of flat ArcGIS feature table
- Copy details to clipboard button per parcel
- Add parcels to list + CSV export
- Search list with summary table + CSV download
- No more layer filter or pagination (not needed for app API)
New EterraClient methods:
- searchImmovableByIdentifier (exact cadaster lookup)
- fetchCounties / fetchAdminUnitsByCounty (workspace resolution)
- Add /api/eterra/search queries eTerra ArcGIS REST API directly
by NATIONAL_CADASTRAL_REFERENCE, NATIONAL_CADNR, or INSPIRE_ID
across TERENURI_ACTIVE + CLADIRI_ACTIVE layers
- Search tab now queries eTerra live (not local DB) with 600ms debounce
- Requires session connected + UAT selected to search
- Updated placeholder and empty state messages in Romanian
- Add session-store.ts: global singleton for shared eTerra session state
with job tracking (registerJob/unregisterJob/getRunningJobs)
- Add GET/POST /api/eterra/session: connect/disconnect with job-running guard
- Export routes: credential fallback chain (body > session > env vars),
register/unregister active jobs for disconnect protection
- Login route: also creates server-side session
- ConnectionPill: session-aware display with job count, no credential form
- Auto-connect: triggers on first UAT keystroke via autoConnectAttempted ref
- Session polling: all clients poll GET /api/eterra/session every 30s
- Multi-client: any browser sees shared connection state
- Add visual-copilot module (iframe embed, env: NEXT_PUBLIC_VIM_URL)
- Sidebar collapse to icon-only with localStorage persistence
- Tooltips on collapsed nav items
- Full-viewport layout for canvas routes (/visual-copilot)
- Register module in modules.ts + feature flag in flags.ts
- Registratura v0.4.0 with QuickLook preview description
- New task 3.03d: QuickLook + bug fixes (NAS links, overflow, vault filter)
- Session log updated with all fixes and new features
- attachment-preview.tsx added to key files list
Radix ScrollArea Viewport allows horizontal scroll by default, causing
NAS attachment cards to extend beyond the sheet edge. Override viewport
overflow-x to hidden and move px-6 padding inside the content div so
the viewport constrains content width properly.
- Detail sheet: add overflow-hidden to ScrollArea content wrapper so
NAS attachment cards with long paths don't push badges/copy icon
beyond the sheet boundary
- Password vault: reset category filter to 'all' and clear search
after adding/editing an entry so user sees the new entry immediately
instead of landing on a filtered empty view
Add overflow-hidden to card container, min-w-0 to flex row, and
truncate to path text div so long NAS paths don't push badges
and copy icon outside the visible area.
Browsers block file:/// URLs from web pages for security. Changed:
- Detail sheet: click on NAS attachment copies path to clipboard with
'Copiat!' green badge feedback. Full UNC path shown below.
- Entry form: NAS link click = copy path (removed window.open fallback)
- Removed unused toFileUrl/toFileUrlByIp imports from form
- User pastes in Explorer address bar to open the file
- New nas-paths.ts config: drive mappings, UNC normalization, file:/// URL builder
- RegistryAttachment type extended with optional networkPath field
- 'Link NAS' button in attachment section opens inline path input
- Network path entries shown with blue HardDrive icon + NAS badge
- Click opens in Explorer via file:/// URL, copy path button on hover
- P:\ auto-converted to \\newamun\Proiecte UNC path
- Short display path shows share + last 2 segments
- Validation: warns if path doesn't match known NAS mappings
Replace placeholder WiFi string display with actual QR code rendered
on canvas via 'qrcode' library. Dialog now shows scannable QR image
with copy-image and download-PNG buttons.
Registratura improvements:
- Thread Explorer: new 'Fire conversatie' tab with timeline view, search, stats, gap tracking (la noi/la institutie), export to text report
- Interactive I/O toggle: replaced direction dropdown with visual blue/orange button group (Intrat/Iesit with icons)
- Doc type UX: alphabetical sort + immediate selection after adding custom type
- AC Validity Tracker: full Autorizatie de Construire lifecycle workflow (12mo validity, execution phases, extension request, required docs checklist, monthly reminders, abandonment/expiry tracking)
Password Vault rework (renamed to 'Parole Uzuale' v0.3.0):
- New categories: WiFi, Portale Primarii, Avize Online, PIN Semnatura, Software, Hardware (replaced server/database/api)
- Category icons (lucide-react) throughout list and form
- WiFi QR code dialog with connection string copy
- Context-aware form (PIN vs password label, hide email for WiFi/PIN, hide URL for WiFi, hide generator for PIN)
- Dynamic stat cards showing top 3 categories by count
- Removed encryption banner
- Updated i18n, flags, config
- ManicTime parser service: parse/serialize Tags.txt format, classify lines into project/phase/activity
- API route /api/manictime: GET (read + sync plan), POST (pull/push/both with backup versioning)
- ManicTimeSyncPanel component: connection check, stats grid, import/export/full sync with confirmation dialog
- Integrated into Tag Manager module with live sync status
- Docker: MANICTIME_TAGS_PATH env var, SMB volume mount /mnt/manictime
- Hierarchy validation: project codes, company association, duplicate detection
- Version bump to 0.2.0
- Created src/core/crypto/ with AES-256-GCM encrypt/decrypt (PBKDF2 key derivation)
- Created /api/vault route: CRUD with server-side password encryption
- PATCH /api/vault migration endpoint to re-encrypt legacy plaintext passwords
- Rewrote use-vault hook to use dedicated /api/vault instead of generic storage
- Updated UI: amber 'not encrypted' warning green 'encrypted' badge
- Added ENCRYPTION_SECRET env var to docker-compose.yml and stack.env
- Module version bumped to 0.2.0
- Renamed from 'Sabloane Word' to 'Biblioteca Sabloane' (Template Library)
- Multi-format support: Word, Excel, PDF, DWG, Archicad with auto-detection
- Auto versioning: 'Revizie Noua' button archives current version, bumps semver
- Version history dialog: browse and download any previous version
- Simplified UX: file upload vs external link toggle, auto-detect placeholders
silently for .docx, hide placeholders section for non-Word formats
- File type icons: distinct icons for docx, xlsx, archicad, dwg, pdf
- Updated stats cards: Word/Excel count, DWG/Archicad count, versioned count
- Backward compatible: old entries without fileType/versionHistory get defaults
- AD prefill: 'Din cont' button pre-fills name + company from Authentik session
- Logo size slider: 50%-200% scale control in Stil & Aranjare section
- Promotional banner: configurable image+link below signature with preview
- US/SDT custom graphics: dedicated dash (US) and dot (SDT) decorative icons
replacing Beletage's grey/accent slashes for company-specific branding
- Remove 'initials' type, expirationDate, legalStatus, usageNotes fields
- Add subcategory field with creatable input + 6 default categories
- Add TIFF upload support with client-side utif2 decode for preview
- Store original TIFF data separately for faithful downloads
- Add download dropdown: Original file, Word (.docx), PDF (.pdf)
- Group assets by subcategory in list view
- Add subcategory filter in search bar
- Install docx, jspdf, utif2 packages
- Closes 3.07
The client-side migration was downloading 25-50MB of base64 data to the
browser before showing anything. getAllEntries also lost its lightweight flag.
Fix:
- New POST /api/storage/migrate-blobs endpoint runs entirely server-side
(loads entries one-at-a-time from PostgreSQL, never sends heavy data to browser)
- Restore lightweight:true on getAllEntries (strips remaining base64 in API)
- Migration fires on mount (fire-and-forget) while list loads independently
- Remove client-side migrateEntryBlobs function
Replace complex prisma.\ with simple Prisma findMany + JS stripping.
Now that entries are inherently small (base64 in separate blob namespace),
JS-based stripping is instant. Also fix migration to check flag before loading.
Root cause: even with SQL-level stripping, PostgreSQL must TOAST-decompress
entire multi-MB JSONB values from disk before any processing. For 5 entries
with PDF attachments (25-50MB total), this takes several seconds.
Fix: store base64 attachment data in separate namespace 'registratura-blobs'.
Main entries are inherently small (~1-2KB). List queries never touch heavy data.
Changes:
- registry-service.ts: extractBlobs/mergeBlobs split base64 on save/load,
migrateEntryBlobs() one-time migration for existing entries
- use-registry.ts: dual namespace (registratura + registratura-blobs),
migration runs on first mount
- registratura-module.tsx: removed useContacts/useTags hooks that triggered
2 unnecessary API fetches on page load (write-only ops use direct storage)
Before: 3 API calls on mount, one reading 25-50MB from PostgreSQL
After: 1 API call on mount, reading ~5-10KB total
Previous fix stripped data in Node.js AFTER Prisma loaded the full JSON
from PostgreSQL. For 5 entries with PDF attachments, this still meant
25-50MB transferring from DB to Node.js on every page load.
Now uses prisma.\ with nested jsonb_each/jsonb_object_agg to
strip data/fileData/imageUrl strings >1KB inside the database itself.
Heavy base64 never leaves PostgreSQL when lightweight=true.