diff --git a/fusion_claims/CLAUDE.md b/fusion_claims/CLAUDE.md index 9314bd1c..b7333db2 100644 --- a/fusion_claims/CLAUDE.md +++ b/fusion_claims/CLAUDE.md @@ -3104,3 +3104,40 @@ After 9 rounds of deep diving, here's what CLAUDE.md covers vs the codebase: - Add new gotchas in the right format - Understand the soft-dep on `fusion_faxes` + `fusion_pdf_preview` - Know the deployment fact that fusion_portal is always co-installed + +## 47. Service Booking wizard — two CSS gotchas (client action, `static/src/scss/service_booking.scss` + `xml/service_booking.xml`) + +The OWL "Book a Service" wizard renders inside the Odoo **backend** (`web.assets_backend`), +so the full Bootstrap 5 + Odoo stylesheet is live around it. Two non-obvious traps bit this +wizard and were fixed in **v19.0.9.6.0** (a first, blind CSS pass in 19.0.9.5.0 did not fix +the real cause — verify with a render, not by eye): + +1. **Never reuse Bootstrap layout class names inside a backend component — namespace them.** + The wizard originally used `row` / `card` / `grid` / `btn`. Scoping the rules under + `.o_service_booking` does **not** stop Bootstrap's *global* `.row{display:flex; + margin-left/right:calc(-.5*32px)}`, `.card{display:flex;flex-direction:column}`, + `.grid{grid-template-rows:…}`, `.btn{…}` from also applying (they win for any property + the scoped rule doesn't set). Measured live: every wizard `.row` computed + `display:flex; margin-left:-16px; margin-right:-16px` — the negative gutter pulled fields + to the card edges and flexed label+input pairs. **Fix:** all custom layout classes are + `sb-*` (`sb-row`/`sb-card`/`sb-grid`/`sb-btn`). Keep that prefix for any new wizard class + that could collide with Bootstrap (`col`, `container`, `form-*`, `badge`, …). + +2. **A nested `@media` block must come AFTER the base rule it overrides (equal specificity).** + SCSS preserves source order. The responsive `@media (max-width:560px){ .two,.three{ + grid-template-columns:1fr } … }` was nested high in the file, *before* the base + `.two{grid-template-columns:1fr 1fr}` / `.three` / `.timepick` rules. Both selectors have + the same specificity, so the later base rule overrode the media query — it was **dead**, + and the inner field-grids never collapsed to one column on a phone (fields crammed 2–3 + across). `matchMedia('(max-width:560px)')` returned true while the columns stayed 2-up — + the tell that it's a cascade-order bug, not a media-match bug. **Fix:** all responsive + `@media` overrides live at the **end** of the `.o_service_booking { … }` block. + +**How it was verified (do this, don't eyeball):** pull the live compiled bundle from prod +(`env['ir.qweb']._get_asset_bundle('web.assets_backend').css()` returns `ir.attachment` +record(s) in Odoo 19 — read `.raw`, not a string), render the wizard markup against it with +the real web-client height/scroll chain (`html,body{height:100%}` → `.o_web_client` flex +column → 46px navbar + `.o_action_manager{flex:1;min-height:0}` so the wizard's +`height:100%;overflow:auto` scrolls) at 320/390/768/1280, and read computed +`grid-template-columns` / `margin-left` / `display`. A standalone vanilla-Bootstrap repro is +**not** faithful — it rendered fine and falsely cleared the bug. diff --git a/fusion_claims/__manifest__.py b/fusion_claims/__manifest__.py index 86e95d55..3224b587 100644 --- a/fusion_claims/__manifest__.py +++ b/fusion_claims/__manifest__.py @@ -5,7 +5,7 @@ { 'name': 'Fusion Claims', - 'version': '19.0.9.5.0', + 'version': '19.0.9.6.0', 'category': 'Sales', 'summary': 'Complete ADP Claims Management with Dashboard, Sales Integration, Billing Automation, and Two-Stage Verification.', 'description': """ diff --git a/fusion_claims/static/src/scss/service_booking.scss b/fusion_claims/static/src/scss/service_booking.scss index 307ece27..dc1b124a 100644 --- a/fusion_claims/static/src/scss/service_booking.scss +++ b/fusion_claims/static/src/scss/service_booking.scss @@ -66,26 +66,17 @@ .step.draft { margin-left: auto; color: var(--sb-money); background: var(--sb-money-soft); } .body { padding: 20px 24px 6px; } - .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; } - @media (max-width: 780px) { .grid { grid-template-columns: 1fr; } } - @media (max-width: 560px) { - .wrap { margin: 12px auto; padding: 0 10px; } - .body { padding: 14px 16px 4px; } - .topbar { padding: 14px 16px; } - .foot { padding: 14px 16px; flex-wrap: wrap; } - .two, .three { grid-template-columns: 1fr; } - .timepick { flex-wrap: wrap; } - } + .sb-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; } - .card { + .sb-card { background: var(--sb-card); border: 1px solid var(--sb-border); border-radius: 13px; padding: 16px 17px; box-shadow: 0 1px 3px rgba(16, 24, 40, .08), 0 1px 2px rgba(16, 24, 40, .06); } - .card.span2 { grid-column: 1 / -1; } - .card h3 { + .sb-card.span2 { grid-column: 1 / -1; } + .sb-card h3 { margin: 0 0 13px; font-size: 11.5px; font-weight: 700; @@ -96,8 +87,8 @@ align-items: center; gap: 7px; } - .card h3 .dot { width: 7px; height: 7px; border-radius: 50%; background: linear-gradient(135deg, #5ba848, #2e7aad); } - .card h3 .tag { + .sb-card h3 .dot { width: 7px; height: 7px; border-radius: 50%; background: linear-gradient(135deg, #5ba848, #2e7aad); } + .sb-card h3 .tag { margin-left: auto; font-size: 10px; font-weight: 700; @@ -109,8 +100,8 @@ } label.fl { display: block; font-size: 12px; font-weight: 600; color: var(--sb-muted); margin: 0 0 5px; } - .row { margin-bottom: 12px; } - .row:last-child { margin-bottom: 0; } + .sb-row { margin-bottom: 12px; } + .sb-row:last-child { margin-bottom: 0; } .two { display: grid; grid-template-columns: 1fr 1fr; gap: 11px; } .three { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 9px; } @@ -276,7 +267,7 @@ } .foot .spacer { margin-right: auto; font-size: 12px; color: var(--sb-faint); } - .btn { + .sb-btn { border: none; border-radius: 10px; padding: 11px 18px; @@ -285,13 +276,32 @@ cursor: pointer; font-family: inherit; } - .btn.ghost { background: transparent; color: var(--sb-muted); border: 1px solid var(--sb-border); } - .btn.primary { + .sb-btn.ghost { background: transparent; color: var(--sb-muted); border: 1px solid var(--sb-border); } + .sb-btn.primary { color: #fff; background: linear-gradient(135deg, #5ba848, #2e7aad); box-shadow: 0 3px 10px color-mix(in srgb, #2e7aad 40%, transparent); } - .btn[disabled] { opacity: .6; cursor: not-allowed; } + .sb-btn[disabled] { opacity: .6; cursor: not-allowed; } .hide { display: none !important; } + + // Responsive overrides — MUST come AFTER the base layout rules above. These + // selectors (.two/.three/.timepick/.sb-grid/.foot/…) have the same specificity + // as their base rules, so the cascade only lets the media query win when it is + // emitted later in the source. Previously this block sat right after .sb-grid + // (BEFORE the base .two/.three/.timepick rules), so the later base rules + // overrode it and the inner field-grids never collapsed to one column on a + // phone — fields crammed side-by-side. Keep these last. + @media (max-width: 780px) { + .sb-grid { grid-template-columns: 1fr; } + } + @media (max-width: 560px) { + .wrap { margin: 12px auto; padding: 0 10px; } + .body { padding: 14px 16px 4px; } + .topbar { padding: 14px 16px; } + .foot { padding: 14px 16px; flex-wrap: wrap; } + .two, .three { grid-template-columns: 1fr; } + .timepick { flex-wrap: wrap; } + } } diff --git a/fusion_claims/static/src/xml/service_booking.xml b/fusion_claims/static/src/xml/service_booking.xml index cb159716..188c2e99 100644 --- a/fusion_claims/static/src/xml/service_booking.xml +++ b/fusion_claims/static/src/xml/service_booking.xml @@ -20,11 +20,11 @@
-
+
-
+

Customer

-
+
@@ -33,22 +33,22 @@
-
+
Inbound call? Type the phone number — we match the contact & their history.
-
+
-
-
+
+
📍
-
+
@@ -58,9 +58,9 @@
-
+

Service & Pricing$ REVENUE

-
+
-
+
-
+
@@ -152,17 +152,17 @@
-
+

Location

In-shop jobAt the store — no call-out, labour @ $/hr
-
+
📍
-
+
@@ -170,11 +170,11 @@
-
+

Job details

-
-
+
+
Under manufacturer warrantyParts not billed when covered
POD requiredCapture proof of delivery on completion
@@ -197,8 +197,8 @@
Local time · America/Toronto · km away - - + +