1052 lines
63 KiB
XML
1052 lines
63 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
|
<odoo>
|
|
|
|
<!-- ============================================================ -->
|
|
<!-- ASSESSMENT LIST PAGE -->
|
|
<!-- ============================================================ -->
|
|
<template id="portal_quotation_list" name="Equipment Assessments List">
|
|
<t t-call="portal.portal_layout">
|
|
<t t-set="breadcrumbs_searchbar" t-value="True"/>
|
|
|
|
<div class="o_portal_my_home">
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<h3>Equipment Assessments</h3>
|
|
<div class="dropdown">
|
|
<button class="btn btn-primary dropdown-toggle" type="button"
|
|
data-bs-toggle="dropdown" aria-expanded="false">
|
|
<i class="fa fa-plus me-1"/> New Assessment
|
|
</button>
|
|
<ul class="dropdown-menu dropdown-menu-end">
|
|
<t t-foreach="equipment_types" t-as="etype">
|
|
<li>
|
|
<a class="dropdown-item"
|
|
t-attf-href="/my/quotation/builder/new?equipment_type=#{etype.code}">
|
|
<i t-attf-class="fa #{etype.icon or 'fa-cog'} me-2"/>
|
|
<t t-out="etype.name"/>
|
|
</a>
|
|
</li>
|
|
</t>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<t t-if="not assessments">
|
|
<div class="alert alert-info text-center">
|
|
No assessments yet. Click "New Assessment" to start.
|
|
</div>
|
|
</t>
|
|
|
|
<t t-if="assessments">
|
|
<table class="table table-hover">
|
|
<thead>
|
|
<tr>
|
|
<th>Reference</th>
|
|
<th>Client</th>
|
|
<th>Equipment</th>
|
|
<th>Date</th>
|
|
<th>Status</th>
|
|
<th>Total</th>
|
|
<th/>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<t t-foreach="assessments" t-as="a">
|
|
<tr>
|
|
<td><t t-out="a.reference"/></td>
|
|
<td><t t-out="a.client_name or 'N/A'"/></td>
|
|
<td><t t-out="equip_type_map.get(a.equipment_type, a.equipment_type or '')"/></td>
|
|
<td><t t-out="a.assessment_date" t-options="{'widget': 'date'}"/></td>
|
|
<td>
|
|
<span t-attf-class="badge #{
|
|
'text-bg-info' if a.state == 'draft' else
|
|
'text-bg-warning' if a.state == 'review' else
|
|
'text-bg-success' if a.state == 'quoted' else
|
|
'text-bg-secondary'
|
|
}">
|
|
<t t-out="dict(a._fields['state'].selection).get(a.state, '')"/>
|
|
</span>
|
|
</td>
|
|
<td>$<t t-out="'%.2f' % a.total_estimate"/></td>
|
|
<td>
|
|
<a t-attf-href="/my/quotation/builder/#{a.id}/edit"
|
|
class="btn btn-sm btn-outline-primary">
|
|
<t t-if="a.state == 'draft'">Continue</t>
|
|
<t t-else="">View</t>
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
</t>
|
|
</tbody>
|
|
</table>
|
|
</t>
|
|
</div>
|
|
</t>
|
|
</template>
|
|
|
|
<!-- ============================================================ -->
|
|
<!-- MULTI-STEP ASSESSMENT FORM (Dynamic) -->
|
|
<!-- ============================================================ -->
|
|
<template id="portal_quotation_form" name="Equipment Assessment Form">
|
|
<t t-call="portal.portal_layout">
|
|
<t t-set="additional_title">Equipment Assessment</t>
|
|
|
|
<div class="wc-assessment-form" id="wcAssessmentForm">
|
|
|
|
<!-- Success/Error Messages -->
|
|
<t t-if="request.params.get('success') == 'saved'">
|
|
<div class="alert alert-success alert-dismissible fade show">
|
|
Assessment saved successfully.
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert"/>
|
|
</div>
|
|
</t>
|
|
<t t-if="request.params.get('success') == 'quotation_generated'">
|
|
<div class="alert alert-success alert-dismissible fade show">
|
|
<i class="fa fa-check-circle me-1"/> Quotation generated successfully!
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert"/>
|
|
</div>
|
|
</t>
|
|
<t t-if="request.params.get('error')">
|
|
<div class="alert alert-danger alert-dismissible fade show">
|
|
<t t-out="request.params.get('error')"/>
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert"/>
|
|
</div>
|
|
</t>
|
|
|
|
<!-- ===== DYNAMIC Step Indicator ===== -->
|
|
<div class="wc-steps mb-4">
|
|
<div class="d-flex justify-content-between">
|
|
<t t-foreach="flow_steps" t-as="fstep">
|
|
<div t-attf-class="wc-step-indicator text-center flex-fill #{'active' if fstep_index == 0 else ''}"
|
|
t-att-data-step="fstep_index + 1">
|
|
<div class="wc-step-number rounded-circle d-inline-flex align-items-center justify-content-center">
|
|
<t t-out="fstep_index + 1"/>
|
|
</div>
|
|
<div class="wc-step-label small mt-1">
|
|
<t t-out="fstep.name"/>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Open in Backend link — only for internal users -->
|
|
<t t-if="assessment and request.env.user.has_group('base.group_user')">
|
|
<div class="text-end mb-2">
|
|
<a t-attf-href="/odoo/fusion-quotation-builder/#{assessment.id}"
|
|
class="btn btn-sm btn-outline-secondary" target="_blank">
|
|
<i class="fa fa-pencil me-1"/> Open in Backend
|
|
</a>
|
|
</div>
|
|
</t>
|
|
|
|
<form method="post"
|
|
t-att-action="'/quotation/form/' + access_token + '/save'
|
|
if is_public else '/my/quotation/builder/save'"
|
|
id="wcForm" class="wc-form">
|
|
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
|
|
<input type="hidden" name="assessment_id"
|
|
t-att-value="assessment.id if assessment else 0"/>
|
|
<input type="hidden" name="current_step" id="currentStep" value="1"/>
|
|
<input type="hidden" name="action" id="formAction" value="save"/>
|
|
<!-- Store access token for JS to detect public mode -->
|
|
<t t-if="is_public">
|
|
<input type="hidden" name="access_token" id="accessToken"
|
|
t-att-value="access_token"/>
|
|
</t>
|
|
|
|
<!-- Hidden fields for line data (populated by JS) -->
|
|
<input type="hidden" name="line_product_ids" id="lineProductIds" value=""/>
|
|
<input type="hidden" name="line_section_ids" id="lineSectionIds" value=""/>
|
|
<input type="hidden" name="line_build_types" id="lineBuildTypes" value=""/>
|
|
<input type="hidden" name="line_quantities" id="lineQuantities" value=""/>
|
|
<input type="hidden" name="line_rationales" id="lineRationales" value=""/>
|
|
|
|
<!-- ===== DYNAMIC Step Content ===== -->
|
|
<t t-foreach="flow_steps" t-as="fstep">
|
|
<div t-attf-class="wc-step #{'d-none' if fstep_index > 0 else ''}"
|
|
t-att-data-step="fstep_index + 1"
|
|
t-att-data-step-type="fstep.step_type">
|
|
<t t-if="fstep.step_type == 'client_info'"
|
|
t-call="fusion_quotations.fq_step_client"/>
|
|
<t t-if="fstep.step_type == 'measurements'"
|
|
t-call="fusion_quotations.fq_step_measurements"/>
|
|
<t t-if="fstep.step_type == 'product_select'"
|
|
t-call="fusion_quotations.fq_step_product_select"/>
|
|
<t t-if="fstep.step_type == 'options'"
|
|
t-call="fusion_quotations.fq_step_options"/>
|
|
<t t-if="fstep.step_type == 'review'"
|
|
t-call="fusion_quotations.fq_step_review"/>
|
|
<t t-if="fstep.step_type == 'custom'"
|
|
t-call="fusion_quotations.fq_step_custom"/>
|
|
</div>
|
|
</t>
|
|
|
|
<!-- ============================================ -->
|
|
<!-- NAVIGATION BUTTONS -->
|
|
<!-- ============================================ -->
|
|
<div class="d-flex justify-content-between mt-4 pt-3 border-top">
|
|
<button type="button" class="btn btn-outline-secondary" id="btnPrev"
|
|
style="display:none;">
|
|
<i class="fa fa-arrow-left me-1"/> Previous
|
|
</button>
|
|
<div class="ms-auto d-flex gap-2">
|
|
<button type="submit" class="btn btn-outline-primary" id="btnSave"
|
|
name="action" value="save">
|
|
<i class="fa fa-save me-1"/> Save Draft
|
|
</button>
|
|
<button type="button" class="btn btn-primary" id="btnNext">
|
|
Next <i class="fa fa-arrow-right ms-1"/>
|
|
</button>
|
|
<button type="submit" class="btn btn-success d-none" id="btnGenerate"
|
|
name="action" value="generate">
|
|
<i class="fa fa-magic me-1"/> Generate Quotation
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
</form>
|
|
</div>
|
|
</t>
|
|
</template>
|
|
|
|
<!-- ============================================================ -->
|
|
<!-- SUB-TEMPLATE: Client & Equipment (step_type=client_info) -->
|
|
<!-- ============================================================ -->
|
|
<template id="fq_step_client" name="Step: Client & Equipment">
|
|
<!--
|
|
Config-driven client step. The step's fields_json controls which
|
|
optional field groups are shown. Example fields_json:
|
|
{
|
|
"show_health_card": true,
|
|
"show_dob": true,
|
|
"show_adp_fields": true,
|
|
"show_wheelchair_category": true,
|
|
"show_powerchair_category": false
|
|
}
|
|
Steps with no fields_json (or empty {}) show only the base fields:
|
|
name, contact, address, and equipment type selector.
|
|
-->
|
|
<t t-set="client_config" t-value="step_fields.get(fstep.id, {})"/>
|
|
<h4 class="mb-3">Client & Equipment</h4>
|
|
|
|
<div class="row g-3">
|
|
<!-- ── LEFT COLUMN: Client Info ── -->
|
|
<div class="col-md-6">
|
|
<div class="card wc-section-card h-100">
|
|
<div class="card-header"><h5 class="mb-0"><i class="fa fa-user me-2"/>Client</h5></div>
|
|
<div class="card-body">
|
|
<div class="mb-3 wc-search-container" style="position:relative;z-index:100;">
|
|
<label class="form-label fw-bold">Search Existing Client</label>
|
|
<div class="input-group">
|
|
<span class="input-group-text"><i class="fa fa-search"/></span>
|
|
<input type="text" class="form-control" id="clientSearch"
|
|
placeholder="Type name, phone, or health card..."
|
|
autocomplete="off"/>
|
|
</div>
|
|
<div id="clientSearchResults" class="list-group d-none"
|
|
style="position:absolute;z-index:1060;width:100%;max-height:300px;overflow-y:auto;background:var(--bs-body-bg);box-shadow:0 8px 30px rgba(0,0,0,.18);border:1px solid var(--bs-border-color);border-top:none;border-radius:0 0 .5rem .5rem;"/>
|
|
<input type="hidden" name="partner_id" id="partnerId"
|
|
t-att-value="assessment.partner_id.id if assessment and assessment.partner_id else 0"/>
|
|
</div>
|
|
<div id="selectedClient" class="alert alert-info d-none mb-3">
|
|
<strong id="selectedClientName"/>
|
|
<button type="button" class="btn btn-sm btn-outline-secondary ms-2"
|
|
id="clearClient">Change</button>
|
|
</div>
|
|
<div id="newClientFields">
|
|
<div class="row g-2 mb-2">
|
|
<div class="col">
|
|
<input type="text" name="client_first_name" class="form-control"
|
|
placeholder="First Name"
|
|
t-att-value="assessment.client_first_name if assessment else ''"/>
|
|
</div>
|
|
<div class="col">
|
|
<input type="text" name="client_last_name" class="form-control"
|
|
placeholder="Last Name"
|
|
t-att-value="assessment.client_last_name if assessment else ''"/>
|
|
</div>
|
|
</div>
|
|
<div class="row g-2 mb-2">
|
|
<div class="col">
|
|
<input type="tel" name="client_phone" class="form-control"
|
|
placeholder="Phone"
|
|
t-att-value="assessment.client_phone if assessment else ''"/>
|
|
</div>
|
|
<div class="col">
|
|
<input type="email" name="client_email" class="form-control"
|
|
placeholder="Email"
|
|
t-att-value="assessment.client_email if assessment else ''"/>
|
|
</div>
|
|
</div>
|
|
<input type="text" name="client_street" class="form-control mb-2"
|
|
placeholder="Street Address"
|
|
t-att-value="assessment.client_street if assessment else ''"/>
|
|
<div class="row g-2 mb-2">
|
|
<div class="col">
|
|
<input type="text" name="client_city" class="form-control"
|
|
placeholder="City"
|
|
t-att-value="assessment.client_city if assessment else ''"/>
|
|
</div>
|
|
<div class="col">
|
|
<select name="client_state_id" class="form-select">
|
|
<option value="">Province</option>
|
|
<t t-foreach="provinces" t-as="prov">
|
|
<option t-att-value="prov.id"
|
|
t-att-selected="assessment and assessment.client_state_id.id == prov.id">
|
|
<t t-out="prov.name"/>
|
|
</option>
|
|
</t>
|
|
</select>
|
|
</div>
|
|
<div class="col-3">
|
|
<input type="text" name="client_zip" class="form-control"
|
|
placeholder="Postal Code"
|
|
t-att-value="assessment.client_zip if assessment else ''"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- Health Card — controlled by step config -->
|
|
<t t-if="client_config.get('show_health_card') or client_config.get('show_dob')">
|
|
<div class="row g-2 mb-2">
|
|
<t t-if="client_config.get('show_health_card')">
|
|
<div class="col">
|
|
<label class="form-label">Health Card</label>
|
|
<input type="text" name="client_health_card" class="form-control"
|
|
t-att-value="assessment.client_health_card if assessment else ''"/>
|
|
</div>
|
|
</t>
|
|
<t t-if="client_config.get('show_dob')">
|
|
<div class="col">
|
|
<label class="form-label">Date of Birth</label>
|
|
<input type="date" name="client_dob" class="form-control"
|
|
t-att-value="assessment.client_dob if assessment else ''"/>
|
|
</div>
|
|
</t>
|
|
</div>
|
|
</t>
|
|
</div><!-- /card-body -->
|
|
</div><!-- /card -->
|
|
</div>
|
|
|
|
<!-- ── RIGHT COLUMN: Equipment Config ── -->
|
|
<div class="col-md-6">
|
|
<div class="card wc-section-card h-100">
|
|
<div class="card-header"><h5 class="mb-0"><i class="fa fa-cogs me-2"/>Equipment</h5></div>
|
|
<div class="card-body">
|
|
<!-- Equipment type — show current type as label + hidden field -->
|
|
<div class="mb-3">
|
|
<label class="form-label fw-bold">Equipment Type</label>
|
|
<div class="d-flex align-items-center gap-2">
|
|
<t t-foreach="equipment_types" t-as="etype">
|
|
<t t-if="etype.code == equipment_type">
|
|
<span class="badge bg-primary fs-6 py-2 px-3">
|
|
<i t-attf-class="fa #{etype.icon or 'fa-cog'} me-1"/> <t t-out="etype.name"/>
|
|
</span>
|
|
</t>
|
|
</t>
|
|
<input type="hidden" name="equipment_type" t-att-value="equipment_type"/>
|
|
</div>
|
|
</div>
|
|
<!-- Wheelchair category — controlled by step config -->
|
|
<t t-if="client_config.get('show_wheelchair_category')">
|
|
<div class="mb-3" id="wheelchairTypeGroup">
|
|
<label class="form-label">Wheelchair Category</label>
|
|
<select name="wheelchair_type" class="form-select">
|
|
<option value="">Select...</option>
|
|
<option value="type_1" t-att-selected="assessment and assessment.wheelchair_type == 'type_1'">Type 1 - Standard</option>
|
|
<option value="type_2" t-att-selected="assessment and assessment.wheelchair_type == 'type_2'">Type 2 - Lightweight</option>
|
|
<option value="type_3" t-att-selected="assessment and assessment.wheelchair_type == 'type_3'">Type 3 - Ultra Lightweight</option>
|
|
<option value="type_4" t-att-selected="assessment and assessment.wheelchair_type == 'type_4'">Type 4 - Rigid Frame</option>
|
|
<option value="type_5" t-att-selected="assessment and assessment.wheelchair_type == 'type_5'">Type 5 - Dynamic Tilt</option>
|
|
</select>
|
|
</div>
|
|
</t>
|
|
<!-- Powerchair category — controlled by step config -->
|
|
<t t-if="client_config.get('show_powerchair_category')">
|
|
<div class="mb-3" id="powerchairTypeGroup">
|
|
<label class="form-label">Powerchair Category</label>
|
|
<select name="powerchair_type" class="form-select">
|
|
<option value="">Select...</option>
|
|
<option value="type_1" t-att-selected="assessment and assessment.powerchair_type == 'type_1'">Power Base Type 1</option>
|
|
<option value="type_2" t-att-selected="assessment and assessment.powerchair_type == 'type_2'">Power Base Type 2</option>
|
|
<option value="type_3" t-att-selected="assessment and assessment.powerchair_type == 'type_3'">Power Base Type 3</option>
|
|
</select>
|
|
</div>
|
|
</t>
|
|
<!-- ADP fields (client type + reason) — controlled by step config -->
|
|
<t t-if="client_config.get('show_adp_fields')">
|
|
<div class="mb-3">
|
|
<label class="form-label">Client Type</label>
|
|
<select name="client_type" class="form-select">
|
|
<option value="reg" t-att-selected="not assessment or assessment.client_type == 'reg'">REG - Regular ADP (75/25)</option>
|
|
<option value="ods" t-att-selected="assessment and assessment.client_type == 'ods'">ODS - ODSP (100% ADP)</option>
|
|
<option value="acs" t-att-selected="assessment and assessment.client_type == 'acs'">ACS - ACSD (100% ADP)</option>
|
|
<option value="owp" t-att-selected="assessment and assessment.client_type == 'owp'">OWP - Ontario Works (100% ADP)</option>
|
|
</select>
|
|
</div>
|
|
<input type="hidden" name="build_type" value="modular"/>
|
|
<div class="mb-3">
|
|
<label class="form-label">Reason for Application</label>
|
|
<select name="reason_for_application" class="form-select">
|
|
<option value="">Select...</option>
|
|
<option value="first_access" t-att-selected="assessment and assessment.reason_for_application == 'first_access'">First Access</option>
|
|
<option value="additions" t-att-selected="assessment and assessment.reason_for_application == 'additions'">Additions</option>
|
|
<option value="modifications" t-att-selected="assessment and assessment.reason_for_application == 'modifications'">Modifications</option>
|
|
<option value="replacements" t-att-selected="assessment and assessment.reason_for_application == 'replacements'">Replacements</option>
|
|
</select>
|
|
</div>
|
|
</t>
|
|
</div><!-- /card-body -->
|
|
</div><!-- /card -->
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- ============================================================ -->
|
|
<!-- SUB-TEMPLATE: Measurements (step_type=measurements) -->
|
|
<!-- ============================================================ -->
|
|
<template id="fq_step_measurements" name="Step: Measurements">
|
|
<h4 class="mb-3"><t t-out="fstep.name"/></h4>
|
|
<t t-if="fstep.help_text">
|
|
<p class="text-muted"><t t-out="fstep.help_text"/></p>
|
|
</t>
|
|
<t t-if="not fstep.help_text">
|
|
<p class="text-muted">All measurements are required for the ADP application.</p>
|
|
</t>
|
|
|
|
<!-- Dynamic fields from fields_json -->
|
|
<t t-set="mfields" t-value="step_fields.get(fstep.id, [])"/>
|
|
<t t-if="mfields">
|
|
<div class="row g-3">
|
|
<t t-foreach="mfields" t-as="mf">
|
|
<div class="col-md-6">
|
|
<div class="wc-measurement-field p-3 border rounded">
|
|
<label class="form-label fw-bold" t-out="mf.get('label', '')"/>
|
|
<t t-if="mf.get('type') == 'selection'">
|
|
<select t-att-name="mf.get('name')" class="form-select"
|
|
t-att-required="mf.get('required')">
|
|
<option value="">Select...</option>
|
|
<t t-foreach="mf.get('options', [])" t-as="opt">
|
|
<option t-att-value="opt[0]"
|
|
t-att-selected="field_values.get(mf.get('name')) == str(opt[0])"
|
|
t-out="opt[1]"/>
|
|
</t>
|
|
</select>
|
|
</t>
|
|
<t t-if="mf.get('type') != 'selection'">
|
|
<div class="d-flex gap-2 align-items-center">
|
|
<input t-att-type="'number' if mf.get('type') in ('float', 'integer') else 'text'"
|
|
t-att-step="mf.get('step', '0.25') if mf.get('type') == 'float' else ('1' if mf.get('type') == 'integer' else '')"
|
|
t-att-name="mf.get('name')"
|
|
class="form-control wc-measurement-input"
|
|
t-att-data-upcharge-field="mf.get('name')"
|
|
t-att-value="field_values.get(mf.get('name'), '')"
|
|
t-att-required="mf.get('required')"/>
|
|
<t t-if="mf.get('units')">
|
|
<select t-att-name="mf.get('unit_field', mf.get('name') + '_unit')"
|
|
class="form-select wc-unit-select" style="width:100px;">
|
|
<t t-foreach="mf.get('units', [])" t-as="unit">
|
|
<option t-att-value="unit"
|
|
t-att-selected="field_values.get(mf.get('unit_field', mf.get('name') + '_unit')) == unit"
|
|
t-out="unit"/>
|
|
</t>
|
|
</select>
|
|
</t>
|
|
<t t-if="mf.get('unit') and not mf.get('units')">
|
|
<span class="input-group-text" t-out="mf.get('unit')"/>
|
|
</t>
|
|
<span class="wc-upcharge-badge d-none"/>
|
|
</div>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
</div>
|
|
</t>
|
|
|
|
<!-- Fallback: hardcoded wheelchair measurement fields -->
|
|
<t t-if="not mfields">
|
|
<div class="row g-3">
|
|
<t t-foreach="[
|
|
('seat_width', 'Seat Width', 'seat_width_unit', 'inches'),
|
|
('seat_depth', 'Seat Depth', 'seat_depth_unit', 'inches'),
|
|
('finished_seat_to_floor_height', 'Finished Seat to Floor Height', 'seat_to_floor_unit', 'inches'),
|
|
('back_cane_height', 'Back Cane Height', 'cane_height_unit', 'inches'),
|
|
('finished_back_height', 'Finished Back Height', 'back_height_unit', 'inches'),
|
|
('finished_leg_rest_length', 'Finished Leg Rest Length', 'leg_rest_unit', 'inches'),
|
|
]" t-as="mfield">
|
|
<div class="col-md-6">
|
|
<div class="wc-measurement-field p-3 border rounded">
|
|
<label class="form-label fw-bold" t-out="mfield[1]"/>
|
|
<div class="d-flex gap-2 align-items-center">
|
|
<input type="number" step="0.25" t-att-name="mfield[0]"
|
|
class="form-control wc-measurement-input"
|
|
t-att-data-upcharge-field="mfield[0]"
|
|
t-att-value="field_values.get(mfield[0], '')"/>
|
|
<select t-att-name="mfield[2]" class="form-select wc-unit-select" style="width:100px;">
|
|
<option value="cm"
|
|
t-att-selected="field_values.get(mfield[2]) == 'cm'">cm</option>
|
|
<option value="inches"
|
|
t-att-selected="not field_values.get(mfield[2]) or field_values.get(mfield[2]) == 'inches'">inches</option>
|
|
</select>
|
|
<span class="wc-upcharge-badge d-none"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
<!-- Client Weight -->
|
|
<div class="col-md-6">
|
|
<div class="wc-measurement-field p-3 border rounded">
|
|
<label class="form-label fw-bold">Client Weight</label>
|
|
<div class="d-flex gap-2 align-items-center">
|
|
<input type="number" step="1" name="client_weight"
|
|
class="form-control wc-measurement-input"
|
|
data-upcharge-field="client_weight"
|
|
t-att-value="assessment and assessment.client_weight or ''"/>
|
|
<select name="client_weight_unit" class="form-select wc-unit-select" style="width:100px;">
|
|
<option value="kg"
|
|
t-att-selected="assessment and assessment.client_weight_unit == 'kg'">kg</option>
|
|
<option value="lbs"
|
|
t-att-selected="not assessment or assessment.client_weight_unit == 'lbs'">lbs</option>
|
|
</select>
|
|
<span class="wc-upcharge-badge d-none"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
|
|
<!-- Upcharges Preview -->
|
|
<div id="upchargePreview" class="mt-3 d-none">
|
|
<h5>Auto-Applied Upcharges</h5>
|
|
<div id="upchargeList"/>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- ============================================================ -->
|
|
<!-- SUB-TEMPLATE: Product Selection (step_type=product_select) -->
|
|
<!-- ============================================================ -->
|
|
<template id="fq_step_product_select" name="Step: Product Selection">
|
|
<h4 class="mb-3"><t t-out="fstep.name"/></h4>
|
|
<t t-if="fstep.help_text">
|
|
<p class="text-muted"><t t-out="fstep.help_text"/></p>
|
|
</t>
|
|
|
|
<div class="mb-3 wc-search-container" style="position:relative;z-index:100;">
|
|
<label class="form-label fw-bold">Search Product</label>
|
|
<div class="input-group">
|
|
<span class="input-group-text"><i class="fa fa-search"/></span>
|
|
<input type="text" class="form-control wc-product-search"
|
|
id="frameSearch" t-att-data-section="fstep.section_code or 'frame'"
|
|
placeholder="Type product name, model, or ADP code..."
|
|
autocomplete="off"/>
|
|
</div>
|
|
<div id="frameSearchResults" class="list-group d-none"
|
|
style="position:absolute;z-index:1060;width:100%;max-height:300px;overflow-y:auto;background:var(--bs-body-bg);box-shadow:0 8px 30px rgba(0,0,0,.18);border:1px solid var(--bs-border-color);border-top:none;border-radius:0 0 .5rem .5rem;"/>
|
|
<input type="hidden" name="frame_product_tmpl_id" id="frameProductTmplId"
|
|
t-att-value="assessment.frame_product_tmpl_id.id if assessment and assessment.frame_product_tmpl_id else ''"/>
|
|
<input type="hidden" name="frame_product_id" id="frameProductId"
|
|
t-att-value="assessment.frame_product_id.id if assessment and assessment.frame_product_id else ''"/>
|
|
</div>
|
|
<div id="selectedFrame" class="d-none" style="position:relative;z-index:1;">
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between align-items-start">
|
|
<div>
|
|
<h5 class="card-title mb-1" id="selectedFrameName"/>
|
|
<div class="d-flex gap-4 text-muted small">
|
|
<span>ADP Code: <strong id="selectedFrameCode"/></span>
|
|
<span>ADP Price: $<strong id="selectedFramePrice"/></span>
|
|
</div>
|
|
</div>
|
|
<button type="button" class="btn btn-sm btn-outline-secondary"
|
|
id="clearFrame"><i class="fa fa-times me-1"/>Change</button>
|
|
</div>
|
|
<!-- Frame Configurator (shown when template has multiple variants) -->
|
|
<div id="frameConfigurator" class="wc-configurator-panel d-none">
|
|
<div class="wc-config-title">
|
|
<i class="fa fa-sliders me-2"/>Configure Options
|
|
</div>
|
|
<div id="frameAttributeSelectors" class="row g-2"/>
|
|
<div id="frameVariantInfo" class="d-none">
|
|
<span class="wc-variant-resolved">
|
|
<i class="fa fa-check-circle"/>
|
|
<span>Resolved: <strong id="frameVariantName"/></span>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Standard options for this section -->
|
|
<div id="standardFrameOptions" class="mt-3" style="position:relative;z-index:1;">
|
|
<h5>Standard Options</h5>
|
|
<div id="frameOptionsGrid" class="row g-2"/>
|
|
</div>
|
|
|
|
<div class="mt-3" style="position:relative;z-index:1;">
|
|
<label class="form-label">Notes</label>
|
|
<textarea name="frame_notes" class="form-control" rows="2"
|
|
t-out="assessment.frame_notes if assessment else ''"/>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- ============================================================ -->
|
|
<!-- SUB-TEMPLATE: Options & Accessories (step_type=options) -->
|
|
<!-- ============================================================ -->
|
|
<template id="fq_step_options" name="Step: Options">
|
|
<h4 class="mb-3"><t t-out="fstep.name"/></h4>
|
|
<t t-if="fstep.help_text">
|
|
<p class="text-muted"><t t-out="fstep.help_text"/></p>
|
|
</t>
|
|
|
|
<!-- Seating/Positioning sections with side tabs -->
|
|
<t t-if="fstep.section_code == 'seating'">
|
|
<p class="text-muted mb-3">Select seating and positioning devices. Choose Modular or Custom Fabricated for each.</p>
|
|
<div class="wc-seating-tabs" id="seatingTabsContainer">
|
|
<div class="row g-0">
|
|
<!-- ── TAB NAV (left on desktop, top on mobile) ── -->
|
|
<div class="col-md-3 wc-tab-nav-col">
|
|
<div class="nav flex-md-column wc-tab-nav" id="seatingTabNav" role="tablist"
|
|
aria-orientation="vertical">
|
|
<t t-set="first_visible" t-value="True"/>
|
|
<t t-foreach="sections" t-as="section">
|
|
<t t-if="section.has_build_type and section.code != 'accessories'">
|
|
<button type="button" role="tab"
|
|
t-attf-class="wc-tab-btn wc-equipment-section #{'active' if first_visible and (section.equipment_type in ('both', equipment_type) or (section.equipment_type == 'wheelchair' and equipment_type in ('manual_wheelchair', 'power_wheelchair'))) else ''}"
|
|
t-att-data-equipment-type="section.equipment_type"
|
|
t-attf-id="tab_#{section.code}"
|
|
t-attf-data-bs-target="#panel_#{section.code}"
|
|
t-att-aria-controls="'panel_' + section.code"
|
|
t-att-data-section-code="section.code"
|
|
t-att-style="'' if (section.equipment_type in ('both', equipment_type) or (section.equipment_type == 'wheelchair' and equipment_type in ('manual_wheelchair', 'power_wheelchair'))) else 'display:none;'">
|
|
<i t-attf-class="fa #{section.icon or 'fa-cube'} wc-tab-icon"/>
|
|
<span class="wc-tab-label"><t t-out="section.name"/></span>
|
|
<span class="wc-tab-badge" title="Selected items">0</span>
|
|
</button>
|
|
<t t-if="first_visible and (section.equipment_type in ('both', equipment_type) or (section.equipment_type == 'wheelchair' and equipment_type in ('manual_wheelchair', 'power_wheelchair')))">
|
|
<t t-set="first_visible" t-value="False"/>
|
|
</t>
|
|
</t>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ── TAB CONTENT (right on desktop, below on mobile) ── -->
|
|
<div class="col-md-9 wc-tab-content-col">
|
|
<div class="wc-tab-content" id="seatingTabContent">
|
|
<t t-set="first_visible" t-value="True"/>
|
|
<t t-foreach="sections" t-as="section">
|
|
<t t-if="section.has_build_type and section.code != 'accessories'">
|
|
<div role="tabpanel"
|
|
t-attf-class="wc-tab-panel wc-equipment-section #{'active' if first_visible and (section.equipment_type in ('both', equipment_type) or (section.equipment_type == 'wheelchair' and equipment_type in ('manual_wheelchair', 'power_wheelchair'))) else ''}"
|
|
t-attf-id="panel_#{section.code}"
|
|
t-att-data-equipment-type="section.equipment_type"
|
|
t-att-data-section-code="section.code"
|
|
t-attf-aria-labelledby="tab_#{section.code}">
|
|
|
|
<!-- Build Type + Measurements row (always visible) -->
|
|
<div class="d-flex flex-wrap align-items-end gap-3 mb-3">
|
|
<div>
|
|
<label class="form-label fw-bold mb-1" style="font-size:0.78rem;">Build Type</label>
|
|
<div class="d-flex gap-2">
|
|
<label class="btn btn-sm btn-outline-secondary wc-radio-btn">
|
|
<input type="radio" t-attf-name="section_build_#{section.code}"
|
|
value="modular" checked="checked"
|
|
class="wc-section-build-type"
|
|
t-att-data-section="section.code"/>
|
|
Modular
|
|
</label>
|
|
<label class="btn btn-sm btn-outline-secondary wc-radio-btn">
|
|
<input type="radio" t-attf-name="section_build_#{section.code}"
|
|
value="custom_fabricated"
|
|
class="wc-section-build-type"
|
|
t-att-data-section="section.code"/>
|
|
Custom Fabricated
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<t t-if="section.has_width">
|
|
<div style="min-width:100px;">
|
|
<label class="form-label mb-1" style="font-size:0.78rem;" t-out="section.width_label"/>
|
|
<input type="number" step="0.25" class="form-control form-control-sm"
|
|
t-attf-name="section_width_#{section.code}"/>
|
|
</div>
|
|
</t>
|
|
<t t-if="section.has_depth">
|
|
<div style="min-width:100px;">
|
|
<label class="form-label mb-1" style="font-size:0.78rem;" t-out="section.depth_label"/>
|
|
<input type="number" step="0.25" class="form-control form-control-sm"
|
|
t-attf-name="section_depth_#{section.code}"/>
|
|
</div>
|
|
</t>
|
|
<t t-if="section.has_height">
|
|
<div style="min-width:100px;">
|
|
<label class="form-label mb-1" style="font-size:0.78rem;" t-out="section.height_label"/>
|
|
<input type="number" step="0.25" class="form-control form-control-sm"
|
|
t-attf-name="section_height_#{section.code}"/>
|
|
</div>
|
|
</t>
|
|
</div>
|
|
|
|
<!-- ── Collapsible section groups (accordion within tab) ── -->
|
|
<div class="wc-section-groups">
|
|
|
|
<!-- Group: Main section products -->
|
|
<div class="wc-section-group open">
|
|
<button type="button" class="wc-group-header">
|
|
<i t-attf-class="fa #{section.icon or 'fa-cube'} wc-group-icon"/>
|
|
<span class="wc-group-title"><t t-out="section.name"/></span>
|
|
<span class="wc-group-count">0</span>
|
|
<i class="fa fa-chevron-down wc-group-chevron"/>
|
|
</button>
|
|
<div class="wc-group-body">
|
|
<div class="wc-section-options" t-att-data-section="section.code"
|
|
t-att-data-section-id="section.id">
|
|
<div class="wc-option-filter-wrap mb-2">
|
|
<div class="input-group input-group-sm">
|
|
<span class="input-group-text"><i class="fa fa-filter"/></span>
|
|
<input type="text" class="form-control wc-option-filter"
|
|
placeholder="Filter products..."
|
|
autocomplete="off"/>
|
|
</div>
|
|
</div>
|
|
<div class="wc-options-grid row g-2"/>
|
|
<div class="mt-2 wc-search-container">
|
|
<input type="text" class="form-control form-control-sm wc-product-search"
|
|
t-att-data-section="section.code"
|
|
placeholder="Search catalogue for more..."
|
|
autocomplete="off"/>
|
|
<div class="wc-search-results list-group d-none"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Group: Sub-sections (collapsed by default) -->
|
|
<t t-foreach="section.child_ids" t-as="child">
|
|
<div class="wc-section-group">
|
|
<button type="button" class="wc-group-header">
|
|
<i class="fa fa-cube wc-group-icon"/>
|
|
<span class="wc-group-title"><t t-out="child.name"/></span>
|
|
<span class="wc-group-count">0</span>
|
|
<i class="fa fa-chevron-down wc-group-chevron"/>
|
|
</button>
|
|
<div class="wc-group-body">
|
|
<div class="wc-section-options" t-att-data-section="child.code"
|
|
t-att-data-section-id="child.id">
|
|
<div class="wc-option-filter-wrap mb-2">
|
|
<div class="input-group input-group-sm">
|
|
<span class="input-group-text"><i class="fa fa-filter"/></span>
|
|
<input type="text" class="form-control wc-option-filter"
|
|
placeholder="Filter products..."
|
|
autocomplete="off"/>
|
|
</div>
|
|
</div>
|
|
<div class="wc-options-grid row g-2"/>
|
|
<div class="mt-1 wc-search-container">
|
|
<input type="text" class="form-control form-control-sm wc-product-search"
|
|
t-att-data-section="child.code"
|
|
placeholder="Search catalogue for more..."
|
|
autocomplete="off"/>
|
|
<div class="wc-search-results list-group d-none"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
<t t-if="first_visible and (section.equipment_type in ('both', equipment_type) or (section.equipment_type == 'wheelchair' and equipment_type in ('manual_wheelchair', 'power_wheelchair')))">
|
|
<t t-set="first_visible" t-value="False"/>
|
|
</t>
|
|
</t>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
|
|
<!-- ADP Options & Accessories (flat checkbox grids) -->
|
|
<t t-if="fstep.section_code != 'seating'">
|
|
<p class="text-muted">
|
|
Select applicable options. Items marked with * require clinical rationale.
|
|
</p>
|
|
<div id="adpOptionsGrid" class="row g-2">
|
|
<!-- Populated by JS from section options -->
|
|
</div>
|
|
|
|
<h4 class="mt-4 mb-3">Accessories</h4>
|
|
<div id="accessoriesGrid" class="row g-2">
|
|
<!-- Populated by JS -->
|
|
</div>
|
|
<div class="mt-2 wc-search-container">
|
|
<input type="text" class="form-control wc-product-search"
|
|
data-section="accessories"
|
|
placeholder="Search for accessories..."
|
|
autocomplete="off"/>
|
|
<div class="wc-search-results list-group d-none"/>
|
|
</div>
|
|
</t>
|
|
</template>
|
|
|
|
<!-- ============================================================ -->
|
|
<!-- SUB-TEMPLATE: Review & Generate (step_type=review) -->
|
|
<!-- ============================================================ -->
|
|
<template id="fq_step_review" name="Step: Review & Generate">
|
|
<h4 class="mb-3">Review & Generate Quotation</h4>
|
|
|
|
<!-- Selected Items Summary -->
|
|
<div id="reviewSummary">
|
|
<table class="table table-sm">
|
|
<thead>
|
|
<tr>
|
|
<th>Section</th>
|
|
<th>Product</th>
|
|
<th>ADP Code</th>
|
|
<th>Build</th>
|
|
<th class="text-end">Qty</th>
|
|
<th class="text-end">Price</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="reviewTableBody"/>
|
|
<tfoot>
|
|
<tr class="fw-bold">
|
|
<td colspan="5" class="text-end">Estimated Total:</td>
|
|
<td class="text-end" id="reviewTotal">$0.00</td>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- Upcharges Summary -->
|
|
<div id="reviewUpcharges" class="d-none">
|
|
<h5>Auto-Applied Upcharges</h5>
|
|
<div id="reviewUpchargeList"/>
|
|
</div>
|
|
|
|
<!-- Measurements Summary -->
|
|
<div class="card mt-3">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">Measurements Summary</h5>
|
|
</div>
|
|
<div class="card-body" id="reviewMeasurements"/>
|
|
</div>
|
|
|
|
<div class="mt-3">
|
|
<label class="form-label">Additional Notes</label>
|
|
<textarea name="notes" class="form-control" rows="3"
|
|
t-out="assessment.notes if assessment else ''"/>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- ============================================================ -->
|
|
<!-- SUB-TEMPLATE: Custom Fields (step_type=custom) -->
|
|
<!-- ============================================================ -->
|
|
<template id="fq_step_custom" name="Step: Custom Fields">
|
|
<h4 class="mb-3"><t t-out="fstep.name"/></h4>
|
|
<t t-if="fstep.help_text">
|
|
<p class="text-muted"><t t-out="fstep.help_text"/></p>
|
|
</t>
|
|
|
|
<t t-set="cfields" t-value="step_fields.get(fstep.id, [])"/>
|
|
<t t-if="cfields">
|
|
<div class="row g-3">
|
|
<t t-foreach="cfields" t-as="cf">
|
|
<div class="col-md-6">
|
|
<div class="p-3 border rounded">
|
|
<label class="form-label fw-bold" t-out="cf.get('label', '')"/>
|
|
<t t-if="cf.get('type') == 'selection'">
|
|
<select t-att-name="cf.get('name')" class="form-select"
|
|
t-att-required="cf.get('required')">
|
|
<option value="">Select...</option>
|
|
<t t-foreach="cf.get('options', [])" t-as="opt">
|
|
<option t-att-value="opt[0]"
|
|
t-att-selected="field_values.get(cf.get('name')) == str(opt[0])"
|
|
t-out="opt[1]"/>
|
|
</t>
|
|
</select>
|
|
</t>
|
|
<t t-if="cf.get('type') == 'text'">
|
|
<textarea t-att-name="cf.get('name')" class="form-control" rows="3"
|
|
t-att-required="cf.get('required')"
|
|
t-out="field_values.get(cf.get('name'), '')"/>
|
|
</t>
|
|
<t t-if="cf.get('type') == 'boolean'">
|
|
<div class="form-check">
|
|
<input type="checkbox" class="form-check-input"
|
|
t-att-name="cf.get('name')"
|
|
t-att-checked="field_values.get(cf.get('name'))"/>
|
|
<label class="form-check-label" t-out="cf.get('label', '')"/>
|
|
</div>
|
|
</t>
|
|
<t t-if="cf.get('type') in ('float', 'integer')">
|
|
<div class="input-group">
|
|
<input t-att-type="'number'"
|
|
t-att-step="'0.01' if cf.get('type') == 'float' else '1'"
|
|
t-att-name="cf.get('name')"
|
|
class="form-control"
|
|
t-att-value="field_values.get(cf.get('name'), '')"
|
|
t-att-required="cf.get('required')"/>
|
|
<t t-if="cf.get('unit')">
|
|
<span class="input-group-text" t-out="cf.get('unit')"/>
|
|
</t>
|
|
</div>
|
|
</t>
|
|
<t t-if="cf.get('type') not in ('selection', 'text', 'boolean', 'float', 'integer')">
|
|
<input type="text" t-att-name="cf.get('name')" class="form-control"
|
|
t-att-value="field_values.get(cf.get('name'), '')"
|
|
t-att-required="cf.get('required')"/>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
</div>
|
|
</t>
|
|
<t t-if="not cfields">
|
|
<div class="alert alert-info">
|
|
No custom fields configured for this step.
|
|
</div>
|
|
</t>
|
|
</template>
|
|
|
|
<!-- ============================================================ -->
|
|
<!-- EMBEDDABLE FORM (no portal chrome — for iframe embedding) -->
|
|
<!-- ============================================================ -->
|
|
<template id="portal_quotation_form_embed" name="Equipment Assessment Form (Embed)">
|
|
<t t-call="web.frontend_layout">
|
|
<t t-set="no_header" t-value="True"/>
|
|
<t t-set="no_footer" t-value="True"/>
|
|
|
|
<div class="container-fluid py-3 wc-assessment-form" id="wcAssessmentForm">
|
|
|
|
<!-- Success/Error Messages -->
|
|
<t t-if="request.params.get('success') == 'saved'">
|
|
<div class="alert alert-success alert-dismissible fade show">
|
|
Assessment saved successfully.
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert"/>
|
|
</div>
|
|
</t>
|
|
<t t-if="request.params.get('success') == 'quotation_generated'">
|
|
<div class="alert alert-success alert-dismissible fade show">
|
|
<i class="fa fa-check-circle me-1"/> Quotation generated!
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert"/>
|
|
</div>
|
|
</t>
|
|
<t t-if="request.params.get('error')">
|
|
<div class="alert alert-danger alert-dismissible fade show">
|
|
<t t-out="request.params.get('error')"/>
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert"/>
|
|
</div>
|
|
</t>
|
|
|
|
<!-- Dynamic Step Indicator -->
|
|
<div class="wc-steps mb-4">
|
|
<div class="d-flex justify-content-between">
|
|
<t t-foreach="flow_steps" t-as="fstep">
|
|
<div t-attf-class="wc-step-indicator text-center flex-fill #{'active' if fstep_index == 0 else ''}"
|
|
t-att-data-step="fstep_index + 1">
|
|
<div class="wc-step-number rounded-circle d-inline-flex align-items-center justify-content-center">
|
|
<t t-out="fstep_index + 1"/>
|
|
</div>
|
|
<div class="wc-step-label small mt-1">
|
|
<t t-out="fstep.name"/>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
|
|
<form method="post"
|
|
t-att-action="'/quotation/form/' + access_token + '/save'"
|
|
id="wcForm" class="wc-form">
|
|
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
|
|
<input type="hidden" name="assessment_id"
|
|
t-att-value="assessment.id if assessment else 0"/>
|
|
<input type="hidden" name="current_step" id="currentStep" value="1"/>
|
|
<input type="hidden" name="action" id="formAction" value="save"/>
|
|
<input type="hidden" name="access_token" id="accessToken"
|
|
t-att-value="access_token"/>
|
|
|
|
<!-- Hidden fields for line data -->
|
|
<input type="hidden" name="line_product_ids" id="lineProductIds" value=""/>
|
|
<input type="hidden" name="line_section_ids" id="lineSectionIds" value=""/>
|
|
<input type="hidden" name="line_build_types" id="lineBuildTypes" value=""/>
|
|
<input type="hidden" name="line_quantities" id="lineQuantities" value=""/>
|
|
<input type="hidden" name="line_rationales" id="lineRationales" value=""/>
|
|
|
|
<p class="text-muted text-center mb-3">
|
|
<i class="fa fa-cogs me-1"/>
|
|
Equipment Assessment Form
|
|
</p>
|
|
|
|
<!-- NOTE: The embed template reuses the same step structure.
|
|
For a full implementation, you would include the same step
|
|
content from the main template. For now we render a minimal
|
|
placeholder that the JS widget will populate. -->
|
|
<div class="text-center py-5 text-muted">
|
|
<i class="fa fa-spinner fa-spin fa-2x mb-3 d-block"/>
|
|
Loading assessment form...
|
|
</div>
|
|
|
|
<!-- Navigation Buttons -->
|
|
<div class="wc-form-navigation mt-4 border-top pt-3">
|
|
<div class="d-flex justify-content-between">
|
|
<button type="submit" class="btn btn-outline-secondary" id="btnSaveDraft">
|
|
<i class="fa fa-save me-1"/> Save Draft
|
|
</button>
|
|
<div>
|
|
<button type="button" class="btn btn-secondary d-none" id="btnPrev">
|
|
<i class="fa fa-arrow-left me-1"/> Previous
|
|
</button>
|
|
<button type="button" class="btn btn-primary" id="btnNext">
|
|
Next <i class="fa fa-arrow-right ms-1"/>
|
|
</button>
|
|
<button type="submit" class="btn btn-success d-none" id="btnGenerate"
|
|
name="action" value="generate">
|
|
<i class="fa fa-magic me-1"/> Generate Quotation
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</form>
|
|
</div>
|
|
</t>
|
|
</template>
|
|
|
|
<!-- ============================================================ -->
|
|
<!-- PUBLIC FORM — INVALID TOKEN PAGE -->
|
|
<!-- ============================================================ -->
|
|
<template id="public_form_invalid" name="Invalid Assessment Link">
|
|
<t t-call="web.frontend_layout">
|
|
<div class="container py-5">
|
|
<div class="row justify-content-center">
|
|
<div class="col-md-6 text-center">
|
|
<i class="fa fa-chain-broken fa-4x text-muted mb-3 d-block"/>
|
|
<h3 class="mb-3">This link is no longer valid</h3>
|
|
<t t-if="status == 'not_found'">
|
|
<p class="text-muted">
|
|
The assessment form you're trying to access doesn't exist
|
|
or the link has expired.
|
|
</p>
|
|
</t>
|
|
<t t-if="status == 'cancelled'">
|
|
<p class="text-muted">
|
|
This assessment has been cancelled and is no longer
|
|
available for editing.
|
|
</p>
|
|
</t>
|
|
<a href="/" class="btn btn-primary mt-3">
|
|
<i class="fa fa-home me-1"/> Go Home
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
</template>
|
|
|
|
</odoo>
|