Commit Graph

223 Commits

Author SHA1 Message Date
AI Assistant a52f9e7586 feat(parcel-sync): redesign PostGIS/QGIS section with clear instructions
- Renamed "Setup PostGIS" to "Activeaza compatibilitate QGIS"
- Tooltip: "Operatie sigura, reversibila. Nu modifica datele existente."
- After setup: shows step-by-step QGIS connection instructions
  (host, port, database, views, SRID)
- Button hidden after successful setup (shows instructions instead)
- Clear explanation for non-technical users

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 14:01:35 +02:00
AI Assistant 88754250a8 docs: update CLAUDE.md + SKILLS.md with ePay integration, performance fixes
- ParcelSync version 0.6.0 with ePay CF extract ordering
- ANCPI ePay in Current Integrations table
- Static WORKSPACE_TO_COUNTY mapping documented
- GisUat geometry select optimization documented
- Feature count cache (5-min TTL) documented
- ePay endpoint gotchas, auth flow, order flow
- Cleaned outdated info, focused on actionable gotchas

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 13:57:42 +02:00
AI Assistant 2886703d0f perf(parcel-sync): use useDeferredValue for UAT search input
React's useDeferredValue lets the input update immediately while
deferring the expensive filter (3186 items) to a lower priority.
Removes the setTimeout debounce in favor of React's built-in
concurrent rendering scheduler. Input stays responsive.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 12:30:47 +02:00
AI Assistant 62777e9778 fix(ancpi): UAT debounce + list tooltips + expired download + ePay retry
1. UAT search: 150ms debounce prevents slow re-renders on keystroke
2. Lista mea tooltips: "Scoate Extrase CF" shows exact credit cost,
   status badges show expiry dates and clear instructions
3. Expired extracts: both Descarcă (old version) + Actualizează shown
4. ePay auto-connect: retry 2x with 3s delay, check session before
   connect, re-attempt on disconnect detection

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 12:14:34 +02:00
AI Assistant 5a6ab36aa7 feat(ancpi): selectable extracts with numbered ZIP download
- Checkbox on each row (ordered selection → numbered files in ZIP)
- "Descarcă selecție (N)" button appears when items selected
- Tooltip shows position in ZIP: "#1 in ZIP", "#2 in ZIP"
- Select-all checkbox in header
- Tooltips on Descarcă tot + Descarcă selecție buttons

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 10:37:03 +02:00
AI Assistant 87281bc690 fix(ancpi): make Actualizeaza button prominent + add tooltips in tab
- Actualizeaza button: orange bg, white text, clearly clickable
- Tooltip: "Comandă extras CF nou (1 credit) / Extrasul actual a expirat"
- Descarca button: tooltip "Descarcă extras CF (nrCadastral)"

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 10:23:54 +02:00
AI Assistant 7d30e28fdc fix(ancpi): parse CF numbers and solutii separately, zip by position
The nested JSON in ePay HTML breaks [^}]* regex. New approach:
find all CF.stringValues independently, find all solutii independently,
then zip them by position (they appear in same order in HTML).
This correctly maps CF number → document for batch orders.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 10:06:20 +02:00
AI Assistant a826f45b24 feat(ancpi): re-download with CF matching + tooltips + animations
Re-download: all 7 orders re-downloaded using documentsByCadastral
for correct CF→document matching. No more hardcoded order→parcel map.

Tooltips on all CF extract UI elements:
- No extract: "Comandă extras CF (1 credit)"
- Valid: "Valid până la DD.MM.YYYY" + "Descarcă extras CF"
- Expired: "Expirat pe DD.MM.YYYY" + "Comandă extras CF nou (1 credit)"
- Processing: "Comanda în curs de procesare"

Animations: Loader2 spinner while ordering, transition to green check.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 09:38:23 +02:00
AI Assistant 0c94af75d3 fix(ancpi): correct PDF-to-parcel matching + UAT search priority
Critical fix: batch order documents are now matched by CF number
from parsed metadateCereri (documentsByCadastral), not by index.
Prevents PDF content mismatch when ePay returns docs in different order.

UAT search: name matches shown first, county-only matches after.
Typing "cluj" now shows CLUJ-NAPOCA before county "Cluj" matches.

Cleaned MinIO + DB of incorrectly mapped old test data.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 09:29:11 +02:00
AI Assistant a59d9bc923 feat(ancpi): complete ePay UI redesign + ZIP download + smart batch ordering
UI Redesign:
- ePay auto-connect when UAT is selected (no manual button)
- Credit badge with tooltip ("N credite ePay disponibile")
- Search result cards show CF status: Valid (green), Expirat (orange),
  Lipsă (gray), Se proceseaza (yellow pulse)
- Action buttons on each card: download/update/order CF extract
- "Lista mea" numbered rows + CF Status column + smart batch button
  "Scoate Extrase CF": skips valid, re-orders expired, orders new
- "Descarca Extrase CF" button → ZIP archive with numbered files
- Extrase CF tab simplified: clean table, filters (Toate/Valabile/
  Expirate/In procesare), search, download-all ZIP

Backend:
- GET /api/ancpi/download-zip?ids=... → JSZip streaming
- GET /api/ancpi/orders: multi-cadastral status check with statusMap
  (valid/expired/none/processing) + latestById

Data:
- Simulated expired extract for 328611 (Cluj-Napoca, expired 2026-03-17)
- Cleaned old error records from DB

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 09:13:51 +02:00
AI Assistant b7302d274a docs: update SKILLS.md with complete ANCPI ePay documentation 2026-03-23 04:20:37 +02:00
AI Assistant c9ecd284c7 feat(ancpi): complete ePay UI + dedup protection
UI Components (Phase 4):
- epay-connect.tsx: connection widget with credit badge, auto-connect
- epay-order-button.tsx: per-parcel "Extras CF" button with status
- epay-tab.tsx: full "Extrase CF" tab with orders table, filters,
  download/refresh actions, new order form
- Minimal changes to parcel-sync-module.tsx: 5th tab + button on
  search results + ePay connect widget

Dedup Protection:
- epay-queue.ts: batch-level dedup (60s window, canonical key from
  sorted cadastral numbers)
- order/route.ts: request nonce idempotency (60s cache)
- test/route.ts: refresh protection (30s cache)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 04:19:19 +02:00
AI Assistant fcc6f8cc20 fix(ancpi): strip diacritics from MinIO metadata headers (ASCII only) 2026-03-23 03:51:10 +02:00
AI Assistant af30088ee6 fix(ancpi): simplify document parsing, avoid catastrophic regex backtracking 2026-03-23 03:43:44 +02:00
AI Assistant 6185defa8b fix(ancpi): decode HTML entities before parsing document info from OrderDetails 2026-03-23 03:38:36 +02:00
AI Assistant e63ec4c6c8 fix(ancpi): parse Angular ng-click downloadFile pattern for document IDs 2026-03-23 03:32:21 +02:00
AI Assistant 84b862471c fix(ancpi): add multiple document parsing patterns + debug logging 2026-03-23 03:26:40 +02:00
AI Assistant 8488a53e3b feat(ancpi): batch ordering + download existing orders
Major rewrite:
- Queue now processes batches: addToCart×N → saveMetadata×N → ONE
  submitOrder → poll → download ALL documents → store in MinIO
- Removed unique constraint on orderId (shared across batch items)
- Added step=download to test endpoint: downloads PDFs from 5
  existing orders (9685480-9685484) and stores in MinIO
- step=order now uses enqueueBatch for 2 test parcels (61904, 309952)
  as ONE ePay order instead of separate orders

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 03:20:36 +02:00
AI Assistant 08cd7164cb fix(ancpi): GET CheckoutConfirmationSubmit after EditCartSubmit
EditCartSubmit returns 200 (not redirect) — Angular does client-side
redirect to CheckoutConfirmationSubmit.action. Added this step to
actually confirm the order before looking for the orderId.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 02:49:57 +02:00
AI Assistant 6c60572a3e fix(ancpi): find NEW orderId after submit, track known IDs in queue
submitOrder now captures the previous orderId BEFORE submitting, then
searches for a NEW orderId that isn't in the knownOrderIds set. Queue
passes knownOrderIds between sequential items to prevent duplicate
orderId assignment (unique constraint violation).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 02:43:21 +02:00
AI Assistant c452bd9fb7 fix(ancpi): use form-data multipart for saveProductMetadataForBasketItem
Angular uses doPostAsFormMultipart — the save endpoint requires
multipart/form-data, not application/x-www-form-urlencoded.
Install form-data package and restore multipart upload.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 02:33:30 +02:00
AI Assistant fd86910ae3 fix(ancpi): remove form-data dependency, use URLSearchParams for save
form-data package not installed — crashes at runtime. Use
URLSearchParams instead (the Angular source uses doPostAsForm
which is form-urlencoded, so this should work too).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 02:24:45 +02:00
AI Assistant bcb7aeac64 fix(ancpi): accept SAVE_OK as success code from saveMetadata 2026-03-23 02:19:28 +02:00
AI Assistant 7fc46f75bd fix(ancpi): ePay county IDs = WORKSPACE_IDs, UAT IDs = SIRUTA codes
Zero discovery calls needed! ePay internal county IDs are identical
to eTerra WORKSPACE_IDs (CLUJ=127, ALBA=10, etc.) and ePay UAT IDs
are SIRUTA codes (Cluj-Napoca=54975, Florești=57706). Queue now
uses workspacePk + siruta directly from GisUat DB.
Flow: AddToCart → saveMetadata → EditCartSubmit → Poll+Download.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 02:11:41 +02:00
AI Assistant e13a9351be fix(ancpi): complete rewrite based on Angular source code analysis
All endpoints and payloads verified against epaymentAngularApp.js:
- EpayJsonInterceptor: form-urlencoded (not JSON), uses reqType param
- County IDs: internal ANCPI IDs from judeteNom (NOT 0-41 indices)
- UAT lookup: reqType=nomenclatorUAT&countyId=<internal_ID>
- Save metadata: reqType=saveProductMetadataForBasketItem (multipart)
  with productMetadataJSON using stringValues[] arrays
- SearchEstate: field names are identificator/judet/uat (not identifier/countyId/uatId)
- Download PDF: Content-Type: application/pdf in request header
- Queue resolves county+UAT IDs dynamically via getCountyList+getUatList

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 02:01:39 +02:00
AI Assistant eb8cd18210 fix(ancpi): use JSON body for EpayJsonInterceptor + EditCartItemJson
Root cause from ePay Angular analysis:
- EpayJsonInterceptor needs Content-Type: application/json + {"judet": N}
- EditCartItemJson needs JSON with bigDecimalValue/stringValue structure
- SearchEstate needs basketId in body for JSON response
- Queue skips SearchEstate (data already from eTerra), uses
  configureCartItem → submitOrder flow directly

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 01:55:30 +02:00
AI Assistant 23bddf6752 feat(ancpi): test EditCartItemJson + SearchEstate with AJAX headers 2026-03-23 01:42:43 +02:00
AI Assistant 665a51d794 feat(ancpi): extract Angular AJAX endpoints from ShowCartItems page 2026-03-23 01:37:45 +02:00
AI Assistant d367b5f736 fix(ancpi): add SearchEstate debug logging, try without uatId, add cart first
SearchEstate might need active cart and/or different headers.
Add X-Requested-With: XMLHttpRequest, make uatId optional, log raw
response (type, length, sample), and add-to-cart before searching.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 01:27:30 +02:00
AI Assistant 0447908007 fix(ancpi): GET login page before POST to establish form tokens
OpenAM requires an initial GET to set session cookies before the
credentials POST. Without it, POST returns 500 and only sets
AMAuthCookie (intermediate) instead of iPlanetDirectoryPro (final SSO).
Then navigate to ePay goto URL to establish JSESSIONID.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 01:06:42 +02:00
AI Assistant 887e3f423e fix(ancpi): try HTTP URL for ePay session establishment
OpenAM goto URL is http://epay.ancpi.ro:80 (HTTP, not HTTPS).
Try multiple URL variants to establish JSESSIONID after OpenAM auth.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 00:56:24 +02:00
AI Assistant 04c74c78e4 fix(ancpi): add credit parsing debug logging 2026-03-23 00:43:37 +02:00
AI Assistant e35b50e5c2 fix(ancpi): recognize AMAuthCookie as valid OpenAM session cookie
ANCPI's OpenAM uses AMAuthCookie instead of iPlanetDirectoryPro.
Accept AMAuthCookie, iPlanetDirectoryPro, or JSESSIONID as valid
session indicators. Navigate to ePay after auth to establish JSESSIONID.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 00:38:11 +02:00
AI Assistant b9993f0573 fix(ancpi): follow full redirect chain for OpenAM login, add cookie debug
Let axios follow all redirects (maxRedirects=10) so cookie jar captures
iPlanetDirectoryPro from the chain. Explicitly navigate to ePay after
login to ensure JSESSIONID. Log all cookies for debugging. Last resort:
verify login by checking if credit info is visible on ePay page.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 00:32:30 +02:00
AI Assistant 259f56396b fix(ancpi): use full OpenAM login URL with module + goto params
OpenAM requires module=SelfRegistration and goto= redirect URL.
Also handle 302 manually to capture iPlanetDirectoryPro cookie,
then follow redirect to ePay for JSESSIONID.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 00:27:54 +02:00
AI Assistant 3921852eb5 feat(parcel-sync): add ANCPI ePay CF extract ordering backend
Foundation (Phase 1):
- CfExtract Prisma model with version tracking, expiry, MinIO path
- epay-types.ts: all ePay API response types
- epay-counties.ts: WORKSPACE_ID → ePay county index mapping (42 counties)
- epay-storage.ts: MinIO helpers (bucket, naming, upload, download)
- docker-compose.yml: ANCPI env vars

ePay Client (Phase 2):
- epay-client.ts: full HTTP client (login, credits, cart, search estate,
  submit order, poll status, download PDF) with cookie jar + auto-relogin
- epay-session-store.ts: separate session from eTerra

Queue + API (Phase 3):
- epay-queue.ts: sequential FIFO queue (global cart constraint),
  10-step workflow per order with DB status updates at each step
- POST /api/ancpi/session: connect/disconnect
- POST /api/ancpi/order: create single or bulk orders
- GET /api/ancpi/orders: list all extracts
- GET /api/ancpi/credits: live credit balance
- GET /api/ancpi/download: stream PDF from MinIO

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 00:09:52 +02:00
AI Assistant 86e43cecae fix(parcel-sync): show 'jud.' prefix before county name in UAT dropdown
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 23:03:05 +02:00
AI Assistant 899b5c4cf7 fix(parcel-sync): populate county data during login, not via PATCH
Root cause: PATCH endpoint created a new EterraClient which tried
to re-login with expired session → 401. Now county refresh runs
immediately after successful login in the session route, using the
same authenticated client (fire-and-forget). Component reloads UAT
data 5s after connection to pick up fresh county info.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 22:23:46 +02:00
AI Assistant 8fa89a7675 fix(parcel-sync): restore SIRUTA in dropdown, add county debug output
- Restore SIRUTA code display in parentheses next to UAT name
- PATCH response now includes debug samples (sampleUat keys, county
  raw data) visible in browser console for diagnosing matching issues
- POST endpoint now supports resync (upsert mode, safe to call again)
- Client logs full PATCH result to browser console for debugging

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 21:34:29 +02:00
AI Assistant 431291c410 fix(parcel-sync): robust county population + local feature count in dropdown
- PATCH /api/eterra/uats: handle nested responses (unwrapArray), try
  multiple field names (extractName/extractCode), log sample UAT for
  debugging, match by code first then by name
- GET /api/eterra/uats: include localFeatures count per SIRUTA via
  GisFeature groupBy query
- Dropdown: show green badge with local feature count, county with dash
- Add SKILLS.md for ParcelSync/eTerra/GIS module context

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 21:18:22 +02:00
AI Assistant 86c39473a5 feat(parcel-sync): show county in UAT search dropdown via eTerra data
PATCH /api/eterra/uats fetches counties from eTerra nomenclature and
LIMITE_UAT layer, then batch-updates GisUat records with county name
and workspacePk. Auto-triggers on first eTerra connection when county
data is missing. Helps distinguish same-name UATs in different counties.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 20:46:13 +02:00
AI Assistant 2a25e4b160 fix(registratura): replace parentheses with en-dash in contact display
"Name – Company" instead of "Name (Company)" for sender/recipient.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 04:15:37 +02:00
AI Assistant c8aee1b58e fix(registratura): remove parentheses from institution-only contacts, add live contact sync
- Fix display format: institutions without a person name no longer show
  as "(Company)" — now shows just "Company"
- Three-case formatting: name+company → "Name (Company)", only company
  → "Company", only name → "Name"
- Registry table now resolves sender/recipient live from address book
  via contactMap — edits in address book reflect immediately in registry
- New contacts created via quick-contact are added to contactMap on the fly

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 04:04:17 +02:00
AI Assistant 8e2534ebe3 feat: quick contact dialog from registratura supports name OR company
- QuickContactDialog now has Company/Organization field
- Either name or company is required (same logic as address book)
- Auto-sets type to "institution" when only company is provided
- Display name in registry form uses company as fallback

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 21:10:34 +02:00
AI Assistant 9d73697fb0 feat: address book - require name OR company, add department to contact persons
- Either name or company/organization is now required (not just name)
- When only company is set, it shows as primary display name
- Added department field to ContactPerson sub-entities
- Department shown as badge in card and detail views
- Updated vCard export to handle nameless contacts and department field
- Sort contacts by name or company (whichever is set)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 21:07:06 +02:00
AI Assistant f5deccd8ea refactor(pdf-compress): replace Ghostscript with qpdf + iLovePDF API
Ghostscript -sDEVICE=pdfwrite fundamentally re-encodes fonts, causing
garbled text regardless of parameters. This cannot be fixed.

New approach:
- Local: qpdf-only lossless structural optimization (5-30% savings,
  zero corruption risk — fonts and images completely untouched)
- Cloud: iLovePDF API integration (auth → start → upload → process →
  download) with 3 levels (recommended/extreme/low), proper image
  recompression without font corruption

Frontend: 3 modes (cloud recommended, cloud extreme, local lossless).
Docker: ILOVEPDF_PUBLIC_KEY env var added.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 17:50:46 +02:00
AI Assistant d75fcb1d1c fix(pdf-compress): remove /screen preset that destroys font encoding
The -dPDFSETTINGS=/screen GS preset overwrites font encoding tables,
producing garbled text in output PDFs. Replace with individual params
that ONLY compress images while preserving fonts intact.

Three quality levels via GS (no Stirling dependency):
- extreme: 100 DPI, QFactor 1.2 (~quality 35)
- high: 150 DPI, QFactor 0.76 (~quality 50)
- balanced: 200 DPI, QFactor 0.4 (~quality 70)

Route all UI modes through the GS endpoint with level parameter.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 17:19:42 +02:00
AI Assistant 81c61d8411 fix(registratura): show expiry/AC alerts regardless of entry status
Status "inchis" means the correspondence is resolved, not that the
document validity stopped mattering. Remove status filters from both
ImminentActions dashboard and detail panel Remindere section.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 15:49:48 +02:00
AI Assistant 0cd28de733 refactor(registratura): focus imminent actions on expiry/AC only, remove deadlines
Strip institutional legal deadlines from the dashboard — show only documents
expiring and AC validity within 60-day horizon. Rename to "De reînnoit".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 09:47:19 +02:00
AI Assistant 8275ed1d95 feat(registratura): add reminders section, expiry helpers, imminent actions dashboard
- Add "Remindere & Alerte" section in entry detail panel showing AC validity,
  expiry date, and tracked deadline status with color-coded indicators
- Add quick expiry date buttons (6/12/24 months from document date) in entry form
- Default dosare view to show only active threads
- Add ImminentActions dashboard component showing urgent items (expired docs,
  AC validity warnings, overdue/imminent deadlines) sorted by urgency

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 09:42:54 +02:00