Merge branch 'dev' of bitbucket.org:jubilian/nhance into dev
This commit is contained in:
commit
6681d6eb98
@ -214,6 +214,7 @@ $routes->group("/employee", ["filter" => "authMVC"], function ($routes) {
|
||||
$routes->get("retail-endorsement-list", "EmployeeController::retailendorsementlist");
|
||||
$routes->post("retail-endorsement-save", "EmployeeController::retailendorsementsave");
|
||||
$routes->get("getTPADataVariationReport/(:num)", "EmployeeController::getTPADataVariationReport/$1");
|
||||
$routes->get("bulkGenerateEcardAndStoreinS3", "EmployeeController::bulkGenerateEcardAndStoreinS3");
|
||||
});
|
||||
|
||||
|
||||
@ -509,6 +510,8 @@ $routes->get('cli/sendZeptoMail', 'MasterController::testZeptoSMTP');
|
||||
$routes->cli('cli/processjob', 'JobWorker::processJob');
|
||||
$routes->cli('cli/processjobs', 'JobWorker::processJobs');
|
||||
|
||||
$routes->cli('cli/bulkGenerateEcardAndStoreinS3', 'EmployeeController::bulkGenerateEcardAndStoreinS3');
|
||||
|
||||
$routes->get("processjob", "JobWorker::processJob");
|
||||
$routes->cli('cli/new_gmail_token', 'MasterController::generateNewGmailAPIToken');
|
||||
$routes->cli('cli/send_mail_cli', 'MasterController::testGmailAPIViaCLI');
|
||||
|
||||
@ -9,6 +9,7 @@ use App\Libraries\GmailAPI;
|
||||
use App\Libraries\MyGoogleDrive;
|
||||
use App\Libraries\RuleImportService;
|
||||
use App\Libraries\DataServiceSqlite;
|
||||
use App\Libraries\S3Service;
|
||||
use App\Controllers\Home;
|
||||
|
||||
/**
|
||||
@ -90,5 +91,14 @@ class Services extends BaseService
|
||||
|
||||
return new RuleImportService();
|
||||
}
|
||||
|
||||
public static function getS3Service($getShared = true)
|
||||
{
|
||||
if ($getShared) {
|
||||
return static::getSharedInstance('getS3Service');
|
||||
}
|
||||
|
||||
return new S3Service();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -92,13 +92,25 @@ class ApiServiceController extends BaseController
|
||||
|
||||
}
|
||||
|
||||
// $employee_policy = $this->employeePolicyModel
|
||||
// ->select('employees.*, employee_polices.tpa_id , employee_polices.rand_string , employee_polices.uhid as uhid , client_policy.tpa_id as tpa_primary_id ')
|
||||
// ->join('employees', 'employee_polices.employee_id = employees.id', 'left')
|
||||
// ->join('client_policy', 'employee_polices.client_policy_id = client_policy.id', 'left')
|
||||
// ->where('employee_polices.employee_id',$id)
|
||||
// ->where('employee_polices.client_policy_id',$client_policy_id)
|
||||
// ->where('employee_polices.is_active', 1 )->findAll();
|
||||
|
||||
$employee_policy = $this->employeePolicyModel
|
||||
->select('employees.*, employee_polices.tpa_id , employee_polices.rand_string , employee_polices.uhid as uhid , client_policy.tpa_id as tpa_primary_id ')
|
||||
->join('employees', 'employee_polices.employee_id = employees.id', 'left')
|
||||
->join('client_policy', 'employee_polices.client_policy_id = client_policy.id', 'left')
|
||||
->where('employee_polices.employee_id',$id)
|
||||
->where('employee_polices.client_policy_id',$client_policy_id)
|
||||
->where('employee_polices.is_active', 1 )->findAll();
|
||||
->where('employees.emp_code', $emp_code)
|
||||
->where('employee_polices.client_policy_id', $client_policy_id)
|
||||
->where('employee_polices.is_active', 1)
|
||||
->where('employees.is_active', 1)
|
||||
->where('employee_polices.status', 'active')
|
||||
->where('employees.emp_status', 'active')
|
||||
->findAll();
|
||||
|
||||
|
||||
if(count($employee_policy) > 0)
|
||||
|
||||
@ -2726,7 +2726,9 @@ class ClientController extends AdminController
|
||||
$search_term = 'GMC';
|
||||
} else if (preg_match($gpa_pattern, $subject) || $policy_type_id == 6 || $policy_type_id == 7) {
|
||||
$search_term = 'GPA';
|
||||
} else {
|
||||
} else if($policy_type_id == 72){
|
||||
$search_term = 'GMC';
|
||||
}else {
|
||||
$search_term = "";
|
||||
}
|
||||
|
||||
@ -2736,7 +2738,7 @@ class ClientController extends AdminController
|
||||
if ($search_term === 'GPA' || $policy_type_id == 6 || $policy_type_id == 7) {
|
||||
|
||||
$premiumData = $this->policyPremium1Model->where(['client_id' => $record['client_id'], 'client_policy_id' => $client_policy_id, 'is_active' => 1])->findAll();
|
||||
} else if ($search_term === 'GMC') {
|
||||
} else if ($search_term === 'GMC' || $policy_type_id == 72) {
|
||||
|
||||
$premiumData = $this->policyPremium2Model->where(['client_id' => $record['client_id'], 'client_policy_id' => $client_policy_id, 'is_active' => 1])->findAll();
|
||||
|
||||
@ -2766,7 +2768,7 @@ class ClientController extends AdminController
|
||||
// echo '<pre>';
|
||||
// echo $family_floater;
|
||||
|
||||
if ($search_term === 'GMC') {
|
||||
if ($search_term === 'GMC' || $policy_type_id == 72 ) {
|
||||
|
||||
// $resultss = [];
|
||||
$resultss = $results;
|
||||
@ -3400,6 +3402,7 @@ class ClientController extends AdminController
|
||||
[
|
||||
'Status' => true,
|
||||
'code' => 200,
|
||||
'policy_type_id' => $record['policy_type_id'],
|
||||
'data' => $record['policy_terms'],
|
||||
'policy_addon' => $record['is_addon'],
|
||||
'emp_count_by_policy' => $emp_count_by_policy
|
||||
@ -4225,34 +4228,41 @@ class ClientController extends AdminController
|
||||
$client_policy_id = $this->request->getPost("client_policy_id");
|
||||
$policy_terms = $this->request->getPost("policy_terms");
|
||||
$policy_terms = json_decode($policy_terms, true);
|
||||
|
||||
$data = [];
|
||||
|
||||
foreach ($policy_terms as $key => $value) {
|
||||
|
||||
if (str_ends_with($key, '_display')) {
|
||||
|
||||
$original_key = substr($key, 0, -8);
|
||||
|
||||
$newKey = implode(' ', array_map('ucfirst', explode('_', $original_key)));
|
||||
|
||||
$data['enrollment_display_key'][$newKey] = $policy_terms[$original_key] ?? " ";
|
||||
} else {
|
||||
|
||||
$data[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($data)) {
|
||||
$policy_terms['is_payable_employee']['self'] = 0;
|
||||
$policy_terms = json_encode($data);
|
||||
}
|
||||
|
||||
|
||||
$record = $this->clientPolicyModel->where('id', $client_policy_id)->first();
|
||||
// print_r($policy_terms); die;
|
||||
|
||||
if(empty($policy_terms)){
|
||||
return $this->respond(['status' => false, 'code' => 404, 'message' => 'Policy terms data could not be empty', 'formdata' => $this->request->getPost()], 200);
|
||||
}
|
||||
|
||||
if ($record) {
|
||||
|
||||
$data = [];
|
||||
|
||||
foreach ($policy_terms as $key => $value) {
|
||||
|
||||
if (str_ends_with($key, '_display')) {
|
||||
|
||||
$original_key = substr($key, 0, -8);
|
||||
|
||||
$newKey = implode(' ', array_map('ucfirst', explode('_', $original_key)));
|
||||
|
||||
$data['enrollment_display_key'][$newKey] = $policy_terms[$original_key] ?? " ";
|
||||
} else {
|
||||
|
||||
$data[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
if($record['policy_type_id'] == 72){
|
||||
$data['enrollment_display_key'] = $this->otherPolicyTermsDisplayKeyConstruct($policy_terms);
|
||||
}
|
||||
|
||||
if (!empty($data)) {
|
||||
$policy_terms['is_payable_employee']['self'] = 0;
|
||||
$policy_terms = json_encode($data);
|
||||
}
|
||||
|
||||
$update = $this->clientPolicyModel->where('id', $client_policy_id)->set('policy_terms', $policy_terms)->update();
|
||||
|
||||
if ($update) {
|
||||
@ -4260,12 +4270,34 @@ class ClientController extends AdminController
|
||||
} else {
|
||||
return $this->respond(['status' => false, 'code' => 404, 'message' => 'Failed to update data', 'formdata' => $this->request->getPost()], 200);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
return $this->respond(['status' => false, 'code' => 404, 'message' => 'Failed to store data. The client policy does not exist', 'formdata' => $this->request->getPost()], 200);
|
||||
}
|
||||
}
|
||||
|
||||
public function otherPolicyTermsDisplayKeyConstruct($data)
|
||||
{
|
||||
$labels = $data['special_condition_label'] ?? [];
|
||||
$values = $data['special_condition_input'] ?? [];
|
||||
|
||||
$special_conditions = [];
|
||||
|
||||
foreach ($labels as $i => $label) {
|
||||
$value = $values[$i] ?? '';
|
||||
|
||||
// skip if label or value is empty
|
||||
if (trim($label) === '' || trim($value) === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$special_conditions[$label] = $value;
|
||||
}
|
||||
|
||||
return $special_conditions;
|
||||
}
|
||||
|
||||
public function checkPolicyType($policy_type_id, $client_branch_id, $client_id)
|
||||
{
|
||||
|
||||
@ -5971,8 +6003,8 @@ class ClientController extends AdminController
|
||||
// ---------- EMP SERVICE CONTROLLER --------------------------------------------------------------------------------
|
||||
|
||||
$empServiceController = new EmployeeServiceController();
|
||||
// $res = $empServiceController->excelFileFormatValidation(['file_id' => '1126']);
|
||||
// $res = $empServiceController->excelFileDataValidation(['file_id' => '865']);
|
||||
// $res = $empServiceController->excelFileFormatValidation(['file_id' => '1248']);
|
||||
// $res = $empServiceController->excelFileDataValidation(['file_id' => '1248']);
|
||||
// $res = $empServiceController->employeesOnboardPreprocess(['file_id' => 1183]);
|
||||
// $res = $empServiceController->employeesOnboardPreprocess(['file_id' => 1169]);
|
||||
// $res = $empServiceController->employeesOnboardProcess(['file_id' => 835]);
|
||||
|
||||
@ -101,6 +101,14 @@ class EmployeeController extends AdminController
|
||||
|
||||
public function list()
|
||||
{
|
||||
|
||||
// $s3 = \Config\Services::getS3Service();
|
||||
// $file = WRITEPATH.'uploads/excel/inception.ods';
|
||||
// print_rr($s3->exists('inception_1766475604.ods'));
|
||||
// print_rr($s3->getPresignedUrl('inception_1766.ods'));
|
||||
// print_rr($s3->upload($file));
|
||||
// print_rr($s3->download('inception_1766475604.ods',__DIR__));
|
||||
// die();
|
||||
// $model = new UserModel();
|
||||
$data = [];
|
||||
$data['status'] = ['draft' => 'Draft', 'enrolled' => 'Enrolled', 'active' => 'Active', 'inactive' => 'In-Active', 'pending' => 'Pending'];
|
||||
@ -1429,7 +1437,10 @@ class EmployeeController extends AdminController
|
||||
//E-CARD DOWNLOAD FUNCTION USING DOM PDF ( CURRENTLY USING THIS ) Dompdf
|
||||
public function generateIDCardForEmployee($rand_string, $people = 0, $mode = 0)
|
||||
{
|
||||
// dd($rand_string, $people);
|
||||
$mode = (int)$mode;
|
||||
|
||||
|
||||
// dd($rand_string, $people,$mode);
|
||||
$this->myLogger->logme('error', 'generateIDCardForEmployee params .' . json_encode(["rand_string" => $rand_string, "people" => $people, "mode" => $mode]));
|
||||
try {
|
||||
$this->myLogger->logme('error', 'generateIDCardForEmployee function started.');
|
||||
@ -1438,7 +1449,7 @@ class EmployeeController extends AdminController
|
||||
$this->myLogger->logme('error', 'Fetching employee code and client policy ID.');
|
||||
|
||||
$get_emp_code_and_client_policy_id = $this->employeePolicyModel
|
||||
->select('employee_polices.client_policy_id, employees.emp_code, tpa.short_name,employees.client_id')
|
||||
->select('employee_polices.client_policy_id, employees.emp_code,employees.name, employee_polices.tpa_id,tpa.short_name,employees.client_id')
|
||||
->join('client_policy', 'client_policy.id = employee_polices.client_policy_id')
|
||||
->join('employees', 'employees.id = employee_polices.employee_id')
|
||||
->join('tpa', 'tpa.id = client_policy.tpa_id')
|
||||
@ -1465,7 +1476,44 @@ class EmployeeController extends AdminController
|
||||
$client_policy_id = $get_emp_code_and_client_policy_id['client_policy_id'];
|
||||
$emp_code = $get_emp_code_and_client_policy_id['emp_code'];
|
||||
$client_id = $get_emp_code_and_client_policy_id['client_id'];
|
||||
|
||||
|
||||
//new step check in S3 if yes then fetch from S3 bucket
|
||||
$s3_key = 'ecard_'.$get_emp_code_and_client_policy_id['name'].'('.$get_emp_code_and_client_policy_id['emp_code'].')'.'_'.$get_emp_code_and_client_policy_id['tpa_id'].'.pdf';
|
||||
// echo $s3_key;die();
|
||||
$s3 = \Config\Services::getS3Service();
|
||||
if($s3->exists($s3_key) && $mode != 2) //2 => for bulk generate so skip s3 check and generate PDF
|
||||
{
|
||||
//get pre signed url & make it download
|
||||
$s3_url = $s3->getPresignedUrl($s3_key );
|
||||
|
||||
$pdf = file_get_contents($s3_url['url']);
|
||||
if ($mode == 1) {
|
||||
// Inline view
|
||||
return $this->response
|
||||
->setHeader('Content-Type', 'application/pdf')
|
||||
->setHeader('Content-Disposition', 'inline; filename="' . $s3_key . '"')
|
||||
->setBody($pdf);
|
||||
} else if($mode == 0){
|
||||
// echo 'Force download';//die();
|
||||
return $this->response
|
||||
->setHeader('Content-Type', 'application/pdf')
|
||||
->setHeader('Content-Disposition', 'attachment; filename="' . $s3_key . '"')
|
||||
->setBody($pdf);
|
||||
}
|
||||
// exit();
|
||||
}
|
||||
else
|
||||
{
|
||||
if($mode !== 2)
|
||||
{
|
||||
$data['message'] = 'Inprogress... Comeback later! or Contact support';
|
||||
return view('errors/404', $data);
|
||||
}
|
||||
}
|
||||
|
||||
//in else generate PDF and upload it in s3
|
||||
// echo 'else';
|
||||
// exit();
|
||||
// Step 2: Fetch ECard data
|
||||
$this->myLogger->logme('error', 'Fetching ECard data using client_policy_id: ' . $client_policy_id . ' and emp_code: ' . $emp_code);
|
||||
$data = $this->employeePolicyModel->getECardDataUsingMd5($client_policy_id, $emp_code,$client_id);
|
||||
@ -1562,6 +1610,7 @@ class EmployeeController extends AdminController
|
||||
}
|
||||
|
||||
// return $html;
|
||||
// echo $html;die();
|
||||
// DomPdf
|
||||
$options = new Options();
|
||||
$options->set('isRemoteEnabled', true);
|
||||
@ -1585,28 +1634,34 @@ class EmployeeController extends AdminController
|
||||
$dompdf->addInfo("Title", "E-Card");
|
||||
|
||||
$filename = 'ecard_' . date('Y-m-d_H-i-s') . '.pdf';
|
||||
|
||||
$temp_local_path = WRITEPATH.'/tmp/'.$s3_key;
|
||||
file_put_contents(($temp_local_path),$dompdf->output());
|
||||
// if($mode == 1){
|
||||
// $dompdf->stream($filename, ['Attachment' => false]); // Inline view
|
||||
// }else{
|
||||
// $dompdf->stream($filename, ['Attachment' => true]); // Force download
|
||||
// }
|
||||
$aws_upload_res = $s3->upload(($temp_local_path));
|
||||
// print_rr($aws_upload_res);
|
||||
// echo '<br>PDF generated and uploaded to S3 successfully.<br>';
|
||||
unlink($temp_local_path);
|
||||
|
||||
if ($mode == 1) {
|
||||
// Inline view
|
||||
return $this->response
|
||||
->setHeader('Content-Type', 'application/pdf')
|
||||
->setHeader('Content-Disposition', 'inline; filename="' . $filename . '"')
|
||||
->setBody($dompdf->output());
|
||||
} else {
|
||||
// Force download
|
||||
return $this->response
|
||||
->setHeader('Content-Type', 'application/pdf')
|
||||
->setHeader('Content-Disposition', 'attachment; filename="' . $filename . '"')
|
||||
->setBody($dompdf->output());
|
||||
}
|
||||
// exit();
|
||||
// if ($mode == 1) {
|
||||
// // Inline view
|
||||
// return $this->response
|
||||
// ->setHeader('Content-Type', 'application/pdf')
|
||||
// ->setHeader('Content-Disposition', 'inline; filename="' . $filename . '"')
|
||||
// ->setBody($dompdf->output());
|
||||
// } else {
|
||||
// // Force download
|
||||
// return $this->response
|
||||
// ->setHeader('Content-Type', 'application/pdf')
|
||||
// ->setHeader('Content-Disposition', 'attachment; filename="' . $filename . '"')
|
||||
// ->setBody($dompdf->output());
|
||||
// }
|
||||
|
||||
$this->myLogger->logme('error', 'generateIDCardForEmployee function completed successfully.');
|
||||
// $this->myLogger->logme('error', 'generateIDCardForEmployee function completed successfully.');
|
||||
|
||||
} catch (\Exception $e) {
|
||||
$this->myLogger->logme('error', 'Exception occurred: ' . $e->getMessage());
|
||||
@ -3764,206 +3819,389 @@ class EmployeeController extends AdminController
|
||||
array $notInTPA,
|
||||
array $notInNhance,
|
||||
array $reviewNeeded,
|
||||
string $filename = 'employee_review.xlsx'
|
||||
) {
|
||||
string $filename = 'employee_review.xlsx')
|
||||
{
|
||||
|
||||
function setCell($sheet, int $col, int $row, $value)
|
||||
{
|
||||
$cell = Coordinate::stringFromColumnIndex($col) . $row;
|
||||
$sheet->setCellValue($cell, $value);
|
||||
}
|
||||
function setCell($sheet, int $col, int $row, $value)
|
||||
{
|
||||
$cell = Coordinate::stringFromColumnIndex($col) . $row;
|
||||
$sheet->setCellValue($cell, $value);
|
||||
}
|
||||
|
||||
$EXPORT_COLUMNS = [
|
||||
$EXPORT_COLUMNS = [
|
||||
|
||||
// Sheet 1 — Not in TPA
|
||||
'not_in_tpa' => [
|
||||
'emp_code' => 'Employee Code',
|
||||
'name' => 'Employee Name',
|
||||
'relationship' => 'Relation',
|
||||
'dob' => 'Date of Birth',
|
||||
'gender' => 'Gender',
|
||||
'mobile' => 'Mobile No',
|
||||
'email_corporate' => 'Corporate Email',
|
||||
'policy_no' => 'Policy No',
|
||||
'tpa_name' => 'TPA Name',
|
||||
'change_event' => 'Change Event',
|
||||
],
|
||||
// Sheet 1 — Not in TPA
|
||||
'not_in_tpa' => [
|
||||
'emp_code' => 'Employee Code',
|
||||
'name' => 'Employee Name',
|
||||
'relationship' => 'Relation',
|
||||
'dob' => 'Date of Birth',
|
||||
'gender' => 'Gender',
|
||||
'mobile' => 'Mobile No',
|
||||
'email_corporate' => 'Corporate Email',
|
||||
'policy_no' => 'Policy No',
|
||||
'tpa_name' => 'TPA Name',
|
||||
'change_event' => 'Change Event',
|
||||
],
|
||||
|
||||
// Sheet 2 — Not in Nhance
|
||||
'not_in_nhance' => [
|
||||
'emp_code' => 'Employee Code',
|
||||
'name' => 'Employee Name',
|
||||
'relation' => 'Relation',
|
||||
'dob' => 'Date of Birth',
|
||||
'gender' => 'Gender',
|
||||
'age' => 'Age',
|
||||
'tpa_id' => 'TPA Member ID',
|
||||
],
|
||||
// Sheet 2 — Not in Nhance
|
||||
'not_in_nhance' => [
|
||||
'emp_code' => 'Employee Code',
|
||||
'name' => 'Employee Name',
|
||||
'relation' => 'Relation',
|
||||
'dob' => 'Date of Birth',
|
||||
'gender' => 'Gender',
|
||||
'age' => 'Age',
|
||||
'tpa_id' => 'TPA Member ID',
|
||||
],
|
||||
|
||||
// Sheet 3 — Review Needed (DB side)
|
||||
'review_main' => [
|
||||
'emp_code' => 'Employee Code',
|
||||
'name' => 'Employee Name',
|
||||
'relationship' => 'Relation',
|
||||
'dob' => 'Date of Birth',
|
||||
'gender' => 'Gender',
|
||||
'policy_no' => 'Policy No',
|
||||
'uhid' => 'UHID',
|
||||
'change_event' => 'Change event'
|
||||
],
|
||||
// Sheet 3 — Review Needed (DB side)
|
||||
'review_main' => [
|
||||
'emp_code' => 'Employee Code',
|
||||
'name' => 'Employee Name',
|
||||
'relationship' => 'Relation',
|
||||
'dob' => 'Date of Birth',
|
||||
'gender' => 'Gender',
|
||||
'policy_no' => 'Policy No',
|
||||
'uhid' => 'UHID',
|
||||
'change_event' => 'Change event'
|
||||
],
|
||||
|
||||
// Sheet 3 — Review Needed (TPA side)
|
||||
'review_tpa' => [
|
||||
'emp_code' => 'TPA Employee Code',
|
||||
'name' => 'TPA Name',
|
||||
'relation' => 'TPA Relation',
|
||||
'dob' => 'TPA DOB',
|
||||
'gender' => 'TPA Gender',
|
||||
'tpa_id' => 'TPA Member ID',
|
||||
'age' => 'TPA Age',
|
||||
],
|
||||
];
|
||||
// Sheet 3 — Review Needed (TPA side)
|
||||
'review_tpa' => [
|
||||
'emp_code' => 'TPA Employee Code',
|
||||
'name' => 'TPA Name',
|
||||
'relation' => 'TPA Relation',
|
||||
'dob' => 'TPA DOB',
|
||||
'gender' => 'TPA Gender',
|
||||
'tpa_id' => 'TPA Member ID',
|
||||
'age' => 'TPA Age',
|
||||
],
|
||||
];
|
||||
|
||||
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$spreadsheet = new Spreadsheet();
|
||||
|
||||
/* =========================================================
|
||||
* SHEET 1 — NOT IN TPA
|
||||
* ========================================================= */
|
||||
$sheet1 = $spreadsheet->getActiveSheet();
|
||||
$sheet1->setTitle('Not in TPA');
|
||||
/* =========================================================
|
||||
* SHEET 1 — NOT IN TPA
|
||||
* ========================================================= */
|
||||
$sheet1 = $spreadsheet->getActiveSheet();
|
||||
$sheet1->setTitle('Not in TPA');
|
||||
|
||||
$cols = $EXPORT_COLUMNS['not_in_tpa'];
|
||||
$cols = $EXPORT_COLUMNS['not_in_tpa'];
|
||||
|
||||
$colNo = 1;
|
||||
foreach ($cols as $label) {
|
||||
setCell($sheet1, $colNo++, 1, $label);
|
||||
}
|
||||
|
||||
$rowNo = 2;
|
||||
foreach ($notInTPA as $row) {
|
||||
$colNo = 1;
|
||||
foreach ($cols as $key => $label) {
|
||||
setCell($sheet1, $colNo++, $rowNo, $row[$key] ?? '');
|
||||
foreach ($cols as $label) {
|
||||
setCell($sheet1, $colNo++, 1, $label);
|
||||
}
|
||||
$rowNo++;
|
||||
}
|
||||
|
||||
foreach (range(1, count($cols)) as $c) {
|
||||
$sheet1->getColumnDimension(Coordinate::stringFromColumnIndex($c))->setAutoSize(true);
|
||||
}
|
||||
$rowNo = 2;
|
||||
foreach ($notInTPA as $row) {
|
||||
$colNo = 1;
|
||||
foreach ($cols as $key => $label) {
|
||||
setCell($sheet1, $colNo++, $rowNo, $row[$key] ?? '');
|
||||
}
|
||||
$rowNo++;
|
||||
}
|
||||
|
||||
/* =========================================================
|
||||
* SHEET 2 — NOT IN NHANCE
|
||||
* ========================================================= */
|
||||
$sheet2 = $spreadsheet->createSheet();
|
||||
$sheet2->setTitle('Not in Nhance');
|
||||
foreach (range(1, count($cols)) as $c) {
|
||||
$sheet1->getColumnDimension(Coordinate::stringFromColumnIndex($c))->setAutoSize(true);
|
||||
}
|
||||
|
||||
$cols = $EXPORT_COLUMNS['not_in_nhance'];
|
||||
/* =========================================================
|
||||
* SHEET 2 — NOT IN NHANCE
|
||||
* ========================================================= */
|
||||
$sheet2 = $spreadsheet->createSheet();
|
||||
$sheet2->setTitle('Not in Nhance');
|
||||
|
||||
$colNo = 1;
|
||||
foreach ($cols as $label) {
|
||||
setCell($sheet2, $colNo++, 1, $label);
|
||||
}
|
||||
$cols = $EXPORT_COLUMNS['not_in_nhance'];
|
||||
|
||||
$rowNo = 2;
|
||||
foreach ($notInNhance as $row) {
|
||||
$colNo = 1;
|
||||
foreach ($cols as $key => $label) {
|
||||
setCell($sheet2, $colNo++, $rowNo, $row[$key] ?? '');
|
||||
foreach ($cols as $label) {
|
||||
setCell($sheet2, $colNo++, 1, $label);
|
||||
}
|
||||
$rowNo++;
|
||||
}
|
||||
|
||||
foreach (range(1, count($cols)) as $c) {
|
||||
$sheet2->getColumnDimension(Coordinate::stringFromColumnIndex($c))->setAutoSize(true);
|
||||
}
|
||||
$rowNo = 2;
|
||||
foreach ($notInNhance as $row) {
|
||||
$colNo = 1;
|
||||
foreach ($cols as $key => $label) {
|
||||
setCell($sheet2, $colNo++, $rowNo, $row[$key] ?? '');
|
||||
}
|
||||
$rowNo++;
|
||||
}
|
||||
|
||||
/* =========================================================
|
||||
* SHEET 3 — REVIEW NEEDED
|
||||
* ========================================================= */
|
||||
$sheet3 = $spreadsheet->createSheet();
|
||||
$sheet3->setTitle('Review Needed');
|
||||
foreach (range(1, count($cols)) as $c) {
|
||||
$sheet2->getColumnDimension(Coordinate::stringFromColumnIndex($c))->setAutoSize(true);
|
||||
}
|
||||
|
||||
$mainCols = $EXPORT_COLUMNS['review_main'];
|
||||
$tpaCols = $EXPORT_COLUMNS['review_tpa'];
|
||||
/* =========================================================
|
||||
* SHEET 3 — REVIEW NEEDED
|
||||
* ========================================================= */
|
||||
$sheet3 = $spreadsheet->createSheet();
|
||||
$sheet3->setTitle('Review Needed');
|
||||
|
||||
// headers
|
||||
$colNo = 1;
|
||||
foreach ($mainCols as $label) {
|
||||
setCell($sheet3, $colNo++, 1, $label);
|
||||
}
|
||||
foreach ($tpaCols as $label) {
|
||||
setCell($sheet3, $colNo++, 1, $label);
|
||||
}
|
||||
$mainCols = $EXPORT_COLUMNS['review_main'];
|
||||
$tpaCols = $EXPORT_COLUMNS['review_tpa'];
|
||||
|
||||
// rows
|
||||
$rowNo = 2;
|
||||
foreach ($reviewNeeded as $row) {
|
||||
|
||||
// main (DB)
|
||||
// headers
|
||||
$colNo = 1;
|
||||
foreach ($mainCols as $key => $label) {
|
||||
setCell($sheet3, $colNo++, $rowNo, $row[$key] ?? '');
|
||||
foreach ($mainCols as $label) {
|
||||
setCell($sheet3, $colNo++, 1, $label);
|
||||
}
|
||||
foreach ($tpaCols as $label) {
|
||||
setCell($sheet3, $colNo++, 1, $label);
|
||||
}
|
||||
|
||||
$tpaData = [];
|
||||
$notMatching = [];
|
||||
// rows
|
||||
$rowNo = 2;
|
||||
foreach ($reviewNeeded as $row) {
|
||||
|
||||
if (($row['match']['status'] ?? '') === 'matched') {
|
||||
$tpaData = $row['match']['tpa_record'] ?? [];
|
||||
$notMatching = $row['match']['not_matching'] ?? [];
|
||||
}
|
||||
|
||||
// TPA
|
||||
foreach ($tpaCols as $key => $label) {
|
||||
setCell($sheet3, $colNo++, $rowNo, $tpaData[$key] ?? '');
|
||||
}
|
||||
|
||||
// highlight mismatches
|
||||
foreach ($notMatching as $field) {
|
||||
|
||||
if (isset($mainCols[$field])) {
|
||||
$idx = array_keys($mainCols);
|
||||
$pos = array_search($field, $idx);
|
||||
$cell = Coordinate::stringFromColumnIndex($pos + 1) . $rowNo;
|
||||
$sheet3->getStyle($cell)->getFill()
|
||||
->setFillType(Fill::FILL_SOLID)
|
||||
->getStartColor()->setARGB('FFFFFF00');
|
||||
// main (DB)
|
||||
$colNo = 1;
|
||||
foreach ($mainCols as $key => $label) {
|
||||
setCell($sheet3, $colNo++, $rowNo, $row[$key] ?? '');
|
||||
}
|
||||
|
||||
if (isset($tpaCols[$field])) {
|
||||
$idx = array_keys($tpaCols);
|
||||
$pos = array_search($field, $idx);
|
||||
$cell = Coordinate::stringFromColumnIndex(count($mainCols) + $pos + 1) . $rowNo;
|
||||
$sheet3->getStyle($cell)->getFill()
|
||||
->setFillType(Fill::FILL_SOLID)
|
||||
->getStartColor()->setARGB('FFFFFF00');
|
||||
$tpaData = [];
|
||||
$notMatching = [];
|
||||
|
||||
if (($row['match']['status'] ?? '') === 'matched') {
|
||||
$tpaData = $row['match']['tpa_record'] ?? [];
|
||||
$notMatching = $row['match']['not_matching'] ?? [];
|
||||
}
|
||||
|
||||
// TPA
|
||||
foreach ($tpaCols as $key => $label) {
|
||||
setCell($sheet3, $colNo++, $rowNo, $tpaData[$key] ?? '');
|
||||
}
|
||||
|
||||
// highlight mismatches
|
||||
foreach ($notMatching as $field) {
|
||||
|
||||
if (isset($mainCols[$field])) {
|
||||
$idx = array_keys($mainCols);
|
||||
$pos = array_search($field, $idx);
|
||||
$cell = Coordinate::stringFromColumnIndex($pos + 1) . $rowNo;
|
||||
$sheet3->getStyle($cell)->getFill()
|
||||
->setFillType(Fill::FILL_SOLID)
|
||||
->getStartColor()->setARGB('FFFFFF00');
|
||||
}
|
||||
|
||||
if (isset($tpaCols[$field])) {
|
||||
$idx = array_keys($tpaCols);
|
||||
$pos = array_search($field, $idx);
|
||||
$cell = Coordinate::stringFromColumnIndex(count($mainCols) + $pos + 1) . $rowNo;
|
||||
$sheet3->getStyle($cell)->getFill()
|
||||
->setFillType(Fill::FILL_SOLID)
|
||||
->getStartColor()->setARGB('FFFFFF00');
|
||||
}
|
||||
}
|
||||
|
||||
$rowNo++;
|
||||
}
|
||||
|
||||
$rowNo++;
|
||||
foreach (range(1, count($mainCols) + count($tpaCols)) as $c) {
|
||||
$sheet3->getColumnDimension(Coordinate::stringFromColumnIndex($c))->setAutoSize(true);
|
||||
}
|
||||
|
||||
/* =========================================================
|
||||
* OUTPUT
|
||||
* ========================================================= */
|
||||
$writer = new Xlsx($spreadsheet);
|
||||
|
||||
if (ob_get_length()) ob_end_clean();
|
||||
|
||||
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
||||
header('Content-Disposition: attachment; filename="' . $filename . '"');
|
||||
header('Cache-Control: max-age=0');
|
||||
|
||||
$writer->save('php://output');
|
||||
exit;
|
||||
}
|
||||
|
||||
foreach (range(1, count($mainCols) + count($tpaCols)) as $c) {
|
||||
$sheet3->getColumnDimension(Coordinate::stringFromColumnIndex($c))->setAutoSize(true);
|
||||
public function bulkGenerateEcardAndStoreinS3(array $params = [])
|
||||
{
|
||||
/**
|
||||
* ---------------------------
|
||||
* 1. Detect mode + collect inputs
|
||||
* ---------------------------
|
||||
*/
|
||||
|
||||
$client_policy_id = $params['client_policy_id'] ?? null;
|
||||
$emp_code = $params['emp_code'] ?? null;
|
||||
$emp_pk = $params['emp_pk'] ?? null;
|
||||
$batch_size = $params['batch_size'] ?? null;
|
||||
$batch_no = $params['batch_no'] ?? null;
|
||||
$execution_mode = $params['execution_mode'] ?? 'sequential';//parallel
|
||||
$total_batches = $params['total_batches'] ?? 0;//parallel
|
||||
|
||||
if (is_cli()) {
|
||||
$mode = 'CLI';
|
||||
|
||||
|
||||
} elseif (empty($params) && $this->request->getMethod() === 'get') {
|
||||
$mode = 'WEB';
|
||||
|
||||
$client_policy_id = $this->request->getGet('client_policy_id');
|
||||
$emp_code = $this->request->getGet('emp_code');
|
||||
$emp_pk = $this->request->getGet('emp_pk');
|
||||
$execution_mode = $this->request->getGet('execution_mode') ?? 'sequential';
|
||||
$batch_size = (int) ($this->request->getGet('batch_size') ?? 100);
|
||||
if($client_policy_id == null || empty($client_policy_id) || $client_policy_id == '')
|
||||
{
|
||||
return $this->respond(['status' => false, 'code' => 200, 'message' => 'client_policy_id is necessary'], 200);
|
||||
}
|
||||
/**
|
||||
* ---------------------------
|
||||
* 2. Calculate total records
|
||||
* ---------------------------
|
||||
*/
|
||||
$total = $this->employeePolicyModel
|
||||
->join('employees', 'employees.id = employee_polices.employee_id')
|
||||
->where('employees.emp_status', 'active')
|
||||
->where('employees.is_active', '1')
|
||||
->where("employee_polices.tpa_id IS NOT NULL AND employee_polices.tpa_id <> ''")
|
||||
->where('employee_polices.status', 'active')
|
||||
->where('employee_polices.is_active', '1')
|
||||
->where('employee_polices.client_policy_id', $client_policy_id)
|
||||
->countAllResults();
|
||||
|
||||
$total_batches = (int) ceil($total / $batch_size);
|
||||
// echo $total_batches;die();
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* ---------------------------
|
||||
* 3. Push JOB per batch
|
||||
* ---------------------------
|
||||
*/
|
||||
if ($execution_mode === 'parallel') {
|
||||
for ($batch_no = 1; $batch_no <= $total_batches; $batch_no++) {
|
||||
Jobs::addJob([
|
||||
'job_name' => 'bulkGenerateEcardAndStoreinS3',
|
||||
'payload' => [
|
||||
'client_policy_id' => $client_policy_id,
|
||||
'emp_code' => $emp_code,
|
||||
'emp_pk' => $emp_pk,
|
||||
'batch_size' => $batch_size,
|
||||
'batch_no' => $batch_no,
|
||||
'total_batches' => $total_batches,
|
||||
'execution_mode' => 'parallel',
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($execution_mode === 'sequential') {
|
||||
Jobs::addJob([
|
||||
'job_name' => 'bulkGenerateEcardAndStoreinS3',
|
||||
'payload' => [
|
||||
'client_policy_id' => $client_policy_id,
|
||||
'batch_size' => $batch_size,
|
||||
'batch_no' => $batch_no ?? 1,
|
||||
'total_batches' => $total_batches,
|
||||
'execution_mode' => 'sequential',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->respond(['status' => true, 'code' => 200, 'message' => 'E-card generation started',
|
||||
'total' => $total,
|
||||
'batches' => $total_batches,], 200);
|
||||
// return [
|
||||
// 'status' => true,
|
||||
// 'message' => 'E-card generation started',
|
||||
// 'total' => $total,
|
||||
// 'batches' => $total_batches,
|
||||
// ];
|
||||
}
|
||||
//else {
|
||||
// return true;
|
||||
// }
|
||||
|
||||
/**
|
||||
* ---------------------------
|
||||
* 4. Build query (CLI execution)
|
||||
* ---------------------------
|
||||
*/
|
||||
$builder = $this->employeePolicyModel
|
||||
->select('
|
||||
employee_polices.client_policy_id,
|
||||
employees.emp_code,
|
||||
tpa.short_name,
|
||||
employees.client_id,
|
||||
employee_polices.rand_string,
|
||||
employees.id as emp_id
|
||||
')
|
||||
->join('client_policy', 'client_policy.id = employee_polices.client_policy_id')
|
||||
->join('employees', 'employees.id = employee_polices.employee_id')
|
||||
->join('tpa', 'tpa.id = client_policy.tpa_id')
|
||||
->where('employees.emp_status', 'active')
|
||||
->where('employees.is_active', '1')
|
||||
->where("employee_polices.tpa_id IS NOT NULL AND employee_polices.tpa_id <> ''")
|
||||
->where('employee_polices.status', 'active')
|
||||
->where('employee_polices.is_active', '1')
|
||||
->where('employee_polices.client_policy_id', $client_policy_id);
|
||||
|
||||
if (!empty($emp_code)) {
|
||||
$builder->where('employees.emp_code', $emp_code);
|
||||
}
|
||||
|
||||
if (!empty($emp_pk)) {
|
||||
$builder->where('employees.id', $emp_pk);
|
||||
}
|
||||
|
||||
/**
|
||||
* ---------------------------
|
||||
* 5. Apply batching (only if provided)
|
||||
* ---------------------------
|
||||
*/
|
||||
if (!empty($batch_size) && !empty($batch_no)) {
|
||||
$offset = ($batch_no - 1) * $batch_size;
|
||||
$builder->limit($batch_size, $offset);
|
||||
}
|
||||
|
||||
$res = $builder->findAll();
|
||||
|
||||
/**
|
||||
* ---------------------------
|
||||
* 6. Process batch
|
||||
* ---------------------------
|
||||
*/
|
||||
foreach ($res as $row) {
|
||||
$ecard_gen_res = $this->generateIDCardForEmployee(
|
||||
$row['rand_string'],
|
||||
0,
|
||||
2 // skip S3 check, force regenerate
|
||||
);
|
||||
|
||||
// print_rr([
|
||||
// 'batch' => $batch_no,
|
||||
// 'emp' => $row['emp_code'],
|
||||
// 'res' => $ecard_gen_res,
|
||||
// ]);
|
||||
|
||||
// echo date('Y-m-d H:i:s') . PHP_EOL;
|
||||
}
|
||||
|
||||
if ($execution_mode === 'sequential' && $batch_no < $total_batches) {
|
||||
Jobs::addJob([
|
||||
'job_name' => 'bulkGenerateEcardAndStoreinS3',
|
||||
'payload' => [
|
||||
'client_policy_id' => $client_policy_id,
|
||||
'batch_size' => $batch_size,
|
||||
'batch_no' => ($batch_no + 1),
|
||||
'total_batches' => $total_batches,
|
||||
'execution_mode' => 'sequential',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
// echo "BATCH {$batch_no} COMPLETED @ " . date('Y-m-d H:i:s') . PHP_EOL;
|
||||
return array($batch_no);
|
||||
}
|
||||
|
||||
/* =========================================================
|
||||
* OUTPUT
|
||||
* ========================================================= */
|
||||
$writer = new Xlsx($spreadsheet);
|
||||
|
||||
if (ob_get_length()) ob_end_clean();
|
||||
|
||||
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
||||
header('Content-Disposition: attachment; filename="' . $filename . '"');
|
||||
header('Cache-Control: max-age=0');
|
||||
|
||||
$writer->save('php://output');
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -1029,7 +1029,7 @@ class EmployeeMultiEventServiceController extends BaseController
|
||||
//check physical file
|
||||
if (!file_exists($file_name_with_path)) {
|
||||
//file not found update status and reason
|
||||
$message = "Physcial file not found";
|
||||
$message = "Physcial file not found - " . $file_name_with_path;
|
||||
// echo $message;
|
||||
$this->myLogger->logme('error', ($message . ' for file id ' . $file_id));
|
||||
$this->fileModel->where('id', $file_id)->set(['status' => 'failed', 'reason' => json_encode(['error_summary' => array_count_values([5]), 'error_data' => $message])])->update();
|
||||
@ -1402,7 +1402,7 @@ class EmployeeMultiEventServiceController extends BaseController
|
||||
//check physical file
|
||||
if (!file_exists($file_name_with_path)) {
|
||||
//file not found update status and reason
|
||||
$message = "Physcial file not found";
|
||||
$message = "Physcial file not found - " . $file_name_with_path;
|
||||
$this->myLogger->logme('error', ($message . ' for file id ' . $file_id));
|
||||
$this->fileModel->where('id', $file_id)->set(['status' => 'failed', 'reason' => json_encode(['error_summary' => array_count_values([5]), 'error_data' => $message])])->update();
|
||||
return array('error_summary' => [5], 'error_data' => $message);
|
||||
|
||||
@ -194,6 +194,10 @@ class JobWorker extends AdminController
|
||||
'type' => 'CC', // Handler Category
|
||||
'handler' => 'App\Controllers\MediAssistApiController',
|
||||
],
|
||||
'bulkGenerateEcardAndStoreinS3' => [
|
||||
'type' => 'CC', // Handler Category
|
||||
'handler' => 'App\Controllers\EmployeeController',
|
||||
]
|
||||
];
|
||||
|
||||
|
||||
@ -253,6 +257,8 @@ class JobWorker extends AdminController
|
||||
// print_r($jobdata);
|
||||
// echo 'listen';
|
||||
// die();
|
||||
// $jobdata = ['id' => 11954,'uuid' => '001700c2-8127-4d02-80b3-ea26e7603ccf'];
|
||||
// dd($job);
|
||||
$query = "
|
||||
SELECT id, name, payload, uuid
|
||||
FROM jobs
|
||||
|
||||
268
app/Libraries/S3Service.php
Normal file
268
app/Libraries/S3Service.php
Normal file
@ -0,0 +1,268 @@
|
||||
<?php
|
||||
|
||||
namespace App\Libraries;
|
||||
|
||||
use Aws\S3\S3Client;
|
||||
use Aws\Exception\AwsException;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
|
||||
class S3Service
|
||||
{
|
||||
protected $s3Client;
|
||||
protected $bucket;
|
||||
protected $region;
|
||||
protected $baseUrl;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->bucket = getenv('AWS_BUCKET');
|
||||
$this->region = getenv('AWS_DEFAULT_REGION');
|
||||
|
||||
$this->s3Client = new S3Client([
|
||||
'version' => 'latest',
|
||||
'region' => $this->region,
|
||||
'credentials' => [
|
||||
'key' => getenv('AWS_ACCESS_KEY_ID'),
|
||||
'secret' => getenv('AWS_SECRET_ACCESS_KEY'),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->baseUrl = "https://{$this->bucket}.s3.{$this->region}.amazonaws.com/";
|
||||
// echo $this->baseUrl;die();
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload file to S3
|
||||
*
|
||||
* @param mixed $file File object or file path
|
||||
* @param string $folder Folder path in S3 bucket (optional)
|
||||
* @param string $fileName Custom file name (optional)
|
||||
* @return array ['success' => bool, 'url' => string, 'key' => string, 'message' => string]
|
||||
*/
|
||||
public function upload($file, string $folder = '', string $fileName = null): array
|
||||
{
|
||||
try {
|
||||
// Handle CodeIgniter file upload object
|
||||
if (is_object($file) && method_exists($file, 'isValid')) {
|
||||
if (!$file->isValid()) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Invalid file upload',
|
||||
'url' => null,
|
||||
'key' => null
|
||||
];
|
||||
}
|
||||
$filePath = $file->getTempName();
|
||||
$originalName = $fileName ?? $file->getClientName();
|
||||
} else {
|
||||
// Handle file path string
|
||||
$filePath = $file;
|
||||
$originalName = $fileName ?? basename($file);
|
||||
}
|
||||
|
||||
// Generate unique file name
|
||||
$extension = pathinfo($originalName, PATHINFO_EXTENSION);
|
||||
// $uniqueName = pathinfo($originalName, PATHINFO_FILENAME) . '_' . time() . '.' . $extension;
|
||||
$uniqueName = pathinfo($originalName, PATHINFO_FILENAME) . '.' . $extension;
|
||||
|
||||
// Build S3 key (path)
|
||||
$key = $folder ? rtrim($folder, '/') . '/' . $uniqueName : $uniqueName;
|
||||
|
||||
// Upload to S3
|
||||
// NEW - Faster with streaming and proper content type
|
||||
$finfo = finfo_open(FILEINFO_MIME_TYPE);
|
||||
$mimeType = finfo_file($finfo, $filePath);
|
||||
finfo_close($finfo);
|
||||
|
||||
$result = $this->s3Client->putObject([
|
||||
'Bucket' => $this->bucket,
|
||||
'Key' => $key,
|
||||
'Body' => fopen($filePath, 'rb'), // Stream instead of loading into memory
|
||||
'ContentType' => $mimeType,
|
||||
// 'ACL' => 'public-read',
|
||||
]);
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'url' => $this->baseUrl . $key,
|
||||
'key' => $key,
|
||||
'message' => 'File uploaded successfully'
|
||||
];
|
||||
|
||||
} catch (AwsException $e) {
|
||||
log_message('error', 'S3 Upload Error: ' . $e->getMessage());
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Upload failed: ' . $e->getMessage(),
|
||||
'url' => null,
|
||||
'key' => null
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Download file from S3
|
||||
*
|
||||
* @param string $key S3 object key (file path in bucket)
|
||||
* @param string $savePath Local path to save the file (optional)
|
||||
* @return array ['success' => bool, 'path' => string, 'content' => string, 'message' => string]
|
||||
*/
|
||||
public function download(string $key, string $savePath = null): array
|
||||
{
|
||||
try {
|
||||
$result = $this->s3Client->getObject([
|
||||
'Bucket' => $this->bucket,
|
||||
'Key' => $key,
|
||||
]);
|
||||
|
||||
$content = (string) $result['Body'];
|
||||
|
||||
// If save path is provided, save to local file
|
||||
if ($savePath) {
|
||||
$directory = dirname($savePath);
|
||||
if (!is_dir($directory)) {
|
||||
mkdir($directory, 0755, true);
|
||||
}
|
||||
file_put_contents($savePath, $content);
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'path' => $savePath,
|
||||
'content' => $content,
|
||||
'message' => 'File downloaded successfully'
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'path' => null,
|
||||
'content' => $content,
|
||||
'message' => 'File content retrieved successfully'
|
||||
];
|
||||
|
||||
} catch (AwsException $e) {
|
||||
log_message('error', 'S3 Download Error: ' . $e->getMessage());
|
||||
return [
|
||||
'success' => false,
|
||||
'path' => null,
|
||||
'content' => null,
|
||||
'message' => 'Download failed: ' . $e->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a pre-signed URL for temporary access to a private file
|
||||
*
|
||||
* @param string $key S3 object key
|
||||
* @param int $expiration Expiration time in minutes (default: 60)
|
||||
* @return array ['success' => bool, 'url' => string, 'message' => string]
|
||||
*/
|
||||
public function getPresignedUrl(string $key, int $expiration = 60): array
|
||||
{
|
||||
try {
|
||||
$cmd = $this->s3Client->getCommand('GetObject', [
|
||||
'Bucket' => $this->bucket,
|
||||
'Key' => $key
|
||||
]);
|
||||
|
||||
$request = $this->s3Client->createPresignedRequest($cmd, "+{$expiration} minutes");
|
||||
$presignedUrl = (string) $request->getUri();
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'url' => $presignedUrl,
|
||||
'message' => 'Pre-signed URL generated successfully'
|
||||
];
|
||||
|
||||
} catch (AwsException $e) {
|
||||
log_message('error', 'S3 Presigned URL Error: ' . $e->getMessage());
|
||||
return [
|
||||
'success' => false,
|
||||
'url' => null,
|
||||
'message' => 'Failed to generate URL: ' . $e->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete file from S3
|
||||
*
|
||||
* @param string $key S3 object key
|
||||
* @return array ['success' => bool, 'message' => string]
|
||||
*/
|
||||
public function delete(string $key): array
|
||||
{
|
||||
try {
|
||||
$this->s3Client->deleteObject([
|
||||
'Bucket' => $this->bucket,
|
||||
'Key' => $key,
|
||||
]);
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'message' => 'File deleted successfully'
|
||||
];
|
||||
|
||||
} catch (AwsException $e) {
|
||||
log_message('error', 'S3 Delete Error: ' . $e->getMessage());
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Delete failed: ' . $e->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if file exists in S3
|
||||
*
|
||||
* @param string $key S3 object key
|
||||
* @return bool
|
||||
*/
|
||||
public function exists(string $key): bool
|
||||
{
|
||||
return $this->s3Client->doesObjectExist($this->bucket, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* List files in S3 bucket
|
||||
*
|
||||
* @param string $prefix Folder prefix (optional)
|
||||
* @return array ['success' => bool, 'files' => array, 'message' => string]
|
||||
*/
|
||||
public function listFiles(string $prefix = ''): array
|
||||
{
|
||||
try {
|
||||
$result = $this->s3Client->listObjectsV2([
|
||||
'Bucket' => $this->bucket,
|
||||
'Prefix' => $prefix,
|
||||
]);
|
||||
|
||||
$files = [];
|
||||
if (isset($result['Contents'])) {
|
||||
foreach ($result['Contents'] as $object) {
|
||||
$files[] = [
|
||||
'key' => $object['Key'],
|
||||
'size' => $object['Size'],
|
||||
'last_modified' => $object['LastModified']->format('Y-m-d H:i:s'),
|
||||
'url' => $this->baseUrl . $object['Key']
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'files' => $files,
|
||||
'message' => 'Files retrieved successfully'
|
||||
];
|
||||
|
||||
} catch (AwsException $e) {
|
||||
log_message('error', 'S3 List Error: ' . $e->getMessage());
|
||||
return [
|
||||
'success' => false,
|
||||
'files' => [],
|
||||
'message' => 'Failed to list files: ' . $e->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3,6 +3,33 @@
|
||||
position: absolute;
|
||||
}
|
||||
.dataTables_length label {height: 21px !important;}
|
||||
|
||||
.custom-dropdown-menu {
|
||||
display: none;
|
||||
position: absolute;
|
||||
background-color: #ffffff !important;
|
||||
border: 1px solid rgba(0, 0, 0, 0.15);
|
||||
border-radius: 0.25rem;
|
||||
padding: 0.5rem 0;
|
||||
min-width: 10rem;
|
||||
z-index: 9999;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.custom-dropdown-menu .dropdown-item {
|
||||
display: block !important;
|
||||
width: 100% !important;
|
||||
padding: 0.5rem 1rem !important;
|
||||
color: #212529 !important;
|
||||
text-decoration: none !important;
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.custom-dropdown-menu .dropdown-item:hover {
|
||||
background-color: #f8f9fa !important;
|
||||
color: #16181b !important;
|
||||
cursor: pointer !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- End ADD and EDIT Page HTML -->
|
||||
@ -16,13 +43,15 @@
|
||||
<th class="font-weight-medium">S.No.</th>
|
||||
<th class="font-weight-medium">Category</th>
|
||||
<th class="font-weight-medium">Question</th>
|
||||
<th class="font-weight-medium">Answer</th>
|
||||
<!-- <th class="font-weight-medium">Answer</th> -->
|
||||
<th class="font-weight-medium">Status</th>
|
||||
<th class="font-weight-medium">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if(isset($faq_list)) { $slno = 1; ?>
|
||||
<?php if(!empty($faq_list)) {
|
||||
// <?php if(isset($faq_list)) {
|
||||
$slno = 1; ?>
|
||||
<?php foreach($faq_list as $index => $row) { ?>
|
||||
<tr>
|
||||
<td class="text-center"><?= $slno++; ?></td>
|
||||
@ -36,11 +65,11 @@
|
||||
? htmlspecialchars(substr($question, 0, 50)) . "..."
|
||||
: htmlspecialchars($question);
|
||||
?></td>
|
||||
<td><?php $answer = $row['answer'] ? $row['answer'] : 'N/A';
|
||||
<!-- <td><?php $answer = $row['answer'] ? $row['answer'] : 'N/A';
|
||||
echo (strlen($answer) > 50)
|
||||
? htmlspecialchars(substr($answer, 0, 50)) . "..."
|
||||
: htmlspecialchars($answer);
|
||||
?></td>
|
||||
?></td> -->
|
||||
<td>
|
||||
<span class="<?php if($row['is_active'] == 1){ echo 'badge badge-primary'; }else{ echo 'badge badge-danger'; } ?>">
|
||||
<?php if($row['is_active'] == 1){ echo 'Active'; }else{ echo 'In-Active'; } ?>
|
||||
@ -62,12 +91,7 @@
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
<?php } else { ?>
|
||||
<!-- <tr>
|
||||
<td colspan="6" class="text-center">No data available</td>
|
||||
</tr> -->
|
||||
<?php } ?>
|
||||
<?php } } ?>
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
@ -353,4 +377,93 @@
|
||||
});
|
||||
|
||||
</script>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
const table = document.getElementById("user-table");
|
||||
|
||||
// Create custom dropdown
|
||||
function createCustomDropdown(row) {
|
||||
// Get the original dropdown items
|
||||
const originalDropdown = row.querySelector('.dropdown-menu');
|
||||
if (!originalDropdown) return null;
|
||||
|
||||
// Create new dropdown with proper background and spacing
|
||||
const customDropdown = document.createElement('div');
|
||||
customDropdown.className = 'custom-dropdown-menu';
|
||||
|
||||
// Copy inner content while maintaining icon alignment
|
||||
customDropdown.innerHTML = originalDropdown.innerHTML;
|
||||
|
||||
return customDropdown;
|
||||
}
|
||||
|
||||
let activeDropdown = null;
|
||||
|
||||
// Add click event listener to rows
|
||||
table.querySelectorAll("tbody tr").forEach(row => {
|
||||
const customDropdown = createCustomDropdown(row);
|
||||
if (!customDropdown) return;
|
||||
|
||||
// Append the custom dropdown to the body
|
||||
document.body.appendChild(customDropdown);
|
||||
|
||||
row.addEventListener("click", function(event) {
|
||||
// Ignore clicks on the action column
|
||||
if (event.target.closest('td:last-child')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Hide any active dropdown
|
||||
if (activeDropdown) {
|
||||
activeDropdown.style.display = 'none';
|
||||
}
|
||||
|
||||
// Get click position
|
||||
const rect = event.target.getBoundingClientRect();
|
||||
|
||||
// Position the dropdown with some offset
|
||||
customDropdown.style.display = 'block';
|
||||
customDropdown.style.position = 'fixed';
|
||||
customDropdown.style.left = `${rect.left}px`;
|
||||
customDropdown.style.top = `${rect.bottom + 5}px`; // Add 5px gap
|
||||
|
||||
// Set as active dropdown
|
||||
activeDropdown = customDropdown;
|
||||
|
||||
event.stopPropagation();
|
||||
});
|
||||
|
||||
// Preserve click handlers and add auto-close
|
||||
customDropdown.querySelectorAll('.dropdown-item').forEach(item => {
|
||||
item.addEventListener('click', function(e) {
|
||||
const onclickAttr = this.getAttribute('onclick');
|
||||
if (onclickAttr) {
|
||||
eval(onclickAttr);
|
||||
}
|
||||
|
||||
const href = this.getAttribute('href');
|
||||
if (href && href !== '#') {
|
||||
window.location.href = href;
|
||||
}
|
||||
|
||||
// Close the dropdown after handling the click
|
||||
if (activeDropdown) {
|
||||
activeDropdown.style.display = 'none';
|
||||
activeDropdown = null;
|
||||
}
|
||||
|
||||
e.stopPropagation();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Close dropdown when clicking outside
|
||||
document.addEventListener("click", function() {
|
||||
if (activeDropdown) {
|
||||
activeDropdown.style.display = 'none';
|
||||
activeDropdown = null;
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@ -3,6 +3,32 @@
|
||||
position: absolute;
|
||||
}
|
||||
.dataTables_length label {height: 21px !important;}
|
||||
.custom-dropdown-menu {
|
||||
display: none;
|
||||
position: absolute;
|
||||
background-color: #ffffff !important;
|
||||
border: 1px solid rgba(0, 0, 0, 0.15);
|
||||
border-radius: 0.25rem;
|
||||
padding: 0.5rem 0;
|
||||
min-width: 10rem;
|
||||
z-index: 9999;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.custom-dropdown-menu .dropdown-item {
|
||||
display: block !important;
|
||||
width: 100% !important;
|
||||
padding: 0.5rem 1rem !important;
|
||||
color: #212529 !important;
|
||||
text-decoration: none !important;
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.custom-dropdown-menu .dropdown-item:hover {
|
||||
background-color: #f8f9fa !important;
|
||||
color: #16181b !important;
|
||||
cursor: pointer !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- End ADD and EDIT Page HTML -->
|
||||
@ -23,7 +49,7 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if(isset($data['fe_list'])) { $slno = 1; ?>
|
||||
<?php if(!empty($data['fe_list'])) { $slno = 1; ?>
|
||||
<?php foreach($data['fe_list'] as $index => $row) { ?>
|
||||
<tr>
|
||||
<td class="text-center"><?= $slno++; ?></td>
|
||||
@ -51,12 +77,7 @@
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
<?php } else { ?>
|
||||
<!-- <tr>
|
||||
<td colspan="6" class="text-center">No data available</td>
|
||||
</tr> -->
|
||||
<?php } ?>
|
||||
<?php } } ?>
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
@ -503,4 +524,93 @@
|
||||
}
|
||||
|
||||
</script>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
const table = document.getElementById("user-table");
|
||||
|
||||
// Create custom dropdown
|
||||
function createCustomDropdown(row) {
|
||||
// Get the original dropdown items
|
||||
const originalDropdown = row.querySelector('.dropdown-menu');
|
||||
if (!originalDropdown) return null;
|
||||
|
||||
// Create new dropdown with proper background and spacing
|
||||
const customDropdown = document.createElement('div');
|
||||
customDropdown.className = 'custom-dropdown-menu';
|
||||
|
||||
// Copy inner content while maintaining icon alignment
|
||||
customDropdown.innerHTML = originalDropdown.innerHTML;
|
||||
|
||||
return customDropdown;
|
||||
}
|
||||
|
||||
let activeDropdown = null;
|
||||
|
||||
// Add click event listener to rows
|
||||
table.querySelectorAll("tbody tr").forEach(row => {
|
||||
const customDropdown = createCustomDropdown(row);
|
||||
if (!customDropdown) return;
|
||||
|
||||
// Append the custom dropdown to the body
|
||||
document.body.appendChild(customDropdown);
|
||||
|
||||
row.addEventListener("click", function(event) {
|
||||
// Ignore clicks on the action column
|
||||
if (event.target.closest('td:last-child')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Hide any active dropdown
|
||||
if (activeDropdown) {
|
||||
activeDropdown.style.display = 'none';
|
||||
}
|
||||
|
||||
// Get click position
|
||||
const rect = event.target.getBoundingClientRect();
|
||||
|
||||
// Position the dropdown with some offset
|
||||
customDropdown.style.display = 'block';
|
||||
customDropdown.style.position = 'fixed';
|
||||
customDropdown.style.left = `${rect.left}px`;
|
||||
customDropdown.style.top = `${rect.bottom + 5}px`; // Add 5px gap
|
||||
|
||||
// Set as active dropdown
|
||||
activeDropdown = customDropdown;
|
||||
|
||||
event.stopPropagation();
|
||||
});
|
||||
|
||||
// Preserve click handlers and add auto-close
|
||||
customDropdown.querySelectorAll('.dropdown-item').forEach(item => {
|
||||
item.addEventListener('click', function(e) {
|
||||
const onclickAttr = this.getAttribute('onclick');
|
||||
if (onclickAttr) {
|
||||
eval(onclickAttr);
|
||||
}
|
||||
|
||||
const href = this.getAttribute('href');
|
||||
if (href && href !== '#') {
|
||||
window.location.href = href;
|
||||
}
|
||||
|
||||
// Close the dropdown after handling the click
|
||||
if (activeDropdown) {
|
||||
activeDropdown.style.display = 'none';
|
||||
activeDropdown = null;
|
||||
}
|
||||
|
||||
e.stopPropagation();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Close dropdown when clicking outside
|
||||
document.addEventListener("click", function() {
|
||||
if (activeDropdown) {
|
||||
activeDropdown.style.display = 'none';
|
||||
activeDropdown = null;
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@ -72,6 +72,11 @@
|
||||
margin-left: 30px;
|
||||
width: 689px;
|
||||
}
|
||||
|
||||
.other-speciial-condition{
|
||||
margin-left: 150px;
|
||||
width: 564px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="OtherPolicyTermsForm" style="display:none">
|
||||
@ -83,35 +88,180 @@
|
||||
|
||||
<form role="form" class="parsley-examples" method="post" id="otherPolicyTerms" enctype="multipart/form-data">
|
||||
|
||||
<input type="hidden" value="<?= csrf_hash() ?>" name="<?= csrf_token() ?>"/>
|
||||
<input type="hidden" value="<?= csrf_hash() ?>" name="<?= csrf_token() ?>" />
|
||||
<input type="hidden" name="client_id" id="Client_id" value="<?= isset($client['id']) ? $client['id'] : '' ?>" />
|
||||
<input type="hidden" name="client_policy_id" id="other_client_policy_id" />
|
||||
<input type="hidden" name="policy_id" id="gpa_policy_id" />
|
||||
<input type="hidden" id="emp_count" />
|
||||
|
||||
<div id="sumInsuredDiv" class="row" style="margin-bottom: 10px;">
|
||||
<div class="col-md-6">
|
||||
<label for="sumInsured">Sum Insured</label>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<input style="width: 128%;" type="text" name="sum_insureds" id="sum_insured_others" class="form-control" onkeypress = "return onlyNumbers(event)" onkeyup=" formatNumber(this); si_keup_num_to_word2(this)">
|
||||
</div>
|
||||
<div class="col-md-2" style="position: relative;left: 88px;">
|
||||
<button type="button" class="btn btn-primary si-add-more" onclick="appendOtherSIAddMore()">+</button>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div id='numberToWordOthers' class="text-danger-2" ></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="sumInsuredDiv" class="row" style="margin-bottom: 10px;">
|
||||
<div class="col-md-6">
|
||||
<label for="sumInsured">Sum Insured</label>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<input style="width: 128%;" type="text" name="sum_insureds" id="sum_insured_others" class="form-control" onkeypress="return onlyNumbers(event)" onkeyup=" formatNumber(this); si_keup_num_to_word2(this)">
|
||||
</div>
|
||||
<div class="col-md-2" style="position: relative;left: 88px;">
|
||||
<button type="button" class="btn btn-primary si-add-more" onclick="appendOtherSIAddMore()">+</button>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div id='numberToWordOthers' class="text-danger-2"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 10px;margin-bottom:15px;" id="sum_insured_add_more" ></div>
|
||||
<div style="margin-top: 10px;margin-bottom:15px;" id="sum_insured_add_more"></div>
|
||||
<div id="append_html_for_other_policy_terms"></div>
|
||||
|
||||
<hr>
|
||||
<div id="familyFloaterDiv_others" class="row mb-2">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Family Floater</label>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" id="familyFloaterYes_others" name="family_floater" value="1">
|
||||
<label class="form-check-label" for="familyFloaterYes_others" style="width: 52px !important">Yes</label>
|
||||
|
||||
<input class="form-check-input" type="radio" id="familyFloaterNo_others" name="family_floater" value="0">
|
||||
<label class="form-check-label" for="familyFloaterNo_others">No</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="familyFloaterDiv_others_two" class="row" style="margin-bottom: 10px;">
|
||||
|
||||
<div class="col" style="background-color:#F5FFFF;border-radius:10px;box-shadow:0px 2px 2px 0px #00000040;padding:20px;">
|
||||
|
||||
<!-- SELF -->
|
||||
<div class="d-flex align-items-center policy-base-info">
|
||||
<input type="checkbox" name="family_floaters[]" value="self" id="self_others" checked>
|
||||
|
||||
<span style="margin-right:150px;color:black;">Self:</span>
|
||||
|
||||
<div class="self-age">
|
||||
<span style="margin-right:10px;color:black;">Min Age:</span>
|
||||
<input class="underline-input min_age"
|
||||
type="number"
|
||||
name="self_min_age"
|
||||
id="self_min_age_others"
|
||||
value="<?= isset($self_min_age) && $self_min_age > 0 ? $self_min_age : '18'; ?>"
|
||||
onchange="lowerIsEighteenOthers(this)">
|
||||
</div>
|
||||
|
||||
<div class="self-age">
|
||||
<span style="margin-right:10px;color:black;">Max Age:</span>
|
||||
<input class="underline-input max_age"
|
||||
type="number"
|
||||
name="self_max_age"
|
||||
id="self_max_age_others"
|
||||
value="<?= isset($self_max_age) ? $self_max_age : '60'; ?>"
|
||||
onchange="lowerIsEighteenOthers(this)">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SPOUSE -->
|
||||
<div class="d-flex align-items-center policy-base-info">
|
||||
<input type="checkbox" name="family_floaters[]" value="spouse" id="spouse_others">
|
||||
|
||||
<span style="margin-right:125px;color:black;">Spouse:</span>
|
||||
|
||||
<div class="spouse-age">
|
||||
<span style="margin-right:10px;color:black;">Min Age:</span>
|
||||
<input class="underline-input min_age"
|
||||
type="number"
|
||||
name="spouse_min_age"
|
||||
id="spouse_min_age_others"
|
||||
value="<?= isset($spouse_min_age) ? $spouse_min_age : '18'; ?>"
|
||||
onchange="lowerIsEighteenOthers(this)">
|
||||
</div>
|
||||
|
||||
<div class="spouse-age">
|
||||
<span style="margin-right:10px;color:black;">Max Age:</span>
|
||||
<input class="underline-input max_age"
|
||||
type="number"
|
||||
name="spouse_max_age"
|
||||
id="spouse_max_age_others"
|
||||
value="<?= isset($spouse_max_age) ? $spouse_max_age : '60'; ?>"
|
||||
onchange="lowerIsEighteenOthers(this)">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- CHILDREN -->
|
||||
<div class="d-flex align-items-center policy-base-info">
|
||||
<span style="color:black;" class="mr-2">Children:</span>
|
||||
|
||||
<select name="family_floaters[]" id="children_others" class="px-1" style="margin-right:60px;">
|
||||
<option value="0">None</option>
|
||||
<option value="1">1</option>
|
||||
<option value="2">2</option>
|
||||
<option value="3">3</option>
|
||||
<option value="4">4</option>
|
||||
</select>
|
||||
|
||||
<div class="child-age">
|
||||
<span style="margin-right:10px;color:black;">Min Age:</span>
|
||||
<input class="underline-input min_age"
|
||||
type="number"
|
||||
name="child_min_age"
|
||||
id="child_min_age_others"
|
||||
value="<?= isset($child_min_age) ? $child_min_age : '0'; ?>">
|
||||
</div>
|
||||
|
||||
<div class="child-age">
|
||||
<span style="margin-right:10px;color:black;">Max Age:</span>
|
||||
<input class="underline-input max_age"
|
||||
type="number"
|
||||
name="child_max_age"
|
||||
id="child_max_age_others"
|
||||
value="<?= isset($child_max_age) ? $child_max_age : '25'; ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- OTHER MEMBERS -->
|
||||
<div class="d-flex align-items-center policy-base-info flex-nowrap">
|
||||
|
||||
<span class="fw-bold text-dark mr-3"><b>Select Other Members:</b></span>
|
||||
|
||||
<select name="family_floaters[]" id="family_floaters_others" class="form-select mr-3 px-1">
|
||||
<option value="0">None</option>
|
||||
<option value="1P">Only one parent</option>
|
||||
<option value="2P">Only two parents</option>
|
||||
<option value="1PIL">Only one parent in law</option>
|
||||
<option value="2PIL">Only two parents in law</option>
|
||||
<option value="2EPORPIL">Parents or PIL (Any two)</option>
|
||||
<option value="EPORPIL">Either Parents or PIL</option>
|
||||
<option value="4EPORPIL">Parents + PIL</option>
|
||||
</select>
|
||||
|
||||
<span class="text-dark mr-2">Elders Count:</span>
|
||||
<input class="underline-input"
|
||||
type="number"
|
||||
name="elder_member_count"
|
||||
id="member_count_others"
|
||||
readonly>
|
||||
|
||||
<span class="text-dark mr-2">Min Age:</span>
|
||||
<input class="underline-input"
|
||||
type="number"
|
||||
name="other_member_min_age"
|
||||
id="other_member_min_age_others"
|
||||
value="<?= isset($other_member_min_age) ? $other_member_min_age : '18'; ?>">
|
||||
|
||||
<span class="text-dark mr-2">Max Age:</span>
|
||||
<input class="underline-input"
|
||||
type="number"
|
||||
name="other_member_max_age"
|
||||
id="other_member_max_age_others"
|
||||
value="<?= isset($other_member_max_age) ? $other_member_max_age : '60'; ?>">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="other_special_condition">
|
||||
|
||||
</div>
|
||||
|
||||
<br>
|
||||
@ -126,7 +276,6 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
$("#otherPolicyTerms").submit(function(event) {
|
||||
|
||||
event.preventDefault();
|
||||
@ -162,10 +311,9 @@
|
||||
formObject[baseKey] = [];
|
||||
}
|
||||
formObject[baseKey].push(value.replace(/,/g, ''));
|
||||
} else if(key == 'sum_insureds'){
|
||||
} else if (key == 'sum_insureds') {
|
||||
formObject['sum_insured'] = value.replace(/,/g, '');
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
formObject[key] = value;
|
||||
}
|
||||
}
|
||||
@ -215,7 +363,10 @@
|
||||
}
|
||||
});
|
||||
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
setTimeout(function() {
|
||||
$('.loader').fadeOut();
|
||||
$('.loader-mask').delay(350).fadeOut('slow');
|
||||
@ -295,7 +446,7 @@
|
||||
if (key.includes("special_condition_label") || key.includes(
|
||||
"special_condition_input")) {
|
||||
if (key.includes("special_condition_label")) {
|
||||
|
||||
|
||||
for (let index = 0; index <
|
||||
otherTermsJsonObjectSpecialCondition[key].length; index++) {
|
||||
specialConditionForOthers();
|
||||
@ -319,12 +470,12 @@
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (!key.startsWith("special_condition") && !key.startsWith('multiple_sum_insured') && !key.startsWith("sum_insured") && !key.startsWith("enrollment_display_key") ) {
|
||||
|
||||
termsHTML = `
|
||||
if (!key.startsWith("special_condition") && !key.startsWith('multiple_sum_insured') && !key.startsWith("sum_insured") && !key.startsWith("enrollment_display_key")) {
|
||||
|
||||
termsHTML = `
|
||||
<div class="row" style="margin-bottom: 10px;">
|
||||
<div class="col-md-1">
|
||||
<input type="checkbox" style="margin-top: 12px" name="${key}_display"
|
||||
@ -350,7 +501,7 @@
|
||||
value.forEach(item => appendOtherSIAddMore(item));
|
||||
} else if (typeof value === 'object' && value !== null) {
|
||||
Object.values(value).forEach(item => appendOtherSIAddMore(item));
|
||||
}
|
||||
}
|
||||
// else if (typeof value === 'string') {
|
||||
// appendOtherSIAddMore(value); // Use directly, no loop
|
||||
// }
|
||||
@ -368,10 +519,10 @@
|
||||
|
||||
let elements = document.getElementsByName(key);
|
||||
|
||||
if(key == 'sum_insured'){
|
||||
if (key == 'sum_insured') {
|
||||
elements = document.getElementsByName('sum_insureds')
|
||||
}
|
||||
|
||||
|
||||
console.log(elements)
|
||||
|
||||
if (elements && elements.length > 0) {
|
||||
@ -424,17 +575,19 @@
|
||||
|
||||
}
|
||||
|
||||
if(policy_type_id > 7) {
|
||||
if (policy_type_id > 7 && policy_type_id != 72) {
|
||||
$("#sumInsuredDiv").empty();
|
||||
}
|
||||
|
||||
$("#sumInsuredDiv").empty();
|
||||
if(policy_type_id != 72){
|
||||
$("#familyFloaterDiv_others").empty();
|
||||
$("#familyFloaterDiv_others_two").empty();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<script>
|
||||
|
||||
document.getElementById('otherButtonSpecialCondition').addEventListener('click', function(event) {
|
||||
|
||||
console.log('specialConditionForOthers callback clicked')
|
||||
@ -461,7 +614,7 @@
|
||||
<span class="specialConditionClose" style="color: red; float: right;position: relative;bottom: 28px;left: 15px;">X</span>
|
||||
</label>
|
||||
|
||||
<input type="text" name="special_condition_input[]" id="special_condition_input[]" class="form-control s special_condition_input[]">
|
||||
<input type="text" name="special_condition_input[]" id="special_condition_input[]" class="form-control other-speciial-condition special_condition_input[]">
|
||||
</div>`;
|
||||
console.log($(this));
|
||||
$('.other_special_condition').each(function() {
|
||||
@ -475,7 +628,7 @@
|
||||
|
||||
$(document).on('click', '#otherPolicyTermsClose', function() {
|
||||
|
||||
//clear the intervel for the autosave
|
||||
//clear the intervel for the autosave
|
||||
stopAutoSubmit();
|
||||
|
||||
$('#OtherPolicyTermsForm').css('display', 'none');
|
||||
@ -528,27 +681,27 @@
|
||||
|
||||
if (policy_type == 7) {
|
||||
|
||||
|
||||
|
||||
// <div class="row" style="margin-bottom: 10px;">
|
||||
// <div class="col-md-6">
|
||||
// <label for="sumInsured">Sum Insured</label>
|
||||
// </div>
|
||||
// <div class="col-md-4">
|
||||
// <input style="width: 128%;" type="text" name="sum_insureds" id="sum_insured_others" class="form-control" onkeypress = "return onlyNumbers(event)" onkeyup=" formatNumber(this); si_keup_num_to_word2(this)">
|
||||
// </div>
|
||||
// <div class="col-md-2" style="position: relative;left: 88px;">
|
||||
// <button type="button" class="btn btn-primary si-add-more" onclick="appendOtherSIAddMore()">+</button>
|
||||
// </div>
|
||||
// <div class="col-md-6">
|
||||
// </div>
|
||||
// <div class="col-md-6">
|
||||
// <div id='numberToWordOthers' class="text-danger-2" ></div>
|
||||
// </div>
|
||||
// </div>
|
||||
|
||||
// <div id="sum_insured_add_more" ></div>
|
||||
termsHTML = `
|
||||
// <div class="row" style="margin-bottom: 10px;">
|
||||
// <div class="col-md-6">
|
||||
// <label for="sumInsured">Sum Insured</label>
|
||||
// </div>
|
||||
// <div class="col-md-4">
|
||||
// <input style="width: 128%;" type="text" name="sum_insureds" id="sum_insured_others" class="form-control" onkeypress = "return onlyNumbers(event)" onkeyup=" formatNumber(this); si_keup_num_to_word2(this)">
|
||||
// </div>
|
||||
// <div class="col-md-2" style="position: relative;left: 88px;">
|
||||
// <button type="button" class="btn btn-primary si-add-more" onclick="appendOtherSIAddMore()">+</button>
|
||||
// </div>
|
||||
// <div class="col-md-6">
|
||||
// </div>
|
||||
// <div class="col-md-6">
|
||||
// <div id='numberToWordOthers' class="text-danger-2" ></div>
|
||||
// </div>
|
||||
// </div>
|
||||
|
||||
// <div id="sum_insured_add_more" ></div>
|
||||
termsHTML = `
|
||||
|
||||
<div class="form-group">
|
||||
<div class="row" style="margin-bottom: 10px;">
|
||||
@ -646,27 +799,27 @@
|
||||
|
||||
} else if (policy_type == 6) {
|
||||
|
||||
|
||||
|
||||
// <div class="row" style="margin-bottom: 10px;">
|
||||
// <div class="col-md-6">
|
||||
// <label for="sumInsured">Sum Insured</label>
|
||||
// </div>
|
||||
// <div class="col-md-4" >
|
||||
// <input style="width: 128%;" type="text" name="sum_insureds" id="sum_insured_others" class="form-control" onkeypress = "return onlyNumbers(event)" onkeyup=" formatNumber(this); si_keup_num_to_word2(this)">
|
||||
// </div>
|
||||
// <div class="col-md-2" style="position: relative;left: 88px;">
|
||||
// <button type="button" class="btn btn-primary si-add-more" onclick="appendOtherSIAddMore()">+</button>
|
||||
// </div>
|
||||
// <div class="col-md-6">
|
||||
// </div>
|
||||
// <div class="col-md-6">
|
||||
// <div id='numberToWordOthers' class="text-danger-2" ></div>
|
||||
// </div>
|
||||
// </div>
|
||||
|
||||
// <div id="sum_insured_add_more" ></div>
|
||||
termsHTML = `
|
||||
// <div class="row" style="margin-bottom: 10px;">
|
||||
// <div class="col-md-6">
|
||||
// <label for="sumInsured">Sum Insured</label>
|
||||
// </div>
|
||||
// <div class="col-md-4" >
|
||||
// <input style="width: 128%;" type="text" name="sum_insureds" id="sum_insured_others" class="form-control" onkeypress = "return onlyNumbers(event)" onkeyup=" formatNumber(this); si_keup_num_to_word2(this)">
|
||||
// </div>
|
||||
// <div class="col-md-2" style="position: relative;left: 88px;">
|
||||
// <button type="button" class="btn btn-primary si-add-more" onclick="appendOtherSIAddMore()">+</button>
|
||||
// </div>
|
||||
// <div class="col-md-6">
|
||||
// </div>
|
||||
// <div class="col-md-6">
|
||||
// <div id='numberToWordOthers' class="text-danger-2" ></div>
|
||||
// </div>
|
||||
// </div>
|
||||
|
||||
// <div id="sum_insured_add_more" ></div>
|
||||
termsHTML = `
|
||||
|
||||
<div class="form-group EDLI">
|
||||
|
||||
@ -765,14 +918,14 @@
|
||||
|
||||
function beautifyString(str) {
|
||||
return str
|
||||
.split('_') // Split the string by underscores
|
||||
.map(word =>
|
||||
word.charAt(0).toUpperCase() + word.slice(1).toLowerCase() // Capitalize each word
|
||||
.split('_') // Split the string by underscores
|
||||
.map(word =>
|
||||
word.charAt(0).toUpperCase() + word.slice(1).toLowerCase() // Capitalize each word
|
||||
)
|
||||
.join(' '); // Join with spaces instead of underscores
|
||||
.join(' '); // Join with spaces instead of underscores
|
||||
}
|
||||
|
||||
function autoSaveOtherTerms(){
|
||||
function autoSaveOtherTerms() {
|
||||
|
||||
console.log('autoSaveOtherTerms function called');
|
||||
|
||||
@ -789,10 +942,10 @@
|
||||
var formData = new FormData(form);
|
||||
var hasData = false;
|
||||
|
||||
|
||||
|
||||
for (let [key, value] of formData.entries()) {
|
||||
console.log("Key:", key, "Value:", value);
|
||||
if(key == "sum_insureds"){
|
||||
if (key == "sum_insureds") {
|
||||
if (value && value.toString().trim() !== '') {
|
||||
hasData = true;
|
||||
break;
|
||||
@ -820,10 +973,9 @@
|
||||
formObject[baseKey] = [];
|
||||
}
|
||||
formObject[baseKey].push(value.replace(/,/g, ''));
|
||||
} else if(key == 'sum_insureds'){
|
||||
} else if (key == 'sum_insureds') {
|
||||
formObject['sum_insured'] = value.replace(/,/g, '');
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
formObject[key] = value;
|
||||
}
|
||||
}
|
||||
@ -853,7 +1005,7 @@
|
||||
|
||||
if (res.status == true) {
|
||||
showTinyToast();
|
||||
}else{
|
||||
} else {
|
||||
console.log('Policy terms Dose Not save', 'Error');
|
||||
return;
|
||||
}
|
||||
@ -867,19 +1019,19 @@
|
||||
}
|
||||
|
||||
function debounceothers(fn, delay) {
|
||||
let timeout;
|
||||
return function () {
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(fn, delay);
|
||||
};
|
||||
let timeout;
|
||||
return function() {
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(fn, delay);
|
||||
};
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
$(document).ready(function() {
|
||||
const form = document.querySelector("#otherPolicyTerms");
|
||||
|
||||
// MutationObserver (always runs)
|
||||
const observer = new MutationObserver(() => {
|
||||
$("#otherPolicyTerms").find("input, select, textarea").each(function () {
|
||||
$("#otherPolicyTerms").find("input, select, textarea").each(function() {
|
||||
if (other_auto_save_enable) { // only bind autosave if enabled
|
||||
if (!$(this).attr("data-observed")) {
|
||||
const debouncedSave = debounceothers(autoSaveOtherTerms, 1000);
|
||||
@ -890,18 +1042,103 @@
|
||||
});
|
||||
});
|
||||
|
||||
observer.observe(form, { childList: true, subtree: true });
|
||||
observer.observe(form, {
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
|
||||
// Bind for existing fields (only if enabled)
|
||||
if (other_auto_save_enable) {
|
||||
const debouncedSave = debounceothers(autoSaveOtherTerms, 1000);
|
||||
$("#otherPolicyTerms").find("input, select, textarea").each(function () {
|
||||
$("#otherPolicyTerms").find("input, select, textarea").each(function() {
|
||||
$(this).on("input change", debouncedSave);
|
||||
$(this).attr("data-observed", "true");
|
||||
});
|
||||
} else {
|
||||
console.log("This policy has employee processed (autosave disabled) -- OTHERS") ;
|
||||
console.log("This policy has employee processed (autosave disabled) -- OTHERS");
|
||||
}
|
||||
});
|
||||
|
||||
$('#self_others').change(function() {
|
||||
if ($(this).prop('checked')) {
|
||||
$('.self-age').css('display', '');
|
||||
} else {
|
||||
$('.self-age').css('display', 'none');
|
||||
}
|
||||
});
|
||||
|
||||
$('#spouse_others').change(function() {
|
||||
console.log('spouse changed')
|
||||
if ($(this).prop('checked')) {
|
||||
$('.spouse-age').css('display', '');
|
||||
if ($('#spouse_min_age_others').val() < 17 || $('#spouse_min_age_others').val() == '') {
|
||||
$('#spouse_min_age_others').val(18);
|
||||
}
|
||||
if ($('#spouse_max_age_others').val() < 17 || $('#spouse_max_age_others').val() == '') {
|
||||
$('#spouse_max_age_others').val(60);
|
||||
}
|
||||
} else {
|
||||
$('.spouse-age').css('display', 'none');
|
||||
}
|
||||
});
|
||||
|
||||
$('#children_others').change(function() {
|
||||
if ($(this).val() != 0) {
|
||||
$('.child-age').css('display', '');
|
||||
|
||||
if ($('#child_min_age_others').val() < 0 || $('#child_min_age_others').val() == '') {
|
||||
$('#child_min_age_others').val(0);
|
||||
}
|
||||
if ($('#child_max_age_others').val() < 24 || $('#child_max_age_others').val() == '') {
|
||||
$('#child_max_age_others').val(25);
|
||||
}
|
||||
} else {
|
||||
$('.child-age').css('display', 'none');
|
||||
}
|
||||
});
|
||||
|
||||
$('#family_floaters_others').change(function() {
|
||||
|
||||
checkAdditionPremiumJSON()
|
||||
|
||||
var family_floaters = $(this).val();
|
||||
if ($('#family_floaters').val() != 0) {
|
||||
$('.other-member-age').css('display', '');
|
||||
if ($('#other_member_min_age_others').val() < 17 || $('#other_member_min_age_others').val() == '') {
|
||||
$('#other_member_min_age_others').val(18);
|
||||
}
|
||||
if ($('#other_member_max_age_others').val() < 79 || $('#other_member_max_age_others').val() == '') {
|
||||
$('#other_member_max_age_others').val(60);
|
||||
}
|
||||
|
||||
} else {
|
||||
$('.other-member-age').css('display', 'none');
|
||||
}
|
||||
|
||||
if (family_floaters == '1P') {
|
||||
$('#member_count_others').val(1);
|
||||
} else if (family_floaters == '2P') {
|
||||
$('#member_count_others').val(2);
|
||||
} else if (family_floaters == '1PIL') {
|
||||
$('#member_count_others').val(1);
|
||||
} else if (family_floaters == '2PIL') {
|
||||
$('#member_count_others').val(2);
|
||||
} else if (family_floaters == '2EPORPIL') {
|
||||
$('#member_count_others').val(2);
|
||||
} else if (family_floaters == 'EPORPIL') {
|
||||
$('#member_count_others').val(2);
|
||||
} else if (family_floaters == '4EPORPIL') {
|
||||
$('#member_count_others').val(4);
|
||||
}
|
||||
});
|
||||
|
||||
function lowerIsEighteenOthers(params) {
|
||||
var value = $(params).val();
|
||||
|
||||
if (value < 18) {
|
||||
$(params).val(18);
|
||||
} else if (value > 99) {
|
||||
$(params).val(99);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@ -3522,7 +3522,7 @@ function hideShowPolicyPremium(policy_type_string, unique_id = null)
|
||||
individual = $('#individual_' + unique_id);
|
||||
}
|
||||
|
||||
if (policy_type_string == 'GMC') {
|
||||
if (policy_type_string == 'GMC' || policy_type_string == 'OPD') {
|
||||
premium_type.show();
|
||||
individual.prop('checked', true)
|
||||
} else {
|
||||
|
||||
@ -3,6 +3,32 @@
|
||||
position: absolute;
|
||||
}
|
||||
.dataTables_length label {height: 21px !important;}
|
||||
.custom-dropdown-menu {
|
||||
display: none;
|
||||
position: absolute;
|
||||
background-color: #ffffff !important;
|
||||
border: 1px solid rgba(0, 0, 0, 0.15);
|
||||
border-radius: 0.25rem;
|
||||
padding: 0.5rem 0;
|
||||
min-width: 10rem;
|
||||
z-index: 9999;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.custom-dropdown-menu .dropdown-item {
|
||||
display: block !important;
|
||||
width: 100% !important;
|
||||
padding: 0.5rem 1rem !important;
|
||||
color: #212529 !important;
|
||||
text-decoration: none !important;
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.custom-dropdown-menu .dropdown-item:hover {
|
||||
background-color: #f8f9fa !important;
|
||||
color: #16181b !important;
|
||||
cursor: pointer !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- End ADD and EDIT Page HTML -->
|
||||
@ -24,7 +50,7 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if(isset($data['pos_list'])) { $slno = 1; ?>
|
||||
<?php if(!empty($data['pos_list'])) { $slno = 1; ?>
|
||||
<?php foreach($data['pos_list'] as $index => $row) { ?>
|
||||
<tr>
|
||||
<td class="text-center"><?= $slno++; ?></td>
|
||||
@ -54,12 +80,7 @@
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
<?php } else { ?>
|
||||
<tr>
|
||||
<td colspan="3" class="text-center">No data available</td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
<?php } } ?>
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
@ -480,5 +501,94 @@
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
const table = document.getElementById("user-table");
|
||||
|
||||
// Create custom dropdown
|
||||
function createCustomDropdown(row) {
|
||||
// Get the original dropdown items
|
||||
const originalDropdown = row.querySelector('.dropdown-menu');
|
||||
if (!originalDropdown) return null;
|
||||
|
||||
// Create new dropdown with proper background and spacing
|
||||
const customDropdown = document.createElement('div');
|
||||
customDropdown.className = 'custom-dropdown-menu';
|
||||
|
||||
// Copy inner content while maintaining icon alignment
|
||||
customDropdown.innerHTML = originalDropdown.innerHTML;
|
||||
|
||||
return customDropdown;
|
||||
}
|
||||
|
||||
let activeDropdown = null;
|
||||
|
||||
// Add click event listener to rows
|
||||
table.querySelectorAll("tbody tr").forEach(row => {
|
||||
const customDropdown = createCustomDropdown(row);
|
||||
if (!customDropdown) return;
|
||||
|
||||
// Append the custom dropdown to the body
|
||||
document.body.appendChild(customDropdown);
|
||||
|
||||
row.addEventListener("click", function(event) {
|
||||
// Ignore clicks on the action column
|
||||
if (event.target.closest('td:last-child')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Hide any active dropdown
|
||||
if (activeDropdown) {
|
||||
activeDropdown.style.display = 'none';
|
||||
}
|
||||
|
||||
// Get click position
|
||||
const rect = event.target.getBoundingClientRect();
|
||||
|
||||
// Position the dropdown with some offset
|
||||
customDropdown.style.display = 'block';
|
||||
customDropdown.style.position = 'fixed';
|
||||
customDropdown.style.left = `${rect.left}px`;
|
||||
customDropdown.style.top = `${rect.bottom + 5}px`; // Add 5px gap
|
||||
|
||||
// Set as active dropdown
|
||||
activeDropdown = customDropdown;
|
||||
|
||||
event.stopPropagation();
|
||||
});
|
||||
|
||||
// Preserve click handlers and add auto-close
|
||||
customDropdown.querySelectorAll('.dropdown-item').forEach(item => {
|
||||
item.addEventListener('click', function(e) {
|
||||
const onclickAttr = this.getAttribute('onclick');
|
||||
if (onclickAttr) {
|
||||
eval(onclickAttr);
|
||||
}
|
||||
|
||||
const href = this.getAttribute('href');
|
||||
if (href && href !== '#') {
|
||||
window.location.href = href;
|
||||
}
|
||||
|
||||
// Close the dropdown after handling the click
|
||||
if (activeDropdown) {
|
||||
activeDropdown.style.display = 'none';
|
||||
activeDropdown = null;
|
||||
}
|
||||
|
||||
e.stopPropagation();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Close dropdown when clicking outside
|
||||
document.addEventListener("click", function() {
|
||||
if (activeDropdown) {
|
||||
activeDropdown.style.display = 'none';
|
||||
activeDropdown = null;
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
"ext-intl": "*",
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*",
|
||||
"aws/aws-sdk-php": "^3.369",
|
||||
"botman/botman": "^2.8",
|
||||
"botman/driver-web": "^1.5",
|
||||
"dompdf/dompdf": "^2.0",
|
||||
@ -72,7 +73,7 @@
|
||||
]
|
||||
},
|
||||
"config": {
|
||||
"optimize-autoloader": true,
|
||||
"optimize-autoloader": false,
|
||||
"preferred-install": "dist",
|
||||
"sort-packages": true,
|
||||
"process-timeout": 900
|
||||
|
||||
Loading…
Reference in New Issue
Block a user