fix(fusion_claims): service-booking wizard responsive — namespace Bootstrap classes + reorder media queries
The 19.0.9.5.0 CSS pass (padding/scroll) did not fix "fields sitting on each
other" on small screens. Deep dive against the live web.assets_backend bundle
found two compounding root causes (both measured, not guessed):
1. Dead media query. The @media(max-width:560px) collapse for the inner field
grids (.two/.three) and .timepick was nested BEFORE the base .two/.three/
.timepick rules. Equal specificity → the later base rule wins → the media
query never applied. matchMedia matched at 320/390px yet grids stayed 2–3
columns and crammed/truncated. Moved all @media overrides to the END of the
.o_service_booking block so they win the cascade.
2. Bootstrap class collision. The wizard reused row/card/grid/btn; Odoo's
backend Bootstrap applies .row{display:flex;margin:0 -16px}, .card{display:flex},
etc. globally even under the scoped parent (they win for properties the scoped
rule doesn't set). Measured: every .row computed display:flex + margin-left/
right:-16px. Renamed all layout classes to sb-* (sb-row/sb-card/sb-grid/sb-btn)
in service_booking.xml + service_booking.scss.
Verified at 320/390/768/1280 against the real prod bundle (computed
grid-template-columns now 1fr at <=560px; .sb-row margin 0 / display block;
2-col desktop intact). Documented both gotchas in fusion_claims/CLAUDE.md §47.
Bump fusion_claims 19.0.9.5.0 -> 19.0.9.6.0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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
|
- Add new gotchas in the right format
|
||||||
- Understand the soft-dep on `fusion_faxes` + `fusion_pdf_preview`
|
- Understand the soft-dep on `fusion_faxes` + `fusion_pdf_preview`
|
||||||
- Know the deployment fact that fusion_portal is always co-installed
|
- 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.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
{
|
{
|
||||||
'name': 'Fusion Claims',
|
'name': 'Fusion Claims',
|
||||||
'version': '19.0.9.5.0',
|
'version': '19.0.9.6.0',
|
||||||
'category': 'Sales',
|
'category': 'Sales',
|
||||||
'summary': 'Complete ADP Claims Management with Dashboard, Sales Integration, Billing Automation, and Two-Stage Verification.',
|
'summary': 'Complete ADP Claims Management with Dashboard, Sales Integration, Billing Automation, and Two-Stage Verification.',
|
||||||
'description': """
|
'description': """
|
||||||
|
|||||||
@@ -66,26 +66,17 @@
|
|||||||
.step.draft { margin-left: auto; color: var(--sb-money); background: var(--sb-money-soft); }
|
.step.draft { margin-left: auto; color: var(--sb-money); background: var(--sb-money-soft); }
|
||||||
|
|
||||||
.body { padding: 20px 24px 6px; }
|
.body { padding: 20px 24px 6px; }
|
||||||
.grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
|
.sb-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; }
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
.sb-card {
|
||||||
background: var(--sb-card);
|
background: var(--sb-card);
|
||||||
border: 1px solid var(--sb-border);
|
border: 1px solid var(--sb-border);
|
||||||
border-radius: 13px;
|
border-radius: 13px;
|
||||||
padding: 16px 17px;
|
padding: 16px 17px;
|
||||||
box-shadow: 0 1px 3px rgba(16, 24, 40, .08), 0 1px 2px rgba(16, 24, 40, .06);
|
box-shadow: 0 1px 3px rgba(16, 24, 40, .08), 0 1px 2px rgba(16, 24, 40, .06);
|
||||||
}
|
}
|
||||||
.card.span2 { grid-column: 1 / -1; }
|
.sb-card.span2 { grid-column: 1 / -1; }
|
||||||
.card h3 {
|
.sb-card h3 {
|
||||||
margin: 0 0 13px;
|
margin: 0 0 13px;
|
||||||
font-size: 11.5px;
|
font-size: 11.5px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
@@ -96,8 +87,8 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 7px;
|
gap: 7px;
|
||||||
}
|
}
|
||||||
.card h3 .dot { width: 7px; height: 7px; border-radius: 50%; background: linear-gradient(135deg, #5ba848, #2e7aad); }
|
.sb-card h3 .dot { width: 7px; height: 7px; border-radius: 50%; background: linear-gradient(135deg, #5ba848, #2e7aad); }
|
||||||
.card h3 .tag {
|
.sb-card h3 .tag {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
font-weight: 700;
|
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; }
|
label.fl { display: block; font-size: 12px; font-weight: 600; color: var(--sb-muted); margin: 0 0 5px; }
|
||||||
.row { margin-bottom: 12px; }
|
.sb-row { margin-bottom: 12px; }
|
||||||
.row:last-child { margin-bottom: 0; }
|
.sb-row:last-child { margin-bottom: 0; }
|
||||||
.two { display: grid; grid-template-columns: 1fr 1fr; gap: 11px; }
|
.two { display: grid; grid-template-columns: 1fr 1fr; gap: 11px; }
|
||||||
.three { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 9px; }
|
.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); }
|
.foot .spacer { margin-right: auto; font-size: 12px; color: var(--sb-faint); }
|
||||||
|
|
||||||
.btn {
|
.sb-btn {
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
padding: 11px 18px;
|
padding: 11px 18px;
|
||||||
@@ -285,13 +276,32 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
}
|
}
|
||||||
.btn.ghost { background: transparent; color: var(--sb-muted); border: 1px solid var(--sb-border); }
|
.sb-btn.ghost { background: transparent; color: var(--sb-muted); border: 1px solid var(--sb-border); }
|
||||||
.btn.primary {
|
.sb-btn.primary {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background: linear-gradient(135deg, #5ba848, #2e7aad);
|
background: linear-gradient(135deg, #5ba848, #2e7aad);
|
||||||
box-shadow: 0 3px 10px color-mix(in srgb, #2e7aad 40%, transparent);
|
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; }
|
.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; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,11 +20,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<div class="grid">
|
<div class="sb-grid">
|
||||||
<!-- CUSTOMER -->
|
<!-- CUSTOMER -->
|
||||||
<div class="card">
|
<div class="sb-card">
|
||||||
<h3><span class="dot"></span>Customer</h3>
|
<h3><span class="dot"></span>Customer</h3>
|
||||||
<div class="row">
|
<div class="sb-row">
|
||||||
<div class="seg full">
|
<div class="seg full">
|
||||||
<button t-att-class="{ on: state.custMode === 'existing' }"
|
<button t-att-class="{ on: state.custMode === 'existing' }"
|
||||||
t-on-click="() => this.setCust('existing')">Existing customer</button>
|
t-on-click="() => this.setCust('existing')">Existing customer</button>
|
||||||
@@ -33,22 +33,22 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div t-if="state.custMode === 'existing'">
|
<div t-if="state.custMode === 'existing'">
|
||||||
<div class="row">
|
<div class="sb-row">
|
||||||
<label class="fl">Search by phone, name or SO</label>
|
<label class="fl">Search by phone, name or SO</label>
|
||||||
<input class="f" t-model="state.soSearch" placeholder="e.g. (416) 555-0142 …"/>
|
<input class="f" t-model="state.soSearch" placeholder="e.g. (416) 555-0142 …"/>
|
||||||
<div class="hint">Inbound call? Type the phone number — we match the contact & their history.</div>
|
<div class="hint">Inbound call? Type the phone number — we match the contact & their history.</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div t-if="state.custMode === 'new'">
|
<div t-if="state.custMode === 'new'">
|
||||||
<div class="row two">
|
<div class="sb-row two">
|
||||||
<div><label class="fl">Client name *</label><input class="f" t-model="state.customer.name" placeholder="Full name"/></div>
|
<div><label class="fl">Client name *</label><input class="f" t-model="state.customer.name" placeholder="Full name"/></div>
|
||||||
<div><label class="fl">Phone *</label><input class="f" t-model="state.customer.phone" placeholder="(416) 555-…"/></div>
|
<div><label class="fl">Phone *</label><input class="f" t-model="state.customer.phone" placeholder="(416) 555-…"/></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row"><label class="fl">Email</label><input class="f" type="email" t-model="state.customer.email" placeholder="client@email.com"/></div>
|
<div class="sb-row"><label class="fl">Email</label><input class="f" type="email" t-model="state.customer.email" placeholder="client@email.com"/></div>
|
||||||
<div class="row"><label class="fl">Address</label>
|
<div class="sb-row"><label class="fl">Address</label>
|
||||||
<div class="with-icon"><input class="f" t-model="state.customer.street" placeholder="Start typing an address…"/><span class="pin">📍</span></div>
|
<div class="with-icon"><input class="f" t-model="state.customer.street" placeholder="Start typing an address…"/><span class="pin">📍</span></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row three">
|
<div class="sb-row three">
|
||||||
<div><label class="fl">Unit</label><input class="f" t-model="state.customer.unit" placeholder="#"/></div>
|
<div><label class="fl">Unit</label><input class="f" t-model="state.customer.unit" placeholder="#"/></div>
|
||||||
<div><label class="fl">Buzz</label><input class="f" t-model="state.customer.buzz" placeholder="—"/></div>
|
<div><label class="fl">Buzz</label><input class="f" t-model="state.customer.buzz" placeholder="—"/></div>
|
||||||
<div><label class="fl">City</label><input class="f" t-model="state.customer.city" placeholder="City"/></div>
|
<div><label class="fl">City</label><input class="f" t-model="state.customer.city" placeholder="City"/></div>
|
||||||
@@ -58,9 +58,9 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- SERVICE & PRICING -->
|
<!-- SERVICE & PRICING -->
|
||||||
<div class="card">
|
<div class="sb-card">
|
||||||
<h3><span class="dot"></span>Service & Pricing<span class="tag">$ REVENUE</span></h3>
|
<h3><span class="dot"></span>Service & Pricing<span class="tag">$ REVENUE</span></h3>
|
||||||
<div class="row two">
|
<div class="sb-row two">
|
||||||
<div>
|
<div>
|
||||||
<label class="fl">Device being serviced</label>
|
<label class="fl">Device being serviced</label>
|
||||||
<select class="f" t-on-change="onDevice">
|
<select class="f" t-on-change="onDevice">
|
||||||
@@ -79,7 +79,7 @@
|
|||||||
<input class="f" t-model="state.issue" placeholder="e.g. won't power on"/>
|
<input class="f" t-model="state.issue" placeholder="e.g. won't power on"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row" t-if="!state.inShop">
|
<div class="sb-row" t-if="!state.inShop">
|
||||||
<label class="fl">Service call type</label>
|
<label class="fl">Service call type</label>
|
||||||
<select class="f"
|
<select class="f"
|
||||||
t-on-change="onCallType">
|
t-on-change="onCallType">
|
||||||
@@ -100,9 +100,9 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- SCHEDULE -->
|
<!-- SCHEDULE -->
|
||||||
<div class="card">
|
<div class="sb-card">
|
||||||
<h3><span class="dot"></span>Schedule</h3>
|
<h3><span class="dot"></span>Schedule</h3>
|
||||||
<div class="row two">
|
<div class="sb-row two">
|
||||||
<div><label class="fl">Date</label><input class="f" type="date" t-model="state.date"/></div>
|
<div><label class="fl">Date</label><input class="f" type="date" t-model="state.date"/></div>
|
||||||
<div><label class="fl">Duration</label>
|
<div><label class="fl">Duration</label>
|
||||||
<select class="f" t-model.number="state.durationHr">
|
<select class="f" t-model.number="state.durationHr">
|
||||||
@@ -114,7 +114,7 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="sb-row">
|
||||||
<label class="fl">Start time</label>
|
<label class="fl">Start time</label>
|
||||||
<div class="timepick">
|
<div class="timepick">
|
||||||
<select class="f" t-model.number="state.hour">
|
<select class="f" t-model.number="state.hour">
|
||||||
@@ -140,7 +140,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="endtime">Ends at <b><t t-esc="endLabel"/></b> · your local time</div>
|
<div class="endtime">Ends at <b><t t-esc="endLabel"/></b> · your local time</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="sb-row">
|
||||||
<label class="fl">Technician</label>
|
<label class="fl">Technician</label>
|
||||||
<select class="f" t-model.number="state.technicianId">
|
<select class="f" t-model.number="state.technicianId">
|
||||||
<option value="">— Choose —</option>
|
<option value="">— Choose —</option>
|
||||||
@@ -152,17 +152,17 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- LOCATION -->
|
<!-- LOCATION -->
|
||||||
<div class="card">
|
<div class="sb-card">
|
||||||
<h3><span class="dot"></span>Location</h3>
|
<h3><span class="dot"></span>Location</h3>
|
||||||
<div class="opt" style="border:none; padding-top:0;">
|
<div class="opt" style="border:none; padding-top:0;">
|
||||||
<div class="lab">In-shop job<small>At the store — no call-out, labour @ $<t t-esc="fmt(state.labour.inshop)"/>/hr</small></div>
|
<div class="lab">In-shop job<small>At the store — no call-out, labour @ $<t t-esc="fmt(state.labour.inshop)"/>/hr</small></div>
|
||||||
<div class="sw" t-att-class="{ on: state.inShop }" t-on-click="toggleInShop"></div>
|
<div class="sw" t-att-class="{ on: state.inShop }" t-on-click="toggleInShop"></div>
|
||||||
</div>
|
</div>
|
||||||
<div t-if="!state.inShop">
|
<div t-if="!state.inShop">
|
||||||
<div class="row"><label class="fl">Job address</label>
|
<div class="sb-row"><label class="fl">Job address</label>
|
||||||
<div class="with-icon"><input class="f" t-model="state.customer.street" placeholder="Auto-fills from customer…"/><span class="pin">📍</span></div>
|
<div class="with-icon"><input class="f" t-model="state.customer.street" placeholder="Auto-fills from customer…"/><span class="pin">📍</span></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row two">
|
<div class="sb-row two">
|
||||||
<div><label class="fl">Unit / Suite</label><input class="f" t-model="state.customer.unit" placeholder="#"/></div>
|
<div><label class="fl">Unit / Suite</label><input class="f" t-model="state.customer.unit" placeholder="#"/></div>
|
||||||
<div><label class="fl">Buzz code</label><input class="f" t-model="state.customer.buzz" placeholder="—"/></div>
|
<div><label class="fl">Buzz code</label><input class="f" t-model="state.customer.buzz" placeholder="—"/></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -170,11 +170,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- JOB DETAILS -->
|
<!-- JOB DETAILS -->
|
||||||
<div class="card span2">
|
<div class="sb-card span2">
|
||||||
<h3><span class="dot"></span>Job details</h3>
|
<h3><span class="dot"></span>Job details</h3>
|
||||||
<div class="two">
|
<div class="two">
|
||||||
<div class="row"><label class="fl">Work description</label><textarea class="f" t-model="state.description" placeholder="Symptom, what to check, history…"></textarea></div>
|
<div class="sb-row"><label class="fl">Work description</label><textarea class="f" t-model="state.description" placeholder="Symptom, what to check, history…"></textarea></div>
|
||||||
<div class="row"><label class="fl">Parts / materials to bring</label><textarea class="f" t-model="state.materials" placeholder="Batteries, controller, casters…"></textarea></div>
|
<div class="sb-row"><label class="fl">Parts / materials to bring</label><textarea class="f" t-model="state.materials" placeholder="Batteries, controller, casters…"></textarea></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="opt"><div class="lab">Under manufacturer warranty<small>Parts not billed when covered</small></div><div class="sw" t-att-class="{ on: state.warranty }" t-on-click="() => state.warranty = !state.warranty"></div></div>
|
<div class="opt"><div class="lab">Under manufacturer warranty<small>Parts not billed when covered</small></div><div class="sw" t-att-class="{ on: state.warranty }" t-on-click="() => state.warranty = !state.warranty"></div></div>
|
||||||
<div class="opt"><div class="lab">POD required<small>Capture proof of delivery on completion</small></div><div class="sw" t-att-class="{ on: state.pod }" t-on-click="() => state.pod = !state.pod"></div></div>
|
<div class="opt"><div class="lab">POD required<small>Capture proof of delivery on completion</small></div><div class="sw" t-att-class="{ on: state.pod }" t-on-click="() => state.pod = !state.pod"></div></div>
|
||||||
@@ -197,8 +197,8 @@
|
|||||||
|
|
||||||
<div class="foot">
|
<div class="foot">
|
||||||
<span class="spacer">Local time · America/Toronto · <t t-esc="state.distanceKm"/> km away</span>
|
<span class="spacer">Local time · America/Toronto · <t t-esc="state.distanceKm"/> km away</span>
|
||||||
<button class="btn ghost" t-on-click="() => this.action.doAction({ type: 'ir.actions.act_window_close' })">Cancel</button>
|
<button class="sb-btn ghost" t-on-click="() => this.action.doAction({ type: 'ir.actions.act_window_close' })">Cancel</button>
|
||||||
<button class="btn primary" t-on-click="submit" t-att-disabled="state.saving">Book & Create SO</button>
|
<button class="sb-btn primary" t-on-click="submit" t-att-disabled="state.saving">Book & Create SO</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user