nhance/app/Views/view_rfq_non_eb_new.php
2026-02-09 13:13:19 +05:30

3423 lines
144 KiB
PHP
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.

<style>
h3 {
margin-bottom: 15px !important;
}
/* Table general styles */
table {
margin-bottom: 15px;
width: 1400px;
min-width: 1200px;
table-layout: fixed;
border-collapse: collapse;
word-wrap: break-word;
font-size: 11px;
}
/* Header and body cells */
th,
td {
padding: 2px 4px !important;
text-align: center;
vertical-align: middle;
line-height: 1 !important;
}
/* Table header */
th {
font-size: 12px;
font-weight: 600;
background-color: #f8f9fa;
text-align: center;
vertical-align: middle;
}
#policyTable,
#policyTable th,
#policyTable td {
border: 1px solid #007bff;
}
/* Fixed width columns */
#policyTable th:nth-child(1),
#policyTable td:nth-child(1) {
width: 110px !important;
min-width: 120px !important;
max-width: 120px !important;
word-wrap: break-word;
white-space: normal;
text-align: center;
vertical-align: middle;
}
#policyTable th:last-child,
#policyTable td:last-child {
width: 260px !important;
min-width: 260px !important;
max-width: 260px !important;
text-align: center;
white-space: nowrap;
text-align: center;
vertical-align: middle;
}
/* Fixed width for particulars columns */
.particulars {
width: 400px !important;
min-width: 400px !important;
max-width: 400px !important;
word-wrap: break-word !important;
white-space: normal !important;
text-align: center !important;
vertical-align: middle !important;
}
/* Fixed width for risk location columns */
.risk-col {
width: 180px !important;
min-width: 180px !important;
max-width: 180px !important;
text-align: center !important;
vertical-align: middle !important;
}
/* Fixed width for index value columns */
.index-val-col {
width: 180px !important;
min-width: 180px !important;
max-width: 180px !important;
text-align: center !important;
vertical-align: middle !important;
}
/* Fixed width for insurer columns */
.insurer-col {
width: 180px !important;
min-width: 180px !important;
max-width: 180px !important;
text-align: center !important;
vertical-align: middle !important;
}
/* Fixed width for total sum insured columns */
.sum_insured {
width: 180px !important;
min-width: 180px !important;
max-width: 180px !important;
text-align: center !important;
vertical-align: middle !important;
}
/* Fixed width for index sum insured columns */
.index_sum_insured {
width: 180px !important;
min-width: 180px !important;
max-width: 180px !important;
text-align: center !important;
;
vertical-align: middle !important;
}
/* Compact rows */
#policyTable tbody tr {
height: 28px !important;
}
/* Compact rows */
#policyTable thead tr {
height: 30px !important;
/* Fixed row height */
}
/* Sub-options and checkboxes */
.sub-options {
margin-left: 15px;
margin-top: 3px;
font-size: 11px;
}
.sub-options label {
margin-right: 12px;
}
input[type="checkbox"] {
transform: scale(0.9);
margin: 1px 3px;
}
/* Labels and forms */
label {
font-size: 11px;
margin-right: 12px;
margin-bottom: 3px;
}
.form-control {
font-size: 11px;
padding: 4px 6px;
}
/* Dropdown items */
.dropdown .btn {
padding: 2px 4px;
font-size: 10px;
margin: 0;
}
.dropdown-menu {
font-size: 11px;
min-width: 100px;
}
.dropdown-item {
padding: 4px 8px;
}
.dropdown-item .form-check-label{
position: relative !important;
left: 30px !important;
}
/* Policy header rows */
.policy-header-row {
cursor: pointer;
background-color: #f8f9fa;
}
/* Hidden policy details rows */
.policy-detail-row {
display: none;
}
/*------------------------------------------------------------------------------------------------------*/
/* Collapsed column styles */
.risk-col.collapsed
{
width: 40px !important;
min-width: 50px !important;
max-width: 50px !important;
overflow: visible !important;
position: relative;
padding: 5px !important;
}
.index-val-col.collapsed
{
width: 40px !important;
min-width: 50px !important;
max-width: 50px !important;
overflow: visible !important;
position: relative;
padding: 5px !important;
}
.insurer-col.collapsed
{
width: 40px !important;
min-width: 50px !important;
max-width: 50px !important;
overflow: visible !important;
position: relative;
padding: 5px !important;
}
/* Hide content when collapsed, but keep dropdown visible */
.risk-col.collapsed>div:not(.dropdown) {
display: none;
}
.insurer-col.collapsed>*:not(.dropdown) {
display: none;
}
.index-val-col.collapsed>*:not(.dropdown) {
display: none;
}
/* Position and style dropdown in collapsed state */
.risk-col.collapsed .dropdown,
.insurer-col.collapsed .dropdown,
.index-val-col.collapsed .dropdown
{
position: static !important;
display: block !important;
width: 100%;
text-align: center;
float: none !important;
}
.risk-col.collapsed .dropdown .btn,
.insurer-col.collapsed .dropdown .btn,
.index-val-col.collapsed .dropdown .btn
{
width: 100% !important;
height: 25px;
display: flex !important;
align-items: center;
justify-content: center;
border: 1px solid #dee2e6;
background-color: #f8f9fa;
font-size: 13px;
font-weight: bold;
}
/* Hide cell content when parent column is collapsed */
.risk-col.collapsed[data-risk-id="1"]~* td.risk-col[data-risk-id="1"],
.risk-col.collapsed[data-risk-id="2"]~* td.risk-col[data-risk-id="2"],
.risk-col.collapsed[data-risk-id="3"]~* td.risk-col[data-risk-id="3"]
{
width: 40px !important;
min-width: 40px !important;
max-width: 40px !important;
overflow: hidden;
text-indent: -9999px;
}
.insurer-col.collapsed[data-insurer-id="1"]~* td.insurer-col[data-insurer-id="1"],
.insurer-col.collapsed[data-insurer-id="2"]~* td.insurer-col[data-insurer-id="2"],
.insurer-col.collapsed[data-insurer-id="3"]~* td.insurer-col[data-insurer-id="3"]
{
width: 40px !important;
min-width: 40px !important;
max-width: 40px !important;
overflow: hidden;
text-indent: -9999px;
}
.index-val-col.collapsed[data-index-value-id="1"]~* td.index-val-col[data-index-value-id="1"],
.index-val-col.collapsed[data-index-value-id="2"]~* td.index-val-col[data-index-value-id="2"],
.index-val-col.collapsed[data-index-value-id="3"]~* td.index-val-col[data-index-value-id="3"]
{
width: 40px !important;
min-width: 40px !important;
max-width: 40px !important;
overflow: hidden;
text-indent: -9999px;
}
/* Apply collapsed state to matching data attributes */
th.risk-col.collapsed[data-risk-id="1"]~tbody tr td[data-risk-id="1"],
th.risk-col.collapsed[data-risk-id="2"]~tbody tr td[data-risk-id="2"],
th.risk-col.collapsed[data-risk-id="3"]~tbody tr td[data-risk-id="3"] {
width: 40px !important;
overflow: hidden;
text-indent: -9999px;
}
th.insurer-col.collapsed[data-insurer-id="1"]~tbody tr td[data-insurer-id="1"],
th.insurer-col.collapsed[data-insurer-id="2"]~tbody tr td[data-insurer-id="2"],
th.insurer-col.collapsed[data-insurer-id="3"]~tbody tr td[data-insurer-id="3"]
{
width: 40px !important;
overflow: hidden;
text-indent: -9999px;
}
th.index-val-col.collapsed[data-index-value-id="1"]~tbody tr td[data-index-value-id="1"],
th.index-val-col.collapsed[data-index-value-id="2"]~tbody tr td[data-index-value-id="2"],
th.index-val-col.collapsed[data-index-value-id="3"]~tbody tr td[data-index-value-id="3"]
{
width: 40px !important;
overflow: hidden;
text-indent: -9999px;
}
/* Transition effect */
.risk-col,
.insurer-col,
.index-val-col
{
transition: width 0.3s ease, min-width 0.3s ease, max-width 0.3s ease;
}
/* Table responsive wrapper */
.table-wrapper {
overflow-x: auto;
max-width: 100%;
}
/* Dropdown menu styling */
.dropdown-menu {
min-width: 180px;
}
/* Add visual indicator for collapsed state */
.risk-col.collapsed .dropdown-toggle::after,
.insurer-col.collapsed .dropdown-toggle::after ,
.index-val-col.collapsed .dropdown-toggle::after
{
content: "";
border: none;
vertical-align: middle;
font-size: 10px;
margin-left: 4px;
}
.risk-col:not(.collapsed) .dropdown-toggle::after,
.insurer-col:not(.collapsed) .dropdown-toggle::after,
.index-val-col:not(.collapsed) .dropdown-toggle::after
{
content: "";
border: none;
vertical-align: middle;
}
/*-------------------------------------------------------------------------------------*/
.table-scroll-container {
width: 100%;
overflow-x: auto;
overflow-y: visible;
}
/* Scrollbar styling */
.table-scroll-container::-webkit-scrollbar {
height: 5px;
}
.table-scroll-container::-webkit-scrollbar-track {
background: #f1f1f1;
/* border-radius: 3px; */
}
.table-scroll-container::-webkit-scrollbar-thumb {
background: #888;
/* border-radius: 3px; */
}
.table-scroll-container::-webkit-scrollbar-thumb:hover {
background: #555;
}
/* Content editable styling */
[contenteditable="true"]:focus {
outline: 2px solid #0d6efd;
outline-offset: -2px;
background-color: #f8f9fa;
}
.policy-collapse {
display: inline-block;
width: 20px; /* increased size */
height: 20px; /* increased size */
margin-left: 8px;
vertical-align: middle;
cursor: pointer;
position: relative;
border: 1.5px solid #4a90e2; /* slightly thicker */
border-radius: 50%;
background: #f8f9fa;
transition: all 0.3s ease;
}
/* Arrow pointing up (collapsed - will expand down when clicked) */
.policy-collapse::before {
content: '';
position: absolute;
top: 6px; /* adjusted for bigger circle */
left: 6px;
width: 6px; /* bigger arrow */
height: 6px;
border-right: 2px solid #4a90e2;
border-bottom: 2px solid #4a90e2;
transform: rotate(-135deg);
transition: transform 0.3s ease;
}
/* Arrow pointing down (expanded - will collapse up when clicked) */
.policy-collapse.expanded::before {
transform: rotate(45deg);
top: 5px;
left: 6px;
}
/* Hover effect */
.policy-collapse:hover {
background: #4a90e2;
transform: scale(1.2); /* slightly larger on hover */
}
.policy-collapse:hover::before {
border-color: white;
}
/* For check box switch */
.switch {
position: relative;
display: inline-block;
width: 54px;
height: 34px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 19px;
width: 19px;
left: 4px;
bottom: 4px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider {
background-color: #2196F3;
}
input:focus + .slider {
box-shadow: 0 0 1px #2196F3;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
/* Rounded sliders */
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}
tbody .sum_insured, tbody .index_sum_insured, tbody .insurer-col {
cursor: pointer;
position: relative;
}
/* Skip cursor pointer for particulars rows */
tbody tr[data-key="particulars"] .sum_insured,
tbody tr[data-key="particulars"] .index_sum_insured,
tbody tr[data-key="particulars"] .insurer-col
{
cursor: default;
}
tbody .sum_insured:hover, tbody .index_sum_insured:hover, tbody .insurer-col:hover {
background-color: #e8f5e8 !important;
}
/* Skip hover effect for particulars rows */
tbody tr[data-key="particulars"] .sum_insured:hover,
tbody tr[data-key="particulars"] .index_sum_insured:hover,
tbody tr[data-key="particulars"] .insurer-col:hover
{
background-color: rgb(224, 254, 201) !important; /* Keep original color */
}
.cell-dropdown {
position: absolute;
top: 100%;
left: 0;
right: 0;
background: white;
border: 1px solid #ccc;
border-top: none;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
z-index: 1000;
max-height: 200px;
overflow-y: auto;
display: none;
}
.cell-dropdown-item {
padding: 8px 12px;
cursor: pointer;
border-bottom: 1px solid #eee;
}
.cell-dropdown-item:hover {
background-color: #f0f8ff;
}
.cell-dropdown-item.selected {
background-color: #007bff;
color: white;
font-weight: bold;
}
.cell-dropdown-item.selected:hover {
background-color: #0056b3;
}
.cell-dropdown-item:last-child {
border-bottom: none;
}
.btn-add-row, .btn-remove-row {
padding: 2px 6px;
font-size: 16px;
line-height: 1.2;
}
</style>
<style>
#policyCheckboxes {
max-width: 100%;
margin: 0 auto;
background: white;
padding: 15px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
/* Hide the actual checkbox */
#policyCheckboxes input[type="checkbox"] {
position: absolute;
opacity: 0;
cursor: pointer;
}
/* Style the labels as compact buttons */
#policyCheckboxes label {
display: inline-block;
margin: 2px;
padding: 4px 8px;
background-color: #ffffff;
border: 1px solid #e0e0e0;
border-radius: 15px;
cursor: pointer;
transition: all 0.3s ease;
font-weight: 500;
text-align: center;
user-select: none;
position: relative;
min-width: 60px;
font-size: 0.75em;
height: 28px;
line-height: 18px;
}
/* Hover effect with your specified border */
#policyCheckboxes label:hover {
background-color: #f8f9fa;
border: 2px solid #007bff;
transform: translateY(-1px);
box-shadow: 0 2px 6px rgba(0,123,255,0.2);
}
/* Checked state - Fixed for proper HTML structure */
#policyCheckboxes input[type="checkbox"]:checked + span,
#policyCheckboxes label:has(input[type="checkbox"]:checked)
{
background-color: #007bff !important;
color: white !important;
border-color: #007bff !important;
box-shadow: 0 2px 8px rgba(0,123,255,0.3) !important;
}
/* Fallback for browsers without :has support */
#policyCheckboxes input[type="checkbox"]:checked {
+ label {
background-color: #007bff !important;
color: white !important;
border-color: #007bff !important;
}
}
/* Fire sub-options styling - default same as main buttons */
.sub-options {
display: inline-block;
margin-left: 8px;
}
/* Sub-options default style (compact) */
.sub-options label {
background-color: #ffffff;
border: 1px solid #e0e0e0;
color: #333333;
font-size: 0.7em;
min-width: 55px;
padding: 3px 6px;
height: 26px;
line-height: 18px;
}
.sub-options label:hover {
background-color: #f8f9fa !important;
border: 2px solid rgba(253, 32, 106, 1) !important;
color: #333333 !important;
}
/* When sub-option checkbox is CHECKED - turn pink - Fixed */
.sub-options input[type="checkbox"]:checked + span,
.sub-options label:has(input[type="checkbox"]:checked) {
background-color: rgba(253, 32, 106, 1) !important;
border-color: #ee3390ff !important;
color: white !important;
}
/* Active/pressed effect */
#policyCheckboxes label:active {
transform: translateY(0);
box-shadow: 0 1px 2px rgba(0,0,0,0.2);
}
/* Responsive design */
@media (max-width: 768px) {
#policyCheckboxes label {
margin: 1px;
font-size: 0.7em;
min-width: 50px;
padding: 3px 6px;
height: 24px;
line-height: 16px;
}
.sub-options {
margin-left: 4px;
}
.sub-options label {
margin: 1px;
min-width: 45px;
padding: 2px 4px;
font-size: 0.65em;
height: 22px;
line-height: 16px;
}
}
/* Focus accessibility */
#policyCheckboxes input[type="checkbox"]:focus + label {
outline: 1px solid #007bff;
outline-offset: 1px;
}
</style>
<h3>RFQ</h3>
<!-- Controls -->
<div>
<button class="btn btn-sm btn-secondary" id="addLocation">+ Add Risk Location</button>&nbsp;&nbsp;
<button class="btn btn-sm btn-primary" onclick="getJson()">Save</button>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<button class="btn btn-sm btn-info" id="addInsurer">+ Add Insurer</button>&nbsp;&nbsp;
<button class="btn btn-sm btn-success" onclick="generateTableFromLocal()">Generate Table</button>
</div>
<br>
<!-- Policies Checkbox -->
<div id="policyCheckboxes">
<label><input type="checkbox" class="policy" value="fire" id="fire_check_box"> Fire</label>
<label><input type="checkbox" class="policy" value="burglary" id="burglary_check_box"> Burglary</label>
<label><input type="checkbox" class="policy" value="flop" id="flop_check_box"> FLOP</label>
<label><input type="checkbox" class="policy" value="all_risk" id="all_risk_check_box"> All Risk</label>
<label><input type="checkbox" class="policy" value="MBD" id="MBD_check_box"> MBD</label>
<label><input type="checkbox" class="policy" value="EEI" id="EEI_check_box"> EEI</label>
<label><input type="checkbox" class="policy" value="fidelity" id="fidelity_check_box"> Fidelity</label>
<label><input type="checkbox" class="policy" value="money" id="money_check_box"> Money</label>
<label><input type="checkbox" class="policy" value="plate_glass" id="plate_glass_check_box"> Plate Glass</label>
<label><input type="checkbox" class="policy" value="neon_sign" id="neon_sign_check_box"> Neon Sign</label>
<label><input type="checkbox" class="policy" value="public_liability" id="public_liability_check_box"> Public
Liability</label>
<label><input type="checkbox" class="policy" value="pedal_cycle" id="pedal_cycle_check_box"> Pedal Cycle</label>
<label><input type="checkbox" class="policy" value="premium" id="premium_check_box"> Premium</label>
</div>
<br>
<!-- table div -->
<div class="table-wrapper table-scroll-container" id="container">
<table id="policyTable" class="table table-bordered">
<thead>
<tr id="policyHeader">
<th>Policy</th>
<th class="particulars">Particulars</th>
<!-- Risk Locations will be inserted here -->
<th class="sum_insured">Total Sum Insured</th>
<th class="index_sum_insured">
Index Sum Insured &nbsp;&nbsp;
<div class="dropdown d-inline-block float-end loc-menu">
<button class="btn btn-sm btn-light dropdown-toggle" type="button" data-toggle="dropdown" aria-expanded="false">
&#x22EE;
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li class="dropdown-item">
<input type="checkbox" class="form-check-input me-2 qcr-check-col" data-risk-id="${riskCount}" checked>
<label class="form-check-label">
QCR
</label>
</li>
<li class="dropdown-item">
<input type="checkbox" class="form-check-input me-2 client-check-col" data-risk-id="${riskCount}" checked>
<label class="form-check-label">
Client
</label>
</li>
</ul>
</div>
</th>
<!-- Insurers will be inserted here -->
<th>Action</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
<!-- Insurer Selection Modal -->
<div class="modal fade" id="insurerModal" tabindex="-1" aria-labelledby="insurerModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="insurerModalLabel">Insurer</h5>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">x</button>
</div>
<div class="modal-body">
<div class="mb-4">
<select id="insurerSelect2" class="form-control" required>
<option value="">Select an insurer</option>
<?php if (isset($insurer) && !empty($insurer) && is_array($insurer)) { ?>
<?php foreach ($insurer as $key => $value): ?>
<option data-id="<?= esc($value['id'] . '-' . $value['insurer_id']) ?>" value="<?= esc($value['insurer_name'] . '-' . $value['branch_code']) ?>"><?= esc($value['insurer_name'] . '-' . $value['branch_code']) ?></option>
<?php endforeach; ?>
<?php } ?>
</select>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" id="insurer_save">Save</button>
</div>
</div>
</div>
</div>
<!-- Risk Location Details Modal -->
<div class="modal fade" id="riskLocationModal" tabindex="-1" aria-labelledby="riskLocationModalLabel"
aria-hidden="true">
<div class="modal-dialog modal-lg">
<!-- wider modal for 3 columns -->
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="riskLocationModalLabel">Add Risk Location Details</h5>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">x</button>
</div>
<div class="modal-body">
<form id="riskLocationForm">
<div class="form-row">
<!-- Location Name -->
<div class="form-group col-md-4">
<label for="locationName" class="form-label">Location Name</label>
<input type="text" class="form-control" id="locationName" placeholder="e.g., Mumbai Branch"
value="Karnataka Branch" required>
</div>
<!-- Address -->
<div class="form-group col-md-4">
<label for="locationAddress" class="form-label">Address</label>
<textarea class="form-control" id="locationAddress" rows="2"
placeholder="Enter full address" required>123, MG Road, Indiranagar, Bengaluru, Karnataka 560038, India</textarea>
</div>
<!-- Constructio -->
<div class="form-group col-md-4">
<label for="construction_type" class="form-label">Construction Type</label>
<select class="form-control" id="construction_type" required>
<option value="">Select</option>
<option value="Kutcha">Kutcha</option>
<option value="Pucca">Pucca</option>
</select>
</div>
<!-- Basement -->
<div class="form-group col-md-4">
<label class="switch" style="position: relative;top: 22px;left: 0px;">
<input id="has_basement" type="checkbox" name="has_basement">
<span class="slider round" style="height: 27px;"></span>
</label>
<label for="has_basement" style="position: relative;top: 25px;left: 0px;">Basement</label>
</div>
<!-- Basement Utilisation -->
<div class="form-group col-md-4 d-none" id="basementUtilisationDiv">
<label for="basement_utilisation" class="form-label">Basement Utilisation</label>
<textarea class="form-control" id="basement_utilisation" rows="2"
placeholder="Describe basement usage"></textarea>
</div>
<!-- Occupancy Risk -->
<div class="form-group col-md-4">
<label for="occupancy_risk" class="form-label">Occupancy Risk</label>
<select class="form-control occupancy-risk" id="occupancy_risk">
<option value="">Select</option>
<option value="OR1">Occupancy 1</option>
<option value="OR2">Occupancy 2</option>
<option value="OR3">Occupancy 3</option>
</select>
</div>
<!-- IIB Code -->
<div class="form-group col-md-4">
<label for="iib_code" class="form-label">IIB Code</label>
<input type="text" class="form-control" id="iib_code"
placeholder="Auto-filled from Occupancy Risk" required>
</div>
<!-- Business Description -->
<div class="form-group col-md-8">
<label for="business_description" class="form-label">Business Description</label>
<textarea class="form-control" id="business_description" rows="2"
placeholder="Enter business description"></textarea>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" id="saveRiskLocation">Add Location</button>
</div>
</div>
</div>
</div>
<script>
// let policyData = {
// "fire": {
// "particulars": {
// "fire_building": " <strong> Building : </strong> All Buildings and civil structures including Super Structure, plinth & foundation, other Civil Structures, Roads, gates, pavements, internal Pathways, stone works, stone arts etc., Street Lights, Landscaping, Play Area, Culvert, drainage, retaining walls, etc. Compound Wall including utility, admin block, security office and all other similar structures pertaining to insured within the premises, Kutcha Constructions, Temporary Sheds, parking Sheds, Shelters, Civil Structure / bed for DG set / Genset, Interlocking Pathways, LVP Pathway Widening, Roads including internal roads, Mini Conference, Swimming pool, rest areas, shelters, Kitchen-Exhaust System Work, Red Earth for Garden, Arch Wall, Retaining Wall, Boundary, compound walls, Water, Vetrified Tiles, Capital Additions related to Building. All other civil structures related to insured at the covered premises.",
// "fire_content": "<strong> Content : </strong> Contents, AC & Accessories, FFF, P & M & Accessories, Electronic Equipments & Accessories, Electrical Items & Fittings, Plumbing Items, Structural Products, Structures, Leasehold Work including Civil Works, Interiors Temporary Structures including Sheds and Containers, Interiors, Plate Glass, Workstations, Office Equipments, Consumables, Goods held in commission / Trust, Trading Goods, Other contents / Items related to Insured's trade, Other Contents related to Insured's Trade",
// },
// "risk_split_up": [
// "Building",
// "Contents",
// "Plant and Machinery",
// "Electronic Equipments",
// "FFF",
// "Office Equipments / Contents",
// "Stocks (RM, WIP, SFG, FG, PM & Any others stocks related to Insured's Trade)"
// ],
// "add_on_covers": [
// "STFI (Storm Tempest Flood & Inundation)",
// "EQ (EarthQuake)",
// "Terrorism",
// "Designated Property Clause",
// "Agreed Bank Clause",
// "Reinstatement Value Clause",
// "Local Authorites Clause",
// "Payment on Account Clause of 75% of Claim amount on recommendation of Surveyor"
// ],
// "inbuilt_covers": [
// "Fire and Allied Perils, Including STFI",
// "Costs for Removal of debris upto 2 % of the claim amount",
// "Additions, alterations or extensions upto 15% of the Sum Insured for that item (excluding stocks)",
// "Temporary removal of stocks upto 10% of value",
// "Start-Up Expenses upto 5 Lakhs",
// "Costs compelled by Municipal Regulations",
// "Cover for Specific Contents- Money upto 50,000",
// "Cover for documents upto 50,000",
// "Cover for computer programmes, information and data upto 5 Lakh",
// "Cover for personal effects of employees, Directors and visitors upto 15,000"
// ],
// "excess": "Policy Fire"
// },
// "burglary": {
// "particulars": "Contents, AC & Accessories, FFF, P & M & Accessories, Electronic Equipments & Accessories, Electrical Items & Fittings, Plumbing Items, Structural Products, Structures, Leasehold Work including Civil Works, Interiors Temporary Structures including Sheds and Containers, Interiors, Plate Glass, Workstations, Office Equipments, Consumables, Goods held in commission / Trust, Trading Goods, Other contents / Items related to Insured's trade, Other Contents related to Insured's Trade",
// "risk_split_up": [
// "Building",
// "Contents",
// "Furniture/Office Equipment",
// "Plant and Machinery",
// "Electrical Fittings",
// "Stock In Process",
// "Raw Material",
// "Stock"
// ],
// "additional_coverage": [
// "First Loss Limit",
// "Theft & RSMD",
// "Waiver of FIR, NTC and Final Investigation reports",
// "Goods held in trust are covered if the same is included in TSI"
// ],
// "excess": "Policy Burglary"
// },
// "flop": {
// "particulars": "Loss of Gross Profit due to peril covered in the Standard Fire & Special Perils Policy.",
// "risk_split_up": ["Sample Flop Risk Split Up"],
// "flop_coverage": [
// "Sum Insured",
// "Indemnity Period",
// "Basis of Sum Insured / Indemnification",
// "Insurance on Gross Profit"
// ],
// "add_on_coverage": [
// "Earthquake",
// "STFI (Storm, Tempest, Flood and Inundation)",
// "Terrorism",
// "Prevention of Access",
// "Auditors Fees",
// "Alternate basis clause",
// "Claims preparation clause",
// "Accumulated stocks clause"
// ],
// "excess": "Policy Flop"
// },
// "all_risk": {
// "particulars": "Laptops, Notebooks Computers, Tablets, Mobile Phones & Cameras, projectors, and other portable equipments.",
// "risk_split_up": ["Sample Risk Split Up"],
// "additional_coverage": [
// "RSMD",
// "Terrorism",
// "Coverage",
// "Electrical and Mechanical Breakdown",
// "Asset warranty clause",
// "Water/liquid damage cover",
// "No restriction on age of the asset / equipments - all the equipments / assets to be covered"
// ],
// "excess": "Policy All Risk"
// },
// "MBD": {
// "particulars": "Plant & Machinery including and not limited to DG Set, Lifts, Refrigerator, Cooler, Air Conditioner, Pump, Water Dispenser, Freezer, Oven, Motor, Transformer, etc. and other P&M related to Insured",
// "risk_split_up": ["Sample Risk Split Up"],
// "additional_coverage": [
// "Accidental / Mechanical breakdown",
// "Express Freight",
// "Air Freight",
// "Third Party Liability (Limited upto 10% of Individual Equipment Value)",
// "Owner's Surrounding Property",
// "Reinstatement Value Clause",
// "Escalation--10% of Individual Equipment Value"
// ],
// "excess": "Policy MBD Excess"
// },
// "EEI": {
// "particulars": "Various electronic Items including and not limited to Computer, Printer, CCTV, Telephone Instruments, Control System, Router, Switches, BMS System, DVD Player, Music Systems, Scanner, Photo Copier and Electronic Lock System, and other electronic items.",
// "risk_split_up": ["Sample Risk Split Up"],
// "additional_coverage": [
// "As per Electronic Equipment Policy including STFI, EQ, Terrorism",
// "Accidental / Electrical Breakdown",
// "Damage to electronic equipment incl. systems software, cost of external data media, cost of reconstruction of data",
// "Waiver of FIR, NTC and Final Investigation reports for claims upto INR 2 Lacs",
// "No restriction on age of the asset / equipments - all the equipments / assets to be covered",
// "Depreciation subject to 10% PA subject to maximum of 50%",
// "Reinstatement Value Clause"
// ],
// "excess": "Policy EEI"
// },
// "fidelity": {
// "particulars": [
// "Dishonest acts / Fraudulent Act of employees",
// "No Of Employees",
// "Per Person Limit",
// "Third Party Fidelity Cover"
// ],
// "risk_split_up": ["Sample Fidelity Risk Split Up"],
// "additional_coverage": [
// "Any One Person",
// "Including contract Employees",
// "Un-named Basis",
// "Employees are covered without any selection",
// "Floater Cover"
// ],
// "excess": "Policy Fidelity"
// },
// "money": {
// "particulars": [
// "Money in transit",
// "Money in Safe",
// "Money in Counter",
// "Annual Transit"
// ],
// "risk_split_up": ["Sample Money Split up"],
// "additional_coverage": [
// "RSMD",
// "Terrorism",
// "Cash by Public Transport/Vehicle",
// "Cash by Two Wheeler",
// "Cash should be carried by the authorised/designated employee of the company. Employee should be read as Pay Rolls of the company including Contract Employee",
// "Employee means any such person as authorised by the Employer either expressly or impliedly in the course of employment",
// "Place of Transit:- Cash in Transit Between Bank to office/ Resorts and offices / resorts to bank and Viceversa"
// ],
// "excess": "Policy Money"
// },
// "plate_Glass": {
// "particulars": "Glass items, including frames and fittings",
// "risk_split_up": ["Sample Plate Glass Risk Split up"],
// "coverage": [
// "Accidental Damage",
// "RSMD",
// "Waiver of Survey upto INR 2 Lacs",
// "Including frames and fittings",
// "Self destruction due to climatic change / due to temp"
// ],
// "excess": "Policy Plate Glass"
// },
// "neon_sign": {
// "particulars": "Neon Tubes, Frame, Electrical Components, Glass Panels, Lettering and Graphics",
// "risk_split_up": ["Sample Neon Sign Risk split up"],
// "coverage": [
// "Accidental/Physical/Electronic Damage",
// "RSMD",
// "Waiver of Survey upto INR 2 Lacs"
// ],
// "excess": "Policy Neon"
// },
// "public_liability": {
// "particulars": [
// "As per Public Liability including extensions such as Lift Liability & Food & Beverage: AOA : AOY - 1:4",
// "AOA:AOY"
// ],
// "risk_split_up": ["Sample Public Liability Risk Split Up"],
// "coverage": [
// "AOG Perils Extension",
// "Third Party Includes visitors",
// "Owners / Tenants occupying the premises",
// "Terrorism",
// "Liability Arising out from use",
// "Ownership or operations of lifts elevators",
// "Food & Beverage Extn"
// ]
// },
// "pedal_cycle": {
// "particulars": "Financial protection against various risks associated with owning and using a bicycle or other pedal-powered vehicle.",
// "risk_split_up": ["Sample Pedal Cycle Risk Split Up"],
// "coverage": [
// "Coverage is Limited to Fire and Allied Perils including Eq as per AIFT",
// "Burglary including Theft and Impact Damage with in the radius of 50Kms from Insured Premises",
// "Reistatement Value Clause",
// "Accidental Damage Cover"
// ],
// "excess": "Policy Pedal Cycle"
// },
// "premium": {
// "premium": [
// "Premium",
// "GST (%)",
// "GST Amount (₹)",
// "Total",
// ]
// }
// };
let policyData = {
"fire": {
"particulars": {
"fire_building": " <strong> Building : </strong> All Buildings and civil structures including Super Structure, plinth & foundation, other Civil Structures, Roads, gates, pavements, internal Pathways, stone works, stone arts etc., Street Lights, Landscaping, Play Area, Culvert, drainage, retaining walls, etc. Compound Wall including utility, admin block, security office and all other similar structures pertaining to insured within the premises, Kutcha Constructions, Temporary Sheds, parking Sheds, Shelters, Civil Structure / bed for DG set / Genset, Interlocking Pathways, LVP Pathway Widening, Roads including internal roads, Mini Conference, Swimming pool, rest areas, shelters, Kitchen-Exhaust System Work, Red Earth for Garden, Arch Wall, Retaining Wall, Boundary, compound walls, Water, Vetrified Tiles, Capital Additions related to Building. All other civil structures related to insured at the covered premises.",
"fire_content": "<strong> Content : </strong> Contents, AC & Accessories, FFF, P & M & Accessories, Electronic Equipments & Accessories, Electrical Items & Fittings, Plumbing Items, Structural Products, Structures, Leasehold Work including Civil Works, Interiors Temporary Structures including Sheds and Containers, Interiors, Plate Glass, Workstations, Office Equipments, Consumables, Goods held in commission / Trust, Trading Goods, Other contents / Items related to Insured's trade, Other Contents related to Insured's Trade",
},
"risk_split_up": [
{ "name": "Building", "default_answer": "Agreed" },
{ "name": "Contents", "default_answer": "Agreed" },
{ "name": "Plant and Machinery", "default_answer": "Agreed" },
{ "name": "Electronic Equipments", "default_answer": "Agreed" },
{ "name": "FFF", "default_answer": "Agreed" },
{ "name": "Office Equipments / Contents", "default_answer": "Agreed" },
{ "name": "Stocks (RM, WIP, SFG, FG, PM & Any others stocks related to Insured's Trade)", "default_answer": "Agreed" }
],
"add_on_covers": [
{ "name": "STFI (Storm Tempest Flood & Inundation)", "default_answer": "Agreed" },
{ "name": "EQ (EarthQuake)", "default_answer": "Agreed" },
{ "name": "Terrorism", "default_answer": "Agreed" },
{ "name": "Designated Property Clause", "default_answer": "Agreed" },
{ "name": "Agreed Bank Clause", "default_answer": "Agreed" },
{ "name": "Reinstatement Value Clause", "default_answer": "Agreed" },
{ "name": "Local Authorites Clause", "default_answer": "Agreed" },
{ "name": "Payment on Account Clause of 75% of Claim amount...", "default_answer": "Agreed" }
],
"inbuilt_covers": [
{ "name": "Fire and Allied Perils, Including STFI", "default_answer": "Agreed" },
{ "name": "Costs for Removal of debris upto 2 % of the claim amount", "default_answer": "Agreed" },
{ "name": "Additions, alterations or extensions upto 15%", "default_answer": "Agreed" },
{ "name": "Temporary removal of stocks upto 10% of value", "default_answer": "Agreed" },
{ "name": "Start-Up Expenses upto 5 Lakhs", "default_answer": "Agreed" },
{ "name": "Costs compelled by Municipal Regulations", "default_answer": "Agreed" },
{ "name": "Cover for Specific Contents- Money upto 50,000", "default_answer": "Agreed" },
{ "name": "Cover for documents upto 50,000", "default_answer": "Agreed" },
{ "name": "Cover for computer programmes upto 5 Lakh", "default_answer": "Agreed" },
{ "name": "Cover for personal effects upto 15,000", "default_answer": "Agreed" }
],
"excess": "Policy Fire"
},
"burglary": {
"particulars": "Contents, AC & Accessories, FFF, P & M & Accessories...",
"risk_split_up": [
{ "name": "Building", "default_answer": "Agreed" },
{ "name": "Contents", "default_answer": "Agreed" },
{ "name": "Furniture/Office Equipment", "default_answer": "Agreed" },
{ "name": "Plant and Machinery", "default_answer": "Agreed" },
{ "name": "Electrical Fittings", "default_answer": "Agreed" },
{ "name": "Stock In Process", "default_answer": "Agreed" },
{ "name": "Raw Material", "default_answer": "Agreed" },
{ "name": "Stock", "default_answer": "Agreed" }
],
"additional_coverage": [
{ "name": "First Loss Limit", "default_answer": "Agreed" },
{ "name": "Theft & RSMD", "default_answer": "Agreed" },
{ "name": "Waiver of FIR, NTC and Final Investigation reports", "default_answer": "Agreed" },
{ "name": "Goods held in trust covered if included in TSI", "default_answer": "Agreed" }
],
"excess": "Policy Burglary"
},
"flop": {
"particulars": "Loss of Gross Profit due to peril covered...",
"risk_split_up": [
{ "name": "Sample Flop Risk Split Up", "default_answer": "Agreed" }
],
"flop_coverage": [
{ "name": "Sum Insured", "default_answer": "Agreed" },
{ "name": "Indemnity Period", "default_answer": "Agreed" },
{ "name": "Basis of Sum Insured / Indemnification", "default_answer": "Agreed" },
{ "name": "Insurance on Gross Profit", "default_answer": "Agreed" }
],
"add_on_coverage": [
{ "name": "Earthquake", "default_answer": "Agreed" },
{ "name": "STFI (Storm, Tempest, Flood and Inundation)", "default_answer": "Agreed" },
{ "name": "Terrorism", "default_answer": "Agreed" },
{ "name": "Prevention of Access", "default_answer": "Agreed" },
{ "name": "Auditors Fees", "default_answer": "Agreed" },
{ "name": "Alternate basis clause", "default_answer": "Agreed" },
{ "name": "Claims preparation clause", "default_answer": "Agreed" },
{ "name": "Accumulated stocks clause", "default_answer": "Agreed" }
],
"excess": "Policy Flop"
},
"all_risk": {
"particulars": "Laptops, Notebooks Computers, Tablets, Mobile Phones & Cameras...",
"risk_split_up": [
{ "name": "Sample Risk Split Up", "default_answer": "Agreed" }
],
"additional_coverage": [
{ "name": "RSMD", "default_answer": "Agreed" },
{ "name": "Terrorism", "default_answer": "Agreed" },
{ "name": "Coverage", "default_answer": "Agreed" },
{ "name": "Electrical and Mechanical Breakdown", "default_answer": "Agreed" },
{ "name": "Asset warranty clause", "default_answer": "Agreed" },
{ "name": "Water/liquid damage cover", "default_answer": "Agreed" },
{ "name": "No restriction on age of the asset", "default_answer": "Agreed" }
],
"excess": "Policy All Risk"
},
"MBD": {
"particulars": "Plant & Machinery including and not limited to DG Set, Lifts...",
"risk_split_up": [
{ "name": "Sample Risk Split Up", "default_answer": "Agreed" }
],
"additional_coverage": [
{ "name": "Accidental / Mechanical breakdown", "default_answer": "Agreed" },
{ "name": "Express Freight", "default_answer": "Agreed" },
{ "name": "Air Freight", "default_answer": "Agreed" },
{ "name": "Third Party Liability (Limited upto 10%)", "default_answer": "Agreed" },
{ "name": "Owner's Surrounding Property", "default_answer": "Agreed" },
{ "name": "Reinstatement Value Clause", "default_answer": "Agreed" },
{ "name": "Escalation--10% of Individual Equipment Value", "default_answer": "Agreed" }
],
"excess": "Policy MBD Excess"
},
"EEI": {
"particulars": "Various electronic Items including and not limited to Computer, Printer, CCTV...",
"risk_split_up": [
{ "name": "Sample Risk Split Up", "default_answer": "Agreed" }
],
"additional_coverage": [
{ "name": "As per Electronic Equipment Policy including STFI, EQ, Terrorism", "default_answer": "Agreed" },
{ "name": "Accidental / Electrical Breakdown", "default_answer": "Agreed" },
{ "name": "Damage to electronic equipment incl. software", "default_answer": "Agreed" },
{ "name": "Waiver of FIR/NTC for claims upto INR 2 Lacs", "default_answer": "Agreed" },
{ "name": "No restriction on age of the asset", "default_answer": "Agreed" },
{ "name": "Depreciation subject to 10% PA max 50%", "default_answer": "Agreed" },
{ "name": "Reinstatement Value Clause", "default_answer": "Agreed" }
],
"excess": "Policy EEI"
},
"fidelity": {
"particulars": [
{ "name": "Dishonest acts / Fraudulent Act of employees", "default_answer": "Agreed" },
{ "name": "No Of Employees", "default_answer": "Agreed" },
{ "name": "Per Person Limit", "default_answer": "Agreed" },
{ "name": "Third Party Fidelity Cover", "default_answer": "Agreed" }
],
"risk_split_up": [
{ "name": "Sample Fidelity Risk Split Up", "default_answer": "Agreed" }
],
"additional_coverage": [
{ "name": "Any One Person", "default_answer": "Agreed" },
{ "name": "Including contract Employees", "default_answer": "Agreed" },
{ "name": "Un-named Basis", "default_answer": "Agreed" },
{ "name": "Employees covered without selection", "default_answer": "Agreed" },
{ "name": "Floater Cover", "default_answer": "Agreed" }
],
"excess": "Policy Fidelity"
},
"money": {
"particulars": [
{ "name": "Money in transit", "default_answer": "Agreed" },
{ "name": "Money in Safe", "default_answer": "Agreed" },
{ "name": "Money in Counter", "default_answer": "Agreed" },
{ "name": "Annual Transit", "default_answer": "Agreed" }
],
"risk_split_up": [
{ "name": "Sample Money Split up", "default_answer": "Agreed" }
],
"additional_coverage": [
{ "name": "RSMD", "default_answer": "Agreed" },
{ "name": "Terrorism", "default_answer": "Agreed" },
{ "name": "Cash by Public Transport/Vehicle", "default_answer": "Agreed" },
{ "name": "Cash by Two Wheeler", "default_answer": "Agreed" },
{ "name": "Cash carried by authorised employee", "default_answer": "Agreed" },
{ "name": "Employee means payroll & contract", "default_answer": "Agreed" },
{ "name": "Place of Transit: Bank ↔ Office/Resorts", "default_answer": "Agreed" }
],
"excess": "Policy Money"
},
"plate_Glass": {
"particulars": "Glass items, including frames and fittings",
"risk_split_up": [
{ "name": "Sample Plate Glass Risk Split up", "default_answer": "Agreed" }
],
"coverage": [
{ "name": "Accidental Damage", "default_answer": "Agreed" },
{ "name": "RSMD", "default_answer": "Agreed" },
{ "name": "Waiver of Survey upto INR 2 Lacs", "default_answer": "Agreed" },
{ "name": "Including frames and fittings", "default_answer": "Agreed" },
{ "name": "Self destruction due to climatic change", "default_answer": "Agreed" }
],
"excess": "Policy Plate Glass"
},
"neon_sign": {
"particulars": "Neon Tubes, Frame, Electrical Components, Glass Panels...",
"risk_split_up": [
{ "name": "Sample Neon Sign Risk split up", "default_answer": "Agreed" }
],
"coverage": [
{ "name": "Accidental/Physical/Electronic Damage", "default_answer": "Agreed" },
{ "name": "RSMD", "default_answer": "Agreed" },
{ "name": "Waiver of Survey upto INR 2 Lacs", "default_answer": "Agreed" }
],
"excess": "Policy Neon"
},
"public_liability": {
"particulars": [
{ "name": "As per Public Liability including extensions", "default_answer": "Agreed" },
{ "name": "AOA:AOY", "default_answer": "Agreed" }
],
"risk_split_up": [
{ "name": "Sample Public Liability Risk Split Up", "default_answer": "Agreed" }
],
"coverage": [
{ "name": "AOG Perils Extension", "default_answer": "Agreed" },
{ "name": "Third Party Includes visitors", "default_answer": "Agreed" },
{ "name": "Owners / Tenants occupying the premises", "default_answer": "Agreed" },
{ "name": "Terrorism", "default_answer": "Agreed" },
{ "name": "Liability Arising out from use", "default_answer": "Agreed" },
{ "name": "Ownership or operations of lifts elevators", "default_answer": "Agreed" },
{ "name": "Food & Beverage Extn", "default_answer": "Agreed" }
]
},
"pedal_cycle": {
"particulars": "Financial protection against risks of bicycles...",
"risk_split_up": [
{ "name": "Sample Pedal Cycle Risk Split Up", "default_answer": "Agreed" }
],
"coverage": [
{ "name": "Coverage is Limited to Fire and Allied Perils", "default_answer": "Agreed" },
{ "name": "Burglary incl. Theft and Impact Damage", "default_answer": "Agreed" },
{ "name": "Reinstatement Value Clause", "default_answer": "Agreed" },
{ "name": "Accidental Damage Cover", "default_answer": "Agreed" }
],
"excess": "Policy Pedal Cycle"
},
"premium": {
"premium": [
"Premium",
"GST (%)",
"GST Amount (₹)",
"Total",
]
}
};
let policyOrder = {
fire: 1,
burglary: 2,
flop: 3,
all_risk: 4,
mbd: 5,
eei: 6,
fidelity: 7,
money: 8,
plate_glass: 9,
neon_sign: 10,
public_liability: 11,
pedal_cycle: 12,
premium: 13
};
// GLOBAL VARABLES DECLARATION
let riskCount = 0; // for Risk Location Count
let insurerCount = 0; // for Insurer Count
let insurerVersionCount = 1; // for Insurer Version controll
let editingInsurerId = null; // for Edit Insurer
let editingLocationIndex = null; // for Edit Risk Location
let risk_location_details = {}; // for Store Risk Location Details
let insurer_details = {}; // for Store Insurer Details
let active_policy_data = {}; // for Store the Active Policy Data (eg : Fire, Burglary) based on the checkbox
$(document).ready(function() {
// Add Risk Location Column (show modal first)
$("#addLocation").click(function() {
editingLocationIndex = null; // Clear editing mode
$("#riskLocationForm")[0].reset();
$("#riskLocationModalLabel").text("Add Risk Location Details");
$("#saveRiskLocation").text("Add Location");
// $("#riskLocationModal").modal('show');
var myModal = new bootstrap.Modal(document.getElementById('riskLocationModal'));
myModal.show();
});
// Save Risk Location Details and Add Column
$("#saveRiskLocation").click(function() {
let locationName = $("#locationName").val().trim();
let locationAddress = $("#locationAddress").val().trim();
let construction_type = $("#construction_type").val();
let has_basement = $("#has_basement").is(":checked");
let basement_utilisation = $("#basement_utilisation").val().trim();
let occupancy_risk = $("#occupancy_risk").val();
let iib_code = $("#iib_code").val().trim();
let business_description = $("#business_description").val().trim();
if (!locationName || !locationAddress) {
alert("Please fill in required fields (Location Name and Address)");
return;
}
if (editingLocationIndex === null) {
// Adding new location count
riskCount++;
// Store details for this risk location
risk_location_details[riskCount] = {
name: locationName,
address: locationAddress,
construction_type: construction_type,
has_basement: has_basement,
basement_utilisation: basement_utilisation,
occupancy_risk: occupancy_risk,
iib_code: iib_code,
business_description: business_description
};
// Create header
let header = `
<th class="risk-col" data-risk-id="${riskCount}">
<div class="loc-details" style="text-align: center;">
<div style="font-weight: bold;">Risk Location ${riskCount}</div>
<div style="font-size: 11px; color: #666; margin-top: 2px;">
<span class="loc_name" style="display: block;">${locationName}</span>
<span class="loc_address" style="display: block;">${locationAddress}</span>
</div>
</div>
<div class="dropdown d-inline-block float-end loc-menu">
<button class="btn btn-sm btn-light dropdown-toggle" type="button" data-toggle="dropdown" aria-expanded="false">
&#x22EE;
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item edit-location" href="#" data-risk-id="${riskCount}">Edit</a></li>
<li><a class="dropdown-item text-danger remove-location" href="#" data-risk-id="${riskCount}">Remove</a></li>
<li><a class="dropdown-item add-index-value" href="#" data-risk-id="${riskCount}">Add Index Value</a></li>
<li><a class="dropdown-item toggle-collapse" href="#" data-risk-id="${riskCount}" data-type="risk">Collapse</a></li>
<li class="dropdown-item">
<input type="checkbox" class="form-check-input me-2 qcr-check-col" data-risk-id="${riskCount}" checked>
<label class="form-check-label">
QCR
</label>
</li>
<li class="dropdown-item">
<input type="checkbox" class="form-check-input me-2 client-check-col" data-risk-id="${riskCount}" checked>
<label class="form-check-label">
Client
</label>
</li>
</ul>
</div>
</th>
`;
// Find index of "Total Sum Insured" column
let sumInsuredIndex = $("#policyHeader th").filter(function() {
return $(this).text().trim().toLowerCase().includes("total sum insured");
}).index();
// Insert header before Total Sum Insured
$("#policyHeader th").eq(sumInsuredIndex).before(header);
// --- after you inserted the new header TH into #policyHeader ---
var $headerRow = $("#policyHeader");
if (!$headerRow.is("tr")) $headerRow = $headerRow.find("tr").first();
var $newTh = $headerRow.find(`th.risk-col[data-risk-id="${riskCount}"]`).first();
if (!$newTh.length) {
console.error("New header not found");
} else {
// compute visual column index (sum of colspans of preceding THs)
var targetVisualIndex = 0;
$headerRow.children("th").each(function() {
if (this === $newTh[0])
return false; // stop when we reach the inserted header
targetVisualIndex += parseInt($(this).attr("colspan")) || 1;
});
// occupied[col] = how many more rows this visual column is still covered by an earlier rowspan
var occupied = [];
$("#policyTable tbody tr").each(function() {
var $tr = $(this);
var $tds = $tr.children("td");
var visual = 0;
var inserted = false;
for (var i = 0; i < $tds.length; i++) {
// skip visual columns already occupied by rowspans
while (occupied[visual] > 0) visual++;
var $td = $tds.eq(i);
// get the full inline style string (if any)
var inlineStyle = $td.attr("style");
// get just the background-color (works whether inline or CSS-applied)
var bgColor = $td.css("background-color");
var colspan = parseInt($td.attr("colspan")) || 1;
var rowspan = parseInt($td.attr("rowspan")) || 1;
// if target is exactly at this visual start -> insert before this cell
if (targetVisualIndex === visual) {
$td.before(
`<td style="background-color:${bgColor}" class="risk-col" data-risk-id="${riskCount}"></td>`);
inserted = true;
break;
}
// if target falls inside this spanning cell -> insert after this cell
if (targetVisualIndex > visual && targetVisualIndex < visual +
colspan) {
$td.after(`<td style="background-color:${bgColor}" class="risk-col" data-risk-id="${riskCount}"></td>`);
inserted = true;
break;
}
// mark columns as occupied by this cell's rowspan
for (var k = 0; k < colspan; k++) {
occupied[visual + k] = rowspan;
}
visual += colspan;
}
if (!inserted) {
// if not inserted yet, append at the end of the row
$tr.append(`<td class="risk-col" data-risk-id="${riskCount}"></td>`);
}
// decrease occupied counters for next row
for (var c = 0; c < occupied.length; c++) {
if (occupied[c] > 0) occupied[c]--;
}
});
// restore editability (if desired)
$("#policyTable tbody tr").find("td:not(:first-child)").attr("contenteditable", "true");
}
} else {
// Editing existing location
let riskId = editingLocationIndex;
risk_location_details[riskId] = {
name: locationName,
address: locationAddress,
construction_type: construction_type,
has_basement: has_basement,
basement_utilisation: basement_utilisation,
occupancy_risk: occupancy_risk,
iib_code: iib_code,
business_description: business_description
};
// Update header
$(`.risk-col[data-risk-id="${riskId}"]`).first().html(`
<div class="loc-details" style="text-align: center;">
<div style="font-weight: bold;">Risk Location ${riskId}</div>
<div style="font-size: 11px; color: #666; margin-top: 2px;">
<span class="loc_name" style="display: block;">${locationName}</span>
<span class="loc_address" style="display: block;">${locationAddress}</span>
</div>
</div>
<div class="dropdown d-inline-block float-end loc-menu">
<button class="btn btn-sm btn-light dropdown-toggle" type="button" data-toggle="dropdown" aria-expanded="false">
&#x22EE;
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item edit-location" href="#" data-risk-id="${riskId}">Edit</a></li>
<li><a class="dropdown-item text-danger remove-location" href="#" data-risk-id="${riskId}">Remove</a></li>
<li><a class="dropdown-item toggle-collapse" href="#" data-risk-id="${riskId}" data-type="risk">Collapse</a></li>
<li class="dropdown-item">
<label class="form-check-label">
<input type="checkbox" class="form-check-input me-2 qcr-check-col" data-risk-id="${riskCount}" checked >
QCR
</label>
</li>
<li class="dropdown-item">
<label class="form-check-label">
<input type="checkbox" class="form-check-input me-2 client-check-col" data-risk-id="${riskCount}" checked>
Client
</label>
</li>
</ul>
</div>
`);
}
// Clear form and close modal
$("#riskLocationForm")[0].reset();
editingLocationIndex = null;
$('.close').click();
console.log("Risk Location JSON:", JSON.stringify(risk_location_details, null, 2));
});
// Add Insurer button click - show modal
$("#addInsurer").click(function() {
// $("#insurerModal").modal('show');
$('#insurerSelect2').val('').select2();
var myModal = new bootstrap.Modal(document.getElementById('insurerModal'));
myModal.show();
});
// Insurer selection from modal
$(document).off("click", "#insurer_save").on("click", "#insurer_save", function(e) {
const $selected = $("#insurerSelect2 option:selected");
const insurerId = $selected.data("id"); // from data-id
let insurerName = $selected.val(); // from value="..."
const insurerText = $selected.text(); // visible text
console.log("ID:", insurerId);
console.log("Value:", insurerName);
console.log("Text:", insurerText);
if(!insurerName){
alert('Please select the insurer');
return;
}
// if(insurer_details[insurerCount] && insurer_details[insurerCount].id == insurerId && insurer_details[insurerCount].name == insurerName){
// if (confirm('Duplicate insurer found. Do you continue with version control?')) {
// insurerName = insurerName + '-V' + insurerVersionCount;
// insurerVersionCount++;
// }
// }
if (insurer_details[insurerCount]
&& insurer_details[insurerCount].id === insurerId
&& insurer_details[insurerCount].name === insurerName) {
if (confirm("Duplicate insurer found. Do you want to continue with version control?")) {
// Initialize version counter if not already set
insurer_details[insurerCount].version = (insurer_details[insurerCount].version || 1) + 1;
// Append version suffix
insurerName = insurerName + "-V" + insurer_details[insurerCount].version;
}
}
// Case 1: Editing existing insurer
if (editingInsurerId !== null) {
insurer_details[editingInsurerId] = {
id: insurerId,
name: insurerName,
};
// Update header text
const $header = $(`.insurer-col[data-insurer-id="${editingInsurerId}"]`);
$header.contents().first()[0].textContent = insurerName + " ";
// Update all rows for this insurer column
$("#policyTable tbody tr").each(function() {
const $cell = $(this).find(
`td.insurer-col[data-insurer-id="${editingInsurerId}"]`);
$cell.html("--"); // or keep old value if you prefer
});
editingInsurerId = null;
$('.close').click();
console.log("Insurer Details JSON:", JSON.stringify(insurer_details, null, 2));
return;
}
// Case 2: Adding new insurer (existing logic)
insurerCount++;
// Add the insurer details to the JSON
insurer_details[insurerCount] = {
id: insurerId,
name: insurerName,
};
const header = `
<th class="insurer-col" data-insurer-id="${insurerCount}" data-insurer-pk="${insurerId}">
${insurerName}
<div class="dropdown d-inline-block float-end">
<button class="btn btn-sm btn-light dropdown-toggle" type="button" data-toggle="dropdown" aria-expanded="false">
&#x22EE;
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item edit-insurer" href="#" data-insurer-id="${insurerCount}">Edit</a></li>
<li><a class="dropdown-item text-danger remove-insurer" href="#" data-insurer-id="${insurerCount}" data-type="insurer">Remove</a></li>
<li><a class="dropdown-item toggle-collapse" href="#" data-insurer-id="${insurerCount}" data-type="insurer">Collapse</a></li>
</ul>
</div>
</th>
`;
let $actionHeader = $("#policyHeader th").filter(function() {
return $(this).text().trim().toLowerCase().includes("action");
}).first();
if (!$actionHeader.length) $actionHeader = $("#policyHeader th").last();
$actionHeader.before(header);
$("#policyTable tbody tr").each(function() {
const $row = $(this);
const $actionCell = $row.children("td").last();
var bgColor = $actionCell.css("background-color");
const prevData = $row.children("td.insurer-col").last().html(); // for getting from the last insurer value
const prevData2 = $row.children("td.sum_insured").html(); // for getting from the total sum insured
const cellHtml =
`<td style="background-color:${bgColor}" class="insurer-col" data-insurer-id="${insurerCount}">${prevData2 != null ? prevData2 : "--"}</td>`;
$actionCell.before(cellHtml);
$row.find("td:not(:first-child)").attr("contenteditable", "true");
});
$('#insurerSelect2').val('').select2();
$('.close').click();
console.log("Insurer Details JSON:", JSON.stringify(insurer_details, null, 2));
});
// Remove Risk Location Column
$(document).on("click", ".remove-location", function(e) {
e.preventDefault();
let riskId = $(this).data('risk-id');
if (confirm('Are you sure you want to remove this risk location?')) {
$(`.risk-col[data-risk-id="${riskId}"]`).remove();
$(`.index-val-col[data-index-value-id="${riskId}"]`).remove();
delete risk_location_details[riskId];
// Update risk count if needed
let remainingRisks = Object.keys(risk_location_details).length;
if (remainingRisks === 0) {
riskCount = 0;
}
}
});
// Remove Insurer Column
$(document).on("click", ".remove-insurer", function(e) {
e.preventDefault();
let insurerId = $(this).data('insurer-id');
if (confirm('Are you sure you want to remove this insurer?')) {
$(`.insurer-col[data-insurer-id="${insurerId}"]`).remove();
// insurerCount--;
delete insurer_details[insurerId];
// Update insurer count if needed
let remainingInsurer = Object.keys(insurer_details).length;
if (remainingInsurer === 0) {
insurerCount = 0;
}
console.log("Insurer Details JSON:", JSON.stringify(insurer_details, null, 2));
}
});
// Edit Risk Location
$(document).on("click", ".edit-location", function(e) {
e.preventDefault();
let riskId = $(this).data('risk-id');
editingLocationIndex = riskId;
let details = risk_location_details[riskId];
if (details) {
$("#locationName").val(details.name);
$("#locationAddress").val(details.address);
$("#construction_type").val(details.construction_type);
$("#has_basement").prop("checked", details.has_basement);
if (details.has_basement) {
$("#basementUtilisationDiv").removeClass("d-none");
$("#basement_utilisation").prop("required", true).val(details.basement_utilisation);
} else {
$("#basementUtilisationDiv").addClass("d-none");
$("#basement_utilisation").prop("required", false).val("");
}
$("#occupancy_risk").val(details.occupancy_risk).trigger("change");
$("#iib_code").val(details.iib_code);
$("#business_description").val(details.business_description);
$("#riskLocationModalLabel").text("Edit Risk Location Details");
$("#saveRiskLocation").text("Update Location");
$("#riskLocationModal").modal("show");
}
});
// Handle Edit Insurer
$(document).on("click", ".edit-insurer", function(e) {
e.preventDefault();
editingInsurerId = $(this).data("insurer-id");
console.log('editingInsurerId : ', editingInsurerId);
$('#insurerSelect2').val('').select2();
var myModal = new bootstrap.Modal(document.getElementById('insurerModal'));
myModal.show();
});
// Handle Add index value column
$(document).on("click", ".add-index-value", function(e) {
e.preventDefault();
let risk_id = $(this).data("risk-id");
console.log('risk_id :', risk_id);
// Get clicked th and its index
let $th = $(this).closest("th");
let colIndex = $th.index(); // column index in the table
console.log('colIndex :', colIndex)
// Clone the header cell
let $newTh = $th.clone(true);
// Example: change class (remove old, add new)
$newTh.removeClass("risk-col").addClass("index-val-col");
// Remove the data-risk-id attribute
$newTh.removeAttr("data-risk-id");
// update data attributes
$newTh.attr("data-index-value-id", risk_id);
let locationName = $th.find(".loc-details .loc_name").html() ?? "";
let locationAddress = $th.find(".loc-details .loc_address").html() ?? "";
console.log("locationName", locationName);
console.log("locationAddress", locationAddress);
let header = `
<div class="loc-details" style="text-align: center;">
<div class="risk_location_" style="font-weight: bold;">Risk Location ${risk_id}</div>
<div style="font-weight: bold;">( Indexed Value )</div>
<div style="font-size: 11px; color: #666; margin-top: 2px;">
<span class="loc_name" style="display: block;">${locationName}</span>
<span class="loc_address" style="display: block;">${locationAddress}</span>
</div>
</div>
<div class="dropdown d-inline-block float-end loc-menu">
<button class="btn btn-sm btn-light dropdown-toggle" type="button" data-toggle="dropdown" aria-expanded="false">
&#x22EE;
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item text-danger remove-index-value" href="#" data-index-value-id="${risk_id}">Remove</a></li>
<li><a class="dropdown-item toggle-collapse" href="#" data-index-value-id="${risk_id}" data-type="index-value">Collapse</a></li>
<li class="dropdown-item">
<input type="checkbox" class="form-check-input me-2 qcr-check-col" data-index-value-id="${risk_id}" checked>
<label class="form-check-label">
QCR
</label>
</li>
<li class="dropdown-item">
<input type="checkbox" class="form-check-input me-2 client-check-col" data-index-value-id="${risk_id}" checked>
<label class="form-check-label">
Client
</label>
</li>
</ul>
</div>
`;
$newTh.html(header);
$th.after($newTh); // insert after clicked column in header
// Clone each row's td in this column
let skipRows = 0;
let baseColIndex = $th.index();
$("#policyTable tbody tr").each(function() {
let colIndex;
if (skipRows > 0) {
skipRows--;
colIndex = baseColIndex - 1; // adjust since rowspan already occupied this column
} else {
colIndex = baseColIndex;
}
let $firstTd = $(this).children("td").eq(0);
console.log('$firstTd', $firstTd);
let rowspan = parseInt($firstTd.attr("rowspan") || 1, 10);
console.log('rowspan :', rowspan);
if (rowspan > 1) {
skipRows = rowspan - 1; // mark how many rows to skip
}
let $td = $(this).children("td").eq(colIndex);
console.log('$td', $td);
if ($td.length) {
let $newTd = $td.clone(false); // false = no event handlers
$newTd.removeClass("risk-col").addClass("index-val-col");
$newTd.removeAttr("data-risk-id");
$newTd.attr("data-index-value-id", risk_id);
$td.after($newTd);
}
});
// Hide specific one
$(`.add-index-value[data-risk-id="${risk_id}"]`).hide();
});
// Remove Index value Column
$(document).on("click", ".remove-index-value", function(e) {
e.preventDefault();
let riskId = $(this).data('index-value-id');
console.log('riskId :', riskId)
if (confirm('Are you sure you want to remove this Index value?')) {
$(`.index-val-col[data-index-value-id="${riskId}"]`).remove();
// Hide specific one
$(`.add-index-value[data-risk-id="${riskId}"]`).show();
}
});
// Main policy checkbox toggle
$(".policy").on("change", function() {
let policy = $(this).val();
let policy_color = getRandomLightColor();
if ($(this).is(":checked")) {
// Add sub-options for Fire policy
if (policy === "fire") {
$(this).parent().after(`
<div class="sub-options" id="fireOptions">
<label><input type="checkbox" class="fireSub" value="fire_building" checked> Fire Building</label>
<label><input type="checkbox" class="fireSub" value="fire_content" checked> Fire Content</label>
</div>
`);
}
if (policyData[policy]) {
let data = policyData[policy];
let rows = [];
Object.keys(data).forEach(key => {
let value = data[key];
console.log('value', value);
console.log('value type', typeof value);
if (key === "particulars") {
let rowId = `row-${policy}`;
// Build row with proper number of columns
let row =
`<tr class="policy-details" id="${rowId}" data-policy="${policy}" data-key="${key}" data-index="0">
<td>${formatHeader(policy)} <i class="mdi mdi-arrow-collapse-up policy-collapse" data-policy="${policy}" aria-hidden="true"></i></td>
<td class="particulars" contenteditable="true" id="${rowId}-particulars">${formatParticulars(value)}</td>`;
// Add empty cells for risk locations
for (let i = 0; i < riskCount; i++) {
row += `<td contenteditable="true" class="risk-col"></td>`;
}
// Add sum insured cell
row +=
`<td class="sum_insured" contenteditable="true">${data.sum_insured || "50000000"}</td>`;
// Add index sum insured cell
row +=
`<td class="index_sum_insured" contenteditable="true">${data.index_sum_insured || "50000000"}</td>`;
// Add empty cells for insurers
for (let i = 0; i < insurerCount; i++) {
row += `<td contenteditable="true" class="insurer-col"></td>`;
}
// Add QCR column
let qcrId = `qcrCheckbox_${rowId}`;
row += `<td contenteditable="false" class="action"></td>`;
row += `</tr>`;
rows.push(row);
// $("#policyTable tbody").append(row);
// $(`#${rowId}`).fadeIn();
} else if (Array.isArray(value) && key == "premium") {
value.forEach((item, index) => {
let rowId = `row-${policy}-${key}-${index}`;
let firstCell = index === 0 ?
`<td rowspan="${value.length}">${formatHeader(key)}</td>` :
"";
let row = `<tr class="policy-details" id="${rowId}" data-policy="${policy}" data-key="${key}" data-index="${index}" data-array-length="${value.length}">
${firstCell}
<td class="particulars" contenteditable="true">${item}</td>`;
// Add empty cells for risk locations
for (let i = 0; i < riskCount; i++) {
row +=
`<td contenteditable="true" class="risk-col"></td>`;
}
// Add sum insured cell
row +=
`<td class="sum_insured" contenteditable="true"></td>`;
// Add index sum insured cell
row +=
`<td class="index_sum_insured" contenteditable="true"></td>`;
// Add empty cells for insurers
for (let i = 0; i < insurerCount; i++) {
row +=
`<td contenteditable="true" class="insurer-col"></td>`;
}
// Add QCR column
let qcrId = `qcrCheckbox_${rowId}`;
row += `<td contenteditable="false" class="action" ></td>`;
row += `</tr>`;
rows.push(row);
// $("#policyTable tbody").append(row);
// $(`#${rowId}`).fadeIn();
});
} else if (Array.isArray(value) && key !== "particulars") {
value.forEach((item, index) => {
let rowId = `row-${policy}-${key}-${index}`;
let firstCell = index === 0 ?
`<td rowspan="${value.length}">${formatHeader(key)}</td>` :
"";
let row = `<tr class="policy-details ${policy}_${key}" id="${rowId}" data-policy="${policy}" data-key="${key}" data-index="${index}" data-array-length="${value.length}">
${firstCell}
<td class="particulars" contenteditable="true">${item.name}</td>`;
// Add empty cells for risk locations
for (let i = 0; i < riskCount; i++) {
row +=
`<td contenteditable="true" class="risk-col"></td>`;
}
// Add sum insured cell
row +=
`<td class="sum_insured" contenteditable="true">${item.default_answer || "50000000"}</td>`;
// Add index sum insured cell
row +=
`<td class="index_sum_insured" contenteditable="true">${item.default_answer || "50000000"}</td>`;
// Add empty cells for insurers
for (let i = 0; i < insurerCount; i++) {
row +=
`<td contenteditable="true" class="insurer-col"></td>`;
}
// Add QCR column
let qcrId = `qcrCheckbox_${rowId}`;
row += `<td contenteditable="false" class="action">
<input class="qcr-checkbox" id="${qcrId}" name="qcr" type="checkbox" checked>
<label for="${qcrId}">QCR</label>
<input class="client-checkbox" name="stc" type="checkbox" checked>
<label> Client </label>
<button class="btn btn-primary btn-sm btn-add-row">+</button>
<button class="btn btn-danger btn-sm btn-remove-row">-</button>
</td>`;
row += `</tr>`;
rows.push(row);
// $("#policyTable tbody").append(row);
// $(`#${rowId}`).fadeIn();
});
} else if (key === "excess") {
// Normal string/value row
let rowId = `row-${policy}-${key}`;
let row = `<tr class="policy-details ${policy}_${key}" id="${rowId}" data-policy="${policy}" data-key="${key}" data-index="0">
<td rowspan="1" contenteditable="true">${formatHeader(key)}</td>
<td class="particulars" contenteditable="true">${formatParticulars(value)}</td>`;
// Add empty cells for risk locations
for (let i = 0; i < riskCount; i++) {
row += `<td contenteditable="true" class="risk-col"></td>`;
}
// Add sum insured cell
row +=
`<td class="sum_insured" contenteditable="true">${data.sum_insured || "50000000"}</td>`;
// Add index sum insured cell
row +=
`<td class="index_sum_insured" contenteditable="true">${data.sum_insured || "50000000"}</td>`;
// Add empty cells for insurers
for (let i = 0; i < insurerCount; i++) {
row += `<td contenteditable="true" class="insurer-col"></td>`;
}
// Add QCR column
let qcrId = `qcrCheckbox_${rowId}`;
row += `<td contenteditable="false" class="action">
<input class="qcr-checkbox" id="${qcrId}" name="qcr" type="checkbox" checked>
<label for="${qcrId}">QCR</label>
<input class="client-checkbox" name="stc" type="checkbox" checked> <label> Client </label>
<button class="btn btn-primary btn-sm btn-add-row">+</button>
<button class="btn btn-danger btn-sm btn-remove-row">-</button>
</td>`;
row += `</tr>`;
rows.push(row);
// $("#policyTable tbody").append(row);
// $(`#${rowId}`).fadeIn();
}
});
// Insert all rows at once in correct order
if (rows.length > 0) {
let $rows = $(rows.join(""));
insertPolicyRows($rows, policy);
$rows.hide().fadeIn();
// 🎨 Add random light color for each <td>
$rows.find("td").each(function () {
$(this).css("background-color", policy_color);
});
}
}
} else {
// Remove policy rows and sub-options
$(`.policy-details[id^="row-${policy}"]`).fadeOut(function() {
$(this).remove();
});
if (policy === "fire") {
$("#fireOptions").remove();
}
}
});
// Handle the policy order
function insertPolicyRows(row, policy) {
let newPriority = policyOrder[policy] || 999; // default lowest if not found
let inserted = false;
$("#policyTable tbody tr.policy-details").each(function() {
let existingPolicy = $(this).data("policy");
let existingPriority = policyOrder[existingPolicy] || 999;
if (newPriority < existingPriority) {
$(this).before(row);
inserted = true;
return false; // break loop
}
});
if (!inserted) {
$("#policyTable tbody").append(row); // add at end if no higher priority found
}
}
// Utility: find the group's first row (the row which holds the rowspan cell)
function findGroupFirstRow($tr) {
return $tr.prevAll().addBack().filter(function() {
return $(this).find('td[rowspan], th[rowspan]').length > 0;
}).last();
}
// Utility: count rows that belong to the same group (from the first row until the next group header or end)
function countGroupRows($firstRow) {
if (!$firstRow || $firstRow.length === 0) return 0;
let count = 0;
let $cur = $firstRow;
while ($cur && $cur.length) {
// stop if we reach another header row (except the firstRow itself)
if ($cur[0] !== $firstRow[0] && $cur.find('td[rowspan], th[rowspan]').length > 0) break;
count++;
$cur = $cur.next();
console.log("$cur", $cur)
}
let excess_id = $firstRow[0].id;
console.log("Original ID:", excess_id);
let parts = excess_id.split('-');
console.log("Split parts:", parts);
let lastPart = parts[parts.length - 1];
console.log("Last part:", lastPart);
if (lastPart == "excess" && count > 2) {
count = count - 1;
}
return count;
}
// Update rowspan attribute for the group's header cell
function updateRowspanForGroup($anyRowInGroup) {
let $firstRow = findGroupFirstRow($anyRowInGroup);
console.log('$firstRow :', $firstRow);
if (!$firstRow || $firstRow.length === 0) return;
let $cell = $firstRow.find('td[rowspan], th[rowspan]').first();
console.log("$cell ", $cell)
if ($cell.length === 0) return;
let cnt = countGroupRows($firstRow);
console.log("rowspan cnt", cnt);
$cell.attr('rowspan', cnt);
}
// Add row handler (+) using the Action Column
$(document).on("click", ".btn-add-row", function(e) {
e.preventDefault();
let $tr = $(this).closest("tr");
// clone WITHOUT copying event handlers
let $clone = $tr.clone(false);
// remove any header cell (the rowspan cell) in the cloned row so header isn't duplicated
$clone.find('td[rowspan], th[rowspan]').remove();
// make ids unique in clone (and update labels pointing to them)
$clone.find('[id]').each(function() {
let $el = $(this);
let oldId = $el.attr('id');
if (!oldId) return;
let newId = oldId + '_' + Date.now() + '_' + Math.floor(Math.random() * 1000);
$el.attr('id', newId);
// update label[s] inside the clone that referenced oldId
$clone.find('label[for="' + oldId + '"]').attr('for', newId);
});
// clear editable cells and form fields inside clone
$clone.find('[contenteditable]').text();
$clone.find('input[type="text"], input[type="number"], textarea').val();
$clone.find('select').prop('selectedIndex', 0);
$clone.find('input[type="checkbox"], input[type="radio"]').prop('checked', true);
// insert the clone after current row
$tr.after($clone);
// update rowspan for this group
updateRowspanForGroup($tr);
});
// Remove row handler (-) using the Action Column
$(document).on("click", ".btn-remove-row", function(e) {
e.preventDefault();
let $tr = $(this).closest("tr");
let $firstRow = findGroupFirstRow($tr);
let groupCount = countGroupRows($firstRow);
if (confirm('Are you sure you want to remove this ROW?')) {
// If this row contains the header (rowspan cell)
let $headerCell = $tr.find('td[rowspan], th[rowspan]').first();
if ($headerCell.length > 0) {
// If it's the only row in the group, prevent removal
if (groupCount <= 1) {
alert("Cannot remove the last row of this group.");
return;
}
// Move header cell to the next row, then remove the current row
let $next = $tr.next();
if ($next.length === 0) {
alert("Cannot remove header row (no next row to move header to).");
return;
}
let $detachedHeader = $headerCell.detach(); // remove but keep element
// prepend header cell to the next row (so it becomes the group's new first row)
$next.prepend($detachedHeader);
// now remove the original row
$tr.remove();
// update rowspan based on the new first row
updateRowspanForGroup($next);
} else {
// normal row removal (not a header)
if (groupCount <= 1) {
// safety: if groupCount is 1 and row is not header, still avoid leaving group empty
alert("Cannot remove the last row of this group.");
return;
}
$tr.remove();
// update rowspan referring to the group's header (pass any neighbor row)
updateRowspanForGroup($firstRow);
}
}
});
// Fire sub-options handling ( Fire content, Fire building ) using the checkbox
$(document).on("change", ".fireSub", function() {
let data = policyData["fire"];
let value = data['risk_split_up']
let policy = "fire"
console.log(value);
let bg = "";
$('tr.fire_risk_split_up').each(function() {
bg = $(this).find('td:first').css('background-color');
console.log(bg);
});
console.log('bg color :', bg);
$('.fire_risk_split_up').remove();
let fireBuilding = $(".fireSub[value='fire_building']");
let fireContent = $(".fireSub[value='fire_content']");
let fireBuildingChecked = fireBuilding.is(":checked");
let fireContentChecked = fireContent.is(":checked");
// Prevent both from being unchecked
if (!fireBuildingChecked && !fireContentChecked) {
$(this).prop("checked", true);
return;
}
let fireParticularsContainer = $("#row-fire-particulars");
if (fireParticularsContainer.length > 0) {
let content = "";
if (fireBuildingChecked) {
content += policyData["fire"].particulars.fire_building + "<br><br>";
$('.fire_risk_split_up').remove();
appendBuildingAndContent(value, "Buildings", bg);
}
if (fireContentChecked) {
content += policyData["fire"].particulars.fire_content;
$('.fire_risk_split_up').remove();
appendBuildingAndContent(value, "Contents", bg);
}
if (fireBuildingChecked && fireContentChecked) {
$('.fire_risk_split_up').remove();
appendBuildingAndContent(value, "All", bg);
}
fireParticularsContainer.html(content);
}
});
// function for append the Fire sub option rows
function appendBuildingAndContent(value, type, bgColor) {
console.log(value);
if (type === "Buildings") {
value = value.slice(0, 1);
} else if (type === "Contents") {
value = value.slice(1); // keep all except index 0
} else {
value = value;
}
let firstTwo = value.slice(0, 2);
// Get remaining items
let remaining = value.slice(2);
console.log("✅ First 2 items:", firstTwo);
console.log("✅ Remaining items:", remaining);
let row = ""; // ✅ initialize correctly
value.forEach((item, index) => {
let condition = true;
if (condition) {
let policy = "fire";
let key = "risk_split_up";
let rowId = `row-${policy}-${key}-${index}`;
let firstCell = index === 0 ? `<td style="background-color:${bgColor}" rowspan="${value.length}">${formatHeader(key)}</td>` : "";
row += `<tr class="policy-details fire_risk_split_up"
id="${rowId}"
data-policy="${policy}"
data-key="${key}"
data-index="${index}"
data-array-length="${value.length}">
${firstCell}
<td style="background-color:${bgColor}" class="particulars" contenteditable="true">${item.name}</td>`;
// Empty cells for risk locations
for (let i = 0; i < riskCount; i++) {
row += `<td style="background-color:${bgColor}" contenteditable="true" class="risk-col"></td>`;
}
// Sum insured cell
row += `<td style="background-color:${bgColor}" class="sum_insured" contenteditable="true">${item.default_answer}</td>`;
// index Sum insured cell
row += `<td style="background-color:${bgColor}" class="index_sum_insured" contenteditable="true">${item.default_answer}</td>`;
// Insurer cells
for (let i = 0; i < insurerCount; i++) {
row += `<td style="background-color:${bgColor}" contenteditable="true" class="insurer-col"></td>`;
}
// Action column
let qcrId = `qcrCheckbox_${rowId}`;
row += `<td style="background-color:${bgColor}" class="action">
<input class="qcr-checkbox" id="${qcrId}" name="qcr" type="checkbox" checked>
<label for="${qcrId}">QCR</label>
<input class="client-checkbox" name="stc" type="checkbox" checked> <label> Client </label>
<button class="btn btn-primary btn-sm btn-add-row">+</button>
<button class="btn btn-danger btn-sm btn-remove-row">-</button>
</td>`;
row += `</tr>`;
}
});
// ✅ insert rows after #row-fire
if (row) {
$("#row-fire").after(row);
}
}
// Formats particulars (array, object, or string) into an HTML-friendly string
function formatParticulars(particulars) {
if (Array.isArray(particulars)) {
return particulars.join("<br>");
} else if (typeof particulars === "object" && particulars !== null) {
return Object.values(particulars).join("<br><br>");
} else {
return particulars || "";
}
}
// Converts a string with underscores into a human-readable ( Example: "risk_split_up" → "Risk Split Up" )
function formatHeader(str) {
if (!str) return "";
// Replace underscores with spaces and capitalize
let words = str.replace(/_/g, ' ').split(' ');
words = words.map(word => word.charAt(0).toUpperCase() + word.slice(1));
return words.join(' ');
}
// Convert labels to snake_case keys
function toSnakeCase(str) {
return str.toLowerCase().replace(/\s+/g, "_").replace(/-/g, "_");
}
// Builds a JSON object of policies with their active status and selected sub-options
function buildPolicyJson() {
active_policy_data = {};
$("#policyCheckboxes .policy").each(function() {
let policy = $(this).val();
let isChecked = $(this).is(":checked") ? 1 : 0;
active_policy_data[policy] = {
active: isChecked,
options: {}
};
// If checked, capture sub-options
let subOptions = $(this).closest("label").next(".sub-options").find("input");
subOptions.each(function() {
let optionKey = toSnakeCase($(this).val());
active_policy_data[policy].options[optionKey] = $(this).is(":checked") ? 1 : 0;
});
});
}
// Listen for changes
$("#policyCheckboxes").on("change", "input[type=checkbox]", function() {
buildPolicyJson();
console.log("Updated Policy JSON:", JSON.stringify(active_policy_data, null, 2));
});
// This is for the Action column QCR uncheck and check row wise check
$(document).on("change", ".qcr-checkbox", function() {
let $row = $(this).closest("td"); // find the same cell
if (!$(this).is(":checked")) {
$row.find(".client-checkbox").prop("checked", false);
} else {
$row.find(".client-checkbox").prop("checked", true);
}
});
// This function for Risk Location Cloumn show or hide in the QCR
$(document).on("change", ".qcr-check-col", function() {
let $menu = $(this).closest(".dropdown-menu");
// If QCR is unchecked → uncheck Client in the same dropdown
if (!$(this).is(":checked")) {
$menu.find(".client-check-col").prop("checked", false);
} else {
$menu.find(".client-check-col").prop("checked", true);
}
});
// Basement show/hide
$("#has_basement").on("change", function() {
if ($(this).is(":checked")) {
$("#basementUtilisationDiv").removeClass("d-none");
$("#basementUtilisation").prop("required", true);
} else {
$("#basementUtilisationDiv").addClass("d-none");
$("#basementUtilisation").prop("required", false).val("");
}
});
// Occupancy Risk → fill IIB Code
$("#occupancy_risk").on("change", function() {
let selectedText = $(this).find("option:selected").val();
if (selectedText) {
$("#iib_code").val(selectedText).prop("required", true);
} else {
$("#iib_code").val("").prop("required", false);
}
});
// Toggle policy rows on clicking the collapse icon
$(document).on("click", ".policy-collapse", function() {
let policy = $(this).data("policy"); // get policy from data attribute
console.log('policy', policy);
let $iconRow = $(this).closest("tr"); // row containing the clicked icon
console.log('iconRow', $iconRow);
// Find all rows of the same policy except the icon row
let $policyRows = $(`tr.policy-details[data-policy="${policy}"]`).not($iconRow);
// Toggle visibility
$policyRows.toggle();
// Optional: change icon to indicate collapse/expand
if ($policyRows.is(":visible")) {
$(this).removeClass("mdi-arrow-collapse-down").addClass("mdi-arrow-collapse-up");
} else {
$(this).removeClass("mdi-arrow-collapse-up").addClass("mdi-arrow-collapse-down");
}
});
// Sum the location value changes in any risk-col input (contenteditable)
$(document).on("input", "#policyTable tbody td.risk-col[contenteditable='true']", function() {
let $row = $(this).closest("tr"); // current row
let sum = 0;
// Loop through all risk-col cells in this row
$row.find("td.risk-col").each(function() {
let val = parseFloat($(this).text().replace(/,/g, '')) ||
0; // remove commas and convert to number
sum += val;
});
// Update sum_insured column
if (sum != 0) {
$row.find("td.sum_insured").text(sum);
}
});
// Sum the index-value value changes in any index-val-col input (contenteditable)
$(document).on("input", "#policyTable tbody td.index-val-col[contenteditable='true']", function() {
let $row = $(this).closest("tr"); // current row
let sum = 0;
// Loop through all index-val-col cells in this row
$row.find("td.index-val-col").each(function() {
let val = parseFloat($(this).text().replace(/,/g, '')) ||
0; // remove commas and convert to number
sum += val;
});
// Update sum_insured column
if (sum != 0) {
$row.find("td.index_sum_insured").text(sum);
}
});
// Handle collapse/expand functionality
document.addEventListener('click', function(e) {
if (e.target.classList.contains('toggle-collapse')) {
e.preventDefault();
const type = e.target.dataset.type;
console.log('type', type)
let id = null;
if(type === 'risk'){
id = e.target.dataset.riskId;
}else if(type === 'insurer'){
id = e.target.dataset.insurerId;
}else if(type === 'index-value') {
id = e.target.dataset.indexValueId;
}
console.log('id', id)
// Find the header column
let headerCol;
if (type === 'risk') {
headerCol = document.querySelector(`th.risk-col[data-risk-id="${id}"]`);
} else if(type === 'insurer') {
headerCol = document.querySelector(`th.insurer-col[data-insurer-id="${id}"]`);
}else if(type === 'index-value') {
headerCol = document.querySelector(`th.index-val-col[data-index-value-id="${id}"]`);
}
console.log('headerCol', headerCol)
let attrName = type === 'risk' ? 'risk-id' : type === 'insurer' ? 'insurer-id' : 'index-value-id';
console.log('attrName', attrName);
if (headerCol) {
const isCollapsed = headerCol.classList.contains('collapsed');
if (isCollapsed) {
// Expand
headerCol.classList.remove('collapsed');
e.target.textContent = 'Collapse';
// Show corresponding body cells
// const bodyCells = document.querySelectorAll(`td[data-${type === 'risk' ? 'risk' : 'insurer'}-id="${id}"]`);
const bodyCells = document.querySelectorAll(`td[data-${attrName}="${id}"]`);
// console.log('bodyCells', bodyCells);
bodyCells.forEach(cell => {
cell.style.width = '';
cell.style.minWidth = '';
cell.style.maxWidth = '';
cell.style.overflow = '';
cell.style.textIndent = '';
});
} else {
// Collapse
headerCol.classList.add('collapsed');
e.target.textContent = 'Expand';
// Hide corresponding body cells content
// const bodyCells = document.querySelectorAll(`td[data-${type === 'risk' ? 'risk' : 'insurer'}-id="${id}"]`);
const bodyCells = document.querySelectorAll(`td[data-${attrName}="${id}"]`);
// console.log('bodyCells', bodyCells);
bodyCells.forEach(cell => {
cell.style.width = '40px';
cell.style.minWidth = '40px';
cell.style.maxWidth = '40px';
cell.style.overflow = 'hidden';
cell.style.textIndent = '-9999px';
});
}
}
}
});
});
// This document ready function to handle the document init load function
$(document).ready(function(){
$('#insurerSelect2').select2({
placeholder: 'Search and select an insurer',
allowClear: true,
});
})
// Function to generate random light color
function getRandomLightColor() {
// High RGB values = lighter colors
let r = Math.floor(200 + Math.random() * 55); // 200255
let g = Math.floor(200 + Math.random() * 55);
let b = Math.floor(200 + Math.random() * 55);
return `rgb(${r}, ${g}, ${b})`;
}
</script>
<script>
// ----------------- GENERATE JSON FROM TABLE FUNCTIONS ------------------------------------------------------------------------------------
// Function to convert table to JSON with rowspan information
function convertTableToJSON() {
const table = document.getElementById('policyTable');
if (!table) {
throw new Error('Table with ID "policyTable" not found');
}
// Extract risk locations
const risk_locations = [];
const riskHeaders = table.querySelectorAll('th.risk-col');
riskHeaders.forEach(header => {
const riskId = header.getAttribute('data-risk-id');
const nameElement = header.querySelector('.loc_name');
const addressElement = header.querySelector('.loc_address');
const checkboxStates = getRiskLocationCheckboxStates(header);
let details = risk_location_details[riskId];
risk_locations.push({
id: riskId,
name: nameElement ? nameElement.textContent.trim() : '',
address: addressElement ? addressElement.textContent.trim() : '',
construction_type: details.construction_type,
has_basement: details.has_basement,
basement_utilisation: details.basement_utilisation,
occupancy_risk: details.occupancy_risk,
iib_code: details.iib_code,
business_description: details.business_descript,
qcr: checkboxStates.qcr,
client: checkboxStates.client
});
});
// Extract insurers
const insurers = [];
const insurerHeaders = table.querySelectorAll('th.insurer-col');
insurerHeaders.forEach(header => {
const insurerId = header.getAttribute('data-insurer-id');
const insurerName = header.textContent.trim().split('\n')[0];
insurers.push({
id: insurerId,
name: insurerName
});
});
// Extract policies
const policies = [];
const policyRows = table.querySelectorAll('tr[data-policy][data-key="particulars"]');
policyRows.forEach(row => {
const policyId = row.getAttribute('data-policy');
const policyName = row.querySelector('td:first-child').textContent.trim();
const particulars = row.querySelector('td.particulars').innerHTML.trim();
const totalSumInsured = parseFloat(row.querySelector('td.sum_insured').textContent.trim()) || 0;
const indexSumInsured = parseFloat(row.querySelector('td.index_sum_insured').textContent.trim()) || 0;
// Extract policy row data (risk locations and insurers)
const policyRowData = extractPolicyRowData(table, policyId, risk_locations, insurers);
const policy = {
id: policyId,
name: policyName,
particulars: particulars.replace(/<br\s*\/?>/gi, '\n').replace(/<[^>]*>/g, ''),
total_sum_insured: totalSumInsured,
index_sum_insured: indexSumInsured,
risk_locations: policyRowData.risk_locations,
insurers: policyRowData.insurers
};
// Extract different sections for this policy
const sections = extractPolicySections(table, policyId, risk_locations, insurers);
Object.assign(policy, sections);
policies.push(policy);
});
return {
risk_locations,
insurers,
policies,
risk_location_details,
active_policy_data
};
}
function extractPolicySections(table, policyId, risk_locations, insurers) {
const sections = {};
// Find all rows for this policy
const policyRows = table.querySelectorAll(`tr[data-policy="${policyId}"]`);
// Group rows by section type (data-key attribute)
const sectionGroups = {};
policyRows.forEach(row => {
const key = row.getAttribute('data-key');
if (key && key !== 'particulars') {
if (!sectionGroups[key]) {
sectionGroups[key] = [];
}
sectionGroups[key].push(row);
}
});
// Process each section
Object.keys(sectionGroups).forEach(sectionKey => {
const rows = sectionGroups[sectionKey];
if (sectionKey === 'excess') {
sections.excess = extractRowData(rows[0], risk_locations, insurers);
} else {
// const sectionName = convertSectionKey(sectionKey);
const sectionName = sectionKey;
sections[sectionName] = {
items: rows.map(row => extractRowData(row, risk_locations, insurers)),
rowspan: rows.length > 0 ? getRowspan(rows[0]) : 1,
sectionTitle: getSectionTitle(rows[0])
};
}
});
return sections;
}
function extractPolicyRowData(table, policyId, risk_locations, insurers) {
// console.log("table" , table)
// console.log("policyId" , policyId)
// console.log("risk_locations" , risk_locations)
// console.log("insurers" , insurers)
// Find the particulars row for this policy
const particularsRow = table.querySelector(`tr[data-policy="${policyId}"][data-key="particulars"]`);
console.log("particularsRow" , particularsRow)
if (!particularsRow) {
return { risk_locations: {}, insurers: {} };
}
const policy_risk_locations = {};
const policy_insurers = {};
// Extract risk location data
const riskCells = particularsRow.querySelectorAll('td.risk-col');
riskCells.forEach(cell => {
const riskId = cell.getAttribute('data-risk-id');
// console.log("riskId" , riskId)
const value = cell.textContent.trim();
policy_risk_locations[riskId] = value;
});
// Extract insurer data
const insurerCells = particularsRow.querySelectorAll('td.insurer-col');
insurerCells.forEach(cell => {
const insurerId = cell.getAttribute('data-insurer-id');
// console.log("insurerId" , insurerId)
const value = cell.textContent.trim();
policy_insurers[insurerId] = value;
});
return {
risk_locations: policy_risk_locations,
insurers: policy_insurers
};
}
function extractRowData(row, risk_locations, insurers) {
const particularsCell = row.querySelector('td.particulars');
const sumInsuredCell = row.querySelector('td.sum_insured');
const indexSumInsuredCell = row.querySelector('td.index_sum_insured');
const qcrCheckbox = row.querySelector('input.qcr-checkbox');
const clientCheckbox = row.querySelector('input.client-checkbox');
const data = {
item: particularsCell ? particularsCell.textContent.trim() : '',
sum_insured: sumInsuredCell ? (parseFloat(sumInsuredCell.textContent.trim()) || 0) : 0,
index_sum_insured: indexSumInsuredCell ? (parseFloat(indexSumInsuredCell.textContent.trim()) || 0) : 0,
risk_locations: {},
insurers: {},
qcr: qcrCheckbox ? qcrCheckbox.checked : false,
client: clientCheckbox ? clientCheckbox.checked : false
};
// Extract risk location values
risk_locations.forEach(location => {
const riskCell = row.querySelector(`td.risk-col[data-risk-id="${location.id}"]`);
data.risk_locations[location.id] = riskCell ? riskCell.textContent.trim() : '';
});
// Extract insurer values
insurers.forEach(insurer => {
const insurerCell = row.querySelector(`td.insurer-col[data-insurer-id="${insurer.id}"]`);
data.insurers[insurer.id] = insurerCell ? insurerCell.textContent.trim() : '';
});
return data;
}
function getRowspan(row) {
const firstCell = row.querySelector('td[rowspan]');
return firstCell ? parseInt(firstCell.getAttribute('rowspan')) : 1;
}
function getSectionTitle(row) {
const firstCell = row.querySelector('td[rowspan]');
return firstCell ? firstCell.textContent.trim() : '';
}
function convertSectionKey(key) {
return key.replace(/_([a-z])/g, (match, letter) => letter.toUpperCase());
}
// Helper function to get checkbox states from risk location headers
function getRiskLocationCheckboxStates(header) {
const dropdownCheckboxes = header.querySelectorAll('.dropdown-menu input[type="checkbox"]');
return {
qcr: dropdownCheckboxes[0] ? dropdownCheckboxes[0].checked : false,
client: dropdownCheckboxes[1] ? dropdownCheckboxes[1].checked : false
};
}
// ------------- GENERATE TABLE FROM JSON FUNCTIONS --------------------------------------------------------------
// Function to generate table from JSON
function generateTableFromJSON(jsonData) {
const { risk_locations, insurers, policies, active_policy_data } = jsonData;
// set the risk locations count
riskCount = risk_locations.length;
// set the insurer count
insurerCount = insurers.length;
// set the location details
risk_location_details = jsonData.risk_location_details;
// function for set policy checkbox
setActivePolicyCheckboxesFromJson(active_policy_data);
let html = `<table id="policyTable" class="table table-bordered">
<thead>
<tr id="policyHeader">
<th>Policy</th>
<th>Particulars</th>`;
// Add risk location headers
risk_locations.forEach(location => {
html += `
<th class="risk-col" data-risk-id="${location.id}">
<div class="loc-details" style="text-align: center;">
<div style="font-weight: bold;">Risk Location ${location.id}</div>
<div style="font-size: 11px; color: #666; margin-top: 2px;">
<span class="loc_name" style="display: block;">${location.name}</span>
<span class="loc_address" style="display: block;">${location.address}</span>
</div>
</div>
<div class="dropdown d-inline-block float-end loc-menu">
<button class="btn btn-sm btn-light dropdown-toggle" type="button" data-toggle="dropdown" aria-expanded="false">
&#x22EE;
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item edit-location" href="#" data-risk-id="${location.id}">Edit</a></li>
<li><a class="dropdown-item text-danger remove-location" href="#" data-risk-id="${location.id}">Remove</a></li>
<li><a class="dropdown-item toggle-collapse" href="#" data-risk-id="${location.id}" data-type="risk">Collapse</a></li>
<li class="dropdown-item">
<label class="form-check-label">
<input type="checkbox" class="form-check-input me-2 qcr-check-col" data-risk-id="${location.id}" ${location.qcr ? 'checked' : ''}>
QCR
</label>
</li>
<li class="dropdown-item">
<label class="form-check-label">
<input type="checkbox" class="form-check-input me-2 client-check-col" data-risk-id="${location.id}" ${location.client ? 'checked' : ''}>
Client
</label>
</li>
</ul>
</div>
</th>
`;
});
html += `<th class="sum_insured">Total Sum Insured</th>`;
html += `
<th class="index_sum_insured">
Index Sum Insured &nbsp;&nbsp;
<div class="dropdown d-inline-block float-end loc-menu">
<button class="btn btn-sm btn-light dropdown-toggle" type="button" data-toggle="dropdown" aria-expanded="false">
&#x22EE;
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li class="dropdown-item">
<input type="checkbox" class="form-check-input me-2 qcr-check-col" data-risk-id="${riskCount}" checked>
<label class="form-check-label">
QCR
</label>
</li>
<li class="dropdown-item">
<input type="checkbox" class="form-check-input me-2 client-check-col" data-risk-id="${riskCount}" checked>
<label class="form-check-label">
Client
</label>
</li>
</ul>
</div>
</th>
`;
// Add insurer headers
insurers.forEach(insurer => {
html += `
<th class="insurer-col" data-insurer-id="${insurer.id}">
${insurer.name}
<div class="dropdown d-inline-block float-end">
<button class="btn btn-sm btn-light dropdown-toggle" type="button" data-toggle="dropdown" aria-expanded="false">
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item edit-insurer" href="#" data-insurer-id="${insurer.id}">Edit</a></li>
<li><a class="dropdown-item text-danger remove-insurer" href="#" data-insurer-id="${insurer.id}">Remove</a></li>
</ul>
</div>
</th>`;
});
html += `<th>Action</th>
</tr>
</thead>
<tbody>`;
// Generate policy rows
policies.forEach(policy => {
// get the random_color
let random_color = getRandomLightColor();
// Main policy row
html += generatePolicyRow(policy, risk_locations, insurers, random_color);
// Generate section rows
Object.keys(policy).forEach(key => {
if (key !== 'id' && key !== 'name' && key !== 'particulars' && key !== 'total_sum_insured') {
const section = policy[key];
if (key === 'excess') {
html += generateExcessRow(policy.id, section, risk_locations, insurers, random_color);
} else if (section.items) {
html += generateSectionRows(policy.id, key, section, risk_locations, insurers, random_color);
}
}
});
});
html += `</tbody></table>`;
return html;
}
function generatePolicyRow(policy, risk_locations, insurers, random_color) {
let html =
` <tr class="policy-details" id="row-${policy.id}" data-policy="${policy.id}" data-key="particulars" data-index="0">
<td style="background-color:${random_color}">${policy.name} <i class="mdi mdi-arrow-collapse-up policy-collapse" data-policy="${policy.id}" aria-hidden="true"></i></td>
<td style="background-color:${random_color}" class="particulars" contenteditable="true" id="row-${policy.id}-particulars">${policy.particulars}</td>`;
// Risk location cells
risk_locations.forEach(location => {
html += `<td style="background-color:${random_color}" class="risk-col" data-risk-id="${location.id}" contenteditable="true">${policy.risk_locations?.[location.id] ?? ""}</td>`;
});
html += `<td style="background-color:${random_color}" class="sum_insured" contenteditable="true">${policy.total_sum_insured}</td>`;
html += `<td style="background-color:${random_color}" class="index_sum_insured" contenteditable="true">${policy.total_sum_insured}</td>`;
// Insurer cells
insurers.forEach(insurer => {
html += `<td style="background-color:${random_color}" class="insurer-col" data-insurer-id="${insurer.id}" contenteditable="true">${policy.insurers?.[insurer.id] ?? '--'}</td>`;
});
html += `<td style="background-color:${random_color}" contenteditable="false" class="action"></td></tr>`;
return html;
}
function generateSectionRows(policyId, sectionKey, section, risk_locations, insurers, random_color) {
let html = '';
const sectionClass = `${policyId}_${sectionKey}`;
section.items.forEach((item, index) => {
const isFirstRow = index === 0;
html +=
`<tr class="policy-details ${sectionClass}" id="row-${policyId}-${sectionKey}-${index}" data-policy="${policyId}" data-key="${sectionKey}" data-index="${index}" data-array-length="${section.items.length}">`;
if (isFirstRow) {
html += `<td style="background-color:${random_color}" rowspan="${section.rowspan}">${section.sectionTitle}</td>`;
}
html += `<td style="background-color:${random_color}" class="particulars" contenteditable="true">${item.item}</td>`;
// Risk location cells
risk_locations.forEach(location => {
const value = item.risk_locations[location.id] || '';
html +=
`<td style="background-color:${random_color}" class="risk-col" data-risk-id="${location.id}" contenteditable="true">${value}</td>`;
});
// sum insured
html += `<td style="background-color:${random_color}" class="sum_insured" contenteditable="true">${item.sum_insured}</td>`;
// index sum insured
html += `<td style="background-color:${random_color}" class="index_sum_insured" contenteditable="true">${item.sum_insured}</td>`;
// Insurer cells
insurers.forEach(insurer => {
const value = item.insurers[insurer.id] || '--';
html +=
`<td style="background-color:${random_color}" class="insurer-col" data-insurer-id="${insurer.id}" contenteditable="true">${value}</td>`;
});
html += `<td style="background-color:${random_color}" contenteditable="false" class="action">
<input class="qcr-checkbox" id="qcrCheckbox_row-${policyId}-${sectionKey}-${index}" name="qcr" type="checkbox" ${item.qcr ? 'checked' : ''}>
<label for="qcrCheckbox_row-${policyId}-${sectionKey}-${index}">QCR</label>
<input class="client-checkbox" name="stc" type="checkbox" ${item.client ? 'checked' : ''}> <label> Client </label>
<button class="btn btn-primary btn-sm btn-add-row">+</button>
<button class="btn btn-danger btn-sm btn-remove-row">-</button>
</td></tr>`;
});
return html;
}
function generateExcessRow(policyId, excess, risk_locations, insurers, random_color) {
let html = `<tr class="policy-details ${policyId}_excess" id="row-${policyId}-excess" data-policy="${policyId}" data-key="excess" data-index="0">
<td style="background-color:${random_color}" rowspan="1" contenteditable="true">Excess</td>
<td style="background-color:${random_color}" class="particulars" contenteditable="true">${excess.item}</td>`;
// Risk location cells
risk_locations.forEach(location => {
const value = excess.risk_locations[location.id] || '';
html += `<td style="background-color:${random_color}" class="risk-col" data-risk-id="${location.id}" contenteditable="true">${value}</td>`;
});
// sum insured
html += `<td style="background-color:${random_color}" class="sum_insured" contenteditable="true">${excess.sum_insured}</td>`;
// index sum insured
html += `<td style="background-color:${random_color}" class="index_sum_insured" contenteditable="true">${excess.sum_insured}</td>`;
// Insurer cells
insurers.forEach(insurer => {
const value = excess.insurers[insurer.id] || '--';
html += `<td style="background-color:${random_color}" class="insurer-col" data-insurer-id="${insurer.id}" contenteditable="true">${value}</td>`;
});
html += `<td style="background-color:${random_color}" contenteditable="false" class="action">
<input class="qcr-checkbox" id="qcrCheckbox_row-${policyId}-excess" name="qcr" type="checkbox" ${excess.qcr ? 'checked' : ''}>
<label for="qcrCheckbox_row-${policyId}-excess">QCR</label>
<input class="client-checkbox" name="stc" type="checkbox" ${excess.client ? 'checked' : ''}> <label> Client </label>
<button class="btn btn-primary btn-sm btn-add-row">+</button>
<button class="btn btn-danger btn-sm btn-remove-row">-</button>
</td></tr>`;
return html;
}
function setActivePolicyCheckboxesFromJson(data) {
for (let key in data) {
if (key == "fire" && data['fire'].active === 1) {
$('#fire_check_box').parent().after(`
<div class="sub-options" id="fireOptions">
<label><input type="checkbox" class="fireSub" value="fire_building" id="fire_building_check_box" checked> Fire Building</label>
<label><input type="checkbox" class="fireSub" value="fire_content" id="fire_content_check_box" checked> Fire Content</label>
</div>
`);
}
if (data.hasOwnProperty(key)) {
// Check top-level checkbox if active
if (data[key].active === 1) {
$("#" + key + '_check_box').prop("checked", true);
} else {
$("#" + key + '_check_box').prop("checked", false);
}
// Check option checkboxes if value is 1
if (data[key].options) {
for (let optionKey in data[key].options) {
if (data[key].options.hasOwnProperty(optionKey)) {
if (data[key].options[optionKey] === 1) {
$("#" + optionKey + '_check_box').prop("checked", true);
} else {
$("#" + optionKey + '_check_box').prop("checked", false);
}
}
}
}
}
}
}
// ------------- CALLBACK FOR SAVE AND VIEW FUNCTIONS --------------------------------------------------------------
// save function
function getJson() {
// Usage example:
const jsonData = convertTableToJSON();
console.log(JSON.stringify(jsonData, null, 2));
localStorage.setItem('insuranceData', JSON.stringify(jsonData));
let url = '<?= base_url('test/saverfq') ?>';
// Data to send in the AJAX request
let requestData = {
json: jsonData,
};
// Send AJAX request
sendAjaxRequestForGlobal(url, 'POST', requestData, function(response) {
console.log('Data fetched successfully:', response);
}, function(xhr, status, error) {
console.error('Error fetching data:', error);
console.error(xhr.responseText);
toastr.error('An error occurred while checking the CD amount.', 'Error');
});
}
// view function
function generateTableFromLocal() {
$('#policyTable').remove();
let jsonData = localStorage.getItem('insuranceData');
console.log("jsonData", jsonData);
jsonData = JSON.parse(jsonData);
console.log("jsonData", jsonData);
// Generate table from JSON
const tableHTML = generateTableFromJSON(jsonData);
document.getElementById('container').innerHTML = tableHTML;
}
</script>
<script>
$(document).ready(function() {
// Define dropdown options for each type
const dropdownOptions = {
sum_insured: [
'Agreed',
'Market Value',
'Replacement Value',
'Book Value',
'Reinstatement Value',
'Declared Value'
],
index_sum_insured: [
'Agreed',
'Indexed',
'Non-Indexed',
'Automatic Indexation',
'Manual Indexation'
],
insurer: [
'Agreed',
'Company A',
'Company B',
'Company C',
'HDFC ERGO',
'ICICI Lombard',
'Bajaj Allianz',
'New India Assurance',
'Oriental Insurance'
]
};
let currentDropdown = null;
// Function to create dropdown menu
function createDropdown($cell, options) {
const $dropdown = $('<div class="cell-dropdown"></div>');
const currentValue = $cell.text().trim(); // Get current cell value
$.each(options, function(index, option) {
const $option = $('<div class="cell-dropdown-item"></div>').text(option);
// Highlight if this option matches the current cell value
if (option === currentValue) {
$option.addClass('selected');
}
$option.on('click', function(e) {
e.stopPropagation();
selectOption($cell, option);
});
$dropdown.append($option);
});
return $dropdown;
}
// Function to select an option
function selectOption($cell, value) {
$cell.text(value);
hideAllDropdowns();
}
// Function to show dropdown
function showDropdown($cell) {
hideAllDropdowns();
// Determine dropdown type based on class
let dropdownType;
if ($cell.hasClass('sum_insured')) {
dropdownType = 'sum_insured';
} else if ($cell.hasClass('index_sum_insured')) {
dropdownType = 'index_sum_insured';
} else if ($cell.hasClass('insurer-col')) {
dropdownType = 'insurer';
}
const options = dropdownOptions[dropdownType];
if (options) {
const $dropdown = createDropdown($cell, options);
$cell.append($dropdown);
$dropdown.show();
currentDropdown = $dropdown;
}
}
// Function to hide all dropdowns
function hideAllDropdowns() {
$('.cell-dropdown').remove();
currentDropdown = null;
}
// Event delegation for dropdown cells (works with dynamically added rows) - ONLY TBODY
$(document).on('click', 'tbody .sum_insured, tbody .index_sum_insured, tbody .insurer-col', function(e) {
e.stopPropagation();
const $cell = $(this);
const $row = $cell.closest('tr');
// Skip dropdown if row has data-key="particulars"
if ($row.attr('data-key') === 'particulars') {
return; // Exit without showing dropdown
}
// If this cell already has a dropdown open, close it
if (currentDropdown && currentDropdown.parent().is($cell)) {
hideAllDropdowns();
} else {
showDropdown($cell);
}
});
// Close dropdown when clicking outside
$(document).on('click', function(e) {
if (!$(e.target).closest('tbody .sum_insured, tbody .index_sum_insured, tbody .insurer-col').length) {
hideAllDropdowns();
}
});
// Prevent dropdown from closing when clicking inside it
$(document).on('click', '.cell-dropdown', function(e) {
e.stopPropagation();
});
});
</script>