11 KiB
Session Summary: Duimstok PWA MVP Build
Date: 2026-04-16 Project: ProRail Duimstok-inspecties Bovenbouw Repo: https://git.en-masse.nl/randy/duimstok.prorail.nl.git
Phase 1: Monolithic HTML Refactor
Goal: Split a single-file app (duimstok-wissel-gw-sw.html, 1566 lines) into a layered DDD structure.
What was done:
- Created
src/Domain/,src/Application/,src/Infrastructure/layers - Converted ES modules to classic IIFE scripts attaching to
window.App.*namespace - Embedded CSV seed data as a string constant in
seedOrders.js(nofetch()needed) - Removed all inline styles (
style="") and inline event handlers (onclick,oninput, etc.) - Replaced inline styles with CSS classes (
.flex-spacer,.cap-arrow.lg,.section-sub.sm,.fotonummers-cell) - Wired all event listeners via
addEventListenerinmain.jsand service modules
Result: 30 files, ~2163 lines. Runs by double-clicking public/index.html -- no server needed.
File structure:
01_Applicatie/
├── public/
│ ├── index.html
│ ├── css/ base.css, overview.css, form.css, modals.css, responsive.css
│ └── js/ namespace.js, main.js
└── src/
├── Domain/ sectionMap.js, scoring.js, orderParser.js
├── Application/ state.js, persistence.js, screens.js, photoService.js,
│ inspectionForm.js, orderOverview.js, xmlImport.js, exportService.js
└── Infrastructure/ utils.js, geolocation.js, db.js, seedOrders.js, csvLoader.js
Phase 2: PWA Research & Planning
Prompt used: "what are the next steps create a PWA version of this MVP, ideally with offline photo upload functionality which sync when back online. Whats the current supported state of PWA regarding these offline functionalities atm"
Key findings:
- Service Worker, Cache API, IndexedDB, Manifest, installability all work cross-browser (Chromium, Firefox, Safari/iOS 16.4+)
- Background Sync (
syncevent) only works on Chromium -- not Firefox, not Safari/iOS - Periodic Background Sync: Chromium only (engagement-gated)
- Background Fetch: Chromium only
- Push notifications: universal for installed PWAs (iOS 16.4+)
- Persistent storage: universal, but Safari historically aggressive on eviction
Key constraint: iOS has no true background sync. Design around draining the queue when the user reopens the app and when the online event fires.
6-step incremental plan identified:
- Installable shell (manifest + service worker cache)
- Switch photo storage from data URLs to Blobs
- Sync queue in IndexedDB
- Trigger drain on every available signal (online event, app open, SW sync, manual button)
- Persistent storage + install hint
- Backend stub (Symfony controller for POST /api/inspections)
Phase 3: PWA Scaffold Implementation
Prompt used: "Goal is still conversion later on to Symfony, but for now we do want the PWA functionality (add an additional installation and instruction screen for the PWA installation)"
Constraint flagged: PWA features require HTTPS or localhost -- they cannot work from file://.
Resolution: Dual-mode codebase:
file://mode: app works exactly as before; PWA init gated bylocation.protocol !== 'file:'and quietly skippedhttp://localhostor deployed mode: service worker registers, app becomes installable, install screen shows platform-specific instructions
Files created:
public/manifest.webmanifest-- app manifest with icons, standalone display, ProRail blue themepublic/sw.js-- service worker with app shell precaching (cache-first for statics, network-first for API)public/icons/icon.svg-- SVG app icon (ProRail blue with white ruler lines)public/css/install.css-- styles for the install instruction screensrc/Application/pwa.js-- service worker registration,beforeinstallpromptcapture, standalone detectionsrc/Application/installScreen.js-- platform-specific install instructions (Chrome/Edge, iOS Safari, Firefox, generic)
HTML changes:
- Added PWA meta tags (
theme-color,apple-mobile-web-app-capable, manifest link, apple-touch-icon) - Added "Installeren" button to overview toolbar
- Added
screen-installsection with back button and dynamic body - Added
<link>forinstall.css - Added
<script>tags forpwa.jsandinstallScreen.js
Phase 4: Git Repository Setup
Prompt used: "ini git repo and commit and push, add files and folders, repo https://git.en-masse.nl/randy/duimstok.prorail.nl.git" + "to the develop branch"
Decisions made:
- Repo root:
01_Applicatie/(app code only; ProRail reference PDFs/photos stay outside) - Deleted old monolithic files:
duimstok-wissel-gw-sw.htmland backup copy - Created
.gitignore(DS_Store, Thumbs.db, editors, node_modules, env files) - Created
README.mdwith project structure documentation - Created
_docs/.gitkeep
Git Flow applied:
- Initialized repo with
developbranch -- baseline commit (README, .gitignore, _docs/) - Created
feature/initial-pwa-scaffoldbranch off develop - Committed all app code (30 files, 2163 insertions)
- Pushed
develop(baseline only, no staging deploy triggered) - Pushed
feature/initial-pwa-scaffold(feature branch, no deploy implications) - Suggested PR creation on Forgejo before merging to develop
Phase 5: Photo Blob Storage Refactor
Branch: feature/photo-blob-storage (off feature/initial-pwa-scaffold)
What changed:
- Photos now stored as native
Blobobjects instead of base64 data URLs (~33% smaller) photoSrc()helper usesURL.createObjectURL(blob)for rendering thumbnailsURL.revokeObjectURL()called on lightbox close and when replacing images (memory cleanup)- Removed
readFileAsDataUrl()fromutils.js(no longer needed) - Removed
dataUrlToBlob()fromutils.js(no longer needed) db.js: removedJSON.parse(JSON.stringify(formData))deep-clone -- IndexedDB handles Blobs nativelyexportService.js: downloadsp.blobdirectly instead of converting from data URL
Files changed: photoService.js, exportService.js, db.js, utils.js
Phase 6: CSV Upload Persistence
Branch: feature/csv-upload-persistence
What changed:
- New IndexedDB object store
ordersCsv(DB version bumped 3 -> 4) db.js: addedsaveOrdersCsv(csvText)andloadOrdersCsv()functionscsvLoader.js:loadOrdersFromUpload()now returns{ text, orders }so raw CSV text gets persistedmain.jsinit: loads stored CSV from IndexedDB, falls back to embedded seed data if none storedmain.jsupload handler: saves CSV text to IndexedDB after parsing
Files changed: db.js, csvLoader.js, main.js
Phase 7: Persistent Storage Request
Branch: feature/persistent-storage-request
What changed:
- New file
src/Infrastructure/persistentStorage.jswithisPersisted()andrequestPersistence() - Calls
navigator.storage.persist()after the first successful save (reduces iOS eviction risk) - One-time flag
persistenceRequestedprevents repeated calls - Updated SW cache version to
duimstok-v2 - Added
persistentStorage.jsto bothindex.htmlscript tags and SW precache list
Files changed/created: persistentStorage.js (new), persistence.js, sw.js, index.html
Phase 8: Multi-Inspection Type Scoping (Not Yet Implemented)
Prompt used: "continue with question 1: spoor en overweg in map C:...\04_Gegenereerd door SAP_incl WW_xslx_xlm\XML"
Research done:
- Read
_docs/switch-inspection-diagram.md-- confirms existing wissel_GW photo layout - Read SAP XML templates for Spoor and Overwegbevloering from reference folders
- Identified 3 inspection types: Wissel, Overwegbevloering, Spoor
Proposed scope for feature/inspection-type-routing:
- Routing layer that derives type from
order.objectsoort - Three form panels in
index.html, shown based on type - Wissel: no visual change (existing 10-slot layout)
- Overwegbevloering: stub form with photos section, no wissel diagram
- Spoor: stub form emphasizing start/end-KM fields from CSV
- Export works for all three types
Status: Scoped, awaiting answers to 3 questions before implementation.
Global Rules Established
These rules were added to ~/.claude/CLAUDE.md during the session:
| Rule | Context |
|---|---|
| No backwards compatibility in MVPs | User corrected fallback code for old photo data format |
No Co-Authored-By: Claude in commits |
User interrupted a commit that included the attribution line |
| Git Flow workflow | User requested: develop = staging, master = production, feature branches for all work |
_docs/ for project documentation |
User requested: all docs in project root _docs/ folder |
file:// runnable MVPs |
User requested: must work by opening public/index.html directly |
| Vanilla HTML/JS/CSS, no frameworks | Pre-existing rule (maps to eventual Symfony/Twig conversion) |
| DDD folder structure | Pre-existing rule (Domain/Application/Infrastructure layers) |
| Forgejo at git.en-masse.nl | User specified as default remote host |
Shortcuts & Commands Used
| Command/Action | Purpose | Times Used |
|---|---|---|
/usage |
Check token consumption | 2x |
continue |
Resume after idle or interruption | Multiple |
merge |
Trigger merge-to-develop flow | Multiple |
yes / 1 / 3 |
Quick selection from numbered options | Multiple |
| Interrupted commits | Added global rules mid-flow (no co-author, no backwards compat) | 3x |
Git History (Linear on develop)
b005193 Request persistent storage after first save
92ad6a9 Persist uploaded order CSVs across app restarts
e86aa5b Store photos as native Blobs instead of base64 data URLs
776bd63 Add PWA-ready MVP: split HTML/CSS/JS + DDD layering + service worker
367bbca Initial repo scaffold on develop
Branch lifecycle:
- All feature branches created off
develop(or stacked off previous feature branch when develop was behind) - All merged via fast-forward (
--ff-only) - All deleted (local + remote) after merge
- Only
developremains as active branch masternot yet created (awaiting production readiness)
Open Items
- Manual verify: capture photo, render thumbnail, reload, check thumbnail persists, export produces valid JPG
- Manual verify: upload CSV, hard-reload, confirm modified orders still appear
- Implement
feature/inspection-type-routing(Wissel/Overwegbevloering/Spoor forms) - Create
masterbranch when ready for production deployment - Set up CI on Forgejo
- Backend stub (Symfony POST /api/inspections) to enable sync queue testing
- Authentication for inspectors (currently free-text name -- needs SSO/token discussion with ProRail)