- 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
- 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
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.
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.
ROOT CAUSE: RegistryEntry stores file attachments as base64 strings in JSON.
A single 5MB PDF becomes ~6.7MB of base64. With 6 entries, the exportAll()
endpoint was sending 30-60MB of JSON on every page load taking 2+ minutes.
Fix: Added ?lightweight=true parameter to /api/storage GET endpoint.
When enabled, stripHeavyFields() recursively removes large 'data' and
'fileData' string fields (>1KB) from JSON values, replacing with '__stripped__'.
Changes:
- /api/storage route.ts: stripHeavyFields() + lightweight query param
- StorageService.export(): accepts { lightweight?: boolean } option
- DatabaseStorageAdapter.export(): passes lightweight flag to API
- LocalStorageAdapter.export(): accepts option (no-op, localStorage is fast)
- useStorage.exportAll(): passes options through
- registry-service.ts: getAllEntries() uses lightweight=true by default
- registry-service.ts: new getFullEntry() loads single entry with full data
- use-registry.ts: exports loadFullEntry() for on-demand full loading
- registratura-module.tsx: handleEdit/handleNavigateEntry load full entry
Result: List loading transfers ~100KB instead of 30-60MB. Editing loads
full data for a single entry on demand (~5-10MB for one entry vs all).
Complete Next.js 16 application with 13 fully implemented modules:
Email Signature, Word XML Generator, Registratura, Dashboard,
Tag Manager, IT Inventory, Address Book, Password Vault,
Mini Utilities, Prompt Generator, Digital Signatures,
Word Templates, and AI Chat.
Includes core platform systems (module registry, feature flags,
storage abstraction, i18n, theming, auth stub, tagging),
16 technical documentation files, Docker deployment config,
and legacy HTML tool reference.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>