diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 3daa030c..3e18c556 100755 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -871,6 +871,7 @@ $routes->group("/bdsReport", ["filter" => "authMVC"], function ($routes) { //New Tickets $routes->group("/ticket", ["filter" => "authMVC"], function ($routes) { $routes->match( ['get', 'post'], 'list','TicketController::ticketList'); + $routes->post('export','TicketController::exportTickets'); $routes->get('feedback-list','TicketController::feedbackList'); $routes->match(['get', 'post'], 'claim-upload','TicketController::claimDumpUpload'); $routes->get('remove','TicketController::removeTicket'); diff --git a/app/Controllers/LeadsController.php b/app/Controllers/LeadsController.php index e83c0ea2..1c1b154a 100644 --- a/app/Controllers/LeadsController.php +++ b/app/Controllers/LeadsController.php @@ -463,19 +463,31 @@ class LeadsController extends BaseController // If an existing client is selected from the dropdown, `client_id` will be present. if (empty($postData['client_id'])) { $ignoreClientId = null; + $lead = null; if ($id) { // This is an update of a lead $lead = $this->leadsModel->find($id); - if ($lead && !empty($lead['is_client_created'])) { - $ignoreClientId = $lead['is_client_created']; + if ($lead && ! empty($lead['is_client_created'])) { + $ignoreClientId = (int) $lead['is_client_created']; } } + $submittedName = trim((string) ($postData['client_name'] ?? '')); + $submittedShort = trim((string) ($postData['client_short_name'] ?? '')); + $nameUnchanged = $lead && strcasecmp(trim((string) ($lead['client_name'] ?? '')), $submittedName) === 0; + $shortUnchanged = $lead && strcasecmp(trim((string) ($lead['client_short_name'] ?? '')), $submittedShort) === 0; + if ($ignoreClientId) { $clientNameRules .= '|is_unique[clients.client_name,id,' . $ignoreClientId . ']'; $clientShortNameRules .= '|is_unique[clients.short_name,id,' . $ignoreClientId . ']'; } else { - $clientNameRules .= '|is_unique[clients.client_name]'; - $clientShortNameRules .= '|is_unique[clients.short_name]'; + // On edit, unchanged names belong to this opportunity — do not treat as duplicates. + // (Client row may already exist even when is_client_created is unset.) + if (! $nameUnchanged) { + $clientNameRules .= '|is_unique[clients.client_name]'; + } + if (! $shortUnchanged) { + $clientShortNameRules .= '|is_unique[clients.short_name]'; + } } } @@ -768,6 +780,15 @@ class LeadsController extends BaseController $data['client_id'] = 0; $data['client_branch_id'] = 0; $data['source_policy_id'] = 0; + + // Keep existing client link on update when "Existing Client" is off. + // Otherwise is_client_created is wiped to null and later edits fail uniqueness checks. + if (! empty($data['id'])) { + $existingLead = $this->leadsModel->find($data['id']); + if ($existingLead && ! empty($existingLead['is_client_created'])) { + $data['is_client_created'] = $existingLead['is_client_created']; + } + } } } @@ -4168,7 +4189,7 @@ class LeadsController extends BaseController $data = $this->leadsModel->where('leads.id', $lead_id)->where('leads.is_active', 1)->first(); if (! $data) { - $this->myLogger->logme('featchLeadDataAndInsertClient', "Lead not found or inactive", ['lead_id' => $lead_id]); + $this->myLogger->logme('error', 'featchLeadDataAndInsertClient: Lead not found or inactive', ['lead_id' => $lead_id]); return $this->respond(['status' => false, 'message' => 'Failed to create Client', 'data' => null], 200); } @@ -4186,10 +4207,10 @@ class LeadsController extends BaseController ], 200); } - $this->myLogger->logme('featchLeadDataAndInsertClient', "Client creation failed after processing", ['lead_id' => $lead_id]); + $this->myLogger->logme('error', 'featchLeadDataAndInsertClient: Client creation failed after processing', ['lead_id' => $lead_id]); return $this->respond(['status' => false, 'message' => 'Failed to create client', 'data' => $data], 200); } catch (\Throwable $e) { - $this->myLogger->logme('error', 'featchLeadDataAndInsertClient failed'); + $this->myLogger->logme('error', 'featchLeadDataAndInsertClient failed: ' . $e->getMessage()); return $this->respond(['status' => false, 'message' => 'Internal server error', 'error' => $e->getMessage()], 500); } } @@ -4210,17 +4231,40 @@ class LeadsController extends BaseController return false; } - $client_id = $this->clientModel->insert($this->prepareClientData($data)); - if (! $client_id) { - return false; - } + // Reuse client/branch from a previous partial attempt (client created, policy failed). + $existingClientId = (int) ($data['is_client_created'] ?? 0); + if ($existingClientId > 0) { + $client_id = $existingClientId; - $branch_id = $this->clientBranchModel->insert($this->prepareClientBranchData($data, $client_id)); - if (! $branch_id) { - return false; - } + $existingBranch = $this->clientBranchModel + ->where('client_id', $client_id) + ->where('branch_name', $data['branch_name'] ?? null) + ->where('branch_code', $data['branch_code'] ?? null) + ->where('is_active', 1) + ->first(); - $this->levelContactModel->insert($this->prepareContactData($data, $branch_id)); + if ($existingBranch) { + $branch_id = (int) $existingBranch['id']; + } else { + $branch_id = $this->clientBranchModel->insert($this->prepareClientBranchData($data, $client_id)); + if (! $branch_id) { + return false; + } + $this->levelContactModel->insert($this->prepareContactData($data, $branch_id)); + } + } else { + $client_id = $this->clientModel->insert($this->prepareClientData($data)); + if (! $client_id) { + return false; + } + + $branch_id = $this->clientBranchModel->insert($this->prepareClientBranchData($data, $client_id)); + if (! $branch_id) { + return false; + } + + $this->levelContactModel->insert($this->prepareContactData($data, $branch_id)); + } $policy_ids = []; $lead_ids = []; @@ -4258,7 +4302,7 @@ class LeadsController extends BaseController 'lead_ids' => $lead_ids, ]; } catch (\Throwable $e) { - $this->myLogger->logme('error', 'createClientWithMatchingLeadsPolicies failed'); + $this->myLogger->logme('error', 'createClientWithMatchingLeadsPolicies failed: ' . $e->getMessage()); return false; } } @@ -4349,7 +4393,7 @@ class LeadsController extends BaseController return $client_policy_id; } catch (\Throwable $e) { - $this->myLogger->logme('error', 'createClientPolicyWithLeadData failed'); + $this->myLogger->logme('error', 'createClientPolicyWithLeadData failed: ' . $e->getMessage() . ' at ' . $e->getFile() . ':' . $e->getLine()); return false; } } @@ -4607,9 +4651,20 @@ class LeadsController extends BaseController //Function for convert the RFQ and QCR Json to Policy Terms Json public function convertQCRJsonToPolicyTerms($data, $policy_type, $proposel_name, $insurer_name) { + // Extract special conditions from original QCR data first. + // transformProposelData drops the Quote Asked column, which is the label source. + $specialConditionTerms = $this->extractSpecialConditionsFromQCR( + $data, + $policy_type, + $proposel_name, + $insurer_name + ); + //transform the data into the currernt proposel and insurere ( get single proposel ) $data = $this->transformProposelData($data, $proposel_name, $insurer_name); + $terms_array = $specialConditionTerms; + // Initialize age_ratio based on policy type $age_ratio = $policy_type == 2 ? [ 'self' => ['min' => '18', 'max' => '60'], @@ -4641,21 +4696,14 @@ class LeadsController extends BaseController continue; } + // Special conditions already extracted above. + if (str_starts_with((string) $item, 'special_condition')) { + continue; + } + switch (true) { - // CASE 1: Handle special conditions - case str_starts_with($item, 'special_condition') && $parentth === $proposel_name && $subth === $insurer_name: - // log_message("error","INPUT".json_encode($input_value)); - $parts = explode('-', $input_value); - - $labelKey = $policy_type == 1 ? 'gpa_special_condition_label' : 'special_condition_label'; - $inputKey = $policy_type == 1 ? 'gpa_special_condition_input' : 'special_condition_input'; - - $terms_array[$labelKey][] = $parts[0] ?? ''; - $terms_array[$inputKey][] = $parts[1] ?? ''; - break; - - // CASE 2: Handle sum insured + // CASE 1: Handle sum insured case in_array($item, ['sum_insured', 'sumInsured2']): $si_amt = explode(',', $value); $terms_array[$item] = $si_amt[0] ?? ''; @@ -4668,7 +4716,7 @@ class LeadsController extends BaseController break; - // CASE 3: Handle family floaters + // CASE 2: Handle family floaters case $item === 'family_composition': $terms_array['family_floaters'] = isJsonString($input_value) ? json_decode($input_value, true) : $input_value; @@ -4691,6 +4739,138 @@ class LeadsController extends BaseController return json_encode($terms_array); } + /** + * Build special_condition_label/input arrays from QCR rows. + * + * Mapping: + * - Canonical RFQ hidden format "label--value" (or display "label - value") on insurer cell + * - Otherwise Quote Asked = label, insurer cell = value + * + * @return array> + */ + private function extractSpecialConditionsFromQCR($data, $policy_type, $proposel_name, $insurer_name): array + { + $labelKey = $policy_type == 1 ? 'gpa_special_condition_label' : 'special_condition_label'; + $inputKey = $policy_type == 1 ? 'gpa_special_condition_input' : 'special_condition_input'; + $result = [ + $labelKey => [], + $inputKey => [], + ]; + + if (empty($data['table_data']['data']) || ! is_array($data['table_data']['data'])) { + return []; + } + + foreach ($data['table_data']['data'] as $dataRow) { + $item = $dataRow['items'] ?? ''; + if (! str_starts_with((string) $item, 'special_condition')) { + continue; + } + + $quoteAsked = ''; + $insurerDisplay = ''; + $insurerInput = ''; + + foreach ($dataRow['data'] ?? [] as $cellData) { + $parentth = $cellData['parentth'] ?? ''; + $subth = $cellData['subth'] ?? ''; + + if ($parentth !== $proposel_name) { + continue; + } + + $cellInput = isset($cellData['input_value']) && $cellData['input_value'] !== '' + ? $cellData['input_value'] + : ($cellData['value'] ?? ''); + $cellValue = $cellData['value'] ?? ''; + + if ($subth === 'Quote Asked') { + $quoteAsked = $this->normalizeSpecialConditionCellText($cellValue, $cellInput); + } + + if ($subth === $insurer_name) { + $insurerDisplay = is_string($cellValue) ? trim($cellValue) : ''; + $insurerInput = is_string($cellInput) ? trim((string) $cellInput) : ''; + } + } + + [$label, $input] = $this->parseSpecialConditionParts($insurerInput, $insurerDisplay, $quoteAsked); + + if ($label === '' && $input === '') { + continue; + } + + $result[$labelKey][] = $label; + $result[$inputKey][] = $input; + } + + if ($result[$labelKey] === [] && $result[$inputKey] === []) { + return []; + } + + return $result; + } + + /** + * Prefer visible cell text; fall back to hidden input_value. + */ + private function normalizeSpecialConditionCellText($display_value, $input_value): string + { + $display = is_string($display_value) ? trim($display_value) : ''; + if ($display !== '') { + return $display; + } + + return is_string($input_value) ? trim($input_value) : ''; + } + + /** + * Parse RFQ/QCR special-condition cells into [label, value]. + * + * @return array{0: string, 1: string} + */ + private function parseSpecialConditionParts($input_value, $display_value = '', $quote_asked = ''): array + { + // Prefer canonical hidden "label--value"; otherwise prefer visible insurer text + // (QCR edits often update the cell text but leave the hidden input stale/empty). + $raw = ''; + if (is_string($input_value) && str_contains($input_value, '--')) { + $raw = trim($input_value); + } elseif (is_string($display_value) && trim($display_value) !== '') { + $raw = trim($display_value); + } elseif (is_string($input_value)) { + $raw = trim($input_value); + } + + $quoteAsked = is_string($quote_asked) ? trim($quote_asked) : ''; + + if ($raw === '' && $quoteAsked === '') { + return ['', '']; + } + + if ($raw !== '' && str_contains($raw, '--')) { + $parts = explode('--', $raw, 2); + return [trim($parts[0] ?? ''), trim($parts[1] ?? '')]; + } + + if ($raw !== '' && str_contains($raw, ' - ')) { + $parts = explode(' - ', $raw, 2); + return [trim($parts[0] ?? ''), trim($parts[1] ?? '')]; + } + + // QCR columns: Quote Asked = label, insurer response = value + if ($quoteAsked !== '' && $raw !== '') { + return [$quoteAsked, $raw]; + } + + if ($quoteAsked !== '') { + return [$quoteAsked, $quoteAsked]; + } + + // Single insurer cell with no Quote Asked / separator: use as label and value + return [$raw, $raw]; + } + public function convertNonEbQCRJsonToPolicyTerms($allTableData) { @@ -4747,6 +4927,14 @@ class LeadsController extends BaseController public function getPlacementJson(array $data): ?string { $proposel_data = json_decode($data['proposel_data'] ?? '', true) ?? []; + $proposelName = $proposel_data['proposel_name'] ?? ''; + $insurerName = $proposel_data['insurer_name'] ?? ''; + + // Non-EB placement JSON is filtered by the won proposal/insurer selection. + // Without that selection, skip placement terms rather than throwing. + if ($proposelName === '' || $insurerName === '') { + return null; + } $QCRData = $this->RFQModel ->where('is_active', 1) @@ -4764,26 +4952,26 @@ class LeadsController extends BaseController return null; } - $placement_json_data = array_map(function ($jsonData) use ($proposel_data) { + $placement_json_data = array_map(function ($jsonData) use ($proposelName, $insurerName) { return $this->transformNonEbProposelData( $jsonData, - $proposel_data['proposel_name'] ?? '', - $proposel_data['insurer_name'] ?? '', + $proposelName, + $insurerName, null, 1 ); }, $jsonArray); - if (! empty($proposalData)) { + if (! empty($proposalData['proposal_data']['over_all_column_data']) && is_array($proposalData['proposal_data']['over_all_column_data'])) { foreach ($proposalData['proposal_data']['over_all_column_data'] as $key => &$proposal) { // Keep only the required proposal - if ($key !== $proposel_data['proposel_name']) { + if ($key !== $proposelName) { unset($proposalData['proposal_data']['over_all_column_data'][$key]); } else { // Within the matched proposal, filter insurers if (! empty($proposal['insurers'])) { - $proposal['insurers'] = array_values(array_filter($proposal['insurers'], function ($insurer) use ($proposel_data) { - return $insurer['display_name'] === $proposel_data['insurer_name']; + $proposal['insurers'] = array_values(array_filter($proposal['insurers'], function ($insurer) use ($insurerName) { + return ($insurer['display_name'] ?? null) === $insurerName; })); } } diff --git a/app/Controllers/TicketController.php b/app/Controllers/TicketController.php index c549be78..cb197432 100644 --- a/app/Controllers/TicketController.php +++ b/app/Controllers/TicketController.php @@ -461,6 +461,155 @@ class TicketController extends BaseController } } + public function exportTickets() + { + $format = $this->request->getPost('export_format') ?? 'csv'; + $rows = $this->ticketSearch(); // uses same POST filters as the list + + // Restrict to DataTables-visible rows when client sends export_ids + if ($this->request->getPost('export_ids') !== null) { + $exportIds = array_values(array_filter(array_map('intval', explode(',', (string) $this->request->getPost('export_ids'))))); + if (empty($exportIds)) { + $rows = []; + } else { + $idOrder = array_flip($exportIds); + $rows = array_values(array_filter($rows, function ($row) use ($idOrder) { + return isset($idOrder[(int) ($row['id'] ?? 0)]); + })); + usort($rows, function ($a, $b) use ($idOrder) { + return ($idOrder[(int) $a['id']] ?? 0) <=> ($idOrder[(int) $b['id']] ?? 0); + }); + } + } + + $headers = [ + 'Created At', 'Updated At', 'Status', 'Policy Type', 'Claim Number', 'TPA ID', 'Emp ID', 'Emp Name', + 'Insured Name', 'Corporate Name', 'ACM', 'Insurer', 'TPA', 'Policy No', + 'Priority', 'Relationship', 'Emp Mobile', 'Emp Email', 'Emp Personal Email', + 'Mode of Intimation', 'Claim Type', 'Claim Category', 'Hospital Name', 'DOA', 'DOD', + 'Claim Amount', 'POD No.', 'Date of Join', 'Date of Inception', 'DOB', + 'Date of Accident', 'Date of Death', 'Date of Intimation', 'Sum Insured', + 'Raised Date', 'Registration Date', 'Query Received Date', 'Denial Date', + 'Approved Date', 'Settled Date', 'Denial Reason', 'Approved Letter', + 'Approved Amount', 'UTR Details', 'Settle Letter', 'Return Remark', + 'Cancel Remark', 'AWB No & Courier Name', 'Non ID Reason', + 'Payment Initiate Date', 'Approved Description', 'TAT', 'Claim Created By', + ]; + + $claimTypeMap = $this->claimType; + $priorityMap = $this->priorityType; + $modeMap = $this->modeOFIntimate; + $ticketTypeMap = $this->ticketType; + + $buildRow = function (array $row) use ($claimTypeMap, $priorityMap, $modeMap, $ticketTypeMap): array { + $typeId = (int) ($row['ticket_type_id'] ?? 0); + $claimKey = $row['claim_type'] ?? ''; + $claimTypeLabel = $claimTypeMap[$typeId][$claimKey] ?? ''; + $claimCreatedBy = strtoupper((string) ($row['claim_created_by'] ?? '')); + $claimCreatedByLabel = $claimCreatedBy === 'CRM' ? 'STAFF' : $claimCreatedBy; + $claimCategory = trim((string) ($row['tpa_claim_type'] ?? '')); + + return [ + $row['ticket_created_date'] ?? '', + $row['ticket_updated_date'] ?? '', + $row['status'] ?? '', + str_replace('Claim-', '', $ticketTypeMap[$typeId] ?? ''), + $row['claim_no'] ?? '', + $row['tpa_no'] ?? '', + $row['emp_code'] ?? '', + $row['emp_name'] ?? '', + $row['insured_name'] ?? '', + $row['short_name'] ?? '', + $row['acm_name'] ?? '', + $row['insurer_name'] ?? '', + $row['tpa_name'] ?? '', + $row['policy_no'] ?? '', + $priorityMap[$row['priority'] ?? 0] ?? '', + $row['relationship'] ?? '', + $row['emp_mobile'] ?? '', + $row['emp_mail'] ?? '', + $row['emp_personal_mail'] ?? '', + $modeMap[$row['mode_of_intimation'] ?? ''] ?? '', + $claimTypeLabel, + $claimCategory, + $row['hospital_name'] ?? '', + $row['doa'] ?? '', + $row['dod'] ?? '', + $row['claim_amount'] ?? '', + $row['pod_no'] ?? '', + $row['date_of_join'] ?? '', + $row['date_of_incep'] ?? '', + $row['dob'] ?? '', + $row['date_of_accident'] ?? '', + $row['date_of_death'] ?? '', + $row['date_of_intimat'] ?? '', + $row['si_amt'] ?? '', + $row['raised_date'] ?? '', + $row['registration_date'] ?? '', + $row['query_received_date'] ?? '', + $row['denial_date'] ?? '', + $row['approved_date'] ?? '', + $row['settled_date'] ?? '', + $row['denial_reason'] ?? '', + $row['approved_letter'] ?? '', + $row['approved_amount'] ?? '', + $row['utr_details'] ?? '', + $row['settle_letter'] ?? '', + $row['return_remark'] ?? '', + $row['cancel_remark'] ?? '', + $row['awb_no_courier_name'] ?? '', + $row['non_id_reason'] ?? '', + $row['pay_initiate_date'] ?? '', + $row['approved_description'] ?? '', + $row['tat'] ?? '', + $claimCreatedByLabel, + ]; + }; + + $filename = 'Claim-List-' . date('Y-m-d'); + + if ($format === 'excel') { + $spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + $sheet->setTitle('Claim List'); + + $sheet->fromArray([$headers], null, 'A1'); + $rowIndex = 2; + foreach ($rows as $row) { + $sheet->fromArray([$buildRow($row)], null, 'A' . $rowIndex); + $rowIndex++; + } + + // Bold header row + $lastCol = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex(count($headers)); + $sheet->getStyle('A1:' . $lastCol . '1')->getFont()->setBold(true); + + $writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet); + header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); + header('Content-Disposition: attachment; filename="' . $filename . '.xlsx"'); + header('Cache-Control: max-age=0'); + ob_end_clean(); + $writer->save('php://output'); + exit; + } + + // Default: CSV + header('Content-Type: text/csv; charset=UTF-8'); + header('Content-Disposition: attachment; filename="' . $filename . '.csv"'); + header('Cache-Control: max-age=0'); + ob_end_clean(); + + $out = fopen('php://output', 'w'); + // UTF-8 BOM for Excel compatibility + fwrite($out, "\xEF\xBB\xBF"); + fputcsv($out, $headers); + foreach ($rows as $row) { + fputcsv($out, $buildRow($row)); + } + fclose($out); + exit; + } + public function ticketSearch($action = null) { $db = db_connect(); @@ -620,6 +769,12 @@ class TicketController extends BaseController // print_r($search_data); die() $where = []; foreach ($search_data as $search_objects => $key) { + if (stripos((string)$search_objects, 'csrf') !== false) { + continue; + } + if ($search_objects === 'export_format' || $search_objects === 'export_ids') { + continue; + } if ($key != null && $key != '' && $key != 0 && $search_objects != 'is_dashboard') { if($search_objects == "date_type" && $search_data[$search_objects] === 'ticket_created_date'){ @@ -730,6 +885,15 @@ class TicketController extends BaseController } } + $exportIdsRaw = $this->request->getPost('export_ids'); + if ($exportIdsRaw !== null) { + $exportIds = array_values(array_filter(array_map('intval', explode(',', (string) $exportIdsRaw)))); + if (empty($exportIds)) { + return []; + } + $query->whereIn('tm.id', $exportIds); + } + $data = $query->get()->getResultArray(); // dd($db->getLastQuery()->getQuery());die; // dd($data); diff --git a/app/Controllers/VidalApiController.php b/app/Controllers/VidalApiController.php index f754a7fd..ce9b27c3 100644 --- a/app/Controllers/VidalApiController.php +++ b/app/Controllers/VidalApiController.php @@ -113,7 +113,7 @@ class VidalApiController extends BaseController } if ($httpCode !== 200 && $httpCode !== 201) { - tpa_claim_push_log($claimId, "VIDAL - Claim Push | File upload failed with HTTP Code: $httpCode | Response: $uploadResponse"); + tpa_claim_push_log($claimId, "VIDAL - Claim Push | File upload failed with HTTP Code: $httpCode | URL: $signedUrl | Request Body: $payload | Response: $uploadResponse"); return [ "status" => false, "message" => "File upload failed", diff --git a/app/Views/ticket_list.php b/app/Views/ticket_list.php index fe1c1cf4..d77cead0 100644 --- a/app/Views/ticket_list.php +++ b/app/Views/ticket_list.php @@ -30,6 +30,15 @@ table.dataTable tbody td { color: #6c757d !important; } +.emp-name-cell { + font-weight: 500; +} + +.tpa-id-truncate { + cursor: default; + white-space: nowrap; +} + .policy-type-icon { display: inline-flex; align-items: center; @@ -93,6 +102,38 @@ table.dataTable tbody td { white-space: nowrap; } +#scroll-horizontal-datatable_wrapper .dt-buttons { + position: relative; +} + +#scroll-horizontal-datatable_wrapper .dt-button-collection { + z-index: 9999 !important; +} + +#scroll-horizontal-datatable_wrapper .ticket-backend-export-menu { + display: none; + background: #00999E; + border: 1px solid #00999E; + border-radius: .25rem; + box-shadow: 0 0.5rem 1rem rgba(0,0,0,.15); + padding: .25rem 0; +} + +#scroll-horizontal-datatable_wrapper .ticket-backend-export-menu .dropdown-item { + display: block; + width: 100%; + padding: .4rem .9rem; + clear: both; + color: #fff; + text-decoration: none; + white-space: nowrap; +} + +#scroll-horizontal-datatable_wrapper .ticket-backend-export-menu .dropdown-item:hover { + background-color: #007A7E; + color: #fff; +} + @@ -1150,7 +1156,79 @@ placeholder: "Start typing here...", // Optional placeholder text }; + var RFQ_MODAL_IDS = [ + 'list_model', + 'internal_mail_modal', + 'placement_mail_modal', + 'view_demography_modal', + 'other_docs_upload_modal' + ]; + + function relocateRfqModalsToBody() { + RFQ_MODAL_IDS.forEach(function (modalId) { + var modalEl = document.getElementById(modalId); + if (modalEl && modalEl.parentElement !== document.body) { + document.body.appendChild(modalEl); + } + }); + } + + function clearOrphanedModalBackdrops() { + document.querySelectorAll('.modal-backdrop').forEach(function (el) { + el.remove(); + }); + document.body.classList.remove('modal-open'); + document.body.style.removeProperty('padding-right'); + } + + /** + * Open a Bootstrap modal reliably on this page: + * - moves it under (escapes #wrapper overflow:hidden) + * - prefers Bootstrap 4 window.bootstrap.Modal from vendor.min.js + * - reuses one instance per element + */ + function openBootstrapModal(modalId) { + var modalEl = document.getElementById(modalId); + if (!modalEl) { + console.error('Modal not found:', modalId); + return null; + } + + if (modalEl.parentElement !== document.body) { + document.body.appendChild(modalEl); + } + + clearOrphanedModalBackdrops(); + + if (typeof bootstrap !== 'undefined' && bootstrap.Modal) { + var existing = (typeof jQuery !== 'undefined') ? jQuery(modalEl).data('bs.modal') : null; + if (existing && typeof existing.show === 'function') { + existing.show(); + return existing; + } + var modal = new bootstrap.Modal(modalEl); + if (typeof jQuery !== 'undefined') { + jQuery(modalEl).data('bs.modal', modal); + } + modal.show(); + return modal; + } + + if (typeof jQuery !== 'undefined' && jQuery.fn && typeof jQuery.fn.modal === 'function') { + jQuery(modalEl).modal('show'); + // BS3 uses .in while theme CSS expects .show + modalEl.classList.add('show'); + modalEl.style.display = 'block'; + modalEl.removeAttribute('aria-hidden'); + return null; + } + + console.error('No compatible Bootstrap modal API found for', modalId); + return null; + } + $(document).ready(function() { + relocateRfqModalsToBody(); const path = window.location.pathname; // Get the current URL path const segments = path.split('/'); // Split the path into segments @@ -4027,8 +4105,7 @@ function showModal(mail_type) { }else if(mail_type == 2){ - var myModal = new bootstrap.Modal(document.getElementById('internal_mail_modal')); - myModal.show(); + openBootstrapModal('internal_mail_modal'); // getMailContent('internal_mail_content'); @@ -4057,8 +4134,7 @@ function insurerAndClientMailPopUp(){ ajaxRequestForGetMailData(url); - var myModal = new bootstrap.Modal(document.getElementById('list_model')); - myModal.show(); + openBootstrapModal('list_model'); } function placementMailPopUp() { @@ -4093,8 +4169,7 @@ function placementMailPopUp() { } // Show the modal - const myModal = new bootstrap.Modal(document.getElementById('placement_mail_modal')); - myModal.show(); + openBootstrapModal('placement_mail_modal'); } function ajaxRequestForGetMailData(url) { @@ -7354,10 +7429,9 @@ function appendMultiFileData(data) { // } function viewDocs(){ - // Show the modal - const myModal = new bootstrap.Modal(document.getElementById('other_docs_upload_modal')); - myModal.show(); + openBootstrapModal('other_docs_upload_modal'); } + window.viewDocs = viewDocs; @@ -7846,8 +7920,7 @@ function appendMultiFileData(data) { } // Show the modal - const myModal = new bootstrap.Modal(document.getElementById('view_demography_modal')); - myModal.show(); + openBootstrapModal('view_demography_modal'); // Hide loader $('.loader').fadeOut(); @@ -7857,8 +7930,7 @@ function appendMultiFileData(data) { $('#demography_modal_body').append("

No Demography Data Found or Wrong File

") // Show the modal - const myModal = new bootstrap.Modal(document.getElementById('view_demography_modal')); - myModal.show(); + openBootstrapModal('view_demography_modal'); console.error('Error fetching data:', error); console.error(xhr.responseText);