nhance/app/Views/digit_motor/journey.php
2026-07-21 09:32:13 +05:30

673 lines
31 KiB
PHP

<?php
$quote = $quote ?? null;
$quoteId = $quote_id ?? ($quote['id'] ?? null);
$vehicle = $quote['vehicle'] ?? [];
$kyc = $quote['kyc'] ?? [];
$payment = $quote['payment'] ?? [];
$policy = $quote['policy'] ?? [];
$status = $quote['status'] ?? 'DRAFT';
$statusToStep = [
'DRAFT' => 0,
'QUOTED' => 1,
'CREATED' => 2,
'KYC_DONE' => 3,
'PAID' => 4,
'EFFECTIVE' => 4,
];
$initialStep = $statusToStep[$status] ?? 0;
$coverage = is_array($quote['coverage_details'] ?? null) ? $quote['coverage_details'] : [];
?>
<style>
:root{
--dm-teal:#02a8b5;
--dm-teal-dark:#028a95;
--dm-teal-soft:#e7f6f7;
--dm-ink:#1B1D23;
--dm-muted:#6B6A63;
--dm-line:#DEDBD2;
--dm-paper:#F7F5F0;
--dm-green:#1F7A4D;
--dm-green-bg:#E4F2EA;
--dm-steel:#35577A;
--dm-steel-bg:#E7EEF4;
--dm-red:#C43D3D;
--dm-red-bg:#FBEAEA;
}
.dm-page{width:100%;padding:24px 8px 24px;}
.dm-toolbar{
display:flex;justify-content:space-between;align-items:center;
margin:12px 0 16px;padding-top:8px;gap:12px;flex-wrap:wrap;
position:relative;z-index:2;
}
.dm-back{
display:inline-flex;align-items:center;gap:6px;
color:var(--dm-teal-dark)!important;font-weight:600;font-size:14px;text-decoration:none!important;
}
.dm-back:hover{color:var(--dm-teal)!important;}
.dm-enquiry{font-size:12px;color:var(--dm-muted);}
.dm-enquiry strong{
display:inline-block;margin-left:6px;font-family:monospace;font-size:13px;color:var(--dm-teal-dark);font-weight:600;
}
/* Progress stepper — compact */
.dm-route-wrap{
background:linear-gradient(135deg,#026a72 0%,#02a8b5 100%);
padding:12px 20px 14px;border-radius:10px;margin-bottom:14px;
}
.dm-route{position:relative;height:48px;width:100%;}
.dm-road{
position:absolute;top:14px;left:28px;right:28px;height:3px;
background:rgba(255,255,255,.22);border-radius:2px;z-index:1;
}
.dm-fill{
position:absolute;top:14px;left:28px;height:3px;
background:#fff;border-radius:2px;width:0;z-index:1;
transition:width .45s ease, left .45s ease;
}
.dm-stops{
position:absolute;top:0;left:0;right:0;bottom:0;
display:flex;justify-content:space-between;align-items:flex-start;z-index:2;
}
.dm-stop{flex:0 0 auto;width:64px;text-align:center;cursor:pointer;}
.dm-dot{
width:22px;height:22px;border-radius:50%;
background:#026a72;border:2px solid rgba(255,255,255,.55);
margin:4px auto 0;display:flex;align-items:center;justify-content:center;
color:rgba(255,255,255,.7);font-size:11px;transition:all .2s;position:relative;z-index:3;
}
.dm-stop.done .dm-dot{background:#fff;border-color:#fff;color:var(--dm-teal-dark);}
.dm-stop.active .dm-dot{
background:#fff;border-color:#fff;color:var(--dm-teal-dark);
box-shadow:0 0 0 3px rgba(255,255,255,.28);
}
.dm-stop-label{
margin-top:4px;font-size:11px;color:rgba(255,255,255,.7);white-space:nowrap;font-weight:500;line-height:1.2;
}
.dm-stop.active .dm-stop-label,.dm-stop.done .dm-stop-label{color:#fff;font-weight:600;}
.dm-panel{
background:#fff;border:1px solid var(--dm-line);border-radius:12px;padding:28px 28px 24px;position:relative;
box-shadow:0 4px 16px rgba(2,168,181,.06);
}
.dm-panel-head{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:20px;}
.dm-eyebrow{font-size:11px;color:var(--dm-teal-dark);text-transform:uppercase;letter-spacing:.08em;font-weight:600;margin-bottom:6px;}
.dm-title{font-size:20px;font-weight:600;color:var(--dm-ink);}
.dm-desc{font-size:13px;color:var(--dm-muted);margin-top:6px;max-width:640px;line-height:1.5;}
.dm-index{font-family:monospace;font-size:32px;color:#d9ecee;font-weight:500;}
.dm-step{display:none;}
.dm-step.active{display:block;}
.dm-section{
font-size:11px;color:var(--dm-muted);text-transform:uppercase;letter-spacing:.06em;font-weight:600;
margin:22px 0 12px;padding-top:16px;border-top:1px solid var(--dm-line);
}
.dm-section:first-of-type{margin-top:0;padding-top:0;border-top:none;}
.dm-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:14px 16px;}
.dm-grid.g3{grid-template-columns:repeat(3,1fr);}
.dm-field{display:flex;flex-direction:column;gap:6px;}
.dm-field.full{grid-column:1/-1;}
.dm-field label{font-size:12px;color:var(--dm-muted);font-weight:500;margin:0;}
.dm-field input,.dm-field select{
height:38px;border:1px solid var(--dm-line);border-radius:8px;padding:0 12px;font-size:13.5px;
background:var(--dm-paper);outline:none;width:100%;
}
.dm-field input:focus,.dm-field select:focus{border-color:var(--dm-teal);background:#fff;}
.dm-chips{display:flex;gap:8px;flex-wrap:wrap;}
.dm-chip{
padding:7px 14px;border:1px solid var(--dm-line);border-radius:20px;font-size:12.5px;cursor:pointer;
color:var(--dm-muted);user-select:none;background:#fff;transition:all .15s;
}
.dm-chip.on{background:var(--dm-teal);border-color:var(--dm-teal);color:#fff;}
.dm-actions{display:flex;justify-content:space-between;align-items:center;margin-top:26px;padding-top:18px;border-top:1px solid var(--dm-line);}
.dm-btn{
height:40px;padding:0 18px;border-radius:8px;font-size:13.5px;font-weight:500;cursor:pointer;
border:1px solid var(--dm-line);background:#fff;color:var(--dm-ink);display:inline-flex;align-items:center;gap:6px;
}
.dm-btn.primary{background:var(--dm-teal);border-color:var(--dm-teal);color:#fff;}
.dm-btn.primary:hover{background:var(--dm-teal-dark);}
.dm-btn.ghost{border-color:transparent;color:var(--dm-muted);}
.dm-btn:disabled{opacity:.45;cursor:not-allowed;}
.dm-quote-cards{display:grid;grid-template-columns:1fr 1fr;gap:14px;}
.dm-quote-card{border:1px solid var(--dm-line);border-radius:12px;padding:18px;cursor:pointer;position:relative;}
.dm-quote-card.selected{border:2px solid var(--dm-teal);padding:17px;background:var(--dm-teal-soft);}
.dm-quote-card .tag{font-size:11px;color:var(--dm-muted);text-transform:uppercase;letter-spacing:.05em;}
.dm-quote-card .amount{font-size:24px;font-weight:600;margin:8px 0 2px;color:var(--dm-ink);}
.dm-quote-card .idv{font-size:12px;color:var(--dm-muted);}
.dm-check{
position:absolute;top:14px;right:14px;width:20px;height:20px;border-radius:50%;
background:var(--dm-teal);color:#fff;display:none;align-items:center;justify-content:center;font-size:11px;
}
.dm-quote-card.selected .dm-check{display:flex;}
.dm-status{border:1px solid var(--dm-line);border-radius:12px;padding:18px;display:flex;align-items:center;gap:14px;}
.dm-icon{width:42px;height:42px;border-radius:10px;display:flex;align-items:center;justify-content:center;font-size:18px;flex-shrink:0;}
.dm-icon.ok{background:var(--dm-green-bg);color:var(--dm-green);}
.dm-icon.wait{background:var(--dm-steel-bg);color:var(--dm-steel);}
.dm-icon.warn{background:var(--dm-red-bg);color:var(--dm-red);}
.dm-st-title{font-weight:600;font-size:14.5px;}
.dm-st-sub{font-size:12.5px;color:var(--dm-muted);margin-top:3px;}
.dm-pill{display:inline-block;padding:3px 10px;border-radius:20px;font-size:11px;font-weight:600;}
.dm-pill.ok{background:var(--dm-green-bg);color:var(--dm-green);}
.dm-pill.wait{background:var(--dm-steel-bg);color:var(--dm-steel);}
.dm-ref{width:100%;border-collapse:collapse;margin-top:16px;}
.dm-ref tr{border-top:1px solid var(--dm-line);}
.dm-ref tr:first-child{border-top:none;}
.dm-ref td{padding:10px 0;font-size:13px;}
.dm-ref td:first-child{color:var(--dm-muted);width:44%;}
.dm-ref td:last-child{text-align:right;font-family:monospace;}
.dm-doc{display:flex;align-items:center;justify-content:space-between;border:1px solid var(--dm-line);border-radius:10px;padding:12px 14px;margin-top:10px;}
.dm-alert{display:none;margin-bottom:14px;padding:10px 14px;border-radius:8px;font-size:13px;}
.dm-alert.error{display:block;background:var(--dm-red-bg);color:var(--dm-red);}
.dm-alert.ok{display:block;background:var(--dm-green-bg);color:var(--dm-green);}
.dm-loading{opacity:.55;pointer-events:none;}
@media(max-width:768px){
.dm-grid,.dm-grid.g3,.dm-quote-cards{grid-template-columns:1fr;}
.dm-stop{width:52px;}
.dm-stop-label{font-size:10px;}
.dm-road{left:22px;right:22px;}
.dm-route{height:44px;}
}
</style>
<div class="container-fluid dm-page">
<div class="dm-toolbar">
<a href="<?= base_url('digit-motor/list') ?>" class="dm-back">
<i class="mdi mdi-arrow-left"></i> Back to list
</a>
<div class="dm-enquiry">Enquiry ID <strong id="enquiryIdTag"><?= esc($quote['enquiry_id'] ?? '— new —') ?></strong></div>
</div>
<div class="dm-route-wrap">
<div class="dm-route" id="dmRoute">
<div class="dm-road"></div>
<div class="dm-fill" id="dmFill"></div>
<div class="dm-stops" id="dmStops"></div>
</div>
</div>
<div class="dm-panel" id="dmPanel">
<div id="dmAlert" class="dm-alert"></div>
<!-- Step 0: Quick Quote -->
<div class="dm-step active" data-step="0">
<div class="dm-panel-head">
<div>
<div class="dm-eyebrow">Step 1 of 5 · quick quote</div>
<div class="dm-title">Vehicle and coverage details</div>
<div class="dm-desc">Enter the vehicle to fetch an indicative premium before creating the policy quote.</div>
</div>
<div class="dm-index">01</div>
</div>
<div class="dm-section">Vehicle</div>
<div class="dm-grid g3">
<div class="dm-field"><label>Registration number</label><input id="f_reg" value="<?= esc($vehicle['license_plate_number'] ?? '') ?>" placeholder="GJ04DA8726"></div>
<div class="dm-field"><label>Vehicle code</label><input id="f_vcode" value="<?= esc($vehicle['vehicle_maincode'] ?? '') ?>" placeholder="1113811407"></div>
<div class="dm-field"><label>Registration date</label><input type="date" id="f_reg_date" value="<?= esc($vehicle['registration_date'] ?? '') ?>"></div>
<div class="dm-field"><label>Manufacture date</label><input type="date" id="f_mfg_date" value="<?= esc($vehicle['manufacture_date'] ?? '') ?>"></div>
<div class="dm-field"><label>Chassis / VIN</label><input id="f_vin" value="<?= esc($vehicle['vehicle_identification_number'] ?? '') ?>"></div>
<div class="dm-field"><label>Engine number</label><input id="f_engine" value="<?= esc($vehicle['engine_number'] ?? '') ?>"></div>
<div class="dm-field"><label>Pincode</label><input id="f_pin" value="<?= esc($quote['pincode'] ?? '') ?>" maxlength="6"></div>
<div class="dm-field"><label>Product code</label>
<select id="f_product">
<option value="20101" <?= ($quote['insurance_product_code'] ?? '') == '20101' ? 'selected' : '' ?>>20101 · Private Car Package</option>
<option value="20102" <?= ($quote['insurance_product_code'] ?? '20102') == '20102' ? 'selected' : '' ?>>20102 · Private Car</option>
</select>
</div>
<div class="dm-field"><label>Policy start</label><input type="date" id="f_start" value="<?= esc($quote['start_date'] ?? '') ?>"></div>
</div>
<div class="dm-section">Previous policy</div>
<div class="dm-grid g3">
<div class="dm-field"><label>Previous insurer code</label><input id="f_prev_ins" value="<?= esc($quote['previous_insurer_code'] ?? '') ?>" placeholder="190"></div>
<div class="dm-field"><label>Prior policy expiry</label><input type="date" id="f_prev_exp" value="<?= esc($quote['previous_policy_expiry_date'] ?? '') ?>"></div>
<div class="dm-field"><label>Claim in last year</label>
<div class="dm-chips" style="height:38px;align-items:center;">
<div class="dm-chip" data-group="claim" data-val="1" onclick="dmToggleExclusive(this)">Yes</div>
<div class="dm-chip on" data-group="claim" data-val="0" onclick="dmToggleExclusive(this)">No</div>
</div>
</div>
</div>
<div class="dm-section">Coverage add-ons</div>
<div class="dm-chips" id="addonChips">
<div class="dm-chip on" data-key="personal_accident" onclick="this.classList.toggle('on')">Personal accident</div>
<div class="dm-chip" data-key="zero_dep" onclick="this.classList.toggle('on')">Zero depreciation</div>
<div class="dm-chip" data-key="engine_protect" onclick="this.classList.toggle('on')">Engine protect</div>
<div class="dm-chip" data-key="rsa" onclick="this.classList.toggle('on')">Roadside assistance</div>
<div class="dm-chip" data-key="consumables" onclick="this.classList.toggle('on')">Consumables</div>
<div class="dm-chip" data-key="key_protect" onclick="this.classList.toggle('on')">Key & lock</div>
</div>
<div class="dm-actions">
<span style="font-size:11.5px;color:var(--dm-muted);font-family:monospace;">POST · motorQuickQuote</span>
<button class="dm-btn primary" type="button" onclick="dmQuickQuote()">Get quote <i class="mdi mdi-arrow-right"></i></button>
</div>
</div>
<!-- Step 1: Create Quote -->
<div class="dm-step" data-step="1">
<div class="dm-panel-head">
<div>
<div class="dm-eyebrow">Step 2 of 5 · create quote</div>
<div class="dm-title">Confirm premium plan</div>
<div class="dm-desc">Review the quick-quote premium, add policyholder details, then create the Digit quote.</div>
</div>
<div class="dm-index">02</div>
</div>
<div class="dm-quote-cards">
<div class="dm-quote-card selected" id="planCard">
<div class="dm-check"><i class="mdi mdi-check"></i></div>
<div class="tag">Quoted premium</div>
<div class="amount" id="planPremium">₹<?= isset($quote['premium']) ? number_format((float)$quote['premium'], 0) : '—' ?></div>
<div class="idv" id="planIdv">IDV ₹<?= isset($quote['idv']) ? number_format((float)$quote['idv'], 0) : '—' ?></div>
</div>
</div>
<div class="dm-section">Policyholder</div>
<div class="dm-grid">
<div class="dm-field"><label>First name</label><input id="f_fname" value=""></div>
<div class="dm-field"><label>Last name</label><input id="f_lname" value=""></div>
<div class="dm-field"><label>Mobile</label><input id="f_mobile" value=""></div>
<div class="dm-field"><label>Email</label><input id="f_email" value=""></div>
<div class="dm-field"><label>PAN</label><input id="f_pan" value=""></div>
<div class="dm-field"><label>Date of birth</label><input type="date" id="f_dob" value=""></div>
<div class="dm-field full"><label>Address</label><input id="f_address" value=""></div>
</div>
<div class="dm-actions">
<button class="dm-btn ghost" type="button" onclick="dmGo(0)"><i class="mdi mdi-arrow-left"></i> Back</button>
<button class="dm-btn primary" type="button" onclick="dmCreateQuote()">Create quote <i class="mdi mdi-arrow-right"></i></button>
</div>
</div>
<!-- Step 2: KYC -->
<div class="dm-step" data-step="2">
<div class="dm-panel-head">
<div>
<div class="dm-eyebrow">Step 3 of 5 · KYC status</div>
<div class="dm-title">Identity verification</div>
<div class="dm-desc">Poll KYC status before the payment link can be issued.</div>
</div>
<div class="dm-index">03</div>
</div>
<div class="dm-status" id="kycStatusCard">
<div class="dm-icon wait"><i class="mdi mdi-shield-check"></i></div>
<div>
<div class="dm-st-title" id="kycTitle"><?= esc($kyc['kyc_verification_status'] ?? 'Not checked') ?></div>
<div class="dm-st-sub" id="kycSub">Click refresh to fetch latest KYC status from Digit.</div>
</div>
<div class="ml-auto"><span class="dm-pill wait" id="kycPill"><?= esc($kyc['kyc_verification_status'] ?? 'Pending') ?></span></div>
</div>
<table class="dm-ref">
<tr><td>Quote number</td><td id="refQuoteNo"><?= esc($quote['quote_number'] ?? '—') ?></td></tr>
<tr><td>Application ID</td><td id="refAppId"><?= esc($quote['application_id'] ?? '—') ?></td></tr>
<tr><td>KYC reference</td><td id="refKycRef"><?= esc($kyc['reference_id'] ?? '—') ?></td></tr>
<tr><td>OVD link</td><td id="refKycLink"><?= !empty($kyc['link']) ? '<a href="'.esc($kyc['link']).'" target="_blank">Open</a>' : '—' ?></td></tr>
</table>
<div class="dm-actions">
<button class="dm-btn ghost" type="button" onclick="dmGo(1)"><i class="mdi mdi-arrow-left"></i> Back</button>
<div>
<button class="dm-btn" type="button" onclick="dmKycStatus()"><i class="mdi mdi-refresh"></i> Refresh KYC</button>
<button class="dm-btn primary" type="button" onclick="dmGo(3)">Proceed to payment <i class="mdi mdi-arrow-right"></i></button>
</div>
</div>
</div>
<!-- Step 3: Payment -->
<div class="dm-step" data-step="3">
<div class="dm-panel-head">
<div>
<div class="dm-eyebrow">Step 4 of 5 · payment</div>
<div class="dm-title">Collect premium</div>
<div class="dm-desc">Generates a Digit UI payment link for the customer to complete the transaction.</div>
</div>
<div class="dm-index">04</div>
</div>
<div class="dm-grid">
<div class="dm-field"><label>Premium amount</label><input id="payPremium" readonly value="<?= isset($quote['premium']) ? 'INR '.number_format((float)$quote['premium'], 2, '.', '') : '' ?>"></div>
<div class="dm-field"><label>Payment mode</label><input value="EB · Digit UI" readonly></div>
</div>
<div class="dm-status" style="margin-top:18px;" id="payStatusCard">
<div class="dm-icon wait"><i class="mdi mdi-link-variant"></i></div>
<div>
<div class="dm-st-title" id="payTitle"><?= !empty($payment['dispatcher_response']) ? 'Payment link ready' : 'No payment link yet' ?></div>
<div class="dm-st-sub" id="paySub"><?= esc($payment['digit_payment_id'] ?? 'Generate a link to continue') ?></div>
</div>
<div class="ml-auto">
<?php if (!empty($payment['dispatcher_response'])): ?>
<a class="dm-btn" id="payOpenBtn" href="<?= esc($payment['dispatcher_response']) ?>" target="_blank">Open link <i class="mdi mdi-open-in-new"></i></a>
<?php else: ?>
<a class="dm-btn" id="payOpenBtn" href="#" style="display:none;" target="_blank">Open link</a>
<?php endif; ?>
</div>
</div>
<div class="dm-actions">
<button class="dm-btn ghost" type="button" onclick="dmGo(2)"><i class="mdi mdi-arrow-left"></i> Back</button>
<div>
<button class="dm-btn" type="button" onclick="dmPaymentLink()"><i class="mdi mdi-link-variant"></i> Generate link</button>
<button class="dm-btn primary" type="button" onclick="dmPolicyStatus(); dmGo(4);">Check policy status <i class="mdi mdi-arrow-right"></i></button>
</div>
</div>
</div>
<!-- Step 4: Policy -->
<div class="dm-step" data-step="4">
<div class="dm-panel-head">
<div>
<div class="dm-eyebrow">Step 5 of 5 · policy</div>
<div class="dm-title">Policy status & documents</div>
<div class="dm-desc">Confirm payment outcome, then download schedule and proposal PDFs.</div>
</div>
<div class="dm-index">05</div>
</div>
<div class="dm-status" id="policyStatusCard">
<div class="dm-icon <?= in_array(strtoupper($policy['policy_status'] ?? $status), ['EFFECTIVE','COMPLETE','COMPLETED']) ? 'ok' : 'wait' ?>">
<i class="mdi mdi-file-check"></i>
</div>
<div>
<div class="dm-st-title" id="polTitle">
<?= esc(($policy['policy_number'] ?? $quote['quote_number'] ?? 'Policy') . ' · ' . ($policy['policy_status'] ?? $status)) ?>
</div>
<div class="dm-st-sub" id="polSub">Refresh status after customer payment, then fetch PDFs.</div>
</div>
<div class="ml-auto"><span class="dm-pill <?= in_array(strtoupper($policy['policy_status'] ?? ''), ['EFFECTIVE','COMPLETE']) ? 'ok' : 'wait' ?>" id="polPill"><?= esc($policy['policy_status'] ?? $status) ?></span></div>
</div>
<div class="dm-section">Documents</div>
<div class="dm-doc">
<div><i class="mdi mdi-file-pdf-box text-danger"></i> Policy schedule</div>
<a class="dm-btn" id="dlSchedule" href="<?= esc($policy['schedule_path'] ?? '#') ?>" target="_blank" <?= empty($policy['schedule_path']) ? 'style="pointer-events:none;opacity:.4"' : '' ?>>Download</a>
</div>
<div class="dm-doc">
<div><i class="mdi mdi-file-pdf-box text-danger"></i> Proposal form</div>
<a class="dm-btn" id="dlProposal" href="<?= esc($policy['proposal_path'] ?? '#') ?>" target="_blank" <?= empty($policy['proposal_path']) ? 'style="pointer-events:none;opacity:.4"' : '' ?>>Download</a>
</div>
<div class="dm-actions">
<button class="dm-btn ghost" type="button" onclick="dmGo(3)"><i class="mdi mdi-arrow-left"></i> Back</button>
<div>
<button class="dm-btn" type="button" onclick="dmPolicyStatus()"><i class="mdi mdi-refresh"></i> Refresh status</button>
<button class="dm-btn primary" type="button" onclick="dmPolicyPdf()"><i class="mdi mdi-download"></i> Fetch PDFs</button>
</div>
</div>
</div>
</div>
</div>
<script>
window.DM = {
quoteId: <?= $quoteId ? (int)$quoteId : 'null' ?>,
base: '<?= rtrim(base_url('digit-motor'), '/') ?>',
step: <?= (int)$initialStep ?>
};
const dmSteps = ["Quote","Create","KYC","Pay","Policy"];
const dmIcons = ["mdi-car","mdi-file-plus","mdi-shield-check","mdi-credit-card","mdi-file-check"];
(function initRoute(){
const stops = document.getElementById('dmStops');
dmSteps.forEach((label,i)=>{
const el = document.createElement('div');
el.className = 'dm-stop';
el.dataset.i = i;
el.innerHTML = '<div class="dm-dot"><i class="mdi '+dmIcons[i]+'"></i></div><div class="dm-stop-label">'+label+'</div>';
el.onclick = ()=> dmGo(i);
stops.appendChild(el);
});
dmGo(window.DM.step, true);
})();
function dmGo(i, silent){
window.DM.step = i;
document.querySelectorAll('.dm-step').forEach(v=> v.classList.toggle('active', Number(v.dataset.step)===i));
document.querySelectorAll('.dm-stop').forEach(s=>{
const n = Number(s.dataset.i);
s.classList.toggle('done', n < i);
s.classList.toggle('active', n === i);
});
// Progress fill between stop centers — dots sit above the line (no crossover)
const stops = document.querySelectorAll('.dm-stop');
const fill = document.getElementById('dmFill');
if (fill && stops.length > 1){
const first = stops[0].offsetLeft + stops[0].offsetWidth / 2;
const current = stops[i].offsetLeft + stops[i].offsetWidth / 2;
fill.style.left = first + 'px';
fill.style.width = Math.max(0, current - first) + 'px';
fill.style.maxWidth = 'none';
}
}
function dmToggleExclusive(el){
const g = el.dataset.group;
document.querySelectorAll('.dm-chip[data-group="'+g+'"]').forEach(c=> c.classList.remove('on'));
el.classList.add('on');
}
function dmAlert(msg, ok){
if (window.toastr) {
if (ok) {
toastr.success(msg, 'Success');
} else {
toastr.error(msg, 'Error');
}
return;
}
// Fallback if toastr is unavailable
const el = document.getElementById('dmAlert');
if (!el) { alert(msg); return; }
el.className = 'dm-alert ' + (ok ? 'ok' : 'error');
el.textContent = msg;
if (ok) setTimeout(function(){ el.className='dm-alert'; el.textContent=''; }, 4000);
}
function dmBusy(on){
document.getElementById('dmPanel').classList.toggle('dm-loading', !!on);
}
function dmAddonFlags(){
const flags = {};
document.querySelectorAll('#addonChips .dm-chip').forEach(c=>{
flags[c.dataset.key] = c.classList.contains('on');
});
return flags;
}
function dmPost(url, body){
return $.ajax({
url: url,
type: 'POST',
contentType: 'application/json',
dataType: 'json',
data: JSON.stringify(body || {})
});
}
function dmGet(url){
return $.ajax({ url: url, type: 'GET', dataType: 'json' });
}
function dmQuickQuote(){
const claimChip = document.querySelector('.dm-chip[data-group="claim"].on');
const body = {
license_plate_number: $('#f_reg').val().trim(),
vehicle_maincode: $('#f_vcode').val().trim(),
registration_date: $('#f_reg_date').val(),
manufacture_date: $('#f_mfg_date').val(),
vehicle_identification_number: $('#f_vin').val().trim(),
engine_number: $('#f_engine').val().trim(),
pincode: $('#f_pin').val().trim(),
insurance_product_code: $('#f_product').val(),
start_date: $('#f_start').val() || null,
end_date: null,
previous_insurer_code: $('#f_prev_ins').val() || null,
previous_policy_expiry_date: $('#f_prev_exp').val() || null,
is_claim_in_last_year: claimChip ? claimChip.dataset.val === '1' : false,
coverages: dmAddonFlags(),
enquiry_id: window.DM.quoteId ? ($('#enquiryIdTag').text().trim() !== '— new —' ? $('#enquiryIdTag').text().trim() : null) : null
};
if (body.start_date){
const d = new Date(body.start_date);
d.setFullYear(d.getFullYear()+1);
d.setDate(d.getDate()-1);
body.end_date = d.toISOString().slice(0,10);
}
dmBusy(true);
dmPost(window.DM.base + '/quotes/quick', body)
.done(function(res){
if (!res.status){ dmAlert(res.message || 'Quick quote failed'); return; }
window.DM.quoteId = res.data.quote_id;
$('#enquiryIdTag').text(res.data.enquiry_id);
const prem = res.data.premium != null ? Number(res.data.premium) : null;
const idv = res.data.idv != null ? Number(res.data.idv) : null;
$('#planPremium').text(prem != null ? '₹' + prem.toLocaleString('en-IN', {maximumFractionDigits:0}) : '—');
$('#planIdv').text(idv != null ? 'IDV ₹' + idv.toLocaleString('en-IN', {maximumFractionDigits:0}) : 'IDV —');
$('#payPremium').val(prem != null ? 'INR ' + prem.toFixed(2) : '');
dmAlert('Quick quote generated.', true);
history.replaceState(null, '', window.DM.base + '/journey/' + window.DM.quoteId);
dmGo(1);
})
.fail(function(xhr){
const msg = (xhr.responseJSON && xhr.responseJSON.message) || 'Quick quote failed.';
dmAlert(msg);
})
.always(function(){ dmBusy(false); });
}
function dmCreateQuote(){
if (!window.DM.quoteId){ dmAlert('Run quick quote first.'); return; }
const body = {
first_name: $('#f_fname').val().trim(),
last_name: $('#f_lname').val().trim(),
mobile: $('#f_mobile').val().trim(),
email: $('#f_email').val().trim(),
pan: $('#f_pan').val().trim(),
dob: $('#f_dob').val(),
address: $('#f_address').val().trim(),
start_date: $('#f_start').val() || null,
coverages: dmAddonFlags()
};
if (body.start_date){
const d = new Date(body.start_date);
d.setFullYear(d.getFullYear()+1);
d.setDate(d.getDate()-1);
body.end_date = d.toISOString().slice(0,10);
}
dmBusy(true);
dmPost(window.DM.base + '/quotes/' + window.DM.quoteId + '/create', body)
.done(function(res){
if (!res.status){ dmAlert(res.message || 'Create quote failed'); return; }
$('#refQuoteNo').text(res.data.quote_number || '—');
$('#refAppId').text(res.data.application_id || '—');
if (res.data.premium != null){
$('#payPremium').val('INR ' + Number(res.data.premium).toFixed(2));
$('#planPremium').text('₹' + Number(res.data.premium).toLocaleString('en-IN', {maximumFractionDigits:0}));
}
dmAlert('Quote created.', true);
dmGo(2);
})
.fail(function(xhr){
dmAlert((xhr.responseJSON && xhr.responseJSON.message) || 'Create quote failed.');
})
.always(function(){ dmBusy(false); });
}
function dmKycStatus(){
if (!window.DM.quoteId){ dmAlert('No quote loaded.'); return; }
dmBusy(true);
dmGet(window.DM.base + '/kyc/' + window.DM.quoteId)
.done(function(res){
if (!res.status){ dmAlert(res.message || 'KYC fetch failed'); return; }
const k = res.data.kyc || {};
const st = (res.data.status || k.kyc_verification_status || 'UNKNOWN').toString();
$('#kycTitle').text(st);
$('#kycPill').text(st).removeClass('ok wait').addClass(/done|verif|success|complete/i.test(st) ? 'ok' : 'wait');
$('#refKycRef').text(k.reference_id || '—');
$('#refKycLink').html(k.link ? '<a href="'+k.link+'" target="_blank">Open</a>' : '—');
$('#kycSub').text('Last checked just now.');
dmAlert('KYC status updated.', true);
})
.fail(function(xhr){
dmAlert((xhr.responseJSON && xhr.responseJSON.message) || 'KYC fetch failed.');
})
.always(function(){ dmBusy(false); });
}
function dmPaymentLink(){
if (!window.DM.quoteId){ dmAlert('No quote loaded.'); return; }
dmBusy(true);
dmPost(window.DM.base + '/payments/' + window.DM.quoteId + '/link', {})
.done(function(res){
if (!res.status){ dmAlert(res.message || 'Payment link failed'); return; }
const link = res.data.dispatcher_response;
const pay = res.data.payment || {};
$('#payTitle').text(link ? 'Payment link ready' : 'Link missing in response');
$('#paySub').text(pay.digit_payment_id || 'digit payment');
if (link){
$('#payOpenBtn').attr('href', link).show();
}
dmAlert('Payment link generated.', true);
})
.fail(function(xhr){
dmAlert((xhr.responseJSON && xhr.responseJSON.message) || 'Payment link failed.');
})
.always(function(){ dmBusy(false); });
}
function dmPolicyStatus(){
if (!window.DM.quoteId){ dmAlert('No quote loaded.'); return; }
dmBusy(true);
dmGet(window.DM.base + '/policies/' + window.DM.quoteId + '/status')
.done(function(res){
if (!res.status){ dmAlert(res.message || 'Policy status failed'); return; }
const p = res.data.policy || {};
const st = (res.data.status || p.policy_status || 'UNKNOWN').toString();
$('#polTitle').text((p.policy_number || 'Policy') + ' · ' + st);
$('#polPill').text(st).removeClass('ok wait').addClass(/effective|complete|active/i.test(st) ? 'ok' : 'wait');
dmAlert('Policy status updated.', true);
})
.fail(function(xhr){
dmAlert((xhr.responseJSON && xhr.responseJSON.message) || 'Policy status failed.');
})
.always(function(){ dmBusy(false); });
}
function dmPolicyPdf(){
if (!window.DM.quoteId){ dmAlert('No quote loaded.'); return; }
dmBusy(true);
dmGet(window.DM.base + '/policies/' + window.DM.quoteId + '/pdf')
.done(function(res){
if (!res.status){ dmAlert(res.message || 'PDF fetch failed'); return; }
const p = res.data.policy || {};
if (p.schedule_path){
$('#dlSchedule').attr('href', p.schedule_path).css({pointerEvents:'auto',opacity:1});
}
if (p.proposal_path){
$('#dlProposal').attr('href', p.proposal_path).css({pointerEvents:'auto',opacity:1});
}
dmAlert('PDF links updated.', true);
})
.fail(function(xhr){
dmAlert((xhr.responseJSON && xhr.responseJSON.message) || 'PDF fetch failed.');
})
.always(function(){ dmBusy(false); });
}
window.addEventListener('resize', function(){ dmGo(window.DM.step, true); });
</script>