- EquipmentDetail: collapsible node groups (clients grouped by mesh node) - Signal strength as RSSI % (0-255 per 802.11-2020) with 10-tone color scale - Management IP clickable link to device web GUI (/superadmin/) - Fibre status compact top bar (status + Rx/Tx power when available) - targo-hub: WAN IP detection across all VLAN interfaces - targo-hub: full WiFi client count (direct + EasyMesh mesh repeaters) - targo-hub: /devices/:id/hosts endpoint with client-to-node mapping - ClientsPage: start empty, load only on search (no auto-load all) - nginx: dynamic ollama resolver (won't crash if ollama is down) - Cleanup: remove unused BillingKPIs.vue and TagInput.vue - New docs and migration scripts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
416 lines
15 KiB
Markdown
416 lines
15 KiB
Markdown
# Field Tech App — Wizard UX & Form Customizer
|
|
|
|
## Design Principles
|
|
|
|
1. **Minimal inputs per screen** — Techs are manual workers, not desk jockeys. Max 2-3 fields per step.
|
|
2. **Carousel/swipe navigation** — Next/Back with swipe gestures, not scrolling a long form.
|
|
3. **Big touch targets** — Fat fingers in work gloves. Buttons 48px+ height, inputs 56px+.
|
|
4. **Auto-advance** — When a field is selected (like equipment_type), auto-advance to next step.
|
|
5. **Offline-first** — Everything queued if no signal. Sync indicator always visible.
|
|
6. **Customizable** — Admin can add/remove/reorder steps via an Odoo Studio-like builder.
|
|
|
|
---
|
|
|
|
## Wizard Component Architecture
|
|
|
|
### Core: `WizardCarousel.vue`
|
|
|
|
A reusable carousel-based wizard that renders steps from a JSON definition:
|
|
|
|
```vue
|
|
<WizardCarousel
|
|
:steps="wizardSteps"
|
|
:context="{ job, customer, location }"
|
|
@complete="onComplete"
|
|
@cancel="onCancel"
|
|
/>
|
|
```
|
|
|
|
Each step is a self-contained screen rendered in a `q-carousel-slide`:
|
|
- Swipe left/right to navigate
|
|
- Bottom progress dots
|
|
- "Suivant" / "Précédent" buttons (large, thumb-friendly)
|
|
- Step counter: "Étape 2/5"
|
|
|
|
### Step Definition Schema
|
|
|
|
```json
|
|
{
|
|
"id": "equipment_type",
|
|
"title": "Type d'équipement",
|
|
"icon": "router",
|
|
"fields": [
|
|
{
|
|
"name": "equipment_type",
|
|
"type": "select-cards",
|
|
"label": "Quel équipement?",
|
|
"options": [
|
|
{ "value": "ONT", "label": "ONT", "icon": "settings_input_hdmi", "color": "blue" },
|
|
{ "value": "Routeur", "label": "Routeur WiFi", "icon": "wifi", "color": "green" },
|
|
{ "value": "Décodeur TV", "label": "Décodeur TV", "icon": "tv", "color": "purple" },
|
|
{ "value": "Téléphone IP", "label": "Téléphone", "icon": "phone", "color": "orange" }
|
|
],
|
|
"auto_advance": true
|
|
}
|
|
],
|
|
"visible_if": null,
|
|
"required": true
|
|
}
|
|
```
|
|
|
|
### Field Types
|
|
|
|
| Type | Render | Use Case |
|
|
|------|--------|----------|
|
|
| `select-cards` | Big tappable cards with icons | Equipment type, status, connection type |
|
|
| `text` | Single large input | Serial number, name |
|
|
| `scan` | Camera button → barcode scanner | Serial, MAC address |
|
|
| `photo` | Camera button → photo capture | Equipment photo, site photo |
|
|
| `signature` | Touch signature pad | Customer sign-off |
|
|
| `toggle` | Large yes/no toggle | Signal OK?, WiFi working? |
|
|
| `notes` | Textarea (expandable) | Completion notes |
|
|
| `date` | Date picker | Schedule date |
|
|
| `search` | Autocomplete search | Customer lookup |
|
|
| `checklist` | List of checkable items | Installation checklist |
|
|
| `info` | Read-only display card | Summary, confirmation |
|
|
|
|
---
|
|
|
|
## Example Wizards
|
|
|
|
### 1. Installation Completion Wizard
|
|
|
|
When tech taps "Compléter" on a Dispatch Job:
|
|
|
|
```
|
|
Step 1: "Équipements installés" [checklist]
|
|
☐ ONT branché et signal OK
|
|
☐ Routeur WiFi configuré
|
|
☐ Décodeur TV branché (if TV service)
|
|
☐ Téléphone IP testé (if phone service)
|
|
|
|
Step 2: "Numéros de série" [scan]
|
|
→ Camera opens, scan ONT barcode
|
|
→ Auto-detected: RCMG19E0AB57
|
|
→ "Suivant" button
|
|
|
|
Step 3: "Signal ONT" [toggle + text]
|
|
Signal OK? [OUI / NON]
|
|
Niveau signal: [-20 dBm] (optional)
|
|
|
|
Step 4: "WiFi" [toggle]
|
|
Client connecté au WiFi? [OUI / NON]
|
|
SSID affiché: "Gigafibre-Tremblay"
|
|
|
|
Step 5: "Photo du boîtier" [photo]
|
|
📸 Prendre une photo
|
|
|
|
Step 6: "Signature client" [signature]
|
|
Le client confirme que le service fonctionne
|
|
|
|
Step 7: "Résumé" [info]
|
|
✓ ONT: RCMG19E0AB57
|
|
✓ Signal: -18.5 dBm
|
|
✓ WiFi: OK
|
|
✓ Photo: 1 prise
|
|
✓ Signé par: Jean Tremblay
|
|
[Confirmer l'installation]
|
|
```
|
|
|
|
### 2. Equipment Scan & Link Wizard
|
|
|
|
When tech scans a barcode (from ScanPage or Job context):
|
|
|
|
```
|
|
Step 1: "Scanner" [scan]
|
|
📸 Scanner le code-barres
|
|
→ Detected: RCMG19E0AB57
|
|
|
|
Step 2: "Type" [select-cards]
|
|
ONT / Routeur / Décodeur / Téléphone
|
|
→ Tap "ONT" → auto-advance
|
|
|
|
Step 3: "Confirmer" [info]
|
|
Serial: RCMG19E0AB57
|
|
Type: ONT
|
|
Client: Jean Tremblay (from job context)
|
|
Adresse: 123 rue Principale
|
|
[Lier à ce client]
|
|
```
|
|
|
|
### 3. Repair/Diagnostic Wizard
|
|
|
|
```
|
|
Step 1: "Problème signalé" [info]
|
|
Ticket: "Pas d'internet depuis hier"
|
|
Client: Jean Tremblay
|
|
Adresse: 123 rue Principale
|
|
|
|
Step 2: "Signal ONT" [toggle + text]
|
|
Signal présent? [OUI / NON]
|
|
Niveau: [-XX dBm]
|
|
|
|
Step 3: "Test WiFi" [toggle]
|
|
WiFi fonctionne? [OUI / NON]
|
|
|
|
Step 4: "Action prise" [select-cards]
|
|
Redémarrage / Remplacement ONT / Remplacement routeur / Reconnexion fibre / Autre
|
|
|
|
Step 5: "Nouvel équipement" [scan] (if replacement)
|
|
Scanner le nouveau serial
|
|
|
|
Step 6: "Notes" [notes]
|
|
Détails supplémentaires...
|
|
|
|
Step 7: "Résultat" [toggle]
|
|
Service rétabli? [OUI / NON]
|
|
[Fermer le ticket]
|
|
```
|
|
|
|
---
|
|
|
|
## Form Customizer (Odoo Studio-like)
|
|
|
|
### Concept
|
|
|
|
An admin page in the Ops app (`/ops/form-builder`) that lets managers customize the tech wizard steps without code changes.
|
|
|
|
### How It Works
|
|
|
|
1. **Wizard Templates** stored as JSON documents in ERPNext (new doctype: `Wizard Template`)
|
|
2. **Each template** = ordered list of steps with field definitions
|
|
3. **Admin UI** = drag-and-drop step editor:
|
|
- Add step (from field type library)
|
|
- Reorder steps (drag handle)
|
|
- Edit step properties (label, options, required, visibility condition)
|
|
- Remove step
|
|
- Preview on mock phone screen
|
|
4. **Templates linked to job types**: Installation, Réparation, Maintenance, Retrait, etc.
|
|
5. **Field app fetches** the template for the current job type and renders it dynamically
|
|
|
|
### Wizard Template Doctype
|
|
|
|
```
|
|
Wizard Template
|
|
name: "Installation FTTH"
|
|
job_type: "Installation"
|
|
is_active: Check
|
|
steps: (child table: Wizard Template Step)
|
|
- step_order: 1
|
|
step_id: "equipment_checklist"
|
|
title: "Équipements installés"
|
|
field_type: "checklist"
|
|
field_config: '{"items":["ONT branché","Routeur configuré","Décodeur branché"]}'
|
|
required: 1
|
|
visible_condition: null
|
|
- step_order: 2
|
|
step_id: "ont_scan"
|
|
title: "Scanner ONT"
|
|
field_type: "scan"
|
|
field_config: '{"placeholder":"Scanner le code-barres ONT"}'
|
|
required: 1
|
|
- ...
|
|
```
|
|
|
|
### Admin Builder UI
|
|
|
|
```
|
|
┌─ Form Builder: Installation FTTH ─────────────────────────┐
|
|
│ │
|
|
│ ┌─ Step 1 ─────────────────────────── [≡] [✏️] [🗑] ──┐ │
|
|
│ │ 📋 Checklist: "Équipements installés" │ │
|
|
│ │ Items: ONT, Routeur, Décodeur, Téléphone │ │
|
|
│ └─────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ ┌─ Step 2 ─────────────────────────── [≡] [✏️] [🗑] ──┐ │
|
|
│ │ 📷 Scan: "Scanner ONT" │ │
|
|
│ │ Required: Oui │ │
|
|
│ └─────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ ┌─ Step 3 ─────────────────────────── [≡] [✏️] [🗑] ──┐ │
|
|
│ │ ✅ Toggle: "Signal ONT OK?" │ │
|
|
│ └─────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ [+ Ajouter une étape] │
|
|
│ │
|
|
│ ┌─ Aperçu téléphone ──┐ │
|
|
│ │ ┌────────────────┐ │ │
|
|
│ │ │ Étape 1/7 │ │ │
|
|
│ │ │ │ │ │
|
|
│ │ │ ☐ ONT branché │ │ │
|
|
│ │ │ ☐ Routeur │ │ │
|
|
│ │ │ ☐ Décodeur │ │ │
|
|
│ │ │ │ │ │
|
|
│ │ │ [Suivant →] │ │ │
|
|
│ │ │ ● ○ ○ ○ ○ ○ ○ │ │ │
|
|
│ │ └────────────────┘ │ │
|
|
│ └──────────────────────┘ │
|
|
│ │
|
|
│ [Sauvegarder] [Publier] │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
### 4. Equipment Swap Wizard (Defective → Replacement)
|
|
|
|
When tech taps "Remplacer" on a device or from the "Plus" menu:
|
|
|
|
```
|
|
Step 1: "Équipement défectueux" [search/scan]
|
|
Scan le code-barres de l'ancien équipement
|
|
→ OU chercher par numéro de série
|
|
→ Affiche: RCMG19E0AB57 — ONT Raisecom HT803G-WS2
|
|
Client: Jean Tremblay
|
|
Adresse: 123 rue Principale
|
|
|
|
Step 2: "Raison du remplacement" [select-cards]
|
|
🔴 Défectueux (ne s'allume plus)
|
|
🟡 Performance dégradée
|
|
🔵 Mise à niveau (upgrade)
|
|
⚪ Autre
|
|
|
|
Step 3: "Nouvel équipement" [scan]
|
|
📸 Scanner le code-barres du remplacement
|
|
→ Detected: TPLGA1E7FB90
|
|
→ Auto-detect type: XX230v (ONT)
|
|
|
|
Step 4: "Confirmer le remplacement" [info]
|
|
❌ Ancien: RCMG19E0AB57 → sera marqué "Défectueux"
|
|
✅ Nouveau: TPLGA1E7FB90 → sera activé
|
|
|
|
Données transférées automatiquement:
|
|
• WiFi SSID/mot de passe ✓
|
|
• SIP (VoIP) credentials ✓
|
|
• Port OLT: 0/3/2 ONT:12 ✓
|
|
• VLANs: inet/mgmt/tel/tv ✓
|
|
|
|
⚠️ L'OLT sera reconfiguré (ancien ONT supprimé, nouveau enregistré)
|
|
|
|
[Confirmer le remplacement]
|
|
|
|
Step 5: "Vérification" [toggle]
|
|
Nouveau ONT en ligne? [OUI / NON]
|
|
Signal OK? [OUI / NON]
|
|
Services fonctionnels? [OUI / NON]
|
|
|
|
[Terminer]
|
|
```
|
|
|
|
**Backend flow (POST /provision/swap):**
|
|
1. Marks old equipment as "Défectueux" in ERPNext
|
|
2. Creates new Service Equipment with transferred WiFi/VoIP/OLT data
|
|
3. Generates OLT swap commands (delete old ONT, register new)
|
|
4. n8n executes OLT commands via SSH
|
|
5. ACS pushes config to new device on bootstrap
|
|
6. Ops team notified via SSE
|
|
|
|
### 5. Quick Actions Menu (field app "Plus" page)
|
|
|
|
```
|
|
┌──────────────────────────────────────────┐
|
|
│ Actions rapides │
|
|
│ │
|
|
│ ┌────────┐ ┌────────┐ ┌────────┐ │
|
|
│ │ 📷 │ │ 🔄 │ │ 🔧 │ │
|
|
│ │ Scan │ │ Swap │ │ Diag │ │
|
|
│ │ équip. │ │ équip. │ │ réseau │ │
|
|
│ └────────┘ └────────┘ └────────┘ │
|
|
│ │
|
|
│ ┌────────┐ ┌────────┐ ┌────────┐ │
|
|
│ │ 📋 │ │ 📸 │ │ ✏️ │ │
|
|
│ │ Check │ │ Photo │ │ Note │ │
|
|
│ │ -list │ │ site │ │ rapide │ │
|
|
│ └────────┘ └────────┘ └────────┘ │
|
|
└──────────────────────────────────────────┘
|
|
```
|
|
|
|
Each action opens the corresponding wizard flow.
|
|
|
|
---
|
|
|
|
## Implementation Plan
|
|
|
|
### Phase 1: WizardCarousel Component
|
|
1. Create `apps/field/src/components/WizardCarousel.vue` — carousel renderer
|
|
2. Create `apps/field/src/components/wizard-fields/` — one component per field type:
|
|
- `SelectCards.vue` (big tappable cards)
|
|
- `ScanField.vue` (camera + barcode)
|
|
- `ToggleField.vue` (yes/no)
|
|
- `PhotoField.vue` (camera capture)
|
|
- `SignatureField.vue` (touch pad)
|
|
- `ChecklistField.vue` (checkable items)
|
|
- `TextField.vue` (single input)
|
|
- `NotesField.vue` (textarea)
|
|
- `InfoField.vue` (read-only summary)
|
|
3. Create `apps/field/src/composables/useWizard.js` — step state, validation, submission
|
|
|
|
### Phase 2: Hardcoded Wizards
|
|
1. Build "Installation Complete" wizard (7 steps as above)
|
|
2. Build "Equipment Scan & Link" wizard (3 steps)
|
|
3. Integrate into TasksPage "Complete Job" action
|
|
4. Replace current ScanPage equipment creation dialog with wizard flow
|
|
|
|
### Phase 3: Wizard Template Doctype
|
|
1. Create `Wizard Template` + `Wizard Template Step` doctypes in ERPNext
|
|
2. Seed with Installation, Repair, Maintenance templates
|
|
3. Field app fetches template by job_type on wizard open
|
|
4. WizardCarousel renders dynamically from template
|
|
|
|
### Phase 4: Admin Form Builder (Ops App)
|
|
1. Build `/ops/form-builder` page
|
|
2. Drag-and-drop step editor (vue-draggable)
|
|
3. Step property editor (sidebar panel)
|
|
4. Phone preview panel
|
|
5. Publish → saves to ERPNext Wizard Template
|
|
|
|
---
|
|
|
|
## CSS / Styling Notes
|
|
|
|
```scss
|
|
// Wizard slide — full height, centered content
|
|
.wizard-slide {
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
padding: 24px 16px;
|
|
min-height: calc(100vh - 160px); // account for header + progress bar
|
|
}
|
|
|
|
// Select cards — large touch targets
|
|
.select-card {
|
|
min-height: 80px;
|
|
border-radius: 16px;
|
|
font-size: 18px;
|
|
margin-bottom: 12px;
|
|
transition: transform 0.15s, box-shadow 0.15s;
|
|
&:active { transform: scale(0.97); }
|
|
&.selected {
|
|
border: 3px solid var(--q-primary);
|
|
box-shadow: 0 0 0 4px rgba(var(--q-primary-rgb), 0.2);
|
|
}
|
|
}
|
|
|
|
// Progress dots
|
|
.wizard-dots {
|
|
display: flex;
|
|
justify-content: center;
|
|
gap: 8px;
|
|
padding: 16px;
|
|
.dot {
|
|
width: 10px; height: 10px;
|
|
border-radius: 50%;
|
|
background: #ccc;
|
|
&.active { background: var(--q-primary); width: 24px; border-radius: 5px; }
|
|
&.done { background: var(--q-positive); }
|
|
}
|
|
}
|
|
|
|
// Navigation buttons — thumb-friendly
|
|
.wizard-nav {
|
|
display: flex;
|
|
gap: 12px;
|
|
padding: 16px;
|
|
.q-btn { min-height: 56px; font-size: 16px; flex: 1; border-radius: 12px; }
|
|
}
|
|
```
|