Permit-iframe hardening:
- public/embed.php — replace the 302 redirect on unauthenticated visits
with an in-iframe HTML "Sign in to view the map" card (HTTP 401)
whose primary button uses target="_top" to break the iframe and send
the parent window to the SSO portal. The 302 was broken UX inside an
iframe because the LUSPA portal refuses to be framed.
- public/embed.php + public/.htaccess — strip X-Frame-Options at the
embed endpoint (defence in depth). Apache's <Files "embed.php">
Header always unset X-Frame-Options + PHP's header_remove() both
ensure the only iframe-policy header on the response is our CSP
frame-ancestors (which already allows the permits subdomain). Fixes
Safari's "Refused to display ... because it set 'X-Frame-Options'
to 'SAMEORIGIN'" when the container's reverse proxy injects it.
Import UX refinements:
- Spinner overlay (index.html #import-spinner-overlay + main.js
showImportSpinner/hideImportSpinner) shown during the file-drop →
mapping-modal gap. Wired at the top of each handle*Import and at
every error / early-return path; hidden by stageImport() just before
openImportMappingModal() so it spans both the JS parse and the
SQLocal staging insert.
- Per-feature client_uuid tagging — each imported OL feature now
carries _externalImportId + _clientUuid set in stageImport(). These
tags are the link that lets later edits find the matching staging
row, and they are passed through to addExternalImportFeatures.
- Geometry-edit persistence — new public callback registry
MapView.onFeatureModified(cb) fired from a modifyend listener on
_modifyInteraction. main.js handler writes the new WKT (EPSG:4326)
back to external_import_features.geometry_wkt via new helper
updateExternalImportFeatureGeometry(clientUuid, wkt). Non-imported
features carry no tags, so the handler is a no-op for them.
- Delete persistence — removefeature listener on each imported layer's
source. New helper deleteExternalImportFeature(clientUuid) runs an
atomic DELETE + decrement of external_imports.feature_count and
broadcasts the changes so the LayerSwitcher badge can recount.
- Field-mapping dropdown — sample values + bold field names.
New helpers sampleSourceValues(fc) in import-detect.js (picks first
non-empty value per attribute, JSON-stringifies objects, collapses
whitespace, truncates to 35 chars) and toBoldUnicode(s) in
import-modal.js (ASCII letters/digits → Mathematical Alphanumeric
Symbols block). Options now read as "𝐮𝐩𝐧 — [12345-6789]";
HTML/CSS bold doesn't render inside <option> elements, so Unicode
bold codepoints are the cross-browser way.
Workshop deliverables:
- LUPMIS2_Improvements_Mar_to_Jun_2026.docx — handout mirroring the
slide deck one-to-one (160 paragraphs, branded styling).
- LUPMIS2_Workshop_Mar_to_Jun_2026.pptx — 16-slide pptxgenjs deck
(16:9 widescreen, brand palette, hero + content + closing masters,
embedded staged-upload diagram on slide 9).
- LUPMIS2_Staged_Upload_Flow.svg + .png — three swim-lane diagram of
the staged-upload pipeline with a dedicated "Client QA Gate"
callout. Hand-crafted SVG + 2400 px PNG.
save_gps_trail.php diagnosis (no code change, on the database team):
the reported "CORS" error is a missing endpoint — Apache returns 404
with no CORS headers and the browser surfaces it as access-control.
Once the endpoint is deployed the API server's global CORS handling
attaches the right headers and the GPS-trail sync will work without
client changes.
dist/ rebuilt.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
UPN-grid layer:
- src/database.js — new upn_grid SQLocal table (id, districtid, upn_prefix,
geometry_wkt) + saveUpnGrid / getLocalUpnGrid; cache-once-per-district.
- src/remotedb.js — getUpnGrid → get_upn_grid_per_district.php.
- main.js loadUpnGrid + upnGridToGeoJSON in the Administration group, with
a zoom-aware style: white casing under a bolder violet dashed stroke
(visible against parcels) and upn_prefix labels rendered only when
resolution ≤ 7 m/px (≈ scale ≤ 1:25,000).
- main.js click handler: single click on a UPN-grid cell opens an info
popup showing the upn_prefix.
External-dataset import → staging → upload (client-side complete):
- src/database.js — external_imports + external_import_features tables,
plus createExternalImport / addExternalImportFeatures /
updateExternalImport / getExternalImport / getExternalImportFeatures /
listExternalImports / remapImportedFeatureProperties /
deleteExternalImport. Status enum: imported/mapped/other/uploading/
submitted/migrated/failed (aligned with the database team's staged-
upload model — lu_parcels_upload_tmp + supervisor review).
- src/import-detect.js — pure helpers: detectTargetType(),
autoMapFields(), applyFieldMapping(), listSourceFields() + TARGET_TYPES
/ TARGET_FIELDS registries.
- src/import-modal.js — Bootstrap mapping modal: target dropdown,
field-rename table, three actions (Cancel / Save / Save + Upload now).
- main.js — stageImport hooked into addImportedGeoJSON (the single
convergence point for shp/GeoJSON/KML drops); handleImportModalResult
applies the mapping in one transaction; runUpload builds the real
payload (district_id + api_token from remotePost, user_id_upload from
SSO session, per-feature client_uuid/geom/props) and currently logs +
toasts — the upload_<target>.php endpoints are not yet live.
- index.html — #importMappingModal markup.
- MapView._decorateLayerListItem — import-state chip (Upload N /
spinner / ✓ submitted / ✓ live / N errors) dispatching
lupmis:import-chip-click; src/styles/layerswitcher.css — chip variants.
GIS export from Area / Circle Analysis popups:
- MapView._showAnalysisPopup now accepts an exportContext (clipGeometry +
parcelFeatures + zoneFeatures + otherByLayer) and renders an "Export
GIS" button next to "Export PDF". Click dispatches lupmis:export-gis.
- index.html — #exportGisModal markup.
- src/export-gis-modal.js — Bootstrap modal: format toggle (GeoJSON
default / Shapefile / KML), filename, field-rename table with SHP
10-char DBF warning.
- src/gis-export.js — writers: GeoJSON via Blob, KML via OL KMLFormat,
Shapefile via shp-write (with DBF-safe name sanitiser).
- Adds shp-write@0.3.2 dependency.
MapView style options:
- addGeoJSONLayer now accepts strokeDash for line-dash patterns (used by
the UPN-grid layer and available for any future contextual overlay).
Service Worker v9 → v10 to evict the stale shell/module caches on the
next deploy.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>