gigafibre-fsm/services/targo-hub/preview/index.html
louispaulb 41d9b5f316 feat: flow editor, Gemini QR scanner with offline queue, dispatch planning v2
Major additions accumulated over 9 days — single commit per request.

Flow editor (new):
- Generic visual editor for step trees, usable by project wizard + agent flows
- PROJECT_KINDS / AGENT_KINDS catalogs decouple UI from domain
- Drag-and-drop reorder via vuedraggable with scope isolation per peer group
- Chain-aware depends_on rewrite on reorder (sequential only — DAGs preserved)
- Variable picker with per-applies_to catalog (Customer / Quotation /
  Service Contract / Issue / Subscription), insert + copy-clipboard modes
- trigger_condition helper with domain-specific JSONLogic examples
- Global FlowEditorDialog mounted once in MainLayout, Odoo inline pattern
- Server: targo-hub flow-runtime.js, flow-api.js, flow-templates.js
- ERPNext: Flow Template/Run doctypes, scheduler, 5 seeded system templates
- depends_on chips resolve to step labels instead of opaque "s4" ids

QR/OCR scanner (field app):
- Camera capture → Gemini Vision via targo-hub with 8s timeout
- IndexedDB offline queue retries photos when signal returns
- Watcher merges late-arriving scan results into the live UI

Dispatch:
- Planning mode (draft → publish) with offer pool for unassigned jobs
- Shared presets, recurrence selector, suggested-slots dialog
- PublishScheduleModal, unassign confirmation

Ops app:
- ClientDetailPage composables extraction (useClientData, useDeviceStatus,
  useWifiDiagnostic, useModemDiagnostic)
- Project wizard: shared detail sections, wizard catalog/publish composables
- Address pricing composable + pricing-mock data
- Settings redesign hosting flow templates

Targo-hub:
- Contract acceptance (JWT residential + DocuSeal commercial tracks)
- Referral system
- Modem-bridge diagnostic normalizer
- Device extractors consolidated

Migration scripts:
- Invoice/quote print format setup, Jinja rendering
- Additional import + fix scripts (reversals, dates, customers, payments)

Docs:
- Consolidated: old scattered MDs → HANDOFF, ARCHITECTURE, DATA_AND_FLOWS,
  FLOW_EDITOR_ARCHITECTURE, BILLING_AND_PAYMENTS, CPE_MANAGEMENT,
  APP_DESIGN_GUIDELINES
- Archived legacy wizard PHP for reference
- STATUS snapshots for 2026-04-18/19

Cleanup:
- Removed ~40 generated PDFs/HTMLs (invoice_preview*, rendered_jinja*)
- .gitignore now covers invoice preview output + nested .DS_Store

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 10:44:17 -04:00

105 lines
7.1 KiB
HTML

<!DOCTYPE html><html lang="fr"><head>
<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>Entente de service — TARGO</title>
<style>
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;background:#f5f7fa;color:#1a1a1a;padding:16px;line-height:1.5}
.card{background:#fff;border-radius:14px;padding:24px;max-width:560px;margin:0 auto;box-shadow:0 4px 16px rgba(0,0,0,.06)}
.brand{display:flex;align-items:center;gap:12px;padding-bottom:16px;border-bottom:2px solid #019547;margin-bottom:18px}
.brand .num{margin-left:auto;font-size:12px;color:#888;text-align:right}
h1{font-size:20px;font-weight:800;color:#019547;margin-bottom:4px;letter-spacing:.2px}
.sub{color:#6b7280;font-size:13px;margin-bottom:18px}
.field{display:flex;justify-content:space-between;gap:12px;padding:9px 0;border-bottom:1px solid #f1f5f9}
.field:last-child{border-bottom:none}
.field .label{color:#6b7280;font-size:13px}
.field .value{font-weight:600;font-size:14px;text-align:right}
h3{font-size:14px;color:#019547;margin-top:18px;margin-bottom:8px;font-weight:700;text-transform:uppercase;letter-spacing:.3px}
table{width:100%;border-collapse:collapse;margin:6px 0 10px 0;font-size:12.5px}
th{text-align:left;padding:6px 8px;background:#f4fff6;color:#00733a;font-weight:700;border-bottom:2px solid #019547}
th.r,td.r{text-align:right}
td{padding:6px 8px;border-bottom:1px solid #f1f5f9}
tfoot td{font-weight:700;color:#00733a;background:#f4fff6;border-top:1px solid #019547;border-bottom:none}
.callout{background:#fffbeb;border:1px solid #fde68a;border-radius:10px;padding:12px 14px;margin:14px 0;font-size:13px;line-height:1.55}
.callout .head{color:#b45309;font-weight:700;margin-bottom:4px}
.legal{font-size:12px;color:#6b7280;margin-top:14px;line-height:1.55}
.legal a{color:#019547;text-decoration:none;font-weight:600}
.btn{display:block;width:100%;padding:16px;background:#019547;color:#fff;border:none;border-radius:12px;font-size:16px;font-weight:700;cursor:pointer;margin-top:18px;letter-spacing:.3px}
.btn:active{background:#01733a}
.btn:disabled{background:#9ca3af;cursor:wait}
.ok{text-align:center;padding:40px 20px}
.ok .check{width:64px;height:64px;border-radius:50%;background:#019547;color:#fff;display:flex;align-items:center;justify-content:center;margin:0 auto 16px;font-size:32px;font-weight:700}
.ok h2{color:#019547;margin-bottom:8px;font-size:22px}
.ok p{color:#4b5563;font-size:14px}
#result{display:none}
</style></head><body>
<div class="card" id="contract">
<div class="brand">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 326.39 70.35" width="130" height="28"><path fill="#019547" d="M25.83,15H8.41a4.14,4.14,0,0,1-3.89-2.71L0,1.18H66.59L62.07,12.27A4.14,4.14,0,0,1,58.18,15H40.76V69.19H25.81Z"/><path fill="#019547" d="M90.74.68h18.55a5.63,5.63,0,0,1,5.63,5.6l.26,62.91H99.55L99.76,54H71.87L66.41,65.51a6.45,6.45,0,0,1-5.84,3.68H49.35L73.53,12.11A18.7,18.7,0,0,1,90.74.68M100,40.73l.07-26h-8a6.75,6.75,0,0,0-6.26,4.18L76.73,40.73Z"/><path fill="#019547" d="M124.51,6.81a5.64,5.64,0,0,1,5.65-5.65H155.6c8.64,0,15.37,2.44,19.81,6.91,3.81,3.78,5.84,9.14,5.84,15.53v.18c0,11-5.92,17.87-14.59,21.1l16.61,24.29h-14a6.51,6.51,0,0,1-5.39-2.87L151.21,47.41H139.44V69.17h-15Zm30.12,27.41c7.28,0,11.45-3.89,11.45-9.62v-.21c0-6.42-4.46-9.7-11.74-9.7H139.46V34.22Z"/><path fill="#019547" d="M184.74,35.37v-.18C184.74,15.85,199.8,0,220.4,0,232.65,0,240,3.31,247.13,9.33l-6.28,7.57a5.18,5.18,0,0,1-6.84,1,23.71,23.71,0,0,0-14.11-4.13c-10.88,0-19.52,9.62-19.52,21.18v.19c0,12.43,8.54,21.57,20.6,21.57a24,24,0,0,0,14.09-4.07V42.91h-8.68A6.41,6.41,0,0,1,220,36.53V30h29.54V59.52a44,44,0,0,1-29,10.78c-21.18,0-35.74-14.8-35.74-34.93"/><path fill="#019547" d="M254.09,35.37v-.18C254.09,15.85,269.33,0,290.33,0s36.06,15.66,36.06,35v.18c0,19.34-15.27,35.19-36.24,35.19s-36.06-15.64-36.06-35m56.63,0v-.18c0-11.67-8.54-21.39-20.6-21.39S269.73,23.34,269.73,35v.18c0,11.67,8.54,21.37,20.6,21.37S310.72,47,310.72,35.37"/></svg>
<div class="num">Référence<br><strong>CTR-2026-00042</strong></div>
</div>
<h1>Récapitulatif de votre service</h1>
<div class="sub">Un résumé clair des modalités convenues — rien de plus.</div>
<div class="intro" style="background:#f4fff6;border:1px solid #bde5cb;border-radius:10px;padding:12px 14px;margin-bottom:16px;font-size:13px;color:#00733a;line-height:1.55">
Voici un récapitulatif de votre service Gigafibre. Il reprend ce que nous avons convenu ensemble pour que tout soit clair des deux côtés. Aucune surprise — tout est écrit noir sur blanc ci-dessous.
</div>
<div class="field"><span class="label">Client</span><span class="value">Louis-Paul Bourdon</span></div>
<div class="field"><span class="label">Mensualité</span><span class="value">79.95 $/mois <span style="color:#9ca3af;font-weight:400;font-size:12px">(+taxes)</span></span></div>
<div class="field"><span class="label">Durée</span><span class="value">24 mois</span></div>
<div class="field"><span class="label">Début prévu</span><span class="value">2026-04-25</span></div>
<h3>Promotions appliquées</h3>
<table>
<tbody><tr>
<td>Installation standard</td>
<td class="r"><s style="color:#9ca3af">288 $</s><strong>0 $</strong></td>
</tr><tr>
<td>Carte cadeau bienvenue</td>
<td class="r"><s style="color:#9ca3af">120 $</s><strong>0 $</strong></td>
</tr></tbody>
<tfoot>
<tr><td>Valeur totale de la promotion</td><td class="r">408 $</td></tr>
<tr><td colspan="2" style="font-weight:400;color:#00733a;font-size:12px;text-align:left;">Étalée sur 24 mois</td></tr>
</tfoot>
</table>
<div class="callout">
<div class="head">Changement avant 24 mois ?</div>
Pas de pénalité. On récupère simplement la portion non étalée de la promotion, au prorata des mois restants sur 24 mois. Rien de plus.
</div>
<div class="legal">
En cliquant <strong>« J'accepte »</strong>, vous confirmez que les informations ci-dessus correspondent à ce que nous avons convenu. Les <a href="https://www.targo.ca/conditions" target="_blank">conditions complètes du service</a> (facturation, équipement, Loi 25, juridiction du Québec) s'appliquent également. Votre confirmation est horodatée.
</div>
<button class="btn" onclick="accept()">J'accepte ce récapitulatif</button>
</div>
<div class="card ok" id="result">
<div class="check"></div>
<h2>C'est confirmé !</h2>
<p>Merci ! Une copie du récapitulatif vous sera envoyée par courriel sous peu.</p>
</div>
<script>
async function accept(){
const btn=document.querySelector('.btn');btn.disabled=true;btn.textContent='Enregistrement...';
try{
const r=await fetch('/contract/confirm',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({token:'PREVIEW_TOKEN_DEMO'})});
const d=await r.json();
if(d.ok){document.getElementById('contract').style.display='none';document.getElementById('result').style.display='block';}
else{btn.textContent='Erreur : '+(d.error||'Réessayez');btn.disabled=false;}
}catch(e){btn.textContent='Erreur réseau';btn.disabled=false;}
}
</script></body></html>