diff --git a/apps/ops/infra/nginx.conf b/apps/ops/infra/nginx.conf index 129958a..9151dfc 100644 --- a/apps/ops/infra/nginx.conf +++ b/apps/ops/infra/nginx.conf @@ -4,6 +4,8 @@ server { root /usr/share/nginx/html; index index.html; + resolver 127.0.0.11 valid=30s; + # ERPNext API proxy — token injected server-side (never in JS bundle) # To rotate: edit this file + docker restart ops-frontend location /api/ { @@ -17,9 +19,10 @@ server { proxy_set_header X-Forwarded-Proto https; } - # Ollama Vision API proxy — for bill/invoice OCR + # Ollama Vision API proxy — for bill/invoice OCR (dynamic resolve, won't crash if ollama is down) location /ollama/ { - proxy_pass http://ollama:11434/; + set $ollama_upstream http://ollama:11434; + proxy_pass $ollama_upstream/; proxy_set_header Host $host; proxy_read_timeout 300s; proxy_send_timeout 300s; diff --git a/apps/ops/src/components/customer/BillingKPIs.vue b/apps/ops/src/components/customer/BillingKPIs.vue deleted file mode 100644 index ec2f6c3..0000000 --- a/apps/ops/src/components/customer/BillingKPIs.vue +++ /dev/null @@ -1,44 +0,0 @@ - - - diff --git a/apps/ops/src/components/shared/TagInput.vue b/apps/ops/src/components/shared/TagInput.vue deleted file mode 100644 index e16cb36..0000000 --- a/apps/ops/src/components/shared/TagInput.vue +++ /dev/null @@ -1,137 +0,0 @@ - - - - - diff --git a/apps/ops/src/components/shared/detail-sections/EquipmentDetail.vue b/apps/ops/src/components/shared/detail-sections/EquipmentDetail.vue index 8184283..7340f5a 100644 --- a/apps/ops/src/components/shared/detail-sections/EquipmentDetail.vue +++ b/apps/ops/src/components/shared/detail-sections/EquipmentDetail.vue @@ -41,6 +41,179 @@ placeholder="—" @saved="v => doc.ownership = v.value" /> + + +
+
+ Diagnostic en direct + {{ online ? 'En ligne' : 'Hors ligne' }} + {{ timeAgo(device.lastInform) }} + +
+ + +
+ + Fibre + {{ device.opticalStatus || '—' }} + + +
+ + +
+
Adresses IP
+
+
+ {{ roleLabel(iface.role) }} + {{ iface.ip }} + {{ iface.ip }} + ({{ iface.name }}) +
+
+
+ + +
+
WiFi
+
+ + +
+ Total clients + + {{ device.wifi.totalClients }} + + ({{ device.wifi.directClients }} direct + {{ device.wifi.meshClients }} mesh) + + +
+
+
+ + +
+
EasyMesh ({{ device.mesh.length }} noeuds)
+
+
+
+ + {{ node.name || 'Node ' + node.id }} + {{ node.clients }} clients +
+
+ {{ node.ip }} + · {{ node.mac }} +
+
+
+
+ + +
+
Ethernet
+
+
+ {{ port.label }} + + {{ port.status || '—' }} + + +
+
+
+ + +
+
+
Firmware ACS{{ device.firmware }}
+
SSID{{ device.ssid }}
+
Uptime{{ formatUptime(device.uptime) }}
+
+
+ + +
+
+ + Appareils connectes + ({{ hosts.total }}) + +
+
+ Interrogation de l'equipement... +
+
+ +
+
+ + + + {{ group.label }} + + {{ group.clients.length }} + + {{ group.ip }} + · {{ group.mac }} +
+
+ + + + + + + + + + + + + + + + + + + + + +
SignalNomIPMACConnexionBail
+ + + {{ formatSignal(h.signal) }} + + + {{ h.name || '—' }}{{ h.ip || '—' }}{{ h.mac || '—' }}{{ h.band || h.connType || '—' }}{{ h.leaseRemaining != null ? formatLease(h.leaseRemaining) : '—' }}
+
+
+
+
Aucun appareil
+
+
+
+ Chargement diagnostic ACS... +
+
Information OLT
- - - diff --git a/apps/ops/src/pages/ClientsPage.vue b/apps/ops/src/pages/ClientsPage.vue index 1d6ed8a..beef76d 100644 --- a/apps/ops/src/pages/ClientsPage.vue +++ b/apps/ops/src/pages/ClientsPage.vue @@ -10,7 +10,7 @@
@@ -150,6 +150,9 @@ function onRequest (props) { doSearch() } -onMounted(doSearch) +// Don't auto-load all clients — start empty, load only on search or if ?q= is present +onMounted(() => { + if (search.value.trim()) doSearch() +}) watch(() => route.query.q, (q) => { if (q) { search.value = q; doSearch() } }) diff --git a/docs/CUSTOMER-FLOW-ARCHITECTURE.md b/docs/CUSTOMER-FLOW-ARCHITECTURE.md index fffc12c..af43275 100644 --- a/docs/CUSTOMER-FLOW-ARCHITECTURE.md +++ b/docs/CUSTOMER-FLOW-ARCHITECTURE.md @@ -228,7 +228,25 @@ One-time charges: 4. **Installation preference**: - Choose from 3 available dates (next 2 weeks, exclude weekends) - OR "Contactez-moi" (we call to schedule) -5. **Payment** via Stripe (first month + one-time charges) +5. **Payment** (Stripe — card registered but NOT charged) + +### Payment Strategy + +**Website path (self-service)**: +- Customer registers their card on Stripe (SetupIntent, not PaymentIntent) +- We create a Stripe Customer + attach the payment method +- **No charge is taken at checkout** — customer sees: "Aucun frais avant la complétion de l'installation à votre satisfaction" +- Payment is collected AFTER installation is confirmed complete by the technician +- On installation completion → auto-charge first month + one-time fees via Stripe + +**Agent path (phone order)**: +- No payment collected upfront +- Agent creates the order and an invoice is generated +- Agent can send a **payment link** via email or SMS to the customer: + - Stripe Checkout Session or Payment Link → customer pays when ready + - SMS via Twilio: "Votre commande est confirmée! Payez ici: {link}" + - Email via Mailjet: order summary + payment button +- Payment can also be collected on-site by tech or post-installation ### What Happens on Submit @@ -243,7 +261,14 @@ POST /api/checkout { sku: "TELEPMENS", type: "phone" } ], preferred_dates: ["2026-04-07", "2026-04-09", "2026-04-11"], - stripe_payment_intent: "pi_xxx" + payment: { + method: "stripe_setup", // website: card registered, not charged + stripe_customer_id: "cus_xxx", // Stripe Customer created + stripe_payment_method: "pm_xxx", // Card saved for later charge + // OR for agent path: + method: "invoice_later", // agent: no payment upfront + send_payment_link: "email|sms|both" // optional: send link to customer + } } ``` diff --git a/docs/DATA-STRUCTURE-FOUNDATION.md b/docs/DATA-STRUCTURE-FOUNDATION.md index d88604d..f335169 100644 --- a/docs/DATA-STRUCTURE-FOUNDATION.md +++ b/docs/DATA-STRUCTURE-FOUNDATION.md @@ -151,9 +151,20 @@ Output: customer data ready for creation ### Step 4: Payment (Stripe) ``` -Input: card details (Stripe Elements) -Output: payment_intent confirmed - First month pro-rated + one-time charges +WEBSITE PATH: + Input: card details (Stripe Elements) + Output: SetupIntent confirmed — card SAVED, NOT charged + Stripe Customer + PaymentMethod created + Customer sees: "Aucun frais avant la complétion de l'installation" + Charge triggered AFTER tech confirms installation complete + +AGENT PATH: + No payment collected upfront + Invoice generated → agent sends payment link via email/SMS + POST /api/send-payment-link + { customer_id, method: "email|sms|both" } + → Creates Stripe Payment Link or Checkout Session + → Sends via Twilio SMS / Mailjet email ``` ### Step 5: Order Creation (atomic — all or nothing) diff --git a/docs/DESIGN_GUIDELINES.md b/docs/DESIGN_GUIDELINES.md new file mode 100644 index 0000000..3134666 --- /dev/null +++ b/docs/DESIGN_GUIDELINES.md @@ -0,0 +1,67 @@ +# Gigafibre FSM — Design Guidelines + +This document outlines the standard design principles and conventions for adding new features and modules to the Gigafibre FSM (Field Service Management) application. Adhering to these guidelines ensures scalability, maintainability, and highly efficient AI-assisted development (by keeping context windows small and token usage low). + +## 1. Modular Architecture (Feature-Sliced Design) + +To avoid a monolithic `src/` folder that overwhelms both developers and AI tools, organize code by **feature** rather than strictly by technical type. + +**Do Not Do This (Technical Grouping):** +```text +src/ + components/ (contains dispatch, inventory, and customer components all mixed together) + store/ (one massive pinia store for everything) + api/ (one 2000-line api.js file) +``` + +**Do This (Feature Grouping):** +```text +src/ + features/ + dispatch/ + components/ + store.ts + api.ts + types.ts + equipment/ + components/ + store.ts + api.ts + types.ts + shared/ + ui/ (generic buttons, dialogs) + utils/ +``` +*Why?* When you need AI to build a new dispatch feature, you only need to feed it the `features/dispatch/` folder, drastically reducing token usage and hallucination. + +## 2. API & ERPNext Abstraction + +Never make raw API calls (`axios.get`) directly inside a Vue component. +* All ERPNext interactions must go through a dedicated API service file (`features/{module}/api.ts`). +* **Rule:** Vue components should only dispatch actions (via Pinia) or call cleanly abstracted service functions. They should not care about Frappe endpoints or REST wrappers. + +## 3. UI Component Standardization (Quasar) + +* **Composition API:** Use Vue 3 `