The ops tech module at /ops/#/j/* had drifted from the field app in two ways:
1. Scanner — a prior "restoration" re-added html5-qrcode, but the
design has always been native <input capture="environment"> → Gemini
2.5 Flash via targo-hub /vision/barcodes (up to 3 codes) and
/vision/equipment (structured labels, up to 5). Revert useScanner.js
+ ScanPage.vue + TechScanPage.vue to commit e50ea88 and drop
html5-qrcode from both package.json + lockfiles. No JS barcode
library, no camera stream, no polyfills.
2. Equipment UX — TechJobDetailPage.vue was a 186-line stub missing the
Ajouter bottom-sheet (Scanner / Rechercher / Créer), the debounced
SN-then-MAC search, the 5-field create dialog, Type + Priority
selects on the info card, and the location-detail contact expansion.
Port the full UX from apps/field/src/pages/JobDetailPage.vue (526
lines) into the ops module (458 lines after consolidation).
Rebuilt and deployed both apps. Remote smoke test confirms 0 bundles
reference html5-qrcode and the new TechJobDetailPage.1075b3b8.js chunk
(16.7 KB vs ~5 KB stub) ships the equipment bottom-sheet strings.
Docs:
- docs/features/tech-mobile.md — new. Documents all three delivery
surfaces (legacy SSR /t/{jwt}, transitional apps/field/, unified
/ops/#/j/*), Gemini-native scanner pipeline, equipment UX, magic-link
JWT, cutover plan. Replaces an earlier stub that incorrectly
referenced html5-qrcode.
- docs/features/dispatch.md — new. Dispatch board, scheduling, tags,
travel-time optimization, magic-link SMS, SSE updates.
- docs/features/customer-portal.md — new. Plan A passwordless magic-link
at portal.gigafibre.ca, Stripe self-service, file inventory.
- docs/architecture/module-interactions.md — new. One-page call graph
with sequence diagrams for the hot paths.
- docs/README.md — expanded module index (§2) now lists every deployed
surface with URL + primary doc + primary code locations (was missing
dispatch, tickets, équipe, rapports, telephony, network, agent-flows,
OCR, every customer-portal page). New cross-module edge map in §4.
- docs/features/README.md + docs/architecture/README.md — cross-link
all new docs.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
280 lines
19 KiB
Markdown
280 lines
19 KiB
Markdown
# Gigafibre FSM — Documentation
|
|
|
|
> **Start here.** Pick a row by intent, jump to the module index when you
|
|
> need scope, or go straight to the cross-module map when you're chasing a
|
|
> call that hops surfaces.
|
|
|
|
**Last refreshed:** 2026-04-22 (expanded module index, added dispatch /
|
|
customer portal / tech mobile docs, cross-interaction matrix)
|
|
|
|
---
|
|
|
|
## 1. Quick nav by intent
|
|
|
|
| I want to… | Open |
|
|
|---|---|
|
|
| **See the plan** — what's shipped, what's queued, with clickable links to every live module | [roadmap.md](roadmap.md) |
|
|
| **Understand the system end-to-end** — services, containers, networks, SSO, Traefik routes | [architecture/overview.md](architecture/overview.md) |
|
|
| **See who calls who** — one-page matrix of every module → module interaction | [architecture/module-interactions.md](architecture/module-interactions.md) |
|
|
| **Build a new feature** — UI patterns, folder layout, router conventions, Pinia, feature-sliced design | [architecture/app-design.md](architecture/app-design.md) |
|
|
| **Touch ERPNext data** — doctypes, customer → subscription → equipment → invoice, "Lead to Live" flow | [architecture/data-model.md](architecture/data-model.md) |
|
|
| **Work on dispatch** — drag-and-drop scheduling, tech assignment, travel-time optimization, magic-link SMS | [features/dispatch.md](features/dispatch.md) |
|
|
| **Work on the tech mobile app** — scan-to-identify, equipment install, job status, JWT auth | [features/tech-mobile.md](features/tech-mobile.md) |
|
|
| **Work on the customer portal** — passwordless magic-link, invoice + ticket self-service | [features/customer-portal.md](features/customer-portal.md) |
|
|
| **Work on billing, Stripe, payments** — subscription lifecycle, invoice OCR, payment reconciliation, PPA | [features/billing-payments.md](features/billing-payments.md) |
|
|
| **Touch CPE / modems / ONTs** — GenieACS, Oktopus, TR-069 → TR-369 migration, TP-Link XX230v diagnostics | [features/cpe-management.md](features/cpe-management.md) |
|
|
| **Build or debug the scanner / OCR** — Gemini vision pipeline, barcode/equipment/invoice endpoints, offline queue, AI_API_KEY rotation | [features/vision-ocr.md](features/vision-ocr.md) |
|
|
| **Work on agent flows / automation** — Flow Template model, trigger wiring, step editor, runtime contract | [features/flow-editor.md](features/flow-editor.md) |
|
|
| **Audit the wizard's SKU mapping vs legacy** | [reference/erpnext-item-diff.md](reference/erpnext-item-diff.md) |
|
|
| **Inspect the legacy PHP wizard** | [reference/legacy-wizard/](reference/legacy-wizard/) |
|
|
| **Read a historical status snapshot** | [archive/status-snapshots/](archive/status-snapshots/) |
|
|
|
|
---
|
|
|
|
## 2. Module index
|
|
|
|
Every deployed surface, by app. Each row links to its primary doc and
|
|
names the exact code paths so you can `grep` from here.
|
|
|
|
### 2a. Ops SPA (`apps/ops/`) — `https://erp.gigafibre.ca/ops/`
|
|
|
|
**Single pane of glass for internal teams.** Quasar PWA (SPA mode in dev,
|
|
PWA mode in prod), Vue 3 + Pinia, hash router. Wrapped by Authentik via
|
|
Traefik `forwardAuth` on `/ops/*`. Bottom of the same app hosts the tech
|
|
mobile module on `/j/*` with magic-link auth.
|
|
|
|
| Module | URL | Purpose | Primary doc | Primary code |
|
|
|---|---|---|---|---|
|
|
| **Dashboard** | `/ops/#/` | KPIs (MRR, AR, active tickets), dispatch summary, live SSE feed | — | `src/pages/DashboardPage.vue` |
|
|
| **Clients** | `/ops/#/clients` · `/clients/:id` | Customer 360: profile, subscriptions, invoices, equipment, tickets, communication timeline | — | `src/pages/ClientsPage.vue` · `ClientDetailPage.vue` · `src/modules/clients/` |
|
|
| **Dispatch** | `/ops/#/dispatch` | Drag-and-drop tech schedule, job board, travel-time optimization, SMS magic-link issuance | [features/dispatch.md](features/dispatch.md) | `src/pages/DispatchPage.vue` · `src/modules/dispatch/` |
|
|
| **Tickets** | `/ops/#/tickets` | Support ticket board, SLA clock, linked equipment + jobs | — | `src/pages/TicketsPage.vue` · `src/modules/tickets/` |
|
|
| **Équipe** | `/ops/#/equipe` | Technician roster, skills/tags, availability, today's jobs | [features/dispatch.md §6](features/dispatch.md) | `src/pages/EquipePage.vue` · `src/modules/equipe/` |
|
|
| **Rapports** | `/ops/#/rapports` + `/revenus` · `/ventes` · `/taxes` · `/ar` | Revenue, sales, tax, AR reports (renders in-browser; PDF export via hub) | [features/billing-payments.md §9](features/billing-payments.md) | `src/pages/Rapports*.vue` · `src/modules/rapports/` |
|
|
| **OCR** | `/ops/#/ocr` | Upload scanned supplier bills → Gemini parse → pre-fill Purchase Invoice | [features/vision-ocr.md §4](features/vision-ocr.md) | `src/pages/OcrPage.vue` |
|
|
| **Settings** | `/ops/#/settings` | Feature flags, integration keys, user management (mirror of ERPNext) | — | `src/pages/SettingsPage.vue` |
|
|
| **Telephony** | `/ops/#/telephony` | SIP click-to-dial (Twilio Voice SDK + sip.js fallback), call history | [architecture/overview.md §3](architecture/overview.md) | `src/pages/TelephonyPage.vue` |
|
|
| **Agent Flows** | `/ops/#/agent-flows` | Visual editor for Flow Template (automation runtime) | [features/flow-editor.md](features/flow-editor.md) | `src/pages/AgentFlowsPage.vue` |
|
|
| **Network** | `/ops/#/network` | Cytoscape-rendered OLT/ONT topology, live link status from GenieACS | [features/cpe-management.md](features/cpe-management.md) | `src/pages/NetworkPage.vue` |
|
|
|
|
### 2b. Ops tech module (`apps/ops/src/modules/tech/`) — `https://erp.gigafibre.ca/ops/#/j/*`
|
|
|
|
**Same SPA, different mount.** Techs hit a magic-link SMS, land on `/j/:token`,
|
|
the token redirects to `/j` with JWT in localStorage. Mobile-first layout
|
|
with bottom tab bar.
|
|
|
|
| Route | Purpose | Primary doc | Primary code |
|
|
|---|---|---|---|
|
|
| `/j` | Today's jobs, grouped by status | [features/tech-mobile.md §4a](features/tech-mobile.md) | `TechTasksPage.vue` |
|
|
| `/j/job/:name` | Job detail + equipment management (Ajouter/Scanner/Rechercher/Créer) | [features/tech-mobile.md §5](features/tech-mobile.md) | `TechJobDetailPage.vue` |
|
|
| `/j/scan` | Standalone scanner (native camera → Gemini) | [features/tech-mobile.md §3](features/tech-mobile.md) | `TechScanPage.vue` |
|
|
| `/j/device/:serial` | Per-device detail, diagnostic shortcut | — | `TechDevicePage.vue` |
|
|
| `/j/diagnostic` | In-browser speed test + GenieACS probe | [features/cpe-management.md](features/cpe-management.md) | `TechDiagnosticPage.vue` |
|
|
| `/j/more` | Settings, token expiry, logout | — | `TechMorePage.vue` |
|
|
| `/j/:token` | Magic-link handler (last route, accepts any JWT) | [features/tech-mobile.md §2](features/tech-mobile.md) | `TechTasksPage.vue` (same component, different entry) |
|
|
|
|
### 2c. Field PWA (`apps/field/`) — `https://erp.gigafibre.ca/field/`
|
|
|
|
**Transitional standalone PWA, retiring.** Authentik-protected; byte-for-byte
|
|
closer to the legacy workflow techs had before the Ops tech module
|
|
existed. No magic-link — tech logs in via Authentik. Kept alive while the
|
|
SMS link still points at the hub SSR page (see tech-mobile doc §1).
|
|
|
|
Same page set as §2b (TasksPage, ScanPage, JobDetailPage, DevicePage,
|
|
DiagnosticPage, MorePage), same Gemini-native scanner composable.
|
|
`apps/field/src/pages/JobDetailPage.vue` is the reference implementation
|
|
we port *from* when the Ops tech module drifts.
|
|
|
|
### 2d. Customer portal (`apps/client/`) — `https://portal.gigafibre.ca/`
|
|
|
|
**Passwordless self-service.** Standalone Quasar PWA served by a dedicated
|
|
nginx container (`client-portal`) bind-mounted from `/opt/client-app/` on
|
|
the Proxmox VM. No Authentik — customers get a magic-link email (HS256
|
|
JWT, 24h TTL).
|
|
|
|
| Module | URL | Purpose | Primary doc | Primary code |
|
|
|---|---|---|---|---|
|
|
| **Login** | `/#/login` | Email entry → magic-link email send | [features/customer-portal.md §3](features/customer-portal.md) | `src/pages/LoginPage.vue` |
|
|
| **Dashboard** | `/#/` | Balance, next invoice, active tickets, recent activity | [features/customer-portal.md §4a](features/customer-portal.md) | `DashboardPage.vue` |
|
|
| **Account** | `/#/account` | Profile, saved payment methods (Stripe PM), address book | [features/billing-payments.md §6](features/billing-payments.md) | `AccountPage.vue` |
|
|
| **Catalog / Cart** | `/#/catalog` · `/#/cart` | Shop add-ons (extra mesh, static IP), add to Stripe Checkout | [features/billing-payments.md](features/billing-payments.md) | `CatalogPage.vue` · `CartPage.vue` |
|
|
| **Invoices** | `/#/invoices` · `/#/invoices/:name` | List + PDF of all Sales Invoices, Stripe-linked pay button | [features/billing-payments.md §8](features/billing-payments.md) | `InvoicesPage.vue` · `InvoiceDetailPage.vue` |
|
|
| **Tickets** | `/#/tickets` · `/#/tickets/:name` | Create support ticket, reply thread, attach photos | — | `TicketsPage.vue` · `TicketDetailPage.vue` |
|
|
| **Messages** | `/#/messages` | Unified comm thread (SMS + email) with support | — | `MessagesPage.vue` |
|
|
| **Payment flows** | `/#/payment-success` · `/payment-cancel` · `/payment-card-added` · `/order-success` | Stripe return URL landings | [features/billing-payments.md §7](features/billing-payments.md) | `Payment*.vue` · `OrderSuccessPage.vue` |
|
|
|
|
### 2e. Legacy portal (`apps/portal/`) — **retired 2026-04-22**
|
|
|
|
Old ERPNext-embedded customer portal. Host `client.gigafibre.ca` now 307s
|
|
to `portal.gigafibre.ca/*` via Traefik; see
|
|
`apps/portal/traefik-client-portal.yml`. Codebase retained as historical
|
|
reference only. Do not build.
|
|
|
|
### 2f. Marketing site (`apps/website/`) — `https://www.gigafibre.ca/`
|
|
|
|
React + Vite marketing site. Self-contained, no ERPNext dependency,
|
|
no docs beyond its own README.
|
|
|
|
---
|
|
|
|
## 3. Services (backends)
|
|
|
|
Everything in `services/*` is backend infrastructure. Usually you don't
|
|
edit these unless you're adding a new endpoint.
|
|
|
|
| Service | Port | Role | Primary docs |
|
|
|---|---|---|---|
|
|
| `services/targo-hub/` | `msg.gigafibre.ca:3300` | **API gateway.** Vision (Gemini), magic-link JWT, Twilio SMS, SSE fanout, Stripe webhooks, ACS proxy, Legacy-DB read proxy, tech-mobile SSR | [overview.md §3](architecture/overview.md) · [vision-ocr.md](features/vision-ocr.md) · [customer-portal.md](features/customer-portal.md) · [tech-mobile.md](features/tech-mobile.md) · [billing-payments.md](features/billing-payments.md) |
|
|
| `services/modem-bridge/` | `:3301` (internal) | Playwright + Chromium wrapper that logs into TP-Link XX230v modems and reads encrypted TR-181 data | [cpe-management.md](features/cpe-management.md) |
|
|
| `services/docuseal/` | `docs.gigafibre.ca:3000` | Contract signing for commercial quotes + residential service agreements | [flow-editor.md §5](features/flow-editor.md) (briefly) |
|
|
| `services/legacy-db/` | `:3305` (internal) | Read-only proxy over the retired MariaDB for lookups that haven't migrated | [architecture/overview.md](architecture/overview.md) |
|
|
|
|
**ERPNext** (`erp.gigafibre.ca`) is not under `services/` — it's a standard
|
|
Frappe-Bench install on the VM. See
|
|
[architecture/data-model.md](architecture/data-model.md) for the doctype
|
|
catalog.
|
|
|
|
---
|
|
|
|
## 4. Cross-module interactions (one-page map)
|
|
|
|
> Full matrix + sequence diagrams in
|
|
> [architecture/module-interactions.md](architecture/module-interactions.md).
|
|
> The summary below is just the high-traffic edges.
|
|
|
|
```text
|
|
┌───────────────────────────────────────┐
|
|
│ ERPNext (source of truth) │
|
|
│ Customer · Subscription · │
|
|
│ Equipment · Dispatch Job · │
|
|
│ Sales Invoice · Ticket │
|
|
└──┬──────────────┬──────────┬──────────┘
|
|
│ │ │
|
|
REST+token │ REST+token │ REST+token
|
|
│ │ │
|
|
┌────────────▼──┐ ┌───────▼────────┐ │
|
|
│ targo-hub │ │ Ops SPA │ │
|
|
│ (gateway) │◄──┤ apps/ops/ │ │
|
|
│ │ └────┬───────────┘ │
|
|
│ /vision/* │ │ │
|
|
│ /magic-link/* │ │ bundles │
|
|
│ /sse │ │ tech module │
|
|
│ /sms │ │ at /j/* │
|
|
│ /stripe/* │ │ │
|
|
│ /acs/* │ └─────────────┘
|
|
│ /t/{token} │ │
|
|
└──┬──────┬─────┘ │
|
|
│ │ │
|
|
SMS │ │ email (magic link) │
|
|
/SSE │ │ │
|
|
▼ ▼ ▼
|
|
┌─────────────────┐ ┌────────────────────────┐
|
|
│ Tech mobile │ │ Customer portal │
|
|
│ (SSR OR Vue) │ │ apps/client/ │
|
|
│ /t or /ops/#/j/ │ │ portal.gigafibre.ca │
|
|
└─────────────────┘ └────────────────────────┘
|
|
```
|
|
|
|
**Highest-traffic edges (read these docs first if you're chasing one):**
|
|
|
|
| From | To | Via | Where it's documented |
|
|
|---|---|---|---|
|
|
| Dispatch (Ops) | Tech mobile | Magic-link SMS → hub/magic-link.js → Twilio | [dispatch.md §5](features/dispatch.md) + [tech-mobile.md §2](features/tech-mobile.md) |
|
|
| Tech mobile scanner | Gemini | POST hub `/vision/barcodes` + `/vision/equipment` | [vision-ocr.md §2](features/vision-ocr.md) + [tech-mobile.md §3](features/tech-mobile.md) |
|
|
| Ops OCR page | Gemini | POST hub `/vision/invoice` | [vision-ocr.md §3](features/vision-ocr.md) |
|
|
| Customer portal | ERPNext | Authenticated via customer JWT → hub proxies `/api/resource/*` | [customer-portal.md §6](features/customer-portal.md) |
|
|
| Stripe | ERPNext | Webhook → hub `/stripe/webhook` → creates Payment Entry | [billing-payments.md §7](features/billing-payments.md) |
|
|
| Ops dispatch | SSE live updates | hub `/sse` (tech status, job flip, ETA) | [dispatch.md §4](features/dispatch.md) |
|
|
| Agent-flows | Any module | Runtime in hub → triggers any ERPNext op via service token | [flow-editor.md §6](features/flow-editor.md) |
|
|
| GenieACS | Modem-bridge | hub `/acs/probe` → modem-bridge for TP-Link deep params | [cpe-management.md §4](features/cpe-management.md) |
|
|
|
|
---
|
|
|
|
## 5. Folder map
|
|
|
|
```text
|
|
docs/
|
|
├── README.md ← you are here
|
|
├── roadmap.md ← phase tracker + live module URLs
|
|
│
|
|
├── architecture/ ← the system itself
|
|
│ ├── overview.md (services, Docker, Traefik, SSO, retirement plan)
|
|
│ ├── data-model.md (ERPNext doctypes + "Lead to Live")
|
|
│ ├── app-design.md (Vue/Quasar patterns, feature-sliced layout)
|
|
│ └── module-interactions.md (full call graph + sequence diagrams)
|
|
│
|
|
├── features/ ← one doc per business capability
|
|
│ ├── README.md (per-doc index)
|
|
│ ├── dispatch.md (Ops dispatch board, scheduling, tags, SMS)
|
|
│ ├── tech-mobile.md (Gemini-native scanner, equipment UX, JWT)
|
|
│ ├── customer-portal.md (Plan A magic-link, Stripe self-service)
|
|
│ ├── billing-payments.md (Stripe, invoices, subscriptions, PPA)
|
|
│ ├── cpe-management.md (GenieACS, Oktopus, XX230v, modem-bridge)
|
|
│ ├── vision-ocr.md (Gemini, scan pipeline, offline queue, keys)
|
|
│ └── flow-editor.md (Flow Template, triggers, runtime)
|
|
│
|
|
├── reference/ ← lookup material
|
|
│ ├── erpnext-item-diff.md (new SKUs vs legacy gestionclient)
|
|
│ └── legacy-wizard/ (read-only snapshot of the PHP wizard)
|
|
│
|
|
├── assets/ ← PPTX decks, screenshots, generated diagrams
|
|
│
|
|
└── archive/ ← frozen docs, do not edit
|
|
├── HANDOFF-2026-04-18.md
|
|
├── MIGRATION.md
|
|
├── LEGACY-ACCOUNTING-ANALYSIS.md
|
|
└── status-snapshots/ (dated session reports)
|
|
```
|
|
|
|
---
|
|
|
|
## 6. Conventions
|
|
|
|
- **Edit in place.** Don't copy-paste a section into a new file — link to it instead.
|
|
- **Module index = source of truth for URLs.** If you change a route or a
|
|
deployed host, update §2 of this file *and* the relevant feature doc.
|
|
`grep -r "erp.gigafibre.ca" docs/` should not return stale hostnames.
|
|
- **Cross-doc links use repo-relative paths,** e.g.
|
|
`[features/dispatch.md](features/dispatch.md)` — so `git mv`ing a doc
|
|
surfaces every incoming link.
|
|
- **Code comments that reference a doc** use the full repo-relative path,
|
|
e.g. `// See docs/features/vision-ocr.md §10` — so `grep docs/` still
|
|
surfaces them if we reorganize again.
|
|
- **Dated snapshots go to `archive/status-snapshots/`.** Don't create
|
|
`STATUS_YYYY-MM-DD.md` at the root anymore.
|
|
- **The root `README.md`** (one level up) is the repo introduction for
|
|
non-technical readers; `docs/README.md` (this file) is the engineering
|
|
index.
|
|
|
|
---
|
|
|
|
## 7. What changed on 2026-04-22
|
|
|
|
Big reorganization:
|
|
|
|
1. **New feature docs** added: `dispatch.md`, `customer-portal.md`,
|
|
`tech-mobile.md`. The first was missing entirely despite being the
|
|
most-used surface in Ops; the second codifies the Plan A magic-link
|
|
cutover; the third replaces a stale `tech-mobile.md` that incorrectly
|
|
referenced `html5-qrcode` (never shipped — scanner has always been
|
|
native camera + Gemini).
|
|
2. **New architecture doc** added: `module-interactions.md` — the
|
|
one-page call graph with sequence diagrams for the hot paths (dispatch
|
|
→ SMS → tech scan → equipment install).
|
|
3. **Module index** (§2 above) now lists *every* deployed surface, not
|
|
just the three that had feature docs. Dispatch, Tickets, Équipe,
|
|
Rapports, Telephony, Network, Agent Flows, OCR, and every customer-portal
|
|
page now have a named row.
|
|
4. **Customer portal topology clarified.** `portal.gigafibre.ca` is the
|
|
live SPA; `client.gigafibre.ca` 307s to portal. The legacy
|
|
`apps/portal/` tree is retained as historical reference only.
|
|
5. **Earlier flattening (still in effect):** docs were flattened out of a
|
|
single folder into `architecture/`, `features/`, `reference/`,
|
|
`archive/`. Every file move used `git mv` so `git log --follow` still
|
|
works. The old paths (`docs/ARCHITECTURE.md`, `docs/VISION_AND_OCR.md`,
|
|
etc.) no longer exist — if you land on one of those links from an
|
|
external bookmark, use §1 above to find the new location. Legacy
|
|
references remain only inside `archive/HANDOFF-2026-04-18.md` and
|
|
`archive/status-snapshots/*.md`, where they're preserved as a frozen
|
|
historical record.
|