Files
Odoo-Modules/fusion_portal/views/portal_accessibility_forms.xml
gsinghpal 747c814249 refactor(fusion_portal): rename from fusion_authorizer_portal + modern photo cards on accessibility selector
Rename module fusion_authorizer_portal -> fusion_portal everywhere:
manifest/assets, controllers, models, views, JS (odoo.define + asset URLs),
migration MODULE constants; plus cross-module refs in fusion_schedule,
fusion_repairs, fusion_quotations (depends + inherit_id) and the pdf_filler
import in fusion_claims. Add rename_module.sql for the one-time in-place DB
rename (ir_module_module, ir_model_data, ir_ui_view.key,
ir_module_module_dependency) required on installed envs before -u fusion_portal.
Document the rename gotcha as rule 16 in CLAUDE.md.

Redesign the Accessibility Assessment selector: replace Font Awesome icon tiles
with photo-banner cards using 7 optimized images (1000x750 PNG -> 800x600 JPEG,
~8MB -> 488KB), per-type colour accent bar + centered pill button, hover
lift/zoom. Images ship as module static files so they deploy/sync with the module.

Drop the regenerable graphify-out cache from the module.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 22:38:14 -04:00

1223 lines
72 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2024-2026 Nexa Systems Inc.
License OPL-1 (Odoo Proprietary License v1.0)
Part of the Fusion Claim Assistant product family.
Accessibility Assessment Form Templates
-->
<odoo>
<!-- ============================================================= -->
<!-- STRAIGHT STAIR LIFT FORM -->
<!-- ============================================================= -->
<template id="portal_accessibility_stairlift_straight" name="Straight Stair Lift Assessment">
<t t-call="portal.portal_layout">
<t t-set="breadcrumbs_searchbar" t-value="True"/>
<t t-set="no_breadcrumbs" t-value="True"/>
<div class="container py-4">
<!-- Custom Breadcrumb -->
<nav aria-label="breadcrumb" class="mb-4">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/my">Dashboard</a></li>
<li class="breadcrumb-item"><a href="/my/accessibility">Accessibility</a></li>
<li class="breadcrumb-item active">Straight Stair Lift</li>
</ol>
</nav>
<h2 class="mb-4">
<i class="fa fa-level-up text-primary"></i>
Straight Stair Lift Assessment
</h2>
<form id="accessibility_form">
<input type="hidden" name="assessment_type" value="stairlift_straight"/>
<!-- Client Info -->
<t t-call="fusion_portal.accessibility_client_info_section"/>
<!-- Stair Measurements -->
<div class="card mb-4">
<div class="card-header bg-info text-white">
<h5 class="mb-0"><i class="fa fa-ruler"></i> Stair Measurements</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-4 mb-3">
<label class="form-label">Number of Steps <span class="text-danger">*</span></label>
<input type="number" name="stair_steps" id="stair_steps" class="form-control"
required="required" min="1" placeholder="e.g., 12" onchange="calculateStraightTrackLength()"/>
</div>
<div class="col-md-4 mb-3">
<label class="form-label">Nose to Nose (inches) <span class="text-danger">*</span></label>
<input type="number" name="stair_nose_to_nose" id="stair_nose_to_nose" class="form-control"
required="required" min="1" step="0.5" placeholder="e.g., 10" onchange="calculateStraightTrackLength()"/>
<small class="text-muted">Distance from step nose to next step nose</small>
</div>
<div class="col-md-4 mb-3">
<label class="form-label">Installation Side</label>
<select name="stair_side" class="form-control">
<option value="">-- Select --</option>
<option value="left">Left Side</option>
<option value="right">Right Side</option>
</select>
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Stair Lift Style</label>
<select name="stair_style" class="form-control">
<option value="">-- Select --</option>
<option value="standard">Standard Stair Lift</option>
<option value="slide_track">Slide Track Stair Lift</option>
<option value="foldable_hinge">Foldable Hinge Stair Lift</option>
</select>
</div>
</div>
<!-- Calculated Track Length -->
<div class="alert alert-info">
<div class="row align-items-center">
<div class="col-md-8">
<strong><i class="fa fa-calculator"></i> Calculated Track Length:</strong>
<div class="mt-2">
<span class="h4" id="calculated_length">0</span> <span class="text-muted">inches</span>
<span class="mx-2">|</span>
<span class="h5" id="calculated_length_feet">0</span> <span class="text-muted">feet</span>
<span class="mx-2">|</span>
<span class="h5" id="calculated_length_metres">0</span> <span class="text-muted">metres</span>
</div>
<small class="text-muted">Formula: (Steps x Nose-to-Nose) + 13" top landing</small>
</div>
<div class="col-md-4">
<label class="form-label">Manual Override (inches)</label>
<input type="number" name="stair_manual_length_override" class="form-control"
step="0.5" placeholder="Leave blank to use calculated"/>
</div>
</div>
</div>
<!-- Track Requirements -->
<div class="alert alert-warning mt-3" id="track_requirements_section" style="display: none;">
<strong><i class="fa fa-cogs"></i> Track Requirements:</strong>
<div class="mt-2" id="track_requirements_details">
<!-- Filled by JavaScript -->
</div>
</div>
</div>
</div>
<!-- Additional Features -->
<div class="card mb-4">
<div class="card-header bg-success text-white">
<h5 class="mb-0"><i class="fa fa-cogs"></i> Additional Features</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<div class="form-check mb-2">
<input type="checkbox" name="stair_power_swivel_upstairs" value="true" class="form-check-input" id="power_swivel"/>
<label class="form-check-label" for="power_swivel">Power Swivel (Upstairs)</label>
</div>
<div class="form-check mb-2">
<input type="checkbox" name="stair_power_folding_footrest" value="true" class="form-check-input" id="power_footrest"/>
<label class="form-check-label" for="power_footrest">Power Folding Footrest</label>
</div>
</div>
</div>
</div>
</div>
<!-- Photos -->
<t t-call="fusion_portal.accessibility_photo_section"/>
<!-- Notes -->
<t t-call="fusion_portal.accessibility_notes_section"/>
<!-- Submit Buttons -->
<t t-call="fusion_portal.accessibility_submit_buttons"/>
</form>
</div>
<!-- Scripts -->
<t t-call="fusion_portal.accessibility_form_scripts"/>
<script type="text/javascript">
// Constants for track calculations
var TRACK_LENGTH_INCHES = 92.5; // One standard track length
var MIN_CUT_PIECE_FEET = 3; // Minimum cut piece size
var MIN_CUT_PIECE_INCHES = MIN_CUT_PIECE_FEET * 12; // 36 inches
function calculateStraightTrackLength() {
var steps = parseInt(document.getElementById('stair_steps').value) || 0;
var noseToNose = parseFloat(document.getElementById('stair_nose_to_nose').value) || 0;
var calculated = (steps * noseToNose) + 13; // +13 for top landing
// Display in inches
document.getElementById('calculated_length').textContent = calculated.toFixed(1);
// Convert and display in feet
var feet = calculated / 12;
document.getElementById('calculated_length_feet').textContent = feet.toFixed(2);
// Convert and display in metres
var metres = calculated * 0.0254;
document.getElementById('calculated_length_metres').textContent = metres.toFixed(2);
// Calculate track requirements
calculateTrackRequirements(calculated);
}
function calculateTrackRequirements(totalLengthInches) {
var section = document.getElementById('track_requirements_section');
var details = document.getElementById('track_requirements_details');
if (totalLengthInches &lt;= 0) {
section.style.display = 'none';
return;
}
section.style.display = 'block';
var html = '';
var tracksNeeded = 0;
var trackConfig = '';
if (totalLengthInches &lt;= TRACK_LENGTH_INCHES) {
// Single track is enough
tracksNeeded = 1;
var cutLength = TRACK_LENGTH_INCHES - totalLengthInches;
trackConfig = '&lt;span class="badge bg-success"&gt;Single Track&lt;/span&gt; - ' +
'Use 1 track (' + TRACK_LENGTH_INCHES + '"), cut ' + cutLength.toFixed(1) + '" from one end';
} else if (totalLengthInches &lt;= (TRACK_LENGTH_INCHES * 2)) {
// Two tracks needed, joined in middle
tracksNeeded = 2;
var totalTrackLength = TRACK_LENGTH_INCHES * 2; // 185 inches
var excessLength = totalTrackLength - totalLengthInches;
var cutPerSide = excessLength / 2;
trackConfig = '&lt;span class="badge bg-warning text-dark"&gt;Two Tracks Joined&lt;/span&gt; - ' +
'Use 2 tracks (total ' + totalTrackLength.toFixed(1) + '"), ' +
'cut ' + cutPerSide.toFixed(1) + '" from each outer end, join in middle';
// Check minimum cut piece warning
var remainingPerTrack = TRACK_LENGTH_INCHES - cutPerSide;
if (cutPerSide &gt; 0 &amp;&amp; cutPerSide &lt; MIN_CUT_PIECE_INCHES) {
trackConfig += '&lt;br/&gt;&lt;small class="text-danger"&gt;&lt;i class="fa fa-warning"&gt;&lt;/i&gt; ' +
'Note: Cut piece (' + cutPerSide.toFixed(1) + '") is less than minimum ' + MIN_CUT_PIECE_INCHES + '"&lt;/small&gt;';
}
} else if (totalLengthInches &lt;= (TRACK_LENGTH_INCHES * 3)) {
// Three tracks needed - full track in middle, cuts on sides
tracksNeeded = 3;
var remainingLength = totalLengthInches - TRACK_LENGTH_INCHES; // After middle track
var lengthPerSide = remainingLength / 2;
var cutPerSide = TRACK_LENGTH_INCHES - lengthPerSide;
trackConfig = '&lt;span class="badge bg-danger"&gt;Three Tracks (2 Joints)&lt;/span&gt; - ' +
'Full track in middle (' + TRACK_LENGTH_INCHES + '"), ' +
'cut pieces on each side (' + lengthPerSide.toFixed(1) + '" each, cut ' + cutPerSide.toFixed(1) + '" off)';
// Check minimum cut piece warning
if (lengthPerSide &lt; MIN_CUT_PIECE_INCHES) {
trackConfig += '&lt;br/&gt;&lt;small class="text-danger"&gt;&lt;i class="fa fa-warning"&gt;&lt;/i&gt; ' +
'Warning: Side pieces (' + lengthPerSide.toFixed(1) + '") are less than minimum ' + MIN_CUT_PIECE_INCHES + '"&lt;/small&gt;';
}
} else {
// More than 3 tracks - calculate how many needed
tracksNeeded = Math.ceil(totalLengthInches / TRACK_LENGTH_INCHES);
var totalTrackLength = tracksNeeded * TRACK_LENGTH_INCHES;
var excessLength = totalTrackLength - totalLengthInches;
trackConfig = '&lt;span class="badge bg-dark"&gt;' + tracksNeeded + ' Tracks (' + (tracksNeeded - 1) + ' Joints)&lt;/span&gt; - ' +
'Total track material: ' + totalTrackLength.toFixed(1) + '", ' +
'excess to cut: ' + excessLength.toFixed(1) + '"';
}
// Build the HTML
html += '&lt;div class="mb-2"&gt;';
html += '&lt;strong&gt;Tracks Needed:&lt;/strong&gt; ' + tracksNeeded + ' track(s) @ ' + TRACK_LENGTH_INCHES + '" each';
html += '&lt;/div&gt;';
html += '&lt;div class="mb-2"&gt;';
html += '&lt;strong&gt;Configuration:&lt;/strong&gt; ' + trackConfig;
html += '&lt;/div&gt;';
// Add reference info
html += '&lt;hr class="my-2"/&gt;';
html += '&lt;small class="text-muted"&gt;';
html += '&lt;i class="fa fa-info-circle"&gt;&lt;/i&gt; Track specs: 1 track = ' + TRACK_LENGTH_INCHES + '" | ';
html += 'Min cut piece = ' + MIN_CUT_PIECE_FEET + ' feet (' + MIN_CUT_PIECE_INCHES + '")';
html += '&lt;/small&gt;';
details.innerHTML = html;
}
</script>
</t>
</template>
<!-- ============================================================= -->
<!-- CURVED STAIR LIFT FORM -->
<!-- ============================================================= -->
<template id="portal_accessibility_stairlift_curved" name="Curved Stair Lift Assessment">
<t t-call="portal.portal_layout">
<t t-set="breadcrumbs_searchbar" t-value="True"/>
<t t-set="no_breadcrumbs" t-value="True"/>
<div class="container py-4">
<!-- Custom Breadcrumb -->
<nav aria-label="breadcrumb" class="mb-4">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/my">Dashboard</a></li>
<li class="breadcrumb-item"><a href="/my/accessibility">Accessibility</a></li>
<li class="breadcrumb-item active">Curved Stair Lift</li>
</ol>
</nav>
<h2 class="mb-4">
<i class="fa fa-refresh text-info"></i>
Curved Stair Lift Assessment
</h2>
<form id="accessibility_form">
<input type="hidden" name="assessment_type" value="stairlift_curved"/>
<!-- Client Info -->
<t t-call="fusion_portal.accessibility_client_info_section"/>
<!-- Stair Measurements -->
<div class="card mb-4">
<div class="card-header bg-info text-white">
<h5 class="mb-0"><i class="fa fa-ruler"></i> Stair Measurements</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-4 mb-3">
<label class="form-label">Number of Steps <span class="text-danger">*</span></label>
<input type="number" name="stair_curved_steps" id="stair_curved_steps" class="form-control"
required="required" min="1" placeholder="e.g., 14" onchange="calculateCurvedTrackLength()"/>
</div>
<div class="col-md-4 mb-3">
<label class="form-label">Number of Curves</label>
<input type="number" name="stair_curves_count" id="stair_curves_count" class="form-control"
min="0" value="0" placeholder="e.g., 2" onchange="calculateCurvedTrackLength()"/>
<small class="text-muted">Each curve adds 16" to track length</small>
</div>
</div>
</div>
</div>
<!-- Top Landing Section -->
<div class="card mb-4">
<div class="card-header" style="background: linear-gradient(135deg, #5ba848 0%, #3a8fb7 60%, #2e7aad 100%); color: white;">
<h5 class="mb-0"><i class="fa fa-arrow-up"></i> Top Landing</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Top Landing Type <span class="text-danger">*</span></label>
<select name="stair_top_landing_type" id="stair_top_landing_type" class="form-control" onchange="handleTopLandingChange()">
<option value="none">Standard (No special landing)</option>
<option value="90_exit">90° Exit (+24")</option>
<option value="90_parking">90° Parking (+24")</option>
<option value="180_parking">180° Parking (+48")</option>
<option value="flush_landing">Flush Landing (+12")</option>
<option value="vertical_overrun">Vertical Overrun (Custom)</option>
</select>
</div>
<div class="col-md-6 mb-3" id="top_overrun_custom_container" style="display: none;">
<label class="form-label">Top Overrun Length (inches) <span class="text-danger">*</span></label>
<input type="number" name="top_overrun_custom_length" id="top_overrun_custom_length"
class="form-control" min="1" step="0.5" placeholder="Enter custom length in inches"
onchange="calculateCurvedTrackLength()"/>
<small class="text-muted">Enter the exact overrun measurement needed</small>
</div>
</div>
<div class="row">
<div class="col-md-12 mb-3">
<label class="form-label">Top Landing Photos</label>
<input type="file" name="top_landing_photos" id="top_landing_photos" class="form-control"
accept="image/*" multiple="multiple"/>
<small class="text-muted">Upload photos of the top landing area</small>
<div id="top_landing_preview" class="row mt-2"></div>
</div>
</div>
</div>
</div>
<!-- Bottom Landing Section -->
<div class="card mb-4">
<div class="card-header" style="background: linear-gradient(135deg, #3a8fb7 0%, #2e7aad 60%, #1a6b9a 100%); color: white;">
<h5 class="mb-0"><i class="fa fa-arrow-down"></i> Bottom Landing</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Bottom Landing Type <span class="text-danger">*</span></label>
<select name="stair_bottom_landing_type" id="stair_bottom_landing_type" class="form-control" onchange="handleBottomLandingChange()">
<option value="none">Standard (No special landing)</option>
<option value="90_park">90° Park (+24")</option>
<option value="180_park">180° Park (+48")</option>
<option value="drop_nose">Drop Nose Landing (+12")</option>
<option value="short_vertical">Short Vertical Start (+12")</option>
<option value="horizontal_overrun">Horizontal Overrun (Custom)</option>
</select>
</div>
<div class="col-md-6 mb-3" id="bottom_overrun_custom_container" style="display: none;">
<label class="form-label">Bottom Overrun Length (inches) <span class="text-danger">*</span></label>
<input type="number" name="bottom_overrun_custom_length" id="bottom_overrun_custom_length"
class="form-control" min="1" step="0.5" placeholder="Enter custom length in inches"
onchange="calculateCurvedTrackLength()"/>
<small class="text-muted">Enter the exact overrun measurement needed</small>
</div>
</div>
<div class="row">
<div class="col-md-12 mb-3">
<label class="form-label">Bottom Landing Photos</label>
<input type="file" name="bottom_landing_photos" id="bottom_landing_photos" class="form-control"
accept="image/*" multiple="multiple"/>
<small class="text-muted">Upload photos of the bottom landing area</small>
<div id="bottom_landing_preview" class="row mt-2"></div>
</div>
</div>
</div>
</div>
<!-- Calculated Track Length -->
<div class="card mb-4">
<div class="card-header" style="background: linear-gradient(135deg, #4a9e3f 0%, #5ba848 100%); color: white;">
<h5 class="mb-0"><i class="fa fa-calculator"></i> Track Calculation</h5>
</div>
<div class="card-body">
<div class="alert alert-info mb-3">
<div class="row align-items-center">
<div class="col-md-8">
<strong>Calculated Track Length:</strong>
<span id="curved_calculated_length" class="h3 ml-2">0</span> <span class="text-muted">inches</span>
<span class="mx-2">|</span>
<span id="curved_calculated_feet" class="h5">0</span> <span class="text-muted">feet</span>
<span class="mx-2">|</span>
<span id="curved_calculated_metres" class="h5">0</span> <span class="text-muted">metres</span>
</div>
<div class="col-md-4">
<label class="form-label">Manual Override (inches)</label>
<input type="number" name="stair_curved_manual_override" class="form-control"
step="0.5" placeholder="Leave blank to use calculated"/>
</div>
</div>
</div>
<div id="curved_calculation_breakdown" class="small text-muted">
<!-- Breakdown will be populated by JavaScript -->
</div>
</div>
</div>
<!-- Video Upload Section -->
<div class="card mb-4">
<div class="card-header bg-dark text-white">
<h5 class="mb-0"><i class="fa fa-video-camera"></i> Video Recording</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-12 mb-3">
<label class="form-label">Upload Assessment Video</label>
<input type="file" name="assessment_video" id="assessment_video" class="form-control"
accept="video/*"/>
<small class="text-muted">
<i class="fa fa-info-circle"></i>
Upload a video walkthrough of the staircase. Video will be compressed automatically.
<br/>Supported formats: MP4, MOV, AVI, WebM (Max 100MB)
</small>
<div id="video_preview" class="mt-2" style="display:none;">
<video id="video_player" controls="controls" style="max-width: 100%; max-height: 300px;"></video>
</div>
<div id="video_compress_status" class="mt-2" style="display:none;">
<div class="progress">
<div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar"
id="compress_progress" style="width: 0%">Compressing...</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Additional Features -->
<div class="card mb-4">
<div class="card-header bg-success text-white">
<h5 class="mb-0"><i class="fa fa-cogs"></i> Additional Features</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<h6>Power Features</h6>
<div class="form-check mb-2">
<input type="checkbox" name="stair_power_swivel_upstairs" value="true" class="form-check-input" id="swivel_up"/>
<label class="form-check-label" for="swivel_up">Power Swivel (Upstairs)</label>
</div>
<div class="form-check mb-2">
<input type="checkbox" name="stair_power_swivel_downstairs" value="true" class="form-check-input" id="swivel_down"/>
<label class="form-check-label" for="swivel_down">Power Swivel (Downstairs)</label>
</div>
<div class="form-check mb-2">
<input type="checkbox" name="stair_auto_folding_footrest" value="true" class="form-check-input" id="auto_footrest"/>
<label class="form-check-label" for="auto_footrest">Automatic Folding Footrest</label>
</div>
</div>
<div class="col-md-6">
<h6>Other Features</h6>
<div class="form-check mb-2">
<input type="checkbox" name="stair_auto_folding_hinge" value="true" class="form-check-input" id="auto_hinge"/>
<label class="form-check-label" for="auto_hinge">Automatic Folding Hinge</label>
</div>
<div class="form-check mb-2">
<input type="checkbox" name="stair_auto_folding_seat" value="true" class="form-check-input" id="auto_seat"/>
<label class="form-check-label" for="auto_seat">Automatic Folding Seat</label>
</div>
<div class="form-check mb-2">
<input type="checkbox" name="stair_custom_color" value="true" class="form-check-input" id="custom_color"/>
<label class="form-check-label" for="custom_color">Customizable Colored Seat</label>
</div>
<div class="form-check mb-2">
<input type="checkbox" name="stair_additional_charging" value="true" class="form-check-input" id="add_charging"/>
<label class="form-check-label" for="add_charging">Additional Charging Station</label>
</div>
<div class="form-check mb-2">
<input type="checkbox" name="stair_charging_with_remote" value="true" class="form-check-input" id="charging_remote"/>
<label class="form-check-label" for="charging_remote">Charging Station with Remote</label>
</div>
</div>
</div>
</div>
</div>
<!-- Photos -->
<t t-call="fusion_portal.accessibility_photo_section"/>
<!-- Notes -->
<t t-call="fusion_portal.accessibility_notes_section"/>
<!-- Submit Buttons -->
<t t-call="fusion_portal.accessibility_submit_buttons"/>
</form>
</div>
<!-- Scripts -->
<t t-call="fusion_portal.accessibility_form_scripts"/>
<script type="text/javascript">
// Landing type track lengths (in inches)
// Note: vertical_overrun and horizontal_overrun use custom values from input fields
var TOP_LANDING_LENGTHS = {
'none': 0,
'90_exit': 24,
'90_parking': 24,
'180_parking': 48,
'flush_landing': 12,
'vertical_overrun': 0 // Custom - uses input field
};
var BOTTOM_LANDING_LENGTHS = {
'none': 0,
'90_park': 24,
'180_park': 48,
'drop_nose': 12,
'short_vertical': 12,
'horizontal_overrun': 0 // Custom - uses input field
};
// Handler for top landing type change - show/hide custom field
function handleTopLandingChange() {
var topLandingType = document.getElementById('stair_top_landing_type').value;
var customContainer = document.getElementById('top_overrun_custom_container');
var customInput = document.getElementById('top_overrun_custom_length');
if (topLandingType === 'vertical_overrun') {
customContainer.style.display = 'block';
customInput.required = true;
} else {
customContainer.style.display = 'none';
customInput.required = false;
customInput.value = '';
}
calculateCurvedTrackLength();
}
// Handler for bottom landing type change - show/hide custom field
function handleBottomLandingChange() {
var bottomLandingType = document.getElementById('stair_bottom_landing_type').value;
var customContainer = document.getElementById('bottom_overrun_custom_container');
var customInput = document.getElementById('bottom_overrun_custom_length');
if (bottomLandingType === 'horizontal_overrun') {
customContainer.style.display = 'block';
customInput.required = true;
} else {
customContainer.style.display = 'none';
customInput.required = false;
customInput.value = '';
}
calculateCurvedTrackLength();
}
function calculateCurvedTrackLength() {
var steps = parseInt(document.getElementById('stair_curved_steps').value) || 0;
var curves = parseInt(document.getElementById('stair_curves_count').value) || 0;
var topLandingType = document.getElementById('stair_top_landing_type').value;
var bottomLandingType = document.getElementById('stair_bottom_landing_type').value;
// Calculate each component
var baseLength = steps * 12; // 12" per step
var curvesLength = curves * 16; // 16" per curve
// Top landing length - check for custom overrun
var topLandingLength = 0;
if (topLandingType === 'vertical_overrun') {
topLandingLength = parseFloat(document.getElementById('top_overrun_custom_length').value) || 0;
} else {
topLandingLength = TOP_LANDING_LENGTHS[topLandingType] || 0;
}
// Bottom landing length - check for custom overrun
var bottomLandingLength = 0;
if (bottomLandingType === 'horizontal_overrun') {
bottomLandingLength = parseFloat(document.getElementById('bottom_overrun_custom_length').value) || 0;
} else {
bottomLandingLength = BOTTOM_LANDING_LENGTHS[bottomLandingType] || 0;
}
var total = baseLength + curvesLength + topLandingLength + bottomLandingLength;
// Display in different units
document.getElementById('curved_calculated_length').textContent = total.toFixed(0);
document.getElementById('curved_calculated_feet').textContent = (total / 12).toFixed(2);
document.getElementById('curved_calculated_metres').textContent = (total * 0.0254).toFixed(2);
// Build breakdown HTML
var breakdownHtml = '&lt;strong&gt;Breakdown:&lt;/strong&gt; ';
var parts = [];
if (baseLength > 0) parts.push(steps + ' steps × 12" = ' + baseLength + '"');
if (curvesLength > 0) parts.push(curves + ' curves × 16" = ' + curvesLength + '"');
if (topLandingLength > 0) {
var topLabel = document.getElementById('stair_top_landing_type').options[document.getElementById('stair_top_landing_type').selectedIndex].text;
if (topLandingType === 'vertical_overrun') {
parts.push('Top Overrun (custom) = ' + topLandingLength + '"');
} else {
parts.push('Top: ' + topLabel.split('(')[0].trim() + ' = ' + topLandingLength + '"');
}
}
if (bottomLandingLength > 0) {
var bottomLabel = document.getElementById('stair_bottom_landing_type').options[document.getElementById('stair_bottom_landing_type').selectedIndex].text;
if (bottomLandingType === 'horizontal_overrun') {
parts.push('Bottom Overrun (custom) = ' + bottomLandingLength + '"');
} else {
parts.push('Bottom: ' + bottomLabel.split('(')[0].trim() + ' = ' + bottomLandingLength + '"');
}
}
breakdownHtml += parts.join(' + ') + ' = &lt;strong&gt;' + total + '"&lt;/strong&gt;';
document.getElementById('curved_calculation_breakdown').innerHTML = breakdownHtml;
}
// Photo preview handlers
document.addEventListener('DOMContentLoaded', function() {
// Top landing photos preview
var topPhotosInput = document.getElementById('top_landing_photos');
if (topPhotosInput) {
topPhotosInput.addEventListener('change', function(e) {
previewImages(e.target.files, 'top_landing_preview');
});
}
// Bottom landing photos preview
var bottomPhotosInput = document.getElementById('bottom_landing_photos');
if (bottomPhotosInput) {
bottomPhotosInput.addEventListener('change', function(e) {
previewImages(e.target.files, 'bottom_landing_preview');
});
}
// Video preview
var videoInput = document.getElementById('assessment_video');
if (videoInput) {
videoInput.addEventListener('change', function(e) {
var file = e.target.files[0];
if (file) {
var videoPreview = document.getElementById('video_preview');
var videoPlayer = document.getElementById('video_player');
if (videoPlayer &amp;&amp; videoPreview) {
videoPlayer.src = URL.createObjectURL(file);
videoPreview.style.display = 'block';
}
// Show file size info
var sizeMB = (file.size / (1024 * 1024)).toFixed(2);
console.log('Video size: ' + sizeMB + ' MB');
}
});
}
});
function previewImages(files, previewContainerId) {
var container = document.getElementById(previewContainerId);
if (!container) return;
container.innerHTML = '';
Array.from(files).forEach(function(file, index) {
var reader = new FileReader();
reader.onload = function(e) {
var col = document.createElement('div');
col.className = 'col-3 mb-2';
col.innerHTML = '&lt;img src="' + e.target.result + '" class="img-thumbnail" style="max-height: 80px; object-fit: cover;"/&gt;';
container.appendChild(col);
};
reader.readAsDataURL(file);
});
}
</script>
</t>
</template>
<!-- ============================================================= -->
<!-- VERTICAL PLATFORM LIFT (VPL) FORM -->
<!-- ============================================================= -->
<template id="portal_accessibility_vpl" name="VPL Assessment">
<t t-call="portal.portal_layout">
<t t-set="breadcrumbs_searchbar" t-value="True"/>
<t t-set="no_breadcrumbs" t-value="True"/>
<div class="container py-4">
<!-- Custom Breadcrumb -->
<nav aria-label="breadcrumb" class="mb-4">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/my">Dashboard</a></li>
<li class="breadcrumb-item"><a href="/my/accessibility">Accessibility</a></li>
<li class="breadcrumb-item active">Vertical Platform Lift</li>
</ol>
</nav>
<h2 class="mb-4">
<i class="fa fa-arrows-v text-success"></i>
Vertical Platform Lift Assessment
</h2>
<form id="accessibility_form">
<input type="hidden" name="assessment_type" value="vpl"/>
<!-- Client Info -->
<t t-call="fusion_portal.accessibility_client_info_section"/>
<!-- Room Dimensions -->
<div class="card mb-4">
<div class="card-header bg-success text-white">
<h5 class="mb-0"><i class="fa fa-expand"></i> Room Dimensions</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-4 mb-3">
<label class="form-label">Room Width (inches)</label>
<input type="number" name="vpl_room_width" class="form-control"
min="0" step="0.5" placeholder="Width"/>
</div>
<div class="col-md-4 mb-3">
<label class="form-label">Room Depth (inches)</label>
<input type="number" name="vpl_room_depth" class="form-control"
min="0" step="0.5" placeholder="Depth"/>
</div>
<div class="col-md-4 mb-3">
<label class="form-label">Total Rise Height (inches) <span class="text-danger">*</span></label>
<input type="number" name="vpl_rise_height" class="form-control"
required="required" min="0" step="0.5" placeholder="Rise height"/>
</div>
</div>
</div>
</div>
<!-- Platform Requirements -->
<div class="card mb-4">
<div class="card-header bg-info text-white">
<h5 class="mb-0"><i class="fa fa-cube"></i> Platform Requirements</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6 mb-3">
<div class="form-check">
<input type="checkbox" name="vpl_has_existing_platform" value="true" class="form-check-input" id="existing_platform"/>
<label class="form-check-label" for="existing_platform">Existing Platform Available</label>
</div>
<small class="text-muted">Lift needs anchor bolts; platform must be concrete</small>
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Concrete Depth (inches)</label>
<input type="number" name="vpl_concrete_depth" class="form-control"
min="0" step="0.5" placeholder="Minimum 4 inches required"/>
<small class="text-muted">Minimum 4" required to absorb vibration</small>
</div>
</div>
</div>
</div>
<!-- Power Requirements -->
<div class="card mb-4">
<div class="card-header bg-warning">
<h5 class="mb-0"><i class="fa fa-bolt"></i> Power Requirements</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Model Type</label>
<select name="vpl_model_type" class="form-control">
<option value="">-- Select --</option>
<option value="ac">AC Model (Requires dedicated 15-amp breaker)</option>
<option value="dc">DC Model (No dedicated breaker required)</option>
</select>
</div>
<div class="col-md-6 mb-3">
<div class="form-check mb-2">
<input type="checkbox" name="vpl_has_nearby_plug" value="true" class="form-check-input" id="nearby_plug"/>
<label class="form-check-label" for="nearby_plug">Power Plug Nearby (110V / 15-amp)</label>
</div>
<div class="form-check mb-2">
<input type="checkbox" name="vpl_needs_plug_install" value="true" class="form-check-input" id="plug_install"/>
<label class="form-check-label" for="plug_install">Needs Plug Installation</label>
</div>
</div>
</div>
</div>
</div>
<!-- Certification -->
<div class="card mb-4">
<div class="card-header" style="background: linear-gradient(135deg, #5ba848 0%, #3a8fb7 60%, #2e7aad 100%); color: white;">
<h5 class="mb-0"><i class="fa fa-certificate"></i> Certification</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6 mb-3">
<div class="form-check">
<input type="checkbox" name="vpl_needs_certification" value="true" class="form-check-input" id="needs_cert"/>
<label class="form-check-label" for="needs_cert">Needs City Certification</label>
</div>
<small class="text-muted">Required if installing plug or other electrical work</small>
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Certification Notes</label>
<textarea name="vpl_certification_notes" class="form-control" rows="2"
placeholder="Any certification requirements..."></textarea>
</div>
</div>
</div>
</div>
<!-- Photos -->
<t t-call="fusion_portal.accessibility_photo_section"/>
<!-- Notes -->
<t t-call="fusion_portal.accessibility_notes_section"/>
<!-- Submit Buttons -->
<t t-call="fusion_portal.accessibility_submit_buttons"/>
</form>
</div>
<!-- Scripts -->
<t t-call="fusion_portal.accessibility_form_scripts"/>
</t>
</template>
<!-- ============================================================= -->
<!-- CEILING LIFT FORM -->
<!-- ============================================================= -->
<template id="portal_accessibility_ceiling" name="Ceiling Lift Assessment">
<t t-call="portal.portal_layout">
<t t-set="breadcrumbs_searchbar" t-value="True"/>
<t t-set="no_breadcrumbs" t-value="True"/>
<div class="container py-4">
<!-- Custom Breadcrumb -->
<nav aria-label="breadcrumb" class="mb-4">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/my">Dashboard</a></li>
<li class="breadcrumb-item"><a href="/my/accessibility">Accessibility</a></li>
<li class="breadcrumb-item active">Ceiling Lift</li>
</ol>
</nav>
<h2 class="mb-4">
<i class="fa fa-cloud-upload text-warning"></i>
Ceiling Lift Assessment
</h2>
<form id="accessibility_form">
<input type="hidden" name="assessment_type" value="ceiling_lift"/>
<!-- Client Info -->
<t t-call="fusion_portal.accessibility_client_info_section"/>
<!-- Track Specifications -->
<div class="card mb-4">
<div class="card-header bg-warning">
<h5 class="mb-0"><i class="fa fa-arrows-h"></i> Track Specifications</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Total Track Length (feet) <span class="text-danger">*</span></label>
<input type="number" name="ceiling_track_length" class="form-control"
required="required" min="0" step="0.5" placeholder="e.g., 15"/>
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Horizontal Movement Type</label>
<select name="ceiling_movement_type" class="form-control">
<option value="">-- Select --</option>
<option value="manual">Manual (left-to-right)</option>
<option value="powered">Powered (left-to-right)</option>
</select>
<small class="text-muted">All ceiling lifts move up/down with power</small>
</div>
</div>
</div>
</div>
<!-- Features -->
<div class="card mb-4">
<div class="card-header bg-success text-white">
<h5 class="mb-0"><i class="fa fa-cogs"></i> Additional Features</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<div class="form-check mb-2">
<input type="checkbox" name="ceiling_charging_throughout" value="true" class="form-check-input" id="charging_throughout"/>
<label class="form-check-label" for="charging_throughout">Charging Throughout Track</label>
<small class="text-muted d-block">Instead of single location charging</small>
</div>
<div class="form-check mb-2">
<input type="checkbox" name="ceiling_carry_bar" value="true" class="form-check-input" id="carry_bar"/>
<label class="form-check-label" for="carry_bar">Carry Bar</label>
</div>
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Additional Slings Needed</label>
<input type="number" name="ceiling_additional_slings" class="form-control"
min="0" value="0" placeholder="0"/>
</div>
</div>
</div>
</div>
<!-- Photos -->
<t t-call="fusion_portal.accessibility_photo_section"/>
<!-- Notes -->
<t t-call="fusion_portal.accessibility_notes_section"/>
<!-- Submit Buttons -->
<t t-call="fusion_portal.accessibility_submit_buttons"/>
</form>
</div>
<!-- Scripts -->
<t t-call="fusion_portal.accessibility_form_scripts"/>
</t>
</template>
<!-- ============================================================= -->
<!-- CUSTOM RAMP FORM -->
<!-- ============================================================= -->
<template id="portal_accessibility_ramp" name="Custom Ramp Assessment">
<t t-call="portal.portal_layout">
<t t-set="breadcrumbs_searchbar" t-value="True"/>
<t t-set="no_breadcrumbs" t-value="True"/>
<div class="container py-4">
<!-- Custom Breadcrumb -->
<nav aria-label="breadcrumb" class="mb-4">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/my">Dashboard</a></li>
<li class="breadcrumb-item"><a href="/my/accessibility">Accessibility</a></li>
<li class="breadcrumb-item active">Custom Ramp</li>
</ol>
</nav>
<h2 class="mb-4">
<i class="fa fa-road text-danger"></i>
Custom Ramp Assessment
</h2>
<form id="accessibility_form">
<input type="hidden" name="assessment_type" value="ramp"/>
<!-- Client Info -->
<t t-call="fusion_portal.accessibility_client_info_section"/>
<!-- Ramp Measurements -->
<div class="card mb-4">
<div class="card-header bg-danger text-white">
<h5 class="mb-0"><i class="fa fa-ruler"></i> Ramp Measurements</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-4 mb-3">
<label class="form-label">Total Height (inches) <span class="text-danger">*</span></label>
<input type="number" name="ramp_height" id="ramp_height" class="form-control"
required="required" min="0" step="0.5" placeholder="Height from ground"
onchange="calculateRampLength()"/>
</div>
<div class="col-md-4 mb-3">
<label class="form-label">Ground Incline (degrees)</label>
<input type="number" name="ramp_ground_incline" class="form-control"
min="0" max="45" step="0.5" placeholder="Optional"/>
<small class="text-muted">If ground is already inclined</small>
</div>
<div class="col-md-4 mb-3">
<label class="form-label">Handrail Height (inches)</label>
<input type="number" name="ramp_handrail_height" class="form-control"
value="32" min="32" step="0.5"/>
<small class="text-muted">Minimum 32" required</small>
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<div class="form-check">
<input type="checkbox" name="ramp_at_door" value="true" class="form-check-input"
id="ramp_at_door" onchange="calculateRampLength()"/>
<label class="form-check-label" for="ramp_at_door">Ramp at Door</label>
<small class="text-muted d-block">Requires 5ft landing at door</small>
</div>
</div>
</div>
<!-- Calculated Values -->
<div class="alert alert-info">
<h6><i class="fa fa-calculator"></i> Ontario Building Code Calculations</h6>
<div class="row">
<div class="col-md-4">
<strong>Ramp Length:</strong>
<span id="calc_ramp_length" class="h5 ml-2">0</span> inches
<br/>
<small class="text-muted">1:12 ratio (12" per 1" height)</small>
</div>
<div class="col-md-4">
<strong>Landings Needed:</strong>
<span id="calc_landings" class="h5 ml-2">0</span>
<br/>
<small class="text-muted">Every 30 feet (5ft each)</small>
</div>
<div class="col-md-4">
<strong>Total Length:</strong>
<span id="calc_total_length" class="h5 ml-2">0</span> inches
<br/>
<small class="text-muted">Including landings</small>
</div>
</div>
<div class="row mt-3">
<div class="col-md-6">
<label class="form-label">Manual Override (inches)</label>
<input type="number" name="ramp_manual_override" class="form-control"
step="0.5" placeholder="Leave blank to use calculated"/>
</div>
</div>
</div>
</div>
</div>
<!-- Photos -->
<t t-call="fusion_portal.accessibility_photo_section"/>
<!-- Notes -->
<t t-call="fusion_portal.accessibility_notes_section"/>
<!-- Submit Buttons -->
<t t-call="fusion_portal.accessibility_submit_buttons"/>
</form>
</div>
<!-- Scripts -->
<t t-call="fusion_portal.accessibility_form_scripts"/>
<script type="text/javascript">
function calculateRampLength() {
var height = parseFloat(document.getElementById('ramp_height').value) || 0;
var atDoor = document.getElementById('ramp_at_door').checked;
// Ontario Building Code: 12" length per 1" height
var rampLength = height * 12;
// Landings every 30 feet (360 inches)
var landings = Math.ceil(rampLength / 360);
// Each landing is 5 feet (60 inches)
var landingsLength = landings * 60;
// Door landing if at door
var doorLanding = atDoor ? 60 : 0;
var totalLength = rampLength + landingsLength + doorLanding;
document.getElementById('calc_ramp_length').textContent = rampLength.toFixed(0);
document.getElementById('calc_landings').textContent = landings;
document.getElementById('calc_total_length').textContent = totalLength.toFixed(0);
}
</script>
</t>
</template>
<!-- ============================================================= -->
<!-- BATHROOM MODIFICATION FORM -->
<!-- ============================================================= -->
<template id="portal_accessibility_bathroom" name="Bathroom Modification Assessment">
<t t-call="portal.portal_layout">
<t t-set="breadcrumbs_searchbar" t-value="True"/>
<t t-set="no_breadcrumbs" t-value="True"/>
<div class="container py-4">
<!-- Custom Breadcrumb -->
<nav aria-label="breadcrumb" class="mb-4">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/my">Dashboard</a></li>
<li class="breadcrumb-item"><a href="/my/accessibility">Accessibility</a></li>
<li class="breadcrumb-item active">Bathroom Modification</li>
</ol>
</nav>
<h2 class="mb-4">
<i class="fa fa-bath text-secondary"></i>
Bathroom Modification Assessment
</h2>
<form id="accessibility_form">
<input type="hidden" name="assessment_type" value="bathroom"/>
<!-- Client Info -->
<t t-call="fusion_portal.accessibility_client_info_section"/>
<!-- Modification Description -->
<div class="card mb-4">
<div class="card-header" style="background: linear-gradient(135deg, #2e7aad 0%, #1a6b9a 60%, #155d87 100%); color: white;">
<h5 class="mb-0"><i class="fa fa-pencil"></i> Modification Description</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-12 mb-3">
<label class="form-label">Describe all bathroom modifications needed <span class="text-danger">*</span></label>
<textarea name="bathroom_description" class="form-control" rows="8"
required="required"
placeholder="Describe in detail:
- What modifications are needed?
- Current bathroom layout
- Specific requirements
- Grab bar locations
- Shower/tub modifications
- Flooring changes
- Any other details..."></textarea>
</div>
</div>
</div>
</div>
<!-- Photos -->
<t t-call="fusion_portal.accessibility_photo_section"/>
<!-- Notes -->
<t t-call="fusion_portal.accessibility_notes_section"/>
<!-- Submit Buttons -->
<t t-call="fusion_portal.accessibility_submit_buttons"/>
</form>
</div>
<!-- Scripts -->
<t t-call="fusion_portal.accessibility_form_scripts"/>
</t>
</template>
<!-- ============================================================= -->
<!-- TUB CUTOUT FORM -->
<!-- ============================================================= -->
<template id="portal_accessibility_tub_cutout" name="Tub Cutout Assessment">
<t t-call="portal.portal_layout">
<t t-set="breadcrumbs_searchbar" t-value="True"/>
<t t-set="no_breadcrumbs" t-value="True"/>
<div class="container py-4">
<!-- Custom Breadcrumb -->
<nav aria-label="breadcrumb" class="mb-4">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/my">Dashboard</a></li>
<li class="breadcrumb-item"><a href="/my/accessibility">Accessibility</a></li>
<li class="breadcrumb-item active">Tub Cutout</li>
</ol>
</nav>
<h2 class="mb-4">
<i class="fa fa-cut" style="color: #6c5ce7;"></i>
Tub Cutout Assessment
</h2>
<form id="accessibility_form">
<input type="hidden" name="assessment_type" value="tub_cutout"/>
<!-- Client Info -->
<t t-call="fusion_portal.accessibility_client_info_section"/>
<!-- Tub Measurements -->
<div class="card mb-4">
<div class="card-header" style="background: #6c5ce7; color: white;">
<h5 class="mb-0"><i class="fa fa-ruler"></i> Tub Measurements</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Internal Height of Tub (inches) <span class="text-danger">*</span></label>
<input type="number" name="tub_internal_height" class="form-control"
required="required" min="0" step="0.5" placeholder="Internal height"/>
<small class="text-muted">Measure from inside bottom to top edge</small>
</div>
<div class="col-md-6 mb-3">
<label class="form-label">External Height of Tub (inches) <span class="text-danger">*</span></label>
<input type="number" name="tub_external_height" class="form-control"
required="required" min="0" step="0.5" placeholder="External height"/>
<small class="text-muted">Measure from floor to top edge</small>
</div>
</div>
</div>
</div>
<!-- Additional Supplies -->
<div class="card mb-4">
<div class="card-header bg-info text-white">
<h5 class="mb-0"><i class="fa fa-shopping-cart"></i> Additional Supplies</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-12 mb-3">
<label class="form-label">Additional supplies needed with the modification</label>
<textarea name="tub_additional_supplies" class="form-control" rows="4"
placeholder="List any additional supplies needed:
- Grab bars
- Non-slip mats
- Shower seat
- Other items..."></textarea>
</div>
</div>
</div>
</div>
<!-- Photos -->
<t t-call="fusion_portal.accessibility_photo_section"/>
<!-- Notes -->
<t t-call="fusion_portal.accessibility_notes_section"/>
<!-- Submit Buttons -->
<t t-call="fusion_portal.accessibility_submit_buttons"/>
</form>
</div>
<!-- Scripts -->
<t t-call="fusion_portal.accessibility_form_scripts"/>
</t>
</template>
</odoo>