GWM : icici & volo tpa integration
This commit is contained in:
commit
d2a0c8e7a2
11
.env.sample
11
.env.sample
@ -63,7 +63,8 @@ NHANCE_LOGO =
|
||||
helpdeskURL =
|
||||
TOKENTIMEOUT =
|
||||
POST_ENROLLMENT_APP_LINK =
|
||||
GDRIVE_ROOT_FOLDER_ID =
|
||||
GDRIVE_ROOT_FOLDER_ID =
|
||||
RFQ_PARENT_FOLDER_ID =
|
||||
|
||||
|
||||
email.fromEmail =
|
||||
@ -150,3 +151,11 @@ LEAD_CLIENT_FROM_MAIL_ID =
|
||||
# BDS Daily Report Emails Configuration
|
||||
bds.dailyReportEmails =
|
||||
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# MEDI ASSIST WELLNESS SSO Configuration
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
MEDIASSIST_WELLNESS_KEY =
|
||||
MEDIASSIST_WELLNESS_IV =
|
||||
MEDIASSIST_WELLNESS_LOGIN_URL =
|
||||
|
||||
@ -15,8 +15,16 @@ class Acl
|
||||
'#^/loginPos#' => ['public' => true],
|
||||
'#^/getVerifyPosMobileNo#' => ['public' => true],
|
||||
'#^/getVerifiedPosUserData#' => ['public' => true],
|
||||
'#^/chat#' => ['public' => true],
|
||||
'#^/widget#' => ['public' => true],
|
||||
'#^/chatbottest#' => ['public' => true],
|
||||
'#^/chatbot#' => ['public' => true],
|
||||
'#^/getSSORedirectUrl#' => ['public' => true],
|
||||
'#^/getEmployeePolicy#' => ['public' => true],
|
||||
'#^/downloadFileTableFile#' => ['public' => true],
|
||||
'#^/swagger#' => ['roles' => [ADMIN_ROLE_ID]],
|
||||
'#^/getEmployeeActiveOrInactivePolicy#' => ['roles' => [ADMIN_ROLE_ID, HEAD_ROLE_ID]],
|
||||
'#^/test/chartbrewDashboardDemo#' => ['roles' => [ADMIN_ROLE_ID, HEAD_ROLE_ID]],
|
||||
'#^/bulkEcardDownloadAsZip#' => ['roles' => [ADMIN_ROLE_ID, HEAD_ROLE_ID]],
|
||||
'#^/sheet#' => ['roles' => [ADMIN_ROLE_ID, HEAD_ROLE_ID]],
|
||||
'#^/sendextraparam#' => ['roles' => [ADMIN_ROLE_ID, HEAD_ROLE_ID]],
|
||||
@ -54,6 +62,34 @@ class Acl
|
||||
'roles' => [ ADMIN_ROLE_ID,HEAD_ROLE_ID, MANAGER_ROLE_ID, STAFF_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => []
|
||||
],
|
||||
'#^/hrFileDownload#' => [
|
||||
'roles' => [ ADMIN_ROLE_ID,HEAD_ROLE_ID, MANAGER_ROLE_ID, STAFF_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => []
|
||||
],
|
||||
'#^/sendPushNotification#' => [
|
||||
'roles' => [ ADMIN_ROLE_ID,HEAD_ROLE_ID, MANAGER_ROLE_ID, STAFF_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => []
|
||||
],
|
||||
'#^/sendEmail#' => [
|
||||
'roles' => [ ADMIN_ROLE_ID,HEAD_ROLE_ID, MANAGER_ROLE_ID, STAFF_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => []
|
||||
],
|
||||
'#^/getPolicyLevelEmployeeSummaryData#' => [
|
||||
'roles' => [ ADMIN_ROLE_ID,HEAD_ROLE_ID, MANAGER_ROLE_ID, STAFF_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => []
|
||||
],
|
||||
'#^/cdTransactionData#' => [
|
||||
'roles' => [ ADMIN_ROLE_ID,HEAD_ROLE_ID, MANAGER_ROLE_ID, STAFF_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => []
|
||||
],
|
||||
'#^/claimsSearch#' => [
|
||||
'roles' => [ ADMIN_ROLE_ID,HEAD_ROLE_ID, MANAGER_ROLE_ID, STAFF_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [CLAIMS_TEAM_ID]
|
||||
],
|
||||
'#^/getBackToEnrolledDetails#' => [
|
||||
'roles' => [ ADMIN_ROLE_ID,HEAD_ROLE_ID, MANAGER_ROLE_ID, STAFF_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => []
|
||||
],
|
||||
|
||||
// ===================== USER MANAGEMENT =====================
|
||||
'#^/user#' => [
|
||||
@ -88,6 +124,18 @@ class Acl
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [FINANCE_TEAM_ID,POS_TEAM_ID]
|
||||
],
|
||||
'#^/dmsSearch#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [FINANCE_TEAM_ID,POS_TEAM_ID]
|
||||
],
|
||||
'#^/downloadGdriveFile#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [FINANCE_TEAM_ID,POS_TEAM_ID]
|
||||
],
|
||||
'#^/driveListFiles#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [FINANCE_TEAM_ID,POS_TEAM_ID]
|
||||
],
|
||||
'#^/bds_upload#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [FINANCE_TEAM_ID,POS_TEAM_ID]
|
||||
@ -108,12 +156,60 @@ class Acl
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [FINANCE_TEAM_ID,POS_TEAM_ID]
|
||||
],
|
||||
'#^/importRules#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [FINANCE_TEAM_ID,POS_TEAM_ID]
|
||||
],
|
||||
|
||||
// ===================== CLAIMS / TICKETS =====================
|
||||
'#^/ticket#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [CLAIMS_TEAM_ID]
|
||||
],
|
||||
'#^/ticketSave#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [CLAIMS_TEAM_ID]
|
||||
],
|
||||
'#^/ticketList#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [CLAIMS_TEAM_ID]
|
||||
],
|
||||
'#^/ticketConversationSave#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [CLAIMS_TEAM_ID]
|
||||
],
|
||||
'#^/ticketConversationList#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [CLAIMS_TEAM_ID]
|
||||
],
|
||||
'#^/ticketType#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [CLAIMS_TEAM_ID]
|
||||
],
|
||||
'#^/ticketHistoryList#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [CLAIMS_TEAM_ID]
|
||||
],
|
||||
'#^/ticketAutoFetchDetails#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [CLAIMS_TEAM_ID]
|
||||
],
|
||||
'#^/initiateClaim#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [CLAIMS_TEAM_ID]
|
||||
],
|
||||
'#^/departments#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [CLAIMS_TEAM_ID]
|
||||
],
|
||||
'#^/get_ticket_data#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [CLAIMS_TEAM_ID]
|
||||
],
|
||||
'#^/claimView#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [CLAIMS_TEAM_ID]
|
||||
],
|
||||
'#^/claim_mis#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [CLAIMS_TEAM_ID]
|
||||
@ -126,11 +222,11 @@ class Acl
|
||||
],
|
||||
'#^/rfq#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [SALES_TEAM_ID]
|
||||
'teams' => [BUSINESS_SUPPORT_TEAM_ID, SALES_TEAM_ID]
|
||||
],
|
||||
'#^/sales#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID],
|
||||
'teams' => [SALES_TEAM_ID]
|
||||
'teams' => [BUSINESS_SUPPORT_TEAM_ID, SALES_TEAM_ID]
|
||||
],
|
||||
|
||||
// ===================== CMS / CONTENT =====================
|
||||
@ -138,6 +234,22 @@ class Acl
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID,STAFF_ROLE_ID],
|
||||
'teams' => []
|
||||
],
|
||||
'#^/add_advertise_image#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID,STAFF_ROLE_ID],
|
||||
'teams' => []
|
||||
],
|
||||
'#^/remove_advertise_image#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID,STAFF_ROLE_ID],
|
||||
'teams' => []
|
||||
],
|
||||
'#^/getAdvertiseImage#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID,STAFF_ROLE_ID],
|
||||
'teams' => []
|
||||
],
|
||||
'#^/showAdvertiseImage#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID,STAFF_ROLE_ID],
|
||||
'teams' => []
|
||||
],
|
||||
'#^/frontend_content#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID,STAFF_ROLE_ID],
|
||||
'teams' => []
|
||||
@ -146,6 +258,10 @@ class Acl
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID,STAFF_ROLE_ID],
|
||||
'teams' => []
|
||||
],
|
||||
'#^/getFEContent#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID,STAFF_ROLE_ID],
|
||||
'teams' => []
|
||||
],
|
||||
|
||||
// ===================== LOGS =====================
|
||||
'#^/logs#' => [
|
||||
@ -167,9 +283,25 @@ class Acl
|
||||
// ===================== WEBHOOKS / 3RD PARTY =====================
|
||||
'#^/dispatchWebhookData#' => ['public' => true],
|
||||
'#^/retrieveWebhookData#' => ['public' => true],
|
||||
// '#^/ICICI#' => ['public' => true],
|
||||
// '#^/Vidal#' => ['public' => true],
|
||||
// '#^/MediAssist#' => ['public' => true],
|
||||
'#^/ecardRequest#' => ['public' => true],
|
||||
'#^/getTPAID#' => ['public' => true],
|
||||
'#^/fileDownload#' => ['public' => true],
|
||||
'#^/generateAuthToken#' => ['public' => true],
|
||||
'#^/createEnrollmentBatch#' => ['roles' => [ADMIN_ROLE_ID, HEAD_ROLE_ID], 'teams' => [ENROLLMENT_TEAM_ID]],
|
||||
'#^/getEnrollmentBatchStatus#' => ['roles' => [ADMIN_ROLE_ID, HEAD_ROLE_ID], 'teams' => [ENROLLMENT_TEAM_ID]],
|
||||
'#^/fetchUHIDDetails#' => ['roles' => [ADMIN_ROLE_ID, HEAD_ROLE_ID], 'teams' => [ENROLLMENT_TEAM_ID]],
|
||||
'#^/FhplGetBenefDetails#' => ['roles' => [ADMIN_ROLE_ID]],
|
||||
'#^/EcardRequest#' => ['public' => true],
|
||||
'#^/HospitalNetwork#' => ['public' => true],
|
||||
'#^/VidalGetBenefDetails#' => ['public' => true],
|
||||
'#^/ClaimDetail#' => ['public' => true],
|
||||
'#^/SubmitClaim#' => ['roles' => [ADMIN_ROLE_ID,HEAD_ROLE_ID,ACCOUNT_MANAGER_ROLE_ID,STAFF_ROLE_ID]],
|
||||
'#^/IntimateClaim#' => ['roles' => [ADMIN_ROLE_ID,HEAD_ROLE_ID,ACCOUNT_MANAGER_ROLE_ID,STAFF_ROLE_ID]],
|
||||
'#^/IRSubmission#' => ['roles' => [ADMIN_ROLE_ID,HEAD_ROLE_ID,ACCOUNT_MANAGER_ROLE_ID,STAFF_ROLE_ID]],
|
||||
'#^/ClaimStatusUpdate#' => ['roles' => [ADMIN_ROLE_ID,HEAD_ROLE_ID,ACCOUNT_MANAGER_ROLE_ID,STAFF_ROLE_ID]],
|
||||
'#^/syncTpaClaimToNhance#' => ['roles' => [ADMIN_ROLE_ID,HEAD_ROLE_ID,ACCOUNT_MANAGER_ROLE_ID,STAFF_ROLE_ID]],
|
||||
'#^/HealthIndiaGetBenefDetails#' => ['roles' => [ADMIN_ROLE_ID,HEAD_ROLE_ID,ACCOUNT_MANAGER_ROLE_ID,STAFF_ROLE_ID]],
|
||||
'#^/testTracelog#' => ['roles' => [ADMIN_ROLE_ID]],
|
||||
|
||||
// ===================== CLI =====================
|
||||
'#^/cli/#' => ['public' => true],
|
||||
|
||||
@ -131,3 +131,4 @@ define('UPLOAD_EXT_POLICY_DOCS', ['pdf', 'jpg', 'jpeg', 'png', 'xls', 'xlsx']);
|
||||
define('UPLOAD_EXT_LEAD_FILES', ['xls', 'xlsx', 'pdf', 'jpg', 'jpeg', 'png']);
|
||||
define('UPLOAD_EXT_EXCEL', ['xls', 'xlsx', 'ods', 'csv']);
|
||||
define('UPLOAD_EXT_MAIL_ATTACHMENTS', ['pdf', 'jpg', 'jpeg', 'png', 'doc', 'docx', 'xls', 'xlsx']);
|
||||
define('UPLOAD_EXT_NON_EB_RACK_RATE', ['pdf', 'xls', 'xlsx']);
|
||||
|
||||
@ -69,8 +69,8 @@ class Filters extends BaseConfig
|
||||
'before' => [
|
||||
'HttpRequestLog' => ['except' => 'cli/*'],
|
||||
'Cors',
|
||||
// 'AclFilter' => ['except' => ['login', 'logout', 'auth/*', 'oauth2callback','claim-form-download', 'claims-feedback-form', 'autobookstackLogin','employeeRest/*','processjob','getCommission','downloadEmployeeEcardZip']],
|
||||
'SecurityInputFilter' => ['except' => ['/client/notification/create','/ticket/crud_mail_template/*','test_mail','leads/sendMail'] ],
|
||||
'AclFilter' => ['except' => ['login', 'logout', 'auth/*', 'oauth2callback','claim-form-download', 'claims-feedback-form', 'autobookstackLogin','employeeRest/*','processjob','getCommission','downloadEmployeeEcardZip', 'downloadClaimFile/*']],
|
||||
'SecurityInputFilter' => ['except' => ['/client/notification/create','/ticket/crud_mail_template/*','test_mail','leads/sendMail', 'ticket/reply'] ],
|
||||
'GlobalPostFileUploadGuard'
|
||||
// 'csrf',
|
||||
// 'invalidchars',
|
||||
|
||||
@ -7,9 +7,17 @@ class RfqConfig extends BaseConfig
|
||||
{
|
||||
/**
|
||||
* Google Drive parent folder IDs for RFQ and QCR sheets.
|
||||
* Values are loaded from .env (RFQ_PARENT_FOLDER_ID).
|
||||
*/
|
||||
public string $rfqParentFolderId = '1MnYh5PTPDlc9mYMGsjmTv02y8EZ-BQf1';
|
||||
public string $qcrParentFolderId = '1MnYh5PTPDlc9mYMGsjmTv02y8EZ-BQf1';
|
||||
public string $rfqParentFolderId = '';
|
||||
public string $qcrParentFolderId = '';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->rfqParentFolderId = env('RFQ_PARENT_FOLDER_ID', '1MnYh5PTPDlc9mYMGsjmTv02y8EZ-BQf1');
|
||||
$this->qcrParentFolderId = env('RFQ_PARENT_FOLDER_ID', '1MnYh5PTPDlc9mYMGsjmTv02y8EZ-BQf1');
|
||||
}
|
||||
|
||||
/**
|
||||
* Default permissions for created sheets.
|
||||
@ -48,4 +56,27 @@ class RfqConfig extends BaseConfig
|
||||
'groups' => [],
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Placeholder token for past 3 years claims table (written as multi-row values at that cell).
|
||||
*/
|
||||
public string $rfqClaimsPlaceholder = '{{CLAIMS_DETAILS}}';
|
||||
|
||||
/**
|
||||
* Maps Google Sheet placeholder => internal key from buildRfqPlaceholderData().
|
||||
* Add these exact strings in your RFQ templates (any sheet/tab).
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
public array $rfqPlaceholders = [
|
||||
'{{INSURED_NAME}}' => 'insured_name',
|
||||
'{{COMMUNICATION_ADDRESS}}' => 'communication_address',
|
||||
'{{GST}}' => 'gst',
|
||||
'{{PAN}}' => 'pan',
|
||||
'{{POLICY_PERIOD}}' => 'policy_period',
|
||||
'{{OPPORTUNITY_TYPE}}' => 'opportunity_type',
|
||||
'{{RISK_LOCATION}}' => 'risk_location',
|
||||
'{{OCCUPANCY}}' => 'occupancy',
|
||||
'{{CLAIMS_DETAILS}}' => 'claims_details',
|
||||
];
|
||||
}
|
||||
|
||||
@ -100,6 +100,7 @@ $routes->group("/user", ["filter" => "authMVC"], function ($routes) {
|
||||
$routes->get("list", "UserController::list");
|
||||
$routes->get("getuser/(:hash)", "UserController::getuser/$1");
|
||||
$routes->get("deactive/(:hash)", "UserController::deactive/$1");
|
||||
$routes->get("activateUser/(:any)", "UserController::activateUser/$1");
|
||||
$routes->get("rolesandteams", "UserController::getRolesAndTeams");
|
||||
$routes->get("getUserActivityHistory", "UserController::getUserActivityHistory");
|
||||
$routes->match(['get','post','put'], 'partner', 'UserController::partner');
|
||||
@ -170,6 +171,9 @@ $routes->group("/client", ["filter" => "authMVC"], function ($routes) {
|
||||
$routes->post("policyGMCTerms", "ClientController::policyGMCTerms");
|
||||
$routes->get("getterms", "ClientController::getterms");
|
||||
$routes->get("remove/(:any)", "ClientController::removePolicy/$1");
|
||||
$routes->get("getNonEbRackRateFiles", "ClientController::getNonEbRackRateFiles");
|
||||
$routes->post("uploadNonEbRackRateFile", "ClientController::uploadNonEbRackRateFile");
|
||||
$routes->post("removeNonEbRackRateFile", "ClientController::removeNonEbRackRateFile");
|
||||
});
|
||||
|
||||
$routes->group("kyc", ["filter" => "authMVC"], function ($routes) {
|
||||
@ -227,6 +231,8 @@ $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("getTPADataVariationReportView/(:num)", "EmployeeController::getTPADataVariationReport/$1/view");
|
||||
$routes->get("proceedTPADataVariationNextStep/(:num)", "EmployeeController::proceedTPADataVariationNextStep/$1");
|
||||
$routes->get("bulkGenerateEcardAndStoreinS3", "EmployeeController::bulkGenerateEcardAndStoreinS3");
|
||||
$routes->get('clearCdSession', 'EmployeeController::clearCdSession');
|
||||
$routes->get('checkSessionStatus', 'EmployeeController::checkSessionStatus');
|
||||
@ -447,6 +453,9 @@ $routes->group("/util", ["filter" => "authMVC"], function ($routes) {
|
||||
$routes->post('insufficientCdBalanceHrMailSend', 'EmployeeController::insufficientCdBalanceHrMailSend');
|
||||
$routes->get('getTpaClaimDumpErrorData/(:any)', 'TicketServiceController::getTpaClaimDumpErrorData/$1');
|
||||
$routes->get('croneDailyActivityReport', 'DashboardController::croneDailyActivityReport');
|
||||
$routes->get('insertSampleTpaApiData/(:any)', 'TestingController::insertSampleTpaApiData/$1');
|
||||
$routes->get('listEmployeeCountByClientPolicy', 'TestingController::listEmployeeCountByClientPolicy');
|
||||
$routes->get('testMediAssistWellness','TestingController::testMediAssistWellness');
|
||||
});
|
||||
|
||||
$routes->post("policy_tranction/sendInstallmentRemainderMail","PolicyTransactionController::sendInstallmentRemainderMail");
|
||||
@ -511,6 +520,8 @@ $routes->group("leads", ["filter" => "authMVC"], function ($routes) {
|
||||
$routes->post("create", "LeadsController::createLead");
|
||||
$routes->get("list/(:any)", "LeadsController::getLeadDataForEdit/$1");
|
||||
$routes->get("createRfqSheet", "LeadsController::createRfqSheet");
|
||||
$routes->get("createQcrSheet", "LeadsController::createQcrSheet");
|
||||
$routes->post("uploadLeadAttachment", "LeadsController::uploadLeadAttachment");
|
||||
$routes->get("mailTemplate", "LeadsController::getLeadMailTemplate");
|
||||
$routes->get("sendMail", "LeadsController::sendMailWithAttachement");
|
||||
$routes->post("sendMail", "LeadsController::sendMailWithAttachement");
|
||||
@ -550,6 +561,8 @@ $routes->cli('cli/sendZeptoMail', 'MasterController::testZeptoSMTP');
|
||||
$routes->cli('cli/check_bounce_mail_cli', 'MasterController::testCheckBounceMails');
|
||||
$routes->cli('cli/app_check_list', 'MasterController::appCheckList');
|
||||
$routes->cli('cli/new_gdrive_token', 'GoogleDriveController::generateNewGoogleDriveAccessToken');
|
||||
$routes->cli('cli/list-sheet-folder-files', 'GoogleSheetController::listFolderSheetFilesCli');
|
||||
$routes->cli('cli/list-sheet-folder-files/(:any)', 'GoogleSheetController::listFolderSheetFilesCli/$1');
|
||||
$routes->cli('cli/check_env', 'MasterController::checkEnv');
|
||||
|
||||
//crone job
|
||||
@ -877,6 +890,7 @@ $routes->group('test', function($routes) {
|
||||
$routes->get('logo_renaming','TestingController::logo_renaming');
|
||||
$routes->get('testingquerys','TestingController::testingquerys');
|
||||
$routes->get('thzReminderCrone','ThzController::getOpenTicketsOlderThan24HoursAndAssignNextLevel');
|
||||
$routes->get('chartbrewDashboardDemo','TestingController::chartbrewDashboardDemo');
|
||||
// $routes->get('createDefaultMailTempalteInDB','TestingController::createDefaultMailTempalteInCrossDB');
|
||||
// $routes->get('createDefaultMailTempalteInSameDB','TestingController::createDefaultMailTempalteInSameDB');
|
||||
});
|
||||
@ -1033,6 +1047,10 @@ $routes->group('sales', function($routes) {
|
||||
$routes->get('dashboard', 'SalesController::dashboard');
|
||||
$routes->get('branchLevelDashboard', 'SalesController::branchLevelDashboard');
|
||||
$routes->get('salesManagerLevelDashboard', 'SalesController::salesManagerLevelDashboard');
|
||||
|
||||
//Validations
|
||||
$routes->get('checkDuplicate', 'SalesController::checkDuplicate');
|
||||
$routes->get('searchClients', 'SalesController::searchClients');
|
||||
});
|
||||
|
||||
// Expence Module Route Group
|
||||
|
||||
@ -245,7 +245,13 @@ class ApiServiceController extends BaseController
|
||||
$policy_id = $this->request->getPost('client_policy_id') ?? null;
|
||||
|
||||
$fileModel = new BatchFileModel();
|
||||
$filesData = $fileModel->where('is_active', 1)->where('event_type', 'api')->where('status', 'inprogress')->countAllResults();
|
||||
$filesData = $fileModel
|
||||
->join('client_policy', 'client_policy.id = batch_files.client_policy_id')
|
||||
->where('client_policy.tpa_id', $tpa_id)
|
||||
->where('batch_files.is_active', 1)
|
||||
->where('batch_files.event_type', 'api')
|
||||
->where('batch_files.status', 'inprogress')
|
||||
->countAllResults();
|
||||
|
||||
if($filesData > 0){
|
||||
log_message('error', "TPA GetBenefDetails job is already in process.");
|
||||
@ -453,13 +459,17 @@ class ApiServiceController extends BaseController
|
||||
$userParams = [];
|
||||
foreach ($data as $row) {
|
||||
|
||||
if($row['wellness_vendor_id'] != null)
|
||||
if($row['wellness_vendor_id'] == $this->vidal_primary_key)
|
||||
{
|
||||
$vidalApiController = new VidalApiController();
|
||||
return $vidalApiController->getWellnessSSORedirectUrl($row['email']);
|
||||
|
||||
}else if ($row['wellness_vendor_id'] == $this->medi_assist_primary_key)
|
||||
{
|
||||
$mediAssistController = new MediAssistApiController();
|
||||
return $mediAssistController->getWellnessSSORedirectUrl($row['planId'], $row['memberId']);
|
||||
}
|
||||
else if ($row['planId'] != null) // VISIT
|
||||
else if ($row['planId'] != null && empty($row['wellness_vendor_id'])) // VISIT
|
||||
{
|
||||
$userParams['name'] = $row['name'];
|
||||
$userParams['email'] = $row['email'];
|
||||
|
||||
@ -226,6 +226,14 @@ class AppContentManagementController extends AdminController
|
||||
|
||||
if ($this->request->getMethod() === 'post') {
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* STEP 1: INITIAL VALIDATION
|
||||
* --------------------------------------------------------------------------
|
||||
* These are the basic validation rules. For 'content' and 'notes', we only
|
||||
* check if they are provided and within the allowed length.
|
||||
* The more advanced security check for script tags happens next.
|
||||
*/
|
||||
$rules = [
|
||||
'fe_id' => [
|
||||
'rules' => 'permit_empty|integer|is_natural',
|
||||
@ -239,7 +247,7 @@ class AppContentManagementController extends AdminController
|
||||
'errors' => [
|
||||
'required' => 'Type is required',
|
||||
'max_length' => 'Type cannot exceed 255 characters',
|
||||
'regex_match' => 'Type contains invalid characters'
|
||||
'regex_match' => 'Type can contain only letters, numbers, spaces, _ and -'
|
||||
]
|
||||
],
|
||||
'content_section' => [
|
||||
@ -247,7 +255,7 @@ class AppContentManagementController extends AdminController
|
||||
'errors' => [
|
||||
'required' => 'Content Section is required',
|
||||
'max_length' => 'Content Section cannot exceed 255 characters',
|
||||
'regex_match' => 'Content Section contains invalid characters'
|
||||
'regex_match' => 'Content Section can contain only letters, numbers, spaces, _ and -'
|
||||
]
|
||||
],
|
||||
'heading' => [
|
||||
@ -255,25 +263,23 @@ class AppContentManagementController extends AdminController
|
||||
'errors' => [
|
||||
'required' => 'Heading is required',
|
||||
'max_length' => 'Heading cannot exceed 255 characters',
|
||||
'regex_match' => 'Heading contains invalid characters. Only letters, numbers, spaces and basic punctuation are allowed'
|
||||
'regex_match' => 'Heading can contain only letters, numbers, spaces, and these characters: . , ; : ! ? ( ) & / -'
|
||||
]
|
||||
],
|
||||
'content' => [
|
||||
'rules' => 'required|max_length[5000]|regex_match[/^[a-zA-Z0-9 _\-.,;:!?()&\/\r\n]+$/]',
|
||||
'content' => [
|
||||
'rules' => 'required|max_length[5000]',
|
||||
'errors' => [
|
||||
'required' => 'Content is required',
|
||||
'max_length' => 'Content cannot exceed 5000 characters',
|
||||
'regex_match' => 'Content contains invalid characters. Only letters, numbers, spaces and basic punctuation are allowed'
|
||||
'required' => 'Content is required',
|
||||
'max_length' => 'Content cannot exceed 5000 characters',
|
||||
]
|
||||
],
|
||||
'notes' => [
|
||||
'rules' => 'required|max_length[1500]|regex_match[/^[a-zA-Z0-9 _\-.,;:!?()&\/\r\n]+$/]',
|
||||
'rules' => 'required|max_length[1500]',
|
||||
'errors' => [
|
||||
'required' => 'Notes are required',
|
||||
'max_length' => 'Notes cannot exceed 1500 characters',
|
||||
'regex_match' => 'Notes contains invalid characters. Only letters, numbers, spaces and basic punctuation are allowed'
|
||||
'required' => 'Notes are required',
|
||||
'max_length' => 'Notes cannot exceed 1500 characters',
|
||||
]
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
if (!$this->validate($rules)) {
|
||||
@ -284,9 +290,60 @@ class AppContentManagementController extends AdminController
|
||||
'errors' => $this->validator->getErrors()
|
||||
]);
|
||||
}
|
||||
$request_post_data = $this->request->getPost();
|
||||
$data = sanitizeInputArrayAdvanced($request_post_data);
|
||||
$id = $data['fe_id'] ?? null;
|
||||
|
||||
/**************************************************************************
|
||||
* REFACTORED SANITIZATION LOGIC (XSS Protection)
|
||||
**************************************************************************
|
||||
*
|
||||
* Per the user's request, we are avoiding the generic `sanitizeInputArrayAdvanced`
|
||||
* on the `content` and `notes` fields, as they require special HTML
|
||||
* handling.
|
||||
*
|
||||
* The new process is:
|
||||
* 1. Get the raw `content` and `notes` directly from the POST request.
|
||||
* 2. Perform the critical XSS validation on this raw content using `hasXssTags()`.
|
||||
* If it fails, the request is rejected immediately. This satisfies all
|
||||
* the failure test cases (Tests 4-9).
|
||||
* 3. Take all *other* POST data and sanitize it using the generic
|
||||
* `sanitizeInputArrayAdvanced` function.
|
||||
* 4. Sanitize the now-validated `content` and `notes` using our specific
|
||||
* `sanitizeHtml()` function, which allows safe HTML.
|
||||
* 5. Combine the sanitized data into a final array for database insertion.
|
||||
*
|
||||
*************************************************************************/
|
||||
|
||||
// Step 1: Get raw `content` and `notes`.
|
||||
$rawContent = $this->request->getPost('content');
|
||||
$rawNotes = $this->request->getPost('notes');
|
||||
|
||||
// Step 2: Perform critical XSS validation on raw input.
|
||||
$xssErrors = [];
|
||||
if ($this->hasXssTags($rawContent)) {
|
||||
$xssErrors['content'] = 'Content contains restricted tags. Script, iframe and event handlers are not allowed';
|
||||
}
|
||||
if ($this->hasXssTags($rawNotes)) {
|
||||
$xssErrors['notes'] = 'Notes contains restricted tags. Script, iframe and event handlers are not allowed';
|
||||
}
|
||||
|
||||
if (!empty($xssErrors)) {
|
||||
return $this->response->setStatusCode(400)->setJSON([
|
||||
'status' => false,
|
||||
'message' => 'Input validation failed',
|
||||
'code' => 400,
|
||||
'errors' => $xssErrors
|
||||
]);
|
||||
}
|
||||
|
||||
// Step 3: Sanitize all *other* POST data.
|
||||
$otherPostData = $this->request->getPost();
|
||||
unset($otherPostData['content'], $otherPostData['notes']);
|
||||
$data = sanitizeInputArrayAdvanced($otherPostData);
|
||||
|
||||
// Step 4 & 5: Sanitize and re-combine `content` and `notes`.
|
||||
$data['content'] = $this->sanitizeHtml($rawContent);
|
||||
$data['notes'] = $this->sanitizeHtml($rawNotes);
|
||||
|
||||
$id = $data['fe_id'] ?? null;
|
||||
|
||||
if (!empty($id) && (!ctype_digit((string)$id) || (int)$id <= 0)) {
|
||||
return $this->response->setStatusCode(400)->setJSON([
|
||||
@ -408,27 +465,29 @@ class AppContentManagementController extends AdminController
|
||||
if ($method === 'post') {
|
||||
$rules = [
|
||||
'category' => [
|
||||
'rules' => 'required|max_length[100]|alpha_numeric_space',
|
||||
// Allow letters, numbers, spaces and / - . , " '
|
||||
'rules' => 'required|max_length[100]|regex_match[/^[a-zA-Z0-9 \\/\\-\\.\,\"\\\']+$/]',
|
||||
'errors' => [
|
||||
'required' => 'Category is required',
|
||||
'max_length' => 'Category cannot exceed 100 characters',
|
||||
'alpha_numeric_space' => 'Category contains invalid characters'
|
||||
'required' => 'Category is required',
|
||||
'max_length' => 'Category cannot exceed 100 characters',
|
||||
'regex_match' => 'Category can contain only letters, numbers, spaces, and these characters: / - . , " \'',
|
||||
|
||||
]
|
||||
],
|
||||
'question' => [
|
||||
'rules' => 'required|max_length[1000]|regex_match[/^[a-zA-Z0-9 _\-.,;:!?()&\/\r\n]+$/]',
|
||||
// Allow only letters, numbers, spaces and basic punctuation . , ; : ! ? ( ) & / -
|
||||
'rules' => 'required|max_length[1000]|regex_match[/^[a-zA-Z0-9 _\\-.,;:!?()&\\/]+$/]',
|
||||
'errors' => [
|
||||
'required' => 'Question is required',
|
||||
'max_length' => 'Question cannot exceed 1000 characters',
|
||||
'regex_match' => 'Question contains invalid characters. Only letters, numbers, spaces and basic punctuation are allowed'
|
||||
'regex_match' => 'Question can contain only letters, numbers, spaces, and these characters: . , ; : ! ? ( ) & / -',
|
||||
]
|
||||
],
|
||||
'answer' => [
|
||||
'rules' => 'required|max_length[5000]|regex_match[/^[a-zA-Z0-9 _\-.,;:!?()&\/\r\n]+$/]',
|
||||
'rules' => 'required|max_length[5000]',
|
||||
'errors' => [
|
||||
'required' => 'Answer is required',
|
||||
'max_length' => 'Answer cannot exceed 5000 characters',
|
||||
'regex_match' => 'Answer contains invalid characters. Only letters, numbers, spaces and basic punctuation are allowed'
|
||||
]
|
||||
]
|
||||
];
|
||||
@ -443,9 +502,50 @@ class AppContentManagementController extends AdminController
|
||||
]);
|
||||
}
|
||||
|
||||
$request_post_data = $this->request->getPost();
|
||||
$sanitized_post_data = sanitizeInputArrayAdvanced($request_post_data);
|
||||
$data = array_filter($sanitized_post_data, fn($v) => $v !== '' && $v !== null);
|
||||
/**************************************************************************
|
||||
* XSS PROTECTION FOR 'question' and 'answer'
|
||||
**************************************************************************
|
||||
*
|
||||
* Applying the same security model as `frontend_content`.
|
||||
*
|
||||
* 1. Validate raw `question` and `answer` for malicious tags using `hasXssTags()`.
|
||||
* If found, reject the request immediately.
|
||||
* 2. Sanitize all *other* fields using the generic `sanitizeInputArrayAdvanced`.
|
||||
* 3. Sanitize the `question` and `answer` using the HTML-aware `sanitizeHtml()`
|
||||
* function to allow safe tags before saving.
|
||||
*
|
||||
*************************************************************************/
|
||||
|
||||
// Step 1: Validate raw input for XSS threats.
|
||||
$rawQuestion = $this->request->getPost('question');
|
||||
$rawAnswer = $this->request->getPost('answer');
|
||||
$xssErrors = [];
|
||||
|
||||
if ($this->hasXssTags($rawQuestion)) {
|
||||
$xssErrors['question'] = 'Question contains restricted tags. Script, iframe and event handlers are not allowed';
|
||||
}
|
||||
if ($this->hasXssTags($rawAnswer)) {
|
||||
$xssErrors['answer'] = 'Answer contains restricted tags. Script, iframe and event handlers are not allowed';
|
||||
}
|
||||
|
||||
if (!empty($xssErrors)) {
|
||||
return $this->response->setStatusCode(400)->setJSON([
|
||||
'status' => 'error',
|
||||
'message' => 'Input validation failed',
|
||||
'code' => 400,
|
||||
'errors' => $xssErrors,
|
||||
'ref' => $ref
|
||||
]);
|
||||
}
|
||||
|
||||
// Step 2 & 3: Sanitize and combine data.
|
||||
$otherPostData = $this->request->getPost();
|
||||
unset($otherPostData['question'], $otherPostData['answer']);
|
||||
$data = sanitizeInputArrayAdvanced($otherPostData);
|
||||
|
||||
$data['question'] = $this->sanitizeHtml($rawQuestion);
|
||||
$data['answer'] = $this->sanitizeHtml($rawAnswer);
|
||||
|
||||
$id = $data['faq_id'] ?? null;
|
||||
|
||||
if (!empty($id) && (!ctype_digit((string)$id) || (int)$id <= 0)) {
|
||||
@ -470,12 +570,6 @@ class AppContentManagementController extends AdminController
|
||||
$msg = "Updated";
|
||||
}
|
||||
|
||||
// if ($returnType === 'web') {
|
||||
// return redirect()->back()->with($status ? 'success' : 'error', "FAQ $msg " . ($status ? 'successfully' : 'failed'));
|
||||
// }
|
||||
|
||||
// return $this->response->setJSON([
|
||||
// ])->setStatusCode($result ? 200 : 400);
|
||||
return $this->response->setJSON([
|
||||
'status' => $status ? 'success' : 'error',
|
||||
'message' => "FAQ $msg " . ($status ? 'successfully' : 'failed'),
|
||||
@ -611,4 +705,105 @@ class AppContentManagementController extends AdminController
|
||||
|
||||
// }
|
||||
|
||||
|
||||
// Add these two private methods inside AppContentManagementController
|
||||
|
||||
|
||||
/**
|
||||
* =================================================================================
|
||||
* HTML SANITIZATION & VALIDATION HELPER METHODS
|
||||
* =================================================================================
|
||||
* The following two methods are the core of the XSS protection logic.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* sanitizeHtml()
|
||||
*
|
||||
* This function cleans a string of HTML, ensuring it is safe to display in a browser.
|
||||
* It allows a specific set of safe HTML tags and removes any dangerous attributes
|
||||
* from those tags.
|
||||
*
|
||||
* @param string $input The raw HTML string from user input.
|
||||
* @return string The cleaned, safe HTML string.
|
||||
*/
|
||||
private function sanitizeHtml(string $input): string
|
||||
{
|
||||
/**
|
||||
* Define a whitelist of allowed HTML tags. Any tag not in this list will be
|
||||
* completely removed. We are allowing basic formatting, lists, tables, etc.
|
||||
*/
|
||||
// ✅ Added <s>, <u>, <h1>-<h6>, <blockquote>, <pre>, <code>, <hr> for Jodit support
|
||||
$allowed_tags = '<p><b><i><s><u><strong><em><ul><ol><li><br><a><img><table><thead><tbody><tr><th><td><span><div><h1><h2><h3><h4><h5><h6><blockquote><pre><code><hr><sub><sup>';
|
||||
|
||||
// Use strip_tags() to remove all tags that are not in our whitelist.
|
||||
$clean = strip_tags($input, $allowed_tags);
|
||||
|
||||
/**
|
||||
* Define a blacklist of dangerous attributes. These are often used for XSS
|
||||
* attacks (e.g., `onclick`, `onmouseover`). We search for and remove these
|
||||
* attributes from any remaining tags.
|
||||
*/
|
||||
$dangerous_attrs = [
|
||||
'/\s*on\w+\s*=\s*["\'][^"\']*["\']/i', // e.g., onclick="..."
|
||||
'/\s*on\w+\s*=\s*[^\s>]*/i', // e.g., onclick=...
|
||||
'/\s*javascript\s*:[^"\'"]*/i', // e.g., href="javascript:..."
|
||||
'/\s*vbscript\s*:[^"\'"]*/i', // e.g., href="vbscript:..."
|
||||
];
|
||||
|
||||
// Use preg_replace to find and remove the dangerous attributes.
|
||||
foreach ($dangerous_attrs as $pattern) {
|
||||
$clean = preg_replace($pattern, '', $clean);
|
||||
}
|
||||
|
||||
return $clean;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* hasXssTags()
|
||||
*
|
||||
* This function scans a string for common XSS-related tags, protocols, and event
|
||||
* handlers. It is used as a primary check to quickly reject any input that is
|
||||
* clearly malicious.
|
||||
*
|
||||
* @param string $str The raw string from user input.
|
||||
* @return bool Returns `true` if a dangerous pattern is found, `false` otherwise.
|
||||
*/
|
||||
private function hasXssTags(string $str): bool
|
||||
{
|
||||
// Decode the string to handle entities (e.g., `%3Cscript%3E`) and prevent evasion.
|
||||
$decoded = html_entity_decode($str, ENT_QUOTES, 'UTF-8');
|
||||
$decoded = urldecode($decoded);
|
||||
$decoded = str_replace(["\0", "\x00"], '', $decoded); // Remove null bytes
|
||||
|
||||
/**
|
||||
* Define a blacklist of dangerous patterns. This includes tags like `<script>`
|
||||
* and `<iframe>`, as well as patterns like `javascript:` and `onclick=`.
|
||||
* The `/i` flag makes the search case-insensitive.
|
||||
*/
|
||||
$dangerous_patterns = [
|
||||
'/<\s*script/i', // <script
|
||||
'/<\s*\/\s*script/i', // </script
|
||||
'/javascript\s*:/i', // javascript:
|
||||
'/vbscript\s*:/i', // vbscript:
|
||||
'/<\s*iframe/i', // <iframe>
|
||||
'/<\s*object/i', // <object>
|
||||
'/<\s*embed/i', // <embed>
|
||||
'/<\s*applet/i', // <applet>
|
||||
'/on\w+\s*=/i', // on...= (e.g., onclick=, onmouseover=, onerror=)
|
||||
'/data\s*:\s*text\/html/i', // data:text/html
|
||||
'/expression\s*\(/i', // CSS expression()
|
||||
'/\balert\s*\(/i', // alert(...)
|
||||
];
|
||||
|
||||
// Loop through the patterns and check if any of them exist in the decoded string.
|
||||
foreach ($dangerous_patterns as $pattern) {
|
||||
if (preg_match($pattern, $decoded)) return true; // Found a threat
|
||||
}
|
||||
|
||||
// If we get here, no threats were found.
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@ -964,6 +964,8 @@ class ClientController extends AdminController
|
||||
'tpa_branch_code' => $item->tpa_branch_code,
|
||||
'branch_name' => $item->branch_name ?? ' - ',
|
||||
'policy_type_id' => $item->policy_type_id,
|
||||
'allocg' => $item->allocg,
|
||||
'lead_misc' => $item->lead_misc,
|
||||
];
|
||||
}, $rawList)) : [];
|
||||
$editData['client_policy']['role'] = get_role_id();
|
||||
@ -1004,8 +1006,8 @@ class ClientController extends AdminController
|
||||
]
|
||||
],
|
||||
'short_name' => [
|
||||
'rules' => 'required|regex_match[/^[a-zA-Z0-9_-]+$/]|min_length[2]|max_length[15]',
|
||||
'errors' => [
|
||||
'rules' => 'required|regex_match[/^[a-zA-Z0-9_\- ]+$/]|min_length[2]|max_length[15]',
|
||||
'errors' => [
|
||||
'required' => 'Client Short Name is required',
|
||||
'regex_match' => 'Client Short Name can only contain letters, numbers, hyphens and underscores.',
|
||||
'min_length' => 'Client Short Name must be at least 2 characters.',
|
||||
@ -1013,9 +1015,8 @@ class ClientController extends AdminController
|
||||
]
|
||||
],
|
||||
'pan' => [
|
||||
'rules' => 'required|regex_match[/^[A-Z]{5}[0-9]{4}[A-Z]$/]',
|
||||
'rules' => 'permit_empty|regex_match[/^[A-Z]{5}[0-9]{4}[A-Z]$/]',
|
||||
'errors' => [
|
||||
'required' => 'PAN Number is required.',
|
||||
'regex_match' => 'Invalid PAN format. Example: ABCDE1234F'
|
||||
]
|
||||
],
|
||||
@ -1120,7 +1121,7 @@ class ClientController extends AdminController
|
||||
]
|
||||
],
|
||||
'short_name' => [
|
||||
'rules' => 'required|regex_match[/^[a-zA-Z0-9_-]+$/]',
|
||||
'rules' => 'required|regex_match[/^[a-zA-Z0-9_\- ]+$/]|min_length[2]|max_length[15]',
|
||||
'errors' => [
|
||||
'required' => 'Client Short Name is required',
|
||||
'regex_match' => 'Client Short Name can only contain letters, numbers, hyphens and underscores.',
|
||||
@ -1129,9 +1130,8 @@ class ClientController extends AdminController
|
||||
]
|
||||
],
|
||||
'pan' => [
|
||||
'rules' => 'required|regex_match[/^[A-Z]{5}[0-9]{4}[A-Z]$/]',
|
||||
'rules' => 'permit_empty|regex_match[/^[A-Z]{5}[0-9]{4}[A-Z]$/]',
|
||||
'errors' => [
|
||||
'required' => 'PAN Number is required.',
|
||||
'regex_match' => 'Invalid PAN format. Example: ABCDE1234F'
|
||||
]
|
||||
],
|
||||
@ -1876,10 +1876,10 @@ class ClientController extends AdminController
|
||||
]
|
||||
],
|
||||
'designation.*' => [
|
||||
'rules' => 'required|alpha_space',
|
||||
'rules' => 'required|regex_match[/^[A-Za-z0-9\-_\/ ]+$/]',
|
||||
'errors' => [
|
||||
'required' => 'Designation is required',
|
||||
'alpha_space' => 'Designation may contain only letters and spaces'
|
||||
'regex_match' => 'Designation may only contain letters, numbers, /, -, _ and spaces'
|
||||
]
|
||||
],
|
||||
'email.*' => [
|
||||
@ -2283,10 +2283,10 @@ class ClientController extends AdminController
|
||||
]
|
||||
],
|
||||
'designation.*' => [
|
||||
'rules' => 'required|alpha_space',
|
||||
'rules' => 'required|regex_match[/^[A-Za-z0-9\-_\/ ]+$/]',
|
||||
'errors' => [
|
||||
'required' => 'Designation is required',
|
||||
'alpha_space' => 'Designation may contain only letters and spaces'
|
||||
'regex_match' => 'Designation may only contain letters, numbers, /, -, _ and spaces'
|
||||
]
|
||||
],
|
||||
'email.*' => [
|
||||
@ -2467,10 +2467,10 @@ class ClientController extends AdminController
|
||||
]
|
||||
],
|
||||
'designation.*' => [
|
||||
'rules' => 'required|alpha_space',
|
||||
'rules' => 'required|regex_match[/^[A-Za-z0-9\-_\/ ]+$/]',
|
||||
'errors' => [
|
||||
'required' => 'Designation is required',
|
||||
'alpha_space' => 'Designation may contain only letters and spaces'
|
||||
'regex_match' => 'Designation may only contain letters, numbers, /, -, _ and spaces'
|
||||
]
|
||||
],
|
||||
'email.*' => [
|
||||
@ -3115,6 +3115,89 @@ class ClientController extends AdminController
|
||||
}
|
||||
}
|
||||
|
||||
public function getNonEbRackRateFiles()
|
||||
{
|
||||
$clientPolicyId = $this->request->getGet('client_policy_id');
|
||||
$policy = $this->clientPolicyModel->where('id', $clientPolicyId)->first();
|
||||
|
||||
if (!$policy) {
|
||||
return $this->respond(['status' => false, 'message' => 'Policy not found'], 200);
|
||||
}
|
||||
|
||||
$files = [];
|
||||
$jsonField = is_array($policy) ? ($policy['non_eb_rack_rate_files'] ?? null) : ($policy->non_eb_rack_rate_files ?? null);
|
||||
|
||||
if (!empty($jsonField)) {
|
||||
$files = json_decode($jsonField, true) ?? [];
|
||||
}
|
||||
|
||||
return $this->respond(['status' => true, 'files' => $files], 200);
|
||||
}
|
||||
|
||||
public function uploadNonEbRackRateFile()
|
||||
{
|
||||
$clientPolicyId = $this->request->getPost('client_policy_id');
|
||||
$policy = $this->clientPolicyModel->where('id', $clientPolicyId)->first();
|
||||
|
||||
if (!$policy) {
|
||||
return $this->respond(['status' => false, 'message' => 'Policy not found'], 200);
|
||||
}
|
||||
|
||||
$uploadFilePath = WRITEPATH . 'uploads/non_eb_rack_rate';
|
||||
$fileName = file_Upload($this->request->getFile('file'), $uploadFilePath, UPLOAD_EXT_NON_EB_RACK_RATE);
|
||||
|
||||
if (empty($fileName)) {
|
||||
return $this->respond(['status' => false, 'message' => 'Invalid file. Only PDF and Excel files are allowed.'], 200);
|
||||
}
|
||||
|
||||
$originalName = $this->request->getFile('file')->getClientName();
|
||||
$ext = pathinfo($fileName, PATHINFO_EXTENSION);
|
||||
|
||||
$jsonField = is_array($policy) ? ($policy['non_eb_rack_rate_files'] ?? null) : ($policy->non_eb_rack_rate_files ?? null);
|
||||
$files = !empty($jsonField) ? (json_decode($jsonField, true) ?? []) : [];
|
||||
|
||||
$fileEntry = [
|
||||
'name' => $fileName,
|
||||
'original_name' => $originalName,
|
||||
'type' => $ext,
|
||||
'uploaded_at' => date('Y-m-d H:i:s'),
|
||||
];
|
||||
|
||||
$files[] = $fileEntry;
|
||||
|
||||
$this->clientPolicyModel->where('id', $clientPolicyId)->set(['non_eb_rack_rate_files' => json_encode($files)])->update();
|
||||
|
||||
return $this->respond(['status' => true, 'file' => $fileEntry], 200);
|
||||
}
|
||||
|
||||
public function removeNonEbRackRateFile()
|
||||
{
|
||||
$clientPolicyId = $this->request->getPost('client_policy_id');
|
||||
$filename = $this->request->getPost('filename');
|
||||
$policy = $this->clientPolicyModel->where('id', $clientPolicyId)->first();
|
||||
|
||||
if (!$policy) {
|
||||
return $this->respond(['status' => false, 'message' => 'Policy not found'], 200);
|
||||
}
|
||||
|
||||
$jsonField = is_array($policy) ? ($policy['non_eb_rack_rate_files'] ?? null) : ($policy->non_eb_rack_rate_files ?? null);
|
||||
$files = !empty($jsonField) ? (json_decode($jsonField, true) ?? []) : [];
|
||||
|
||||
$files = array_values(array_filter($files, function ($file) use ($filename) {
|
||||
return $file['name'] !== $filename;
|
||||
}));
|
||||
|
||||
$this->clientPolicyModel->where('id', $clientPolicyId)->set(['non_eb_rack_rate_files' => json_encode($files)])->update();
|
||||
|
||||
// Delete physical file
|
||||
$filePath = WRITEPATH . 'uploads/non_eb_rack_rate/' . $filename;
|
||||
if (file_exists($filePath)) {
|
||||
unlink($filePath);
|
||||
}
|
||||
|
||||
return $this->respond(['status' => true], 200);
|
||||
}
|
||||
|
||||
public function deactivatePolicyTransactionsPolicy($clientPolicyId)
|
||||
{
|
||||
// Deactivate policy_transaction records
|
||||
@ -5803,6 +5886,99 @@ class ClientController extends AdminController
|
||||
|
||||
// print_r($postData); die;
|
||||
$client_type = $postData['client_type'] ?? null;
|
||||
$from_modal = $postData['from_modal'] ?? null;
|
||||
|
||||
$rules = [
|
||||
'client_name' => [
|
||||
'label' => 'Client Name',
|
||||
'rules' => 'required|regex_match[/^[a-zA-Z0-9 ,\/_\-]+$/]',
|
||||
'errors' => [
|
||||
'required' => 'Client Name is required.',
|
||||
'regex_match' => 'Client Name only allows letters, numbers, spaces, and , / _ -'
|
||||
]
|
||||
],
|
||||
'short_name' => [
|
||||
'label' => 'Client Short Name',
|
||||
'rules' => 'required|regex_match[/^[a-zA-Z0-9,\/_\-]+$/]|is_unique[clients.short_name]',
|
||||
'errors' => [
|
||||
'required' => 'Short Name is required.',
|
||||
'regex_match' => 'Short Name only allows letters, numbers, and , / _ -',
|
||||
'is_unique' => 'This Short Name is already in use.'
|
||||
]
|
||||
],
|
||||
'pan' => [
|
||||
'label' => 'PAN',
|
||||
'rules' => 'permit_empty|regex_match[/^[A-Z]{5}[0-9]{4}[A-Z]{1}$/]',
|
||||
'errors' => [
|
||||
'regex_match' => 'Invalid PAN format (Example: ABCDE1234F).'
|
||||
]
|
||||
],
|
||||
'entity_type_id' => [
|
||||
'label' => 'Entity Type',
|
||||
'rules' => 'required',
|
||||
'errors' => [
|
||||
'required' => 'Please select an Entity Type.'
|
||||
]
|
||||
],
|
||||
'branch_name' => [
|
||||
'label' => 'Branch Name',
|
||||
'rules' => 'required|regex_match[/^[a-zA-Z0-9 ,\/_\-]+$/]',
|
||||
'errors' => [
|
||||
'required' => 'Branch Name is required.',
|
||||
'regex_match' => 'Branch Name only allows letters, numbers, spaces, and , / _ -'
|
||||
]
|
||||
],
|
||||
'branch_code' => [
|
||||
'label' => 'Branch Code',
|
||||
'rules' => 'required|regex_match[/^[a-zA-Z0-9,\/_\-]+$/]',
|
||||
'errors' => [
|
||||
'required' => 'Branch Code is required.',
|
||||
'regex_match' => 'Branch Code only allows letters, numbers, and , / _ -'
|
||||
]
|
||||
],
|
||||
'name' => [
|
||||
'label' => 'Contact Person Name',
|
||||
'rules' => 'required|regex_match[/^[a-zA-Z0-9 ,\/_\-]+$/]',
|
||||
'errors' => [
|
||||
'required' => 'Name is required.',
|
||||
'regex_match' => 'Name only allows letters, numbers, spaces, and , / _ -'
|
||||
]
|
||||
],
|
||||
'mobile' => [
|
||||
'label' => 'Mobile',
|
||||
'rules' => 'required|numeric|exact_length[10]',
|
||||
'errors' => [
|
||||
'required' => 'Mobile number is required.',
|
||||
'numeric' => 'Mobile must contain only digits.',
|
||||
'exact_length' => 'Mobile number must be exactly 10 digits.'
|
||||
]
|
||||
],
|
||||
'email' => [
|
||||
'label' => 'Email',
|
||||
'rules' => 'required|valid_email',
|
||||
'errors' => [
|
||||
'required' => 'Email is required.',
|
||||
'valid_email' => 'Please provide a valid email address.'
|
||||
]
|
||||
],
|
||||
'gst' => [
|
||||
'label' => 'GST',
|
||||
'rules' => 'required|regex_match[/^\d{2}[A-Z]{5}\d{4}[A-Z]{1}[A-Z\d]{1}[Z]{1}[A-Z\d]{1}$/]',
|
||||
'errors' => [
|
||||
'required' => 'GST Number is required.',
|
||||
'regex_match' => 'Invalid GST format (Example: 12ABCDE1234F5Z6).'
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
if (isset($from_modal) && !$this->validate($rules)) {
|
||||
return $this->response->setStatusCode(400)->setJSON([
|
||||
'status' => false,
|
||||
'message' => 'Input validation failed',
|
||||
'code' => 400,
|
||||
'errors' => $this->validator->getErrors()
|
||||
]);
|
||||
}
|
||||
|
||||
$client_insert = null;
|
||||
if ($client_type == 2) {
|
||||
@ -5928,7 +6104,8 @@ class ClientController extends AdminController
|
||||
],
|
||||
|
||||
];
|
||||
if (!$this->validate($rules)) {
|
||||
|
||||
if (!$this->validate($rules)) {
|
||||
return $this->response->setStatusCode(400)->setJSON([
|
||||
'status' => false,
|
||||
'message' => 'Input validation failed',
|
||||
@ -5936,6 +6113,7 @@ class ClientController extends AdminController
|
||||
'errors' => $this->validator->getErrors()
|
||||
]);
|
||||
}
|
||||
|
||||
$data = $this->request->getPost();
|
||||
$sanitized_post_data = sanitizeInputArrayAdvanced($data);
|
||||
$id = $sanitized_post_data['vehicle_primary_key'] ?? null;
|
||||
@ -6287,12 +6465,13 @@ class ClientController extends AdminController
|
||||
$client_type = $this->request->getGet('client_type') ?? null;
|
||||
|
||||
// Check in Policy Transaction (BDS)
|
||||
$bds_count = $this->policyTransactionModel
|
||||
$bds_data = $this->policyTransactionModel
|
||||
->where('is_active', 1)
|
||||
->where('action_type', 'inception')
|
||||
->where('TRIM(policy_no)', $policy_no)
|
||||
->where('policy_no IS NOT NULL AND policy_no <> ""')
|
||||
->countAllResults();
|
||||
->orderBy('id', 'desc')
|
||||
->first() ?? [];
|
||||
|
||||
// Check in Enrollment (client_policy)
|
||||
$client_policy_data = $this->clientPolicyModel
|
||||
@ -6302,14 +6481,15 @@ class ClientController extends AdminController
|
||||
->first();
|
||||
|
||||
// Decide message + count
|
||||
if ($bds_count > 0) {
|
||||
if (count($bds_data) > 0) {
|
||||
|
||||
return $this->respond([
|
||||
'status' => true,
|
||||
'message' => "This policy number is already linked to another policy in the BDS",
|
||||
'code' => 409,
|
||||
'data' => $bds_count,
|
||||
'data' => $bds_data['id'],
|
||||
'received_data' => $received_data,
|
||||
'pt_id' => $bds_data['id'],
|
||||
'client_policy_id' => $client_policy_data['id'] ?? null
|
||||
], 200);
|
||||
|
||||
@ -6328,8 +6508,9 @@ class ClientController extends AdminController
|
||||
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'message' => 'No Data Found',
|
||||
'message' => 'Policy number not found in BDS or Enrollment, so proceed to add a new policy.',
|
||||
'code' => 404,
|
||||
'is_dublicate' => false,
|
||||
'received_data' => $received_data,
|
||||
'client_policy_id' => null
|
||||
], 200);
|
||||
|
||||
@ -721,21 +721,43 @@ class DashboardController extends AdminController
|
||||
return $this->respond(['status' => false, 'code' => 400, 'message' => 'No template found', 'message2' => 'Failed'], 200);
|
||||
}
|
||||
|
||||
|
||||
$emp_data = $this->employeePolicyModel->getEmployeePolicyForEcard($policy_id);
|
||||
if (count($emp_data) == 0) {
|
||||
return $this->respond(['status' => false, 'code' => 400, 'message' => 'No employees found', 'message2' => 'Failed'], 200);
|
||||
}
|
||||
$ids = array_column($emp_data, 'id');
|
||||
|
||||
|
||||
$client_policy_data = $this->clientPolicyModel->where('id', $policy_id)->first();
|
||||
$sendMail = false;
|
||||
|
||||
if ($client_policy_data['policy_type_id'] == 3 && $client_policy_data['is_addon'] == 3) {
|
||||
$sendMail = true;
|
||||
} else {
|
||||
$relationships = array_column($emp_data, 'relationship');
|
||||
if (in_array('Self', $relationships)) {
|
||||
$sendMail = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($sendMail) {
|
||||
$message = 'Mail Queued';
|
||||
Jobs::addJob(['job_name' => 'sendMailForDownloadingECard','payload' => ['ids' => $ids,'client_policy_id' => $policy_id]]);
|
||||
$this->myLogger->logme('error', 'sendManualEcard - Mail Queued');
|
||||
} else {
|
||||
$message = 'E-card has already been sent to those employees.';
|
||||
$this->myLogger->logme('error', 'sendManualEcard - E-card has already been sent to those employees.');
|
||||
}
|
||||
|
||||
// print_r(($ids)); die;
|
||||
// print_r($this->clientPolicyModel->getLastQuery()); die;
|
||||
// $this->myLogger->logme('error', "Policy details fetched: " . json_encode($client_policy_data));
|
||||
Jobs::addJob(['job_name' => 'sendMailForDownloadingECard', 'payload' => ['ids' => $ids, 'client_policy_id' => $policy_id]]);
|
||||
// Jobs::addJob(['job_name' => 'sendMailForDownloadingECard', 'payload' => ['ids' => $ids, 'client_policy_id' => $policy_id]]);
|
||||
// $empEmpDataServiceController = new EmpDataServiceController();
|
||||
// $empEmpDataServiceController->sendMailForDownloadingECard($ids);
|
||||
|
||||
|
||||
return $this->respond(['status' => true, 'code' => 200, 'message' => 'Mail Queued'], 200);
|
||||
return $this->respond(['status' => true, 'code' => 200, 'message' => $message], 200);
|
||||
}
|
||||
|
||||
public function data_construct_for_bds($data)
|
||||
|
||||
@ -3891,6 +3891,13 @@ class EmpDataServiceController extends BaseController
|
||||
|
||||
$policy_name = $this->getPolicyNameUsingClientPolicyId($client_policy_id);
|
||||
$job_details = new Jobs();
|
||||
|
||||
$r = Jobs::addJob(['job_name' => 'visitOffBoard', 'payload' => [
|
||||
'memberIds' => $employee_policy_table_primaryKey ?? [],
|
||||
'policyNumber' => $policy_name['policy_no'] ?? null ,
|
||||
'source' => 'NHANCE'
|
||||
]]);
|
||||
|
||||
$r = Jobs::addJob(['job_name' => 'cashDepositCalculationForDeletion', 'payload' => [
|
||||
'employeeIds' => $employee_policy_table_primaryKey,
|
||||
'client_id' => $client_id,
|
||||
@ -4703,7 +4710,7 @@ class EmpDataServiceController extends BaseController
|
||||
|
||||
public function getPolicyNameUsingClientPolicyId($client_policy_id)
|
||||
{
|
||||
return $this->clientPolicyModel->select('policy_type.policy_type as policy_name')
|
||||
return $this->clientPolicyModel->select('policy_type.policy_type as policy_name, client_policy.policy_no')
|
||||
->join('policy_type', 'policy_type.id = client_policy.policy_type_id')
|
||||
->where('client_policy.id', $client_policy_id)
|
||||
->first();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -2272,8 +2272,10 @@ class EmployeeRestController extends AdminController
|
||||
where is_active = 1
|
||||
and status = $emp_policy_status
|
||||
and client_policy_id = client_policy.id
|
||||
) as total_premium
|
||||
) as total_premium,
|
||||
CASE WHEN policy_type.allocg IN ('Non-EB', 'Marine') THEN 'Non-EB' ELSE 'EB' END as allocg
|
||||
", false)
|
||||
->join('policy_type', 'policy_type.id = client_policy.policy_type_id', 'left')
|
||||
->where('md5(client_policy.client_id)', $this->request->getGet('client_id'))
|
||||
->where('client_policy.client_branch_id', $this->request->getGet('client_branch_id'))
|
||||
->where('client_policy.is_active', 1)
|
||||
@ -2404,6 +2406,9 @@ class EmployeeRestController extends AdminController
|
||||
|
||||
if ($this->request->is('get')) {
|
||||
|
||||
$client_id = $this->request->getGet('client_id') ?? null;
|
||||
|
||||
|
||||
$data['claim_status'] = $this->claimStatusModel
|
||||
->select('id,ticket_type, display_name as claim_status')
|
||||
->where('is_active', 1)
|
||||
@ -2411,12 +2416,57 @@ class EmployeeRestController extends AdminController
|
||||
->groupBy('display_name')
|
||||
->findAll();
|
||||
|
||||
$data['ticket_type'] = [
|
||||
["ticket_type" => "1", "type_name" => "Claim-GMC"],
|
||||
["ticket_type" => "2", "type_name" => "Claim-GPA"],
|
||||
["ticket_type" => "3", "type_name" => "EDLI"],
|
||||
["ticket_type" => "4", "type_name" => "GTLI"],
|
||||
];
|
||||
if (!empty($client_id)) {
|
||||
|
||||
if (is_string($client_id) && preg_match('/^[a-f0-9]{32}$/i', $client_id)) {
|
||||
$client_data = $this->clientModel->where('MD5(id)', $client_id)->first();
|
||||
$client_id = $client_data['id'] ?? null;
|
||||
}
|
||||
|
||||
$client_policy_data = $this->clientPolicyModel
|
||||
->where('client_id', $client_id)
|
||||
->where('is_active', 1)
|
||||
->groupBy('policy_type_id')
|
||||
->findAll();
|
||||
|
||||
$data['ticket_type'] = [];
|
||||
$addedTypes = [];
|
||||
|
||||
foreach ($client_policy_data as $value) {
|
||||
|
||||
if (in_array($value['policy_type_id'], [2,3,4,5]) && !in_array('1', $addedTypes)) {
|
||||
$data['ticket_type'][] = ["ticket_type" => "1", "type_name" => "Claim-GMC"];
|
||||
$addedTypes[] = '1';
|
||||
|
||||
} elseif (in_array($value['policy_type_id'], [1]) && !in_array('2', $addedTypes)) {
|
||||
$data['ticket_type'][] = ["ticket_type" => "2", "type_name" => "Claim-GPA"];
|
||||
$addedTypes[] = '2';
|
||||
|
||||
} elseif (in_array($value['policy_type_id'], [6]) && !in_array('3', $addedTypes)) {
|
||||
$data['ticket_type'][] = ["ticket_type" => "3", "type_name" => "EDLI"];
|
||||
$addedTypes[] = '3';
|
||||
|
||||
} elseif (in_array($value['policy_type_id'], [7]) && !in_array('4', $addedTypes)) {
|
||||
$data['ticket_type'][] = ["ticket_type" => "4", "type_name" => "GTLI"];
|
||||
$addedTypes[] = '4';
|
||||
|
||||
} elseif (in_array($value['policy_type_id'], [72]) && !in_array('72', $addedTypes)) {
|
||||
$data['ticket_type'][] = ["ticket_type" => "72", "type_name" => "OPD"];
|
||||
$addedTypes[] = '72';
|
||||
}
|
||||
}
|
||||
|
||||
usort($data['ticket_type'], fn($a,$b) => $a['ticket_type'] <=> $b['ticket_type']);
|
||||
|
||||
} else {
|
||||
|
||||
$data['ticket_type'] = [
|
||||
["ticket_type" => "1", "type_name" => "Claim-GMC"],
|
||||
["ticket_type" => "2", "type_name" => "Claim-GPA"],
|
||||
["ticket_type" => "3", "type_name" => "EDLI"],
|
||||
["ticket_type" => "4", "type_name" => "GTLI"],
|
||||
];
|
||||
}
|
||||
|
||||
$claim_type = $this->ticketController->claimType;
|
||||
unset($claim_type[1][2]);
|
||||
@ -4472,6 +4522,29 @@ class EmployeeRestController extends AdminController
|
||||
];
|
||||
// print_r($post_data); die;
|
||||
|
||||
|
||||
$ClientPolicyData = $this->clientPolicyModel
|
||||
->select("
|
||||
client_policy.id as client_policy_id ,
|
||||
client_policy.client_id as client_id,
|
||||
client_policy.policy_type_id as policy_type_id,
|
||||
client_policy.is_addon as is_addon ,
|
||||
client_policy.open_for_enrollment as OpenForEnrollment ,
|
||||
client_policy.inception_type as inception_type,
|
||||
client_policy.policy_no as policy_no,
|
||||
client_policy.insurer_id as insurer_id,
|
||||
DATE_FORMAT(client_policy.policy_start_date, '%d-%m-%Y') AS policy_start_date,
|
||||
DATE_FORMAT(client_policy.policy_end_date, '%d-%m-%Y') AS policy_expiry_date,
|
||||
CASE WHEN policy_type.allocg IN ('Non-EB', 'Marine') THEN 'Non-EB' ELSE 'EB' END as allocg
|
||||
", false)
|
||||
->join('policy_type', 'policy_type.id = client_policy.policy_type_id', 'left')
|
||||
->where('md5(client_policy.client_id)', $post_data['client_id'])
|
||||
->where('client_policy.client_branch_id', $post_data['client_branch_id'])
|
||||
->where('client_policy.is_active', 1)
|
||||
// ->where('client_policy.policy_status', $this->request->getGet('policy_status'))
|
||||
->whereIn('client_policy.id', $post_data['policy_id'])
|
||||
->first();
|
||||
|
||||
if (empty($post_data['client_id'])) {
|
||||
return $this->respondCreated(['status' => false, 'message' => 'Client is required', 'data' => []]);
|
||||
}
|
||||
@ -4490,7 +4563,7 @@ class EmployeeRestController extends AdminController
|
||||
|
||||
// print_r($client_data); die;
|
||||
|
||||
if ($client_data['hr_file_processed_by'] == 1) {
|
||||
if ($client_data['hr_file_processed_by'] == 1 && $ClientPolicyData['allocg'] == 'EB') {
|
||||
$responce = $this->fileUploadInFilesTable($post_data);
|
||||
} else {
|
||||
$responce = $this->fileUploadInHrFileUploadTable($post_data);
|
||||
|
||||
@ -2575,4 +2575,29 @@ class EmployeeServiceController extends AdminController
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the inception Excel column configuration used for
|
||||
* validating and processing Employee Upload with Events files.
|
||||
* This allows other controllers to generate compatible Excel files.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getInceptionExcelColumns(): array
|
||||
{
|
||||
return $this->inception_excel_columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the correction Excel column configuration used for
|
||||
* validating and processing Employee correction files.
|
||||
* This is reused by other controllers when they need to generate
|
||||
* a correction-compatible Excel programmatically.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getCorrectionExcelColumns(): array
|
||||
{
|
||||
return $this->correction_excel_columns;
|
||||
}
|
||||
}
|
||||
@ -34,8 +34,7 @@ class ExpenseController extends AdminController
|
||||
try {
|
||||
$data['tab_name'] = 'Expense';
|
||||
$data['page_name'] = 'Expense';
|
||||
$descriptionPattern = '/^[a-zA-Z0-9\s\.\,\-\(\)\/\&\+]+$/u';
|
||||
|
||||
$descriptionPattern = '/^[a-zA-Z0-9\s\.\,\-\(\)\/\&\+\'"]+$/u';
|
||||
// Raw GET filters
|
||||
$rawFilters = $this->request->getGet() ?? [];
|
||||
$rawFilters = is_array($rawFilters) ? $rawFilters : [];
|
||||
@ -97,6 +96,7 @@ class ExpenseController extends AdminController
|
||||
// Clients for dropdown
|
||||
$data['clients'] = $this->clientModel
|
||||
->select('id, client_name, short_name')
|
||||
->where('client_type', 1)
|
||||
->where('is_active', 1)
|
||||
->orderBy('client_name', 'ASC')
|
||||
->findAll();
|
||||
@ -228,11 +228,12 @@ class ExpenseController extends AdminController
|
||||
],
|
||||
],
|
||||
'amount' => [
|
||||
'rules' => 'required|numeric|greater_than_equal_to[0]',
|
||||
'rules' => 'required|numeric|greater_than_equal_to[0]|less_than_equal_to[100000000]',
|
||||
'errors' => [
|
||||
'required' => 'Amount is required',
|
||||
'numeric' => 'Amount must be numeric',
|
||||
'greater_than_equal_to' => 'Amount cannot be negative',
|
||||
'less_than_equal_to' => 'Amount cannot be greater than 100 Cr.',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
<?php namespace App\Controllers;
|
||||
|
||||
use App\Libraries\GoogleSheetLib;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use CodeIgniter\Controller;
|
||||
|
||||
class GoogleSheetController extends Controller
|
||||
@ -133,4 +134,23 @@ class GoogleSheetController extends Controller
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* CLI: php public/index.php cli/list-sheet-folder-files [folderId]
|
||||
*/
|
||||
public function listFolderSheetFilesCli(string $folderId = '')
|
||||
{
|
||||
if (! is_cli()) {
|
||||
return $this->response
|
||||
->setStatusCode(405)
|
||||
->setJSON(['status' => 'error', 'message' => 'CLI only route']);
|
||||
}
|
||||
|
||||
$folderId = CLI::getOption('folderId') ?: $folderId;
|
||||
$json = $this->sheetLib->listFolderSheetIdsAsJson($folderId);
|
||||
file_put_contents('sheetid.json', $json);
|
||||
CLI::write($json);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -246,6 +246,10 @@ class JobWorker extends AdminController
|
||||
'getEmployeeEcardFromTmpFolderAndZipToS3' => [
|
||||
'type' => 'CC', // Handler Category
|
||||
'handler' => 'App\Controllers\EmployeeController',
|
||||
],
|
||||
'visitOffBoard' => [
|
||||
'type' => 'CC', // Handler Category
|
||||
'handler' => 'App\Controllers\EmployeeController',
|
||||
]
|
||||
];
|
||||
|
||||
|
||||
@ -1,21 +1,18 @@
|
||||
<?php
|
||||
namespace App\Controllers;
|
||||
|
||||
|
||||
use App\Controllers\Jobs;
|
||||
|
||||
use CodeIgniter\API\ResponseTrait;
|
||||
|
||||
|
||||
use Exception;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Border;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Alignment;
|
||||
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Border;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Fill;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
|
||||
|
||||
use App\Models\UserModel;
|
||||
use App\Models\ClientModel;
|
||||
@ -38,38 +35,9 @@ use App\Models\SalesActualLeadModel;
|
||||
use App\Models\SalesContactPersonModel;
|
||||
|
||||
use App\Helpers\MailHelper;
|
||||
|
||||
use App\Helpers\ExcelMergeHelper;
|
||||
use App\Helpers\ExcelSanitizeHelper;
|
||||
use App\Helpers\MailHelper;
|
||||
use App\Libraries\GoogleSheetLib;
|
||||
use App\Models\ClientBranchModel;
|
||||
use App\Models\ClientModel;
|
||||
use App\Models\ClientPolicyModel;
|
||||
use App\Models\GmailSentHistoryModel;
|
||||
use App\Models\InsurerBranchModel;
|
||||
use App\Models\InsurerModel;
|
||||
use App\Models\KYCEntityTypeModel;
|
||||
use App\Models\LeadFilesModel;
|
||||
use App\Models\LeadInstallmentPaymentDetails;
|
||||
use App\Models\LeadsModel;
|
||||
use App\Models\LevelContactModel;
|
||||
use App\Models\OccupancyMasterModel;
|
||||
use App\Models\PolicyTransactionStatusModel;
|
||||
use App\Models\PolicyTypeModel;
|
||||
use App\Models\RFQModel;
|
||||
use App\Models\TPABranchModel;
|
||||
use App\Models\UserModel;
|
||||
use CodeIgniter\API\ResponseTrait;
|
||||
use Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Alignment;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Border;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Fill;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
|
||||
|
||||
class LeadsController extends BaseController
|
||||
{
|
||||
@ -402,7 +370,9 @@ class LeadsController extends BaseController
|
||||
$actual_lead_id = ! empty($actual_lead_id) ? $actual_lead_id : null;
|
||||
$postData = $this->request->getPost();
|
||||
$data = $this->prepareLeadData();
|
||||
$rules = [
|
||||
|
||||
|
||||
$rules = [
|
||||
|
||||
'lead_type' => [
|
||||
'rules' => 'integer',
|
||||
@ -412,21 +382,6 @@ class LeadsController extends BaseController
|
||||
'rules' => 'required',
|
||||
'errors' => ['required' => 'Issuer is required'],
|
||||
],
|
||||
'entity_type_id' => [
|
||||
'rules' => 'required',
|
||||
'errors' => ['required' => 'Entity Type is required'],
|
||||
],
|
||||
'client_name' => [
|
||||
'rules' => 'required|regex_match[/^[a-zA-Z0-9_ -]+$/]',
|
||||
'errors' => [
|
||||
'required' => 'Client Name is required',
|
||||
'regex_match' => 'Client Name only letters, numbers, space, hyphens and underscores are allowed',
|
||||
],
|
||||
],
|
||||
'client_short_name' => [
|
||||
'rules' => 'required|regex_match[/^[a-zA-Z0-9_-]+$/]',
|
||||
'errors' => ['required' => 'Client Short Name is required', 'regex_match' => 'Client Short Name only letters, numbers, hyphens and underscores are allowed'],
|
||||
],
|
||||
'gst' => [
|
||||
'rules' => 'required|regex_match[/^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z]{1}[1-9A-Z]{1}Z[0-9A-Z]{1}$/]',
|
||||
'errors' => [
|
||||
@ -492,6 +447,30 @@ class LeadsController extends BaseController
|
||||
],
|
||||
];
|
||||
|
||||
if (isset($postData['lead_type']) && $postData['lead_type'] == 1) {
|
||||
|
||||
$rules['entity_type_id'] = [
|
||||
'rules' => 'required',
|
||||
'errors' => ['required' => 'Entity Type is required'],
|
||||
];
|
||||
|
||||
$rules['client_name'] = [
|
||||
'rules' => 'required|regex_match[/^[a-zA-Z0-9_ -]+$/]',
|
||||
'errors' => [
|
||||
'required' => 'Client Name is required',
|
||||
'regex_match' => 'Client Name only letters, numbers, space, hyphens and underscores are allowed',
|
||||
],
|
||||
];
|
||||
|
||||
$rules['client_short_name'] = [
|
||||
'rules' => 'required|regex_match[/^[a-zA-Z0-9_-]+$/]',
|
||||
'errors' => [
|
||||
'required' => 'Client Short Name is required',
|
||||
'regex_match' => 'Client Short Name only letters, numbers, hyphens and underscores are allowed'
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($postData as $key => $value) {
|
||||
// Check if the key starts with 'docs_name_'
|
||||
if (strpos($key, 'docs_name_') === 0) {
|
||||
@ -508,10 +487,6 @@ class LeadsController extends BaseController
|
||||
// print_r($rules); die;
|
||||
|
||||
if ((int) $this->request->getPost('lead_form_type') === 2) {
|
||||
$rules['client_type'] = [
|
||||
'rules' => 'required',
|
||||
'errors' => ['required' => 'Client Type is required'],
|
||||
];
|
||||
$rules['policy_type_id'] = [
|
||||
'rules' => 'required',
|
||||
'errors' => ['required' => 'Policy Type is required'],
|
||||
@ -659,7 +634,6 @@ class LeadsController extends BaseController
|
||||
$request_data = $this->request->getPost();
|
||||
$data = sanitizeInputArrayAdvanced($request_data);
|
||||
$data['client_type'] = 1;
|
||||
$data['pan'] = "";
|
||||
|
||||
if ($data['lead_type'] != 1) {
|
||||
$client_data = $this->clientModel->where('id', $data['client_id'])->where('is_active', 1)->first();
|
||||
@ -875,6 +849,7 @@ class LeadsController extends BaseController
|
||||
'source_policy_id' => $data['source_policy_id'] ?? 0,
|
||||
'policy_type_id' => $value,
|
||||
'salse_person_id' => $data['salse_person_id'] ?? 0,
|
||||
'rfq_qcr_viewers' => $data['rfq_qcr_viewers'] ?? null,
|
||||
'insurer_id' => $insurer_id ?? 0,
|
||||
'insurer_branch_id' => $insurer_branch_id ?? 0,
|
||||
'tpa_id' => $tpa_id ?? 0,
|
||||
@ -1152,6 +1127,59 @@ class LeadsController extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
public function uploadLeadAttachment()
|
||||
{
|
||||
try {
|
||||
$leadId = (int) $this->request->getPost('lead_id');
|
||||
$docsName = trim($this->request->getPost('docs_name') ?? '');
|
||||
|
||||
if ($leadId <= 0) {
|
||||
return $this->respond(['status' => 'error', 'code' => 400, 'message' => 'Invalid lead id'], 400);
|
||||
}
|
||||
if (empty($docsName)) {
|
||||
return $this->respond(['status' => 'error', 'code' => 400, 'message' => 'Document label is required'], 400);
|
||||
}
|
||||
|
||||
$file = $this->request->getFile('attachment_file');
|
||||
if (! $file || ! $file->isValid() || $file->hasMoved()) {
|
||||
return $this->respond(['status' => 'error', 'code' => 400, 'message' => 'Invalid or missing file'], 400);
|
||||
}
|
||||
|
||||
$uploadFilePath = WRITEPATH . 'uploads/lead_files/';
|
||||
$fileName = file_Upload_for_lead($file, $uploadFilePath, UPLOAD_EXT_LEAD_FILES);
|
||||
|
||||
if (empty($fileName)) {
|
||||
return $this->respond(['status' => 'error', 'code' => 400, 'message' => 'File upload failed. Check file type or size.'], 400);
|
||||
}
|
||||
|
||||
$this->leadFilesModel->insert([
|
||||
'lead_id' => $leadId,
|
||||
'docs_name' => $docsName,
|
||||
'file_name' => $fileName,
|
||||
]);
|
||||
|
||||
// Return refreshed attachment HTML
|
||||
$attachments = $this->leadFilesModel
|
||||
->where('lead_id', $leadId)
|
||||
->where('type !=', 2)
|
||||
->where('is_active', 1)
|
||||
->findAll();
|
||||
|
||||
$attachmentHtml = view('rfq/attachment_files', ['multi_file_data' => $attachments]);
|
||||
|
||||
return $this->respond([
|
||||
'status' => 'success',
|
||||
'code' => 200,
|
||||
'message' => 'File uploaded successfully',
|
||||
'attachment_html' => $attachmentHtml,
|
||||
], 200);
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
$this->myLogger->logme('error', 'uploadLeadAttachment: ' . $e->getMessage());
|
||||
return $this->respond(['status' => 'error', 'code' => 500, 'message' => 'Upload failed'], 500);
|
||||
}
|
||||
}
|
||||
|
||||
//--------RFQ-----------------------------------------------------------------------------------------------
|
||||
|
||||
// public function viewRFQ($id, $type = 1)
|
||||
@ -3380,6 +3408,15 @@ class LeadsController extends BaseController
|
||||
// Handle case where param_cc_mail is not valid
|
||||
// return $this->respond(['status' => 'failed','code' => 400,'data' => '','message' => 'CC mail not found!'], 200);
|
||||
}
|
||||
|
||||
// Append external CC emails for client mail (sent as email strings, not user IDs)
|
||||
if ($recipient_type === 'client' && ! empty($params['external_cc'])) {
|
||||
$externalCCs = json_decode($params['external_cc'], true);
|
||||
if (is_array($externalCCs)) {
|
||||
$cc_mails = array_merge($cc_mails, array_values(array_filter(array_map('trim', $externalCCs))));
|
||||
}
|
||||
}
|
||||
|
||||
} else if ($recipient_type == 'placement') {
|
||||
|
||||
if(isset($params['cc']) && !empty($params['cc'])) {
|
||||
@ -3431,9 +3468,12 @@ class LeadsController extends BaseController
|
||||
|
||||
//get file path to attach
|
||||
|
||||
if ($lead_data["lead_form_type"] == 2) {
|
||||
// For Non-EB, download RFQ/QCR file from Google Sheet (if configured)
|
||||
$file_info = $this->downloadFileFromGoogleSheet($lead_data);
|
||||
if ($lead_data["lead_form_type"] == 2 && $recipient_type === 'placement') {
|
||||
// For Non-EB placement: copy QCR sheet as placement file, then download it
|
||||
$file_info = $this->createAndDownloadPlacementSheet($lead_data);
|
||||
} else if ($lead_data["lead_form_type"] == 2) {
|
||||
// For Non-EB, download RFQ/QCR file from Google Sheet based on recipient type
|
||||
$file_info = $this->downloadFileFromGoogleSheet($lead_data, $recipient_type);
|
||||
} else {
|
||||
$file_info = $this->constructExcelToSaveTemp($lead_id, ($file_type == 'rfq' ? 1 : 2), $propsal_and_insurer);
|
||||
}
|
||||
@ -3474,8 +3514,17 @@ class LeadsController extends BaseController
|
||||
// print_r($attachments); die;
|
||||
|
||||
//get recipient address
|
||||
if ($recipient_type == 'insurer' || $recipient_type == 'placement') {
|
||||
|
||||
if ($recipient_type == 'placement') {
|
||||
// Placement TO is client contact email sent directly
|
||||
if(!is_array($recipient_mail)) {
|
||||
$recipient_mail = [$recipient_mail];
|
||||
}
|
||||
$recipient_data = [];
|
||||
foreach ($recipient_mail as $mail) {
|
||||
$recipient_data[] = ['name' => $lead_data['contact_person_name'] ?? '', 'email' => trim($mail)];
|
||||
}
|
||||
} else if ($recipient_type == 'insurer') {
|
||||
|
||||
if(!is_array($recipient_mail)) {
|
||||
$recipient_mail = [$recipient_mail];
|
||||
}
|
||||
@ -4567,7 +4616,17 @@ class LeadsController extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
// Client contact from lead data for placement TO
|
||||
$clientContact = null;
|
||||
if (! empty($lead['contact_person_email'])) {
|
||||
$clientContact = [
|
||||
'name' => $lead['contact_person_name'] ?? '',
|
||||
'email' => $lead['contact_person_email'],
|
||||
];
|
||||
}
|
||||
|
||||
$data['insurer_contacts'] = $insurerContacts;
|
||||
$data['client_contact'] = $clientContact;
|
||||
|
||||
return $this->respond(
|
||||
[
|
||||
@ -4647,13 +4706,16 @@ class LeadsController extends BaseController
|
||||
'selected_lead_type' => $type,
|
||||
'actual_lead_id' => $actual_lead_id,
|
||||
];
|
||||
$data['tab_name'] = 'Opportunities';
|
||||
$data['page_name'] = 'Opportunities';
|
||||
|
||||
if ($actual_lead_id > 0) {
|
||||
|
||||
// 🔹 Client Details (Single Row)
|
||||
$data['actual_lead_client_details'] = $this->leadModel
|
||||
->select('company_name, email, phone, address, website, gst_number, status, assigned_to')
|
||||
->where('lead_id', $actual_lead_id)
|
||||
->select('sales_actual_leads.company_name, clients.short_name, sales_actual_leads.email, sales_actual_leads.phone, sales_actual_leads.address, sales_actual_leads.website, sales_actual_leads.gst_number, sales_actual_leads.status, sales_actual_leads.assigned_to')
|
||||
->join('clients', 'clients.id = sales_actual_leads.client_id', 'left')
|
||||
->where('sales_actual_leads.lead_id', $actual_lead_id)
|
||||
->first(); // first row only
|
||||
|
||||
|
||||
@ -4791,15 +4853,21 @@ class LeadsController extends BaseController
|
||||
|
||||
$this->myLogger->logme('error', "RFQ Sheet: starting for lead_id={$leadId}");
|
||||
|
||||
// Fetch lead + policy type (including misc JSON from both)
|
||||
// Fetch lead + policy type + client address (LEFT JOIN for communication address on EB leads)
|
||||
$lead = $this->leadsModel
|
||||
->select('
|
||||
leads.*,
|
||||
policy_type.policy_type,
|
||||
policy_type.long_name,
|
||||
policy_type.misc as policy_misc
|
||||
policy_type.misc as policy_misc,
|
||||
clients.address1 as client_address1,
|
||||
clients.address2 as client_address2,
|
||||
clients.city as client_city,
|
||||
clients.state as client_state,
|
||||
clients.pincode as client_pincode
|
||||
')
|
||||
->join('policy_type', 'leads.policy_type_id = policy_type.id', 'left')
|
||||
->join('clients', 'leads.client_id = clients.id', 'left')
|
||||
->where('leads.id', $leadId)
|
||||
->where('leads.is_active', 1)
|
||||
->first();
|
||||
@ -4922,22 +4990,76 @@ class LeadsController extends BaseController
|
||||
$newSheetId = $sheetLib->copyTemplate($templateId, $sheetName, $parentFolderId);
|
||||
$this->myLogger->logme('error', "RFQ Sheet: template copied | lead_id={$leadId}, template_id={$templateId}, new_sheet_id={$newSheetId}");
|
||||
|
||||
// Fill placeholders across all sheets (see Config\RfqConfig::$rfqPlaceholders)
|
||||
try {
|
||||
/** @var \Config\RfqConfig $rfqCfg */
|
||||
$rfqCfg = config('RfqConfig');
|
||||
$placeholderData = $this->buildRfqPlaceholderData($lead);
|
||||
$claimsPh = $rfqCfg->rfqClaimsPlaceholder ?? '{{CLAIMS_DETAILS}}';
|
||||
$map = $rfqCfg->rfqPlaceholders ?? [];
|
||||
$textReplacements = [];
|
||||
|
||||
foreach ($map as $placeholder => $key) {
|
||||
if ($placeholder === $claimsPh) {
|
||||
continue;
|
||||
}
|
||||
$textReplacements[$placeholder] = (string) ($placeholderData['values'][$key] ?? '');
|
||||
}
|
||||
|
||||
$sheetLib->findAndReplaceAllSheets($newSheetId, $textReplacements);
|
||||
|
||||
if ($placeholderData['claims_table'] !== null) {
|
||||
$written = $sheetLib->writeTableAtPlaceholder(
|
||||
$newSheetId,
|
||||
$claimsPh,
|
||||
$placeholderData['claims_table']
|
||||
);
|
||||
|
||||
if (! $written) {
|
||||
$sheetLib->findAndReplaceAllSheets($newSheetId, [
|
||||
$claimsPh => $placeholderData['claims_placeholder_fallback'],
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
$sheetLib->findAndReplaceAllSheets($newSheetId, [
|
||||
$claimsPh => $placeholderData['claims_placeholder_fallback'],
|
||||
]);
|
||||
}
|
||||
|
||||
$this->myLogger->logme('error', "RFQ Sheet: placeholders applied | lead_id={$leadId}, sheet_id={$newSheetId}");
|
||||
} catch (\Throwable $e) {
|
||||
$this->myLogger->logme(
|
||||
'error',
|
||||
"RFQ Sheet: placeholder fill failed | lead_id={$leadId}, error=" . $e->getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
// Apply permissions: get sales users from DB (team_id 5) and add as editors
|
||||
$salesTeam = $this->userModel
|
||||
->select('user_profiles.email')
|
||||
->join('user_teams', 'user_profiles.id = user_teams.user_id')
|
||||
->where('user_teams.team_id', 5)
|
||||
->where('user_teams.is_active', 1)
|
||||
->where('user_profiles.is_active', 1)
|
||||
->findAll();
|
||||
$editorEmails = array_values(array_filter(array_unique(array_column($salesTeam, 'email'))));
|
||||
if (! empty($editorEmails)) {
|
||||
// $salesTeam = $this->userModel
|
||||
// ->select('user_profiles.email')
|
||||
// ->join('user_teams', 'user_profiles.id = user_teams.user_id')
|
||||
// ->where('user_teams.team_id', 5)
|
||||
// ->where('user_teams.is_active', 1)
|
||||
// ->where('user_profiles.is_active', 1)
|
||||
// ->findAll();
|
||||
// $editorEmails = array_values(array_filter(array_unique(array_column($salesTeam, 'email'))));
|
||||
|
||||
// Decode rfq_qcr_viewers from lead and add as viewers
|
||||
$editorEmails = [];
|
||||
if (! empty($lead['rfq_qcr_viewers'])) {
|
||||
$decoded = json_decode($lead['rfq_qcr_viewers'], true);
|
||||
if (is_array($decoded)) {
|
||||
$editorEmails = array_values(array_filter(array_unique($decoded)));
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty($editorEmails)) {
|
||||
try {
|
||||
$sheetLib->applyPermissions($newSheetId, [
|
||||
'editors' => $editorEmails,
|
||||
'viewers' => [],
|
||||
// 'viewers' => $viewerEmails,
|
||||
]);
|
||||
$this->myLogger->logme('error', "RFQ Sheet: permissions applied (sales team) | sheet_id={$newSheetId}, count=" . count($editorEmails));
|
||||
$this->myLogger->logme('error', "RFQ Sheet: permissions applied | sheet_id={$newSheetId}, editors=" . count($editorEmails) );
|
||||
} catch (\Throwable $e) {
|
||||
$this->myLogger->logme(
|
||||
'error',
|
||||
@ -5006,17 +5128,344 @@ class LeadsController extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build values for RFQ Google Sheet placeholders (see RfqConfig::$rfqPlaceholders).
|
||||
*
|
||||
* @return array{values: array<string, string>, claims_table: ?list<list<string|int|float>>, claims_placeholder_fallback: string}
|
||||
*/
|
||||
private function buildRfqPlaceholderData(array $lead): array
|
||||
{
|
||||
helper('excel_util_helper');
|
||||
|
||||
$custom = [];
|
||||
|
||||
if (! empty($lead['custom_fields'])) {
|
||||
$decoded = json_decode($lead['custom_fields'], true);
|
||||
|
||||
if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
|
||||
$custom = $decoded;
|
||||
}
|
||||
}
|
||||
|
||||
$clientParts = array_filter([
|
||||
trim((string) ($lead['client_address1'] ?? '')),
|
||||
trim((string) ($lead['client_address2'] ?? '')),
|
||||
trim((string) ($lead['client_city'] ?? '')),
|
||||
trim((string) ($lead['client_state'] ?? '')),
|
||||
trim((string) ($lead['client_pincode'] ?? '')),
|
||||
], static function ($s) {
|
||||
return $s !== '';
|
||||
});
|
||||
$clientAddr = implode(', ', $clientParts);
|
||||
|
||||
$cfParts = array_filter([
|
||||
trim((string) ($custom['address'] ?? '')),
|
||||
trim((string) ($custom['pincode'] ?? '')),
|
||||
], static function ($s) {
|
||||
return $s !== '';
|
||||
});
|
||||
$cfAddr = implode(', ', $cfParts);
|
||||
|
||||
$communicationAddress = $clientAddr !== '' ? $clientAddr : $cfAddr;
|
||||
|
||||
$policyPeriod = '';
|
||||
|
||||
if (! empty($lead['policy_start_date']) && ! empty($lead['policy_end_date'])) {
|
||||
$policyPeriod = date('d-m-Y', strtotime((string) $lead['policy_start_date']))
|
||||
. ' to '
|
||||
. date('d-m-Y', strtotime((string) $lead['policy_end_date']));
|
||||
}
|
||||
|
||||
$lt = (int) ($lead['lead_type'] ?? 0);
|
||||
$opportunityType = $this->leadType[$lt] ?? '';
|
||||
|
||||
$riskRaw = (string) ($custom['risk_location'] ?? '');
|
||||
$riskLocation = $riskRaw !== ''
|
||||
? ucwords(str_replace('_', ' ', strtolower($riskRaw)))
|
||||
: '';
|
||||
|
||||
$occupancyRaw = (string) ($custom['business_description'] ?? $custom['occupancy_risk'] ?? '');
|
||||
$occupancy = $occupancyRaw !== ''
|
||||
? ucwords(str_replace('_', ' ', strtolower($occupancyRaw)))
|
||||
: '';
|
||||
|
||||
$claimsTable = null;
|
||||
$claimsFallback = 'No claims data available';
|
||||
|
||||
if (! empty($lead['fin_years_claims'])) {
|
||||
$claimJson = json_decode($lead['fin_years_claims'], true);
|
||||
|
||||
if (json_last_error() === JSON_ERROR_NONE && is_array($claimJson)) {
|
||||
$finyear = $claimJson['finyear'] ?? null;
|
||||
|
||||
if (is_array($finyear) && $finyear !== []) {
|
||||
$claimsTable = $this->buildRfqClaimsTableValues($finyear);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$values = [
|
||||
'insured_name' => trim((string) ($lead['client_name'] ?? $lead['client_short_name'] ?? '')),
|
||||
'communication_address' => $communicationAddress,
|
||||
'gst' => trim((string) ($lead['gst'] ?? '')),
|
||||
'pan' => trim((string) ($lead['pan'] ?? '')),
|
||||
'policy_period' => $policyPeriod,
|
||||
'opportunity_type' => $opportunityType,
|
||||
'risk_location' => $riskLocation,
|
||||
'occupancy' => $occupancy,
|
||||
'claims_details' => '',
|
||||
];
|
||||
|
||||
return [
|
||||
'values' => $values,
|
||||
'claims_table' => $claimsTable,
|
||||
'claims_placeholder_fallback' => $claimsFallback,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<array<string, mixed>> $finyear
|
||||
*
|
||||
* @return list<list<string|int|float>>
|
||||
*/
|
||||
private function buildRfqClaimsTableValues(array $finyear): array
|
||||
{
|
||||
helper('excel_util_helper');
|
||||
|
||||
$first = $finyear[0] ?? null;
|
||||
|
||||
if (! is_array($first) || $first === []) {
|
||||
return [['No data available']];
|
||||
}
|
||||
|
||||
$headers = [];
|
||||
|
||||
foreach (array_keys($first) as $key) {
|
||||
$headers[] = ucwords(str_replace('_', ' ', (string) $key));
|
||||
}
|
||||
|
||||
$rows = [];
|
||||
$rows[] = $headers;
|
||||
|
||||
foreach ($finyear as $record) {
|
||||
if (! is_array($record)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$line = [];
|
||||
|
||||
foreach ($record as $arrayKey => $value) {
|
||||
if (in_array($arrayKey, ['sum_insured', 'claim_amount', 'settled'], true)) {
|
||||
$line[] = function_exists('formatIndianCurrency')
|
||||
? formatIndianCurrency((int) $value)
|
||||
: (string) $value;
|
||||
} else {
|
||||
$line[] = $value === null || $value === '' ? '' : (string) $value;
|
||||
}
|
||||
}
|
||||
|
||||
$rows[] = $line;
|
||||
}
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
public function createQcrSheet()
|
||||
{
|
||||
try {
|
||||
$leadId = (int) $this->request->getGet('lead_id');
|
||||
|
||||
if ($leadId <= 0) {
|
||||
return $this->respond(
|
||||
['status' => 'error', 'code' => 400, 'message' => 'Invalid lead id'],
|
||||
400
|
||||
);
|
||||
}
|
||||
|
||||
$this->myLogger->logme('error', "QCR Sheet: starting for lead_id={$leadId}");
|
||||
|
||||
$lead = $this->leadsModel
|
||||
->select('
|
||||
leads.*,
|
||||
policy_type.policy_type,
|
||||
policy_type.long_name,
|
||||
policy_type.misc as policy_misc
|
||||
')
|
||||
->join('policy_type', 'leads.policy_type_id = policy_type.id', 'left')
|
||||
->where('leads.id', $leadId)
|
||||
->where('leads.is_active', 1)
|
||||
->first();
|
||||
|
||||
if (! $lead) {
|
||||
$this->myLogger->logme('error', "QCR Sheet: lead not found | lead_id={$leadId}");
|
||||
return $this->respond(
|
||||
['status' => 'error', 'code' => 404, 'message' => 'Lead not found'],
|
||||
404
|
||||
);
|
||||
}
|
||||
|
||||
$leadMisc = [];
|
||||
if (! empty($lead['misc'])) {
|
||||
$decoded = json_decode($lead['misc'], true);
|
||||
if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
|
||||
$leadMisc = $decoded;
|
||||
}
|
||||
}
|
||||
|
||||
// If QCR sheet already exists, just return it
|
||||
if (! empty($leadMisc['qcr_sheet_id'])) {
|
||||
$sheetId = (string) $leadMisc['qcr_sheet_id'];
|
||||
$sheetLib = new GoogleSheetLib();
|
||||
$sheetUrl = $sheetLib->sheetUrl($sheetId);
|
||||
|
||||
$this->myLogger->logme('error', "QCR Sheet: existing sheet found | lead_id={$leadId}, sheet_id={$sheetId}");
|
||||
|
||||
return $this->respond(
|
||||
[
|
||||
'status' => 'success',
|
||||
'code' => 200,
|
||||
'sheet_id' => $sheetId,
|
||||
'sheet_url' => $sheetUrl,
|
||||
'message' => 'QCR sheet already exists',
|
||||
],
|
||||
200
|
||||
);
|
||||
}
|
||||
|
||||
// Source is the existing RFQ sheet for this lead
|
||||
$rfqSheetId = $leadMisc['rfq_sheet_id'] ?? null;
|
||||
if (empty($rfqSheetId)) {
|
||||
$this->myLogger->logme('error', "QCR Sheet: rfq_sheet_id missing in leads.misc | lead_id={$leadId}");
|
||||
return $this->respond(
|
||||
['status' => 'error', 'code' => 400, 'message' => 'RFQ sheet not found. Please create the RFQ sheet first.'],
|
||||
400
|
||||
);
|
||||
}
|
||||
|
||||
// Load config for folder id and permissions
|
||||
$parentFolderId = null;
|
||||
$permissions = [];
|
||||
|
||||
try {
|
||||
/** @var \Config\RfqConfig $rfqConfig */
|
||||
$rfqConfig = config('RfqConfig');
|
||||
if ($rfqConfig) {
|
||||
$parentFolderId = $rfqConfig->rfqParentFolderId ?? null;
|
||||
$permissions = $rfqConfig->permissions ?? [];
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$this->myLogger->logme('error', 'QCR Sheet: error loading RfqConfig | ' . $e->getMessage());
|
||||
}
|
||||
|
||||
if (! $parentFolderId) {
|
||||
return $this->respond(
|
||||
['status' => 'error', 'code' => 400, 'message' => 'Google config not correct, contact administrator'],
|
||||
400
|
||||
);
|
||||
}
|
||||
|
||||
// Build filename: same as RFQ naming but with QCR
|
||||
$string = 'QCR';
|
||||
$current_year = date('Y');
|
||||
$next_year = $current_year + 1;
|
||||
$policy_year = "{$current_year}-{$next_year}";
|
||||
|
||||
$clientName = trim($lead['client_name'] ?? $lead['client_short_name'] ?? '');
|
||||
$policyType = trim($lead['policy_type'] ?? '');
|
||||
|
||||
if (! empty($lead['policy_end_date'])) {
|
||||
$policy_expiry = strtotime($lead['policy_end_date']);
|
||||
$formatted_policy = date('d-m-Y', $policy_expiry);
|
||||
$sheetName = "{$clientName}_{$policyType}_{$string}_{$policy_year}(Due On {$formatted_policy}).xlsx";
|
||||
} else {
|
||||
$sheetName = "{$clientName}_{$policyType}_{$string}_{$policy_year}_.xlsx";
|
||||
}
|
||||
|
||||
$sheetLib = new GoogleSheetLib();
|
||||
$newSheetId = $sheetLib->copyTemplate($rfqSheetId, $sheetName, $parentFolderId);
|
||||
$this->myLogger->logme('error', "QCR Sheet: copied from RFQ sheet | lead_id={$leadId}, rfq_sheet_id={$rfqSheetId}, new_sheet_id={$newSheetId}");
|
||||
|
||||
// Apply permissions: sales team as editors
|
||||
$salesTeam = $this->userModel
|
||||
->select('user_profiles.email')
|
||||
->join('user_teams', 'user_profiles.id = user_teams.user_id')
|
||||
->where('user_teams.team_id', 5)
|
||||
->where('user_teams.is_active', 1)
|
||||
->where('user_profiles.is_active', 1)
|
||||
->findAll();
|
||||
$editorEmails = array_values(array_filter(array_unique(array_column($salesTeam, 'email'))));
|
||||
|
||||
// Decode rfq_qcr_viewers from lead and add as viewers
|
||||
$viewerEmails = [];
|
||||
if (! empty($lead['rfq_qcr_viewers'])) {
|
||||
$decoded = json_decode($lead['rfq_qcr_viewers'], true);
|
||||
if (is_array($decoded)) {
|
||||
$viewerEmails = array_values(array_filter(array_unique($decoded)));
|
||||
}
|
||||
}
|
||||
|
||||
if (! empty($editorEmails) || ! empty($viewerEmails)) {
|
||||
try {
|
||||
$sheetLib->applyPermissions($newSheetId, [
|
||||
'editors' => $editorEmails,
|
||||
'viewers' => $viewerEmails,
|
||||
]);
|
||||
$this->myLogger->logme('error', "QCR Sheet: permissions applied | sheet_id={$newSheetId}, editors=" . count($editorEmails) . ", viewers=" . count($viewerEmails));
|
||||
} catch (\Throwable $e) {
|
||||
$this->myLogger->logme('error', "QCR Sheet: failed to apply permissions | sheet_id={$newSheetId}, error=" . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// No protections applied for QCR as of now
|
||||
|
||||
// Save qcr_sheet_id to leads.misc and update status
|
||||
$leadMisc['qcr_sheet_id'] = $newSheetId;
|
||||
$this->leadsModel->update($leadId, [
|
||||
'misc' => json_encode($leadMisc),
|
||||
'status' => 'qcr_created',
|
||||
]);
|
||||
|
||||
$sheetUrl = $sheetLib->sheetUrl($newSheetId);
|
||||
|
||||
$this->myLogger->logme('error', "QCR Sheet: created and stored | lead_id={$leadId}, sheet_id={$newSheetId}");
|
||||
|
||||
return $this->respond(
|
||||
[
|
||||
'status' => 'success',
|
||||
'code' => 200,
|
||||
'sheet_id' => $newSheetId,
|
||||
'sheet_url' => $sheetUrl,
|
||||
'message' => 'QCR sheet created successfully',
|
||||
],
|
||||
200
|
||||
);
|
||||
} catch (\Throwable $e) {
|
||||
$errorDetails = [
|
||||
'message' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
];
|
||||
|
||||
$this->myLogger->logme('error', 'QCR Sheet: exception occurred | ' . json_encode($errorDetails));
|
||||
|
||||
return $this->respond(
|
||||
['status' => 'error', 'code' => 500, 'message' => 'Failed to create QCR sheet'],
|
||||
500
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Download RFQ/QCR Excel file from Google Sheet for Non-EB leads.
|
||||
*
|
||||
* Expects $lead_data to contain misc JSON with rfq_sheet_id or qcr_sheet_id.
|
||||
* Returns an array compatible with constructNonEbExcelToSaveTemp:
|
||||
* [
|
||||
* 'filePath' => string,
|
||||
* 'fileName' => string,
|
||||
* ]
|
||||
* Sheet selection based on recipient_type:
|
||||
* - 'insurer' → rfq_sheet_id
|
||||
* - 'client' → qcr_sheet_id
|
||||
* - 'internal' → based on lead status: qcr_created/qcr_sent → qcr_sheet_id, else rfq_sheet_id
|
||||
*
|
||||
* @param string $recipientType insurer|client|internal
|
||||
*/
|
||||
protected function downloadFileFromGoogleSheet(array $lead_data): array
|
||||
protected function downloadFileFromGoogleSheet(array $lead_data, string $recipientType = 'insurer'): array
|
||||
{
|
||||
try {
|
||||
$leadId = (int) ($lead_data['id'] ?? 0);
|
||||
@ -5032,17 +5481,31 @@ class LeadsController extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
// Prefer RFQ sheet id, fallback to QCR if needed
|
||||
$sheetId = $misc['rfq_sheet_id'] ?? ($misc['qcr_sheet_id'] ?? null);
|
||||
// Pick sheet id based on recipient type
|
||||
$sheetId = null;
|
||||
$sheetType = '';
|
||||
|
||||
if ($recipientType === 'client') {
|
||||
$sheetId = $misc['qcr_sheet_id'] ?? null;
|
||||
$sheetType = 'qcr_sheet_id';
|
||||
} elseif ($recipientType === 'internal') {
|
||||
$leadStatus = $lead_data['status'] ?? '';
|
||||
if (in_array($leadStatus, ['qcr_created', 'qcr_sent'])) {
|
||||
$sheetId = $misc['qcr_sheet_id'] ?? null;
|
||||
$sheetType = 'qcr_sheet_id';
|
||||
} else {
|
||||
$sheetId = $misc['rfq_sheet_id'] ?? null;
|
||||
$sheetType = 'rfq_sheet_id';
|
||||
}
|
||||
} else {
|
||||
// insurer or default
|
||||
$sheetId = $misc['rfq_sheet_id'] ?? null;
|
||||
$sheetType = 'rfq_sheet_id';
|
||||
}
|
||||
|
||||
if (empty($sheetId)) {
|
||||
$this->myLogger->logme('error', "GSHEET DOWNLOAD: sheet id missing in misc | lead_id={$leadId}");
|
||||
// Fallback: behave like old flow and construct Non-EB Excel locally
|
||||
// $file_info = $this->constructNonEbExcelToSaveTemp($leadId, 1, null);
|
||||
// return [
|
||||
// 'filePath' => $file_info['filePath'],
|
||||
// 'fileName' => $file_info['fileName'],
|
||||
// ];
|
||||
$this->myLogger->logme('error', "GSHEET DOWNLOAD: {$sheetType} missing in leads.misc | lead_id={$leadId}, recipient_type={$recipientType}");
|
||||
throw new \RuntimeException("{$sheetType} not found in leads. Please create the sheet first.");
|
||||
}
|
||||
|
||||
$this->myLogger->logme('error', "GSHEET DOWNLOAD: starting download | lead_id={$leadId}, sheet_id={$sheetId}");
|
||||
@ -5101,6 +5564,143 @@ class LeadsController extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a placement Google Sheet by copying the QCR sheet (Non-EB only).
|
||||
*
|
||||
* - Reads qcr_sheet_id from leads.misc
|
||||
* - If placement_sheet_id already exists, reuses it
|
||||
* - Copies the QCR sheet, replacing "QCR" with "Placement" in the filename
|
||||
* - Applies same editor/viewer permissions as QCR
|
||||
* - Saves placement_sheet_id to leads.misc
|
||||
* - Downloads the placement sheet as Excel and returns file info
|
||||
*/
|
||||
protected function createAndDownloadPlacementSheet(array $lead_data): array
|
||||
{
|
||||
$leadId = (int) ($lead_data['id'] ?? 0);
|
||||
$sheetLib = new GoogleSheetLib();
|
||||
|
||||
// Decode leads.misc
|
||||
$leadMisc = [];
|
||||
if (! empty($lead_data['misc'])) {
|
||||
$decoded = json_decode($lead_data['misc'], true);
|
||||
if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
|
||||
$leadMisc = $decoded;
|
||||
}
|
||||
}
|
||||
|
||||
// If placement sheet already exists, download and return it
|
||||
if (! empty($leadMisc['placement_sheet_id'])) {
|
||||
$placementSheetId = (string) $leadMisc['placement_sheet_id'];
|
||||
$this->myLogger->logme('error', "Placement Sheet: existing sheet found | lead_id={$leadId}, sheet_id={$placementSheetId}");
|
||||
return $this->downloadPlacementExcel($sheetLib, $placementSheetId, $leadId);
|
||||
}
|
||||
|
||||
// QCR sheet must exist to create placement
|
||||
$qcrSheetId = $leadMisc['qcr_sheet_id'] ?? null;
|
||||
if (empty($qcrSheetId)) {
|
||||
$this->myLogger->logme('error', "Placement Sheet: qcr_sheet_id missing in leads.misc | lead_id={$leadId}");
|
||||
throw new \RuntimeException('QCR sheet not found. Please create the QCR sheet first.');
|
||||
}
|
||||
|
||||
// Get QCR filename and replace "QCR" with "Placement"
|
||||
$qcrFileName = $sheetLib->getFileName($qcrSheetId);
|
||||
$placementFileName = str_ireplace('QCR', 'Placement', $qcrFileName);
|
||||
$this->myLogger->logme('error', "Placement Sheet: renaming | qcr_name={$qcrFileName}, placement_name={$placementFileName}");
|
||||
|
||||
// Load folder ID from config
|
||||
$parentFolderId = null;
|
||||
try {
|
||||
$rfqConfig = config('RfqConfig');
|
||||
if ($rfqConfig) {
|
||||
$parentFolderId = $rfqConfig->rfqParentFolderId ?? null;
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$this->myLogger->logme('error', 'Placement Sheet: error loading RfqConfig | ' . $e->getMessage());
|
||||
}
|
||||
|
||||
if (! $parentFolderId) {
|
||||
throw new \RuntimeException('Google config not correct, contact administrator');
|
||||
}
|
||||
|
||||
// Copy QCR sheet as placement
|
||||
$placementSheetId = $sheetLib->copyTemplate($qcrSheetId, $placementFileName, $parentFolderId);
|
||||
$this->myLogger->logme('error', "Placement Sheet: copied from QCR | lead_id={$leadId}, qcr_sheet_id={$qcrSheetId}, placement_sheet_id={$placementSheetId}");
|
||||
|
||||
// Apply same permissions as QCR (editors: sales team, viewers: rfq_qcr_viewers)
|
||||
$salesTeam = $this->userModel
|
||||
->select('user_profiles.email')
|
||||
->join('user_teams', 'user_profiles.id = user_teams.user_id')
|
||||
->where('user_teams.team_id', 5)
|
||||
->where('user_teams.is_active', 1)
|
||||
->where('user_profiles.is_active', 1)
|
||||
->findAll();
|
||||
$editorEmails = array_values(array_filter(array_unique(array_column($salesTeam, 'email'))));
|
||||
|
||||
$viewerEmails = [];
|
||||
if (! empty($lead_data['rfq_qcr_viewers'])) {
|
||||
$decoded = json_decode($lead_data['rfq_qcr_viewers'], true);
|
||||
if (is_array($decoded)) {
|
||||
$viewerEmails = array_values(array_filter(array_unique($decoded)));
|
||||
}
|
||||
}
|
||||
|
||||
if (! empty($editorEmails) || ! empty($viewerEmails)) {
|
||||
try {
|
||||
$sheetLib->applyPermissions($placementSheetId, [
|
||||
'editors' => $editorEmails,
|
||||
'viewers' => $viewerEmails,
|
||||
]);
|
||||
$this->myLogger->logme('error', "Placement Sheet: permissions applied | sheet_id={$placementSheetId}, editors=" . count($editorEmails) . ", viewers=" . count($viewerEmails));
|
||||
} catch (\Throwable $e) {
|
||||
$this->myLogger->logme('error', "Placement Sheet: failed to apply permissions | sheet_id={$placementSheetId}, error=" . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Save placement_sheet_id to leads.misc
|
||||
$leadMisc['placement_sheet_id'] = $placementSheetId;
|
||||
$this->leadsModel->update($leadId, [
|
||||
'misc' => json_encode($leadMisc),
|
||||
]);
|
||||
$this->myLogger->logme('error', "Placement Sheet: saved to leads.misc | lead_id={$leadId}, placement_sheet_id={$placementSheetId}");
|
||||
|
||||
return $this->downloadPlacementExcel($sheetLib, $placementSheetId, $leadId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Download a placement Google Sheet as Excel to tmp directory.
|
||||
*/
|
||||
private function downloadPlacementExcel(GoogleSheetLib $sheetLib, string $sheetId, int $leadId): array
|
||||
{
|
||||
$binary = $sheetLib->downloadExcel($sheetId);
|
||||
|
||||
if (! $binary) {
|
||||
$this->myLogger->logme('error', "Placement Sheet: empty content from Google | lead_id={$leadId}, sheet_id={$sheetId}");
|
||||
throw new \RuntimeException('Empty content from Google Sheet for placement');
|
||||
}
|
||||
|
||||
$filename = "lead_{$leadId}_placement_" . date('Ymd_His') . '.xlsx';
|
||||
$uploadDir = WRITEPATH . 'tmp/';
|
||||
$uploadFilePath = $uploadDir . $filename;
|
||||
|
||||
if (! is_dir($uploadDir)) {
|
||||
if (! mkdir($uploadDir, 0777, true) && ! is_dir($uploadDir)) {
|
||||
throw new \RuntimeException('Failed to create temporary directory');
|
||||
}
|
||||
}
|
||||
|
||||
$bytes = file_put_contents($uploadFilePath, $binary);
|
||||
if ($bytes === false) {
|
||||
throw new \RuntimeException('Failed to write placement Excel file');
|
||||
}
|
||||
|
||||
$this->myLogger->logme('error', "Placement Sheet: downloaded | lead_id={$leadId}, path={$uploadFilePath}");
|
||||
|
||||
return [
|
||||
'filePath' => $uploadFilePath,
|
||||
'fileName' => $filename,
|
||||
];
|
||||
}
|
||||
|
||||
public function generateViewPageHtml($policy_type_id, $data = [])
|
||||
{
|
||||
$data['insurer'] = $this->insurerBranchModel->getInsurerBranchesWithInsurerNames();
|
||||
|
||||
@ -573,78 +573,89 @@ class MediAssistApiController extends BaseController
|
||||
}
|
||||
|
||||
// Extract Claim Status
|
||||
$claimData = $response['data']['claimsData'][0];
|
||||
$currentStatus = $claimData['claim_Current_Status'] ?? '';
|
||||
$tpa_claim_no = $claimData['tpA_CLAIM_NO'] ?? '';
|
||||
$tpa_claim_type = $claimData['typE_OF_CLAIM'] ?? '';
|
||||
$tpa_ailments = ($claimData['ailment'] ?? '') . ' - ' . ($claimData['ailmenT_DESC'] ?? '');
|
||||
$allClaimData = $response['data']['claimsData'];
|
||||
|
||||
// VALID STATUS LIST
|
||||
$validStatuses = [
|
||||
"Claim Received" => 1,
|
||||
"In Progress" => 5,
|
||||
"Processed" => 11,
|
||||
"Claim Paid" => 11,
|
||||
"Denied" => 13,
|
||||
"Cancelled" => 13,
|
||||
|
||||
"Information Awaited" => 4,
|
||||
"Confirmation Awaited" => 4,
|
||||
"Information Awaited Reminder" => 4,
|
||||
"Information Awaited Final Reminder" => 4,
|
||||
"Insurer Concurrence Awaited" => 6,
|
||||
"Closed" => 12,
|
||||
|
||||
"Physical Documents Awaited" => 9,
|
||||
"Processed - Payment Initiated" => 10,
|
||||
"Processed - Transaction Failed" => 10,
|
||||
"Processed - Account Details Updated" => 10,
|
||||
"Processed - Debit Note Raised With Insurer for Payment"=> 10,
|
||||
"Processed - Payment Initiated by Insurer" => 10,
|
||||
"Payment - Refunded to Insurer" => 14,
|
||||
"Processed - Processing Payment" => 10,
|
||||
"Processed - Physical Documents Awaited" => 9,
|
||||
|
||||
// Extra Mappings (based on your DB list)
|
||||
"NON ID" => 1,
|
||||
"ID NOT GENERATED" => 2,
|
||||
"CDA" => 3,
|
||||
"REJECTED" => 8,
|
||||
"APPROVED" => 9,
|
||||
"PAYMENT INITIATED" => 10,
|
||||
"SETTLED" => 11,
|
||||
"RETURNED" => 14,
|
||||
"UNDER PROCESS - TPA" => 61,
|
||||
"DENIAL REVIEW AWAITED" => 66,
|
||||
];
|
||||
$currentStatus = "";
|
||||
foreach ($allClaimData as $claimData) {
|
||||
|
||||
$updateArray = [
|
||||
'tpa_claim_status' => $currentStatus,
|
||||
'updated_at' => date('Y-m-d H:i:s'),
|
||||
];
|
||||
$currentStatus = $claimData['claim_Current_Status'] ?? '';
|
||||
$tpa_claim_no = $claimData['tpA_CLAIM_NO'] ?? '';
|
||||
$tpa_claim_type = $claimData['typE_OF_CLAIM'] ?? '';
|
||||
$tpa_ailments = ($claimData['ailment'] ?? '') . ' - ' . ($claimData['ailmenT_DESC'] ?? '');
|
||||
|
||||
if (isset($validStatuses[$currentStatus])) {
|
||||
$updateArray['claim_status_id'] = $validStatuses[$currentStatus];
|
||||
// VALID STATUS LIST
|
||||
$validStatuses = [
|
||||
"Claim Received" => 1,
|
||||
"In Progress" => 5,
|
||||
"Processed" => 11,
|
||||
"Claim Paid" => 11,
|
||||
"Denied" => 13,
|
||||
"Cancelled" => 13,
|
||||
|
||||
"Information Awaited" => 4,
|
||||
"Confirmation Awaited" => 4,
|
||||
"Information Awaited Reminder" => 4,
|
||||
"Information Awaited Final Reminder" => 4,
|
||||
"Insurer Concurrence Awaited" => 6,
|
||||
"Closed" => 12,
|
||||
|
||||
"Physical Documents Awaited" => 9,
|
||||
"Processed - Payment Initiated" => 10,
|
||||
"Processed - Transaction Failed" => 10,
|
||||
"Processed - Account Details Updated" => 10,
|
||||
"Processed - Debit Note Raised With Insurer for Payment"=> 10,
|
||||
"Processed - Payment Initiated by Insurer" => 10,
|
||||
"Payment - Refunded to Insurer" => 14,
|
||||
"Processed - Processing Payment" => 10,
|
||||
"Processed - Physical Documents Awaited" => 9,
|
||||
|
||||
// Extra Mappings (based on your DB list)
|
||||
"NON ID" => 1,
|
||||
"ID NOT GENERATED" => 2,
|
||||
"CDA" => 3,
|
||||
"REJECTED" => 8,
|
||||
"APPROVED" => 9,
|
||||
"PAYMENT INITIATED" => 10,
|
||||
"SETTLED" => 11,
|
||||
"RETURNED" => 14,
|
||||
"UNDER PROCESS - TPA" => 61,
|
||||
"DENIAL REVIEW AWAITED" => 66,
|
||||
];
|
||||
|
||||
$updateArray = [
|
||||
'tpa_claim_status' => $currentStatus,
|
||||
'updated_at' => date('Y-m-d H:i:s'),
|
||||
'last_updated_by' => 'API',
|
||||
];
|
||||
|
||||
if (isset($validStatuses[$currentStatus])) {
|
||||
$updateArray['claim_status_id'] = $validStatuses[$currentStatus];
|
||||
}
|
||||
if (empty($ticket['tpa_claim_id']) || $ticket['tpa_claim_id'] === null) {
|
||||
$updateArray['tpa_claim_id'] = $tpa_claim_no;
|
||||
}
|
||||
if (empty($ticket['claim_number']) || $ticket['claim_number'] === null) {
|
||||
$updateArray['claim_number'] = $tpa_claim_no;
|
||||
}
|
||||
if (!empty($tpa_claim_type)) {
|
||||
$updateArray['tpa_claim_type'] = $tpa_claim_type;
|
||||
}
|
||||
if (!empty($tpa_ailments)) {
|
||||
$updateArray['tpa_ailments'] = $tpa_ailments;
|
||||
}
|
||||
|
||||
|
||||
if($ticket['doa'] == $this->mediDate($claimData['datE_OF_ADMISSION'] ?? null) || $ticket['claim_number'] == $tpa_claim_no){
|
||||
// UPDATE ticket_master
|
||||
$this->db->table('ticket_master')->where('id', $claimId)->update($updateArray);
|
||||
|
||||
// LOG UPDATE
|
||||
log_message('error',"MEDI_ASSIST - Claim Status SUCCESS | Updated ticket ID $claimId with Claim Status: $currentStatus");
|
||||
}else{
|
||||
log_message('error', "MEDI_ASSIST | Fetch Claim Status | Claim Status NOT UPDATED | TicketID={$claimId} | TicketDOA={$ticket['doa']} | ClaimDOA={$claimData['datE_OF_ADMISSION']} | Status={$currentStatus}");
|
||||
}
|
||||
|
||||
}
|
||||
if (empty($ticket['tpa_claim_id']) || $ticket['tpa_claim_id'] === null) {
|
||||
$updateArray['tpa_claim_id'] = $tpa_claim_no;
|
||||
}
|
||||
if (empty($ticket['claim_number']) || $ticket['claim_number'] === null) {
|
||||
$updateArray['claim_number'] = $tpa_claim_no;
|
||||
}
|
||||
if (!empty($tpa_claim_type)) {
|
||||
$updateArray['tpa_claim_type'] = $tpa_claim_type;
|
||||
}
|
||||
if (!empty($tpa_ailments)) {
|
||||
$updateArray['tpa_ailments'] = $tpa_ailments;
|
||||
}
|
||||
|
||||
|
||||
// UPDATE ticket_master
|
||||
$this->db->table('ticket_master')->where('id', $claimId)->update($updateArray);
|
||||
|
||||
// LOG UPDATE
|
||||
log_message('error',"MEDI_ASSIST - Claim Status SUCCESS | Updated ticket ID $claimId with Claim Status: $currentStatus");
|
||||
|
||||
return ['status' => true,'message' => 'Claim Status updated.','updated_status' => $currentStatus,'api_response' => $response];
|
||||
}
|
||||
@ -796,6 +807,10 @@ class MediAssistApiController extends BaseController
|
||||
'gender' => strtoupper($row['benefSex'] ?? null),
|
||||
'self' => strtolower($row['relName'] ?? '') === 'self' ? 1 : 0,
|
||||
|
||||
'si' => $row['sum_insured'] ?? null,
|
||||
'doj' => $this->mediDate($row['benefWEF'] ?? null),
|
||||
|
||||
|
||||
'tpa_id' => trim($row['benefMediAssistID'] ?? null),
|
||||
'age' => is_numeric($row['benefAge'] ?? null)
|
||||
? (int) $row['benefAge']
|
||||
@ -890,6 +905,9 @@ class MediAssistApiController extends BaseController
|
||||
// CALL API
|
||||
$response = call_third_party_api($url, $method, $headers, $body);
|
||||
|
||||
log_message('error','MEDI_ASSIST - 2 hours Claim Status API Response: ' . json_encode($response));
|
||||
|
||||
|
||||
if ($response['status'] != true || empty($response['data']['claimsData'][0])) {
|
||||
log_message('error','MEDI_ASSIST - Claim Status FAILED | for ticket ID: ' . $claimId.' | response: '.json_encode($response));
|
||||
$error_data[$claimId] = [
|
||||
@ -900,77 +918,94 @@ class MediAssistApiController extends BaseController
|
||||
}
|
||||
|
||||
// Extract Claim Status
|
||||
$claimData = $response['data']['claimsData'][0];
|
||||
$currentStatus = $claimData['claim_Current_Status'] ?? '';
|
||||
$tpa_claim_no = $claimData['tpA_CLAIM_NO'] ?? '';
|
||||
$tpa_claim_type = $claimData['typE_OF_CLAIM'] ?? '';
|
||||
$tpa_ailments = ($claimData['ailment'] ?? '') . ' - ' . ($claimData['ailmenT_DESC'] ?? '');
|
||||
// $allClaimData = $response['data']['claimsData'][0];
|
||||
$allClaimData = $response['data']['claimsData'];
|
||||
|
||||
// VALID STATUS LIST
|
||||
$validStatuses = [
|
||||
|
||||
"Claim Received" => 1,
|
||||
"In Progress" => 5,
|
||||
"Processed" => 11,
|
||||
"Claim Paid" => 11,
|
||||
"Denied" => 13,
|
||||
"Cancelled" => 13,
|
||||
|
||||
"Information Awaited" => 4,
|
||||
"Confirmation Awaited" => 4,
|
||||
"Information Awaited Reminder" => 4,
|
||||
"Information Awaited Final Reminder" => 4,
|
||||
"Insurer Concurrence Awaited" => 6,
|
||||
"Closed" => 12,
|
||||
|
||||
"Physical Documents Awaited" => 9,
|
||||
"Processed - Payment Initiated" => 10,
|
||||
"Processed - Transaction Failed" => 10,
|
||||
"Processed - Account Details Updated" => 10,
|
||||
"Processed - Debit Note Raised With Insurer for Payment"=> 10,
|
||||
"Processed - Payment Initiated by Insurer" => 10,
|
||||
"Payment - Refunded to Insurer" => 14,
|
||||
"Processed - Processing Payment" => 10,
|
||||
"Processed - Physical Documents Awaited" => 9,
|
||||
|
||||
// Extra Mappings (based on your DB list)
|
||||
"NON ID" => 1,
|
||||
"ID NOT GENERATED" => 2,
|
||||
"CDA" => 3,
|
||||
"REJECTED" => 8,
|
||||
"APPROVED" => 9,
|
||||
"PAYMENT INITIATED" => 10,
|
||||
"SETTLED" => 11,
|
||||
"RETURNED" => 14,
|
||||
"UNDER PROCESS - TPA" => 61,
|
||||
"DENIAL REVIEW AWAITED" => 66,
|
||||
];
|
||||
foreach ($allClaimData as $claimData) {
|
||||
|
||||
$currentStatus = $claimData['claim_Current_Status'] ?? '';
|
||||
$tpa_claim_no = $claimData['tpA_CLAIM_NO'] ?? '';
|
||||
$tpa_claim_type = $claimData['typE_OF_CLAIM'] ?? '';
|
||||
$tpa_ailments = ($claimData['ailment'] ?? '') . ' - ' . ($claimData['ailmenT_DESC'] ?? '');
|
||||
|
||||
$updateArray = [
|
||||
'tpa_claim_status' => $currentStatus,
|
||||
'tpa_claim_id' => $tpa_claim_no,
|
||||
'claim_number' => $tpa_claim_no,
|
||||
'updated_at' => date('Y-m-d H:i:s'),
|
||||
];
|
||||
if (isset($validStatuses[$currentStatus])) {
|
||||
$updateArray['claim_status_id'] = $validStatuses[$currentStatus];
|
||||
}
|
||||
if (!empty($tpa_claim_type)) {
|
||||
$updateArray['tpa_claim_type'] = $tpa_claim_type;
|
||||
}
|
||||
if (!empty($tpa_ailments)) {
|
||||
$updateArray['tpa_ailments'] = $tpa_ailments;
|
||||
}
|
||||
// VALID STATUS LIST
|
||||
$validStatuses = [
|
||||
|
||||
// UPDATE ticket_master
|
||||
$this->db->table('ticket_master')->where('id', $claimId)->update($updateArray);
|
||||
$status_updated_count ++;
|
||||
|
||||
// LOG UPDATE
|
||||
log_message('error',"MEDI_ASSIST - Claim Status SUCCESS | Updated ticket ID $claimId with Claim Status: $currentStatus");
|
||||
"Claim Received" => 1,
|
||||
"In Progress" => 5,
|
||||
"Processed" => 11,
|
||||
"Claim Paid" => 11,
|
||||
"Denied" => 13,
|
||||
"Cancelled" => 13,
|
||||
|
||||
"Information Awaited" => 4,
|
||||
"Confirmation Awaited" => 4,
|
||||
"Information Awaited Reminder" => 4,
|
||||
"Information Awaited Final Reminder" => 4,
|
||||
"Insurer Concurrence Awaited" => 6,
|
||||
"Closed" => 12,
|
||||
|
||||
"Physical Documents Awaited" => 9,
|
||||
"Processed - Payment Initiated" => 10,
|
||||
"Processed - Transaction Failed" => 10,
|
||||
"Processed - Account Details Updated" => 10,
|
||||
"Processed - Debit Note Raised With Insurer for Payment"=> 10,
|
||||
"Processed - Payment Initiated by Insurer" => 10,
|
||||
"Payment - Refunded to Insurer" => 14,
|
||||
"Processed - Processing Payment" => 10,
|
||||
"Processed - Physical Documents Awaited" => 9,
|
||||
|
||||
// Extra Mappings (based on your DB list)
|
||||
"NON ID" => 1,
|
||||
"ID NOT GENERATED" => 2,
|
||||
"CDA" => 3,
|
||||
"REJECTED" => 8,
|
||||
"APPROVED" => 9,
|
||||
"PAYMENT INITIATED" => 10,
|
||||
"SETTLED" => 11,
|
||||
"RETURNED" => 14,
|
||||
"UNDER PROCESS - TPA" => 61,
|
||||
"DENIAL REVIEW AWAITED" => 66,
|
||||
];
|
||||
|
||||
$updateArray = [
|
||||
'tpa_claim_status' => $currentStatus,
|
||||
'updated_at' => date('Y-m-d H:i:s'),
|
||||
'last_updated_by' => 'API',
|
||||
];
|
||||
|
||||
if (isset($validStatuses[$currentStatus])) {
|
||||
$updateArray['claim_status_id'] = $validStatuses[$currentStatus];
|
||||
}
|
||||
if (empty($ticket['tpa_claim_id']) || $ticket['tpa_claim_id'] === null) {
|
||||
$updateArray['tpa_claim_id'] = $tpa_claim_no;
|
||||
}
|
||||
if (empty($ticket['claim_number']) || $ticket['claim_number'] === null) {
|
||||
$updateArray['claim_number'] = $tpa_claim_no;
|
||||
}
|
||||
if (!empty($tpa_claim_type)) {
|
||||
$updateArray['tpa_claim_type'] = $tpa_claim_type;
|
||||
}
|
||||
if (!empty($tpa_ailments)) {
|
||||
$updateArray['tpa_ailments'] = $tpa_ailments;
|
||||
}
|
||||
|
||||
if($ticket['doa'] == $this->mediDate($claimData['datE_OF_ADMISSION'] ?? null) || $ticket['claim_number'] == $tpa_claim_no){
|
||||
// UPDATE ticket_master
|
||||
$this->db->table('ticket_master')->where('id', $claimId)->update($updateArray);
|
||||
$status_updated_count ++;
|
||||
|
||||
// LOG UPDATE
|
||||
log_message('error',"MEDI_ASSIST - Claim Status SUCCESS | Updated ticket ID $claimId with Claim Status: $currentStatus");
|
||||
}else{
|
||||
log_message('error', "MEDI_ASSIST | 2 hours | Claim Status NOT UPDATED | TicketID={$claimId} | TicketDOA={$ticket['doa']} | ClaimDOA={$claimData['datE_OF_ADMISSION']} | Status={$currentStatus}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log_message('error',"MEDI_ASSIST - Claim Status ENDED | 2 hours | Total Tickets={".count($TicketData)."} | Status Updated={$status_updated_count} | Errors={".count($error_data)."}");
|
||||
|
||||
return $this->response->setJSON([
|
||||
'status' => true,
|
||||
'message' => 'Claim Status updated.',
|
||||
@ -1314,7 +1349,72 @@ class MediAssistApiController extends BaseController
|
||||
}
|
||||
|
||||
|
||||
public function getWellnessSSORedirectUrl($planId, $emp_code)
|
||||
{
|
||||
log_message('error', 'MEDI_ASSIST - Wellness SSO URL generation | Plan ID: ' . $planId . ' | Employee code: ' . $emp_code);
|
||||
if (empty($planId) || empty($emp_code)) {
|
||||
log_message('error', 'MEDI_ASSIST - Wellness SSO URL generation | Plan ID and employee code are required.');
|
||||
return [
|
||||
'status' => 'failed',
|
||||
'message' => 'Coming soon........!',
|
||||
];
|
||||
}
|
||||
|
||||
// Configuration – prefer environment variables, fall back to demo values
|
||||
$keyString = env('MEDIASSIST_WELLNESS_KEY');
|
||||
$ivString = env('MEDIASSIST_WELLNESS_IV');
|
||||
$loginUrlTemplate = env('MEDIASSIST_WELLNESS_LOGIN_URL');
|
||||
$cipher_algorithm = 'AES-256-CBC';
|
||||
|
||||
if (empty($keyString) || empty($ivString) || empty($loginUrlTemplate)) {
|
||||
log_message('error', 'MEDI_ASSIST - Wellness SSO URL generation | Key string, IV string and login URL are required.');
|
||||
return [
|
||||
'status' => 'failed',
|
||||
'message' => 'Key string, IV string and login URL are required.',
|
||||
];
|
||||
}
|
||||
|
||||
// Plain SSO JSON payload, as per Medi Assist sample
|
||||
$payload = [
|
||||
'Id' => $emp_code,
|
||||
'expiryTime' => time() + (10 * 60), // 10 minutes
|
||||
'CPartnerId' => $planId,
|
||||
];
|
||||
|
||||
$plainJson = json_encode($payload, JSON_UNESCAPED_SLASHES);
|
||||
|
||||
// Derive a 32‑byte key (AES‑256) and 16‑byte IV from the provided strings
|
||||
// $key = substr(hash('sha256', $keyString, true), 0, 32);
|
||||
// $iv = substr(hash('md5', $ivString, true), 0, 16);
|
||||
|
||||
// Encrypt with AES‑256‑CBC + PKCS7 padding (OpenSSL default)
|
||||
$cipherTextRaw = openssl_encrypt($plainJson, $cipher_algorithm, $keyString, OPENSSL_RAW_DATA, $ivString);
|
||||
|
||||
|
||||
if ($cipherTextRaw === false) {
|
||||
log_message('error', 'MEDI_ASSIST - Wellness SSO URL generation | Encryption failed while generating Medi Assist wellness token.');
|
||||
return [
|
||||
'status' => 'failed',
|
||||
'message' => 'Encryption failed while generating Medi Assist wellness token.',
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
// Base64 encode and URL‑encode for use as EncryptedSSO
|
||||
$encryptedSSO = urlencode(base64_encode($cipherTextRaw));
|
||||
// $encryptedSSO = rtrim(strtr(base64_encode($cipherTextRaw), '+/', '-_'), '=');
|
||||
|
||||
// Build the final login URL
|
||||
$loginUrl = str_replace(['{0}', '{1}'], [$planId, $encryptedSSO], $loginUrlTemplate);
|
||||
|
||||
log_message('error', 'MEDI_ASSIST - Wellness SSO URL generation | Medi Assist wellness SSO URL generated successfully.');
|
||||
|
||||
return [
|
||||
'status' => 'success',
|
||||
'message' => 'Medi Assist wellness SSO URL generated successfully.',
|
||||
'data' => $loginUrl
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@ -549,6 +549,8 @@ class PolicyTransactionController extends BaseController
|
||||
public function viewInception()
|
||||
{
|
||||
$bds_edit_pt_id = $this->request->getGet('pt_id') ?? null;
|
||||
$view = $this->request->getGet('view') ?? null;
|
||||
|
||||
$data['tab_name'] = 'Policies';
|
||||
$data['page_name'] = 'Policies';
|
||||
|
||||
@ -618,7 +620,7 @@ class PolicyTransactionController extends BaseController
|
||||
$issuer = empty($issuer) ? 0 : $issuer;
|
||||
$status = empty($status) ? 0 : $status; // Corrected from `$issuer`
|
||||
|
||||
if(empty($bds_edit_pt_id)){
|
||||
if(empty($bds_edit_pt_id) && empty($view)){
|
||||
if ($this->request->is('get')) {
|
||||
|
||||
// Fetch inception data list
|
||||
@ -925,7 +927,6 @@ class PolicyTransactionController extends BaseController
|
||||
// ==========================================
|
||||
'client_type' => ['label' => 'Client Type', 'rules' => 'required', 'errors' => ['required' => 'Client Type must be selected.']],
|
||||
'client_id' => ['label' => 'Client', 'rules' => 'required', 'errors' => ['required' => 'Please select a Client.']],
|
||||
'client_branch_id' => ['label' => 'Client Branch', 'rules' => 'required', 'errors' => ['required' => 'Client branch is required']],
|
||||
'issue_type' => ['label' => 'Business Type', 'rules' => 'required', 'errors' => ['required' => 'Please select a Business Type.']],
|
||||
'ref' => ['label' => 'Reference', 'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9 _-]+$/]', 'errors' => [
|
||||
'regex_match' => 'The {field} can only contain letters, numbers, spaces, dashes, and underscores.'
|
||||
@ -997,14 +998,14 @@ class PolicyTransactionController extends BaseController
|
||||
'pos_id' => ['label' => 'POS', 'rules' => 'permit_empty', 'errors' => []],
|
||||
'doc_name.*' => [
|
||||
'label' => 'Policy Document Name',
|
||||
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9 _-]+$/]|min_length[2]|max_length[100]',
|
||||
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9 \/_-]+$/]|min_length[2]|max_length[100]',
|
||||
'errors' => [
|
||||
'regex_match' => 'The {field} can only contain letters, numbers, spaces, dashes, and underscores.'
|
||||
]
|
||||
],
|
||||
'other_docs_name.*' => [
|
||||
'label' => 'Vehicle Document Name',
|
||||
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9 _-]+$/]|min_length[2]|max_length[100]',
|
||||
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9 \/_-]+$/]|min_length[2]|max_length[100]',
|
||||
'errors' => [
|
||||
'regex_match' => 'The {field} can only contain letters, numbers, spaces, dashes, and underscores.'
|
||||
]
|
||||
@ -1052,6 +1053,9 @@ class PolicyTransactionController extends BaseController
|
||||
'exp_amt.*' => ['label' => 'Expected Amount', 'rules' => 'permit_empty|decimal', 'errors' => ['decimal' => 'The Expected Amount field must contain a valid number.']]
|
||||
];
|
||||
|
||||
if(isset($post_data['client_type']) && $post_data['client_type'] == 1){
|
||||
$rules['client_branch_id'] = ['label' => 'Client Branch', 'rules' => 'required', 'errors' => ['required' => 'Client branch is required']];
|
||||
}
|
||||
|
||||
$isValid = $this->validate($rules);
|
||||
|
||||
@ -2668,7 +2672,7 @@ class PolicyTransactionController extends BaseController
|
||||
'co_share_id.*' => ['label' => 'Record ID', 'rules' => 'permit_empty','errors' => []],
|
||||
'doc_name.*' => [
|
||||
'label' => 'Policy Document Name',
|
||||
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9 _-]+$/]|min_length[2]|max_length[100]',
|
||||
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9 \/_-]+$/]|min_length[2]|max_length[100]',
|
||||
'errors' => [
|
||||
'regex_match' => 'The {field} can only contain letters, numbers, spaces, dashes, and underscores.'
|
||||
]
|
||||
@ -5618,17 +5622,18 @@ class PolicyTransactionController extends BaseController
|
||||
$policy_end_date = trim($row[11]);
|
||||
$revenue_type = trim($row[12]);
|
||||
|
||||
$base_premium = trim($row[16]);
|
||||
$non_commission_premium_amount = trim($row[17]);
|
||||
$tp_premium = trim($row[18]);
|
||||
$igst = trim($row[19]);
|
||||
$cgst = trim($row[20]);
|
||||
$sgst = trim($row[21]);
|
||||
$stamp_duty = trim($row[22]);
|
||||
$base_premium = cleanNumber($row[16]);
|
||||
$non_commission_premium_amount = cleanNumber($row[17]);
|
||||
$tp_premium = cleanNumber($row[18]);
|
||||
$igst = cleanNumber($row[19]);
|
||||
$cgst = cleanNumber($row[20]);
|
||||
$sgst = cleanNumber($row[21]);
|
||||
$stamp_duty = cleanNumber($row[22]);
|
||||
$agreed_amount = cleanNumber($row[23]);
|
||||
$agreed_bp_percentage = cleanNumber($row[24]);
|
||||
$agreed_tp_percentage = cleanNumber($row[25]);
|
||||
|
||||
// $total = trim($row[23]);
|
||||
$agreed_amount = trim($row[23]);
|
||||
$agreed_bp_percentage = trim($row[24]);
|
||||
$agreed_tp_percentage = trim($row[25]);
|
||||
// $actual_bp_amount = trim($row[27]);
|
||||
// $actual_tp_amount = trim($row[28]);
|
||||
// $actual_bp_percentage = trim($row[29]);
|
||||
@ -5647,16 +5652,16 @@ class PolicyTransactionController extends BaseController
|
||||
$rewards = 0;
|
||||
|
||||
$calculation = calculateMotorPolicyAmounts([
|
||||
'base_premium' => trim($row[16]),
|
||||
'non_commission_premium_amount' => trim($row[17]),
|
||||
'tp_premium' => trim($row[18]),
|
||||
'igst' => trim($row[19]),
|
||||
'cgst' => trim($row[20]),
|
||||
'sgst' => trim($row[21]),
|
||||
'stamp_duty' => trim($row[22]),
|
||||
'agreed_amount' => trim($row[23]),
|
||||
'agreed_bp_percentage' => trim($row[24]),
|
||||
'agreed_tp_percentage' => trim($row[25]),
|
||||
'base_premium' => $base_premium,
|
||||
'non_commission_premium_amount' => $non_commission_premium_amount,
|
||||
'tp_premium' => $tp_premium,
|
||||
'igst' => $igst,
|
||||
'cgst' => $cgst,
|
||||
'sgst' => $sgst,
|
||||
'stamp_duty' => $stamp_duty,
|
||||
'agreed_amount' => $agreed_amount,
|
||||
'agreed_bp_percentage' => $agreed_bp_percentage,
|
||||
'agreed_tp_percentage' => $agreed_tp_percentage,
|
||||
'standard_bp_percentage' => 15.00,
|
||||
'standard_tp_percentage' => 2.5
|
||||
]);
|
||||
|
||||
@ -1272,7 +1272,7 @@ class RestAuthenticationController extends AdminController
|
||||
log_message('error', ' ');
|
||||
log_message('error', ' ************************************* POST END **************************************** ');
|
||||
log_message('error', ' ');
|
||||
return $this->respond(['status' => 'success','code' => 200,'data' => "", 'message' => "Mpin - exist" , 'Mpin' =>$employeeData["mpin"], 'is_mpin_skipped' => $employeeData['is_mpin_skipped'] ?? 0, 'is_biometric_enabled' => $employeeData['is_biometric_enabled'] ?? 0],200);
|
||||
return $this->respond(['status' => 'success','code' => 200,'data' => "", 'message' => "Mpin - exist" , 'Mpin' =>$employeeData["mpin"], 'is_mpin_skipped' => $employeeData['is_mpin_skipped'] ?? '0', 'is_biometric_enabled' => $employeeData['is_biometric_enabled'] ?? '0'],200);
|
||||
} else {
|
||||
$this->myLogger->logme("error", "REST-AUTH-CONTROLLER - checkMpin: Mpin - not found");
|
||||
log_message('error', ' ');
|
||||
|
||||
@ -9,6 +9,7 @@ use App\Models\SalesActivityModel;
|
||||
use App\Models\SalesLeadNoteModel;
|
||||
use App\Models\SalesTargetModel;
|
||||
use App\Models\UserModel;
|
||||
use App\Models\ClientModel;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
use CodeIgniter\API\ResponseTrait;
|
||||
|
||||
@ -22,6 +23,7 @@ class SalesController extends BaseController
|
||||
protected $noteModel;
|
||||
protected $userModel;
|
||||
protected $targetModel;
|
||||
protected $clientModel;
|
||||
|
||||
|
||||
public function __construct()
|
||||
@ -32,6 +34,7 @@ class SalesController extends BaseController
|
||||
$this->noteModel = new SalesLeadNoteModel();
|
||||
$this->userModel = new UserModel();
|
||||
$this->targetModel = new SalesTargetModel();
|
||||
$this->clientModel = new ClientModel();
|
||||
}
|
||||
|
||||
|
||||
@ -125,9 +128,13 @@ class SalesController extends BaseController
|
||||
->where('nhance_branch_id', $nhance_branch_id) // same branch
|
||||
->get()->getResultArray();
|
||||
|
||||
// Add "(Head)" label to heads so dropdown is clear
|
||||
// Add "(Head) (Admin)" label
|
||||
foreach ($branch_heads as &$head) {
|
||||
$head['first_name'] = $head['first_name'] . ' (Head)';
|
||||
if ($head['role'] == 1) {
|
||||
$head['first_name'] = $head['first_name'] . ' (Admin)';
|
||||
} elseif ($head['role'] == 5) {
|
||||
$head['first_name'] = $head['first_name'] . ' (Head)';
|
||||
}
|
||||
}
|
||||
unset($head);
|
||||
|
||||
@ -155,12 +162,25 @@ class SalesController extends BaseController
|
||||
->whereIn('role', [1, 5]) // Sales Head roles
|
||||
->get()->getResultArray();
|
||||
|
||||
// Add "(Head)" label to all heads
|
||||
// Add "(Head) (Admin)" label
|
||||
foreach ($all_heads as &$head) {
|
||||
$head['first_name'] = $head['first_name'] . ' (Head)';
|
||||
if ($head['role'] == 1) {
|
||||
$head['first_name'] = $head['first_name'] . ' (Admin)';
|
||||
} elseif ($head['role'] == 5) {
|
||||
$head['first_name'] = $head['first_name'] . ' (Head)';
|
||||
}
|
||||
}
|
||||
unset($head);
|
||||
|
||||
// ================================================================
|
||||
// QUERY 5: Get ALL clients
|
||||
// ================================================================
|
||||
$all_clients = $db->table('clients')
|
||||
->select('id, client_name,short_name,email,phone')
|
||||
->where('is_active', 1)
|
||||
->get()->getResultArray();
|
||||
$data['$all_clients'] = $all_clients;
|
||||
|
||||
// ================================================================
|
||||
// BUILD: sales_manager_with_head = branch heads + branch managers
|
||||
// ================================================================
|
||||
@ -272,6 +292,38 @@ class SalesController extends BaseController
|
||||
return $this->fail('Please assign this lead to a user.');
|
||||
}
|
||||
|
||||
// ── Client logic — only runs if client_id key exists in payload ──
|
||||
if (array_key_exists('client_id', $data)) {
|
||||
|
||||
$clientId = !empty($data['client_id']) ? (int)$data['client_id'] : null;
|
||||
|
||||
$clientData = array_filter([
|
||||
'client_name' => $data['company_name'] ?? null,
|
||||
'short_name' => $data['short_name'] ?? null,
|
||||
'email' => $data['email'] ?? null,
|
||||
'phone' => $data['phone'] ?? null,
|
||||
], fn($v) => $v !== null && $v !== '');
|
||||
|
||||
if ($clientId) {
|
||||
// Existing client — update name/short_name/email/phone if provided
|
||||
if (!empty($clientData)) {
|
||||
$clientData['updated_by'] = $this->getUserId();
|
||||
$this->clientModel->update($clientId, $clientData);
|
||||
}
|
||||
} else {
|
||||
// New client — insert and get ID
|
||||
$clientData['created_by'] = $this->getUserId();
|
||||
$clientData['is_active'] = 1;
|
||||
$this->clientModel->insert($clientData);
|
||||
$clientId = $this->clientModel->getInsertID();
|
||||
}
|
||||
|
||||
$data['client_id'] = $clientId;
|
||||
}
|
||||
|
||||
// ── Clean up UI-only fields before inserting lead ────────────────
|
||||
unset($data['short_name']);
|
||||
|
||||
if (!$this->leadModel->insert($data)) {
|
||||
return $this->fail($this->leadModel->errors());
|
||||
}
|
||||
@ -351,6 +403,38 @@ class SalesController extends BaseController
|
||||
$data = $this->request->getJSON(true);
|
||||
$data['updated_by'] = $this->getUserId();
|
||||
|
||||
// ── Client logic — only runs if client_id key exists in payload ──
|
||||
if (array_key_exists('client_id', $data)) {
|
||||
|
||||
$clientId = !empty($data['client_id']) ? (int)$data['client_id'] : null;
|
||||
|
||||
$clientData = array_filter([
|
||||
'client_name' => $data['company_name'] ?? null,
|
||||
'short_name' => $data['short_name'] ?? null,
|
||||
'email' => $data['email'] ?? null,
|
||||
'phone' => $data['phone'] ?? null,
|
||||
], fn($v) => $v !== null && $v !== '');
|
||||
|
||||
if ($clientId) {
|
||||
// Existing client — update if we have data
|
||||
if (!empty($clientData)) {
|
||||
$clientData['updated_by'] = $this->getUserId();
|
||||
$this->clientModel->update($clientId, $clientData);
|
||||
}
|
||||
} else {
|
||||
// New client — insert and get ID
|
||||
$clientData['created_by'] = $this->getUserId();
|
||||
$clientData['is_active'] = 1;
|
||||
$this->clientModel->insert($clientData);
|
||||
$clientId = $this->clientModel->getInsertID();
|
||||
}
|
||||
|
||||
$data['client_id'] = $clientId;
|
||||
}
|
||||
|
||||
// ── Clean up UI-only fields before inserting lead ────────────────
|
||||
unset($data['short_name']);
|
||||
|
||||
if (!$this->leadModel->update($id, $data)) {
|
||||
return $this->fail($this->leadModel->errors(), ResponseInterface::HTTP_BAD_REQUEST);
|
||||
}
|
||||
@ -408,6 +492,87 @@ class SalesController extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /sales/searchClients?q=tech
|
||||
*/
|
||||
public function searchClients()
|
||||
{
|
||||
$q = trim($this->request->getGet('q') ?? '');
|
||||
|
||||
if (strlen($q) < 1) {
|
||||
return $this->response
|
||||
->setContentType('application/json')
|
||||
->setBody(json_encode(['data' => []]));
|
||||
}
|
||||
|
||||
$db = \Config\Database::connect();
|
||||
|
||||
$results = $db->table('clients')
|
||||
->select('id, client_name, short_name, email, phone')
|
||||
->like('client_name', $q)
|
||||
->where('client_type', 1)
|
||||
->where('is_active', 1)
|
||||
->limit(10)
|
||||
->get()
|
||||
->getResultArray();
|
||||
|
||||
return $this->response
|
||||
->setContentType('application/json')
|
||||
->setBody(json_encode(['data' => $results]));
|
||||
}
|
||||
/**
|
||||
* GET /sales/checkDuplicate?table=clients&field=short_name&value=TECH
|
||||
*/
|
||||
public function checkDuplicate()
|
||||
{
|
||||
if ($this->request->getMethod() !== 'get') {
|
||||
return $this->response
|
||||
->setStatusCode(405)
|
||||
->setContentType('application/json')
|
||||
->setBody(json_encode(['exists' => false]));
|
||||
}
|
||||
|
||||
$table = $this->request->getGet('table');
|
||||
$field = $this->request->getGet('field');
|
||||
$value = trim($this->request->getGet('value') ?? '');
|
||||
$exclude_id = $this->request->getGet('exclude_id');
|
||||
|
||||
// ── Whitelist ─────────────────────────────────────────────
|
||||
$allowed = [
|
||||
'clients' => ['model' => $this->clientModel, 'pk' => 'id'],
|
||||
];
|
||||
|
||||
// ── Allowed fields per table ──────────────────────────────
|
||||
if (!array_key_exists($table, $allowed) || !in_array($field, ['short_name', 'client_name'])) {
|
||||
return $this->response
|
||||
->setStatusCode(400)
|
||||
->setContentType('application/json')
|
||||
->setBody(json_encode(['exists' => false, 'error' => 'Invalid table or field']));
|
||||
}
|
||||
|
||||
if (empty($value)) {
|
||||
return $this->response
|
||||
->setContentType('application/json')
|
||||
->setBody(json_encode(['exists' => false]));
|
||||
}
|
||||
|
||||
$model = $allowed[$table]['model'];
|
||||
$pk = $allowed[$table]['pk'];
|
||||
|
||||
// ── Case-insensitive match ────────────────────────────────
|
||||
$model->where("LOWER({$field})", strtolower($value));
|
||||
|
||||
if (!empty($exclude_id)) {
|
||||
$model->where("{$pk} !=", (int)$exclude_id);
|
||||
}
|
||||
|
||||
$count = $model->countAllResults();
|
||||
|
||||
return $this->response
|
||||
->setContentType('application/json')
|
||||
->setBody(json_encode(['exists' => $count > 0]));
|
||||
}
|
||||
|
||||
// ==================== CONTACT PERSON APIs ====================
|
||||
|
||||
/**
|
||||
@ -559,6 +724,7 @@ class SalesController extends BaseController
|
||||
'assigned_to' => $this->request->getGet('assigned_to'),
|
||||
'date_from' => $this->request->getGet('date_from'),
|
||||
'date_to' => $this->request->getGet('date_to'),
|
||||
'search' => $this->request->getGet('search'), // ← ADD THIS
|
||||
];
|
||||
|
||||
$result = $this->activityModel->getActivitiesWithFilters($filters, $limit, $offset);
|
||||
@ -652,12 +818,16 @@ class SalesController extends BaseController
|
||||
$data['additional_assigned_ids'] = json_encode($data['additional_assigned_ids']);
|
||||
}
|
||||
|
||||
if (!$this->activityModel->insert($data)) {
|
||||
$activityId = $this->activityModel->insert($data);
|
||||
if (!$activityId) {
|
||||
return $this->fail($this->activityModel->errors(), ResponseInterface::HTTP_BAD_REQUEST);
|
||||
}
|
||||
|
||||
$data['activity_id'] = $activityId;
|
||||
|
||||
// add google calender event
|
||||
$response = $this->addCalenderEvent($data);
|
||||
log_message("error", '[GOOGLE_CALENDER] addCalenderEvent response: ' . json_encode($response));
|
||||
|
||||
$activityId = $this->activityModel->getInsertID();
|
||||
$activity = $this->activityModel->find((int)$activityId);
|
||||
@ -1073,6 +1243,7 @@ public function dashboard()
|
||||
$base = $this->getSalesStaffData();
|
||||
$salesRole = $base['sales_role'];
|
||||
$salesManagerIds = $base['sales_manager_ids'];
|
||||
$salesHeadManagerIds = $base['sales_manager_with_head'];
|
||||
$userId = get_session_userid();
|
||||
|
||||
// Get branch id
|
||||
@ -1103,7 +1274,7 @@ public function dashboard()
|
||||
|
||||
// Route by role
|
||||
if ($salesRole === 'Sales Head') {
|
||||
$this->branchLevelDashboard($nhanceBranchId, $salesManagerIds, $current_fin_year, $fin_years);
|
||||
$this->branchLevelDashboard($nhanceBranchId, $salesHeadManagerIds, $salesManagerIds, $current_fin_year, $fin_years);
|
||||
} elseif ($salesRole === 'Sales Manager') {
|
||||
$this->salesManagerLevelDashboard($userId, $current_fin_year, $fin_years);
|
||||
}
|
||||
@ -1112,17 +1283,20 @@ public function dashboard()
|
||||
// ─────────────────────────────────────────────
|
||||
// branchLevelDashboard()
|
||||
// ─────────────────────────────────────────────
|
||||
public function branchLevelDashboard($branchId, $sales_manager_ids, $current_fin_year, $fin_years)
|
||||
public function branchLevelDashboard($branchId, $branchwise_all_sales_team_ids, $sales_manager_ids, $current_fin_year, $fin_years)
|
||||
{
|
||||
|
||||
try {
|
||||
$sales_manager_ids = array_values(array_filter(array_map('intval', $sales_manager_ids)));
|
||||
$branchwise_all_sales_team_ids = array_column($branchwise_all_sales_team_ids, 'id');
|
||||
|
||||
|
||||
$db = \Config\Database::connect();
|
||||
$fyRange = $this->getFYDateRange($current_fin_year);
|
||||
$fyStart = $fyRange['start'];
|
||||
$fyEnd = $fyRange['end'];
|
||||
|
||||
if (empty($sales_manager_ids)) {
|
||||
if (empty($branchwise_all_sales_team_ids)) {
|
||||
// ── No team members — return empty dashboard ──
|
||||
$data = [
|
||||
'total_leads' => 0,
|
||||
@ -1146,21 +1320,20 @@ public function branchLevelDashboard($branchId, $sales_manager_ids, $current_fin
|
||||
|
||||
// 1. Lead count — FY filtered by created_at
|
||||
$total_leads = $this->leadModel
|
||||
->whereIn('assigned_to', $sales_manager_ids)
|
||||
->whereIn('assigned_to', $branchwise_all_sales_team_ids)
|
||||
->where('created_at >=', $fyStart)
|
||||
->where('created_at <=', $fyEnd)
|
||||
->countAllResults();
|
||||
|
||||
// 2. Total activities — FY filtered by scheduled_date
|
||||
$total_activity = $this->activityModel
|
||||
->whereIn('assigned_to', $sales_manager_ids)
|
||||
->whereIn('assigned_to', $branchwise_all_sales_team_ids)
|
||||
->where('scheduled_date >=', $fyStart)
|
||||
->where('scheduled_date <=', $fyEnd)
|
||||
->countAllResults();
|
||||
|
||||
// 3. Completed activities — FY filtered
|
||||
$total_completed_activity = $this->activityModel
|
||||
->whereIn('assigned_to', $sales_manager_ids)
|
||||
->whereIn('assigned_to', $branchwise_all_sales_team_ids)
|
||||
->where('status', 'completed')
|
||||
->where('scheduled_date >=', $fyStart)
|
||||
->where('scheduled_date <=', $fyEnd)
|
||||
@ -1168,7 +1341,7 @@ public function branchLevelDashboard($branchId, $sales_manager_ids, $current_fin
|
||||
|
||||
// 4. Pending activities — FY filtered
|
||||
$total_pending_activity = $this->activityModel
|
||||
->whereIn('assigned_to', $sales_manager_ids)
|
||||
->whereIn('assigned_to', $branchwise_all_sales_team_ids)
|
||||
->where('status', 'pending')
|
||||
->where('scheduled_date >=', $fyStart)
|
||||
->where('scheduled_date <=', $fyEnd)
|
||||
@ -1187,7 +1360,7 @@ public function branchLevelDashboard($branchId, $sales_manager_ids, $current_fin
|
||||
AND scheduled_date <= '{$fyEnd}') as done_acts", false)
|
||||
->join('roles r', 'r.id = u.role')
|
||||
->where('u.nhance_branch_id', $branchId)
|
||||
->whereIn('u.id', $sales_manager_ids)
|
||||
->whereIn('u.id', $branchwise_all_sales_team_ids)
|
||||
->where('u.is_active', 1)
|
||||
->get()
|
||||
->getResultArray();
|
||||
@ -1199,7 +1372,7 @@ public function branchLevelDashboard($branchId, $sales_manager_ids, $current_fin
|
||||
up.first_name AS assigned_to_name, sa.notes')
|
||||
->join('user_profiles up', 'up.id = sa.assigned_to', 'left')
|
||||
->join('sales_actual_leads sal', 'sal.lead_id = sa.lead_id', 'left')
|
||||
->whereIn('sa.assigned_to', $sales_manager_ids)
|
||||
->whereIn('sa.assigned_to', $branchwise_all_sales_team_ids)
|
||||
->where('sa.status', 'pending')
|
||||
->where('sa.scheduled_date >=', $fyStart)
|
||||
->where('sa.scheduled_date <=', $fyEnd)
|
||||
@ -1219,7 +1392,7 @@ public function branchLevelDashboard($branchId, $sales_manager_ids, $current_fin
|
||||
->join('user_profiles up', 'up.id = sal.assigned_to', 'left')
|
||||
->join('sales_activities sa', 'sa.lead_id = sal.lead_id', 'left')
|
||||
->join('leads l', 'l.actual_lead_id = sal.lead_id', 'left')
|
||||
->whereIn('sal.assigned_to', $sales_manager_ids)
|
||||
->whereIn('sal.assigned_to', $branchwise_all_sales_team_ids)
|
||||
->where('sal.created_at >=', $fyStart)
|
||||
->where('sal.created_at <=', $fyEnd)
|
||||
->groupBy('sal.lead_id, sal.company_name, sal.status, up.first_name')
|
||||
@ -1234,8 +1407,9 @@ public function branchLevelDashboard($branchId, $sales_manager_ids, $current_fin
|
||||
$activityBreakdown = $db->table('sales_activities')
|
||||
->select("activity_type,
|
||||
COUNT(*) AS total,
|
||||
TRUNCATE(COUNT(*) * 100.0 / {$total_activity_safe}, 2) AS percentage_accuracy,
|
||||
ROUND(COUNT(*) * 100.0 / {$total_activity_safe}, 0) AS percentage", false)
|
||||
->whereIn('assigned_to', $sales_manager_ids)
|
||||
->whereIn('assigned_to', $branchwise_all_sales_team_ids)
|
||||
->where('scheduled_date >=', $fyStart)
|
||||
->where('scheduled_date <=', $fyEnd)
|
||||
->groupBy('activity_type')
|
||||
@ -1962,6 +2136,23 @@ public function salesManagerLevelDashboard($userId, $current_fin_year = null, $f
|
||||
];
|
||||
}
|
||||
|
||||
$activity_data = $this->activityModel->where('activity_id', $input['activity_id'])->first();
|
||||
|
||||
|
||||
$emails[] = $user_data['email'];
|
||||
if (isset($activity_data['additional_assigned_ids']) && !empty($activity_data['additional_assigned_ids'])) {
|
||||
$additional_assigned_ids = json_decode($activity_data['additional_assigned_ids'], true) ?? [];
|
||||
|
||||
foreach ($additional_assigned_ids as $additional_assigned_id) {
|
||||
$additional_user_data = $this->userModel->where('id', $additional_assigned_id)->first();
|
||||
|
||||
if (!empty($additional_user_data['email'])) {
|
||||
$emails[] = $additional_user_data['email'];
|
||||
}
|
||||
}
|
||||
}
|
||||
log_message('error', '[GOOGLE_CALENDER] User emails: ' . json_encode($emails));
|
||||
|
||||
/* ---------------- SUMMARY ---------------- */
|
||||
|
||||
$activityType = ucfirst($input['activity_type']);
|
||||
@ -1988,7 +2179,7 @@ public function salesManagerLevelDashboard($userId, $current_fin_year = null, $f
|
||||
'summary' => $summary,
|
||||
'meeting_date' => $input['scheduled_date'],
|
||||
'description' => $input['notes'] ?? '',
|
||||
'emails' => [$user_data['email']],
|
||||
'emails' => $emails,
|
||||
];
|
||||
|
||||
log_message('error', '[GOOGLE_CALENDER] Google Calendar Payload: ' . json_encode($eventData));
|
||||
|
||||
@ -5,8 +5,11 @@ namespace App\Controllers;
|
||||
use App\Controllers\BaseController;
|
||||
use App\Models\EmployeePolicyModel;
|
||||
use App\Models\ClientPolicyModel;
|
||||
use App\Models\TpaApiDataModel;
|
||||
use App\Models\BatchFileModel;
|
||||
use App\Models\InsurerBranchModel;
|
||||
use App\Models\RFQModel;
|
||||
use App\Models\EmployeeModel;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
use CodeIgniter\API\ResponseTrait;
|
||||
use Dompdf\Dompdf;
|
||||
@ -1120,4 +1123,420 @@ class TestingController extends BaseController
|
||||
'metabaseUrl' => 'https://nsights.nhanceindia.in',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert sample data into tpa_api_data for testing variance report (Not in NHANCE, Not in TPA, Need to Review).
|
||||
* Uses client_policy (policy_status=1, is_active=1), employee_policies (active), and employees.
|
||||
*
|
||||
* @param int|null $client_policy_id Optional. If not provided, first eligible policy is used.
|
||||
* @return \CodeIgniter\HTTP\ResponseInterface
|
||||
*/
|
||||
public function insertSampleTpaApiData($client_policy_id = null)
|
||||
{
|
||||
$db = \Config\Database::connect();
|
||||
$clientPolicyModel = new ClientPolicyModel();
|
||||
$employeePolicyModel = new EmployeePolicyModel();
|
||||
$employeeModel = new EmployeeModel();
|
||||
$tpaApiDataModel = new TpaApiDataModel();
|
||||
$batchFileModel = new BatchFileModel();
|
||||
|
||||
// 1. Get policies: policy_status = 1, is_active = 1
|
||||
$policyBuilder = $clientPolicyModel
|
||||
->where('policy_status', 1)
|
||||
->where('is_active', 1);
|
||||
if ($client_policy_id !== null && $client_policy_id !== '') {
|
||||
$policyBuilder->where('id', (int) $client_policy_id);
|
||||
}
|
||||
$policies = $policyBuilder->orderBy('id', 'ASC')->findAll();
|
||||
if (empty($policies)) {
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'message' => 'No active client policy found (policy_status=1, is_active=1).',
|
||||
'data' => [],
|
||||
], 400);
|
||||
}
|
||||
$policy = $policies[0];
|
||||
$client_policy_id = (int) $policy['id'];
|
||||
$client_id = (int) $policy['client_id'];
|
||||
$client_branch_id = !empty($policy['client_branch_id']) ? (int) $policy['client_branch_id'] : 0;
|
||||
|
||||
// 2. Get related employees from employee_policies (active) + employees
|
||||
$empPolicies = $db->table('employee_polices ep')
|
||||
->select('ep.id AS emp_policy_id, ep.employee_id, ep.tpa_id, e.emp_code, e.name, e.dob, e.gender, e.relationship')
|
||||
->join('employees e', 'e.id = ep.employee_id')
|
||||
->where('ep.client_policy_id', $client_policy_id)
|
||||
->where('ep.is_active', 1)
|
||||
->whereIn('ep.status', ['active', 'expired'])
|
||||
->where('e.is_active', 1)
|
||||
->get()
|
||||
->getResultArray();
|
||||
if (empty($empPolicies)) {
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'message' => 'No active employee policies found for this client policy.',
|
||||
'data' => ['client_policy_id' => $client_policy_id],
|
||||
], 400);
|
||||
}
|
||||
|
||||
// 3. Create a test batch file so we have a file_id for tpa_api_data
|
||||
$createdBy = function_exists('get_session_userid') ? get_session_userid() : 1;
|
||||
$batchCode = 'TPA_SAMPLE_' . date('YmdHis') . '_' . bin2hex(random_bytes(4));
|
||||
$batchFileId = $batchFileModel->insert([
|
||||
'client_id' => $client_id,
|
||||
'client_policy_id' => $client_policy_id,
|
||||
'client_branch_id' => $client_branch_id,
|
||||
'batch_code' => $batchCode,
|
||||
'file_name' => 'sample_tpa_data_test_' . date('Y-m-d_His') . '.xlsx',
|
||||
'insurer_or_tpa' => 'tpa',
|
||||
'event_type' => 'api',
|
||||
'actions' => 'fetch',
|
||||
'status' => 'partially success',
|
||||
'count' => 0,
|
||||
'created_by' => $createdBy,
|
||||
'is_active' => 1,
|
||||
]);
|
||||
if (!$batchFileId) {
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'message' => 'Failed to create test batch file.',
|
||||
'data' => [],
|
||||
], 500);
|
||||
}
|
||||
$file_id = (int) $batchFileId;
|
||||
|
||||
$tpaApiDataModel->skipValidation(true);
|
||||
$inserted = ['not_in_nhance' => 0, 'need_to_review' => 0];
|
||||
$toInsert = [];
|
||||
|
||||
// 4. Not in NHANCE: insert TPA records with emp_codes that do NOT exist in NHANCE for this policy
|
||||
$fakeEmpCodes = ['TPA_SAMPLE_NOTINNHANCE_1', 'TPA_SAMPLE_NOTINNHANCE_2'];
|
||||
foreach ($fakeEmpCodes as $i => $empCode) {
|
||||
$toInsert[] = [
|
||||
'file_id' => $file_id,
|
||||
'emp_code' => $empCode,
|
||||
'name' => 'Sample TPA Only ' . ($i + 1),
|
||||
'dob' => '1990-01-' . str_pad((string)(15 + $i), 2, '0', STR_PAD_LEFT),
|
||||
'relation' => 'Self',
|
||||
'gender' => ($i % 2 === 0) ? 'M' : 'F',
|
||||
'self' => 'Sample TPA Only ' . ($i + 1),
|
||||
'tpa_id' => 'TPA' . (1000 + $i),
|
||||
'age' => 32 + $i,
|
||||
'is_active' => 1,
|
||||
'desc' => 'Sample data – Not in NHANCE',
|
||||
'created_by'=> $createdBy,
|
||||
];
|
||||
$inserted['not_in_nhance']++;
|
||||
}
|
||||
|
||||
// 5. Need to Review: same employee as in NHANCE but with different name/dob/gender
|
||||
$needReview = array_slice($empPolicies, 0, min(2, count($empPolicies)));
|
||||
foreach ($needReview as $emp) {
|
||||
$dob = $emp['dob'];
|
||||
if (is_string($dob) && preg_match('/^(\d{4})-(\d{2})-(\d{2})/', $dob, $m)) {
|
||||
$altDob = $m[1] . '-' . $m[2] . '-' . str_pad((string)((int)$m[3] + 1), 2, '0', STR_PAD_LEFT);
|
||||
} else {
|
||||
$altDob = '1995-06-15';
|
||||
}
|
||||
$toInsert[] = [
|
||||
'file_id' => $file_id,
|
||||
'emp_code' => $emp['emp_code'],
|
||||
'name' => '[TPA Altered] ' . ($emp['name'] ?? 'Unknown'),
|
||||
'dob' => $altDob,
|
||||
'relation' => $emp['relationship'] ?? 'Self',
|
||||
'gender' => (strtoupper($emp['gender'] ?? 'M') === 'M') ? 'F' : 'M',
|
||||
'self' => $emp['name'] ?? 'Unknown',
|
||||
'tpa_id' => $emp['tpa_id'] ?? ('T' . $emp['employee_id']),
|
||||
'age' => 30,
|
||||
'is_active' => 1,
|
||||
'desc' => 'Sample data – Need to Review (mismatch)',
|
||||
'created_by'=> $createdBy,
|
||||
];
|
||||
$inserted['need_to_review']++;
|
||||
}
|
||||
|
||||
foreach ($toInsert as $row) {
|
||||
$tpaApiDataModel->insert($row);
|
||||
}
|
||||
|
||||
// Not in TPA: we do NOT insert those into tpa_api_data; NHANCE already has employees. So any employee
|
||||
// we did not add to tpa_api_data will appear as "Not in TPA". We added only "Need to Review" and
|
||||
// "Not in NHANCE" rows; the rest of NHANCE employees remain without TPA rows => they show as Not in TPA.
|
||||
|
||||
return $this->respond([
|
||||
'status' => true,
|
||||
'message' => 'Sample TPA API data inserted successfully.',
|
||||
'data' => [
|
||||
'file_id' => $file_id,
|
||||
'client_policy_id' => $client_policy_id,
|
||||
'client_id' => $client_id,
|
||||
'batch_code' => $batchCode,
|
||||
'inserted' => $inserted,
|
||||
'not_in_tpa_note' => 'Employees in NHANCE that were not added to TPA data will appear as "Not in TPA" when you run the variance report for this file.',
|
||||
],
|
||||
], 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* List employee count per client policy.
|
||||
* No input parameters. Checks all client_policy records and counts linked employee_policies per policy.
|
||||
*
|
||||
* @return \CodeIgniter\HTTP\ResponseInterface
|
||||
*/
|
||||
public function listEmployeeCountByClientPolicy()
|
||||
{
|
||||
$db = \Config\Database::connect();
|
||||
$rows = $db->table('client_policy cp')
|
||||
->select('cp.id AS client_policy_id, COUNT(ep.id) AS employee_policy_count', false)
|
||||
->join('employee_polices ep', 'ep.client_policy_id = cp.id', 'left')
|
||||
->groupBy('cp.id')
|
||||
->orderBy('cp.id', 'ASC')
|
||||
->get()
|
||||
->getResultArray();
|
||||
$list = array_map(function ($row) {
|
||||
return [
|
||||
'client_policy_id' => (int) $row['client_policy_id'],
|
||||
'employee_policy_count' => (int) $row['employee_policy_count'],
|
||||
];
|
||||
}, $rows);
|
||||
return $this->respond([
|
||||
'status' => true,
|
||||
'message' => 'Employee count per client policy.',
|
||||
'data' => $list,
|
||||
], 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Wellness SSO token generation for Medi Assist (MediBuddy).
|
||||
*
|
||||
* This uses the token-based authentication details shared by Medi Assist:
|
||||
* - Cipher: AES-256-CBC
|
||||
* - Padding: PKCS7 (OpenSSL default)
|
||||
* - Login URL: https://login.mediassist.in/SSOLogon.aspx?PartnerCorpId={0}&EncryptedSSO={1}
|
||||
*
|
||||
* Environment variables (recommended):
|
||||
* - MEDIASSIST_WELLNESS_KEY
|
||||
* - MEDIASSIST_WELLNESS_IV
|
||||
* - MEDIASSIST_WELLNESS_PARTNER_CORP_ID
|
||||
* - MEDIASSIST_WELLNESS_LOGIN_URL
|
||||
*
|
||||
* If env values are not present, sensible dummy defaults are used so that
|
||||
* the function can still be exercised.
|
||||
*/
|
||||
public function testMediAssistWellnessOld()
|
||||
{
|
||||
|
||||
// Configuration – prefer environment variables, fall back to demo values
|
||||
$keyString = env('MEDIASSIST_WELLNESS_KEY');
|
||||
$ivString = env('MEDIASSIST_WELLNESS_IV');
|
||||
$loginUrlTemplate = env('MEDIASSIST_WELLNESS_LOGIN_URL' );
|
||||
$partnerCorpId = '15963';
|
||||
$cipher_algorithm = 'AES-256-CBC';
|
||||
|
||||
// Plain SSO JSON payload, as per Medi Assist sample
|
||||
$payload = [
|
||||
'Id' => '15022',
|
||||
'expiryTime' => time() + (10 * 60), // 10 minutes
|
||||
'CPartnerId' => $partnerCorpId,
|
||||
];
|
||||
|
||||
$plainJson = json_encode($payload, JSON_UNESCAPED_SLASHES);
|
||||
|
||||
// Derive a 32‑byte key (AES‑256) and 16‑byte IV from the provided strings
|
||||
// $key = substr(hash('sha256', $keyString, true), 0, 32);
|
||||
// $iv = substr(hash('md5', $ivString, true), 0, 16);
|
||||
|
||||
// Encrypt with AES‑256‑CBC + PKCS7 padding (OpenSSL default)
|
||||
$cipherTextRaw = openssl_encrypt( $plainJson, $cipher_algorithm, $keyString, OPENSSL_RAW_DATA, $ivString);
|
||||
|
||||
|
||||
if ($cipherTextRaw === false) {
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'message' => 'Encryption failed while generating Medi Assist wellness token.',
|
||||
], 500);
|
||||
}
|
||||
|
||||
|
||||
// Base64 encode and URL‑encode for use as EncryptedSSO
|
||||
$encryptedSSO = urlencode(base64_encode($cipherTextRaw));
|
||||
// $encryptedSSO = rtrim(strtr(base64_encode($cipherTextRaw), '+/', '-_'), '=');
|
||||
|
||||
// Build the final login URL
|
||||
$loginUrl = str_replace(['{0}', '{1}'], [$partnerCorpId, $encryptedSSO], $loginUrlTemplate);
|
||||
|
||||
return $this->respond([
|
||||
'status' => true,
|
||||
'message' => 'Medi Assist wellness test URL generated successfully.',
|
||||
'data' => [
|
||||
'loginUrl' => $loginUrl,
|
||||
'encryptedSSO' => $encryptedSSO,
|
||||
'plainPayload' => $payload,
|
||||
],
|
||||
], 200);
|
||||
}
|
||||
|
||||
public function chartbrewDashboardDemo()
|
||||
{
|
||||
// 1. Configuration - Use a Chartbrew-specific Secret Key from .env
|
||||
$chartbrew_base_url = "https://analytics.nhanceindia.in/report/my-first-dashboard-oHbWNWH0";
|
||||
$chartbrew_secret = getenv('METABASE_SECRET_KEY'); // Ensure this is set in your .env
|
||||
|
||||
// 2. Get dynamic parameters (matching your screenshot's naming convention)
|
||||
$policy_id = $this->request->getGet('client_policy_id') ?? 4687;
|
||||
|
||||
// 3. Define the Payload
|
||||
// Note: 'sub' (Subject) should usually be the user ID or a unique identifier
|
||||
// depending on Chartbrew's specific JWT requirements.
|
||||
$payload = [
|
||||
"sub" => [
|
||||
"type" => "Project",
|
||||
"id" => 4, // Integer type usually preferred
|
||||
"sharePolicyId" => 4
|
||||
],
|
||||
"iat" => time(),
|
||||
"exp" => time() + (10 * 60), // 10 minute expiration
|
||||
];
|
||||
|
||||
// 4. Encode the JWT
|
||||
// Make sure you have 'use Firebase\JWT\JWT;' at the top of your controller
|
||||
$token = JWT::encode($payload, $chartbrew_secret, 'HS256');
|
||||
|
||||
// 5. Construct the URL
|
||||
// We use http_build_query to ensure special characters are handled correctly
|
||||
$params = [
|
||||
'token' => $token,
|
||||
'theme' => 'light',
|
||||
'client_policy_id' => $policy_id
|
||||
];
|
||||
|
||||
$url = $chartbrew_base_url . '?' . http_build_query($params);
|
||||
|
||||
// 6. Optional API response
|
||||
if ($this->request->getGet('api') == 1) {
|
||||
return $this->response->setJSON([
|
||||
'status' => 'success',
|
||||
'data' => [
|
||||
'chartbrewUrl' => $url,
|
||||
'policy_id' => $policy_id
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
// 7. Return the View
|
||||
return view('chartbrew_dashboard_view', [
|
||||
'iframe_url' => $url,
|
||||
'policy_id' => $policy_id
|
||||
]);
|
||||
}
|
||||
|
||||
public function testMediAssistWellness()
|
||||
{
|
||||
// Config
|
||||
$keyString = env('MEDIASSIST_WELLNESS_KEY');
|
||||
$ivString = env('MEDIASSIST_WELLNESS_IV');
|
||||
$loginUrlTemplate = env('MEDIASSIST_WELLNESS_LOGIN_URL');
|
||||
$partnerCorpId = '15963';
|
||||
|
||||
$cipher_algorithm = 'AES-256-CBC';
|
||||
|
||||
// Payload (MATCH EXACT CASE from doc)
|
||||
$payload = [
|
||||
'Id' => '15022',
|
||||
'expiryTime' => (string)(time() + 600), // string format safer
|
||||
'CPartnerId' => $partnerCorpId,
|
||||
];
|
||||
|
||||
$plainJson = json_encode($payload, JSON_UNESCAPED_SLASHES);
|
||||
|
||||
// ✅ IMPORTANT: Ensure correct key + IV length (ASCII based)
|
||||
$key = substr(str_pad($keyString, 32, '0'), 0, 32); // 32 bytes
|
||||
$iv = substr(str_pad($ivString, 16, '0'), 0, 16); // 16 bytes
|
||||
|
||||
// Encrypt
|
||||
$cipherTextRaw = openssl_encrypt(
|
||||
$plainJson,
|
||||
$cipher_algorithm,
|
||||
$key,
|
||||
OPENSSL_RAW_DATA,
|
||||
$iv
|
||||
);
|
||||
|
||||
if ($cipherTextRaw === false) {
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'message' => 'Encryption failed.',
|
||||
], 500);
|
||||
}
|
||||
|
||||
// ✅ Use ONLY Base64 (NO urlencode unless explicitly required)
|
||||
$encryptedSSO = base64_encode($cipherTextRaw);
|
||||
|
||||
// Build URL
|
||||
$loginUrl = str_replace(
|
||||
['{0}', '{1}'],
|
||||
[$partnerCorpId, $encryptedSSO],
|
||||
$loginUrlTemplate
|
||||
);
|
||||
|
||||
return $this->respond([
|
||||
'status' => true,
|
||||
'data' => [
|
||||
'loginUrl' => $loginUrl,
|
||||
'encryptedSSO' => $encryptedSSO,
|
||||
'plainPayload' => $payload,
|
||||
'decryptMediAssistWellness' => $this->decryptMediAssistWellness($encryptedSSO),
|
||||
],
|
||||
], 200);
|
||||
}
|
||||
|
||||
public function decryptMediAssistWellness($encryptedSSO)
|
||||
{
|
||||
// Config
|
||||
$keyString = env('MEDIASSIST_WELLNESS_KEY');
|
||||
$ivString = env('MEDIASSIST_WELLNESS_IV');
|
||||
|
||||
$cipher_algorithm = 'AES-256-CBC';
|
||||
|
||||
// Ensure same key + IV format used in encryption
|
||||
$key = substr(str_pad($keyString, 32, '0'), 0, 32); // 32 bytes
|
||||
$iv = substr(str_pad($ivString, 16, '0'), 0, 16); // 16 bytes
|
||||
|
||||
// Decode Base64
|
||||
$cipherTextRaw = base64_decode($encryptedSSO, true);
|
||||
|
||||
|
||||
if ($cipherTextRaw === false) {
|
||||
return [
|
||||
'status' => false,
|
||||
'message' => 'Invalid Base64 string.',
|
||||
];
|
||||
}
|
||||
|
||||
// Decrypt
|
||||
$decrypted = openssl_decrypt(
|
||||
$cipherTextRaw,
|
||||
$cipher_algorithm,
|
||||
$key,
|
||||
OPENSSL_RAW_DATA,
|
||||
$iv
|
||||
);
|
||||
|
||||
if ($decrypted === false) {
|
||||
return [
|
||||
'status' => false,
|
||||
'message' => 'Decryption failed.',
|
||||
];
|
||||
}
|
||||
|
||||
// Convert JSON to array
|
||||
$data = json_decode($decrypted, true);
|
||||
|
||||
return [
|
||||
'status' => true,
|
||||
'data' => [
|
||||
'decryptedRaw' => $decrypted,
|
||||
'decoded' => $data
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@ -573,7 +573,10 @@ class TicketController extends BaseController
|
||||
->join('ticket_claim_status tcs', 'tcs.id = tm.claim_status_id AND tcs.is_active = 1', 'left')
|
||||
->join("({$subquery->getCompiledSelect()}) tat_category", 'tm.id = tat_category.ticket_id', 'left')
|
||||
->where('tm.is_active', 1)
|
||||
->orderBy('tm.id', 'DESC');
|
||||
->whereNotIn('claim_status_id', [11, 12, 13, 22, 24, 32, 34, 42, 44, 47, 53, 58, 65])
|
||||
->orderBy('tm.id', 'DESC')
|
||||
->limit(100);
|
||||
|
||||
|
||||
$data = $query->get()->getResultArray();
|
||||
|
||||
@ -628,6 +631,7 @@ class TicketController extends BaseController
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$query = $db->table('ticket_master tm')
|
||||
->select([
|
||||
'tm.id',
|
||||
@ -1027,7 +1031,8 @@ class TicketController extends BaseController
|
||||
$data['claim_status'] = $this->transFormClaimStatus($claim_status_id, $ticket_type);
|
||||
$data['client_for_motor'] = $this->clientModel->select('id,client_name,phone')->where('client_type', 2)->where('is_active', 1)->findAll();
|
||||
}
|
||||
|
||||
$data['tab_name'] = "Claims";
|
||||
$data['page_name'] = "Claims";
|
||||
return $this->loadLayout('ticket_edit_onbording', $data);
|
||||
}
|
||||
|
||||
@ -1177,9 +1182,9 @@ class TicketController extends BaseController
|
||||
],
|
||||
'tpa_no' => [
|
||||
'label' => 'TPA ID',
|
||||
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9\/]+$/]',
|
||||
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9\/\-_]+$/]',
|
||||
'errors' => [
|
||||
'regex_match' => 'TPA ID can only contain letters, numbers, and /.'
|
||||
'regex_match' => 'TPA ID can only contain letters, numbers, /, -, and _.'
|
||||
]
|
||||
],
|
||||
'emp_mobile' => ['label' => 'Employee Mobile No','rules' => 'required|numeric|exact_length[10]',
|
||||
@ -1232,9 +1237,9 @@ class TicketController extends BaseController
|
||||
],
|
||||
'hospital_address' => [
|
||||
'label' => 'Hospital Address',
|
||||
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9\s\-_.,#\/]+$/]',
|
||||
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9\s.,#\/_-]+$/]',
|
||||
'errors' => [
|
||||
'regex_match' => 'The {field} contains invalid characters (Allowed: letters, numbers, spaces, dashes, commas, dots, # and /).'
|
||||
'regex_match' => 'The {field} contains invalid characters (Allowed: letters, numbers, spaces, -, _, ., ,, # and /).'
|
||||
],
|
||||
],
|
||||
'hospital_state' => [
|
||||
@ -1279,10 +1284,21 @@ class TicketController extends BaseController
|
||||
'rules' => 'permit_empty|numeric',
|
||||
'errors' => ['numeric' => 'Claim Amount must contain only numbers.']
|
||||
],
|
||||
'pod_no' => ['label' => 'POD No','rules' => 'permit_empty','errors' => []],
|
||||
|
||||
'pod_no' => [
|
||||
'label' => 'POD No',
|
||||
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9 \/_-]+$/]',
|
||||
'errors' => [
|
||||
'regex_match' => 'The {field} may only contain letters, numbers, spaces, slashes, underscores, and hyphens.',
|
||||
]
|
||||
],
|
||||
// --- ADDITIONAL / CONDITIONAL FIELDS ---
|
||||
'claim_number' => ['label' => 'Claim Number','rules' => 'permit_empty|alpha_numeric_punct','errors' => ['alpha_numeric_punct' => 'Invalid Claim Number.']],
|
||||
'claim_number' => [
|
||||
'label' => 'Claim Number',
|
||||
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9\/_-]+$/]',
|
||||
'errors' => [
|
||||
'regex_match' => 'Claim Number only allows letters, numbers, slashes (/), hyphens (-), and underscores (_).'
|
||||
]
|
||||
],
|
||||
'denial_date' => ['label' => 'Denial Date','rules' => 'permit_empty|regex_match[/^\d{2}\/\d{2}\/\d{4}$/]','errors' => ['regex_match' => 'Denial Date must be dd/mm/yyyy.']],
|
||||
'approved_amount' => ['label' => 'Approved Amount','rules' => 'permit_empty|numeric',
|
||||
'errors' => ['numeric' => 'Approved amount must be numeric']
|
||||
@ -1530,9 +1546,9 @@ class TicketController extends BaseController
|
||||
],
|
||||
'tpa_no' => [
|
||||
'label' => 'TPA ID',
|
||||
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9\/]+$/]',
|
||||
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9\/\-_]+$/]',
|
||||
'errors' => [
|
||||
'regex_match' => 'TPA ID can only contain letters, numbers, and /.'
|
||||
'regex_match' => 'TPA ID can only contain letters, numbers, /, -, and _.'
|
||||
]
|
||||
],
|
||||
'emp_mobile' => ['label' => 'Employee Mobile No','rules' => 'required|numeric|exact_length[10]',
|
||||
@ -1585,9 +1601,9 @@ class TicketController extends BaseController
|
||||
],
|
||||
'hospital_address' => [
|
||||
'label' => 'Hospital Address',
|
||||
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9\s\-_.,#\/]+$/]',
|
||||
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9\s.,#\/_-]+$/]',
|
||||
'errors' => [
|
||||
'regex_match' => 'The {field} contains invalid characters (Allowed: letters, numbers, spaces, dashes, commas, dots, # and /).'
|
||||
'regex_match' => 'The {field} contains invalid characters (Allowed: letters, numbers, spaces, -, _, ., ,, # and /).'
|
||||
],
|
||||
],
|
||||
'hospital_state' => [
|
||||
@ -1632,10 +1648,22 @@ class TicketController extends BaseController
|
||||
'rules' => 'permit_empty|numeric',
|
||||
'errors' => ['numeric' => 'Claim Amount must contain only numbers.']
|
||||
],
|
||||
'pod_no' => ['label' => 'POD No','rules' => 'permit_empty','errors' => []],
|
||||
'pod_no' => [
|
||||
'label' => 'POD No',
|
||||
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9 \/_-]+$/]',
|
||||
'errors' => [
|
||||
'regex_match' => 'The {field} may only contain letters, numbers, spaces, slashes, underscores, and hyphens.',
|
||||
]
|
||||
],
|
||||
|
||||
// --- ADDITIONAL / CONDITIONAL FIELDS ---
|
||||
'claim_number' => ['label' => 'Claim Number','rules' => 'permit_empty|alpha_numeric_punct','errors' => ['alpha_numeric_punct' => 'Invalid Claim Number.']],
|
||||
'claim_number' => [
|
||||
'label' => 'Claim Number',
|
||||
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9\/_-]+$/]',
|
||||
'errors' => [
|
||||
'regex_match' => 'Claim Number only allows letters, numbers, slashes (/), hyphens (-), and underscores (_).'
|
||||
]
|
||||
],
|
||||
'denial_date' => ['label' => 'Denial Date','rules' => 'permit_empty|regex_match[/^\d{2}\/\d{2}\/\d{4}$/]','errors' => ['regex_match' => 'Denial Date must be dd/mm/yyyy.']],
|
||||
'approved_amount' => ['label' => 'Approved Amount','rules' => 'permit_empty|numeric',
|
||||
'errors' => ['numeric' => 'Approved amount must be numeric']
|
||||
@ -1776,8 +1804,12 @@ class TicketController extends BaseController
|
||||
$ticket_id = $this->request->getPost('ticket_master_id');
|
||||
$old_ticket_data = $this->ticketMasterModel->where('id', $ticket_id)->where('is_active', 1)->first();
|
||||
$ticket_data['claim_status_id'] = $this->getLastMatchedStatus($ticket_data, $old_ticket_data);
|
||||
$ticket_data['last_updated_by'] = 'USER';
|
||||
// print_rr($ticket_data); die;
|
||||
|
||||
$this->myLogger->logme('error', "[UPDATE_CLAIM] Ticket Master ID: {data}", ['data' => $ticket_id]);
|
||||
$this->myLogger->logme('error', "[UPDATE_CLAIM] Old Ticket Data: {data}", ['data' => json_encode($old_ticket_data, JSON_PRETTY_PRINT)]);
|
||||
$this->myLogger->logme('error', "[UPDATE_CLAIM] New Ticket Data: {data}", ['data' => json_encode($ticket_data, JSON_PRETTY_PRINT)]);
|
||||
|
||||
if ($ticket_data) {
|
||||
$return_value = $this->ticketMasterModel->where('id', $ticket_id)->set($ticket_data)->update();
|
||||
@ -2399,6 +2431,8 @@ class TicketController extends BaseController
|
||||
th.old_value,
|
||||
th.new_value,
|
||||
th.created_at,
|
||||
th.updated_by,
|
||||
|
||||
CONCAT_WS(' ', creator.first_name, creator.last_name) AS modified_by,
|
||||
-- Claim Status
|
||||
old_status.claim_status as old_status_value,
|
||||
@ -2413,7 +2447,7 @@ class TicketController extends BaseController
|
||||
new_insured_emp.name as new_insured_id_name,
|
||||
|
||||
ticket_master.ticket_type_id
|
||||
|
||||
|
||||
FROM
|
||||
ticket_history th
|
||||
|
||||
@ -3542,9 +3576,13 @@ class TicketController extends BaseController
|
||||
claim_dump_files.file_name,
|
||||
claim_dump_files.status,
|
||||
claim_dump_files.created_at,
|
||||
up.first_name as user_name
|
||||
up.first_name as user_name,
|
||||
c.client_name,
|
||||
cp.policy_no
|
||||
')
|
||||
->join('user_profiles as up', 'claim_dump_files.created_by = up.id', 'left')
|
||||
->join('client_policy as cp', 'claim_dump_files.client_policy_id = cp.id', 'left')
|
||||
->join('clients as c', 'cp.client_id = c.id', 'left')
|
||||
->where('claim_dump_files.is_active', 1)
|
||||
->orderBy('claim_dump_files.id', 'desc')
|
||||
->findAll();
|
||||
|
||||
@ -115,12 +115,13 @@ class UserController extends AdminController
|
||||
// ======================
|
||||
'emp_code' => [
|
||||
'label' => 'Employee Code',
|
||||
'rules' => 'required|min_length[3]|max_length[15]|regex_match[/^[a-zA-Z0-9_\-\/]+$/]',
|
||||
'rules' => 'required|min_length[3]|max_length[15]|regex_match[/^[a-zA-Z0-9_\-\/]+$/]|is_unique[user_profiles.emp_code]',
|
||||
'errors' => [
|
||||
'required' => 'Employee Code is required',
|
||||
'min_length' => 'Employee Code must be at least 3 characters',
|
||||
'max_length' => 'Employee Code cannot exceed 15 characters',
|
||||
'regex_match' => 'Employee Code can only contain letters, numbers, underscores, hyphens, and forward slashes (/).'
|
||||
'regex_match' => 'Employee Code can only contain letters, numbers, underscores, hyphens, and forward slashes (/).',
|
||||
'is_unique' => 'This Employee Code already exists.'
|
||||
]
|
||||
],
|
||||
|
||||
@ -141,10 +142,11 @@ class UserController extends AdminController
|
||||
// Email
|
||||
// ======================
|
||||
'email' => [
|
||||
'rules' => 'required|regex_match[/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/]',
|
||||
'rules' => 'required|regex_match[/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/]|is_unique[user_profiles.email]',
|
||||
'errors' => [
|
||||
'required' => 'Email address is required.',
|
||||
'regex_match' => 'Please enter a valid email format (e.g., name@domain.com).'
|
||||
'regex_match' => 'Please enter a valid email format (e.g., name@domain.com).',
|
||||
'is_unique' => 'This Email address already exists.'
|
||||
]
|
||||
],
|
||||
|
||||
@ -152,10 +154,11 @@ class UserController extends AdminController
|
||||
// Mobile
|
||||
// ======================
|
||||
'mobile' => [
|
||||
'rules' => 'required|regex_match[/^[6-9][0-9]{9}$/]',
|
||||
'rules' => 'required|regex_match[/^[6-9][0-9]{9}$/]|is_unique[user_profiles.mobile]',
|
||||
'errors' => [
|
||||
'required' => 'Mobile number is required',
|
||||
'regex_match' => 'Enter a valid 10-digit mobile number starting with 6, 7, 8, or 9',
|
||||
'is_unique' => 'This Mobile number already exists.'
|
||||
]
|
||||
],
|
||||
|
||||
@ -192,14 +195,15 @@ class UserController extends AdminController
|
||||
|
||||
$insert = $this->userModel->insert($userData);
|
||||
|
||||
$bookStackData = [
|
||||
'name' => $userData['first_name'],
|
||||
'email' => $userData['email'],
|
||||
// $bookStackData = [
|
||||
// 'name' => $userData['first_name'],
|
||||
// 'email' => $userData['email'],
|
||||
|
||||
];
|
||||
$this->bookStack->createEditUser($bookStackData);
|
||||
// ];
|
||||
// $this->bookStack->createEditUser($bookStackData);
|
||||
|
||||
if ($insert) {
|
||||
|
||||
$teamData['user_id'] = $insert;
|
||||
foreach ($teams as $value) {
|
||||
$teamData['team_id'] = $value;
|
||||
@ -207,42 +211,42 @@ class UserController extends AdminController
|
||||
$this->userTeamsModel->insert($teamData);
|
||||
}
|
||||
|
||||
$db = \Config\Database::connect();
|
||||
$tableName = 'hdz_staff';
|
||||
// $db = \Config\Database::connect();
|
||||
// $tableName = 'hdz_staff';
|
||||
|
||||
// Set default values
|
||||
$admin = 0;
|
||||
$acm = 0;
|
||||
$acm_id = null;
|
||||
// // Set default values
|
||||
// $admin = 0;
|
||||
// $acm = 0;
|
||||
// $acm_id = null;
|
||||
|
||||
if ($userData['role'] == 3) {
|
||||
$admin = 0;
|
||||
$acm = 1;
|
||||
$acm_id = $insert;
|
||||
} else if (in_array($userData['role'], [1, 5])) {
|
||||
$admin = 1;
|
||||
$acm = 0;
|
||||
$acm_id = null;
|
||||
}
|
||||
// if ($userData['role'] == 3) {
|
||||
// $admin = 0;
|
||||
// $acm = 1;
|
||||
// $acm_id = $insert;
|
||||
// } else if (in_array($userData['role'], [1, 5])) {
|
||||
// $admin = 1;
|
||||
// $acm = 0;
|
||||
// $acm_id = null;
|
||||
// }
|
||||
|
||||
$password = '12345678'; // Default password
|
||||
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
|
||||
// $password = '12345678'; // Default password
|
||||
// $hashedPassword = password_hash($password, PASSWORD_DEFAULT);
|
||||
|
||||
$hdz_staff = [
|
||||
'emp_code' => $userData['emp_code'],
|
||||
'fullname' => $userData['first_name'],
|
||||
'username' => strtolower($userData['first_name']),
|
||||
'email' => $userData['email'],
|
||||
'admin' => $admin,
|
||||
'acm' => $acm,
|
||||
'acm_id' => $acm_id,
|
||||
'registration' => time(),
|
||||
'password' => $hashedPassword,
|
||||
'active' => 1,
|
||||
'department' => 'a:7:{i:0;s:1:"1";i:1;s:1:"2";i:2;s:1:"5";i:3;s:1:"3";i:4;s:1:"9";i:5;s:1:"4";i:6;s:2:"10";}',
|
||||
];
|
||||
// $hdz_staff = [
|
||||
// 'emp_code' => $userData['emp_code'],
|
||||
// 'fullname' => $userData['first_name'],
|
||||
// 'username' => strtolower($userData['first_name']),
|
||||
// 'email' => $userData['email'],
|
||||
// 'admin' => $admin,
|
||||
// 'acm' => $acm,
|
||||
// 'acm_id' => $acm_id,
|
||||
// 'registration' => time(),
|
||||
// 'password' => $hashedPassword,
|
||||
// 'active' => 1,
|
||||
// 'department' => 'a:7:{i:0;s:1:"1";i:1;s:1:"2";i:2;s:1:"5";i:3;s:1:"3";i:4;s:1:"9";i:5;s:1:"4";i:6;s:2:"10";}',
|
||||
// ];
|
||||
|
||||
$db->table($tableName)->insert($hdz_staff);
|
||||
// $db->table($tableName)->insert($hdz_staff);
|
||||
|
||||
}
|
||||
}
|
||||
@ -268,7 +272,10 @@ class UserController extends AdminController
|
||||
if (!$this->request->getPost()) {
|
||||
return redirect()->to(base_url('/user/list'));
|
||||
} else {
|
||||
// echo ":/ in 163";
|
||||
|
||||
$data = $this->request->getPost();
|
||||
$userData = sanitizeInputArrayAdvanced($data);
|
||||
$id = $userData['PrimaryKey'];
|
||||
$rules = [
|
||||
|
||||
// ======================
|
||||
@ -297,13 +304,13 @@ class UserController extends AdminController
|
||||
// Employee Code
|
||||
// ======================
|
||||
'emp_code' => [
|
||||
'label' => 'Employee Code',
|
||||
'rules' => 'required|min_length[3]|max_length[15]|regex_match[/^[a-zA-Z0-9_\-\/]+$/]',
|
||||
'rules' => "required|min_length[3]|max_length[15]|regex_match[/^[a-zA-Z0-9_\-\/]+$/]|is_unique[user_profiles.emp_code,id,{$id}]",
|
||||
'errors' => [
|
||||
'required' => 'Employee Code is required',
|
||||
'min_length' => 'Employee Code must be at least 3 characters',
|
||||
'max_length' => 'Employee Code cannot exceed 15 characters',
|
||||
'regex_match' => 'Employee Code can only contain letters, numbers, underscores, hyphens, and forward slashes (/).'
|
||||
'regex_match' => 'Employee Code can only contain letters, numbers, underscores, hyphens, and forward slashes (/).',
|
||||
'is_unique' => 'This Employee Code already exists.'
|
||||
]
|
||||
],
|
||||
|
||||
@ -324,10 +331,11 @@ class UserController extends AdminController
|
||||
// Email
|
||||
// ======================
|
||||
'email' => [
|
||||
'rules' => 'required|regex_match[/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/]',
|
||||
'rules' => "required|regex_match[/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/]|is_unique[user_profiles.email,id,{$id}]",
|
||||
'errors' => [
|
||||
'required' => 'Email address is required.',
|
||||
'regex_match' => 'Please enter a valid email format (e.g., name@domain.com).'
|
||||
'regex_match' => 'Please enter a valid email format (e.g., name@domain.com).',
|
||||
'is_unique' => 'This Email address already exists.'
|
||||
]
|
||||
],
|
||||
|
||||
@ -335,10 +343,11 @@ class UserController extends AdminController
|
||||
// Mobile
|
||||
// ======================
|
||||
'mobile' => [
|
||||
'rules' => 'required|regex_match[/^[6-9][0-9]{9}$/]',
|
||||
'rules' => "required|regex_match[/^[6-9][0-9]{9}$/]|is_unique[user_profiles.mobile,id,{$id}]",
|
||||
'errors' => [
|
||||
'required' => 'Mobile number is required',
|
||||
'regex_match' => 'Enter a valid 10-digit mobile number starting with 6, 7, 8, or 9',
|
||||
'is_unique' => 'This Mobile number already exists.'
|
||||
]
|
||||
],
|
||||
|
||||
@ -384,12 +393,12 @@ class UserController extends AdminController
|
||||
$userData['updated_by'] = get_session_userid();
|
||||
$update = $this->userModel->where('id', $id)->set($userData)->update();
|
||||
|
||||
$bookStackData = [
|
||||
'name' => $userData['first_name'],
|
||||
'email' => $userData['email'],
|
||||
];
|
||||
// $bookStackData = [
|
||||
// 'name' => $userData['first_name'],
|
||||
// 'email' => $userData['email'],
|
||||
// ];
|
||||
|
||||
$this->bookStack->createEditUser($bookStackData,$existingData);
|
||||
// $this->bookStack->createEditUser($bookStackData,$existingData);
|
||||
|
||||
|
||||
if ($update) {
|
||||
@ -404,54 +413,54 @@ class UserController extends AdminController
|
||||
}
|
||||
}
|
||||
|
||||
$db = \Config\Database::connect();
|
||||
$tableName = 'hdz_staff';
|
||||
// $db = \Config\Database::connect();
|
||||
// $tableName = 'hdz_staff';
|
||||
|
||||
$hdz_staff = [
|
||||
'emp_code' => $userData['emp_code'],
|
||||
'fullname' => $userData['first_name'],
|
||||
'username' => strtolower($userData['first_name']),
|
||||
'email' => $userData['email'],
|
||||
'registration' => time(),
|
||||
'active' => 1,
|
||||
'department' => 'a:7:{i:0;s:1:"1";i:1;s:1:"2";i:2;s:1:"5";i:3;s:1:"3";i:4;s:1:"9";i:5;s:1:"4";i:6;s:2:"10";}',
|
||||
];
|
||||
// $hdz_staff = [
|
||||
// 'emp_code' => $userData['emp_code'],
|
||||
// 'fullname' => $userData['first_name'],
|
||||
// 'username' => strtolower($userData['first_name']),
|
||||
// 'email' => $userData['email'],
|
||||
// 'registration' => time(),
|
||||
// 'active' => 1,
|
||||
// 'department' => 'a:7:{i:0;s:1:"1";i:1;s:1:"2";i:2;s:1:"5";i:3;s:1:"3";i:4;s:1:"9";i:5;s:1:"4";i:6;s:2:"10";}',
|
||||
// ];
|
||||
|
||||
if ($userData['role'] == 3) {
|
||||
$hdz_staff['admin'] = 0;
|
||||
$hdz_staff['acm'] = 1;
|
||||
$hdz_staff['acm_id'] = $id;
|
||||
} elseif (in_array($userData['role'], [1, 5])) {
|
||||
$hdz_staff['admin'] = 1;
|
||||
$hdz_staff['acm'] = 0;
|
||||
$hdz_staff['acm_id'] = null;
|
||||
} else {
|
||||
// if ($userData['role'] == 3) {
|
||||
// $hdz_staff['admin'] = 0;
|
||||
// $hdz_staff['acm'] = 1;
|
||||
// $hdz_staff['acm_id'] = $id;
|
||||
// } elseif (in_array($userData['role'], [1, 5])) {
|
||||
// $hdz_staff['admin'] = 1;
|
||||
// $hdz_staff['acm'] = 0;
|
||||
// $hdz_staff['acm_id'] = null;
|
||||
// } else {
|
||||
|
||||
return redirect()->to(base_url('/user/list'));
|
||||
}
|
||||
// return redirect()->to(base_url('/user/list'));
|
||||
// }
|
||||
|
||||
// Check if staff data exists
|
||||
$staffData = $db->table($tableName)
|
||||
// ->where('emp_code', $userData['emp_code'])
|
||||
->where('email', $userData['email'])
|
||||
->get()->getResult();
|
||||
// // Check if staff data exists
|
||||
// $staffData = $db->table($tableName)
|
||||
// // ->where('emp_code', $userData['emp_code'])
|
||||
// ->where('email', $userData['email'])
|
||||
// ->get()->getResult();
|
||||
|
||||
if (!empty($staffData)) {
|
||||
// if (!empty($staffData)) {
|
||||
|
||||
// Update existing record
|
||||
$db->table($tableName)
|
||||
// ->where('emp_code', $userData['emp_code'])
|
||||
->where('email', $userData['email'])
|
||||
->set($hdz_staff)->update();
|
||||
// // Update existing record
|
||||
// $db->table($tableName)
|
||||
// // ->where('emp_code', $userData['emp_code'])
|
||||
// ->where('email', $userData['email'])
|
||||
// ->set($hdz_staff)->update();
|
||||
|
||||
} else {
|
||||
// } else {
|
||||
|
||||
$password = '12345678'; // Default password
|
||||
$hdz_staff['password'] = password_hash($password, PASSWORD_DEFAULT);
|
||||
// $password = '12345678'; // Default password
|
||||
// $hdz_staff['password'] = password_hash($password, PASSWORD_DEFAULT);
|
||||
|
||||
// Insert new record
|
||||
$db->table($tableName)->insert($hdz_staff);
|
||||
}
|
||||
// // Insert new record
|
||||
// $db->table($tableName)->insert($hdz_staff);
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
@ -466,7 +475,29 @@ class UserController extends AdminController
|
||||
$emailToDelete = $model->where('id', $id)->first();
|
||||
$deactive = $model->where('id', $id)->set(['is_active' => 0])->update();
|
||||
|
||||
$this->bookStack->deleteUser($emailToDelete);
|
||||
// $this->bookStack->deleteUser($emailToDelete);
|
||||
if($deactive)
|
||||
{
|
||||
// $db = \Config\Database::connect();
|
||||
// $tableName = 'hdz_staff';
|
||||
|
||||
// $db->table($tableName)->insert($hdz_staff);
|
||||
|
||||
|
||||
echo json_encode(array("status" => true));
|
||||
}else{
|
||||
echo json_encode(array("status" => false));
|
||||
}
|
||||
}
|
||||
|
||||
public function activateUser($id = null)
|
||||
{
|
||||
$model = new UserModel();
|
||||
|
||||
$emailToDelete = $model->where('id', $id)->first();
|
||||
$deactive = $model->where('id', $id)->set(['is_active' => 1])->update();
|
||||
|
||||
// $this->bookStack->deleteUser($emailToDelete);
|
||||
if($deactive)
|
||||
{
|
||||
// $db = \Config\Database::connect();
|
||||
|
||||
@ -497,40 +497,52 @@ class VidalApiController extends BaseController
|
||||
}
|
||||
|
||||
// Extract claim status
|
||||
$claimData = $response['data']['data']['claims'][0];
|
||||
$tpa_claim_no = $claimData['claimNumber'] ?? '';
|
||||
$currentStatus = $claimData['status'] ?? '';
|
||||
$tpa_claim_type = $claimData['claimType'] ?? '';
|
||||
// $claimData = $response['data']['data']['claims'][0];
|
||||
$allClaimData = $response['data']['data']['claims'];
|
||||
|
||||
$currentStatus = "";
|
||||
foreach ($allClaimData as $claimData) {
|
||||
|
||||
$tpa_claim_no = $claimData['claimNumber'] ?? '';
|
||||
$currentStatus = $claimData['status'] ?? '';
|
||||
$tpa_claim_type = $claimData['claimType'] ?? '';
|
||||
$doa = !empty($claimData['doa'] ?? null) ? date('Y-m-d', strtotime(str_replace('/', '-', $claimData['doa']))) : null;
|
||||
|
||||
|
||||
// VALID STATUS LIST
|
||||
$validStatuses = [
|
||||
"In-Progress" => 5,
|
||||
"Required Information" => 4,
|
||||
"Paid" => 11,
|
||||
"Rejected" => 8,
|
||||
"Approved" => 8,
|
||||
];
|
||||
|
||||
$updateArray = [
|
||||
'tpa_claim_status' => $currentStatus,
|
||||
'tpa_claim_id' => $tpa_claim_no,
|
||||
// 'claim_number' => $tpa_claim_no, // already updated
|
||||
'updated_at' => date('Y-m-d H:i:s'),
|
||||
];
|
||||
if (isset($validStatuses[$currentStatus])) {
|
||||
$updateArray['claim_status_id'] = $validStatuses[$currentStatus];
|
||||
// VALID STATUS LIST
|
||||
$validStatuses = [
|
||||
"In-Progress" => 5,
|
||||
"Required Information" => 4,
|
||||
"Paid" => 11,
|
||||
"Rejected" => 8,
|
||||
"Approved" => 8,
|
||||
];
|
||||
|
||||
$updateArray = [
|
||||
'tpa_claim_status' => $currentStatus,
|
||||
'tpa_claim_id' => $tpa_claim_no,
|
||||
// 'claim_number' => $tpa_claim_no, // already updated
|
||||
'updated_at' => date('Y-m-d H:i:s'),
|
||||
'last_updated_by' => 'API',
|
||||
];
|
||||
if (isset($validStatuses[$currentStatus])) {
|
||||
$updateArray['claim_status_id'] = $validStatuses[$currentStatus];
|
||||
}
|
||||
if (!empty($tpa_claim_type)) {
|
||||
$updateArray['tpa_claim_type'] = $tpa_claim_type;
|
||||
}
|
||||
|
||||
if($ticket['doa'] == $doa || $ticket['claim_number'] == $tpa_claim_no){
|
||||
// UPDATE ticket_master
|
||||
$this->db->table('ticket_master')->where('id', $claimId)->update($updateArray);
|
||||
|
||||
// LOG UPDATE
|
||||
log_message('error', "VIDAL - Claim Status SUCCESS | Updated ticket ID $claimId with claim status: $currentStatus");
|
||||
}else{
|
||||
log_message('error', "VIDAL - Claim Status NOT UPDATED | TicketID={$claimId} | TicketDOA={$ticket['doa']} | ClaimDOA={$doa} | Status={$currentStatus}");
|
||||
}
|
||||
}
|
||||
if (!empty($tpa_claim_type)) {
|
||||
$updateArray['tpa_claim_type'] = $tpa_claim_type;
|
||||
}
|
||||
|
||||
|
||||
// UPDATE ticket_master
|
||||
$this->db->table('ticket_master')->where('id', $claimId)->update($updateArray);
|
||||
|
||||
// LOG UPDATE
|
||||
log_message('error', "VIDAL - Claim Status SUCCESS | Updated ticket ID $claimId with claim status: $currentStatus");
|
||||
|
||||
return ['status' => true,'message' => 'Claim status updated.','updated_status' => $currentStatus,'api_response' => $response];
|
||||
}
|
||||
@ -613,44 +625,62 @@ class VidalApiController extends BaseController
|
||||
}
|
||||
|
||||
// Extract claim status
|
||||
$claimData = $response['data']['data']['claims'][0];
|
||||
$tpa_claim_no = $claimData['claimNumber'] ?? '';
|
||||
$currentStatus = $claimData['status'] ?? '';
|
||||
$tpa_claim_type = $claimData['claimType'] ?? '';
|
||||
// $claimData = $response['data']['data']['claims'][0];
|
||||
$allClaimData = $response['data']['data']['claims'];
|
||||
|
||||
foreach ($allClaimData as $claimData) {
|
||||
|
||||
$tpa_claim_no = $claimData['claimNumber'] ?? '';
|
||||
$currentStatus = $claimData['status'] ?? '';
|
||||
$tpa_claim_type = $claimData['claimType'] ?? '';
|
||||
$doa = !empty($claimData['doa'] ?? null) ? date('Y-m-d', strtotime(str_replace('/', '-', $claimData['doa']))) : null;
|
||||
|
||||
|
||||
// VALID STATUS LIST
|
||||
$validStatuses = [
|
||||
"In-Progress" => 5,
|
||||
"Required Information" => 4,
|
||||
"Paid" => 11,
|
||||
"Rejected" => 8,
|
||||
"Approved" => 8,
|
||||
];
|
||||
// VALID STATUS LIST
|
||||
$validStatuses = [
|
||||
"In-Progress" => 5,
|
||||
"Required Information" => 4,
|
||||
"Paid" => 11,
|
||||
"Rejected" => 8,
|
||||
"Approved" => 8,
|
||||
];
|
||||
|
||||
$updateArray = [
|
||||
'tpa_claim_status' => $currentStatus,
|
||||
'tpa_claim_id' => $tpa_claim_no,
|
||||
// 'claim_number' => $tpa_claim_no, // already updated
|
||||
'updated_at' => date('Y-m-d H:i:s'),
|
||||
];
|
||||
if (isset($validStatuses[$currentStatus])) {
|
||||
$updateArray['claim_status_id'] = $validStatuses[$currentStatus];
|
||||
$updateArray = [
|
||||
'tpa_claim_status' => $currentStatus,
|
||||
// 'claim_number' => $tpa_claim_no, // already updated
|
||||
'updated_at' => date('Y-m-d H:i:s'),
|
||||
'last_updated_by' => 'API',
|
||||
];
|
||||
|
||||
if (isset($validStatuses[$currentStatus])) {
|
||||
$updateArray['claim_status_id'] = $validStatuses[$currentStatus];
|
||||
}
|
||||
if (!empty($tpa_claim_type)) {
|
||||
$updateArray['tpa_claim_type'] = $tpa_claim_type;
|
||||
}
|
||||
if (empty($ticket['tpa_claim_id']) || $ticket['tpa_claim_id'] === null) {
|
||||
$updateArray['tpa_claim_id'] = $tpa_claim_no;
|
||||
}
|
||||
if (empty($ticket['claim_number']) || $ticket['claim_number'] === null) {
|
||||
$updateArray['claim_number'] = $tpa_claim_no;
|
||||
}
|
||||
|
||||
// UPDATE ticket_master
|
||||
if($ticket['doa'] == $doa || $ticket['claim_number'] == $tpa_claim_no){
|
||||
$this->db->table('ticket_master')->where('id', $claimId)->update($updateArray);
|
||||
|
||||
// LOG UPDATE
|
||||
log_message('error', "VIDAL - Claim Status SUCCESS | Updated ticket ID $claimId with claim status: $currentStatus");
|
||||
$status_updated_count ++;
|
||||
}else{
|
||||
log_message('error', "VIDAL - Claim Status NOT UPDATED | TicketID={$claimId} | TicketDOA={$ticket['doa']} | ClaimDOA={$doa} | Status={$currentStatus}");
|
||||
}
|
||||
|
||||
}
|
||||
if (!empty($tpa_claim_type)) {
|
||||
$updateArray['tpa_claim_type'] = $tpa_claim_type;
|
||||
}
|
||||
|
||||
// UPDATE ticket_master
|
||||
$this->db->table('ticket_master')->where('id', $claimId)->update($updateArray);
|
||||
|
||||
// LOG UPDATE
|
||||
log_message('error', "VIDAL - Claim Status SUCCESS | Updated ticket ID $claimId with claim status: $currentStatus");
|
||||
|
||||
$status_updated_count ++;
|
||||
|
||||
}
|
||||
|
||||
log_message('error', "VIDAL - Claim Status ENDED | Total Tickets={".count($TicketData)."} | Status Updated={$status_updated_count} | Errors={".count($error_data)."}");
|
||||
|
||||
return $this->response->setJSON([
|
||||
'status' => true,
|
||||
'message' => 'Claim status updated.',
|
||||
|
||||
@ -3072,6 +3072,8 @@ if (!function_exists('validate_mobile_value')) {
|
||||
if (!function_exists('validate_positive_number_value')) {
|
||||
function validate_positive_number_value($value)
|
||||
{
|
||||
$value = cleanNumber($value);
|
||||
|
||||
if ($value === "" || $value === null) {
|
||||
return ['status' => true, 'error' => null];
|
||||
}
|
||||
|
||||
@ -1382,3 +1382,16 @@ if (! function_exists('add_google_calender_event')) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('cleanNumber')) {
|
||||
|
||||
function cleanNumber($value){
|
||||
if (empty($value)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$value = str_replace(',', '', trim($value));
|
||||
return is_numeric($value) ? (float)$value : 0;
|
||||
}
|
||||
|
||||
}
|
||||
@ -81,6 +81,53 @@ class GoogleSheetLib
|
||||
return $response->getBody()->getContents();
|
||||
}
|
||||
|
||||
/* ================= LIST FOLDER SHEETS ===================== */
|
||||
|
||||
/**
|
||||
* Lists files inside a Google Drive folder and returns JSON with
|
||||
* filename and sheet id (Drive file id / spreadsheet id).
|
||||
*/
|
||||
public function listFolderSheetIdsAsJson(string $folderId = ''): string
|
||||
{
|
||||
if ($folderId === '') {
|
||||
$folderId = '19uySI-PSFQfFpZvMSnBtirXbwfCTXFBL';
|
||||
}
|
||||
|
||||
$results = [];
|
||||
$pageToken = null;
|
||||
$folderIdEsc = str_replace("'", "\\'", $folderId);
|
||||
$query = "'{$folderIdEsc}' in parents and trashed = false";
|
||||
$listParams = [
|
||||
'q' => $query,
|
||||
'fields' => 'nextPageToken, files(id,name,mimeType)',
|
||||
'pageSize' => 1000,
|
||||
'supportsAllDrives' => true,
|
||||
'includeItemsFromAllDrives' => true,
|
||||
];
|
||||
|
||||
do {
|
||||
if ($pageToken) {
|
||||
$listParams['pageToken'] = $pageToken;
|
||||
} else {
|
||||
unset($listParams['pageToken']);
|
||||
}
|
||||
|
||||
$response = $this->drive->files->listFiles($listParams);
|
||||
|
||||
foreach (($response->getFiles() ?? []) as $file) {
|
||||
// "sheetId" here is the Drive file id (spreadsheetId for Google Sheets)
|
||||
$results[] = [
|
||||
'name' => (string) $file->getName(),
|
||||
'sheetId' => (string) $file->getId(),
|
||||
];
|
||||
}
|
||||
|
||||
$pageToken = $response->getNextPageToken();
|
||||
} while (! empty($pageToken));
|
||||
|
||||
return json_encode($results, JSON_UNESCAPED_SLASHES);
|
||||
}
|
||||
|
||||
/* ================= COPY TEMPLATE ================= */
|
||||
|
||||
public function copyTemplate(string $templateId, string $name, string $folderId): string
|
||||
@ -100,6 +147,184 @@ class GoogleSheetLib
|
||||
return $file->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find/replace text across all sheets in the spreadsheet (batchUpdate).
|
||||
*
|
||||
* @param array<string, string> $replacements placeholder => replacement (empty string allowed)
|
||||
*/
|
||||
public function findAndReplaceAllSheets(string $spreadsheetId, array $replacements): void
|
||||
{
|
||||
if ($replacements === []) {
|
||||
return;
|
||||
}
|
||||
|
||||
$requests = [];
|
||||
|
||||
foreach ($replacements as $find => $replacement) {
|
||||
if ($find === '') {
|
||||
continue;
|
||||
}
|
||||
$requests[] = [
|
||||
'findReplace' => [
|
||||
'find' => (string) $find,
|
||||
'replacement' => (string) $replacement,
|
||||
'allSheets' => true,
|
||||
'matchCase' => false,
|
||||
'matchEntireCell' => false,
|
||||
'includeFormulas' => true,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
if ($requests === []) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->sheets->spreadsheets->batchUpdate(
|
||||
$spreadsheetId,
|
||||
new Google_Service_Sheets_BatchUpdateSpreadsheetRequest([
|
||||
'requests' => $requests,
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Quote a sheet title for A1 notation ranges.
|
||||
*/
|
||||
private function quoteSheetTitleForRange(string $title): string
|
||||
{
|
||||
return "'" . str_replace("'", "''", $title) . "'";
|
||||
}
|
||||
|
||||
/**
|
||||
* 1-based column index to Excel column letters (A, B, …, Z, AA, …).
|
||||
*/
|
||||
private function columnNumberToLetters(int $n): string
|
||||
{
|
||||
$s = '';
|
||||
|
||||
while ($n > 0) {
|
||||
$m = ($n - 1) % 26;
|
||||
$s = chr(65 + $m) . $s;
|
||||
$n = intdiv($n - 1, 26);
|
||||
}
|
||||
|
||||
return $s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sheet title from an A1 range like 'RFQ Page'!A1:ZZ3000 or Sheet1!A1.
|
||||
*/
|
||||
private function parseSheetTitleFromBatchRange(string $rangeStr): string
|
||||
{
|
||||
$parts = explode('!', $rangeStr, 2);
|
||||
$sheet = $parts[0] ?? '';
|
||||
|
||||
if ($sheet === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (str_starts_with($sheet, "'")) {
|
||||
return str_replace("''", "'", trim($sheet, "'"));
|
||||
}
|
||||
|
||||
return $sheet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locate a placeholder in any sheet and write a 2D table starting at that cell.
|
||||
* First row of $tableValues is treated as headers.
|
||||
*
|
||||
* @param list<list<string|int|float>> $tableValues
|
||||
*/
|
||||
public function writeTableAtPlaceholder(string $spreadsheetId, string $placeholder, array $tableValues): bool
|
||||
{
|
||||
if ($placeholder === '' || $tableValues === []) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$meta = $this->sheets->spreadsheets->get(
|
||||
$spreadsheetId,
|
||||
['fields' => 'sheets(properties(title))']
|
||||
);
|
||||
|
||||
$titles = [];
|
||||
|
||||
foreach ($meta->getSheets() ?? [] as $sheet) {
|
||||
$props = $sheet->getProperties();
|
||||
|
||||
if ($props !== null) {
|
||||
$titles[] = (string) $props->getTitle();
|
||||
}
|
||||
}
|
||||
|
||||
if ($titles === []) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$ranges = [];
|
||||
|
||||
// Use row-based range so API returns row 1 == index 0 (leading empty rows are not dropped).
|
||||
foreach ($titles as $title) {
|
||||
$ranges[] = $this->quoteSheetTitleForRange($title) . '!1:3000';
|
||||
}
|
||||
|
||||
$batch = $this->sheets->spreadsheets_values->batchGet($spreadsheetId, [
|
||||
'ranges' => $ranges,
|
||||
'majorDimension' => 'ROWS',
|
||||
]);
|
||||
|
||||
$valueRanges = $batch->getValueRanges() ?? [];
|
||||
|
||||
foreach ($valueRanges as $vr) {
|
||||
$title = $this->parseSheetTitleFromBatchRange((string) ($vr->getRange() ?? ''));
|
||||
|
||||
if ($title === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$values = $vr->getValues() ?? [];
|
||||
|
||||
foreach ($values as $r => $row) {
|
||||
if (! is_array($row)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($row as $c => $cell) {
|
||||
if (! is_string($cell) && ! is_numeric($cell)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$text = (string) $cell;
|
||||
|
||||
if (! str_contains($text, $placeholder)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$startCol = $this->columnNumberToLetters((int) $c + 1);
|
||||
$startRow = (int) $r + 1;
|
||||
$q = $this->quoteSheetTitleForRange($title);
|
||||
$range = "{$q}!{$startCol}{$startRow}";
|
||||
|
||||
$body = new Google_Service_Sheets_ValueRange([
|
||||
'values' => $tableValues,
|
||||
]);
|
||||
|
||||
$this->sheets->spreadsheets_values->update(
|
||||
$spreadsheetId,
|
||||
$range,
|
||||
$body,
|
||||
['valueInputOption' => 'USER_ENTERED']
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ================= PERMISSIONS ================= */
|
||||
|
||||
public function applyPermissions(string $fileId, array $permissions)
|
||||
@ -233,6 +458,18 @@ class GoogleSheetLib
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ================= FILE NAME ================= */
|
||||
|
||||
public function getFileName(string $fileId): string
|
||||
{
|
||||
$file = $this->drive->files->get($fileId, [
|
||||
'supportsAllDrives' => true,
|
||||
'fields' => 'name',
|
||||
]);
|
||||
|
||||
return $file->getName() ?? '';
|
||||
}
|
||||
|
||||
/* ================= URL ================= */
|
||||
|
||||
public function sheetUrl(string $sheetId): string
|
||||
|
||||
@ -368,7 +368,8 @@ abstract class BaseTpaClaimImportService
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
return 61;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -59,6 +59,7 @@ class ClientPolicyModel extends Model
|
||||
"is_from_lead",
|
||||
"wellness_plan_id",
|
||||
"wellness_vendor_id",
|
||||
"non_eb_rack_rate_files",
|
||||
];
|
||||
|
||||
// Callbacks
|
||||
@ -135,6 +136,8 @@ class ClientPolicyModel extends Model
|
||||
->select('tpa_branch.branch_name as tpa_branch_name, tpa_branch.branch_code as tpa_branch_code')
|
||||
->select('policy_type.policy_type as policy_type_name')
|
||||
->select('client_branch.branch_name as branch_name')
|
||||
->select('policy_type.allocg as allocg')
|
||||
->select('leads.misc as lead_misc')
|
||||
->join('insurers', 'insurers.id = client_policy.insurer_id')
|
||||
->join('insurer_branch', 'client_policy.insurer_branch_id = insurer_branch.id')
|
||||
->join('tpa', 'tpa.id = client_policy.tpa_id', 'left')
|
||||
|
||||
@ -374,7 +374,8 @@ class EmployeePolicyModel extends Model
|
||||
public function getEmployeePolicyForEcard($policy_id = 0)
|
||||
{
|
||||
$result = $this->select([
|
||||
'employee_polices.id'
|
||||
'employee_polices.id',
|
||||
'emp.relationship',
|
||||
])
|
||||
->join('employees emp', 'employee_polices.employee_id = emp.id');
|
||||
if ($policy_id !=0 && !empty($policy_id)) {
|
||||
@ -515,11 +516,11 @@ class EmployeePolicyModel extends Model
|
||||
";
|
||||
|
||||
if($insurer_or_tpa == 'insurer'){
|
||||
$sql .= "AND (employee_polices.uhid IS NULL OR employee_polices.uhid <> '')";
|
||||
$sql .= "AND employee_polices.uhid IS NULL";
|
||||
}
|
||||
|
||||
if($insurer_or_tpa == 'tpa'){
|
||||
$sql .= "AND (employee_polices.tpa_id IS NULL OR employee_polices.tpa_id <> '')";
|
||||
$sql .= "AND employee_polices.tpa_id IS NULL";
|
||||
}
|
||||
|
||||
$binds = ["datas" => $datas,"event" => $event
|
||||
|
||||
@ -109,6 +109,7 @@ class LeadsModel extends Model
|
||||
'acm_id',
|
||||
'policy_with_correction',
|
||||
'agreed_percentage', 'misc',
|
||||
'rfq_qcr_viewers',
|
||||
];
|
||||
|
||||
// Callbacks
|
||||
|
||||
@ -1112,7 +1112,7 @@
|
||||
|
||||
// Default to Last 30 Days if No Filters Are Applied
|
||||
if (empty($client_id) && empty($insurer_id) && empty($policy_type_id) && empty($date_type) && empty($issuer) && empty($where)) {
|
||||
$fromDate = date('Y-m-d', strtotime('-90 days'));
|
||||
$fromDate = date('Y-m-d', strtotime('-30 days'));
|
||||
$toDate = date('Y-m-d 23:59:59');
|
||||
|
||||
$builder->where('policy_transaction.created_at >=', $fromDate)
|
||||
@ -3785,7 +3785,7 @@
|
||||
$totalBilled[$key] = ($totalBilled[$key] ?? 0) + (float) ($row['billed_amt'] ?? 0);
|
||||
|
||||
// Store total_irda_amt once
|
||||
if (!isset($totalIrdaMap[$key]) && ($row['total_irda_amt'] ?? 0) > 0) {
|
||||
if (!isset($totalIrdaMap[$key])) {
|
||||
$totalIrdaMap[$key] = (float) $row['total_irda_amt'];
|
||||
}
|
||||
|
||||
@ -3802,11 +3802,24 @@
|
||||
foreach ($result as $row) {
|
||||
$ptId = $row['pt_id'].'-'.$row['insurer_id'];
|
||||
if (!isset($ptSeen[$ptId])) {
|
||||
|
||||
$totalIrdaVal = $totalIrdaMap[$ptId] ?? 0;
|
||||
$totalBilledVal = $totalBilled[$ptId] ?? 0;
|
||||
$addMinus = false;
|
||||
|
||||
if($totalIrdaVal < 0){
|
||||
$totalIrdaVal = abs($totalIrdaVal);
|
||||
$totalBilledVal = abs($totalBilledVal);
|
||||
$addMinus = true;
|
||||
}
|
||||
|
||||
// First entry → set unbilled amount
|
||||
$row['unbilled_amount'] = round(
|
||||
(float) (($totalIrdaMap[$ptId] ?? 0) - ($totalBilled[$ptId] ?? 0)),
|
||||
2
|
||||
);
|
||||
$row['unbilled_amount'] = round((float) ($totalIrdaVal - $totalBilledVal),2 );
|
||||
|
||||
if($addMinus){
|
||||
$row['unbilled_amount'] = ($row['unbilled_amount'] * -1);
|
||||
}
|
||||
|
||||
$ptSeen[$ptId] = true;
|
||||
} else {
|
||||
// Other entries → zero
|
||||
|
||||
@ -65,13 +65,25 @@ class SalesActivityModel extends Model
|
||||
*/
|
||||
public function getActivitiesByLead($leadId, $status = null)
|
||||
{
|
||||
// Convert the INT id to a string, then wrap it in JSON quotes to match ["5", "11"]
|
||||
$subQuery = "(SELECT GROUP_CONCAT(up2.first_name SEPARATOR ', ')
|
||||
FROM user_profiles up2
|
||||
WHERE JSON_CONTAINS(sales_activities.additional_assigned_ids, JSON_QUOTE(CAST(up2.id AS CHAR)))
|
||||
) as additional_assigned_names";
|
||||
$subQuery = "(
|
||||
SELECT GROUP_CONCAT(up2.first_name SEPARATOR ', ')
|
||||
FROM user_profiles up2
|
||||
WHERE sales_activities.additional_assigned_ids IS NOT NULL
|
||||
AND sales_activities.additional_assigned_ids != ''
|
||||
AND sales_activities.additional_assigned_ids != '[]'
|
||||
AND JSON_VALID(sales_activities.additional_assigned_ids)
|
||||
AND JSON_CONTAINS(
|
||||
sales_activities.additional_assigned_ids,
|
||||
JSON_QUOTE(CAST(up2.id AS CHAR))
|
||||
)
|
||||
) as additional_assigned_names";
|
||||
|
||||
$builder = $this->select("sales_activities.*, up1.first_name as assigned_to_name, $subQuery")
|
||||
$builder = $this->db->table('sales_activities')
|
||||
->select("
|
||||
sales_activities.*,
|
||||
up1.first_name as assigned_to_name,
|
||||
{$subQuery}
|
||||
")
|
||||
->join('user_profiles as up1', 'up1.id = sales_activities.assigned_to', 'left')
|
||||
->where('sales_activities.lead_id', $leadId);
|
||||
|
||||
@ -79,7 +91,10 @@ class SalesActivityModel extends Model
|
||||
$builder->where('sales_activities.status', $status);
|
||||
}
|
||||
|
||||
return $builder->orderBy('sales_activities.scheduled_date', 'DESC')->findAll();
|
||||
return $builder
|
||||
->orderBy('sales_activities.scheduled_date', 'DESC')
|
||||
->get()
|
||||
->getResultArray();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -182,10 +197,14 @@ class SalesActivityModel extends Model
|
||||
$builder->whereIn('sales_activities.assigned_to', $assignedToIds);
|
||||
}
|
||||
|
||||
if (!empty($filters['search'])) {
|
||||
$builder->groupStart()
|
||||
if (!empty($filters['search'])) {
|
||||
// $builder->groupStart()
|
||||
$this->groupStart()
|
||||
->like('sales_actual_leads.company_name', $filters['search'])
|
||||
->orLike('sales_activities.status', $filters['search'])
|
||||
->orLike('sales_activities.activity_type', $filters['search'])
|
||||
->orLike('up1.first_name', $filters['search'])
|
||||
->orLike('up2.first_name', $filters['search'])
|
||||
->groupEnd();
|
||||
}
|
||||
|
||||
|
||||
@ -18,6 +18,7 @@ class SalesActualLeadModel extends Model
|
||||
protected $protectFields = true;
|
||||
protected $allowedFields = [
|
||||
'company_name',
|
||||
'client_id',
|
||||
'email',
|
||||
'phone',
|
||||
'address',
|
||||
@ -37,7 +38,7 @@ class SalesActualLeadModel extends Model
|
||||
|
||||
// Validation
|
||||
protected $validationRules = [
|
||||
'company_name' => 'required|alpha_space|min_length[2]|max_length[255]',
|
||||
'company_name' => 'required|regex_match[/^[a-zA-Z0-9\s_-]+$/]|min_length[2]|max_length[255]',
|
||||
'email' => 'required_without[phone]|permit_empty|valid_email|max_length[255]',
|
||||
'phone' => 'required_without[email]|permit_empty|regex_match[/^[0-9+\s]+$/]|min_length[10]|max_length[20]',
|
||||
'status' => 'in_list[New,Potential,Prospects,Not a Prospects]',
|
||||
@ -48,7 +49,8 @@ class SalesActualLeadModel extends Model
|
||||
protected $validationMessages = [
|
||||
'company_name' => [
|
||||
'required' => 'Company Name is Missing',
|
||||
'alpha_space' => 'Company Name must contain only letters and spaces',
|
||||
// 'alpha_space' => 'Company Name must contain only letters and spaces',
|
||||
'regex_match' => 'Company Name can only contain letters, numbers, spaces, hyphens and underscores.',
|
||||
'min_length' => 'Company Name must be at least 2 characters long',
|
||||
'max_length' => 'Company Name must be at most 255 characters long'
|
||||
],
|
||||
@ -59,7 +61,7 @@ class SalesActualLeadModel extends Model
|
||||
],
|
||||
'phone' => [
|
||||
'required_without' => 'Either Email or Mobile Number is Needed.',
|
||||
'regex_match' => 'Phone number can contain only digits, + and spaces.',
|
||||
'regex_match' => 'Mobile number can contain only digits, + and spaces.',
|
||||
'min_length' => 'Mobile Number must be at least 10 characters long',
|
||||
'max_length' => 'Mobile Number must be at most 20 characters long',
|
||||
],
|
||||
@ -77,8 +79,9 @@ class SalesActualLeadModel extends Model
|
||||
*/
|
||||
public function getLeadWithUser($leadId)
|
||||
{
|
||||
return $this->select('sales_actual_leads.*, user_profiles.first_name as assigned_to_name, user_profiles.email as assigned_to_email')
|
||||
return $this->select('sales_actual_leads.*, clients.short_name, user_profiles.first_name as assigned_to_name, user_profiles.email as assigned_to_email')
|
||||
->join('user_profiles', 'user_profiles.id = sales_actual_leads.assigned_to', 'left')
|
||||
->join('clients', 'clients.id = sales_actual_leads.client_id', 'left')
|
||||
->where('sales_actual_leads.lead_id', $leadId)
|
||||
->first();
|
||||
}
|
||||
@ -88,8 +91,9 @@ class SalesActualLeadModel extends Model
|
||||
*/
|
||||
public function getLeadsWithFilters($filters = [], $limit = 10, $offset = 0)
|
||||
{
|
||||
$this->select('sales_actual_leads.*, user_profiles.first_name as assigned_to_name, user_profiles.email as assigned_to_email')
|
||||
->join('user_profiles', 'user_profiles.id = sales_actual_leads.assigned_to', 'left');
|
||||
$this->select('sales_actual_leads.*,clients.short_name, user_profiles.first_name as assigned_to_name, user_profiles.email as assigned_to_email')
|
||||
->join('user_profiles', 'user_profiles.id = sales_actual_leads.assigned_to', 'left')
|
||||
->join('clients', 'clients.id = sales_actual_leads.client_id', 'left');
|
||||
|
||||
if (!empty($filters['status'])) {
|
||||
$this->where('sales_actual_leads.status', $filters['status']);
|
||||
|
||||
@ -94,6 +94,7 @@ class TicketMasterModel extends Model
|
||||
'tpa_claim_type',
|
||||
'tpa_ailments',
|
||||
'claim_dump_ref_id',
|
||||
'last_updated_by',
|
||||
|
||||
];
|
||||
|
||||
|
||||
@ -25,7 +25,9 @@ class TpaApiDataModel extends Model
|
||||
'age',
|
||||
'is_active',
|
||||
'desc',
|
||||
'created_by'
|
||||
'created_by',
|
||||
'si',
|
||||
'doj'
|
||||
];
|
||||
|
||||
// protected $useTimestamps = true;
|
||||
|
||||
@ -185,7 +185,8 @@
|
||||
|
||||
<?php if(in_array(get_role_id(), [1,2,3,4,5]) || (in_array(ENROLLMENT_TEAM_ID, user_team()) || in_array(CLAIMS_TEAM_ID, user_team()) || in_array(MANAGEMENT_TEAM_ID, user_team()) || in_array(FINANCE_TEAM_ID, user_team()) || in_array(BUSINESS_TEAM_ID, user_team()))) { ?>
|
||||
|
||||
<div class="row" id="client_add" >
|
||||
<!-- <div class="row" id="client_add"> -->
|
||||
<div class="row" id="client_add" style="margin-top:12px!important">
|
||||
<div class="col-12">
|
||||
<!-- <div class="card"> -->
|
||||
<div class="card-body">
|
||||
|
||||
@ -355,7 +355,7 @@ table.dataTable tbody td {
|
||||
<div class="row" id="List-page">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body maincard">
|
||||
<div class="card-body">
|
||||
<!-- <div class="row" style="margin-bottom:1rem;">
|
||||
<div class="col-6" style="align-self: center;">
|
||||
<h4 style="position: relative;">Users</h4>
|
||||
@ -388,8 +388,18 @@ table.dataTable tbody td {
|
||||
<div class="btn-group dropdown" style="position: relative !important;left:0px !important;top:0px !important;">
|
||||
<a href="javascript: void(0);" class="dropdown-toggle arrow-none btn btn-light btn-sm" data-toggle="dropdown" aria-expanded="false"><i class="mdi mdi-dots-horizontal"></i></a>
|
||||
<div class="dropdown-menu dropdown-menu-right" style="cursor: pointer;">
|
||||
<a data-toggle="modal" data-target="#con-close-modal" class="dropdown-item btnEdit" data-id="<?php echo $row->id; ?>"><i data-id="<?php echo $row->id; ?>" class="mdi mdi-pencil mr-2 text-muted font-18 vertical-middle btnEdit"></i>Edit</a>
|
||||
<a class="dropdown-item btnDelete" data-id="<?php echo $row->id; ?>"><i data-id="<?php echo $row->id; ?>" class="mdi mdi-delete mr-2 text-muted font-18 vertical-middle btnDelete"></i>Delete</a>
|
||||
<a class="dropdown-item btnEdit" data-id="<?php echo $row->id; ?>"><i data-id="<?php echo $row->id; ?>" class="mdi mdi-pencil mr-2 text-muted font-18 vertical-middle btnEdit"></i>Edit</a>
|
||||
<?php if (in_array(get_role_id(), [1, 5])): ?>
|
||||
<?php if ($row->is_active == 1): ?>
|
||||
<a class="dropdown-item btnDelete" data-id="<?php echo $row->id; ?>">
|
||||
<i class="mdi mdi-delete mr-2 text-muted font-18 vertical-middle"></i>Delete
|
||||
</a>
|
||||
<?php else: ?>
|
||||
<a class="dropdown-item btnActivate" data-id="<?php echo $row->id; ?>">
|
||||
<i class="mdi mdi-account-check mr-2 text-muted font-18 vertical-middle"></i>Activate
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
@ -493,7 +503,7 @@ table.dataTable tbody td {
|
||||
<select class="form-control" id="nhance_branch_id" name="nhance_branch_id" required>
|
||||
<option value="">Select Nhance Branch</option>
|
||||
<?php foreach($NHanceBranchData as $value) { ?>
|
||||
<option value="<?= $value['id'] ?>"><?= $value['branch_name'] ?></option>
|
||||
<option value="<?= $value['id'] ?>" <?= old('nhance_branch_id') == $value['id'] ? 'selected' : '' ?>><?= $value['branch_name'] ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
@ -502,7 +512,7 @@ table.dataTable tbody td {
|
||||
<select class="form-control" id="rm_id" name="rm_id" required>
|
||||
<option value="">Select Reporting Manager</option>
|
||||
<?php foreach($user_data as $value) { ?>
|
||||
<option value="<?= $value['id'] ?>"><?= $value['first_name'] ?></option>
|
||||
<option value="<?= $value['id'] ?>" <?= old('rm_id') == $value['id'] ? 'selected' : '' ?>><?= $value['first_name'] ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
@ -511,11 +521,11 @@ table.dataTable tbody td {
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-6">
|
||||
<label for="emp_code">Employee Code<span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="emp_code" placeholder="Enter Code" name="emp_code" onchange="validateInput(this, 'user_profiles', 'emp_code', 'btnSubmit')" required>
|
||||
<input type="text" class="form-control" id="emp_code" placeholder="Enter Code" name="emp_code" onchange="validateInput(this, 'user_profiles', 'emp_code', 'btnSubmit')" required value="<?= old('emp_code') ?>">
|
||||
</div>
|
||||
<div class="form-group col-md-6">
|
||||
<label for="first_name">Name<span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="first_name" placeholder="Enter Name" name="first_name" required>
|
||||
<input type="text" class="form-control" id="first_name" placeholder="Enter Name" name="first_name" required value="<?= old('first_name') ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -523,19 +533,19 @@ table.dataTable tbody td {
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-6">
|
||||
<label for="email">Email<span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="email" placeholder="Enter Email" name="email" data-parsley-trigger="change" data-parsley-type="email" onchange="validateInput(this, 'user_profiles', 'email', 'btnSubmit')" required>
|
||||
<input type="text" class="form-control" id="email" placeholder="Enter Email" name="email" data-parsley-trigger="change" data-parsley-type="email" onchange="validateInput(this, 'user_profiles', 'email', 'btnSubmit')" required value="<?= old('email') ?>">
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-6">
|
||||
<label for="mobile">Mobile Number<span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="mobile" placeholder="Enter Mobile Number ( starting with 6, 7, 8, or 9 only. )" name="mobile" onkeypress = "return onlyNumbers(event)" maxlength="10" minlength="10" pattern="^[6-9]\d{9}$" data-parsley-type-message="Please enter a valid 10-digit mobile number starting with 6, 7, 8, or 9." data-parsley-required-message="Please enter a valid 10-digit mobile number starting with 6, 7, 8, or 9." required>
|
||||
<input type="text" class="form-control" id="mobile" placeholder="Enter Mobile Number ( starting with 6, 7, 8, or 9 only. )" name="mobile" onkeypress="return onlyNumbers(event)" onchange="validateInput(this, 'user_profiles', 'mobile', 'btnSubmit')" maxlength="10" minlength="10" pattern="^[6-9]\d{9}$" data-parsley-type-message="Please enter a valid 10-digit mobile number starting with 6, 7, 8, or 9." data-parsley-required-message="Please enter a valid 10-digit mobile number starting with 6, 7, 8, or 9." required value="<?= old('mobile') ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-6">
|
||||
<label for="emp_code">User Role<span class="text-danger">*</span>
|
||||
<label for="role">User Role<span class="text-danger">*</span>
|
||||
<span class="tooltip-trigger mdi mdi-information-outline" id="roleAccessMatrixTrigger"></span>
|
||||
</label>
|
||||
<select class="form-control" id="role" name="role" required>
|
||||
@ -557,7 +567,7 @@ table.dataTable tbody td {
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-6">
|
||||
<label for="profile">User Team<span class="text-danger">*</span>
|
||||
<label for="team">User Team<span class="text-danger">*</span>
|
||||
<span class="tooltip-trigger mdi mdi-information-outline" id="accessMatrixTrigger"></span>
|
||||
</label>
|
||||
<select hidden class="form-control" id="team" name="team[]" multiple required>
|
||||
@ -891,6 +901,21 @@ table.dataTable tbody td {
|
||||
<?php foreach (session()->getFlashdata('errors') as $error): ?>
|
||||
toastr.error("<?= addslashes($error) ?>", "Validation Error");
|
||||
<?php endforeach; ?>
|
||||
|
||||
var errorModal = new bootstrap.Modal(document.getElementById('con-close-modal'));
|
||||
errorModal.show();
|
||||
|
||||
<?php if(old('PrimaryKey')): ?>
|
||||
$('#UserForm').attr('action', '<?= base_url('/user/edit') ?>');
|
||||
$('#UserId').val('<?= old('PrimaryKey') ?>');
|
||||
$('#btnSubmit').html('Update');
|
||||
$('.modal-title').text('Update User');
|
||||
$('#email').attr('data-original', '<?= old('email') ?>');
|
||||
$('#mobile').attr('data-original', '<?= old('mobile') ?>');
|
||||
$('#emp_code').attr('data-original', '<?= old('emp_code') ?>');
|
||||
<?php else: ?>
|
||||
$('#UserForm').attr('action', '<?= base_url('/user/create') ?>');
|
||||
<?php endif; ?>
|
||||
});
|
||||
</script>
|
||||
<?php endif; ?>
|
||||
@ -1086,10 +1111,11 @@ table.dataTable tbody td {
|
||||
$('.close').click(function(){
|
||||
$('#UserId').val('');
|
||||
$('#first_name').val('');
|
||||
$('#email').val('');
|
||||
$('#mobile').val('');
|
||||
$('#emp_code').val('');
|
||||
$('#email').val('').removeAttr('data-original');
|
||||
$('#mobile').val('').removeAttr('data-original');
|
||||
$('#emp_code').val('').removeAttr('data-original');
|
||||
$('#role').val('')
|
||||
$('#btnSubmit').prop('disabled', false);
|
||||
})
|
||||
|
||||
// $('body').on('click', '#btnUsers', function () {
|
||||
@ -1103,83 +1129,74 @@ table.dataTable tbody td {
|
||||
// $(this).addClass('active');
|
||||
// loadPartner();
|
||||
// });
|
||||
$('body').on('click', '.btnEdit', function () {
|
||||
|
||||
$('body').on('click', '.btnEdit', function () {
|
||||
var user_id = $(this).attr('data-id');
|
||||
$.ajax({
|
||||
url: '<?php echo base_url('user/getuser/');?>'+user_id,
|
||||
type: "GET",
|
||||
dataType: 'json',
|
||||
success: function (res) {
|
||||
console.log(":():",res);
|
||||
console.log(":():",res.data.nhance_branch_id);
|
||||
$('#updateModal').modal('show');
|
||||
$('#role').val('')
|
||||
$('#UserForm').attr('action', '<?php echo base_url('user/edit');?>');
|
||||
$('#UserId').val(res.data.id);
|
||||
$('#first_name').val(res.data.first_name);
|
||||
$('#last_name').val(res.data.last_name);
|
||||
$('#email').val(res.data.email);
|
||||
$('#mobile').val(res.data.mobile);
|
||||
$('#emp_code').val(res.data.emp_code);
|
||||
$('#role option[value="' + res.data.role + '"]').prop('selected', true);
|
||||
$('#rm_id').val(res.data.rm_id).select2(); // ✅ correct trigger
|
||||
if (res.data.nhance_branch_id !== null &&
|
||||
res.data.nhance_branch_id !== "" &&
|
||||
res.data.nhance_branch_id !== 0) {
|
||||
$('#nhance_branch_id')
|
||||
.val(res.data.nhance_branch_id)
|
||||
.trigger('change.select2');
|
||||
} else {
|
||||
$('#nhance_branch_id')
|
||||
.val(null) // ✅ MUST be null
|
||||
.trigger('change.select2'); // ✅ correct trigger
|
||||
}
|
||||
$('#UserForm').parsley().reset();
|
||||
|
||||
$('#btnSubmit').html('Update');
|
||||
var user_id = $(this).attr('data-id');
|
||||
|
||||
$.each(res.userTeamData, function(index, item) {
|
||||
$('#team option[value="' + item.team_id + '"]').prop('selected', true);
|
||||
$('#team').multiselect('refresh');
|
||||
});
|
||||
$.ajax({
|
||||
url: '<?php echo base_url('user/getuser/');?>' + user_id,
|
||||
type: "GET",
|
||||
dataType: 'json',
|
||||
success: function (res) {
|
||||
// Set form action to edit
|
||||
$('#UserForm').attr('action', '<?php echo base_url('user/edit');?>');
|
||||
$('#UserId').val(res.data.id);
|
||||
$('#first_name').val(res.data.first_name);
|
||||
$('#email').val(res.data.email).attr('data-original', res.data.email);
|
||||
$('#mobile').val(res.data.mobile).attr('data-original', res.data.mobile);
|
||||
$('#emp_code').val(res.data.emp_code).attr('data-original', res.data.emp_code);
|
||||
$('#role').val(res.data.role).trigger('change');
|
||||
$('#rm_id').val(res.data.rm_id).trigger('change.select2');
|
||||
|
||||
},
|
||||
error: function (xhr, status, error) {
|
||||
console.error("Error Details:");
|
||||
console.error("Status Code:", xhr.status);
|
||||
console.error("Status Text:", xhr.statusText);
|
||||
console.error("Response Text:", xhr.responseText);
|
||||
console.error("Ready State:", xhr.readyState);
|
||||
console.error("Response Headers:", xhr.getAllResponseHeaders());
|
||||
console.error("Error Thrown:", error);
|
||||
console.error("Status:", status);
|
||||
if (xhr.status === 400) {
|
||||
let response = JSON.parse(xhr.responseText);
|
||||
let errorMessages = "";
|
||||
let seenMessages = []; // Array to store unique messages
|
||||
if (response.errors) {
|
||||
$.each(response.errors, function (field, message) {
|
||||
if (!seenMessages.includes(message)) {
|
||||
errorMessages += `• ${message}<br>`;
|
||||
seenMessages.push(message); // Mark this message as "seen"
|
||||
}
|
||||
});
|
||||
toastr.error(errorMessages, 'Validation Error', { "allowHtml": true });
|
||||
} else {
|
||||
toastr.warning(response.message || 'Validation failed', 'Warning');
|
||||
}
|
||||
} else if (xhr.status === 403) {
|
||||
let response = JSON.parse(xhr.responseText);
|
||||
toastr.error(response.message, 'Security Policy');
|
||||
} else if (xhr.status === 500) {
|
||||
toastr.error('Something went wrong . Please try again later.', 'Server Error');
|
||||
} else {
|
||||
toastr.error('An unexpected error occurred. Please try again later.', 'Error');
|
||||
}
|
||||
if (res.data.nhance_branch_id) {
|
||||
$('#nhance_branch_id').val(res.data.nhance_branch_id).trigger('change.select2');
|
||||
} else {
|
||||
$('#nhance_branch_id').val(null).trigger('change.select2');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// Reset team first, then set selected
|
||||
$('#team').multiselect('deselectAll', false);
|
||||
$.each(res.userTeamData, function(index, item) {
|
||||
$('#team option[value="' + item.team_id + '"]').prop('selected', true);
|
||||
});
|
||||
$('#team').multiselect('refresh');
|
||||
|
||||
$('.modal-title').text('Update User');
|
||||
$('#btnSubmit').html('Update');
|
||||
|
||||
// ✅ Open modal AFTER data is loaded
|
||||
var myModal = new bootstrap.Modal(document.getElementById('con-close-modal'));
|
||||
myModal.show();
|
||||
},
|
||||
error: function (xhr) {
|
||||
if (xhr.status === 400) {
|
||||
let response = JSON.parse(xhr.responseText);
|
||||
let errorMessages = "";
|
||||
let seenMessages = [];
|
||||
if (response.errors) {
|
||||
$.each(response.errors, function (field, message) {
|
||||
if (!seenMessages.includes(message)) {
|
||||
errorMessages += `• ${message}<br>`;
|
||||
seenMessages.push(message);
|
||||
}
|
||||
});
|
||||
toastr.error(errorMessages, 'Validation Error', { "allowHtml": true });
|
||||
} else {
|
||||
toastr.warning(response.message || 'Validation failed', 'Warning');
|
||||
}
|
||||
} else if (xhr.status === 403) {
|
||||
let response = JSON.parse(xhr.responseText);
|
||||
toastr.error(response.message, 'Security Policy');
|
||||
} else if (xhr.status === 500) {
|
||||
toastr.error('Something went wrong. Please try again later.', 'Server Error');
|
||||
} else {
|
||||
toastr.error('An unexpected error occurred.', 'Error');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('body').on('click', '.btnDelete', function () {
|
||||
|
||||
@ -1201,7 +1218,29 @@ table.dataTable tbody td {
|
||||
})
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
$('body').on('click', '.btnActivate', function () {
|
||||
|
||||
Swal.fire({
|
||||
title: "Are you sure?",
|
||||
text: "You need to active this user",
|
||||
icon: "info",
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "Yes",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
|
||||
var student_id = $(this).attr('data-id');
|
||||
$.get('<?php echo base_url('user/activateUser/');?>'+student_id, function (data) {
|
||||
console.log(data);
|
||||
toastr.success('User actived successfully', 'Success');
|
||||
window.location.reload()
|
||||
})
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('body').on('click', '.btnPartnerEdit', function () {
|
||||
let user = JSON.parse($(this).attr('data-obj')); // ✅ convert back to object
|
||||
@ -1296,13 +1335,96 @@ table.dataTable tbody td {
|
||||
return false;
|
||||
}
|
||||
|
||||
var form = document.getElementById("UserForm");
|
||||
var form = document.getElementById("UserForm");
|
||||
|
||||
// Add submit event listener to the form
|
||||
form.addEventListener("submit", function(event) {
|
||||
// Disable the submit button to avoid multiple submissions
|
||||
// document.getElementById("btnSubmit").disabled = true;
|
||||
form.addEventListener("submit", function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
var btn = document.getElementById('btnSubmit');
|
||||
var currentUserId = $('#UserId').val(); // capture BEFORE ajax
|
||||
btn.disabled = true;
|
||||
btn.innerText = "Saving...";
|
||||
|
||||
var actionUrl = $('#UserForm').attr('action');
|
||||
var formData = new FormData(form);
|
||||
|
||||
$.ajax({
|
||||
url: actionUrl,
|
||||
type: "POST",
|
||||
data: formData,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
success: function(responseText) {
|
||||
|
||||
// Parse returned HTML and look for toastr.error calls (validation errors injected by CI flashdata)
|
||||
var tempDiv = document.createElement('div');
|
||||
tempDiv.innerHTML = responseText;
|
||||
|
||||
var errorMessages = [];
|
||||
tempDiv.querySelectorAll('script').forEach(function(script) {
|
||||
var content = script.innerText || script.textContent;
|
||||
var matches = content.match(/toastr\.error\("([^"]+)"/g);
|
||||
if (matches) {
|
||||
matches.forEach(function(m) {
|
||||
var msg = m.replace(/toastr\.error\("/, '').replace(/"$/, '');
|
||||
if (!errorMessages.includes(msg)) {
|
||||
errorMessages.push(msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (errorMessages.length > 0) {
|
||||
// ❌ VALIDATION FAILED — keep modal OPEN, show all errors in one toast
|
||||
var errorList = '<ul style="margin:4px 0 0 0; padding-left:18px; text-align:left;">';
|
||||
errorMessages.forEach(function(msg) {
|
||||
errorList += '<li>' + msg + '</li>';
|
||||
});
|
||||
errorList += '</ul>';
|
||||
|
||||
toastr.error(errorList, 'Validation Errors', {
|
||||
"allowHtml": true,
|
||||
"timeOut": 8000,
|
||||
"closeButton": true
|
||||
});
|
||||
|
||||
// ✅ Modal stays OPEN — do NOT hide it
|
||||
|
||||
} else {
|
||||
// ✅ SUCCESS
|
||||
toastr.success('User saved successfully!', 'Success');
|
||||
|
||||
// ✅ Bootstrap 4 way to close modal
|
||||
$('#con-close-modal').modal('hide');
|
||||
|
||||
// Full reset
|
||||
form.reset();
|
||||
$('#UserForm').parsley().reset();
|
||||
$('#UserId').val('');
|
||||
$('#nhance_branch_id').val(null).trigger('change.select2');
|
||||
$('#rm_id').val(null).trigger('change.select2');
|
||||
$('#role').val('').trigger('change');
|
||||
$('#team').multiselect('deselectAll', false);
|
||||
$('#team').multiselect('refresh');
|
||||
$('#email').removeAttr('data-original');
|
||||
$('#mobile').removeAttr('data-original');
|
||||
$('#emp_code').removeAttr('data-original');
|
||||
$('.modal-title').text('Add User');
|
||||
$('#btnSubmit').text('Submit').prop('disabled', false);
|
||||
window.location.reload();
|
||||
|
||||
loadUser();
|
||||
}
|
||||
},
|
||||
error: function(xhr) {
|
||||
toastr.error('An unexpected error occurred. Please try again.', 'Error');
|
||||
},
|
||||
complete: function() {
|
||||
btn.disabled = false;
|
||||
btn.innerText = currentUserId ? 'Update' : 'Submit';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function submitPartner(event) {
|
||||
event.preventDefault();
|
||||
@ -1516,7 +1638,7 @@ table.dataTable tbody td {
|
||||
<i class="mdi mdi-dots-horizontal"></i>
|
||||
</a>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a data-toggle="modal" data-target="#con-close-modal" class="dropdown-item btnEdit" data-id="${row.id}">
|
||||
<a class="dropdown-item btnEdit" data-id="${row.id}" style="cursor:pointer;">
|
||||
<i data-id="${row.id}" class="mdi mdi-pencil mr-2 text-muted font-18 vertical-middle btnEdit"></i>Edit
|
||||
</a>
|
||||
<a class="dropdown-item btnDelete" data-id="${row.id}">
|
||||
@ -1962,22 +2084,30 @@ table.dataTable tbody td {
|
||||
|
||||
<script>
|
||||
$(document).on('click', '#btnAdd', function() {
|
||||
// Reset the form
|
||||
$('#UserForm')[0].reset();
|
||||
$('#UserId').val('');
|
||||
|
||||
// Specifically reset Select2 if you are using it
|
||||
$('#nhance_branch_id').val('').trigger('change');
|
||||
$('#rm_id').val('').trigger('change');
|
||||
|
||||
// Update Modal Title and Action
|
||||
$('.modal-title').text('Add User');
|
||||
$('#btnSubmit').text('Submit');
|
||||
$('#UserForm').attr('action', '<?php echo base_url('/user/create');?>');
|
||||
|
||||
var myModal = new bootstrap.Modal(document.getElementById('con-close-modal'));
|
||||
myModal.show();
|
||||
});
|
||||
|
||||
$('#UserForm')[0].reset(); // 1️⃣ reset form values first
|
||||
$('#UserForm').parsley().reset(); // 2️⃣ then reset parsley validation
|
||||
|
||||
$('#btnSubmit').prop('disabled', false); // 3️⃣ re-enable submit button
|
||||
|
||||
$('#nhance_branch_id').val(null).trigger('change.select2');
|
||||
$('#rm_id').val(null).trigger('change.select2');
|
||||
|
||||
$('#team').multiselect('deselectAll', false);
|
||||
$('#team').multiselect('refresh');
|
||||
|
||||
// remove data-original attributes
|
||||
$('#email').removeAttr('data-original');
|
||||
$('#mobile').removeAttr('data-original');
|
||||
$('#emp_code').removeAttr('data-original');
|
||||
|
||||
$('.modal-title').text('Add User');
|
||||
$('#btnSubmit').text('Submit');
|
||||
$('#UserForm').attr('action', '<?php echo base_url('/user/create');?>');
|
||||
|
||||
var myModal = new bootstrap.Modal(document.getElementById('con-close-modal'));
|
||||
myModal.show();
|
||||
});
|
||||
|
||||
// $(document).on('click', '#btnAdd', function(){
|
||||
// $('#first_name').val('');
|
||||
@ -2009,6 +2139,13 @@ table.dataTable tbody td {
|
||||
function validateInput(input, table, field, submitButId){
|
||||
|
||||
let value = $(input).val();
|
||||
let originalValue = $(input).attr('data-original');
|
||||
|
||||
if (originalValue && value.trim().toLowerCase() === originalValue.trim().toLowerCase()) {
|
||||
$('#' + submitButId).prop('disabled', false);
|
||||
return;
|
||||
}
|
||||
|
||||
let label = $(input).closest('.form-group').find('label').text().replace('*', '').trim();
|
||||
|
||||
let message = "Value is duplicate!";
|
||||
@ -2018,7 +2155,8 @@ table.dataTable tbody td {
|
||||
|
||||
checkDuplicateTableFieldValue(table, field, value, function(isDuplicate) {
|
||||
if (isDuplicate) {
|
||||
toastr.warning(message, 'Warning');
|
||||
toastr.warning(message, 'Warning');
|
||||
$(input).focus();
|
||||
// $(input).val('')
|
||||
$('#' + submitButId).prop('disabled', true);
|
||||
} else{
|
||||
|
||||
@ -1,7 +1,4 @@
|
||||
<style>
|
||||
.table-responsive {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.reload:hover {
|
||||
cursor: pointer;
|
||||
@ -13,7 +10,88 @@
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.dataTables_length label {height: 21px !important;}
|
||||
|
||||
/* TPA variation modal layout */
|
||||
#tpa_variation_modal .modal-dialog {
|
||||
max-width: 95%;
|
||||
}
|
||||
|
||||
#tpa_variation_modal .modal-content {
|
||||
max-height: 90vh;
|
||||
}
|
||||
|
||||
#tpa_variation_modal .modal-body {
|
||||
max-height: calc(85vh - 50px);
|
||||
overflow-y: auto;
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
#tpa_variation_modal .table-responsive {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
#tpa_variation_modal table {
|
||||
white-space: nowrap;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
#tpa_variation_modal table thead th,
|
||||
#tpa_variation_modal table tbody td {
|
||||
padding: 2px 6px;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
/* Tabs spacing & styling */
|
||||
#tpa_variation_modal .nav-tabs {
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
margin-bottom: 12px;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
#tpa_variation_modal .nav-tabs .nav-item {
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
#tpa_variation_modal .nav-tabs .nav-link {
|
||||
padding: 6px 14px;
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
|
||||
#tpa_variation_modal .nav-tabs .nav-link.active {
|
||||
background-color: #f8f9fa;
|
||||
border-color: #dee2e6 #dee2e6 transparent;
|
||||
}
|
||||
|
||||
/* Modal footer buttons size */
|
||||
#tpa_variation_modal .modal-footer .btn {
|
||||
padding: 4px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
/* Compact table + buttons (NHance) */
|
||||
#datatable-buttons thead th,
|
||||
#datatable-buttons tbody td {
|
||||
padding: 5px 11px !important;
|
||||
font-size: 13px;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
/* Action dropdown toggle in rows */
|
||||
#datatable-buttons .dropdown-toggle.btn-sm {
|
||||
padding: 4px 9px !important;
|
||||
font-size: 13px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
/* DataTables toolbar buttons (Export/Filter/Clear Filter) */
|
||||
#datatable-buttons_wrapper .dt-buttons .btn {
|
||||
padding: 6px 13px !important;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
#datatable-buttons_wrapper .dt-buttons .btn .btn-custom {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<div class="col-12" id="second_page">
|
||||
@ -72,6 +150,7 @@
|
||||
<?php echo change_date_format($file['created_at'],'Y-m-d H:i:s', 'd M Y h:i a') ?> by <?php echo get_username($file['created_by']) ?>
|
||||
</td>
|
||||
|
||||
|
||||
<td>
|
||||
<?php if ($file['status'] == 'failed') { ?>
|
||||
<?php echo $file['status']; ?>
|
||||
@ -157,36 +236,39 @@
|
||||
<a href="javascript: void(0);" class="dropdown-toggle arrow-none btn btn-light btn-sm"
|
||||
data-toggle="dropdown" aria-expanded="false"><i class="mdi mdi-dots-horizontal"></i></a>
|
||||
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<?php if($file['actions'] != 'export') { ?>
|
||||
|
||||
<?php if (str_starts_with($file['status'], 'failed')) { ?>
|
||||
<a href="#"
|
||||
data-id="<?= $file['id'] ?>"
|
||||
data-client_id="<?= $file['client_id'] ?>"
|
||||
data-client_policy_id="<?= $file['client_policy_id'] ?>"
|
||||
data-insurer_or_tpa="<?= $file['insurer_or_tpa'] ?>"
|
||||
data-event_type="<?= $file['event_type'] ?>"
|
||||
data-actions="<?= $file['actions'] ?>"
|
||||
data-client_branch_id="<?= $file['client_branch_id'] ?>"
|
||||
data-issue_date="<?= $file['policy_issue_date'] ?>"
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
|
||||
onclick="getBatchFileData(this)" class="dropdown-item upload_button" ><i class="mdi mdi-upload mr-2 text-muted font-18 vertical-middle"></i>Re-Upload</a>
|
||||
<?php } ?>
|
||||
<?php if (str_starts_with($file['status'], 'failed')) { ?>
|
||||
<a href="#"
|
||||
data-id="<?= $file['id'] ?>"
|
||||
data-client_id="<?= $file['client_id'] ?>"
|
||||
data-client_policy_id="<?= $file['client_policy_id'] ?>"
|
||||
data-insurer_or_tpa="<?= $file['insurer_or_tpa'] ?>"
|
||||
data-event_type="<?= $file['event_type'] ?>"
|
||||
data-actions="<?= $file['actions'] ?>"
|
||||
data-client_branch_id="<?= $file['client_branch_id'] ?>"
|
||||
data-issue_date="<?= $file['policy_issue_date'] ?>"
|
||||
|
||||
<?php if ($file['actions'] == "import") { ?>
|
||||
<a href="<?= base_url('/util/download_import_file/') . $file['id'] ?>" class="dropdown-item" style="color: #000;" aria-hidden="true" target="_blank" ><i class="mdi mdi-download mr-2 text-muted font-18 vertical-middle"></i>Download</a>
|
||||
<?php } ?>
|
||||
onclick="getBatchFileData(this)" class="dropdown-item upload_button" ><i class="mdi mdi-upload mr-2 text-muted font-18 vertical-middle"></i>Re-Upload</a>
|
||||
<?php } ?>
|
||||
|
||||
<?php if ($file['actions'] == "fetch" && $file['status'] == 'partially success') { ?>
|
||||
<a href="<?= base_url('employee/getTPADataVariationReport/') . $file['id'] ?>" class="dropdown-item" style="color: #000;" aria-hidden="true" target="_blank" ><i class="mdi mdi-download mr-2 text-muted font-18 vertical-middle"></i>Download TPA Variation report</a>
|
||||
<?php if ($file['actions'] == "import") { ?>
|
||||
<a href="<?= base_url('/util/download_import_file/') . $file['id'] ?>" class="dropdown-item" style="color: #000;" aria-hidden="true" target="_blank" ><i class="mdi mdi-download mr-2 text-muted font-18 vertical-middle"></i>Download</a>
|
||||
<?php } ?>
|
||||
|
||||
|
||||
<?php } ?>
|
||||
<?php if ($file['actions'] == "fetch" && $file['status'] == 'partially success') { ?>
|
||||
<a href="<?= base_url('employee/getTPADataVariationReport/') . $file['id'] ?>" class="dropdown-item" style="color: #000;" aria-hidden="true" target="_blank" ><i class="mdi mdi-download mr-2 text-muted font-18 vertical-middle"></i>Download TPA Variation report</a>
|
||||
<a href="#" class="dropdown-item" style="color: #000;" aria-hidden="true" onclick="getTPADataVariationReport(<?= $file['id'] ?>)"><i class="mdi mdi-eye mr-2 text-muted font-18 vertical-middle"></i>View TPA Variation report</a>
|
||||
|
||||
<?php } ?>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php } ?>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
<?php }
|
||||
} ?>
|
||||
@ -228,44 +310,285 @@
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
|
||||
<!-- TPA Data Variation Modal -->
|
||||
<div class="modal fade" id="tpa_variation_modal" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
<div class="modal-dialog modal-xl modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">TPA Data Variation Report</h4>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<ul class="nav nav-tabs" id="tpaVariationTabs" role="tablist">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" id="not-in-nhance-tab" data-toggle="tab" href="#not_in_nhance_tab" role="tab" aria-controls="not_in_nhance_tab" aria-selected="true">Not in Nhance</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" id="not-in-tpa-tab" data-toggle="tab" href="#not_in_tpa_tab" role="tab" aria-controls="not_in_tpa_tab" aria-selected="false">Not in TPA</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" id="need-to-review-tab" data-toggle="tab" href="#need_to_review_tab" role="tab" aria-controls="need_to_review_tab" aria-selected="false">Need to Review</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content pt-3" id="tpaVariationTabContent">
|
||||
<div class="tab-pane fade show active" id="not_in_nhance_tab" role="tabpanel" aria-labelledby="not-in-nhance-tab">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-sm" id="not_in_nhance_table">
|
||||
<thead></thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="not_in_tpa_tab" role="tabpanel" aria-labelledby="not-in-tpa-tab">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-sm" id="not_in_tpa_table">
|
||||
<thead></thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="need_to_review_tab" role="tabpanel" aria-labelledby="need-to-review-tab">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-sm" id="need_to_review_table">
|
||||
<thead></thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">Close</button>
|
||||
<button type="button" id="tpaProceedButton" class="btn btn-primary btn-sm" onclick="handleTPAProceed()">Proceed</button>
|
||||
</div>
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
|
||||
<script>
|
||||
let currentTPAVariationFileId = null;
|
||||
|
||||
const tpaVariationColumns = {
|
||||
not_in_nhance: [
|
||||
{ key: 'emp_code', label: 'Employee Code' },
|
||||
{ key: 'name', label: 'Employee Name' },
|
||||
{ key: 'relation', label: 'Relation' },
|
||||
{ key: 'dob', label: 'Date of Birth' },
|
||||
{ key: 'gender', label: 'Gender' },
|
||||
{ key: 'age', label: 'Age' },
|
||||
{ key: 'tpa_id', label: 'TPA Member ID' },
|
||||
],
|
||||
not_in_tpa: [
|
||||
{ key: 'emp_code', label: 'Employee Code' },
|
||||
{ key: 'name', label: 'Employee Name' },
|
||||
{ key: 'relationship', label: 'Relation' },
|
||||
{ key: 'dob', label: 'Date of Birth' },
|
||||
{ key: 'gender', label: 'Gender' },
|
||||
{ key: 'mobile', label: 'Mobile No' },
|
||||
{ key: 'email_corporate', label: 'Corporate Email' },
|
||||
{ key: 'policy_no', label: 'Policy No' },
|
||||
{ key: 'tpa_name', label: 'TPA Name' },
|
||||
{ key: 'change_event', label: 'Change Event' },
|
||||
],
|
||||
need_to_review: [
|
||||
// DB side
|
||||
{ key: 'emp_code', label: 'Employee Code' },
|
||||
{ key: 'name', label: 'Employee Name' },
|
||||
{ key: 'relationship', label: 'Relation' },
|
||||
{ key: 'dob', label: 'Date of Birth' },
|
||||
{ key: 'gender', label: 'Gender' },
|
||||
{ key: 'policy_no', label: 'Policy No' },
|
||||
{ key: 'uhid', label: 'UHID' },
|
||||
{ key: 'change_event', label: 'Change Event' },
|
||||
// TPA side
|
||||
{ key: 'tpa_emp_code', label: 'TPA Employee Code' },
|
||||
{ key: 'tpa_name', label: 'TPA Name' },
|
||||
{ key: 'tpa_relation', label: 'TPA Relation' },
|
||||
{ key: 'tpa_dob', label: 'TPA Date of Birth' },
|
||||
{ key: 'tpa_gender', label: 'TPA Gender' },
|
||||
{ key: 'tpa_tpa_id', label: 'TPA Member ID' },
|
||||
{ key: 'tpa_age', label: 'TPA Age' },
|
||||
// Mismatch info
|
||||
{ key: 'mismatch_fields', label: 'Mismatch Fields' },
|
||||
],
|
||||
};
|
||||
|
||||
function renderVariationTable(tableSelector, rows, columns) {
|
||||
const $table = $(tableSelector);
|
||||
const $thead = $table.find('thead');
|
||||
const $tbody = $table.find('tbody');
|
||||
|
||||
$thead.empty();
|
||||
$tbody.empty();
|
||||
|
||||
const headerRow = $('<tr></tr>');
|
||||
columns.forEach(col => {
|
||||
headerRow.append($('<th></th>').text(col.label));
|
||||
});
|
||||
$thead.append(headerRow);
|
||||
|
||||
if (!rows || !rows.length) {
|
||||
const emptyRow = $('<tr></tr>');
|
||||
columns.forEach((col, index) => {
|
||||
const td = $('<td></td>');
|
||||
if (index === 0) {
|
||||
td.addClass('text-center text-muted').text('No data found');
|
||||
}
|
||||
emptyRow.append(td);
|
||||
});
|
||||
$tbody.append(emptyRow);
|
||||
return;
|
||||
}
|
||||
|
||||
rows.forEach(row => {
|
||||
const tr = $('<tr></tr>');
|
||||
columns.forEach(col => {
|
||||
let value = row[col.key];
|
||||
if (value === null || value === undefined) {
|
||||
value = '';
|
||||
}
|
||||
tr.append($('<td></td>').text(value));
|
||||
});
|
||||
$tbody.append(tr);
|
||||
});
|
||||
}
|
||||
|
||||
function populateTPAVariationModal(data) {
|
||||
const notInNhance = data.not_in_nhance || [];
|
||||
const notInTpa = data.not_in_tpa || [];
|
||||
const reviewData = (data.mismatch_data || []).map(row => {
|
||||
let mismatchFields = '';
|
||||
const match = row.match || {};
|
||||
|
||||
const notMatching = match.not_matching || [];
|
||||
if (Array.isArray(notMatching) && notMatching.length) {
|
||||
mismatchFields = notMatching.join(', ');
|
||||
} else if (match.status && match.status !== 'matched') {
|
||||
mismatchFields = match.status;
|
||||
}
|
||||
|
||||
const tpaRecord = match.tpa_record || {};
|
||||
|
||||
return Object.assign({}, row, {
|
||||
mismatch_fields: mismatchFields,
|
||||
tpa_emp_code: tpaRecord.emp_code || '',
|
||||
tpa_name: tpaRecord.name || '',
|
||||
tpa_relation: tpaRecord.relation || '',
|
||||
tpa_dob: tpaRecord.dob || '',
|
||||
tpa_gender: tpaRecord.gender || '',
|
||||
tpa_tpa_id: tpaRecord.tpa_id || '',
|
||||
tpa_age: tpaRecord.age || '',
|
||||
});
|
||||
});
|
||||
|
||||
destroyVariationDataTable('#not_in_nhance_table');
|
||||
destroyVariationDataTable('#not_in_tpa_table');
|
||||
destroyVariationDataTable('#need_to_review_table');
|
||||
|
||||
renderVariationTable('#not_in_nhance_table', notInNhance, tpaVariationColumns.not_in_nhance);
|
||||
renderVariationTable('#not_in_tpa_table', notInTpa, tpaVariationColumns.not_in_tpa);
|
||||
renderVariationTable('#need_to_review_table', reviewData, tpaVariationColumns.need_to_review);
|
||||
|
||||
initVariationDataTable('#not_in_nhance_table');
|
||||
initVariationDataTable('#not_in_tpa_table');
|
||||
initVariationDataTable('#need_to_review_table');
|
||||
}
|
||||
|
||||
function showTPAVariationModal() {
|
||||
const modalElement = document.getElementById('tpa_variation_modal');
|
||||
if (!modalElement) {
|
||||
return;
|
||||
}
|
||||
const modal = new bootstrap.Modal(modalElement);
|
||||
modal.show();
|
||||
// Ensure proceed button reflects the currently active tab when modal opens
|
||||
const $activeTab = $('#tpaVariationTabs .nav-link.active');
|
||||
updateTPAProceedButton($activeTab.attr('id'));
|
||||
}
|
||||
|
||||
function initVariationDataTable(tableSelector) {
|
||||
const $table = $(tableSelector);
|
||||
|
||||
$table.DataTable({
|
||||
pageLength: 20,
|
||||
lengthMenu: [[10, 20, 50, 100], [10, 20, 50, 100]],
|
||||
searching: true,
|
||||
paging: true,
|
||||
ordering: false,
|
||||
info: true,
|
||||
autoWidth: false
|
||||
});
|
||||
}
|
||||
|
||||
function destroyVariationDataTable(tableSelector) {
|
||||
const $table = $(tableSelector);
|
||||
if ($.fn.DataTable.isDataTable($table)) {
|
||||
$table.DataTable().clear().destroy();
|
||||
}
|
||||
}
|
||||
|
||||
function updateTPAProceedButton(activeId) {
|
||||
const $button = $('#tpaProceedButton');
|
||||
if (!$button.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Default: visible
|
||||
$button.removeClass('d-none');
|
||||
|
||||
if (activeId === 'not-in-nhance-tab') {
|
||||
$button.text('Proceed - Not in Nhance');
|
||||
} else if (activeId === 'need-to-review-tab') {
|
||||
$button.text('Proceed - Need to Review');
|
||||
} else if (activeId === 'not-in-tpa-tab') {
|
||||
// Hide button for "Not in TPA" section
|
||||
$button.addClass('d-none');
|
||||
} else {
|
||||
$button.text('Proceed');
|
||||
}
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
// Update proceed button label/visibility on tab change
|
||||
$('#tpaVariationTabs a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
|
||||
const activeId = $(e.target).attr('id');
|
||||
updateTPAProceedButton(activeId);
|
||||
});
|
||||
|
||||
$('#datatable-buttons').DataTable({
|
||||
// dom: "<'row'<'col-12 d-flex justify-content-between align-items-center'<'datatable-search dataTables_filter'f><'datatable-buttons dt-buttons'B>>>" +
|
||||
// "<'row'<'col-sm-12'tr>>" +
|
||||
// "<'row'<'col-sm-5'i><'col-sm-7'p>>",
|
||||
// buttons: [{
|
||||
// extend: 'csv',
|
||||
// text: 'CSV',
|
||||
// title: 'Batch List',
|
||||
// className: 'my_class',
|
||||
// }],
|
||||
// initComplete: function(settings, json) {
|
||||
// $('.my_class').css({
|
||||
// position: "relative",
|
||||
// left: "50px"
|
||||
// });
|
||||
// },
|
||||
dom: "<'row'<'col-12 d-flex justify-content-between align-items-center'<'datatable-search dataTables_filter'f><'datatable-buttons dt-buttons'B>>>" +
|
||||
dom: "<'row'<'col-sm-7'f><'col-sm-5 text-right'B>>" + // Filter left, buttons right
|
||||
"<'row'<'col-sm-12'tr>>" +
|
||||
"<'row align-items-center'<'col-5 text-start'i><'col-7 d-flex justify-content-end align-items-center'<'me-2'l>p>>",
|
||||
lengthMenu: [[10, 20, 50, 100], [10, 20, 50, 100]],
|
||||
buttons: [{
|
||||
extend: 'collection',
|
||||
text: '<span class=" btn-custom"> Export </span><i class="mdi mdi-menu-down"></i>',
|
||||
className: 'btn app-btn-secondary ',
|
||||
buttons: [
|
||||
{
|
||||
extend: 'csv',
|
||||
text: '<i class="mdi mdi-file-delimited " ></i><span class=" btn-custom"> CSV </span>',
|
||||
title: 'Batch List',
|
||||
className: 'app-btn-primary ',
|
||||
}
|
||||
]
|
||||
"<'row align-items-center'<'col-5 text-start'i><'col-7 d-flex justify-content-end align-items-center'<'me-2'l>p>>", lengthMenu: [[10, 20, 50, 100], [10, 20, 50, 100]],
|
||||
buttons: [
|
||||
{
|
||||
extend: 'collection',
|
||||
text: '<span class=" btn-custom"> Export </span><i class="mdi mdi-menu-down"></i>',
|
||||
className: 'btn app-btn-secondary mr-2',
|
||||
buttons: [
|
||||
{
|
||||
extend: 'csv',
|
||||
text: '<i class="mdi mdi-file-delimited " ></i><span class=" btn-custom"> CSV </span>',
|
||||
title: 'Batch List',
|
||||
className: 'app-btn-primary ',
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
text: '<i class="mdi mdi-filter" ></i><span class=" btn-custom"> Filter </span>',
|
||||
className: 'btn app-btn-primary mr-2',
|
||||
action: function(e, dt, node, config) {
|
||||
openPolicyFilterNav(2);
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
text: '<i class="mdi mdi-filter-remove"></i><span class="btn-custom"> Clear Filter </span>',
|
||||
className: 'btn app-btn-info mr-2 hide-clear-filter',
|
||||
action: function(e, dt, node, config) {
|
||||
clearFilterData();
|
||||
}
|
||||
}
|
||||
],
|
||||
language: {
|
||||
search: `
|
||||
<div class="datatable-search-wrapper" style="position:relative; display:inline-block;">
|
||||
@ -281,7 +604,6 @@
|
||||
paging: true
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
// Function to format a number in Indian Rupees format
|
||||
@ -393,5 +715,109 @@
|
||||
// });
|
||||
}
|
||||
|
||||
function getTPADataVariationReport(file_id) {
|
||||
|
||||
currentTPAVariationFileId = file_id;
|
||||
|
||||
$('.loader').fadeIn();
|
||||
$('.loader-mask').fadeIn();
|
||||
|
||||
let url = '<?= base_url('employee/getTPADataVariationReportView') ?>/' + file_id;
|
||||
|
||||
sendAjaxRequestForGlobal(url, 'GET', {}, function (response) {
|
||||
|
||||
$('.loader').fadeOut();
|
||||
$('.loader-mask').delay(350).fadeOut('slow');
|
||||
|
||||
if (response && response.status && response.data) {
|
||||
populateTPAVariationModal(response.data);
|
||||
showTPAVariationModal();
|
||||
} else {
|
||||
toastr.warning((response && response.message) || 'No data found for variation report.', 'WARNING');
|
||||
}
|
||||
|
||||
}, function (xhr, status, error) {
|
||||
$('.loader').fadeOut();
|
||||
$('.loader-mask').delay(350).fadeOut('slow');
|
||||
console.error('Error fetching TPA variation data:', error);
|
||||
toastr.error('An error occurred while fetching the variation report.', 'ERROR');
|
||||
});
|
||||
}
|
||||
|
||||
function proceedTPADataVariationNextStep(tabKey) {
|
||||
if (!currentTPAVariationFileId) {
|
||||
toastr.warning('Unable to identify the selected file.', 'WARNING');
|
||||
return;
|
||||
}
|
||||
|
||||
$('.loader').fadeIn();
|
||||
$('.loader-mask').fadeIn();
|
||||
|
||||
const url = '<?= base_url('employee/proceedTPADataVariationNextStep') ?>/' + currentTPAVariationFileId + '?tab=' + encodeURIComponent(tabKey);
|
||||
|
||||
sendAjaxRequestForGlobal(url, 'GET', {}, function (response) {
|
||||
$('.loader').fadeOut();
|
||||
$('.loader-mask').delay(350).fadeOut('slow');
|
||||
|
||||
if (response && response.status) {
|
||||
toastr.success(response.message || 'Proceed to next step initiated successfully.', 'SUCCESS');
|
||||
} else {
|
||||
toastr.warning(response.message || 'Unable to proceed to next step.', 'WARNING');
|
||||
}
|
||||
|
||||
window.location.reload();
|
||||
|
||||
}, function (xhr, status, error) {
|
||||
$('.loader').fadeOut();
|
||||
$('.loader-mask').delay(350).fadeOut('slow');
|
||||
toastr.error('An error occurred while proceeding to the next step.', 'ERROR');
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
|
||||
function proceedNotInNhance() {
|
||||
proceedTPADataVariationNextStep('not_in_nhance');
|
||||
}
|
||||
|
||||
function proceedNotInTPA() {
|
||||
proceedTPADataVariationNextStep('not_in_tpa');
|
||||
}
|
||||
|
||||
function proceedNeedToReview() {
|
||||
proceedTPADataVariationNextStep('need_to_review');
|
||||
}
|
||||
|
||||
function handleTPAProceed() {
|
||||
const $activeTab = $('#tpaVariationTabs .nav-link.active');
|
||||
const activeId = $activeTab.attr('id');
|
||||
|
||||
if (!activeId) {
|
||||
toastr.warning('No active tab selected.', 'WARNING');
|
||||
return;
|
||||
}
|
||||
|
||||
const tabLabel = ($activeTab.text() || '').trim() || 'this tab';
|
||||
|
||||
confirmActionSweertAlert(
|
||||
"Are you sure you want to proceed with the data in this tab?",
|
||||
"Yes, Proceed",
|
||||
"Cancel",
|
||||
"warning"
|
||||
).then(function(isConfirmed) {
|
||||
if (!isConfirmed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (activeId === 'not-in-nhance-tab') {
|
||||
proceedNotInNhance();
|
||||
} else if (activeId === 'not-in-tpa-tab') {
|
||||
proceedNotInTPA();
|
||||
} else if (activeId === 'need-to-review-tab') {
|
||||
proceedNeedToReview();
|
||||
} else {
|
||||
toastr.warning('Unknown tab selected.', 'WARNING');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
@ -67,8 +67,7 @@
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-6">
|
||||
<label for="mobile">CD Account Number<span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="cd_ac_no_for_cd_master" name="cd_ac_no" onkeypress="return onlyNumbers(event)" required data-parsley-type="digits">
|
||||
<small style="margin-left: 10px;font-size: 10px;"><span class="text-danger" id="cd_ac_no_for_cd_master_errorr"></span></small>
|
||||
<input type="text" class="form-control" id="cd_ac_no_for_cd_master" name="cd_ac_no" pattern="[a-zA-Z0-9\/_-]+" title="Only letters, numbers, /, -, and _ are allowed" required> <small style="margin-left: 10px;font-size: 10px;"><span class="text-danger" id="cd_ac_no_for_cd_master_errorr"></span></small>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-6">
|
||||
@ -129,7 +128,7 @@
|
||||
if(res.status == true){
|
||||
|
||||
$('#cd_ac_no_for_cd_master_errorr').text(res.message);
|
||||
// toastr.warning(res.message, 'warning');
|
||||
// toastr.warning(res.message, 'Warning');
|
||||
// $('#cd_ac_no').val('');
|
||||
$('#cd_master_btn_Submit').prop('disabled',true);
|
||||
return;
|
||||
@ -366,4 +365,33 @@
|
||||
}
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
// Target the input by its ID
|
||||
$('#cd_ac_no_for_cd_master').on('keypress', function(e) {
|
||||
// Get the character code
|
||||
var keyCode = e.which || e.keyCode;
|
||||
var char = String.fromCharCode(keyCode);
|
||||
|
||||
// Regular Expression: allow a-z, A-Z, 0-9, /, -, _
|
||||
// If the character doesn't match, prevent it from being typed
|
||||
var regex = /^[a-zA-Z0-9\/_-]+$/;
|
||||
|
||||
if (!regex.test(char)) {
|
||||
e.preventDefault();
|
||||
// Optional: Show a quick error message in your span
|
||||
$('#cd_ac_no_for_cd_master_errorr').text('Character not allowed').fadeOut(2000, function() {
|
||||
$(this).text('').show();
|
||||
});
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Also handle "Paste" to ensure invalid data isn't pasted in
|
||||
$('#cd_ac_no_for_cd_master').on('input', function() {
|
||||
var value = $(this).val();
|
||||
var sanitized = value.replace(/[^a-zA-Z0-9\/_-]/g, '');
|
||||
$(this).val(sanitized);
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
@ -59,7 +59,7 @@ table.dataTable thead th {
|
||||
<div class="row" id="client_list">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body maincard">
|
||||
<div class="card-body">
|
||||
<!-- <div class="row" style="margin-bottom:1rem;">
|
||||
<div class="col-6" style="align-self: center;">
|
||||
<h4 style="position: relative;">CD Master</h4>
|
||||
|
||||
10
app/Views/chartbrew_dashboard_view.php
Normal file
10
app/Views/chartbrew_dashboard_view.php
Normal file
@ -0,0 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<iframe src="<?= $iframe_url ?>" allowTransparency="true" width="1200" height="600" frameborder="0" style="background-color: #ffffff"></iframe>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@ -135,6 +135,8 @@
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
<th class="font-weight-medium">S.No </th>
|
||||
<th class="font-weight-medium">Client</th>
|
||||
<th class="font-weight-medium">Policy</th>
|
||||
<th class="font-weight-medium">File name</th>
|
||||
<th class="font-weight-medium">User/Time</th>
|
||||
<th class="font-weight-medium">Status</th>
|
||||
@ -150,6 +152,8 @@
|
||||
|
||||
<tr>
|
||||
<td class="text-center"><b><?php echo ($key + 1) ?></b></td>
|
||||
<td><?php echo $file['client_name'] ?></td>
|
||||
<td><?php echo $file['policy_no'] ?></td>
|
||||
<td style="overflow: hidden;" class="reload truncate" data-toggle="tooltip" data-placement="top" title="<?php echo $file['file_name'] ?>">
|
||||
<?php echo $file['file_name'] ?>
|
||||
</td>
|
||||
@ -740,13 +744,18 @@
|
||||
}));
|
||||
|
||||
$.each(data, function(index, item) {
|
||||
var option = $('<option>', {
|
||||
value: item.id,
|
||||
text: item.policy_type + '-' + item.policy_no,
|
||||
'data-tpaid': item.tpa_id,
|
||||
});
|
||||
|
||||
$('#client_policy_id').append(option);
|
||||
if(item.policy_type_id == 2 || item.policy_type_id == 3 || item.policy_type_id == 4 || item.policy_type_id == 5) {
|
||||
|
||||
var option = $('<option>', {
|
||||
value: item.id,
|
||||
text: item.policy_type + '-' + item.policy_no,
|
||||
'data-tpaid': item.tpa_id,
|
||||
});
|
||||
|
||||
$('#client_policy_id').append(option);
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -124,8 +124,8 @@ input:checked + .slider:before {
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-4">
|
||||
<label for="pan">PAN<span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="pan" placeholder="Enter PAN Number" data-parsley-error-message="Invalid PAN Number. Example: ABCDE1234F" value="<?= isset($client['pan']) ? $client['pan'] : '' ?>" name="pan" data-parsley-trigger="change" data-parsley-pattern="^[A-Z]{5}[0-9]{4}[A-Z]$" required>
|
||||
<label for="pan">PAN<span class="text-danger"></span></label>
|
||||
<input type="text" class="form-control" id="pan" placeholder="Enter PAN Number" data-parsley-error-message="Invalid PAN Number. Example: ABCDE1234F" value="<?= isset($client['pan']) ? $client['pan'] : '' ?>" name="pan" data-parsley-trigger="change" data-parsley-pattern="^[A-Z]{5}[0-9]{4}[A-Z]$">
|
||||
</div>
|
||||
<!-- <div class="form-group col-md-4">
|
||||
<label for="gst">GST<span class="text-danger">*</span></label>
|
||||
|
||||
@ -254,7 +254,10 @@ input:checked + .slider-branch-contact:before {
|
||||
<div class="form-group col-md-6">
|
||||
<label for="designation">Designation<span class="text-danger">*</span></label>
|
||||
<input value="" type="text" class="form-control" placeholder="Enter Designation Name"
|
||||
name="designation[]" id="designation" required>
|
||||
name="designation[]" id="designation" required
|
||||
pattern="[A-Za-z0-9\-_/ ]+"
|
||||
oninput="this.value = this.value.replace(/[^A-Za-z0-9\-_\/ ]/g, '')"
|
||||
title="Designation may only contain letters, numbers, /, -, _ and spaces only allowed">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
@ -697,7 +700,7 @@ $('body').on('click', '.btnBranchEdit', function() {
|
||||
setTimeout(function() {
|
||||
$('.loader').fadeOut();
|
||||
$('.loader-mask').delay(350).fadeOut('slow');
|
||||
toastr.warning('Something Wrong!', 'warning');
|
||||
toastr.warning('Something Wrong!', 'Warning');
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
@ -736,7 +739,10 @@ function appendContactHtml(contact = false, reset = false) {
|
||||
</div>
|
||||
<div class="form-group col-md-6">
|
||||
<label for="${uniqueId}_designation">Designation<span class="text-danger">*</span></label>
|
||||
<input value="${contact !== undefined && contact !== false ? contact.designation : ''}" type="text" class="form-control" placeholder="Enter Designation Name" name="designation[]" id="${uniqueId}_designation" required>
|
||||
<input value="${contact !== undefined && contact !== false ? contact.designation : ''}" type="text" class="form-control" placeholder="Enter Designation Name" name="designation[]" id="${uniqueId}_designation" required
|
||||
pattern="[A-Za-z0-9\-_/ ]+"
|
||||
oninput="this.value = this.value.replace(/[^A-Za-z0-9\-_\/ ]/g, '')"
|
||||
title="Designation may only contain letters, numbers, /, -, _ and spaces only allowed">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
@ -916,10 +922,10 @@ function removeClientBranch(element) {
|
||||
// console.log(res.status == true);
|
||||
if (res) {
|
||||
if (res.status == true) {
|
||||
toastr.success(res.message, 'success');
|
||||
toastr.success(res.message, 'Success');
|
||||
location.reload();
|
||||
} else {
|
||||
toastr.warning(res.message, 'warning');
|
||||
toastr.warning(res.message, 'Warning');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -1,8 +1,13 @@
|
||||
<script>
|
||||
var pageSubTitle = 'Client Deposit - <?= $clientName[0]["client_name"]; ?>';
|
||||
|
||||
var pageBackButton = '<a href="<?= base_url("client/list"); ?>" data-toggle="tooltip" data-placement="top" title="Back to Client List"><i class="mdi mdi-arrow-left" style="font-size: 17px;"></i></a>';
|
||||
</script>
|
||||
<div class="row" id="client_list">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="row" style="margin-bottom:1rem;">
|
||||
<!-- <div class="row" style="margin-bottom:1rem;">
|
||||
<div class="col-6" style="align-self: center;">
|
||||
|
||||
<h4 style="position: relative;">Client Deposit -
|
||||
@ -12,53 +17,53 @@
|
||||
<div class="col-6" style="text-align: right;">
|
||||
<a href="<?= base_url("client/list"); ?>"><i class="mdi mdi-arrow-left" style="font-size: 17px;"></i></a>
|
||||
</div>
|
||||
<!-- <div class="col-6" style="text-align: right; position: relative;top: 53px;">
|
||||
<!- <div class="col-6" style="text-align: right; position: relative;top: 53px;">
|
||||
<a href="<?= base_url("client/client_deposit"); ?>" type="button" id="btnAdd" class="btn btn-primary waves-effect waves-light" data-toggle="" data-placement="top" title="Add" data-trigger="hover">ADD</a>
|
||||
</div> -->
|
||||
</div>
|
||||
<table data-custom-table-css="table" class="table mb-0 nowrap w-100 table-centered" cellspacing="0" id="tickets-table">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
<th class="font-weight-medium">Insurer Name </th>
|
||||
<th class="font-weight-medium">Insurer Branch Name </th>
|
||||
<th class="font-weight-medium">CD Account Number </th>
|
||||
<th class="font-weight-medium">Current Balance </th>
|
||||
<th class="font-weight-medium">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</div> ->
|
||||
</div> -->
|
||||
<div class="table-responsive">
|
||||
<table data-custom-table-css="table" class="table mb-0 nowrap w-100 table-centered" cellspacing="0" id="tickets-table">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
<th class="font-weight-medium">Insurer Name </th>
|
||||
<th class="font-weight-medium">Insurer Branch Name </th>
|
||||
<th class="font-weight-medium">CD Account Number </th>
|
||||
<th class="font-weight-medium">Current Balance </th>
|
||||
<th class="font-weight-medium">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
<tbody>
|
||||
<?php foreach ($clientData as $value): ?>
|
||||
<tr>
|
||||
<td><?php echo $value->insurer_name;?></td>
|
||||
<td><?php echo $value->insurer_branch_name;?></td>
|
||||
<td><?php echo $value->cd_master_account_no;?></td>
|
||||
|
||||
<td><?php echo $value->insurer_name;?></td>
|
||||
<td><?php echo $value->insurer_branch_name;?></td>
|
||||
<td><?php echo $value->cd_master_account_no;?></td>
|
||||
|
||||
<td>₹
|
||||
<?php
|
||||
$insurerId = $value->insurer_id;
|
||||
$cd_ac_pk = $value->cd_ac_pk;
|
||||
// Check if the balance information exists for the insurer
|
||||
if (isset($balances[$insurerId . '-'. $cd_ac_pk])) {
|
||||
$balance = $balances[$insurerId . '-'. $cd_ac_pk]->balance;
|
||||
echo $balance;
|
||||
} else {
|
||||
echo "N/A"; // Display "Not Available" if balance information is not present
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
<td>
|
||||
<a class="dropdown-item"
|
||||
href="<?= base_url("client/view_deposit/{$value->insurer_id}?client_id={$value->client_id}&cd_ac_pk={$value->cd_ac_pk}"); ?>">
|
||||
<i class="mdi mdi-eye mr-2 text-muted font-18 vertical-middle"></i>View Deposit
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<td>₹
|
||||
<?php
|
||||
$insurerId = $value->insurer_id;
|
||||
$cd_ac_pk = $value->cd_ac_pk;
|
||||
// Check if the balance information exists for the insurer
|
||||
if (isset($balances[$insurerId . '-'. $cd_ac_pk])) {
|
||||
$balance = $balances[$insurerId . '-'. $cd_ac_pk]->balance;
|
||||
echo $balance;
|
||||
} else {
|
||||
echo "N/A"; // Display "Not Available" if balance information is not present
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
<td>
|
||||
<a class="dropdown-item"
|
||||
href="<?= base_url("client/view_deposit/{$value->insurer_id}?client_id={$value->client_id}&cd_ac_pk={$value->cd_ac_pk}"); ?>">
|
||||
<i class="mdi mdi-eye mr-2 text-muted font-18 vertical-middle"></i>View Deposit
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- end col -->
|
||||
@ -70,7 +75,7 @@ $(document).ready(function() {
|
||||
|
||||
$('#tickets-table').DataTable({
|
||||
// dom: "<'row'<'col-sm-0'f><'col-sm-9 text-right'B>>",
|
||||
dom: "<'row'<'col-12 d-flex justify-content-between align-items-center'<'datatable-search dataTables_filter'f><'datatable-buttons dt-buttons'B>>>",
|
||||
dom: "<'row'<'col-12 d-flex justify-content-between align-items-center'<'datatable-search dataTables_filter'f><'datatable-buttons dt-buttons'B>>>" +
|
||||
"<'row'<'col-sm-12'tr>>" +
|
||||
"<'row align-items-center'<'col-5 text-start'i><'col-7 d-flex justify-content-end align-items-center'<'me-2'l>p>>",
|
||||
lengthMenu: [[10, 20, 50, 100], [10, 20, 50, 100]],
|
||||
|
||||
@ -377,7 +377,7 @@
|
||||
setTimeout(function() {
|
||||
$('.loader').fadeOut();
|
||||
$('.loader-mask').delay(350).fadeOut('slow');
|
||||
toastr.warning('Something Wrong!', 'warning');
|
||||
toastr.warning('Something Wrong!', 'Warning');
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
|
||||
@ -149,15 +149,15 @@ table.dataTable thead th {
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="myCenterModalLabel">Lead List</h4>
|
||||
<h4 class="modal-title" id="myCenterModalLabel">Opportunity List</h4>
|
||||
<button type="button" id="close_btn" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="form-group col-md-12">
|
||||
<label for="lead_id"> Lead List <span class="text-danger">*</span></label>
|
||||
<label for="lead_id"> Opportunity List <span class="text-danger">*</span></label>
|
||||
<select class="form-control" id="lead_id" name="lead_id" required>
|
||||
<option value="">Select Lead</option>
|
||||
<option value="">Select Opportunity</option>
|
||||
<?php if(isset($lead_data)) { ?>
|
||||
<?php foreach ($lead_data as $value) { ?>
|
||||
<option value="<?= $value['id']?>"><?= $value['client_name'] ?> - <?= $value['branch_name'] ?> - <?= $value['user_name'] ?></option>
|
||||
@ -312,7 +312,7 @@ $(document).ready(function()
|
||||
lengthMenu: [[10, 20, 50, 100], [10, 20, 50, 100]],
|
||||
buttons: [
|
||||
{
|
||||
text: 'Add From Lead',
|
||||
text: 'Add From Opportunities',
|
||||
className: 'btn-filter add-from-lead', // custom class
|
||||
action: function(e, dt, node, config) {
|
||||
showModal();
|
||||
|
||||
@ -271,14 +271,18 @@
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="row" style="padding-bottom: 10px;">
|
||||
<script>
|
||||
var pageSubTitle = 'Client Onboarding <span id="client_heading"><?php if (isset($client)) { echo ' - ' . addslashes($client['client_name']); } ?></span>';
|
||||
var pageBackButton = '<a href="<?= base_url("client/list"); ?>" aria-label="Back to list"><i class="mdi mdi-arrow-left" style="font-size: 17px;"></i></a>';
|
||||
</script>
|
||||
<!-- <div class="row" style="padding-bottom: 10px;">
|
||||
<div class="col-6" style="align-self: center;">
|
||||
<h4 style="position: relative;">Client Onboarding <span id="client_heading"><?php if (isset($client)) { echo ' - ' . $client['client_name']; } ?></span> </h4>
|
||||
</div>
|
||||
<div class="col-6" style="text-align: right;">
|
||||
<a href="<?= base_url("client/list"); ?>"><i class="mdi mdi-arrow-left" style="font-size: 17px;"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<br>
|
||||
|
||||
@ -450,6 +454,40 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Non EB Rack Rate Modal -->
|
||||
<style>
|
||||
#nonEbRackRateModal .modal-dialog { max-width: 420px !important; width: 420px !important; }
|
||||
#nonEbRackRateModal .modal-body { min-height: auto !important; padding: 10px 15px !important; }
|
||||
#nonEbRackRateModal .modal-header { padding: 8px 15px !important; }
|
||||
#nonEbRackRateModal .modal-footer { padding: 6px 15px !important; }
|
||||
#nonEbRackRateModal .non-eb-upload-row { display: flex; align-items: center; gap: 8px; }
|
||||
#nonEbRackRateModal .non-eb-upload-row input[type="file"] { flex: 1; font-size: 12px; }
|
||||
</style>
|
||||
<div class="modal fade" id="nonEbRackRateModal" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h6 class="modal-title mb-0">Non EB Rack Rate Files</h6>
|
||||
<button type="button" class="close" onclick="if(window._nonEbModal) window._nonEbModal.hide();"><span>×</span></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<input type="hidden" id="non_eb_rack_rate_client_policy_id" />
|
||||
<div class="non-eb-upload-row mb-2">
|
||||
<input type="file" id="non_eb_rack_rate_file" accept=".pdf,.xlsx,.xls" />
|
||||
<button type="button" class="btn btn-primary btn-sm" id="btnUploadNonEbFile" style="white-space:nowrap; height:30px;">
|
||||
<i class="mdi mdi-upload"></i> Upload
|
||||
</button>
|
||||
</div>
|
||||
<small class="text-muted">PDF, Excel only</small>
|
||||
<hr class="my-1">
|
||||
<div id="non_eb_rack_rate_file_list" style="max-height:150px; overflow-y:auto;"></div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary btn-sm" onclick="if(window._nonEbModal) window._nonEbModal.hide();">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let hrAccessControlHtmlAppended = false;
|
||||
@ -643,57 +681,58 @@
|
||||
</script>
|
||||
|
||||
<script>
|
||||
function toggleAccordion(header) {
|
||||
if (event.target.classList.contains('fa-info-circle') ||
|
||||
event.target.parentElement.classList.contains('fa-info-circle')) {
|
||||
getHrActivityHistory();
|
||||
return; // Do nothing if click was on the info icon
|
||||
}
|
||||
const card = header.parentElement;
|
||||
const content = card.querySelector('.card-content');
|
||||
const icon = header.querySelector('.accordion-icon i');
|
||||
|
||||
// function toggleAccordion(header) {
|
||||
// if (event.target.classList.contains('fa-info-circle') ||
|
||||
// event.target.parentElement.classList.contains('fa-info-circle')) {
|
||||
// getHrActivityHistory();
|
||||
// return; // Do nothing if click was on the info icon
|
||||
// }
|
||||
// const card = header.parentElement;
|
||||
// const content = card.querySelector('.card-content');
|
||||
// const icon = header.querySelector('.accordion-icon i');
|
||||
|
||||
// Close all other accordions
|
||||
document.querySelectorAll('.user-card').forEach(otherCard => {
|
||||
if (otherCard !== card) {
|
||||
const otherContent = otherCard.querySelector('.card-content');
|
||||
const otherIcon = otherCard.querySelector('.accordion-icon i');
|
||||
otherIcon.setAttribute('class', 'mdi mdi-chevron-down');
|
||||
// // Close all other accordions
|
||||
// document.querySelectorAll('.user-card').forEach(otherCard => {
|
||||
// if (otherCard !== card) {
|
||||
// const otherContent = otherCard.querySelector('.card-content');
|
||||
// const otherIcon = otherCard.querySelector('.accordion-icon i');
|
||||
// otherIcon.setAttribute('class', 'mdi mdi-chevron-down');
|
||||
|
||||
}
|
||||
});
|
||||
// }
|
||||
// });
|
||||
|
||||
// Toggle current accordion
|
||||
content.classList.toggle('active');
|
||||
if (content.classList.contains('active')) {
|
||||
icon.classList.setAttribute('class', 'mdi mdi-chevron-up');
|
||||
} else {
|
||||
icon.classList.setAttribute('class', 'mdi mdi-chevron-down');
|
||||
}
|
||||
}
|
||||
// // Toggle current accordion
|
||||
// content.classList.toggle('active');
|
||||
// if (content.classList.contains('active')) {
|
||||
// icon.classList.setAttribute('class', 'mdi mdi-chevron-up');
|
||||
// } else {
|
||||
// icon.classList.setAttribute('class', 'mdi mdi-chevron-down');
|
||||
// }
|
||||
// }
|
||||
|
||||
function toggleSection(checkbox, sectionId) {
|
||||
const section = document.getElementById(sectionId);
|
||||
const checkboxes = section.querySelectorAll('input[type="checkbox"]');
|
||||
// function toggleSection(checkbox, sectionId) {
|
||||
// const section = document.getElementById(sectionId);
|
||||
// const checkboxes = section.querySelectorAll('input[type="checkbox"]');
|
||||
|
||||
if (checkbox.checked) {
|
||||
section.classList.remove('hidden');
|
||||
checkboxes.forEach(cb => {
|
||||
cb.disabled = false;
|
||||
});
|
||||
} else {
|
||||
section.classList.add('hidden');
|
||||
checkboxes.forEach(cb => {
|
||||
cb.checked = false;
|
||||
cb.disabled = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
// if (checkbox.checked) {
|
||||
// section.classList.remove('hidden');
|
||||
// checkboxes.forEach(cb => {
|
||||
// cb.disabled = false;
|
||||
// });
|
||||
// } else {
|
||||
// section.classList.add('hidden');
|
||||
// checkboxes.forEach(cb => {
|
||||
// cb.checked = false;
|
||||
// cb.disabled = true;
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
|
||||
function getCheckedValues(name) {
|
||||
const checkboxes = document.querySelectorAll(`input[name="${name}"]:checked`);
|
||||
return Array.from(checkboxes).map(cb => parseInt(cb.value));
|
||||
}
|
||||
// function getCheckedValues(name) {
|
||||
// const checkboxes = document.querySelectorAll(`input[name="${name}"]:checked`);
|
||||
// return Array.from(checkboxes).map(cb => parseInt(cb.value));
|
||||
// }
|
||||
|
||||
function getHrActivityHistory() {
|
||||
console.log(event.target.dataset.id); //return;
|
||||
@ -823,12 +862,6 @@
|
||||
|
||||
}
|
||||
|
||||
// Helper function to get checked checkbox values (if not already defined)
|
||||
function getCheckedValues(namePrefix) {
|
||||
const checkboxes = document.querySelectorAll(`input[name^="${namePrefix}"]:checked`);
|
||||
return Array.from(checkboxes).map(cb => cb.value);
|
||||
}
|
||||
|
||||
$(document).on('change', '.select-all', function() {
|
||||
let $this = $(this);
|
||||
let type = $this.data('type');
|
||||
|
||||
@ -104,7 +104,7 @@ input:checked + .slider_blue::before {
|
||||
|
||||
<div class="row" style="padding-bottom: 10px; position: relative;right: 13px; justify-content: end;">
|
||||
<button type="button" id="BtnAdd" class="btn btn-primary waves-effect waves-light btnAdd btn-sm" style="position: relative;right: 10px;"><span class="mdi mdi-plus-box-outline" aria-hidden="true" style="padding: 5px 10px;"></span>Add Policy</button>
|
||||
<button type="button" id="BtnAddSuccess" class="btn btn-success waves-effect waves-light BtnAddSuccess btn-sm" onclick="showModal()"><span class="mdi mdi-plus-box-outline" aria-hidden="true" style="padding: 5px 10px;"></span>Add Policy From Lead</button>
|
||||
<button type="button" id="BtnAddSuccess" class="btn btn-success waves-effect waves-light BtnAddSuccess btn-sm" onclick="showModal()"><span class="mdi mdi-plus-box-outline" aria-hidden="true" style="padding: 5px 10px;"></span>Add Policy From Opportunities</button>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
@ -339,15 +339,15 @@ input:checked + .slider_blue::before {
|
||||
<div class="modal-dialog modal-dialog-centered modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="myCenterModalLabel">Lead List</h4>
|
||||
<h4 class="modal-title" id="myCenterModalLabel">Opportunity List</h4>
|
||||
<button type="button" id="close_btn" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="form-group col-md-12">
|
||||
<label for="lead_id"> Lead List <span class="text-danger">*</span></label>
|
||||
<label for="lead_id"> Opportunity List <span class="text-danger">*</span></label>
|
||||
<select class="form-control" id="lead_id" name="lead_id" required>
|
||||
<option value="">Select Lead</option>
|
||||
<option value="">Select Opportunity</option>
|
||||
<?php if(isset($lead_data)) { ?>
|
||||
<?php foreach ($lead_data as $value) { ?>
|
||||
<option value="<?= $value['id']?>" data-clientid="<?= $value['client_id']?>", data-branchid="<?= $value['client_branch_id']?>"><?= $value['client_name'] ?> - <?= $value['branch_name'] ?> - <?= $value['user_name'] ?></option>
|
||||
@ -527,13 +527,23 @@ input:checked + .slider_blue::before {
|
||||
<div class="btn-group dropdown">
|
||||
<a href="javascript: void(0);" class="dropdown-toggle arrow-none btn btn-light btn-sm" data-toggle="dropdown" aria-expanded="false"><i class="mdi mdi-dots-horizontal"></i></a>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a href="#" data-id="${item.id}" id="${item.policy_id}" data-cdamt="${item.lead_cd_amount}" class="dropdown-item btnPolicyEdit"><i class="mdi mdi-lead-pencil mr-2 text-muted font-18 vertical-middle"></i>Edit</a>
|
||||
<a href="#" data-id="${item.id}" id="${item.policy_id}" data-cdamt="${item.lead_cd_amount}" class="dropdown-item btnPolicyEdit"><i class="mdi mdi-lead-pencil mr-2 text-muted font-18 vertical-middle"></i>Edit</a>`;
|
||||
|
||||
var isNonEb = (item.allocg === 'Non-EB' || item.allocg === 'Marine');
|
||||
|
||||
if (!isNonEb) {
|
||||
policyTable += `
|
||||
<a href="#" data-id="${item.id}" id="${search_term}" data-typeid="${item.policy_type_id}" class="dropdown-item btnPolicyMaster" ><i class="mdi mdi-wrench mr-2 text-muted font-18 vertical-middle"></i>Terms</a>
|
||||
<a href="#" data-id="${item.id}" id="${search_term}" data-typeid="${item.policy_type_id}" class="dropdown-item btnPolicyModel" data-toggle="modal" data-target="#bs-example-modal-lg"><i class="mdi mdi-book-open mr-2 text-muted font-18 vertical-middle"></i>Rack Rate</a>`;
|
||||
} else {
|
||||
policyTable += `
|
||||
<a href="#" data-misc='${item.lead_misc || ""}' class="dropdown-item btnNonEbTerms"><i class="mdi mdi-file-document-outline mr-2 text-muted font-18 vertical-middle"></i>Non EB Terms</a>
|
||||
<a href="#" data-id="${item.id}" class="dropdown-item btnNonEbRackRateModal"><i class="mdi mdi-book-open mr-2 text-muted font-18 vertical-middle"></i>Non EB Rack Rate</a>`;
|
||||
}
|
||||
|
||||
// Conditionally render the delete option based on the role
|
||||
if (role != 3 && role != 4) {
|
||||
|
||||
|
||||
policyTable += `
|
||||
<a href="#" data-id="${item.id}" id="${search_term}" data-typeid="${item.policy_type_id}" class="dropdown-item" onclick="removepolicy(this)"><i class="mdi mdi-delete mr-2 text-muted font-18 vertical-middle"></i>Delete</a>`;
|
||||
}
|
||||
@ -794,14 +804,24 @@ input:checked + .slider_blue::before {
|
||||
<div class="btn-group dropdown">
|
||||
<a href="javascript: void(0);" class="dropdown-toggle arrow-none btn btn-light btn-sm" data-toggle="dropdown" aria-expanded="false"><i class="mdi mdi-dots-horizontal"></i></a>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a href="#" data-id="${item.id}" id="${item.policy_id}" data-cdamt="${item.lead_cd_amount}" class="dropdown-item btnPolicyEdit"><i class="mdi mdi-lead-pencil mr-2 text-muted font-18 vertical-middle"></i>Edit</a>
|
||||
<a href="#" data-id="${item.id}" id="${item.policy_id}" data-cdamt="${item.lead_cd_amount}" class="dropdown-item btnPolicyEdit"><i class="mdi mdi-lead-pencil mr-2 text-muted font-18 vertical-middle"></i>Edit</a>`;
|
||||
|
||||
var isNonEb = (item.allocg === 'Non-EB' || item.allocg === 'Marine');
|
||||
|
||||
if (!isNonEb) {
|
||||
policyTable += `
|
||||
<a href="#" data-id="${item.id}" id="${search_term}" data-typeid="${item.policy_type_id}" class="dropdown-item btnPolicyMaster" ><i class="mdi mdi-wrench mr-2 text-muted font-18 vertical-middle"></i>Terms</a>
|
||||
<a href="#" data-id="${item.id}" id="${search_term}" data-typeid="${item.policy_type_id}" class="dropdown-item btnPolicyModel" data-toggle="modal" data-target="#bs-example-modal-lg"><i class="mdi mdi-book-open mr-2 text-muted font-18 vertical-middle"></i>Rack Rate</a>`;
|
||||
} else {
|
||||
policyTable += `
|
||||
<a href="#" data-misc='${item.lead_misc || ""}' class="dropdown-item btnNonEbTerms"><i class="mdi mdi-file-document-outline mr-2 text-muted font-18 vertical-middle"></i>Non EB Terms</a>
|
||||
<a href="#" data-id="${item.id}" class="dropdown-item btnNonEbRackRateModal"><i class="mdi mdi-book-open mr-2 text-muted font-18 vertical-middle"></i>Non EB Rack Rate</a>`;
|
||||
}
|
||||
|
||||
// Conditionally render the delete option based on the role
|
||||
if (role != 3 && role != 4) {
|
||||
|
||||
|
||||
|
||||
|
||||
policyTable += `
|
||||
<a href="#" data-id="${item.id}" id="${search_term}" data-typeid="${item.policy_type_id}" class="dropdown-item" onclick="removepolicy(this)"><i class="mdi mdi-delete-outline mr-2 text-muted font-18 vertical-middle"></i>Delete</a>`;
|
||||
}
|
||||
@ -1197,6 +1217,144 @@ input:checked + .slider_blue::before {
|
||||
});
|
||||
});
|
||||
|
||||
$('body').on('click', '.btnNonEbTerms', function(e) {
|
||||
e.preventDefault();
|
||||
var miscData = $(this).attr('data-misc');
|
||||
if (miscData && miscData !== '' && miscData !== 'null') {
|
||||
try {
|
||||
var misc = JSON.parse(miscData);
|
||||
if (misc.placement_sheet_id) {
|
||||
window.open('https://docs.google.com/spreadsheets/d/' + misc.placement_sheet_id, '_blank');
|
||||
} else {
|
||||
alert('No terms found');
|
||||
}
|
||||
} catch (e) {
|
||||
alert('No terms found');
|
||||
}
|
||||
} else {
|
||||
alert('No terms found');
|
||||
}
|
||||
});
|
||||
|
||||
$('body').on('click', '.btnNonEbRackRateModal', function(e) {
|
||||
e.preventDefault();
|
||||
var clientPolicyId = $(this).data('id');
|
||||
$('#non_eb_rack_rate_client_policy_id').val(clientPolicyId);
|
||||
$('#non_eb_rack_rate_file_list').empty();
|
||||
$('#non_eb_rack_rate_file').val('');
|
||||
|
||||
// Load existing files
|
||||
$.ajax({
|
||||
url: '<?= base_url("client/policy/getNonEbRackRateFiles") ?>',
|
||||
method: 'GET',
|
||||
data: { client_policy_id: clientPolicyId },
|
||||
success: function(res) {
|
||||
if (res.status && res.files && res.files.length > 0) {
|
||||
res.files.forEach(function(file) {
|
||||
appendNonEbRackRateFile(file);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
window._nonEbModal = new bootstrap.Modal(document.getElementById('nonEbRackRateModal'));
|
||||
window._nonEbModal.show();
|
||||
});
|
||||
|
||||
function appendNonEbRackRateFile(file) {
|
||||
var icon = file.type === 'pdf' ? 'mdi-file-pdf text-danger' : 'mdi-file-excel text-success';
|
||||
var html = `<div class="d-flex align-items-center justify-content-between border rounded p-1 mb-1" data-filename="${file.name}" style="font-size:12px;">
|
||||
<div><i class="mdi ${icon} font-16 mr-1"></i>${file.original_name}</div>
|
||||
<button type="button" class="btn btn-sm btn-outline-danger py-0 px-1 btnRemoveNonEbFile" data-filename="${file.name}"><i class="mdi mdi-close"></i></button>
|
||||
</div>`;
|
||||
$('#non_eb_rack_rate_file_list').append(html);
|
||||
}
|
||||
|
||||
$('body').on('click', '#btnUploadNonEbFile', function() {
|
||||
var fileInput = document.getElementById('non_eb_rack_rate_file');
|
||||
var file = fileInput.files[0];
|
||||
if (!file) {
|
||||
toastr.warning('Please select a file first.', 'Warning');
|
||||
return;
|
||||
}
|
||||
|
||||
var ext = file.name.split('.').pop().toLowerCase();
|
||||
if (!['pdf', 'xlsx', 'xls'].includes(ext)) {
|
||||
toastr.error('Only PDF and Excel files are allowed.', 'Invalid File');
|
||||
$('#non_eb_rack_rate_file').val('');
|
||||
return;
|
||||
}
|
||||
|
||||
var formData = new FormData();
|
||||
formData.append('file', file);
|
||||
formData.append('client_policy_id', $('#non_eb_rack_rate_client_policy_id').val());
|
||||
|
||||
$('.loader').fadeIn();
|
||||
$('.loader-mask').fadeIn();
|
||||
|
||||
$.ajax({
|
||||
url: '<?= base_url("client/policy/uploadNonEbRackRateFile") ?>',
|
||||
method: 'POST',
|
||||
data: formData,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
success: function(res) {
|
||||
setTimeout(function() {
|
||||
$('.loader').fadeOut();
|
||||
$('.loader-mask').delay(350).fadeOut('slow');
|
||||
}, 500);
|
||||
if (res.status) {
|
||||
toastr.success('File uploaded successfully', 'Success');
|
||||
appendNonEbRackRateFile(res.file);
|
||||
$('#non_eb_rack_rate_file').val('');
|
||||
} else {
|
||||
toastr.error(res.message || 'Upload failed', 'Error');
|
||||
}
|
||||
},
|
||||
error: function(xhr) {
|
||||
setTimeout(function() {
|
||||
$('.loader').fadeOut();
|
||||
$('.loader-mask').delay(350).fadeOut('slow');
|
||||
}, 500);
|
||||
toastr.error('Something went wrong', 'Error');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('body').on('click', '.btnRemoveNonEbFile', function() {
|
||||
var btn = $(this);
|
||||
var filename = btn.data('filename');
|
||||
var clientPolicyId = $('#non_eb_rack_rate_client_policy_id').val();
|
||||
|
||||
Swal.fire({
|
||||
title: "Are you sure?",
|
||||
text: "This file will be removed.",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: "#d33",
|
||||
confirmButtonText: "Yes, remove it",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
$.ajax({
|
||||
url: '<?= base_url("client/policy/removeNonEbRackRateFile") ?>',
|
||||
method: 'POST',
|
||||
data: { client_policy_id: clientPolicyId, filename: filename },
|
||||
success: function(res) {
|
||||
if (res.status) {
|
||||
btn.closest('[data-filename]').remove();
|
||||
toastr.success('File removed', 'Success');
|
||||
} else {
|
||||
toastr.error(res.message || 'Remove failed', 'Error');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
toastr.error('Something went wrong', 'Error');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('body').on('click', '.btnOpenEnroll', function() {
|
||||
|
||||
Swal.fire({
|
||||
@ -2533,9 +2691,19 @@ $(document).ready(function () {
|
||||
<div class="btn-group dropdown">
|
||||
<a href="javascript: void(0);" class="dropdown-toggle arrow-none btn btn-light btn-sm" data-toggle="dropdown" aria-expanded="false"><i class="mdi mdi-dots-horizontal"></i></a>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a href="#" data-id="${item.id}" id="${item.policy_id}" data-cdamt="${item.lead_cd_amount}" class="dropdown-item btnPolicyEdit"><i class="mdi mdi-lead-pencil mr-2 text-muted font-18 vertical-middle"></i>Edit</a>
|
||||
<a href="#" data-id="${item.id}" id="${item.policy_id}" data-cdamt="${item.lead_cd_amount}" class="dropdown-item btnPolicyEdit"><i class="mdi mdi-lead-pencil mr-2 text-muted font-18 vertical-middle"></i>Edit</a>`;
|
||||
|
||||
let isNonEb3 = (item.allocg === 'Non-EB' || item.allocg === 'Marine');
|
||||
|
||||
if (!isNonEb3) {
|
||||
policyTable += `
|
||||
<a href="#" data-id="${item.id}" id="${search_term}" data-typeid="${item.policy_type_id}" class="dropdown-item btnPolicyMaster" ><i class="mdi mdi-wrench mr-2 text-muted font-18 vertical-middle"></i>Terms</a>
|
||||
<a href="#" data-id="${item.id}" id="${search_term}" data-typeid="${item.policy_type_id}" class="dropdown-item btnPolicyModel" data-toggle="modal" data-target="#bs-example-modal-lg"><i class="mdi mdi-book-open mr-2 text-muted font-18 vertical-middle"></i>Rack Rate</a>`;
|
||||
} else {
|
||||
policyTable += `
|
||||
<a href="#" data-misc='${item.lead_misc || ""}' class="dropdown-item btnNonEbTerms"><i class="mdi mdi-file-document-outline mr-2 text-muted font-18 vertical-middle"></i>Non EB Terms</a>
|
||||
<a href="#" data-id="${item.id}" class="dropdown-item btnNonEbRackRateModal"><i class="mdi mdi-book-open mr-2 text-muted font-18 vertical-middle"></i>Non EB Rack Rate</a>`;
|
||||
}
|
||||
|
||||
// Conditionally render the delete option based on the role
|
||||
if (role != 3 && role != 4) {
|
||||
@ -2543,14 +2711,14 @@ $(document).ready(function () {
|
||||
<a href="#" data-id="${item.id}" id="${search_term}" data-typeid="${item.policy_type_id}" class="dropdown-item" onclick="removepolicy(this)"><i class="mdi mdi-delete mr-2 text-muted font-18 vertical-middle"></i>Delete</a>`;
|
||||
}
|
||||
|
||||
policyTable += `
|
||||
policyTable += `
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
if(showExpired == false && checkDateStatus(item.policy_end_date) != 'Expired'){
|
||||
@ -2569,9 +2737,19 @@ $(document).ready(function () {
|
||||
<div class="btn-group dropdown">
|
||||
<a href="javascript: void(0);" class="dropdown-toggle arrow-none btn btn-light btn-sm" data-toggle="dropdown" aria-expanded="false"><i class="mdi mdi-dots-horizontal"></i></a>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a href="#" data-id="${item.id}" id="${item.policy_id}" data-cdamt="${item.lead_cd_amount}" class="dropdown-item btnPolicyEdit"><i class="mdi mdi-lead-pencil mr-2 text-muted font-18 vertical-middle"></i>Edit</a>
|
||||
<a href="#" data-id="${item.id}" id="${item.policy_id}" data-cdamt="${item.lead_cd_amount}" class="dropdown-item btnPolicyEdit"><i class="mdi mdi-lead-pencil mr-2 text-muted font-18 vertical-middle"></i>Edit</a>`;
|
||||
|
||||
let isNonEb4 = (item.allocg === 'Non-EB' || item.allocg === 'Marine');
|
||||
|
||||
if (!isNonEb4) {
|
||||
policyTable += `
|
||||
<a href="#" data-id="${item.id}" id="${search_term}" data-typeid="${item.policy_type_id}" class="dropdown-item btnPolicyMaster" ><i class="mdi mdi-wrench mr-2 text-muted font-18 vertical-middle"></i>Terms</a>
|
||||
<a href="#" data-id="${item.id}" id="${search_term}" data-typeid="${item.policy_type_id}" class="dropdown-item btnPolicyModel" data-toggle="modal" data-target="#bs-example-modal-lg"><i class="mdi mdi-book-open mr-2 text-muted font-18 vertical-middle"></i>Rack Rate</a>`;
|
||||
} else {
|
||||
policyTable += `
|
||||
<a href="#" data-misc='${item.lead_misc || ""}' class="dropdown-item btnNonEbTerms"><i class="mdi mdi-file-document-outline mr-2 text-muted font-18 vertical-middle"></i>Non EB Terms</a>
|
||||
<a href="#" data-id="${item.id}" class="dropdown-item btnNonEbRackRateModal"><i class="mdi mdi-book-open mr-2 text-muted font-18 vertical-middle"></i>Non EB Rack Rate</a>`;
|
||||
}
|
||||
|
||||
// Conditionally render the delete option based on the role
|
||||
if (role != 3 && role != 4) {
|
||||
@ -2579,14 +2757,14 @@ $(document).ready(function () {
|
||||
<a href="#" data-id="${item.id}" id="${search_term}" data-typeid="${item.policy_type_id}" class="dropdown-item" onclick="removepolicy(this)"><i class="mdi mdi-delete mr-2 text-muted font-18 vertical-middle"></i>Delete</a>`;
|
||||
}
|
||||
|
||||
policyTable += `
|
||||
policyTable += `
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -253,16 +253,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Claims -->
|
||||
<h3 class="section-title" style="font-size:17px;color:#2c3e50;border-bottom:2px solid #3498db;display:inline-block;margin:18px 0 12px;padding-bottom:4px;">Claims</h3>
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card" style="width:100%;display:block;background:#f8f9fa;padding:12px 14px;border-radius:6px;border-left:4px solid #d35400;box-sizing:border-box;margin-bottom:10px;">
|
||||
<div class="stat-label">Total Claims Registered for the day</div>
|
||||
<div class="stat-value"><?= $total_claim_count ?></div>
|
||||
<div class="stat-note"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- BDS by Policy Type -->
|
||||
<h3 class="section-title" style="font-size:17px;color:#2c3e50;border-bottom:2px solid #3498db;display:inline-block;margin:18px 0 12px;padding-bottom:4px;">BDS by Policy Type</h3>
|
||||
<div class="table-container" style="width:100%;">
|
||||
@ -294,10 +284,19 @@
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Claims -->
|
||||
<h3 class="section-title" style="font-size:17px;color:#2c3e50;border-bottom:2px solid #3498db;display:inline-block;margin:18px 0 12px;padding-bottom:4px;">Claims</h3>
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card" style="width:100%;display:block;background:#f8f9fa;padding:12px 14px;border-radius:6px;border-left:4px solid #d35400;box-sizing:border-box;margin-bottom:10px;">
|
||||
<div class="stat-label">Total Claims Registered for the day</div>
|
||||
<div class="stat-value"><?= $total_claim_count ?></div>
|
||||
<div class="stat-note"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 class="section-title" style="font-size:17px;color:#2c3e50;border-bottom:2px solid #3498db;display:inline-block;margin:18px 0 12px;padding-bottom:4px;">Claim Status Breakdown</h3>
|
||||
<div style="padding:10px 8px;border:1px solid #eee;border-radius:4px;font-size:12px;text-align:center;color:#7f8c8d;margin-top:8px;">
|
||||
The total claims received so far are listed ticket type-wise.
|
||||
The total claims received so far are listed policy type-wise.
|
||||
</div>
|
||||
|
||||
<?php
|
||||
|
||||
@ -25,83 +25,102 @@
|
||||
|
||||
.dataTables_length label {height: 21px !important;}
|
||||
|
||||
.filter-sidebar {
|
||||
height: 100%;
|
||||
width: 0;
|
||||
position: fixed;
|
||||
z-index: 1001;
|
||||
top: 0;
|
||||
right: 0;
|
||||
background-color: #f8f9fa;
|
||||
overflow-x: hidden;
|
||||
transition: 0.5s;
|
||||
box-shadow: -2px 0 5px rgba(0,0,0,0.1);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.filter-sidebar-header {
|
||||
padding: 15px 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.filter-sidebar-header .closebtn {
|
||||
font-size: 28px;
|
||||
text-decoration: none;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.filter-sidebar .closebtn:hover {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.filter-sidebar-content {
|
||||
padding: 20px;
|
||||
flex-grow: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.filter-sidebar-footer {
|
||||
padding: 15px 20px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
border-top: 1px solid #dee2e6;
|
||||
flex-shrink: 0;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12" style="margin-top: -12px;">
|
||||
<div class="card-body">
|
||||
<div id="accordion" class="mb-3">
|
||||
<div class="card mb-1">
|
||||
<h4 class="m-1">
|
||||
<span>Filter</span>
|
||||
<a id="toggleIcon" class="text-dark float-right" data-toggle="collapse" href="#collapseOne"
|
||||
aria-expanded="true">
|
||||
<i id="icon" class="mdi mdi-chevron-down mr-1 text-primary" style="font-size: 28px;"></i>
|
||||
</a>
|
||||
</h4>
|
||||
<div id="collapseOne" class="collapse show" aria-labelledby="headingOne" data-parent="#accordion">
|
||||
<div class="card-body" style="margin-top: 0px;">
|
||||
<div class="form-group">
|
||||
<form method="post" action="<?= base_url("/dmsSearch"); ?>" id='dms_search_form'>
|
||||
<div class="form-row">
|
||||
<div id="filter-sidebar" class="filter-sidebar">
|
||||
<div class="filter-sidebar-header">
|
||||
<h4 class="m-0">Filter</h4>
|
||||
<a href="javascript:void(0)" class="closebtn" onclick="closeFilterNav()">×</a>
|
||||
</div>
|
||||
<div class="filter-sidebar-content">
|
||||
<form method="post" action="<?= base_url("/dmsSearch"); ?>" id='dms_search_form'>
|
||||
<div class="form-row">
|
||||
|
||||
<div class="form-group col-md-4">
|
||||
<label for="customer">Customer</label>
|
||||
<!-- <div style="position:relative;"> -->
|
||||
<input type="text" class="form-control" id="customer" placeholder="Enter customer name">
|
||||
<input type="hidden" name="customer_id" class="form-control" id="customer_id" placeholder="Enter customer name">
|
||||
<div id="customer_suggestions" class="autocomplete-suggestions" style="display:none;"></div>
|
||||
<!-- </div> -->
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-4">
|
||||
<label for="customer">Customer document name</label>
|
||||
<!-- <div style="position:relative;"> -->
|
||||
<input type="text" name="cus_doc_name" class="form-control" id="cus_doc_name" placeholder="Enter customer doc name ">
|
||||
<!-- </div> -->
|
||||
</div>
|
||||
<div class="form-group col-md-4"></div>
|
||||
|
||||
<div class="form-group col-md-8" style="text-align: center; font-size: small;">
|
||||
OR
|
||||
</div>
|
||||
<div class="form-group col-md-4"></div>
|
||||
|
||||
<div class="form-group col-md-4">
|
||||
<label for="customer">Policy</label>
|
||||
<!-- <div style="position:relative;"> -->
|
||||
<input type="text" class="form-control" id="policy" placeholder="Enter policy no">
|
||||
<input type="hidden" name="policy_id" class="form-control" id="policy_id" placeholder="Enter policy no">
|
||||
<div id="policy_suggestions" class="autocomplete-suggestions" style="display:none;"></div>
|
||||
<!-- </div> -->
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-4">
|
||||
<label for="customer">Policy document name</label>
|
||||
<!-- <div style="position:relative;"> -->
|
||||
<input type="text" name="policy_doc_name" class="form-control" id="policy_doc_name" placeholder="Enter policy doc name ">
|
||||
<!-- </div> -->
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-4 d-flex justify-content-end align-items-end">
|
||||
<button type="button" class="btn app-btn-outline-secondary mr-2" id="clear-filters">Clear</button>
|
||||
<button type="submit" class="btn btn-primary waves-effect waves-light mr-1" id="get-emp-list" onclick="fetchFileList(event);">Submit</button>
|
||||
<!-- <input type="submit" class="btn btn-primary waves-effect waves-light mr-1" id="get-emp-list" onclick="fetchFileList(event);" value="submit"> -->
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-12">
|
||||
<label for="customer">Customer</label>
|
||||
<input type="text" class="form-control" id="customer" placeholder="Enter customer name">
|
||||
<input type="hidden" name="customer_id" class="form-control" id="customer_id" placeholder="Enter customer name">
|
||||
<div id="customer_suggestions" class="autocomplete-suggestions" style="display:none;"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-12">
|
||||
<label for="customer">Customer document name</label>
|
||||
<input type="text" name="cus_doc_name" class="form-control" id="cus_doc_name" placeholder="Enter customer doc name ">
|
||||
</div>
|
||||
<div class="form-group col-md-12" style="text-align: center; font-size: small;">
|
||||
OR
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-12">
|
||||
<label for="customer">Policy</label>
|
||||
<input type="text" class="form-control" id="policy" placeholder="Enter policy no">
|
||||
<input type="hidden" name="policy_id" class="form-control" id="policy_id" placeholder="Enter policy no">
|
||||
<div id="policy_suggestions" class="autocomplete-suggestions" style="display:none;"></div>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-12">
|
||||
<label for="customer">Policy document name</label>
|
||||
<input type="text" name="policy_doc_name" class="form-control" id="policy_doc_name" placeholder="Enter policy doc name ">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="filter-sidebar-footer">
|
||||
<button type="button" class="btn btn-secondary" id="clear-filters">Clear</button>
|
||||
<button type="submit" class="btn btn-primary" id="get-emp-list" onclick="fetchFileList(event);closeFilterNav();">Submit</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- end page title -->
|
||||
<div class="col-12" id="second_page">
|
||||
<div class="card">
|
||||
@ -109,9 +128,7 @@
|
||||
<div class="row" style="padding-bottom: 10px;">
|
||||
<div class="col-6" style="align-self: center;">
|
||||
<h4 style="position: relative;">Files</h4>
|
||||
<!-- <h4 style="position: relative;">File search result</h4> -->
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="table-responsive" style="overflow-x: auto;">
|
||||
<table data-custom-table-css="table" id="scroll-horizontal-datatable" class="table w-100">
|
||||
@ -131,18 +148,15 @@
|
||||
<td><?php echo $row['doc_name']; ?></td>
|
||||
<td><?php echo $row['file_name']; ?></td>
|
||||
<td>
|
||||
<a id="download_1"
|
||||
data-id="1"
|
||||
class="mdi mdi-download"
|
||||
style="font-size: 18px;"
|
||||
<a id="download_1"
|
||||
data-id="1"
|
||||
class="mdi mdi-download"
|
||||
style="font-size: 18px;"
|
||||
target="_blank"
|
||||
href="<?= base_url("/downloadGdriveFile?client_id=" . $row['client_id'] . "&file_type=" . $row['file_type'] . "&file_name=" . $row['file_name']."&client_policy_id=".$row['client_policy_id']); ?>">
|
||||
href="<?= base_url("/downloadGdriveFile?client_id=" . $row['client_id'] . "&file_type=" . $row['file_type'] . "&file_name=" . $row['file_name']."&client_policy_id=".$row['client_policy_id']); ?>">
|
||||
</a>
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
</tr>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
<?php } ?>
|
||||
</tbody>
|
||||
@ -157,10 +171,15 @@
|
||||
|
||||
|
||||
<script>
|
||||
function openDMSFilterNav() {
|
||||
document.getElementById("filter-sidebar").style.width = "350px";
|
||||
}
|
||||
|
||||
function closeFilterNav() {
|
||||
document.getElementById("filter-sidebar").style.width = "0";
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
// $('#client_id').select2();
|
||||
// $('#insurer_id').select2();
|
||||
// $('#policy_type_id').select2();
|
||||
const customer_suggestions = <?php echo json_encode($customers); ?>;
|
||||
const policy_suggestions = <?php echo json_encode($policies); ?>;
|
||||
showSuggestions('customer', 'customer_suggestions', 'customer_id', customer_suggestions);
|
||||
@ -176,31 +195,39 @@
|
||||
if (ticketsTable.length) {
|
||||
ticketsTable.DataTable({
|
||||
scrollX: true,
|
||||
// dom: "<'row'<'col-12 d-flex justify-content-between align-items-center'<'datatable-search dataTables_filter'f><'datatable-buttons dt-buttons'B>>>" +
|
||||
// "<'row'<'col-sm-12'tr>>" +
|
||||
// "<'row'<'col-sm-5'i><'col-sm-7'p>>",
|
||||
dom: "<'row'<'col-12 d-flex justify-content-between align-items-center'<'datatable-search dataTables_filter'f><'datatable-buttons dt-buttons'B>>>" +
|
||||
"<'row'<'col-sm-12'tr>>" +
|
||||
"<'row align-items-center'<'col-5 text-start'i><'col-7 d-flex justify-content-end align-items-center'<'me-2'l>p>>",
|
||||
lengthMenu: [[10, 20, 50, 100], [10, 20, 50, 100]],
|
||||
buttons: [{
|
||||
extend: 'collection',
|
||||
text: '<span class=" btn-custom"> Export </span><i class="mdi mdi-menu-down"></i>',
|
||||
className: 'btn app-btn-secondary ',
|
||||
buttons: [{
|
||||
extend: 'csv',
|
||||
text: '<i class="mdi mdi-file-delimited " ></i><span class=" btn-custom"> CSV </span>',
|
||||
className: 'app-btn-primary ',
|
||||
buttons: [
|
||||
{
|
||||
text: '<i class="mdi mdi-filter"></i><span class="btn-custom"> Filter </span>',
|
||||
className: 'btn app-btn-primary mr-2',
|
||||
action: function (e, dt, node, config) {
|
||||
openDMSFilterNav();
|
||||
},
|
||||
{
|
||||
extend: 'excel',
|
||||
text: '<i class="mdi mdi-file-excel " ></i><span class=" btn-custom"> EXCEL </span>',
|
||||
exportOptions: {
|
||||
orthogonal: 'sort'
|
||||
},
|
||||
className: 'app-btn-primary ',
|
||||
attr: {
|
||||
id: 'openDMSFilterNav'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
extend: 'collection',
|
||||
text: '<span class=" btn-custom"> Export </span><i class="mdi mdi-menu-down"></i>',
|
||||
className: 'btn app-btn-secondary ',
|
||||
buttons: [{
|
||||
extend: 'csv',
|
||||
text: '<i class="mdi mdi-file-delimited " ></i><span class=" btn-custom"> CSV </span>',
|
||||
className: 'app-btn-primary ',
|
||||
},
|
||||
{
|
||||
extend: 'excel',
|
||||
text: '<i class="mdi mdi-file-excel " ></i><span class=" btn-custom"> EXCEL </span>',
|
||||
exportOptions: {
|
||||
orthogonal: 'sort'
|
||||
},
|
||||
className: 'app-btn-primary ',
|
||||
}
|
||||
]
|
||||
}],
|
||||
paging: true,
|
||||
pageLength: 10,
|
||||
@ -208,10 +235,10 @@
|
||||
search: `
|
||||
<div class="datatable-search-wrapper" style="position:relative; display:inline-block; width:100%;">
|
||||
_INPUT_
|
||||
<i class="mdi mdi-magnify datatable-search-icon"
|
||||
<i class="mdi mdi-magnify datatable-search-icon"
|
||||
style="position:absolute; right:10px; top:50%; transform:translateY(-50%); color:#666;"></i>
|
||||
|
||||
<i class="mdi mdi-close-circle datatable-clear-icon"
|
||||
<i class="mdi mdi-close-circle datatable-clear-icon"
|
||||
style="position:absolute; right:10px; top:50%; transform:translateY(-50%); color:#666; display:none; cursor:pointer;"></i>
|
||||
</div>
|
||||
`,
|
||||
@ -228,9 +255,6 @@
|
||||
});
|
||||
|
||||
function showSuggestions(inputID, suggestionBoxID, hiddenInputID, suggestions) {
|
||||
|
||||
// console.log('showSuggestions');
|
||||
// console.log(suggestions);return;
|
||||
const input = document.getElementById(inputID);
|
||||
const suggestionBox = document.getElementById(suggestionBoxID);
|
||||
const hiddenInput = document.getElementById(hiddenInputID);
|
||||
@ -243,7 +267,6 @@
|
||||
const filteredSuggestions = suggestions.filter(item =>
|
||||
item.display_value.toLowerCase().includes(value)
|
||||
);
|
||||
// console.log(filteredSuggestions);
|
||||
filteredSuggestions.forEach(suggestion => {
|
||||
const suggestionItem = document.createElement('div');
|
||||
suggestionItem.textContent = suggestion.display_value;
|
||||
@ -288,11 +311,15 @@
|
||||
alert('choose policy');
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
console.log('called');
|
||||
let customEvent = new CustomEvent('fetchFileListCustomEvent');
|
||||
$('#dms_search_form').submit();
|
||||
|
||||
}
|
||||
|
||||
document.getElementById('clear-filters').addEventListener('click', function() {
|
||||
document.getElementById('customer').value = '';
|
||||
document.getElementById('customer_id').value = '';
|
||||
document.getElementById('cus_doc_name').value = '';
|
||||
document.getElementById('policy').value = '';
|
||||
document.getElementById('policy_id').value = '';
|
||||
document.getElementById('policy_doc_name').value = '';
|
||||
});
|
||||
</script>
|
||||
@ -13,7 +13,7 @@ option:disabled {
|
||||
<div class="card mb-1">
|
||||
|
||||
<h4 class="m-1">
|
||||
<span>Filters</span>
|
||||
<!-- <span>Filters</span> -->
|
||||
<a id="toggleIcon1" class="text-dark float-right" data-toggle="collapse" href="#collapseTwo"
|
||||
aria-expanded="true">
|
||||
<i id="icon1" class="mdi mdi-chevron-down mr-1 text-primary" style="font-size: 28px;"></i>
|
||||
@ -658,6 +658,7 @@ function appendClients(data) {
|
||||
option.attr('selected', true);
|
||||
}
|
||||
$('#client_id').append(option);
|
||||
$('#client_id_for_filter').append(option.clone());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -13,50 +13,88 @@
|
||||
left: 79px;
|
||||
}
|
||||
.dataTables_length label {height: 21px !important;}
|
||||
.filter-sidebar {
|
||||
height: 100%;
|
||||
width: 0;
|
||||
position: fixed;
|
||||
z-index: 1001;
|
||||
top: 0;
|
||||
right: 0;
|
||||
background-color: #f8f9fa;
|
||||
overflow-x: hidden;
|
||||
transition: 0.5s;
|
||||
box-shadow: -2px 0 5px rgba(0,0,0,0.1);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.filter-sidebar-header {
|
||||
padding: 15px 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.filter-sidebar-header .closebtn {
|
||||
font-size: 28px;
|
||||
text-decoration: none;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.filter-sidebar .closebtn:hover {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.filter-sidebar-content {
|
||||
padding: 20px;
|
||||
flex-grow: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.filter-sidebar-footer {
|
||||
padding: 15px 20px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
border-top: 1px solid #dee2e6;
|
||||
flex-shrink: 0;
|
||||
gap: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="container-fluid-min">
|
||||
<div class="row">
|
||||
<div class="col-xl-12">
|
||||
<div class="card-body">
|
||||
<div id="accordion" class="mb-3">
|
||||
<div class="card mb-1">
|
||||
<h4>
|
||||
Filter
|
||||
<a id="toggleIcon" class="text-dark float-right" data-toggle="collapse" href="#collapseOne"
|
||||
aria-expanded="true">
|
||||
<i id="icon" class="mdi mdi-chevron-down mr-1 text-primary" style="font-size: 28px;"></i>
|
||||
</a>
|
||||
</h4>
|
||||
<div id="collapseOne" class="collapse show" aria-labelledby="headingOne" data-parent="#accordion">
|
||||
<div class="card-body">
|
||||
<!-- <div class="text-center"> -->
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-4">
|
||||
<label>Client</label> <br />
|
||||
<select class="form-control" id="clients">
|
||||
<option value="0">Select</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="filter-sidebar" class="filter-sidebar">
|
||||
<div class="filter-sidebar-header">
|
||||
<h4 class="m-0">Filter</h4>
|
||||
<a href="javascript:void(0)" class="closebtn" onclick="closeFilterNav()">×</a>
|
||||
</div>
|
||||
<div class="filter-sidebar-content">
|
||||
<div class="form-group">
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-12">
|
||||
<label>Client</label>
|
||||
<select class="form-control" id="clients">
|
||||
<option value="0">Select</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-12">
|
||||
<label>Branch</label>
|
||||
<select name="branch_id" class="form-control" id="branch_id">
|
||||
<option value="0">Select</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-4">
|
||||
<label>Branch</label> <br />
|
||||
<select name="branch_id" class="form-control" id="branch_id">
|
||||
<option value="0">Select</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group col-md-12">
|
||||
<label>Policy</label>
|
||||
<select class="form-control" id="policies">
|
||||
<option value="0">Select</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-4">
|
||||
<label>Policy</label> <br />
|
||||
<select class="form-control" id="policies">
|
||||
<option value="0">Select</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-4">
|
||||
<label>Status</label> <br />
|
||||
<div class="form-group col-md-12">
|
||||
<label>Status</label> <br />
|
||||
<select class="form-control" id="status1">
|
||||
<option value="0">Select</option>
|
||||
<?php foreach ($status as $key => $value) { ?>
|
||||
@ -65,21 +103,14 @@
|
||||
</option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12" style="text-align: right;">
|
||||
<a href="<?= base_url("employee/endorsement-list"); ?>" class="btn btn-primary waves-effect waves-light" id="get-emp-list" onclick="fetchEmpolyeeList(event);">Submit</a>
|
||||
</div>
|
||||
</div>
|
||||
<!-- </div> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="filter-sidebar-footer">
|
||||
<a href="<?= base_url("/employee/endorsement-list"); ?>" class="btn btn-primary" id="get-emp-list" onclick="fetchEmpolyeeList(event);closeFilterNav();">Submit</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -87,12 +118,12 @@
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="row" style="padding-bottom: 10px;">
|
||||
<!-- <div class="row" style="padding-bottom: 10px;">
|
||||
<div class="col-6" style="align-self: center;">
|
||||
<h4 style="position: relative;">Endorsements</h4>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div> -->
|
||||
<table data-custom-table-css="table" class="table table-hover m-0 table-centered dt-responsive w-100" cellspacing="0" id="tickets-table">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
@ -212,11 +243,9 @@
|
||||
|
||||
|
||||
function fetchClientPolicies() {
|
||||
|
||||
$('#loader').show();
|
||||
var apiURL = '<?php echo base_url(); ?>' + 'util/clients-with-policies';
|
||||
console.log('fetchClientPolicies');
|
||||
// console.log(apiURL);
|
||||
$.ajax({
|
||||
url: apiURL,
|
||||
method: 'GET',
|
||||
@ -225,9 +254,7 @@
|
||||
"X-Requested-With": "XMLHttpRequest"
|
||||
},
|
||||
success: function(response) {
|
||||
// console.log(response.code);
|
||||
// console.log(response.dataStatus);
|
||||
// console.log(response.data);
|
||||
$('#loader').hide(); // ✅ hide AFTER data received
|
||||
if (response.code === 200 && response.dataStatus === true && response.data !== "") {
|
||||
try {
|
||||
clientPolicies = (response.data);
|
||||
@ -280,13 +307,20 @@
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
$('#loader').hide(); // ✅ hide on error too
|
||||
console.error('Error fetching data from API:', error);
|
||||
}
|
||||
});
|
||||
|
||||
$('#loader').hide();
|
||||
}
|
||||
/* Modified by Gemini Code Assist */
|
||||
function openEndorsementListFilterNav() {
|
||||
document.getElementById("filter-sidebar").style.width = "350px";
|
||||
}
|
||||
|
||||
/* Modified by Gemini Code Assist */
|
||||
function closeFilterNav() {
|
||||
document.getElementById("filter-sidebar").style.width = "0";
|
||||
}
|
||||
|
||||
function appendClients(data) {
|
||||
|
||||
@ -432,11 +466,7 @@
|
||||
|
||||
}
|
||||
|
||||
document.getElementById('toggleIcon').addEventListener('click', function() {
|
||||
var icon = document.getElementById('icon');
|
||||
icon.classList.toggle('mdi-chevron-down');
|
||||
icon.classList.toggle('mdi-chevron-up');
|
||||
});
|
||||
|
||||
|
||||
|
||||
$('.emp_data_model').click(function() {
|
||||
@ -611,6 +641,13 @@
|
||||
// openTicket();
|
||||
// }
|
||||
// },
|
||||
{
|
||||
text: '<i class="mdi mdi-filter" ></i><span class=" btn-custom"> Filter </span>',
|
||||
className: 'btn app-btn-primary mr-2',
|
||||
action: function(e, dt, node, config) {
|
||||
openEndorsementListFilterNav();
|
||||
}
|
||||
},
|
||||
{
|
||||
extend: 'collection',
|
||||
text: '<span class=" btn-custom"> Export </span><i class="mdi mdi-menu-down"></i>',
|
||||
@ -632,24 +669,6 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
// buttons: [{
|
||||
// extend: 'csv',
|
||||
// text: 'CSV',
|
||||
// title: 'Endorsement-List',
|
||||
// className: 'my_class',
|
||||
// exportOptions: {
|
||||
// columns: ':not(:last-child)',
|
||||
// format: {
|
||||
// body: function (data, row, column, node) {
|
||||
// // Convert data to string to avoid scientific notation in CSV
|
||||
// if (!isNaN(data) && parseFloat(data) > 1e20) {
|
||||
// return `'${data}`;
|
||||
// }
|
||||
// return data.toString();// Ensures all data is treated as strings
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }],
|
||||
initComplete: function(settings, json) {
|
||||
$('.my_class').css({
|
||||
"position": "relative",
|
||||
|
||||
@ -1,3 +1,35 @@
|
||||
<style>
|
||||
|
||||
.truncate {
|
||||
max-width: 80px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.desc-cell {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
#expense-desc-popup {
|
||||
position: absolute;
|
||||
z-index: 9999;
|
||||
background: #fff;
|
||||
border: 1px solid #ddd;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||
padding: 8px 10px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
max-width: 320px;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
display: none;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<div class="row" id="expense_module">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
@ -80,7 +112,18 @@
|
||||
|
||||
<div class="form-group col-md-4">
|
||||
<label for="amount">Amount <span class="text-danger">*</span></label>
|
||||
<input type="number" step="0.01" min="0" class="form-control" id="amount" name="amount" placeholder="Enter amount" value="<?= isset($filters['amount']) ? esc($filters['amount']) : ''; ?>" required>
|
||||
<input
|
||||
type="number"
|
||||
step="0.01"
|
||||
min="0"
|
||||
max="100000000"
|
||||
class="form-control"
|
||||
id="amount"
|
||||
name="amount"
|
||||
placeholder="Enter amount"
|
||||
value="<?= isset($filters['amount']) ? esc($filters['amount']) : ''; ?>"
|
||||
required
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-4">
|
||||
@ -126,7 +169,16 @@
|
||||
<?= ! empty($row['short_name']) ? ' (' . esc($row['short_name']) . ')' : ''; ?>
|
||||
</td>
|
||||
<td><?= esc($row['policy_no'] ?? ''); ?></td>
|
||||
<td><?= esc($row['description'] ?? ''); ?></td>
|
||||
<td class="desc-cell">
|
||||
<i
|
||||
class="mdi mdi-information-outline text-muted expense-desc-icon"
|
||||
style="cursor: pointer;"
|
||||
data-description="<?= esc($row['description'] ?? ''); ?>"
|
||||
></i>
|
||||
<span class="truncate">
|
||||
<?= esc($row['description'] ?? ''); ?>
|
||||
</span>
|
||||
</td>
|
||||
<td><?= esc($row['approved_by_name'] ?? ''); ?></td>
|
||||
<td><?= number_format((float) ($row['amount'] ?? 0), 2); ?></td>
|
||||
<td>
|
||||
@ -213,8 +265,13 @@
|
||||
|
||||
if (!amount) {
|
||||
errors.push('Amount is required.');
|
||||
} else if (isNaN(amount) || Number(amount) < 0) {
|
||||
errors.push('Amount must be a non-negative number.');
|
||||
} else {
|
||||
const amountNum = Number(amount);
|
||||
if (isNaN(amountNum) || amountNum < 0) {
|
||||
errors.push('Amount must be a non-negative number.');
|
||||
} else if (amountNum > 100000000) {
|
||||
errors.push('Amount cannot be greater than 100 Cr.');
|
||||
}
|
||||
}
|
||||
|
||||
if (!expenseDate) {
|
||||
@ -261,8 +318,11 @@
|
||||
}
|
||||
|
||||
if (amount) {
|
||||
if (isNaN(amount) || Number(amount) < 0) {
|
||||
const amountNum = Number(amount);
|
||||
if (isNaN(amountNum) || amountNum < 0) {
|
||||
errors.push('Amount filter must be a non-negative number.');
|
||||
} else if (amountNum > 100000000) {
|
||||
errors.push('Amount filter cannot be greater than 100 Cr.');
|
||||
}
|
||||
}
|
||||
|
||||
@ -430,10 +490,12 @@
|
||||
|
||||
window.expenseDatePicker = flatpickr('#expense_date', {
|
||||
dateFormat: 'd-m-Y',
|
||||
allowInput: true
|
||||
allowInput: false,
|
||||
maxDate: new Date(),
|
||||
});
|
||||
|
||||
$('#expense-table').DataTable({
|
||||
scrollX: true,
|
||||
dom: "<'row'<'col-12 d-flex justify-content-between align-items-center'<'datatable-search dataTables_filter'f><'datatable-buttons dt-buttons'B>>>" +
|
||||
"<'row'<'col-sm-12'tr>>" +
|
||||
"<'row align-items-center'<'col-5 text-start'i><'col-7 d-flex justify-content-end align-items-center'<'me-2'l>p>>",
|
||||
@ -450,7 +512,18 @@
|
||||
title: 'Expense List',
|
||||
className: 'app-btn-primary ',
|
||||
exportOptions: {
|
||||
columns: ':not(:last-child)'
|
||||
columns: ':not(:last-child)',
|
||||
format: {
|
||||
body: function (data, row, column, node) {
|
||||
// Column index 2 is "Policy No" (S.No=0, Client=1, Policy No=2)
|
||||
if (column === 2) {
|
||||
// Prefix with apostrophe to force Excel treat as text and preserve long numbers
|
||||
var text = $(node).text ? $(node).text() : data;
|
||||
return "'" + text;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -461,7 +534,16 @@
|
||||
className: 'app-btn-primary ',
|
||||
exportOptions: {
|
||||
orthogonal: 'sort',
|
||||
columns: ':not(:last-child)'
|
||||
columns: ':not(:last-child)',
|
||||
format: {
|
||||
body: function (data, row, column, node) {
|
||||
if (column === 2) {
|
||||
var text = $(node).text ? $(node).text() : data;
|
||||
return "'" + text;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -581,6 +663,34 @@
|
||||
$('#btnResetExpense, #btnCancelExpense').on('click', function () {
|
||||
resetExpenseForm();
|
||||
});
|
||||
|
||||
// Show small popup near the info icon on hover (no tooltip)
|
||||
let $popup = $('#expense-desc-popup');
|
||||
if (!$popup.length) {
|
||||
$popup = $('<div id="expense-desc-popup"></div>').appendTo('body');
|
||||
}
|
||||
|
||||
$(document).on('mouseenter', '.expense-desc-icon', function () {
|
||||
const desc = $(this).data('description') || '';
|
||||
if (!desc) {
|
||||
return;
|
||||
}
|
||||
|
||||
$popup.text(desc);
|
||||
|
||||
const offset = $(this).offset();
|
||||
const iconHeight = $(this).outerHeight() || 16;
|
||||
|
||||
$popup.css({
|
||||
top: offset.top + iconHeight + 4,
|
||||
left: offset.left,
|
||||
display: 'block'
|
||||
});
|
||||
});
|
||||
|
||||
$(document).on('mouseleave', '.expense-desc-icon', function () {
|
||||
$popup.hide();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@ -36,7 +36,7 @@
|
||||
<div class="row" id="List-page">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body maincard">
|
||||
<div class="card-body">
|
||||
<table data-custom-table-css="table" class="table table-sm m-0 table-centered dt-responsive nowrap w-100" cellspacing="a" id="user-table">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
@ -60,10 +60,11 @@
|
||||
? htmlspecialchars(substr($category, 0, 50)) . "..."
|
||||
: htmlspecialchars($category);
|
||||
?></td>
|
||||
<td><?php $question = $row['question'] ? $row['question'] : 'N/A';
|
||||
echo (strlen($question) > 50)
|
||||
? htmlspecialchars(substr($question, 0, 50)) . "..."
|
||||
: htmlspecialchars($question);
|
||||
<td><?php
|
||||
$question_text = $row['question'] ? strip_tags($row['question']) : 'N/A';
|
||||
echo (strlen($question_text) > 50)
|
||||
? htmlspecialchars(substr($question_text, 0, 50)) . "..."
|
||||
: htmlspecialchars($question_text);
|
||||
?></td>
|
||||
<!-- <td><?php $answer = $row['answer'] ? $row['answer'] : 'N/A';
|
||||
echo (strlen($answer) > 50)
|
||||
@ -324,7 +325,7 @@
|
||||
}
|
||||
$('.loader, .loader-mask').fadeOut();
|
||||
},
|
||||
error: function () {
|
||||
error: function (xhr, status, error) {
|
||||
$('.loader, .loader-mask').fadeOut();
|
||||
if (xhr.status === 400) {
|
||||
let response = JSON.parse(xhr.responseText);
|
||||
@ -366,7 +367,8 @@
|
||||
$('#FAQForm')[0].reset();
|
||||
$('#faq_id').val(data.id);
|
||||
$('#category').val(data.category);
|
||||
$('#question').val(data.question);
|
||||
// Safely set the question value by stripping any HTML tags
|
||||
$('#question').val($('<div/>').html(data.question || '').text());
|
||||
if (editor) {
|
||||
editor.value = data.answer || '';
|
||||
}
|
||||
@ -374,6 +376,24 @@
|
||||
}
|
||||
|
||||
|
||||
// Simple client-side XSS pattern check to give instant feedback
|
||||
function hasUnsafeHtml(html) {
|
||||
if (!html) return false;
|
||||
|
||||
const decoded = $('<textarea/>').html(html).text().toLowerCase();
|
||||
|
||||
const patterns = [
|
||||
/<\s*script/i,
|
||||
/on\w+\s*=/i,
|
||||
/javascript\s*:/i,
|
||||
/vbscript\s*:/i,
|
||||
/data\s*:\s*text\/html/i,
|
||||
/expression\s*\(/i
|
||||
];
|
||||
|
||||
return patterns.some(p => p.test(decoded));
|
||||
}
|
||||
|
||||
$('#FAQForm').on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
const form = this;
|
||||
@ -393,6 +413,12 @@
|
||||
toastr.warning("Answer is required", 'Warning');
|
||||
return; // Stop the function here
|
||||
}
|
||||
|
||||
// Block unsafe HTML/JS patterns
|
||||
if (hasUnsafeHtml(editor.value)) {
|
||||
toastr.error("Answer contains restricted tags or attributes. Script tags, iframes and event handlers (like onclick, onerror, onmouseover) are not allowed.", "Validation Error");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If valid, submit via AJAX
|
||||
|
||||
@ -8,10 +8,36 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dataTables_length label {height: 21px !important;}
|
||||
|
||||
.column-header { margin-right: 10px; /* Adjust this value as needed */ }
|
||||
|
||||
/* Compact table + buttons (NHance) */
|
||||
#tickets-table thead th,
|
||||
#tickets-table tbody td {
|
||||
padding: 6px 12px !important;
|
||||
font-size: 13px;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
#tickets-table .font-12 {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* Action dropdown toggle in rows */
|
||||
#tickets-table .dropdown-toggle.btn-sm {
|
||||
padding: 4px 9px !important;
|
||||
font-size: 13px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
/* DataTables toolbar buttons (Export/Filter/Clear Filter) */
|
||||
#tickets-table_wrapper .dt-buttons .btn {
|
||||
padding: 6px 13px !important;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
#tickets-table_wrapper .dt-buttons .btn .btn-custom {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@ -170,177 +196,110 @@
|
||||
|
||||
|
||||
<script>
|
||||
$('body').on('click', '.view_emp_list', function() {
|
||||
|
||||
console.log('file_id', 'file_id');
|
||||
$('#emp_data_success').empty();
|
||||
$('#title').html(' ');
|
||||
var file_id = $(this).attr('data-id');
|
||||
console.log(file_id);
|
||||
$('body').on('click', '.view_emp_list', function() {
|
||||
|
||||
var queryParams = {
|
||||
file_id: file_id,
|
||||
};
|
||||
console.log('file_id', 'file_id');
|
||||
$('#emp_data_success').empty();
|
||||
$('#title').html(' ');
|
||||
var file_id = $(this).attr('data-id');
|
||||
console.log(file_id);
|
||||
|
||||
console.log('queryParams', queryParams)
|
||||
const queryString = objectToQueryString(queryParams);
|
||||
console.log('queryString', queryString)
|
||||
|
||||
$('.loader').fadeIn();
|
||||
$('.loader-mask').fadeIn();
|
||||
var uri = '<?= base_url('util/view-success-emp-list') ?>?' + queryString
|
||||
console.log(uri)
|
||||
|
||||
$.ajax({
|
||||
url: uri,
|
||||
data: {
|
||||
var queryParams = {
|
||||
file_id: file_id,
|
||||
},
|
||||
type: "GET",
|
||||
dataType: 'json',
|
||||
processData: false,
|
||||
contentType: false,
|
||||
success: function(res) {
|
||||
};
|
||||
|
||||
console.log(res);
|
||||
console.log('queryParams', queryParams)
|
||||
const queryString = objectToQueryString(queryParams);
|
||||
console.log('queryString', queryString)
|
||||
|
||||
if (res) {
|
||||
$('.loader').fadeIn();
|
||||
$('.loader-mask').fadeIn();
|
||||
var uri = '<?= base_url('util/view-success-emp-list') ?>?' + queryString
|
||||
console.log(uri)
|
||||
|
||||
$.ajax({
|
||||
url: uri,
|
||||
data: {
|
||||
file_id: file_id,
|
||||
},
|
||||
type: "GET",
|
||||
dataType: 'json',
|
||||
processData: false,
|
||||
contentType: false,
|
||||
success: function(res) {
|
||||
|
||||
console.log(res);
|
||||
|
||||
if (res) {
|
||||
setTimeout(function() {
|
||||
$('.loader').fadeOut();
|
||||
$('.loader-mask').delay(350).fadeOut('slow');
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
var title = " - ";
|
||||
|
||||
if (res.file_data) {
|
||||
title += (res.file_data.file_name || "") + " - ";
|
||||
title += (res.file_data.short_name || "") + " - ";
|
||||
title += (res.file_data.policy_name || "") + " - ";
|
||||
title += (res.file_data.action || "") + " - ";
|
||||
title += (res.file_data.status || "");
|
||||
}
|
||||
|
||||
$('#title_header_name').html(title);
|
||||
$('#emp_data_success').html(res.data);
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error(xhr.responseText);
|
||||
console.error(status, error);
|
||||
setTimeout(function() {
|
||||
$('.loader').fadeOut();
|
||||
$('.loader-mask').delay(350).fadeOut('slow');
|
||||
toastr.warning('Something went wrong', 'Warning');
|
||||
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
|
||||
var title = " - ";
|
||||
})
|
||||
|
||||
if (res.file_data) {
|
||||
title += (res.file_data.file_name || "") + " - ";
|
||||
title += (res.file_data.short_name || "") + " - ";
|
||||
title += (res.file_data.policy_name || "") + " - ";
|
||||
title += (res.file_data.action || "") + " - ";
|
||||
title += (res.file_data.status || "");
|
||||
}
|
||||
$('body').on('click', '.upload_button', function() {
|
||||
|
||||
$('#title_header_name').html(title);
|
||||
$('#emp_data_success').html(res.data);
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error(xhr.responseText);
|
||||
console.error(status, error);
|
||||
setTimeout(function() {
|
||||
$('.loader').fadeOut();
|
||||
$('.loader-mask').delay(350).fadeOut('slow');
|
||||
toastr.warning('Something went wrong', 'Warning');
|
||||
$('#uploadForm')[0].reset();
|
||||
console.log('file_id');
|
||||
var fileId = JSON.parse(this.getAttribute('data-id'));
|
||||
console.log(fileId); // Use fileId as needed
|
||||
$('#file_client_id').val(fileId.client_id)
|
||||
$('#file_policy_id').val(fileId.client_policy_id)
|
||||
$('#file_branch_id').val(fileId.client_branch_id)
|
||||
$('#file_upload_actions').val(fileId.action)
|
||||
})
|
||||
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
$('body').on('click', '.truncate2', function(event) {
|
||||
|
||||
})
|
||||
event.preventDefault();
|
||||
// console.log(event);
|
||||
var fileId = JSON.parse(this.getAttribute('data-id'));
|
||||
console.log('Truncate File ID' + fileId);
|
||||
|
||||
|
||||
|
||||
$('body').on('click', '.upload_button', function() {
|
||||
|
||||
$('#uploadForm')[0].reset();
|
||||
console.log('file_id');
|
||||
var fileId = JSON.parse(this.getAttribute('data-id'));
|
||||
console.log(fileId); // Use fileId as needed
|
||||
$('#file_client_id').val(fileId.client_id)
|
||||
$('#file_policy_id').val(fileId.client_policy_id)
|
||||
$('#file_branch_id').val(fileId.client_branch_id)
|
||||
$('#file_upload_actions').val(fileId.action)
|
||||
})
|
||||
|
||||
|
||||
$('body').on('click', '.truncate2', function(event) {
|
||||
|
||||
event.preventDefault();
|
||||
// console.log(event);
|
||||
var fileId = JSON.parse(this.getAttribute('data-id'));
|
||||
console.log('Truncate File ID' + fileId);
|
||||
|
||||
Swal.fire({
|
||||
title: "Do you want to truncate data from uploaded file?",
|
||||
showCancelButton: true,
|
||||
confirmButtonText: "Delete",
|
||||
confirmButtonColor: "#ff3333",
|
||||
}).then((result) => {
|
||||
|
||||
console.log(result);
|
||||
|
||||
if (result.isConfirmed) {
|
||||
|
||||
$('.loader').fadeIn();
|
||||
$('.loader-mask').fadeIn();
|
||||
var apiURL = '<?php echo base_url(); ?>employee/truncate/' + fileId;
|
||||
// console.log('Truncate API URL : ', apiURL);
|
||||
|
||||
$.ajax({
|
||||
url: apiURL,
|
||||
method: 'GET',
|
||||
headers: {
|
||||
"X-Requested-With": "XMLHttpRequest"
|
||||
},
|
||||
success: function(response) {
|
||||
|
||||
// console.log('Truncate Response', response)
|
||||
|
||||
setTimeout(function() {
|
||||
$('.loader').fadeOut();
|
||||
$('.loader-mask').delay(350).fadeOut('slow');
|
||||
}, 1000);
|
||||
|
||||
if (response.code === 200 && response.dataStatus === true && response.data !== "") {
|
||||
Swal.fire({
|
||||
title: "Deleted!",
|
||||
icon: "success"
|
||||
});
|
||||
} else if (response.code === 404 && response.dataStatus === false) {
|
||||
console.error('No data found', response);
|
||||
handleNoDataFound(response, fileId);
|
||||
} else {
|
||||
Swal.fire({
|
||||
title: "Failed!",
|
||||
text: 'Something went wrong! Try later',
|
||||
icon: "error"
|
||||
});
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
|
||||
console.error(xhr.responseText);
|
||||
console.error(status, error);
|
||||
|
||||
setTimeout(function() {
|
||||
$('.loader').fadeOut();
|
||||
$('.loader-mask').delay(350).fadeOut('slow');
|
||||
}, 1000);
|
||||
|
||||
console.error('Error fetching data from API:', error);
|
||||
toastr.error('Something went wrong! Try later', 'Error');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
})
|
||||
|
||||
function handleNoDataFound(response, fileId) {
|
||||
if (response.role == 1 || response.role == 5) {
|
||||
Swal.fire({
|
||||
title: response.message,
|
||||
title: "Do you want to truncate data from uploaded file?",
|
||||
showCancelButton: true,
|
||||
confirmButtonText: "Delete",
|
||||
confirmButtonColor: "#ff3333",
|
||||
}).then((result) => {
|
||||
|
||||
console.log(result);
|
||||
|
||||
if (result.isConfirmed) {
|
||||
|
||||
$('.loader').fadeIn();
|
||||
$('.loader-mask').fadeIn();
|
||||
var apiURL = '<?php echo base_url(); ?>employee/truncate/' + fileId + '/' + <?= get_role_id(); ?>;
|
||||
|
||||
var apiURL = '<?php echo base_url(); ?>employee/truncate/' + fileId;
|
||||
// console.log('Truncate API URL : ', apiURL);
|
||||
|
||||
$.ajax({
|
||||
url: apiURL,
|
||||
method: 'GET',
|
||||
@ -348,6 +307,9 @@ function handleNoDataFound(response, fileId) {
|
||||
"X-Requested-With": "XMLHttpRequest"
|
||||
},
|
||||
success: function(response) {
|
||||
|
||||
// console.log('Truncate Response', response)
|
||||
|
||||
setTimeout(function() {
|
||||
$('.loader').fadeOut();
|
||||
$('.loader-mask').delay(350).fadeOut('slow');
|
||||
@ -360,11 +322,7 @@ function handleNoDataFound(response, fileId) {
|
||||
});
|
||||
} else if (response.code === 404 && response.dataStatus === false) {
|
||||
console.error('No data found', response);
|
||||
Swal.fire({
|
||||
title: "Failed!",
|
||||
text: response.message,
|
||||
icon: "error"
|
||||
});
|
||||
handleNoDataFound(response, fileId);
|
||||
} else {
|
||||
Swal.fire({
|
||||
title: "Failed!",
|
||||
@ -374,127 +332,201 @@ function handleNoDataFound(response, fileId) {
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
|
||||
console.error(xhr.responseText);
|
||||
console.error(status, error);
|
||||
|
||||
setTimeout(function() {
|
||||
$('.loader').fadeOut();
|
||||
$('.loader-mask').delay(350).fadeOut('slow');
|
||||
}, 1000);
|
||||
|
||||
console.error('Error fetching data from API:', error);
|
||||
console.error('Something went wrong! Try later', 'Error');
|
||||
toastr.error('Something went wrong! Try later', 'Error');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
} else {
|
||||
Swal.fire({
|
||||
title: "Failed!",
|
||||
text: response.message,
|
||||
icon: "error"
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
$('#uploadForm').submit(function() {
|
||||
function handleNoDataFound(response, fileId) {
|
||||
if (response.role == 1 || response.role == 5) {
|
||||
Swal.fire({
|
||||
title: response.message,
|
||||
showCancelButton: true,
|
||||
confirmButtonText: "Delete",
|
||||
confirmButtonColor: "#ff3333",
|
||||
}).then((result) => {
|
||||
|
||||
var isValid = $('#uploadForm').parsley().validate();
|
||||
if (!isValid) {
|
||||
console.log('Form is Empty', 'Warning');
|
||||
return;
|
||||
console.log(result);
|
||||
if (result.isConfirmed) {
|
||||
$('.loader').fadeIn();
|
||||
$('.loader-mask').fadeIn();
|
||||
var apiURL = '<?php echo base_url(); ?>employee/truncate/' + fileId + '/' + <?= get_role_id(); ?>;
|
||||
|
||||
$.ajax({
|
||||
url: apiURL,
|
||||
method: 'GET',
|
||||
headers: {
|
||||
"X-Requested-With": "XMLHttpRequest"
|
||||
},
|
||||
success: function(response) {
|
||||
setTimeout(function() {
|
||||
$('.loader').fadeOut();
|
||||
$('.loader-mask').delay(350).fadeOut('slow');
|
||||
}, 1000);
|
||||
|
||||
if (response.code === 200 && response.dataStatus === true && response.data !== "") {
|
||||
Swal.fire({
|
||||
title: "Deleted!",
|
||||
icon: "success"
|
||||
});
|
||||
} else if (response.code === 404 && response.dataStatus === false) {
|
||||
console.error('No data found', response);
|
||||
Swal.fire({
|
||||
title: "Failed!",
|
||||
text: response.message,
|
||||
icon: "error"
|
||||
});
|
||||
} else {
|
||||
Swal.fire({
|
||||
title: "Failed!",
|
||||
text: 'Something went wrong! Try later',
|
||||
icon: "error"
|
||||
});
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
setTimeout(function() {
|
||||
$('.loader').fadeOut();
|
||||
$('.loader-mask').delay(350).fadeOut('slow');
|
||||
}, 1000);
|
||||
|
||||
console.error('Error fetching data from API:', error);
|
||||
console.error('Something went wrong! Try later', 'Error');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Swal.fire({
|
||||
title: "Failed!",
|
||||
text: response.message,
|
||||
icon: "error"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Create FormData object
|
||||
var formData = new FormData($(this)[0]);
|
||||
for (var pair of formData.entries()) {
|
||||
console.log(pair[0] + ', ' + pair[1]);
|
||||
}
|
||||
$('#uploadForm').submit(function() {
|
||||
|
||||
// return false;
|
||||
$.ajax({
|
||||
url: $(this).attr("action"),
|
||||
type: "POST",
|
||||
data: formData,
|
||||
processData: false, // Prevent jQuery from automatically processing the data
|
||||
contentType: false, // Let jQuery handle the content type
|
||||
headers: {
|
||||
// "Content-Type":"multipart/form-data",
|
||||
"X-Requested-With": "XMLHttpRequest"
|
||||
},
|
||||
success: function(response) {
|
||||
var isValid = $('#uploadForm').parsley().validate();
|
||||
if (!isValid) {
|
||||
console.log('Form is Empty', 'Warning');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(response);
|
||||
$('#uploadForm')[0].reset();
|
||||
// Create FormData object
|
||||
var formData = new FormData($(this)[0]);
|
||||
for (var pair of formData.entries()) {
|
||||
console.log(pair[0] + ', ' + pair[1]);
|
||||
}
|
||||
|
||||
if (response.code === 200 && response.dataStatus === true && response
|
||||
.data !== "") {
|
||||
toastr.success(
|
||||
'File upload successs, Data validation is in-progress',
|
||||
'Success');
|
||||
$('.close').click()
|
||||
window.location.reload(true);
|
||||
} else if (response.code === 404 && response.dataStatus === false) {
|
||||
console.error('no data found', response);
|
||||
// alert(response.message);
|
||||
toastr.error(response.message, 'Failed');
|
||||
window.location.reload(true);
|
||||
} else {
|
||||
console.error('Something went wrong!');
|
||||
// alert('Something went wrong! Try later');
|
||||
// return false;
|
||||
$.ajax({
|
||||
url: $(this).attr("action"),
|
||||
type: "POST",
|
||||
data: formData,
|
||||
processData: false, // Prevent jQuery from automatically processing the data
|
||||
contentType: false, // Let jQuery handle the content type
|
||||
headers: {
|
||||
// "Content-Type":"multipart/form-data",
|
||||
"X-Requested-With": "XMLHttpRequest"
|
||||
},
|
||||
success: function(response) {
|
||||
|
||||
console.log(response);
|
||||
$('#uploadForm')[0].reset();
|
||||
|
||||
if (response.code === 200 && response.dataStatus === true && response
|
||||
.data !== "") {
|
||||
toastr.success(
|
||||
'File upload successs, Data validation is in-progress',
|
||||
'Success');
|
||||
$('.close').click()
|
||||
window.location.reload(true);
|
||||
} else if (response.code === 404 && response.dataStatus === false) {
|
||||
console.error('no data found', response);
|
||||
// alert(response.message);
|
||||
toastr.error(response.message, 'Failed');
|
||||
window.location.reload(true);
|
||||
} else {
|
||||
console.error('Something went wrong!');
|
||||
// alert('Something went wrong! Try later');
|
||||
toastr.error('Something went wrong! Try later', 'Error');
|
||||
window.location.reload(true);
|
||||
}
|
||||
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
// Request failed, handle error
|
||||
console.error("Request failed:", status, error);
|
||||
toastr.error('Something went wrong! Try later', 'Error');
|
||||
$('#uploadForm')[0].reset();
|
||||
window.location.reload(true);
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
// Request failed, handle error
|
||||
console.error("Request failed:", status, error);
|
||||
toastr.error('Something went wrong! Try later', 'Error');
|
||||
$('#uploadForm')[0].reset();
|
||||
$('body').on('click', '.reload', function() {
|
||||
|
||||
console.log('status');
|
||||
status = this.getAttribute('data-id')
|
||||
console.log(status);
|
||||
if (status == 'inprogress') {
|
||||
window.location.reload(true);
|
||||
}
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
$('body').on('click', '.reload', function() {
|
||||
$(document).ready(function() {
|
||||
|
||||
console.log('status');
|
||||
status = this.getAttribute('data-id')
|
||||
console.log(status);
|
||||
if (status == 'inprogress') {
|
||||
window.location.reload(true);
|
||||
}
|
||||
})
|
||||
$('#tickets-table').DataTable({
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
$('#tickets-table').DataTable({
|
||||
|
||||
// dom: "<'row'<'col-12 d-flex justify-content-between align-items-center'<'datatable-search dataTables_filter'f><'datatable-buttons dt-buttons'B>>>" +
|
||||
|
||||
|
||||
|
||||
// "<'row'<'col-sm-12'tr>>" +
|
||||
// "<'row'<'col-sm-5'i><'col-sm-7'p>>",
|
||||
dom: "<'row'<'col-12 d-flex justify-content-between align-items-center'<'datatable-search dataTables_filter'f><'datatable-buttons dt-buttons'B>>>" +
|
||||
dom: "<'row'<'col-sm-7'f><'col-sm-5 text-right'B>>" + // Filter left, buttons right
|
||||
"<'row'<'col-sm-12'tr>>" +
|
||||
"<'row align-items-center'<'col-5 text-start'i><'col-7 d-flex justify-content-end align-items-center'<'me-2'l>p>>",
|
||||
lengthMenu: [[10, 20, 50, 100], [10, 20, 50, 100]],
|
||||
buttons: [{
|
||||
extend: 'collection',
|
||||
text: '<span class=" btn-custom"> Export </span><i class="mdi mdi-menu-down"></i>',
|
||||
className: 'btn app-btn-secondary ',
|
||||
buttons: [
|
||||
{
|
||||
extend: 'csv',
|
||||
text: '<i class="mdi mdi-file-delimited " ></i><span class=" btn-custom"> CSV </span>',
|
||||
title: 'Employee-Upload-List',
|
||||
className: 'app-btn-primary ',
|
||||
exportOptions: {
|
||||
columns: ':not(:last-child)'
|
||||
},
|
||||
}
|
||||
]
|
||||
"<'row align-items-center'<'col-5 text-start'i><'col-7 d-flex justify-content-end align-items-center'<'me-2'l>p>>", lengthMenu: [[10, 20, 50, 100], [10, 20, 50, 100]],
|
||||
buttons: [
|
||||
{
|
||||
extend: 'collection',
|
||||
text: '<span class=" btn-custom"> Export </span><i class="mdi mdi-menu-down"></i>',
|
||||
className: 'btn app-btn-secondary mr-2',
|
||||
buttons: [
|
||||
{
|
||||
extend: 'csv',
|
||||
text: '<i class="mdi mdi-file-delimited " ></i><span class=" btn-custom"> CSV </span>',
|
||||
title: 'Employee-Upload-List',
|
||||
className: 'app-btn-primary ',
|
||||
exportOptions: {
|
||||
columns: ':not(:last-child)'
|
||||
},
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
text: '<i class="mdi mdi-filter" ></i><span class=" btn-custom"> Filter </span>',
|
||||
className: 'btn app-btn-primary mr-2',
|
||||
action: function(e, dt, node, config) {
|
||||
openPolicyFilterNav(1);
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
text: '<i class="mdi mdi-filter-remove"></i><span class="btn-custom"> Clear Filter </span>',
|
||||
className: 'btn app-btn-info mr-2 hide-clear-filter',
|
||||
action: function(e, dt, node, config) {
|
||||
clearFilterData();
|
||||
}
|
||||
}
|
||||
],
|
||||
language: {
|
||||
search: `
|
||||
<div class="datatable-search-wrapper" style="position:relative; display:inline-block;">
|
||||
@ -507,27 +539,9 @@ $(document).ready(function() {
|
||||
searchPlaceholder: "Search",
|
||||
emptyTable: '<div class="text-center text-muted">No Data found</div>'
|
||||
},
|
||||
// "buttons": [{
|
||||
// "extend": 'csv',
|
||||
// "text": 'CSV',
|
||||
// "title": 'Employee-Upload-List',
|
||||
// "className": 'my_class',
|
||||
// "exportOptions": {
|
||||
// "columns": ':not(:last-child)'
|
||||
// },
|
||||
// }],
|
||||
// "initComplete": function(settings, json) {
|
||||
// $('.my_class').css({
|
||||
// "position": "relative",
|
||||
// "left": "79px"
|
||||
// });
|
||||
// },
|
||||
// language: {
|
||||
// search: "_INPUT_",
|
||||
// searchPlaceholder: "Search..."
|
||||
// },
|
||||
paging: true,
|
||||
paging: true,
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
@ -35,7 +35,7 @@
|
||||
<div class="row" id="List-page">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body maincard">
|
||||
<div class="card-body">
|
||||
<table data-custom-table-css="table" class="table table-sm m-0 table-centered dt-responsive nowrap w-100" cellspacing="a" id="user-table">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
@ -447,7 +447,7 @@
|
||||
}
|
||||
$('.loader, .loader-mask').fadeOut();
|
||||
},
|
||||
error: function () {
|
||||
error: function (xhr, status, error) {
|
||||
$('.loader, .loader-mask').fadeOut();
|
||||
if (xhr.status === 400) {
|
||||
let response = JSON.parse(xhr.responseText);
|
||||
@ -500,8 +500,29 @@
|
||||
$('#content_section').val(data.content_section);
|
||||
}
|
||||
$('#heading').val(data.heading);
|
||||
// $('#content').val(data.content);
|
||||
// $('#notes').val(data.notes);
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* FIX & EXPLANATION: Safely Loading HTML into the Editor
|
||||
* --------------------------------------------------------------------------
|
||||
* ISSUE:
|
||||
* Loading HTML directly from an AJAX response into a webpage can be a
|
||||
* major security risk (XSS). One might think `data.content` is unsafe.
|
||||
*
|
||||
* RESOLUTION:
|
||||
* This operation is SAFE and CORRECT because of the "defense-in-depth"
|
||||
* strategy we implemented in the `AppContentManagementController`.
|
||||
*
|
||||
* 1. The `data.content` and `data.notes` being received here have already
|
||||
* been validated and sanitized on the server *before* they were ever
|
||||
* saved to the database.
|
||||
* 2. The controller's `sanitizeHtml()` function removed all dangerous tags
|
||||
* (like <script>) and attributes (like `onclick`).
|
||||
*
|
||||
* Therefore, the HTML in `data.content` is trusted. We can safely assign it
|
||||
* directly to the Jodit editor's `.value` property, which will correctly
|
||||
* render the allowed HTML for the user to edit.
|
||||
*/
|
||||
if (content_editor) {
|
||||
content_editor.value = data.content || '';
|
||||
}
|
||||
@ -511,6 +532,24 @@
|
||||
openModal();
|
||||
}
|
||||
|
||||
// Simple client-side XSS pattern check to give instant feedback
|
||||
function hasUnsafeHtml(html) {
|
||||
if (!html) return false;
|
||||
|
||||
const decoded = $('<textarea/>').html(html).text().toLowerCase();
|
||||
|
||||
const patterns = [
|
||||
/<\s*script/i,
|
||||
/on\w+\s*=/i,
|
||||
/javascript\s*:/i,
|
||||
/vbscript\s*:/i,
|
||||
/data\s*:\s*text\/html/i,
|
||||
/expression\s*\(/i
|
||||
];
|
||||
|
||||
return patterns.some(p => p.test(decoded));
|
||||
}
|
||||
|
||||
$('#frontEndContentForm').on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
const form = this;
|
||||
@ -527,6 +566,11 @@
|
||||
toastr.warning("Content is required", 'Warning');
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasUnsafeHtml(content_editor.value)) {
|
||||
toastr.error("Content contains restricted tags or attributes. Script tags, iframes and event handlers (like onclick, onerror, onmouseover) are not allowed.", "Validation Error");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Notes validation
|
||||
@ -535,6 +579,11 @@
|
||||
toastr.warning("Notes is required", 'Warning');
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasUnsafeHtml(notes_editor.value)) {
|
||||
toastr.error("Notes contains restricted tags or attributes. Script tags, iframes and event handlers (like onclick, onerror, onmouseover) are not allowed.", "Validation Error");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If valid, submit via AJAX
|
||||
|
||||
@ -1,4 +1,58 @@
|
||||
<style>
|
||||
/* UI improvements for module cards - all 5 in one row on large screens */
|
||||
.module-cards-row .section {
|
||||
min-height: 60px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0.5rem 0;
|
||||
}
|
||||
.module-cards-row .section-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
min-height: 40px;
|
||||
cursor: default;
|
||||
margin-left: 25px;
|
||||
}
|
||||
.module-cards-row .section-title input[type="checkbox"] {
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.module-cards-row .section-title .mdi {
|
||||
margin-bottom: 0;
|
||||
color: #555;
|
||||
}
|
||||
/* Fallback: ensure 5 equal columns on large screens if row-cols not supported */
|
||||
@media (min-width: 992px) {
|
||||
.module-cards-row > .col {
|
||||
flex: 0 0 20%;
|
||||
max-width: 20%;
|
||||
}
|
||||
}
|
||||
|
||||
/* Checkbox alignment - vertically center with labels */
|
||||
.checkbox-item {
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
gap: 0.5rem;
|
||||
min-height: 1.5rem;
|
||||
}
|
||||
.checkbox-item input[type="checkbox"] {
|
||||
margin: 0 !important;
|
||||
flex-shrink: 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.checkbox-item label {
|
||||
margin: 0 !important;
|
||||
flex: 1;
|
||||
cursor: pointer;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.checkbox-group {
|
||||
margin-left: 8px !important;
|
||||
}
|
||||
|
||||
<?php foreach ($hr_access_data as $key => $value) {
|
||||
$key = $key + 1;
|
||||
?>
|
||||
@ -72,6 +126,27 @@
|
||||
|
||||
<?php foreach ($hr_access_data as $key => $value) {
|
||||
$key = $key + 1;
|
||||
|
||||
// Calculate data counts per user for toggling rules
|
||||
$prePolicyCount = 0;
|
||||
if (!empty($pre_policy_data)) {
|
||||
foreach ($pre_policy_data as $p) {
|
||||
if (!empty($value['pre_hr_id']) && $value['pre_branch_id'] === $p['branch_id']) {
|
||||
$prePolicyCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$postPolicyCount = 0;
|
||||
if (!empty($post_policy_data)) {
|
||||
foreach ($post_policy_data as $p) {
|
||||
if (!empty($value['post_hr_id']) && $value['post_branch_id'] === $p['branch_id']) {
|
||||
$postPolicyCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$cdCount = !empty($post_cd_data) ? count($post_cd_data) : 0;
|
||||
?>
|
||||
|
||||
<!-- User 1 Card -->
|
||||
@ -99,12 +174,12 @@
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div class="content-inner" style="background-color: #F5FFFF;">
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-lg-3">
|
||||
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-5 module-cards-row">
|
||||
<div class="col">
|
||||
<div class="section">
|
||||
<div class="section-title">
|
||||
<input type="checkbox" class="main-checkbox"
|
||||
onchange="toggleSection(this, '<?= 'user' . $key ?>-pre')" name="<?= 'user' . $key ?>_modules"
|
||||
onchange="toggleSection(this, '<?= 'user' . $key ?>-pre', <?= $prePolicyCount ?>)" name="<?= 'user' . $key ?>_modules"
|
||||
value="1" <?php if (in_array(1, $value['allowed_pre_modules'] ?? [])) {
|
||||
echo 'checked';
|
||||
} ?>>
|
||||
@ -161,11 +236,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-3">
|
||||
<div class="col">
|
||||
<div class="section">
|
||||
<div class="section-title">
|
||||
<input type="checkbox" class="main-checkbox"
|
||||
onchange="toggleSection(this, '<?= 'user' . $key ?>-active')" name="<?= 'user' . $key ?>_modules"
|
||||
onchange="toggleSection(this, '<?= 'user' . $key ?>-active', <?= $postPolicyCount ?>)" name="<?= 'user' . $key ?>_modules"
|
||||
value="2" <?php if (in_array(2, $value['allowed_post_modules'])) {
|
||||
echo 'checked';
|
||||
} ?>>
|
||||
@ -236,11 +311,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-3">
|
||||
<div class="col">
|
||||
<div class="section">
|
||||
<div class="section-title">
|
||||
<input type="checkbox" class="main-checkbox"
|
||||
onchange="toggleSection(this, '<?= 'user' . $key ?>-cd')" name="<?= 'user' . $key ?>_modules" value="3" <?php if (in_array(3, $value['allowed_post_modules'])) {
|
||||
onchange="toggleSection(this, '<?= 'user' . $key ?>-cd', <?= $cdCount ?>)" name="<?= 'user' . $key ?>_modules" value="3" <?php if (in_array(3, $value['allowed_post_modules'])) {
|
||||
echo 'checked';
|
||||
} ?>>
|
||||
<i class="mdi mdi-file mr-2"></i>CD statement
|
||||
@ -270,7 +345,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-3">
|
||||
<div class="col">
|
||||
<div class="section">
|
||||
<div class="section-title">
|
||||
<input type="checkbox" class="main-checkbox" name="<?= 'user' . $key ?>_modules" value="4" <?php if (in_array(4, $value['allowed_post_modules'])) {
|
||||
@ -280,6 +355,17 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<div class="section">
|
||||
<div class="section-title">
|
||||
<input type="checkbox" class="main-checkbox" name="<?= 'user' . $key ?>_modules" value="5" <?php if (in_array(5, $value['allowed_post_modules'] ?? [])) {
|
||||
echo 'checked';
|
||||
} ?>>
|
||||
<i class="mdi mdi-chart-line mr-2"></i>Insights
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -302,4 +388,74 @@
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
function toggleAccordion(header) {
|
||||
const card = header.parentElement;
|
||||
const content = card.querySelector('.card-content');
|
||||
const icon = header.querySelector('.accordion-icon i');
|
||||
|
||||
// Close all other accordions
|
||||
document.querySelectorAll('.user-card').forEach(otherCard => {
|
||||
if (otherCard !== card) {
|
||||
const otherContent = otherCard.querySelector('.card-content');
|
||||
const otherIcon = otherCard.querySelector('.accordion-icon i');
|
||||
if (otherContent && otherIcon) {
|
||||
otherContent.classList.remove('active');
|
||||
otherIcon.setAttribute('class', 'mdi mdi-chevron-down');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Toggle current accordion
|
||||
if (content && icon) {
|
||||
content.classList.toggle('active');
|
||||
if (content.classList.contains('active')) {
|
||||
icon.setAttribute('class', 'mdi mdi-chevron-up');
|
||||
} else {
|
||||
icon.setAttribute('class', 'mdi mdi-chevron-down');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function toggleSection(checkbox, sectionId, dataCount) {
|
||||
const section = document.getElementById(sectionId);
|
||||
const checkboxes = section.querySelectorAll('input[type="checkbox"]');
|
||||
|
||||
// If checkbox is being unchecked, always allow without any restriction
|
||||
if (!checkbox.checked) {
|
||||
section.classList.add('hidden');
|
||||
checkboxes.forEach(cb => {
|
||||
cb.checked = false;
|
||||
cb.disabled = true;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// From here, checkbox is being checked
|
||||
// If there is no data, show warning and revert the checkbox state
|
||||
if (!dataCount || dataCount === 0) {
|
||||
checkbox.checked = false;
|
||||
if (typeof toastr !== 'undefined' && toastr.warning) {
|
||||
toastr.warning('No data available for this section.', 'Warning');
|
||||
} else {
|
||||
alert('No data available for this section.');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Data is available and checkbox is being checked - show section and enable inner checkboxes
|
||||
section.classList.remove('hidden');
|
||||
checkboxes.forEach(cb => {
|
||||
cb.disabled = false;
|
||||
});
|
||||
}
|
||||
|
||||
function getCheckedValues(name) {
|
||||
const checkboxes = document.querySelectorAll(`input[name="${name}"]:checked`);
|
||||
return Array.from(checkboxes).map(cb => cb.value);
|
||||
}
|
||||
|
||||
</script>
|
||||
@ -1,12 +1,112 @@
|
||||
<style>
|
||||
.table th,
|
||||
.table td {
|
||||
padding: 8px;
|
||||
}
|
||||
.table th,
|
||||
.table td {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
table.dataTable tbody td {
|
||||
padding: 4px 4px !important;
|
||||
}
|
||||
|
||||
table.dataTable thead th {
|
||||
padding: 4px 4px !important;
|
||||
}
|
||||
|
||||
.dataTables_filter {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.filter-sidebar {
|
||||
height: 100%;
|
||||
width: 0;
|
||||
position: fixed;
|
||||
z-index: 1001;
|
||||
top: 0;
|
||||
right: 0;
|
||||
background-color: #f8f9fa;
|
||||
overflow-x: hidden;
|
||||
transition: 0.5s;
|
||||
box-shadow: -2px 0 5px rgba(0,0,0,0.1);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.filter-sidebar-header {
|
||||
padding: 15px 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.filter-sidebar-header h4 {
|
||||
font-size: 14px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
/* Reduce filter header height (this view only) */
|
||||
#filter-sidebar .filter-sidebar-header {
|
||||
padding: 10px 16px;
|
||||
}
|
||||
|
||||
#filter-sidebar .filter-sidebar-header .closebtn {
|
||||
font-size: 22px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.filter-sidebar-header .closebtn {
|
||||
font-size: 28px;
|
||||
text-decoration: none;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.filter-sidebar .closebtn:hover {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.filter-sidebar-content {
|
||||
padding: 20px;
|
||||
flex-grow: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.filter-sidebar-footer {
|
||||
padding: 15px 20px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
border-top: 1px solid #dee2e6;
|
||||
flex-shrink: 0;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
/* Slightly smaller filter buttons (this view only) */
|
||||
#filter-sidebar .filter-sidebar-footer {
|
||||
padding: 12px 16px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
#filter-sidebar .filter-sidebar-footer .btn {
|
||||
padding: 4px 10px;
|
||||
font-size: 12px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
/* Also shrink DataTables toolbar buttons on this page */
|
||||
.dataTables_wrapper .dt-buttons .btn {
|
||||
padding: 4px 10px;
|
||||
font-size: 12px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.dataTables_wrapper .dt-buttons .btn .btn-custom {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.hide-clear-filter{
|
||||
display: none;
|
||||
}
|
||||
|
||||
table.dataTable tbody td {
|
||||
padding: 4px 4px !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
@ -51,7 +151,106 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="row" id="client_add" style="position: relative; bottom: 25px;">
|
||||
<div id="filter-sidebar" class="filter-sidebar">
|
||||
|
||||
<div class="filter-sidebar-header">
|
||||
<h4 class="m-0">Filter</h4>
|
||||
<a href="javascript:void(0)" class="closebtn" onclick="closeFilterNav()">×</a>
|
||||
</div>
|
||||
|
||||
<div class="filter-sidebar-content">
|
||||
<div class="form-group">
|
||||
<div class="form-group">
|
||||
<div class="form-row">
|
||||
|
||||
<input type="hidden" id="tab_type" value="1">
|
||||
|
||||
<div class="form-group col-md-12">
|
||||
<label>Client<span class="text-danger"></span></label> <br/>
|
||||
<select name="client_id" class="form-control" id="client_id_for_filter">
|
||||
<option value="">Select</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-12">
|
||||
<label>Branch</label> <br />
|
||||
<select name="client_branch_id" class="form-control" id="client_branch_id_for_filter">
|
||||
<option value="0">Select</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-12">
|
||||
<label>Policy<span class="text-danger" id="policy_danger"></span></label> <br />
|
||||
<select name="client_policy_id" class="form-control" id="policy_id_for_filter">
|
||||
<option value="">Select</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-12">
|
||||
<label>Event<span class="text-danger"></span></label> <br />
|
||||
<select name="event_type" class="form-control" id="event_type_for_filter">
|
||||
<option value="">Select</option>
|
||||
<?php
|
||||
if (isset($events) && count($events)) {
|
||||
foreach ($events as $key => $action) {
|
||||
echo "<option value='$key'>$action</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-12" id="date_div">
|
||||
<label>Date<span class="text-danger"></span></label>
|
||||
<div id="reportrange" class="form-control" style="background: #fff; cursor: pointer; padding: 5px 10px; border: 1px solid #ccc; width: 100%">
|
||||
<i class="mdi mdi-calendar-blank"></i>
|
||||
<span></span> <i class="mdi mdi-menu-down"></i>
|
||||
</div>
|
||||
<input type="hidden" id="startDate">
|
||||
<input type="hidden" id="endDate">
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-12 hide_filter_input">
|
||||
<label>Insurer/TPA Data<span class="text-danger"></span></label> <br />
|
||||
<select name="insurer_or_tpa" class="form-control" id="insurer_or_tpa_for_filter">
|
||||
<option value="">Select</option>
|
||||
<?php
|
||||
if (isset($insurer_or_tpa) && count($insurer_or_tpa)) {
|
||||
foreach ($insurer_or_tpa as $key => $action) {
|
||||
echo "<option value=" . $key . ">" . $action . "</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-12 hide_filter_input">
|
||||
<label>Action<span class="text-danger"></span></label> <br />
|
||||
<select name="action_type" class="form-control" id="action_type_for_filter">
|
||||
<option value="">Select</option>
|
||||
<?php
|
||||
if (isset($import_or_export) && count($import_or_export)) {
|
||||
foreach ($import_or_export as $key => $action) {
|
||||
echo "<option value=" . $key . ">" . $action . "</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="filter-sidebar-footer">
|
||||
<a href="<?= base_url("/employee/upload"); ?>" class="btn btn-secondary" id="clear-filters">Clear</a>
|
||||
<a href="<?= base_url("/employee/upload"); ?>" class="btn btn-primary" id="get_base_url" onclick="fetchFilterData(event); closeFilterNav();">Submit</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row" id="client_add">
|
||||
|
||||
<div class="col-12">
|
||||
<div class="card-body">
|
||||
@ -92,9 +291,113 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
$(document).ready(function() {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
let tab = urlParams.get('tab_type');
|
||||
|
||||
console.log('Detected Tab Parameter:', tab);
|
||||
|
||||
// 1. Show/Hide Clear Filter Button
|
||||
if (tab && tab !== "") {
|
||||
$('.hide-clear-filter').show();
|
||||
} else {
|
||||
$('.hide-clear-filter').hide();
|
||||
}
|
||||
|
||||
// 2. The Tab Switcher Logic
|
||||
function activateTab(tabId) {
|
||||
var targetLink = $('#' + tabId);
|
||||
if (targetLink.length > 0) {
|
||||
// Try Method A: Bootstrap 4/5 Standard
|
||||
if (typeof bootstrap !== 'undefined' && bootstrap.Tab) {
|
||||
var tabTrigger = new bootstrap.Tab(targetLink[0]);
|
||||
tabTrigger.show();
|
||||
} else if ($.fn.tab) {
|
||||
targetLink.tab('show');
|
||||
}
|
||||
|
||||
// Try Method B: Manual Fallback (Force Display)
|
||||
$('.nav-link').removeClass('active active-tab').attr('aria-expanded', 'false');
|
||||
targetLink.addClass('active active-tab').attr('aria-expanded', 'true');
|
||||
|
||||
var targetPane = targetLink.attr('href');
|
||||
$('.tab-pane').removeClass('show active');
|
||||
$(targetPane).addClass('show active');
|
||||
|
||||
console.log('Manually activated tab: ' + tabId);
|
||||
}
|
||||
}
|
||||
|
||||
// Execute with a slight delay to ensure includes are loaded
|
||||
setTimeout(function() {
|
||||
if (tab === "2" || tab === "kyc_tab") {
|
||||
activateTab('kyc_tab');
|
||||
} else if (tab === "3" || tab === "hr_tab") {
|
||||
activateTab('hr_tab');
|
||||
} else {
|
||||
activateTab('general_tab');
|
||||
}
|
||||
}, 200);
|
||||
|
||||
// 3. Keep the Sidebar input updated for future filters
|
||||
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
|
||||
$('#tab_type').val($(e.target).attr('id'));
|
||||
});
|
||||
});
|
||||
|
||||
$(document).ready(function(){
|
||||
|
||||
$('#client_id_for_filter').select2();
|
||||
$('#client_branch_id_for_filter').select2();
|
||||
$('#policy_id_for_filter').select2();
|
||||
|
||||
$('#client_id_for_filter').on('change', function() {
|
||||
|
||||
let client_id = $(this).val();
|
||||
console.log('client_id_for_filter', client_id);
|
||||
|
||||
if(branch_list != '') {
|
||||
// console.log(branch_list[client_id]);
|
||||
let data = branch_list[client_id];
|
||||
appendBranchForFilter(data);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
$('#client_branch_id_for_filter').on('change', function() {
|
||||
|
||||
let branch_id = $(this).val();
|
||||
console.log("client_branch_id_for_filter", branch_id);
|
||||
|
||||
if(policy_list != '' && branch_id != '') {
|
||||
let data = policy_list[branch_id];
|
||||
appendPoliciesForFilter(data);
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
/* Modified by Gemini Code Assist */
|
||||
function openPolicyFilterNav(input) {
|
||||
|
||||
if(input == 2){
|
||||
$('.hide_filter_input').show();
|
||||
}else{
|
||||
$('.hide_filter_input').hide();
|
||||
}
|
||||
|
||||
document.getElementById("filter-sidebar").style.width = "350px";
|
||||
}
|
||||
|
||||
/* Modified by Gemini Code Assist */
|
||||
function closeFilterNav() {
|
||||
document.getElementById("filter-sidebar").style.width = "0";
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
if (window.location.hash === '#KYC-DOC-tab') {
|
||||
@ -115,4 +418,181 @@
|
||||
.toLowerCase() // convert everything to lowercase
|
||||
.replace(/\b\w/g, char => char.toUpperCase()); // capitalize each word
|
||||
}
|
||||
|
||||
function appendBranchForFilter(data) {
|
||||
|
||||
$('#client_branch_id_for_filter').empty();
|
||||
$('#client_branch_id_for_filter').append($('<option>', {
|
||||
value: '0',
|
||||
text: 'Select'
|
||||
}));
|
||||
$.each(data, function(index, item) {
|
||||
var option = $('<option>', {
|
||||
value: item.id,
|
||||
text: item.branch_name
|
||||
});
|
||||
if (client_branch_id_param == item.id) {
|
||||
option.attr('selected', true);
|
||||
}
|
||||
$('#client_branch_id_for_filter').append(option);
|
||||
});
|
||||
}
|
||||
|
||||
function appendPoliciesForFilter(data) {
|
||||
|
||||
$('#policy_id_for_filter').empty();
|
||||
$('#policy_id_for_filter').append($('<option>', {
|
||||
value: '0',
|
||||
text: 'Select',
|
||||
selected: true
|
||||
}));
|
||||
|
||||
$.each(data, function(index, item) {
|
||||
|
||||
var option = $('<option>', {
|
||||
value: item.id,
|
||||
text:`${item.policy_type ?? ''} - ${item.policy_no ?? ''}`
|
||||
});
|
||||
if (client_policy_param == item.id) {
|
||||
option.attr('selected', true);
|
||||
}
|
||||
$('#policy_id_for_filter').append(option);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
|
||||
|
||||
var activeTabId = $(e.target).attr('id'); // Gets the ID of the clicked tab
|
||||
var activeTabText = $(e.target).text().trim(); // Gets the text inside the tab
|
||||
|
||||
console.log("Tab Changed!");
|
||||
console.log("Active Tab ID: " + activeTabId);
|
||||
console.log("Active Tab Name: " + activeTabText);
|
||||
|
||||
if(activeTabId == 'kyc_tab'){
|
||||
$('#tab_type').val(2)
|
||||
$('.hide_filter_input').show();
|
||||
}else{
|
||||
$('#tab_type').val(1)
|
||||
$('.hide_filter_input').hide();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function fetchFilterData(event)
|
||||
{
|
||||
event.preventDefault(); // Prevent default action (navigation)
|
||||
|
||||
// 1. Get the current active tab type (from your hidden input)
|
||||
var tab_type = $('#tab_type').val();
|
||||
|
||||
// 2. Get data using the CORRECT IDs from your HTML sidebar
|
||||
var client_id = $('#client_id_for_filter').val();
|
||||
var branch_id = $('#client_branch_id_for_filter').val();
|
||||
var policy_id = $('#policy_id_for_filter').val();
|
||||
var event_type = $('#event_type_for_filter').val();
|
||||
var insurer_or_tpa = $('#insurer_or_tpa_for_filter').val();
|
||||
var action_type = $('#action_type').val(); // This ID was correct
|
||||
|
||||
// Note: If you have date pickers elsewhere, keep these.
|
||||
// Otherwise, ensure they exist in your UI.
|
||||
var start_date = $('#startDate').val() || '';
|
||||
var end_date = $('#endDate').val() || '';
|
||||
|
||||
// 3. Construct the Query Parameters object
|
||||
var queryParams = {
|
||||
tab_type: tab_type,
|
||||
client_id: client_id,
|
||||
branch_id: branch_id,
|
||||
policy_id: policy_id,
|
||||
event_type: event_type,
|
||||
insurer_or_tpa: insurer_or_tpa,
|
||||
action_type: action_type,
|
||||
start_date: start_date,
|
||||
end_date: end_date
|
||||
};
|
||||
|
||||
console.log("Filters applied:", queryParams);
|
||||
|
||||
// 4. Build the Query String and Redirect
|
||||
const queryString = new URLSearchParams(queryParams).toString();
|
||||
const baseUrl = $('#get_base_url').attr('href');
|
||||
console.log("queryString to:", queryString);
|
||||
console.log("baseUrl to:", baseUrl);
|
||||
|
||||
const apiURL = baseUrl + "?" + queryString;
|
||||
|
||||
console.log("Redirecting to:", apiURL);
|
||||
window.location.href = apiURL;
|
||||
}
|
||||
|
||||
function clearFilterData(){
|
||||
|
||||
const url = $('#get_base_url').attr('href');
|
||||
console.log("url to:", url);
|
||||
|
||||
window.location.href = url;
|
||||
}
|
||||
|
||||
$(function() {
|
||||
// 1. Check if dates exist in URL, otherwise leave them null/empty
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const startDateParam = urlParams.get('start_date');
|
||||
const endDateParam = urlParams.get('end_date');
|
||||
|
||||
var start = startDateParam ? moment(startDateParam, 'DD/MM/YYYY') : null;
|
||||
var end = endDateParam ? moment(endDateParam, 'DD/MM/YYYY') : null;
|
||||
|
||||
function cb(start, end) {
|
||||
if (start && end) {
|
||||
$('#reportrange span').html(start.format('D/MM/YYYY') + ' - ' + end.format('D/MM/YYYY'));
|
||||
$('#startDate').val(start.format('DD/MM/YYYY'));
|
||||
$('#endDate').val(end.format('DD/MM/YYYY'));
|
||||
} else {
|
||||
// Display a placeholder if no date is selected
|
||||
$('#reportrange span').html('Select Date Range');
|
||||
$('#startDate').val('');
|
||||
$('#endDate').val('');
|
||||
}
|
||||
}
|
||||
|
||||
$('#reportrange').daterangepicker({
|
||||
autoUpdateInput: false, // CRITICAL: Prevents the picker from forcing a value
|
||||
startDate: start || moment().subtract(29, 'days'),
|
||||
endDate: end || moment(),
|
||||
locale: {
|
||||
cancelLabel: 'Clear',
|
||||
format: 'DD/MM/YYYY'
|
||||
},
|
||||
ranges: {
|
||||
'Today': [moment(), moment()],
|
||||
'Yesterday': [moment().subtract(1, 'days'), moment().subtract(1, 'days')],
|
||||
'Last 7 Days': [moment().subtract(6, 'days'), moment()],
|
||||
'Last 30 Days': [moment().subtract(29, 'days'), moment()],
|
||||
'This Month': [moment().startOf('month'), moment().endOf('month')],
|
||||
'Last Month': [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')]
|
||||
}
|
||||
});
|
||||
|
||||
// Handle the "Apply" button click
|
||||
$('#reportrange').on('apply.daterangepicker', function(ev, picker) {
|
||||
cb(picker.startDate, picker.endDate);
|
||||
});
|
||||
|
||||
// Handle the "Cancel/Clear" button click
|
||||
$('#reportrange').on('cancel.daterangepicker', function(ev, picker) {
|
||||
cb(null, null);
|
||||
});
|
||||
|
||||
// Run once on load
|
||||
cb(start, end);
|
||||
|
||||
$('#clear-filters').on('click', function() {
|
||||
// ... (your existing dropdown resets) ...
|
||||
cb(null, null);
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
@ -488,7 +488,7 @@
|
||||
setTimeout(function() {
|
||||
$('.loader').fadeOut();
|
||||
$('.loader-mask').delay(350).fadeOut('slow');
|
||||
toastr.warning('Something Wrong!', 'warning');
|
||||
toastr.warning('Something Wrong!', 'Warning');
|
||||
}, 1000);
|
||||
},
|
||||
complete: function() {
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
<div class="row" id="insurer_list">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body maincard">
|
||||
<div class="card-body">
|
||||
<!-- <div class="row" style="margin-bottom:1rem;">
|
||||
<div class="col-6" style="align-self: center;">
|
||||
<h4 style="position: relative;">Insurers</h4>
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
<script>
|
||||
var pageSubTitle = 'Insurer';
|
||||
var pageBackButton = '<a href="#" onclick="history.back(); return false;" data-toggle="tooltip" data-placement="top" title="Back"><i class="mdi mdi-arrow-left" style="font-size: 17px;"></i></a>';
|
||||
</script>
|
||||
<div class="row" id="insurer_add">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body maincard">
|
||||
<div class="card-body">
|
||||
<!-- <div class="row" style="padding-bottom: 10px;">
|
||||
<div class="col-6" style="align-self: center;">
|
||||
<h4 style="position: relative;">Insurer</h4>
|
||||
@ -12,14 +16,14 @@
|
||||
</a>
|
||||
</div>
|
||||
</div> -->
|
||||
<div class="row" style="padding-bottom: 10px;padding-left: 5px;">
|
||||
<!-- <div class="row" style="padding-bottom: 10px;padding-left: 5px;">
|
||||
<h4 class="d-flex align-items-center" style="gap: 8px;">
|
||||
<span onclick="history.back()" style="cursor: pointer;">
|
||||
<i class="mdi mdi-chevron-left" style="font-size: 43px;"></i>
|
||||
</span>
|
||||
<span class="title-text ">Insurer</span>
|
||||
</h4>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<div class="tab-wrapper position-relative">
|
||||
<ul class="nav nav-pills navtab-bg" id="myTab">
|
||||
|
||||
@ -351,6 +351,24 @@
|
||||
.hr-item:nth-child(4) { animation-delay: 0.2s; }
|
||||
.hr-item:nth-child(5) { animation-delay: 0.25s; }
|
||||
|
||||
/* Reduce gap between accordion and cards (this tab only) */
|
||||
#KYC-DOC-tab #accordion.mb-3 {
|
||||
margin-bottom: 0.5rem !important;
|
||||
}
|
||||
|
||||
#KYC-DOC-tab #accordion .card.mb-1 {
|
||||
margin-bottom: 0.35rem !important;
|
||||
}
|
||||
|
||||
#KYC-DOC-tab #accordion .card-body {
|
||||
padding-top: 0.75rem;
|
||||
padding-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
#KYC-DOC-tab #file_list {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<div class="tab-pane fade" id="KYC-DOC-tab">
|
||||
@ -360,7 +378,7 @@
|
||||
<div class="card mb-1">
|
||||
|
||||
<h4 class="m-1">
|
||||
<span>Filters</span>
|
||||
<!-- <span>Filters</span> -->
|
||||
<a id="toggleIcon" class="text-dark float-right" data-toggle="collapse" href="#collapseOne" aria-expanded="true">
|
||||
<i id="icon" class="mdi mdi-chevron-down mr-1 text-primary" style="font-size: 28px;"></i>
|
||||
</a>
|
||||
|
||||
@ -8,15 +8,7 @@
|
||||
padding: 4px 4px !important;
|
||||
}
|
||||
|
||||
/* table.dataTable thead th {
|
||||
padding: 4px 4px !important;
|
||||
} */
|
||||
|
||||
|
||||
|
||||
.dataTables_filter {
|
||||
position: absolute;
|
||||
}
|
||||
/* table.dataTable thead th { padding: 4px 4px !important;} */
|
||||
|
||||
.highlight {
|
||||
border: 2px solid red;
|
||||
@ -193,41 +185,6 @@
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="row" style="margin-bottom:1rem;">
|
||||
<div class="col-8" style="align-self: center;">
|
||||
<h4 style="position: relative;">Insurer Statements</h4>
|
||||
<!-- <div id="statusSwitchWrapper" class="dt-switch-wrapper">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" id="statusSwitch">
|
||||
<label class="custom-control-label" for="statusSwitch">Failed Status</label>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
<!-- <div class="col-4">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" id="failedSwitch">
|
||||
<label class="custom-control-label" for="failedSwitch">Failed</label>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<div class="col-4" style="text-align: right;">
|
||||
<div class="row align-items-center">
|
||||
<div class="offset-3 col-5">
|
||||
<div id="statusSwitchWrapper" class="dt-switch-wrapper">
|
||||
<div class="custom-control custom-switch" style="text-align: left;">
|
||||
<input type="checkbox" class="custom-control-input" id="statusSwitch">
|
||||
<label class="custom-control-label" for="statusSwitch">Failed Status</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<button type="button" id="btnAdd" onclick="showFileUploadModal()" class="btn btn-primary waves-effect waves-light">Upload <i class="mdi mdi-upload"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dataTables_length d-flex align-items-center">
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
|
||||
<table data-custom-table-css="table" class="table table-striped mb-0 nowrap" cellspacing="0" id="tickets-table">
|
||||
@ -1815,15 +1772,23 @@
|
||||
});
|
||||
|
||||
// Initialize DataTable
|
||||
// Change Comment: Added dom and buttons properties to arrange search, switch, and upload button on a single line, similar to client_list.php.
|
||||
const table = $('#tickets-table').DataTable({
|
||||
dom: "<'row'<'col-12 d-flex justify-content-between align-items-center'<'datatable-search dataTables_filter'f><'datatable-buttons dt-buttons'B>>>" +
|
||||
"<'row'<'col-sm-12'tr>>" +
|
||||
"<'row align-items-center'<'col-5 text-start'i><'col-7 d-flex justify-content-end align-items-center'<'me-2'l>p>>",
|
||||
buttons: [
|
||||
{
|
||||
text: 'Upload <i class="mdi mdi-upload"></i>',
|
||||
className: 'btn btn-primary waves-effect waves-light',
|
||||
action: function(e, dt, node, config) {
|
||||
showFileUploadModal();
|
||||
}
|
||||
}
|
||||
],
|
||||
// Your existing DataTable options here
|
||||
"order": [], // Disable initial sorting if needed
|
||||
"pageLength": 10,
|
||||
// "dom": '<"top"lf>rt<"bottom"ip><"clear">', // This places length and filter controls at top
|
||||
// "language": {
|
||||
// "lengthMenu": "Show _MENU_ entries",
|
||||
// "search": "Search:"
|
||||
// }
|
||||
language: {
|
||||
lengthMenu: "Show _MENU_ entries",
|
||||
search: `
|
||||
@ -1840,6 +1805,16 @@
|
||||
emptyTable: '<div class="text-center text-muted">No Data found</div>'
|
||||
}
|
||||
});
|
||||
|
||||
// Change Comment: Prepending the 'Failed Status' switch to the DataTable's button container to group it with other controls.
|
||||
$(".datatable-buttons").prepend(`
|
||||
<span id="statusSwitchWrapper" class="dt-switch-wrapper mr-2">
|
||||
<span class="custom-switch" style="text-align: left;">
|
||||
<input type="checkbox" class="custom-control-input" id="statusSwitch">
|
||||
<label class="custom-control-label" for="statusSwitch" style="vertical-align: middle !important;">Failed Status</label>
|
||||
</span>
|
||||
</span>
|
||||
`);
|
||||
|
||||
// Add event listener for switch changes
|
||||
$('#statusSwitch').on('change', function() {
|
||||
|
||||
@ -43,7 +43,7 @@
|
||||
<div class="row" id="kyc_list">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body maincard">
|
||||
<div class="card-body">
|
||||
<!-- <div class="row" style="margin-bottom:1rem;">
|
||||
<div class="col-6" style="align-self: center;">
|
||||
<h4 style="position: relative;">KYC</h4>
|
||||
|
||||
@ -28,19 +28,22 @@
|
||||
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
var pageSubTitle = 'KYC Entity Type';
|
||||
var pageBackButton = '<a href="#" onclick="history.back(); return false;" data-toggle="tooltip" data-placement="top" title="Back"><i class="mdi mdi-arrow-left" style="font-size: 17px;"></i></a>';
|
||||
</script>
|
||||
<div class="row" id="insurer_add">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body maincard">
|
||||
<div class="row" style="padding-bottom: 10px;">
|
||||
<div class="card-body">
|
||||
<!-- <div class="row" style="padding-bottom: 10px;">
|
||||
<h4 class="d-flex align-items-center" style="gap: 8px;">
|
||||
<span onclick="history.back()" style="cursor: pointer;">
|
||||
<i class="mdi mdi-chevron-left" style="font-size: 43px;"></i>
|
||||
</span>
|
||||
<span class="title-text ">KYC Entity Type</span>
|
||||
</h4>
|
||||
</div>
|
||||
</div> -->
|
||||
<!-- <div class="row" style="padding-bottom: 10px;">
|
||||
<div class="col-6" style="align-self: center;">
|
||||
<h4 style="position: relative;">KYC Entity Type</h4>
|
||||
|
||||
@ -11,11 +11,11 @@
|
||||
<!-- ============================================================== -->
|
||||
|
||||
<!-- Footer Start -->
|
||||
<div style="background-color: white !important;">
|
||||
<div style="background-color: white !important;" >
|
||||
<footer class="footer" style="background-color: white !important;">
|
||||
<div class="">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="">
|
||||
<script>
|
||||
document.write(new Date().getFullYear())
|
||||
</script> © NHANCE
|
||||
@ -155,23 +155,68 @@
|
||||
"closeButton": true,
|
||||
};
|
||||
</script>
|
||||
<script>
|
||||
// ── setNavSubtitle: OUTSIDE ready so any page script can call it ──
|
||||
window.setNavSubtitle = function(subtitle, backUrl) {
|
||||
$('#nav-sub-text').html(subtitle);
|
||||
$('#nav-back-btn').attr('href', backUrl);
|
||||
$('#nav-sub-row').removeClass('hidden');
|
||||
};
|
||||
</script>
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
|
||||
|
||||
var pageName = "<?= esc($page_name ?? '') ?>";
|
||||
var userName = "<?= esc(get_session_userdata()->first_name ?? 'NOT SET') ?>";
|
||||
|
||||
// These are set by individual view files BEFORE footer loads
|
||||
var subTitle = (typeof pageSubTitle !== 'undefined') ? pageSubTitle : false;
|
||||
var backButton = (typeof pageBackButton !== 'undefined') ? pageBackButton : false;
|
||||
var titleHtml = "";
|
||||
var $titleEl = $(".top-navbar-title");
|
||||
var $navDiv = $(".top-navbar-div");
|
||||
|
||||
var titleHtml = (pageName && pageName !== "Dashboard") ?
|
||||
`<h3 style="margin-right:5px !important;">${pageName}</h3>` :
|
||||
" ";
|
||||
if (pageName && pageName == "Dashboard") {
|
||||
titleHtml = `<h6 style="margin-right:5px !important;">Welcome ${userName}</h6>
|
||||
<h3>Dashboard</h3>`;
|
||||
$titleEl.css({ display: "block", bottom: "12px" });
|
||||
$navDiv.css({ marginTop: "20px" }); // single title → default 20px
|
||||
|
||||
if (pageName && pageName == "Dashboard") {
|
||||
titleHtml = `<h6 style="margin-right:5px !important;">Welcome <?= esc(get_session_userdata()->first_name ?? 'NOT SET') ?></h6>
|
||||
<h3>Dashboard</h3>`;
|
||||
}
|
||||
} else if (pageName && backButton && subTitle) {
|
||||
titleHtml = `<h3 style="margin-right:5px !important;">${pageName}</h3>`;
|
||||
titleHtml += `<h6>${backButton} ${subTitle}</h6>`;
|
||||
$titleEl.css({ display: "block", bottom: "12px" });
|
||||
$navDiv.css({ marginTop: "0px" }); // double title → 0
|
||||
|
||||
} else if (pageName && subTitle) {
|
||||
titleHtml = `<h3 style="margin-right:5px !important;">${pageName}</h3>`;
|
||||
titleHtml += `<h6>${subTitle}</h6>`;
|
||||
$titleEl.css({ display: "block", bottom: "12px" });
|
||||
$navDiv.css({ marginTop: "0px" }); // double title → 0
|
||||
|
||||
} else {
|
||||
titleHtml = `<h3 style="margin-right:5px !important;">${pageName}</h3>`;
|
||||
$titleEl.css({ display: "flex", bottom: "0px" });
|
||||
$navDiv.css({ marginTop: "20px" }); // single title → default 20px
|
||||
}
|
||||
|
||||
$titleEl.html(titleHtml);
|
||||
|
||||
|
||||
// var pageName = "<?= esc($page_name ?? '') ?>";
|
||||
|
||||
// var titleHtml = (pageName && pageName !== "Dashboard") ?
|
||||
// `<h3 style="margin-right:5px !important;">${pageName}</h3>` :
|
||||
// " ";
|
||||
|
||||
// if (pageName && pageName == "Dashboard") {
|
||||
// titleHtml = `<h6 style="margin-right:5px !important;">Welcome <?= esc(get_session_userdata()->first_name ?? 'NOT SET') ?></h6>
|
||||
// <h3>Dashboard</h3>`;
|
||||
// }
|
||||
|
||||
|
||||
$(".top-navbar-title").html(titleHtml);
|
||||
// $(".top-navbar-title").html(titleHtml);
|
||||
|
||||
|
||||
$('#collapseOne').on('show.bs.collapse', function() {
|
||||
@ -1016,19 +1061,27 @@
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
const activeItem = localStorage.getItem("activeLi");
|
||||
|
||||
if (activeItem) {
|
||||
$('.li-seperate a').removeClass('img-active').addClass('img-inactive');
|
||||
// Use .children('a') to only target the main icon links
|
||||
$('.li-seperate').children('a').removeClass('img-active').addClass('img-inactive');
|
||||
|
||||
// Set the active class only on the top-level anchor of the saved ID
|
||||
$('#' + activeItem).children('a').first().removeClass('img-inactive').addClass('img-active');
|
||||
}
|
||||
|
||||
$('.li-seperate').on('click', function() {
|
||||
// reset all anchors
|
||||
$('.li-seperate a').removeClass('img-active').addClass('img-inactive');
|
||||
// 1. Reset ONLY the top-level anchors of all menu items
|
||||
$('.li-seperate').children('a').removeClass('img-active').addClass('img-inactive');
|
||||
|
||||
// only the immediate <a> inside this <li>
|
||||
// 2. Add active class ONLY to the immediate <a> of the clicked <li>
|
||||
$(this).children('a').first().removeClass('img-inactive').addClass('img-active');
|
||||
|
||||
localStorage.setItem("activeLi", $(this).attr("id"));
|
||||
// 3. Store the ID
|
||||
const id = $(this).attr("id");
|
||||
if(id) {
|
||||
localStorage.setItem("activeLi", id);
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -118,7 +118,9 @@
|
||||
* {
|
||||
font-family: 'Poppins' !important;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #ffffff !important; /* ← ADD THIS */
|
||||
}
|
||||
.content {
|
||||
min-height: 70vh;
|
||||
}
|
||||
@ -142,8 +144,17 @@
|
||||
min-height: 100vh !important;
|
||||
}
|
||||
|
||||
/* REPLACE WITH THIS */
|
||||
body,
|
||||
html,
|
||||
#wrapper,
|
||||
.content-page,
|
||||
body .content-page {
|
||||
margin-top: 90px !important;
|
||||
background-color: #ffffff !important;
|
||||
}
|
||||
|
||||
body .content-page {
|
||||
margin-top: 50px !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -154,7 +165,8 @@
|
||||
z-index: 4;
|
||||
width: 90%;
|
||||
margin-left: 120px !important;
|
||||
background: #fff;
|
||||
background: #ffffff !important;
|
||||
box-shadow: 0 2px 0 #ffffff !important; /* seals bottom edge */
|
||||
}
|
||||
|
||||
.top-navbar-div {
|
||||
@ -164,6 +176,8 @@
|
||||
justify-content: space-between;
|
||||
margin-top: 20px;
|
||||
margin-left: 15px;
|
||||
/* height: 100%;
|
||||
width: 100%; */
|
||||
}
|
||||
|
||||
|
||||
@ -1657,10 +1671,12 @@
|
||||
|
||||
.left-btn {
|
||||
left: 0;
|
||||
border-radius : 10px !important;
|
||||
}
|
||||
|
||||
.right-btn {
|
||||
right: 0;
|
||||
border-radius : 10px !important;
|
||||
}
|
||||
|
||||
input[type="search"]::-webkit-search-cancel-button {
|
||||
@ -1669,9 +1685,166 @@
|
||||
}
|
||||
|
||||
</style>
|
||||
<style>
|
||||
/** Newly Implemented */
|
||||
/* ── Right Actions ─────────────────────────────── */
|
||||
:root {
|
||||
--primary: #02a8b5;
|
||||
--primary-light: #d4f5f6;
|
||||
--trans: 0.2s ease;
|
||||
}
|
||||
|
||||
.topbar-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-left: auto;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* ── Circle Icon Buttons (Bell & Logout) ────────── */
|
||||
.topbar-icon-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
border-radius: 50%;
|
||||
background: var(--primary-light);
|
||||
color: #333;
|
||||
font-size: 17px;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
border: none;
|
||||
outline: none;
|
||||
transition: background var(--trans), color var(--trans);
|
||||
}
|
||||
|
||||
.topbar-icon-btn:hover {
|
||||
background: var(--primary);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* ── Notification Red Dot ───────────────────────── */
|
||||
.notif-dot {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 5px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background: #e53e3e;
|
||||
border-radius: 50%;
|
||||
border: 2px solid #fff;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* ── Profile Image ──────────────────────────────── */
|
||||
.topbar-actions .rounded-circle {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
object-fit: cover;
|
||||
border-radius: 50% !important;
|
||||
border: 2px solid var(--primary-light);
|
||||
cursor: pointer;
|
||||
transition: border-color var(--trans), box-shadow var(--trans);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.topbar-actions .rounded-circle:hover {
|
||||
border-color: var(--primary);
|
||||
box-shadow: 0 0 0 3px var(--primary-light);
|
||||
}
|
||||
|
||||
/* ── Fallback Initial Avatar ────────────────────── */
|
||||
.avatar-btn {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, var(--primary), #02c5cc);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 700;
|
||||
font-size: 15px;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
border: 2px solid var(--primary-light);
|
||||
flex-shrink: 0;
|
||||
user-select: none;
|
||||
transition: box-shadow var(--trans), border-color var(--trans);
|
||||
}
|
||||
|
||||
.avatar-btn:hover {
|
||||
box-shadow: 0 0 0 3px var(--primary-light);
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
|
||||
#policyTransactions,
|
||||
#policyTransactionsSub {
|
||||
min-width: 240px !important;
|
||||
width: max-content !important;
|
||||
}
|
||||
|
||||
#policyTransactions ul,
|
||||
#policyTransactionsSub ul {
|
||||
width: max-content !important;
|
||||
}
|
||||
|
||||
#policyTransactions ul li a,
|
||||
#policyTransactionsSub ul li a {
|
||||
white-space: nowrap !important;
|
||||
width: max-content !important;
|
||||
}
|
||||
|
||||
body[data-sidebar-size="condensed"] .footer {
|
||||
left: 0px !important; }
|
||||
|
||||
|
||||
/* ── Navbar title system ── */
|
||||
.top-navbar-title {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
line-height: 1;
|
||||
}
|
||||
.nav-main-title {
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
color: #101828;
|
||||
line-height: 1.2;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.nav-welcome {
|
||||
font-size: 11px;
|
||||
color: #98a2b3;
|
||||
margin-top: 2px;
|
||||
font-weight: 400;
|
||||
}
|
||||
.nav-sub-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
.nav-sub-row.hidden { display: none; }
|
||||
.nav-sub-text {
|
||||
font-size: 12px;
|
||||
color: #5a6a7a;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 500px;
|
||||
}
|
||||
.btn-back { background: none; border: none; font-size: 22px; cursor: pointer; color: #888; width: 32px; height: 32px; border-radius: 6px; display: flex; align-items: center; justify-content: center; transition: background .2s; }
|
||||
.btn-back:hover { background: #f0f0f0; color: #333; }
|
||||
/** Newly Implemented */
|
||||
</style>
|
||||
|
||||
</head>
|
||||
|
||||
@ -1686,7 +1859,7 @@
|
||||
|
||||
<!-- <img src="<?= base_url() . "public" ?>/assets/images/nhance-loader-fast.gif" height="40" width="40" alt="Loading..."> -->
|
||||
<!-- Begin page -->
|
||||
<div id="wrapper" style="background-color:white;">
|
||||
<div id="wrapper" style="background-color:white !important;">
|
||||
|
||||
|
||||
<!-- ========== Left Sidebar Start ========== -->
|
||||
@ -1907,7 +2080,10 @@
|
||||
|
||||
<?php if (in_array(get_role_id(), [1, 5]) || (in_array(MANAGEMENT_TEAM_ID, user_team()) || in_array(FINANCE_TEAM_ID, user_team()) || in_array(BUSINESS_TEAM_ID, user_team()) || in_array(POS_TEAM_ID, user_team()))) { ?>
|
||||
<li>
|
||||
<a href="<?= base_url('/policy_tranction/inception/list') ?>">Policy</a>
|
||||
<a href="<?= base_url('/policy_tranction/inception/list?view=1') ?>">Policy Add</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="<?= base_url('/policy_tranction/inception/list') ?>">Policy List</a>
|
||||
</li>
|
||||
<!-- <li>
|
||||
<a href="<?= base_url('/policy_tranction/inception/list2') ?>">Policy 2</a>
|
||||
@ -2117,7 +2293,7 @@
|
||||
</li> -->
|
||||
<?php if (in_array(get_role_id(), [1, 5])){ ?>
|
||||
<li>
|
||||
<a href="<?= base_url('sales/loadtargets') ?>"><i class="ri-group-2-fill"></i>Sales Team Targets </a>
|
||||
<a href="<?= base_url('sales/loadtargets') ?>"><i class="ri-group-2-fill"></i> Sales Team Targets </a>
|
||||
</li>
|
||||
<?php } ?>
|
||||
</ul>
|
||||
@ -2157,13 +2333,13 @@
|
||||
</div>
|
||||
</li>
|
||||
<?php } ?>
|
||||
<li class="li-seperate" id="user-manual-li">
|
||||
<!-- <li class="li-seperate" id="user-manual-li">
|
||||
<a id="user_manual" href="<?= base_url("autobookstackLogin") ?>" class="waves-effect img-inactive" target="_blank" rel="noopener noreferrer">
|
||||
<img
|
||||
src="<?= base_url() . "public"; ?>/assets/images/user_manual_sb.png" alt="Logo" height="20">
|
||||
<span>User Manual</span>
|
||||
</a>
|
||||
</li>
|
||||
</li> -->
|
||||
</ul>
|
||||
</div>
|
||||
<!-- End Sidebar -->
|
||||
@ -2180,44 +2356,52 @@
|
||||
<h5>Welcome <?= (isset(get_session_userdata()->first_name) ? get_session_userdata()->first_name : 'NOT SET') ?> </h5>
|
||||
</div>
|
||||
<div class="nav-flex">
|
||||
<!-- Right actions Starts Here-->
|
||||
<div class="topbar-actions">
|
||||
|
||||
<!-- user pic -->
|
||||
<div class="dropdown notification-list topbar-dropdown">
|
||||
<a class=" dropdown-toggle nav-user mr-0 waves-effect waves-light" data-toggle="dropdown" href="#" role="button" aria-haspopup="false" aria-expanded="false">
|
||||
<!-- Avatar: show profile image if exists, else show initial avatar -->
|
||||
<?php
|
||||
$profile = get_userProfile();
|
||||
$default = base_url('public/assets/images/avatar_2x.png');
|
||||
$firstName = isset(get_session_userdata()->first_name) ? get_session_userdata()->first_name : 'A';
|
||||
$initial = strtoupper(substr($firstName, 0, 1));
|
||||
?>
|
||||
|
||||
<?php
|
||||
$profile = get_userProfile();
|
||||
$default = base_url('public/assets/images/avatar_2x.png');
|
||||
?>
|
||||
<?php if (!empty($profile)) : ?>
|
||||
<!-- Profile Image -->
|
||||
<img
|
||||
src="<?= $profile ?>"
|
||||
onerror="this.onerror=null; this.src='<?= $default ?>'; this.style.display='none'; document.getElementById('avatar-fallback').style.display='flex';"
|
||||
alt="user-image"
|
||||
height="38" width="38"
|
||||
class="rounded-circle"
|
||||
style="object-fit:cover; border: 2px solid var(--primary-light); cursor:pointer;"
|
||||
>
|
||||
<!-- Fallback avatar (hidden by default, shown if image fails) -->
|
||||
<div class="avatar-btn" id="avatar-fallback" title="<?= $firstName ?>" style="display:none;">
|
||||
<?= $initial ?>
|
||||
</div>
|
||||
|
||||
<img
|
||||
src="<?= (!empty($profile)) ? $profile : $default; ?>"
|
||||
onerror="this.onerror=null;this.src='<?= $default ?>';"
|
||||
alt="user-image"
|
||||
height="40" width="40"
|
||||
class="rounded-circle">
|
||||
</a>
|
||||
</div>
|
||||
<?php else : ?>
|
||||
<!-- No profile image — show initial avatar -->
|
||||
<div class="avatar-btn" title="<?= $firstName ?>">
|
||||
<?= $initial ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Notification -->
|
||||
<div class="dropdown notification-list topbar-dropdown">
|
||||
<a class="dropdown-toggle right-bar-toggle waves-effect waves-light"
|
||||
style="font-size: 14px; color:#000;background-color:#D4F5F6 !important;
|
||||
border-radius:25px!important;padding:7px; position: relative;">
|
||||
<button class="topbar-icon-btn right-bar-toggle" title="Notifications">
|
||||
<i class="ri-notification-3-line"></i>
|
||||
<span class="notif-dot"></span>
|
||||
</button>
|
||||
|
||||
<i class="fe-bell noti-icon"></i>
|
||||
|
||||
<span class="notification-dot"></span>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Logout -->
|
||||
<div class="dropdown notification-list">
|
||||
<a href="<?= base_url('/logout'); ?>" style="font-size: 18px; color:#000;background-color:#D4F5F6 !important;border-radius:25px;padding:7px;margin-top:0px;">
|
||||
<i class="ri-logout-box-r-line" style=" color:#000;padding:0px;"></i>
|
||||
</a>
|
||||
<a href="<?= base_url('/logout'); ?>" class="topbar-icon-btn" title="Logout">
|
||||
<i class="ri-logout-box-r-line"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
<!-- Right actions Ends Here-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -2,140 +2,177 @@
|
||||
.col-12 {
|
||||
max-width: 98% !important;
|
||||
}
|
||||
.filter-sidebar {
|
||||
height: 100%;
|
||||
width: 0;
|
||||
position: fixed;
|
||||
z-index: 1001;
|
||||
top: 0;
|
||||
right: 0;
|
||||
background-color: #f8f9fa;
|
||||
overflow-x: hidden;
|
||||
transition: 0.5s;
|
||||
box-shadow: -2px 0 5px rgba(0,0,0,0.1);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.filter-sidebar-header {
|
||||
padding: 15px 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.filter-sidebar-header .closebtn {
|
||||
font-size: 28px;
|
||||
text-decoration: none;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.filter-sidebar .closebtn:hover {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.filter-sidebar-content {
|
||||
padding: 20px;
|
||||
flex-grow: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.filter-sidebar-footer {
|
||||
padding: 15px 20px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
border-top: 1px solid #dee2e6;
|
||||
flex-shrink: 0;
|
||||
gap: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="container-fluid-min">
|
||||
<div class="row" id="lead_filter_div">
|
||||
<div class="col-12" style="margin-top: -12px;">
|
||||
<div class="card-body">
|
||||
<div id="accordion" class="mb-3">
|
||||
<div class="card mb-1">
|
||||
<h4 class="m-1">
|
||||
<span>Filter</span>
|
||||
<a id="toggleIcon" class="text-dark float-right" data-toggle="collapse" href="#collapseOne"
|
||||
aria-expanded="true">
|
||||
<i id="icon" class="mdi mdi-chevron-down mr-1 text-primary" style="font-size: 28px;"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
<div id="collapseOne" class="collapse hide" aria-labelledby="headingOne" data-parent="#accordion">
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<div class="form-row">
|
||||
|
||||
<div class="form-group col-md-3">
|
||||
<label for="filter_issuer_type"> Issuer Type <span
|
||||
class="text-danger"></span></label>
|
||||
<select class="form-control" id="filter_issuer_type" name="filter_issuer_type">
|
||||
<option value="0">Select</option>
|
||||
<?php
|
||||
if (isset($issuer) && count($issuer)) {
|
||||
foreach ($issuer as $key => $value) {
|
||||
echo "<option value='" . $key . "'>" . $value . "</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-3">
|
||||
<label for="filter_lead_type"> Opportunity Type <span class="text-danger"></span></label>
|
||||
<select class="form-control" id="filter_lead_type" name="filter_lead_type">
|
||||
<option value="0">Select</option>
|
||||
<?php
|
||||
if (isset($lead_type) && count($lead_type)) {
|
||||
foreach ($lead_type as $key => $value) {
|
||||
echo "<option value='" . $key . "'>" . $value . "</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-3">
|
||||
<label for="filter_client_type"> Client Type <span
|
||||
class="text-danger"></span></label>
|
||||
<select class="form-control" id="filter_client_type" name="filter_client_type">
|
||||
<option value="0">Select</option>
|
||||
<?php
|
||||
if (isset($client_type) && count($client_type)) {
|
||||
foreach ($client_type as $key => $value) {
|
||||
echo "<option value='" . $key . "'>" . $value . "</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-3">
|
||||
<label for="filter_policy_type"> Policy Type <span
|
||||
class="text-danger"></span></label>
|
||||
<select class="form-control" id="filter_policy_type" name="filter_policy_type">
|
||||
<option value="0">Select</option>
|
||||
<?php
|
||||
if (isset($policy_type) && count($policy_type)) {
|
||||
foreach ($policy_type as $key => $value) {
|
||||
echo "<option value='" . $value['id'] . "'>" . $value['policy_type'] . "</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group col-md-3">
|
||||
<label for="filter_client_id">Client<span class="text-danger"></span></label>
|
||||
<select class="form-control" id="filter_client_id" name="filter_client_id">
|
||||
<option value="0">Select</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-3">
|
||||
<label for="filter_client_branch_id">Client Branch<span
|
||||
class="text-danger"></span></label>
|
||||
<select class="form-control" id="filter_client_branch_id"
|
||||
name="filter_client_branch_id">
|
||||
<option value="0">Select</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-3">
|
||||
<label for="filter_lead_status">Status<span class="text-danger"></span></label>
|
||||
<select class="form-control" id="filter_lead_status" name="filter_lead_status">
|
||||
<option value="0">Select</option>
|
||||
<?php
|
||||
if (isset($lead_status) && count($lead_status)) {
|
||||
foreach ($lead_status as $key => $value) {
|
||||
echo "<option value=" . $key . ">" . $value . "</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-12 text-right m-b-0" style="margin-bottom: -15px;">
|
||||
<a href="<?= base_url("/leads/list"); ?>" class="btn btn-secondary"
|
||||
id="clear-filters">Clear</a>
|
||||
<a class="btn btn-primary" id="get-emp-list"
|
||||
onclick="fetchTicketListData();">Submit</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="lead_filter_div"></div>
|
||||
<!-- end page title -->
|
||||
<div id="lead_list_div">
|
||||
<?php include('leads_list.php'); ?>
|
||||
</div>
|
||||
</div>
|
||||
<div id="opportunities-filter-sidebar" class="filter-sidebar">
|
||||
<div class="filter-sidebar-header">
|
||||
<h4 class="m-0">Filter</h4>
|
||||
<a href="javascript:void(0)" class="closebtn" onclick="closeOpportunitiesListFilterNav()">×</a>
|
||||
</div>
|
||||
<div class="filter-sidebar-content">
|
||||
<div class="form-group">
|
||||
<div class="form-row">
|
||||
|
||||
<div class="form-group col-md-12">
|
||||
<label for="filter_issuer_type"> Issuer Type <span
|
||||
class="text-danger"></span></label>
|
||||
<select class="form-control" id="filter_issuer_type" name="filter_issuer_type">
|
||||
<option value="0">Select</option>
|
||||
<?php
|
||||
if (isset($issuer) && count($issuer)) {
|
||||
foreach ($issuer as $key => $value) {
|
||||
echo "<option value='" . $key . "'>" . $value . "</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-12">
|
||||
<label for="filter_lead_type"> Opportunity Type <span class="text-danger"></span></label>
|
||||
<select class="form-control" id="filter_lead_type" name="filter_lead_type">
|
||||
<option value="0">Select</option>
|
||||
<?php
|
||||
if (isset($lead_type) && count($lead_type)) {
|
||||
foreach ($lead_type as $key => $value) {
|
||||
echo "<option value='" . $key . "'>" . $value . "</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-12">
|
||||
<label for="filter_client_type"> Client Type <span
|
||||
class="text-danger"></span></label>
|
||||
<select class="form-control" id="filter_client_type" name="filter_client_type">
|
||||
<option value="0">Select</option>
|
||||
<?php
|
||||
if (isset($client_type) && count($client_type)) {
|
||||
foreach ($client_type as $key => $value) {
|
||||
echo "<option value='" . $key . "'>" . $value . "</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-12">
|
||||
<label for="filter_policy_type"> Policy Type <span
|
||||
class="text-danger"></span></label>
|
||||
<select class="form-control" id="filter_policy_type" name="filter_policy_type">
|
||||
<option value="0">Select</option>
|
||||
<?php
|
||||
if (isset($policy_type) && count($policy_type)) {
|
||||
foreach ($policy_type as $key => $value) {
|
||||
echo "<option value='" . $value['id'] . "'>" . $value['policy_type'] . "</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group col-md-12">
|
||||
<label for="filter_client_id">Client<span class="text-danger"></span></label>
|
||||
<select class="form-control" id="filter_client_id" name="filter_client_id">
|
||||
<option value="0">Select</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-12">
|
||||
<label for="filter_client_branch_id">Client Branch<span
|
||||
class="text-danger"></span></label>
|
||||
<select class="form-control" id="filter_client_branch_id"
|
||||
name="filter_client_branch_id">
|
||||
<option value="0">Select</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-12">
|
||||
<label for="filter_lead_status">Status<span class="text-danger"></span></label>
|
||||
<select class="form-control" id="filter_lead_status" name="filter_lead_status">
|
||||
<option value="0">Select</option>
|
||||
<?php
|
||||
if (isset($lead_status) && count($lead_status)) {
|
||||
foreach ($lead_status as $key => $value) {
|
||||
echo "<option value=" . $key . ">" . $value . "</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="filter-sidebar-footer">
|
||||
<a href="<?= base_url("/leads/list"); ?>" class="btn btn-secondary" id="clear-filters">Clear</a>
|
||||
<a class="btn btn-primary" id="get-emp-list" onclick="fetchTicketListData();">Submit</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function openOpportunitiesListFilterNav() {
|
||||
document.getElementById("opportunities-filter-sidebar").style.width = "350px";
|
||||
}
|
||||
|
||||
function closeOpportunitiesListFilterNav() {
|
||||
document.getElementById("opportunities-filter-sidebar").style.width = "0";
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
@ -167,7 +204,7 @@
|
||||
//----- AJAX FUNCTIONS --------------------------------------------------------------------------------
|
||||
|
||||
function fetchTicketListData() {
|
||||
|
||||
closeOpportunitiesListFilterNav();
|
||||
var issuer_type = $('#filter_issuer_type').val();
|
||||
var lead_type = $('#filter_lead_type').val();
|
||||
var policy_type = $('#filter_policy_type').val();
|
||||
|
||||
@ -102,6 +102,14 @@
|
||||
border-width: 1px !important;
|
||||
}
|
||||
</style>
|
||||
<?php
|
||||
$isLeadEditEb = isset($lead_edit_data) && ! empty($lead_edit_data);
|
||||
$ebSubtitle = $isLeadEditEb ? 'Edit Opportunities - (EB)' : 'Add Opportunities - (EB)';
|
||||
?>
|
||||
<script>
|
||||
var pageSubTitle = '<?= $ebSubtitle ?>';
|
||||
var pageBackButton = '<a href="<?= base_url('leads/list') ?>" data-toggle="tooltip" data-placement="top" title="Back"><i class="mdi mdi-arrow-left" style="font-size: 17px;"></i></a>';
|
||||
</script>
|
||||
|
||||
<div class="container-fluid-min">
|
||||
<div class="row" id="leads_form">
|
||||
@ -120,12 +128,12 @@
|
||||
<a href="<?= base_url('leads/list') ?>" type="button" id="btnAdd"
|
||||
class="btn btn-primary waves-effect waves-light">Back</a>
|
||||
</div> -->
|
||||
<h4 class="d-flex align-items-center" style="gap: 8px;">
|
||||
<!-- <h4 class="d-flex align-items-center" style="gap: 8px;">
|
||||
<a href="<?= base_url('leads/list') ?>" style="cursor: pointer;">
|
||||
<i class="mdi mdi-chevron-left" style="font-size: 43px;"></i>
|
||||
</a>
|
||||
<span class="title-text" id="page_title" style="position: relative;">Add Opportunity</span>
|
||||
</h4>
|
||||
</h4> -->
|
||||
</div>
|
||||
<form role="form" class="parsley-examples" method="post" id="leads_form_id"
|
||||
enctype="multipart/form-data">
|
||||
@ -294,26 +302,26 @@
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-4">
|
||||
<label for="client_branch">Branch Name<span class="text-danger">*</span></label>
|
||||
<label for="branch_name">Branch Name<span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="branch_name" name="branch_name"
|
||||
placeholder="Enter Branch Name" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-4">
|
||||
<label for="client_branch">Branch Code<span class="text-danger">*</span></label>
|
||||
<label for="branch_code">Branch Code<span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="branch_code" name="branch_code"
|
||||
placeholder="Enter Branch Code" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-4 renewalFields">
|
||||
<label for="contact_person_summary">Contact Person</label>
|
||||
<label for="contact_person_summary">Contact Person<span class="text-danger">*</span></label>
|
||||
<select class="form-control" name="contact_person_summary" id="contact_person_summary">
|
||||
<option value="">Select a Contact</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-4 freshFields">
|
||||
<label for="client_branch">Contact Person Name<span class="text-danger">*</span></label>
|
||||
<label for="contact_person_name">Contact Person Name<span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="contact_person_name" name="contact_person_name"
|
||||
placeholder="Enter Contact Name" required>
|
||||
</div>
|
||||
@ -393,7 +401,7 @@
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-4">
|
||||
<label for="">Next Reminder Date<span class="text-danger"></span></label>
|
||||
<label for="next_reminder_date">Next Reminder Date<span class="text-danger"></span></label>
|
||||
<div class="input-icon">
|
||||
<input type="text" class="form-control" id="next_reminder_date" name="next_reminder_date" placeholder="DD/MM/YYYY">
|
||||
<i class="mdi mdi-calendar-blank-outline additional-icon"></i>
|
||||
@ -769,12 +777,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
hide_list_show_add();
|
||||
|
||||
var page_title = 'Edit Opportunity';
|
||||
pageSubTitle = 'Edit Opportunities - (EB)';
|
||||
pageBackButton = '<a href="<?= base_url('leads/list') ?>" data-toggle="tooltip" data-placement="top" title="Back"><i class="mdi mdi-arrow-left" style="font-size: 17px;"></i></a>';
|
||||
|
||||
$('#page_title').text(page_title);
|
||||
$('#leads_primarykey').val(res.data.id);
|
||||
$('#actual_lead_id').val(res.data.actual_lead_id || 0);
|
||||
$('#policy_start_date').val(res.data.policy_end_date);
|
||||
@ -1236,7 +1243,7 @@
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-4">
|
||||
<label for="">Date of Commencement <span class="text-danger"></span></label>
|
||||
<label for="policy_start_date">Date of Commencement <span class="text-danger">*</span></label>
|
||||
<div class="input-icon">
|
||||
<input type="text" class="form-control policy_start_date" id="policy_start_date_${increment}" name="policy_start_date[]" placeholder="Enter DOC" onchange="calculatePolicyMetrics(this)">
|
||||
<i class="mdi mdi-calendar-blank-outline additional-icon"></i>
|
||||
@ -1245,7 +1252,7 @@
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-4">
|
||||
<label for="">Date of Expiry <span class="text-danger"></span></label>
|
||||
<label for="policy_end_date">Date of Expiry <span class="text-danger">*</span></label>
|
||||
<div class="input-icon">
|
||||
<input type="text" class="form-control policy_end_date" id="policy_end_date_${increment}" name="policy_end_date[]" placeholder="Enter DOE">
|
||||
<i class="mdi mdi-calendar-blank-outline additional-icon"></i>
|
||||
@ -1611,8 +1618,19 @@
|
||||
console.log('--- End of calculatePolicyMetrics ---\n');
|
||||
}
|
||||
|
||||
$(document).on("input", "#incept_no_of_lives", function () {
|
||||
if (this.value.length > 10) {
|
||||
this.value = this.value.slice(0, 10); // max 10 digits
|
||||
}
|
||||
this.value = this.value.replace(/\D/g, '');
|
||||
});
|
||||
|
||||
|
||||
$(document).on("input", "#incept_emp_count, #incept_dept_count, #renewal_emp_count, #renewal_dept_count, #exp_emp_count, #exp_dept_count", function() {
|
||||
if (this.value.length > 10) {
|
||||
this.value = this.value.slice(0, 10); // max 10 digits
|
||||
}
|
||||
this.value = this.value.replace(/\D/g, '');
|
||||
calculateTotalLives(this);
|
||||
});
|
||||
|
||||
@ -2451,14 +2469,24 @@
|
||||
$('#client_name').val(actual_lead_client_details.company_name || '');
|
||||
|
||||
$('#gst').val(actual_lead_client_details.gst_number || '');
|
||||
|
||||
// 🔥 Important:
|
||||
// Only auto-generate short name IF empty (avoid overwrite in edit)
|
||||
if (!$('#client_short_name').val()) {
|
||||
$('#client_name').trigger('input');
|
||||
|
||||
existingShortName = actual_lead_client_details.short_name || '';
|
||||
if (existingShortName) {
|
||||
// ── Short name EXISTS — just populate and validate ────────
|
||||
$('#client_short_name').val(existingShortName);
|
||||
// validateInput($('#client_short_name')[0], 'clients', 'short_name');
|
||||
} else {
|
||||
// Run duplicate validation once
|
||||
validateInput($('#client_short_name')[0], "clients", "short_name");
|
||||
// Auto-generate short name from company name
|
||||
// Use a small delay to ensure DOM is ready
|
||||
setTimeout(function () {
|
||||
let baseName = actual_lead_client_details.company_name
|
||||
.trim()
|
||||
.substring(0, 10)
|
||||
.replace(/\s+/g, '')
|
||||
.toUpperCase();
|
||||
|
||||
makeUniqueShortName(baseName);
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -164,6 +164,9 @@ if (isset($selected_lead_type)) {
|
||||
|
||||
$('#client_id').select2();
|
||||
$('#client_branch_id').select2();
|
||||
$('#rfq_qcr_viewers').select2({
|
||||
placeholder: "Select Viewers",
|
||||
});
|
||||
$('#salse_person_id').select2({
|
||||
placeholder: "Select Sales Person",
|
||||
});
|
||||
|
||||
@ -9,7 +9,7 @@ table.dataTable tbody td {
|
||||
}
|
||||
|
||||
.col-12 {
|
||||
max-width: 98% !important;
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
.dataTables_filter {
|
||||
@ -152,7 +152,9 @@ table.dataTable tbody td {
|
||||
}
|
||||
.dataTables_length label {height: 21px !important;}
|
||||
/* Modeal class */
|
||||
|
||||
.table-responsive {
|
||||
overflow-x: auto !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@ -160,10 +162,10 @@ table.dataTable tbody td {
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="row" style="margin-bottom:1rem;">
|
||||
<div class="col-6" style="align-self: center;">
|
||||
<h4 style="position: relative;">Opportunities List</h4>
|
||||
</div>
|
||||
<div class="row mb-2">
|
||||
<!-- <div class="col-sm-12">
|
||||
<h4 class="page-title">Opportunities List</h4>
|
||||
</div> -->
|
||||
|
||||
<div class="col-3" id="status_change" style="text-align: right; position: relative;top: 56px; left: 291px;">
|
||||
<!-- <button type="button" id="btnAdd" class="btn btn-primary waves-effect waves-light" onclick="openLeadTypeAskModal();">Add</button> -->
|
||||
@ -234,13 +236,13 @@ table.dataTable tbody td {
|
||||
<a href="#" class="dropdown-item btnEdit2 btnRfqSheetList" data-id="<?php echo $row['id']; ?>">
|
||||
<i class="mdi mdi-note-text mr-2 text-muted font-18 vertical-middle"></i>RFQ
|
||||
</a>
|
||||
<!-- <?php if (! in_array($row['status'], ['queued', 'rfq_created', 'rfq_sent'])) {?>
|
||||
<?php if (! in_array($row['status'], ['queued', 'rfq_created', 'rfq_sent'])) {?>
|
||||
<?php if (in_array(get_role_id(), [1, 5, 2, 3]) || in_array(BUSINESS_SUPPORT_TEAM_ID, user_team())) {?>
|
||||
<a href="<?php echo base_url('/rfq/nonEB/qcr/') . $row['id']; ?>" class="dropdown-item btnEdit3" data-id="<?php echo $row['id']; ?>">
|
||||
<a href="#" class="dropdown-item btnEdit3 btnQcrSheetList" data-id="<?php echo $row['id']; ?>">
|
||||
<i class="mdi mdi-note-text mr-2 text-muted font-18 vertical-middle"></i>QCR
|
||||
</a>
|
||||
<?php }?>
|
||||
<?php }?> -->
|
||||
<?php }?>
|
||||
|
||||
<?php if ($row['status'] != 'queued' ){?>
|
||||
<a href="#" class="dropdown-item btnInternalMailList" data-id="<?php echo $row['id']; ?>">
|
||||
@ -293,9 +295,34 @@ table.dataTable tbody td {
|
||||
</div><!-- end col -->
|
||||
</div>
|
||||
|
||||
<!-- External CC Disclaimer Confirmation Modal (z-index elevated to appear above other open modals) -->
|
||||
<div class="modal fade" id="external_cc_disclaimer_modal" tabindex="-1" role="dialog" aria-hidden="true" data-backdrop="static" data-keyboard="false" style="z-index: 1070;">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><i class="mdi mdi-alert-outline text-warning mr-2"></i>External Mail Warning</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>This mail will be sent to <strong>external email address(es)</strong>. Please review before proceeding.</p>
|
||||
<p id="external_cc_disclaimer_list" class="text-muted small mb-0"></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
|
||||
<button type="button" class="btn btn-primary" id="confirm_external_send_btn">Yes, Send Mail</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Hidden field to hold selected lead id for mail modals -->
|
||||
<input type="hidden" id="lead_id" value="">
|
||||
|
||||
<!-- Sheet creation wait message (shown over loader overlay) -->
|
||||
<div id="sheet-wait-msg" style="display:none; position:fixed; z-index:99999; left:0; right:0; bottom:calc(50% - 70px); text-align:center; color:#fff; font-size:14px; pointer-events:none;">
|
||||
Creating RFQ/QCR file, this may take a while, please wait...
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="New_Lead_modal" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
@ -484,10 +511,28 @@ table.dataTable tbody td {
|
||||
<h4>Attachments Files</h4>
|
||||
<hr>
|
||||
|
||||
<div class="form-group" id="placement_mail_attachment">
|
||||
<!-- <div class="form-row">
|
||||
<?php echo isset($attachment_html) && !empty($attachment_html) ? $attachment_html : ''; ?>
|
||||
</div> -->
|
||||
<div class="attachment-upload-wrap" data-attachment-target="#placement_mail_attachment">
|
||||
<div class="form-row align-items-end">
|
||||
<div class="form-group col-md-4 mb-2">
|
||||
<label>Document Label <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control att-docs-name" placeholder="Enter document label">
|
||||
</div>
|
||||
<div class="form-group col-md-5 mb-2">
|
||||
<label>File <span class="text-danger">*</span></label>
|
||||
<input type="file" class="form-control att-file-input">
|
||||
</div>
|
||||
<div class="form-group col-md-3 mb-2">
|
||||
<label> </label><br>
|
||||
<button type="button" class="btn btn-secondary btn-sm btn-upload-att">Upload & Add</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" id="placement_mail_attachment"></div>
|
||||
|
||||
<div class="form-group" id="external_cc_placement_wrap">
|
||||
<label for="external_cc_placement">External CC <small class="text-muted">(comma-separated email addresses)</small></label>
|
||||
<input type="text" class="form-control" id="external_cc_placement" placeholder="e.g. john@external.com, jane@external.com">
|
||||
</div>
|
||||
|
||||
<!-- if status is won disable the button -->
|
||||
@ -520,12 +565,34 @@ table.dataTable tbody td {
|
||||
<h4>Attachments Files</h4>
|
||||
<hr>
|
||||
|
||||
<div class="attachment-upload-wrap" data-attachment-target="#insurer_or_clinet_mail_attachment">
|
||||
<div class="form-row align-items-end">
|
||||
<div class="form-group col-md-4 mb-2">
|
||||
<label>Document Label <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control att-docs-name" placeholder="Enter document label">
|
||||
</div>
|
||||
<div class="form-group col-md-5 mb-2">
|
||||
<label>File <span class="text-danger">*</span></label>
|
||||
<input type="file" class="form-control att-file-input">
|
||||
</div>
|
||||
<div class="form-group col-md-3 mb-2">
|
||||
<label> </label><br>
|
||||
<button type="button" class="btn btn-secondary btn-sm btn-upload-att">Upload & Add</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" id="insurer_or_clinet_mail_attachment">
|
||||
<div class="form-row attachmet_row">
|
||||
<?php echo isset($attachment_html) && ! empty($attachment_html) ? $attachment_html : ''; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" id="external_cc_client_wrap" style="display:none;">
|
||||
<label for="external_cc_client">External CC <small class="text-muted">(comma-separated email addresses)</small></label>
|
||||
<input type="text" class="form-control" id="external_cc_client" placeholder="e.g. john@external.com, jane@external.com">
|
||||
</div>
|
||||
|
||||
<div class="form-group text-right m-b-0" id="hide_smbt_btn">
|
||||
<button type="submit" class="btn btn-primary" onclick="constructURL(1)">Send Mail</button>
|
||||
</div>
|
||||
@ -593,6 +660,23 @@ table.dataTable tbody td {
|
||||
<h4>Attachments Files</h4>
|
||||
<hr>
|
||||
|
||||
<div class="attachment-upload-wrap" data-attachment-target="#internal_mail_attachment">
|
||||
<div class="form-row align-items-end">
|
||||
<div class="form-group col-md-4 mb-2">
|
||||
<label>Document Label <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control att-docs-name" placeholder="Enter document label">
|
||||
</div>
|
||||
<div class="form-group col-md-5 mb-2">
|
||||
<label>File <span class="text-danger">*</span></label>
|
||||
<input type="file" class="form-control att-file-input">
|
||||
</div>
|
||||
<div class="form-group col-md-3 mb-2">
|
||||
<label> </label><br>
|
||||
<button type="button" class="btn btn-secondary btn-sm btn-upload-att">Upload & Add</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" id="internal_mail_attachment">
|
||||
<div class="form-row attachmet_row">
|
||||
<?php echo isset($attachment_html) && ! empty($attachment_html) ? $attachment_html : ''; ?>
|
||||
@ -600,7 +684,7 @@ table.dataTable tbody td {
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
|
||||
<div class="form-group text-right m-b-0" id="hide_smbt_btn">
|
||||
<button type="submit" class="btn btn-primary" onclick="constructURL(2)">Send Mail</button>
|
||||
</div>
|
||||
@ -701,19 +785,28 @@ table.dataTable tbody td {
|
||||
|
||||
document.body.appendChild(customDropdown);
|
||||
|
||||
row.addEventListener("click", function(event) {
|
||||
row.addEventListener("click", function(event) {
|
||||
if (event.target.closest('.dropdown-toggle')) {
|
||||
event.stopPropagation();
|
||||
const originalDropdown = event.target.closest('.btn-group').querySelector('.dropdown-menu');
|
||||
if (originalDropdown) {
|
||||
if (activeDropdown) {
|
||||
activeDropdown.style.display = 'none';
|
||||
}
|
||||
const rect = event.target.getBoundingClientRect();
|
||||
customDropdown.style.display = 'block';
|
||||
customDropdown.style.position = 'fixed';
|
||||
customDropdown.style.left = `${rect.left}px`;
|
||||
customDropdown.style.top = `${rect.bottom + 5}px`;
|
||||
activeDropdown = customDropdown;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. CRITICAL CHECK: Stop propagation if the click came from the dropdown toggle itself.
|
||||
if (event.target.closest('.dropdown-toggle')) {
|
||||
event.stopPropagation();
|
||||
|
||||
// **IMPORTANT:** Since you're using a custom script, you need to
|
||||
// manually trigger the logic for your custom detached dropdown here.
|
||||
// The code below should run when the dropdown toggle is clicked:
|
||||
const originalDropdown = event.target.closest('.btn-group').querySelector('.dropdown-menu');
|
||||
if (originalDropdown) {
|
||||
|
||||
// This is the code that opens your custom dropdown
|
||||
if (event.target.closest('td:last-child')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (activeDropdown) {
|
||||
activeDropdown.style.display = 'none';
|
||||
}
|
||||
@ -726,34 +819,9 @@ table.dataTable tbody td {
|
||||
customDropdown.style.top = `${rect.bottom + 5}px`;
|
||||
|
||||
activeDropdown = customDropdown;
|
||||
}
|
||||
event.stopPropagation();
|
||||
});
|
||||
|
||||
return; // Exit the row listener early
|
||||
}
|
||||
|
||||
// 2. Ignore clicks on the entire action column (your existing, good check)
|
||||
if (event.target.closest('td:last-child')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// ... rest of your row logic for general row clicks ...
|
||||
|
||||
if (activeDropdown) {
|
||||
activeDropdown.style.display = 'none';
|
||||
}
|
||||
|
||||
const rect = event.target.getBoundingClientRect();
|
||||
|
||||
customDropdown.style.display = 'block';
|
||||
customDropdown.style.position = 'fixed';
|
||||
customDropdown.style.left = `${rect.left}px`;
|
||||
customDropdown.style.top = `${rect.bottom + 5}px`;
|
||||
|
||||
activeDropdown = customDropdown;
|
||||
event.stopPropagation(); // Only needed if there is a parent handler
|
||||
});
|
||||
|
||||
// Handle clicks on dropdown items
|
||||
customDropdown.querySelectorAll('.dropdown-item').forEach(item => {
|
||||
item.addEventListener('click', function(e) {
|
||||
console.log("##############");
|
||||
@ -782,6 +850,12 @@ table.dataTable tbody td {
|
||||
createRfqSheetFromList(leadId);
|
||||
}
|
||||
|
||||
}else if (this.classList.contains('btnQcrSheetList')) {
|
||||
const leadId = this.getAttribute('data-id');
|
||||
if (leadId) {
|
||||
createQcrSheetFromList(leadId);
|
||||
}
|
||||
|
||||
}else if (this.classList.contains('btnInternalMailList')) {
|
||||
const leadId = this.getAttribute('data-id');
|
||||
if (leadId) {
|
||||
@ -821,6 +895,7 @@ table.dataTable tbody td {
|
||||
href !== '#' &&
|
||||
!this.classList.contains('btnEdit') &&
|
||||
!this.classList.contains('btnRfqSheetList') &&
|
||||
!this.classList.contains('btnQcrSheetList') &&
|
||||
!this.classList.contains('btnInternalMailList') &&
|
||||
!this.classList.contains('btnInsurerMailList') &&
|
||||
!this.classList.contains('btnClientMailList') &&
|
||||
@ -874,11 +949,18 @@ table.dataTable tbody td {
|
||||
lengthMenu: [[10, 20, 50, 100], [10, 20, 50, 100]],
|
||||
buttons: [
|
||||
{
|
||||
text: '<i class="mdi mdi-plus" ></i><span class=" btn-custom"> Add </span>',
|
||||
className: 'btn app-btn-primary mr-2',
|
||||
action: function(e, dt, node, config) {
|
||||
openLeadTypeAskModal();
|
||||
}
|
||||
text: '<i class="mdi mdi-filter" ></i><span class=" btn-custom"> Filter </span>',
|
||||
className: 'btn app-btn-primary mr-2',
|
||||
action: function(e, dt, node, config) {
|
||||
openOpportunitiesListFilterNav();
|
||||
}
|
||||
},
|
||||
{
|
||||
text: '<i class="mdi mdi-plus" ></i><span class=" btn-custom"> Add </span>',
|
||||
className: 'btn app-btn-primary mr-2',
|
||||
action: function(e, dt, node, config) {
|
||||
openLeadTypeAskModal();
|
||||
}
|
||||
},
|
||||
{
|
||||
extend: 'collection',
|
||||
@ -1130,11 +1212,15 @@ table.dataTable tbody td {
|
||||
$('#myCenterModalLabelForClientAndInsurer').html(modalTitle);
|
||||
console.log('modalTitle' + modalTitle);
|
||||
url = '<?php echo base_url('leads/list/') ?>' + lead_id;
|
||||
$('#external_cc_client_wrap').show();
|
||||
$('#external_cc_client').val('');
|
||||
} else {
|
||||
modalTitle = 'Insurer Mail';
|
||||
$('#myCenterModalLabelForClientAndInsurer').html(modalTitle);
|
||||
console.log('modalTitle' + modalTitle);
|
||||
url = '<?php echo base_url('util/getLevelContects') ?>';
|
||||
$('#external_cc_client_wrap').hide();
|
||||
$('#external_cc_client').val('');
|
||||
}
|
||||
|
||||
ajaxRequestForGetMailData(url);
|
||||
@ -1343,20 +1429,84 @@ table.dataTable tbody td {
|
||||
}
|
||||
}
|
||||
|
||||
function validateExternalCC(rawValue) {
|
||||
if (!rawValue || rawValue.trim() === '') return { valid: true, emails: [] };
|
||||
var emails = rawValue.split(',').map(function(e) { return e.trim(); }).filter(function(e) { return e !== ''; });
|
||||
var emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
var invalid = emails.filter(function(e) { return !emailRegex.test(e); });
|
||||
if (invalid.length > 0) {
|
||||
return { valid: false, emails: emails, invalid: invalid };
|
||||
}
|
||||
return { valid: true, emails: emails };
|
||||
}
|
||||
|
||||
function showExternalCCDisclaimer(emails, onConfirmCallback) {
|
||||
var listHtml = 'Sending to: <strong>' + emails.join(', ') + '</strong>';
|
||||
$('#external_cc_disclaimer_list').html(listHtml);
|
||||
window._externalCCSendCallback = onConfirmCallback;
|
||||
var disclaimerModalEl = document.getElementById('external_cc_disclaimer_modal');
|
||||
var disclaimerModal = new bootstrap.Modal(disclaimerModalEl);
|
||||
disclaimerModal.show();
|
||||
// Elevate the latest backdrop so it sits above the parent mail modal
|
||||
setTimeout(function() {
|
||||
$('.modal-backdrop').last().css('z-index', 1065);
|
||||
}, 150);
|
||||
}
|
||||
|
||||
$(document).on('click', '#confirm_external_send_btn', function () {
|
||||
// Close disclaimer modal first
|
||||
$('#external_cc_disclaimer_modal').modal('hide');
|
||||
|
||||
// Execute the pending send callback
|
||||
if (typeof window._externalCCSendCallback === 'function') {
|
||||
window._externalCCSendCallback();
|
||||
window._externalCCSendCallback = null;
|
||||
}
|
||||
});
|
||||
|
||||
function constructURL(url_type) {
|
||||
|
||||
if (url_type == 1) {
|
||||
var validationStatus = checkMailValidation();
|
||||
|
||||
if (validationStatus) {
|
||||
constructURL_ForInsurerAndClientMailSend();
|
||||
} else {
|
||||
if (!validationStatus) {
|
||||
toastr.error("All Client Mail ID's Should Contain Same Domain", "Error");
|
||||
return;
|
||||
}
|
||||
|
||||
if (RFQ_or_QCR == 2) {
|
||||
var externalCCRaw = $('#external_cc_client').val().trim();
|
||||
var result = validateExternalCC(externalCCRaw);
|
||||
if (!result.valid) {
|
||||
toastr.error('Invalid email(s) in External CC: ' + result.invalid.join(', '), 'Error');
|
||||
return;
|
||||
}
|
||||
if (result.emails.length > 0) {
|
||||
showExternalCCDisclaimer(result.emails, function() {
|
||||
constructURL_ForInsurerAndClientMailSend();
|
||||
});
|
||||
} else {
|
||||
constructURL_ForInsurerAndClientMailSend();
|
||||
}
|
||||
} else {
|
||||
constructURL_ForInsurerAndClientMailSend();
|
||||
}
|
||||
|
||||
} else if (url_type == 2) {
|
||||
constructURL_ForInternalMailSend();
|
||||
} else if (url_type == 3) {
|
||||
constructURL_ForPlacementMailSend();
|
||||
var externalCCRaw = $('#external_cc_placement').val().trim();
|
||||
var result = validateExternalCC(externalCCRaw);
|
||||
if (!result.valid) {
|
||||
toastr.error('Invalid email(s) in External CC: ' + result.invalid.join(', '), 'Error');
|
||||
return;
|
||||
}
|
||||
if (result.emails.length > 0) {
|
||||
showExternalCCDisclaimer(result.emails, function() {
|
||||
constructURL_ForPlacementMailSend();
|
||||
});
|
||||
} else {
|
||||
constructURL_ForPlacementMailSend();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1393,6 +1543,8 @@ table.dataTable tbody td {
|
||||
formData.append('selected_attachment_files', JSON.stringify(selectedFiles));
|
||||
|
||||
if (RFQ_or_QCR == 2) {
|
||||
var externalCCClient = validateExternalCC($('#external_cc_client').val().trim()).emails;
|
||||
formData.append('external_cc', JSON.stringify(externalCCClient));
|
||||
formData.append('contact_mail', contact_mail);
|
||||
formData.append('file_type', 'qcr');
|
||||
formData.append('recipient_type', 'client');
|
||||
@ -1526,6 +1678,10 @@ table.dataTable tbody td {
|
||||
formData.append('policy_end_date', policy_end_date);
|
||||
formData.append('policy_start_date', policy_start_date);
|
||||
|
||||
var externalCCPlacement = validateExternalCC($('#external_cc_placement').val().trim()).emails;
|
||||
var mergedPlacementCC = (cc || []).concat(externalCCPlacement);
|
||||
formData.set('cc', JSON.stringify(mergedPlacementCC));
|
||||
|
||||
ajaxRequest(formData);
|
||||
}
|
||||
|
||||
@ -1533,6 +1689,7 @@ table.dataTable tbody td {
|
||||
|
||||
var apiURL = '<?php echo base_url("leads/createRfqSheet") ?>';
|
||||
|
||||
$('#sheet-wait-msg').fadeIn();
|
||||
$('.loader').fadeIn();
|
||||
$('.loader-mask').fadeIn();
|
||||
|
||||
@ -1543,6 +1700,7 @@ table.dataTable tbody td {
|
||||
dataType: 'json',
|
||||
success: function(res) {
|
||||
|
||||
$('#sheet-wait-msg').fadeOut();
|
||||
$('.loader').fadeOut();
|
||||
$('.loader-mask').delay(350).fadeOut('slow');
|
||||
|
||||
@ -1554,6 +1712,7 @@ table.dataTable tbody td {
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
|
||||
$('#sheet-wait-msg').fadeOut();
|
||||
$('.loader').fadeOut();
|
||||
$('.loader-mask').delay(350).fadeOut('slow');
|
||||
console.error(xhr.responseText);
|
||||
@ -1563,6 +1722,101 @@ table.dataTable tbody td {
|
||||
});
|
||||
}
|
||||
|
||||
function createQcrSheetFromList(leadId) {
|
||||
|
||||
var apiURL = '<?php echo base_url("leads/createQcrSheet") ?>';
|
||||
|
||||
$('#sheet-wait-msg').fadeIn();
|
||||
$('.loader').fadeIn();
|
||||
$('.loader-mask').fadeIn();
|
||||
|
||||
$.ajax({
|
||||
url: apiURL,
|
||||
type: "GET",
|
||||
data: { lead_id: leadId },
|
||||
dataType: 'json',
|
||||
success: function(res) {
|
||||
|
||||
$('#sheet-wait-msg').fadeOut();
|
||||
$('.loader').fadeOut();
|
||||
$('.loader-mask').delay(350).fadeOut('slow');
|
||||
|
||||
if (res.status === 'success' && res.code === 200 && res.sheet_url) {
|
||||
window.open(res.sheet_url, '_blank');
|
||||
} else {
|
||||
toastr.error(res.message || 'Failed to create QCR sheet', 'Error');
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
|
||||
$('#sheet-wait-msg').fadeOut();
|
||||
$('.loader').fadeOut();
|
||||
$('.loader-mask').delay(350).fadeOut('slow');
|
||||
console.error(xhr.responseText);
|
||||
console.error(status, error);
|
||||
toastr.error('Failed to create QCR sheet', 'Error');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Delegated click handler for all modal upload buttons
|
||||
$(document).on('click', '.btn-upload-att', function () {
|
||||
var wrap = $(this).closest('.attachment-upload-wrap');
|
||||
var attachTarget = wrap.data('attachment-target');
|
||||
var docsName = wrap.find('.att-docs-name').val().trim();
|
||||
var fileInput = wrap.find('.att-file-input')[0];
|
||||
var file = fileInput && fileInput.files[0];
|
||||
var leadId = $('#lead_id').val();
|
||||
|
||||
if (!leadId) {
|
||||
toastr.error('Lead not selected.', 'Error');
|
||||
return;
|
||||
}
|
||||
if (!docsName) {
|
||||
toastr.error('Please enter a document label.', 'Error');
|
||||
return;
|
||||
}
|
||||
if (!file) {
|
||||
toastr.error('Please select a file to upload.', 'Error');
|
||||
return;
|
||||
}
|
||||
|
||||
var formData = new FormData();
|
||||
formData.append('lead_id', leadId);
|
||||
formData.append('docs_name', docsName);
|
||||
formData.append('attachment_file', file);
|
||||
|
||||
$('.loader').fadeIn();
|
||||
$('.loader-mask').fadeIn();
|
||||
|
||||
$.ajax({
|
||||
url: '<?php echo base_url("leads/uploadLeadAttachment") ?>',
|
||||
type: 'POST',
|
||||
data: formData,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
dataType: 'json',
|
||||
success: function (res) {
|
||||
$('.loader').fadeOut();
|
||||
$('.loader-mask').delay(350).fadeOut('slow');
|
||||
if (res.status === 'success') {
|
||||
$(attachTarget).html(res.attachment_html || '');
|
||||
wrap.find('.att-docs-name').val('');
|
||||
wrap.find('.att-file-input').val('');
|
||||
toastr.success('File uploaded successfully.', 'Success');
|
||||
} else {
|
||||
toastr.error(res.message || 'Upload failed.', 'Error');
|
||||
}
|
||||
},
|
||||
error: function (xhr, status, error) {
|
||||
$('.loader').fadeOut();
|
||||
$('.loader-mask').delay(350).fadeOut('slow');
|
||||
console.error(xhr.responseText, status, error);
|
||||
toastr.error('Upload failed.', 'Error');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function fetchPlacementData(leadId, callback) {
|
||||
|
||||
var apiURL = '<?php echo base_url("rfq/placementData") ?>/' + leadId;
|
||||
@ -1705,33 +1959,19 @@ table.dataTable tbody td {
|
||||
$('#placement_mail_btn').prop('disabled', false);
|
||||
}
|
||||
|
||||
// Populate TO with insurer contacts (id -> contact id)
|
||||
// Populate TO with client contact from lead data
|
||||
var $placementTo = $('#placement_to');
|
||||
$placementTo.empty().append('<option value="">Select TO Mail</option>');
|
||||
if (Array.isArray(data.insurer_contacts)) {
|
||||
data.insurer_contacts.forEach(function (c) {
|
||||
if (c.id) {
|
||||
$placementTo.append(
|
||||
$('<option>', {
|
||||
value: c.id,
|
||||
text: c.display || c.email || ''
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Select all insurer contacts by default, if any
|
||||
var defaultTo = [];
|
||||
if (Array.isArray(data.insurer_contacts)) {
|
||||
data.insurer_contacts.forEach(function (c) {
|
||||
if (c.id) {
|
||||
defaultTo.push(String(c.id));
|
||||
}
|
||||
});
|
||||
}
|
||||
if (defaultTo.length) {
|
||||
$placementTo.val(defaultTo);
|
||||
if (data.client_contact && data.client_contact.email) {
|
||||
var displayText = (data.client_contact.name || '') + ' - ' + data.client_contact.email;
|
||||
$placementTo.append(
|
||||
$('<option>', {
|
||||
value: data.client_contact.email,
|
||||
text: displayText.replace(/^- /, '')
|
||||
})
|
||||
);
|
||||
// Select by default
|
||||
$placementTo.val(data.client_contact.email);
|
||||
}
|
||||
|
||||
window.RFQ_or_QCR = 2; // Placement behaves like QCR for file_type
|
||||
@ -2103,5 +2343,11 @@ table.dataTable tbody td {
|
||||
this.closest('.radio-option').classList.add('selected');
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
if (typeof openOpportunitiesListFilterNav !== 'function') {
|
||||
console.log('Developer Notice: The file lead_filter.php must be loaded by the controller for the filters to work, not leads_list.php.');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@ -47,7 +47,14 @@
|
||||
border-width: 1px !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<?php
|
||||
$isLeadEditNonEb = isset($lead_edit_data) && ! empty($lead_edit_data);
|
||||
$nonEbSubtitle = $isLeadEditNonEb ? 'Edit Opportunities - (Non EB)' : 'Add Opportunities - (Non EB)';
|
||||
?>
|
||||
<script>
|
||||
var pageSubTitle = '<?= $nonEbSubtitle ?>';
|
||||
var pageBackButton = '<a href="<?= base_url('leads/list') ?>" data-toggle="tooltip" data-placement="top" title="Back"><i class="mdi mdi-arrow-left" style="font-size: 17px;"></i></a>';
|
||||
</script>
|
||||
|
||||
<div class="row" id="leads_non_eb_form">
|
||||
<div class="col-12">
|
||||
@ -62,12 +69,12 @@
|
||||
<a href="<?= base_url('leads/list') ?>" type="button" id="btnAdd"
|
||||
class="btn btn-primary waves-effect waves-light">Back</a>
|
||||
</div> -->
|
||||
<h4 class="d-flex align-items-center" style="gap: 8px;">
|
||||
<!-- <h4 class="d-flex align-items-center" style="gap: 8px;">
|
||||
<a href="<?= base_url('leads/list') ?>" style="cursor: pointer;" type="button" id="btnAdd">
|
||||
<i class="mdi mdi-chevron-left" style="font-size: 43px;"></i>
|
||||
</a>
|
||||
<span class="title-text" id="page_title" style="position: relative;">Add Opportunity</span>
|
||||
</h4>
|
||||
</h4> -->
|
||||
</div>
|
||||
<form role="form" class="parsley-examples" method="post" id="leads_non_eb_form_id"
|
||||
enctype="multipart/form-data">
|
||||
@ -368,6 +375,29 @@
|
||||
<!-- other row -->
|
||||
<div class="card" id="collapseOne">
|
||||
<div class="card-body">
|
||||
<div class="form-row">
|
||||
<?php
|
||||
$savedViewers = [];
|
||||
if (!empty($lead_edit_data['rfq_qcr_viewers'] ?? null)) {
|
||||
$decoded = json_decode($lead_edit_data['rfq_qcr_viewers'], true);
|
||||
if (is_array($decoded)) $savedViewers = $decoded;
|
||||
}
|
||||
?>
|
||||
<div class="form-group col-md-12">
|
||||
<label for="rfq_qcr_viewers">RFQ/QCR File Viewers <span class="text-danger">*</span></label>
|
||||
<select class="form-control" id="rfq_qcr_viewers" name="rfq_qcr_viewers" multiple required>
|
||||
<?php if (isset($salse_team)) { ?>
|
||||
<?php foreach ($salse_team as $member) { ?>
|
||||
<?php $email = $member['email'] ?? ''; if (empty($email)) continue; ?>
|
||||
<option value="<?= htmlspecialchars($email) ?>" <?= in_array($email, $savedViewers) ? 'selected' : '' ?>>
|
||||
<?= htmlspecialchars($member['first_name']) ?>
|
||||
</option>
|
||||
<?php } ?>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
|
||||
<!-- <div class="form-group col-md-3">
|
||||
@ -440,6 +470,14 @@
|
||||
<script>
|
||||
|
||||
$(document).ready(function() {
|
||||
$('#rfq_qcr_viewers').parsley({
|
||||
errorsContainer: function(ParsleyField) {
|
||||
if (ParsleyField.$element.hasClass('select2-hidden-accessible')) {
|
||||
return ParsleyField.$element.siblings('.select2-container');
|
||||
}
|
||||
return ParsleyField.$element;
|
||||
}
|
||||
});
|
||||
$('#salse_person_id').parsley({
|
||||
errorsContainer: function(ParsleyField) {
|
||||
if (ParsleyField.$element.hasClass('select2-hidden-accessible')) {
|
||||
@ -593,6 +631,9 @@
|
||||
return;
|
||||
}
|
||||
|
||||
var rfq_qcr_viewers = $("#rfq_qcr_viewers").val();
|
||||
console.log('rfq_qcr_viewers : ', rfq_qcr_viewers);
|
||||
|
||||
var salse_person_id = $("#salse_person_id").val();
|
||||
console.log('salse_person_id : ', salse_person_id);
|
||||
|
||||
@ -636,6 +677,7 @@
|
||||
formData.append('claim_history',cliam_history_status);
|
||||
|
||||
// Append the JSON string to the FormData object
|
||||
formData.append('rfq_qcr_viewers', JSON.stringify(rfq_qcr_viewers));
|
||||
formData.append('salse_person_id', jsonString);
|
||||
formData.append('custom_fields', JSON.stringify(customFieldData));
|
||||
|
||||
@ -1277,14 +1319,24 @@
|
||||
$('#client_name').val(actual_lead_client_details.company_name || '');
|
||||
|
||||
$('#gst').val(actual_lead_client_details.gst_number || '');
|
||||
existingShortName = actual_lead_client_details.short_name || '';
|
||||
if (existingShortName) {
|
||||
// ── Short name EXISTS — just populate and validate ────────
|
||||
$('#client_short_name').val(existingShortName);
|
||||
// validateInput($('#client_short_name')[0], 'clients', 'short_name');
|
||||
|
||||
// 🔥 Important:
|
||||
// Only auto-generate short name IF empty (avoid overwrite in edit)
|
||||
if (!$('#client_short_name').val()) {
|
||||
$('#client_name').trigger('input');
|
||||
} else {
|
||||
// Run duplicate validation once
|
||||
validateInput($('#client_short_name')[0], "clients", "short_name");
|
||||
// Auto-generate short name from company name
|
||||
// Use a small delay to ensure DOM is ready
|
||||
setTimeout(function () {
|
||||
let baseName = actual_lead_client_details.company_name
|
||||
.trim()
|
||||
.substring(0, 10)
|
||||
.replace(/\s+/g, '')
|
||||
.toUpperCase();
|
||||
|
||||
makeUniqueShortName(baseName);
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -8,16 +8,17 @@
|
||||
</div>
|
||||
<div class="modal-body" style="overflow-y: auto;height: 90vh;">
|
||||
<form class="parsley-examples" method="post" id="client_form" enctype="multipart/form-data">
|
||||
<input type="hidden" name="from_modal" value="1">
|
||||
<div class="form-group">
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-12">
|
||||
<label for="client_name">Client Name<span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="client_name" name="client_name" required>
|
||||
<label for="client_name_for_modal">Client Name<span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="client_name_for_modal" name="client_name" required>
|
||||
</div>
|
||||
<div class="form-group col-md-12">
|
||||
<label for="short_name">Client Short name<span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="short_name" name="short_name" onkeyup="validateInputForClient(this, 'clients', 'short_name')" required>
|
||||
<label for="client_short_name_for_modal">Client Short name<span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="client_short_name_for_modal" name="short_name" onkeyup="validateInputForClient(this, 'clients', 'short_name')" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-12">
|
||||
@ -31,7 +32,7 @@
|
||||
<!-- Client Type -->
|
||||
<div class="form-row clienttypediv" style="display: none;">
|
||||
|
||||
<div class="form-group col-md-6">
|
||||
<!-- <div class="form-group col-md-6">
|
||||
<label for="dob">Date of Birth<span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="dob" placeholder="DD/MM/YYYY" name="dob" >
|
||||
</div>
|
||||
@ -40,16 +41,16 @@
|
||||
<label for="cost_center">Mobile<span class="text-danger"></span></label>
|
||||
<input type="text" class="form-control" id="phone" placeholder="Enter Mobile Number" maxlength="10" name="phone"
|
||||
onkeypress="return onlyNumbers(event)" onchange="validateInputForClient(this, 'clients', 'phone')">
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<div class="form-group col-md-12">
|
||||
<!-- <div class="form-group col-md-12">
|
||||
<label for="email2">Email<span class="text-danger">*</span></label>
|
||||
<input type="email" class="form-control" id="email2" placeholder="Enter Email" name="email2">
|
||||
</div>
|
||||
<div class="form-group col-md-12">
|
||||
<label for="aadhar">Aadhar</label>
|
||||
<input type="text" class="form-control" id="aadhar" placeholder="Enter Aadher No" name="aadhar" maxlength="12" onchange="validateInputForClient(this, 'clients', 'aadhar')">
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
</div>
|
||||
|
||||
@ -57,8 +58,8 @@
|
||||
<div class="form-row branchdiv">
|
||||
|
||||
<div class="form-group col-md-12">
|
||||
<label for="short_name">Entity Type<span class="text-danger"></span></label>
|
||||
<select class="form-control" id="entity_type_id_for_client" name="entity_type_id">
|
||||
<label for="short_name">Entity Type<span class="text-danger">*</span></label>
|
||||
<select class="form-control" id="entity_type_id_for_client" name="entity_type_id" required>
|
||||
<option value="" selected>Select Entity</option>
|
||||
<?php
|
||||
if (isset($entity) && count($entity)) {
|
||||
@ -87,7 +88,7 @@
|
||||
|
||||
<div class="form-group col-md-6">
|
||||
<label for="branch_code">Mobile<span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="mobile" maxlength="10" name="mobile" onkeypress="return onlyNumbers(event)" required>
|
||||
<input type="text" class="form-control" id="mobile" maxlength="10" name="mobile" onkeypress="return onlyNumbers(event)" oninput="this.value = this.value.replace(/[^0-9]/g, '');" required>
|
||||
</div>
|
||||
<div class="form-group col-md-12">
|
||||
<label for="email">Email<span class="text-danger">*</span></label>
|
||||
@ -243,6 +244,10 @@
|
||||
appendClients(res.clients, res.client_id);
|
||||
appendBranch(res.branches, res.branch_id);
|
||||
|
||||
$('#client_name').val($('#client_name_for_modal').val());
|
||||
$('#client_short_name').val($('#client_short_name_for_modal').val());
|
||||
$('#contact_person_name').val(res.data.name);
|
||||
|
||||
$('#gst').val(res.data.gst);
|
||||
$('#branch_name').val(res.data.branch_name);
|
||||
$('#branch_code').val(res.data.branch_code);
|
||||
@ -285,11 +290,87 @@
|
||||
error: function (xhr, status, error) {
|
||||
console.error(xhr.responseText);
|
||||
console.error(status, error);
|
||||
isClientFormSubmitting = false;
|
||||
$('.loader').fadeOut();
|
||||
$('.loader-mask').delay(350).fadeOut('slow');
|
||||
setTimeout(function() {
|
||||
$('.loader').fadeOut();
|
||||
$('.loader-mask').delay(350).fadeOut('slow');
|
||||
// $('#client_add_modal_submit_btn').prop('disabled', false);
|
||||
if (xhr.status === 400) {
|
||||
let response = JSON.parse(xhr.responseText);
|
||||
let errorMessages = "";
|
||||
let seenMessages = []; // Array to store unique messages
|
||||
if (response.errors) {
|
||||
$.each(response.errors, function (field, message) {
|
||||
if (!seenMessages.includes(message)) {
|
||||
errorMessages += `• ${message}<br>`;
|
||||
seenMessages.push(message); // Mark this message as "seen"
|
||||
}
|
||||
});
|
||||
toastr.error(errorMessages, 'Validation Error', { "allowHtml": true });
|
||||
} else {
|
||||
toastr.warning(response.message || 'Validation failed', 'Warning');
|
||||
}
|
||||
} else if (xhr.status === 403) {
|
||||
let response = JSON.parse(xhr.responseText);
|
||||
toastr.error(response.message, 'Security Policy');
|
||||
} else if (xhr.status === 404) {
|
||||
toastr.warning('Resource not found', 'Warning');
|
||||
} else if (xhr.status === 500) {
|
||||
toastr.error('Something went wrong . Please try again later.', 'Server Error');
|
||||
} else {
|
||||
toastr.error('An unexpected error occurred. Please try again later.', 'Error');
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('#client_name_for_modal').on('input', function() {
|
||||
clearTimeout(shortNameTimer);
|
||||
shortNameTimer = setTimeout(generateShortNameModal, 200);
|
||||
});
|
||||
|
||||
function generateShortNameModal() {
|
||||
let clientInput = $("#client_name_for_modal");
|
||||
let shortInput = $("#client_short_name_for_modal");
|
||||
let newClientName = clientInput.val().trim();
|
||||
|
||||
console.log(`LN : NEW - ${newClientName}`);
|
||||
if (newClientName.length == 0) {
|
||||
shortInput.val('');
|
||||
return;
|
||||
}
|
||||
|
||||
let shortName = newClientName.substring(0, 10).replace(/\s+/g, '').toUpperCase();
|
||||
console.log(`LN : NEW - ${shortName}`);
|
||||
makeUniqueShortNameModal(shortName);
|
||||
}
|
||||
|
||||
function makeUniqueShortNameModal(baseName) {
|
||||
let input = $("#client_short_name_for_modal")[0];
|
||||
|
||||
checkDuplicateTableFieldValue("clients", "short_name", baseName, function(isDuplicate) {
|
||||
if (isDuplicate) {
|
||||
let counter = 1;
|
||||
function tryNext() {
|
||||
let padded = String(counter).padStart(3, '0'); // 001, 002, 003
|
||||
let newName = baseName + padded;
|
||||
|
||||
checkDuplicateTableFieldValue("clients", "short_name", newName, function(exists) {
|
||||
if (exists) {
|
||||
counter++;
|
||||
tryNext();
|
||||
} else {
|
||||
$("#client_short_name_for_modal").val(newName);
|
||||
validateInput(input, "clients", "short_name");
|
||||
}
|
||||
});
|
||||
}
|
||||
tryNext();
|
||||
} else {
|
||||
$("#client_short_name_for_modal").val(baseName);
|
||||
validateInput(input, "clients", "short_name");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
@ -9,7 +9,7 @@
|
||||
<div class="row" id="List-page">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body maincard">
|
||||
<div class="card-body">
|
||||
<table data-custom-table-css="table" class="table table-sm m-0 table-centered dt-responsive nowrap w-100" cellspacing="a" id="user-table">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
|
||||
@ -34,79 +34,107 @@
|
||||
padding-left: 15px !important;
|
||||
}
|
||||
.dataTables_length label {height: 21px !important;}
|
||||
.filter-sidebar {
|
||||
height: 100%;
|
||||
width: 0;
|
||||
position: fixed;
|
||||
z-index: 1001;
|
||||
top: 0;
|
||||
right: 0;
|
||||
background-color: #f8f9fa;
|
||||
overflow-x: hidden;
|
||||
transition: 0.5s;
|
||||
box-shadow: -2px 0 5px rgba(0,0,0,0.1);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.filter-sidebar-header {
|
||||
padding: 15px 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.filter-sidebar-header .closebtn {
|
||||
font-size: 28px;
|
||||
text-decoration: none;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.filter-sidebar .closebtn:hover {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.filter-sidebar-content {
|
||||
padding: 20px;
|
||||
flex-grow: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.filter-sidebar-footer {
|
||||
padding: 15px 20px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
border-top: 1px solid #dee2e6;
|
||||
flex-shrink: 0;
|
||||
gap: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="container-fluid-min">
|
||||
<div class="col-12" id="outstanding_filter">
|
||||
<div class="card-body">
|
||||
<div id="accordion" class="mb-3">
|
||||
<div class="card mb-1">
|
||||
<h4 class="m-1">
|
||||
<span>Filter</span>
|
||||
<a id="toggleIcon" class="text-dark float-right" data-toggle="collapse" href="#collapseOne"
|
||||
aria-expanded="true">
|
||||
<i id="icon" class="mdi mdi-chevron-down mr-1 text-primary" style="font-size: 28px;"></i>
|
||||
</a>
|
||||
</h4>
|
||||
<div id="collapseOne" class="collapse show" aria-labelledby="headingOne" data-parent="#accordion">
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<div class="form-row">
|
||||
<div id="filter-sidebar" class="filter-sidebar">
|
||||
<div class="filter-sidebar-header">
|
||||
<h4 class="m-0">Filter</h4>
|
||||
<a href="javascript:void(0)" class="closebtn" onclick="closeFilterNav()">×</a>
|
||||
</div>
|
||||
<div class="filter-sidebar-content">
|
||||
<div class="form-group">
|
||||
<div class="form-row">
|
||||
|
||||
<div class="form-group col-md-4">
|
||||
<label for="addon_policy"> Insurer <span
|
||||
class="text-danger">*</span></label>
|
||||
<select class="form-control" id="insurer_id" name="insurer_id" onchange="getInsurerBranch()">
|
||||
<option value="0" selected>Select Insurer</option>
|
||||
<?php if (isset($insurer) && count($insurer)) { ?>
|
||||
<?php foreach ($insurer as $value) { ?>
|
||||
<option value="<?= $value['id'] ?>"><?= $value['name'] ?></option>
|
||||
<?php } ?>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-4">
|
||||
<label for="addon_policy"> Insurer Branch<span
|
||||
class="text-danger">*</span></label>
|
||||
<select class="form-control" id="insurer_branch_id" name="insurer_branch_id">
|
||||
<option value="0" selected>Select Insurer Branch</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-4" style="display: true;" id="date_div">
|
||||
<label>Statement Month<span class="text-danger"></span></label>
|
||||
<div class="input-icon">
|
||||
<input type="text" id="reportrange" class="form-control" readonly style="caret-color: transparent;">
|
||||
<i class="mdi mdi-calendar-blank-outline additional-icon"></i>
|
||||
</div>
|
||||
<!--
|
||||
codeword : ***1***
|
||||
if you cross check means also uncomment "star2star" (***2***)
|
||||
<div id="reportrange" class="form-control"
|
||||
style="background: #fff; cursor: pointer; padding: 5px 10px; border: 1px solid #ccc; width: 100%">
|
||||
<i class="mdi mdi-calendar-blank"></i>
|
||||
<span></span> <i class="mdi mdi-menu-down"></i>
|
||||
</div> -->
|
||||
<input type="hidden" id="startDate">
|
||||
<input type="hidden" id="endDate">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="form-group text-right m-b-0">
|
||||
<a href="<?= base_url("/policy_tranction/report/report-outstanding-list"); ?>"
|
||||
class="btn btn-secondary" id="clear-filters">Clear</a>
|
||||
<a href="<?= base_url("/policy_tranction/report/report-outstanding-list"); ?>"
|
||||
class="btn btn-primary" id="get-emp-list"
|
||||
onclick="fetchEmpolyeeList(event);">Submit</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-12">
|
||||
<label for="addon_policy"> Insurer <span
|
||||
class="text-danger">*</span></label>
|
||||
<select class="form-control" id="insurer_id" name="insurer_id" onchange="getInsurerBranch()">
|
||||
<option value="0" selected>Select Insurer</option>
|
||||
<?php if (isset($insurer) && count($insurer)) { ?>
|
||||
<?php foreach ($insurer as $value) { ?>
|
||||
<option value="<?= $value['id'] ?>"><?= $value['name'] ?></option>
|
||||
<?php } ?>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-12">
|
||||
<label for="addon_policy"> Insurer Branch<span
|
||||
class="text-danger">*</span></label>
|
||||
<select class="form-control" id="insurer_branch_id" name="insurer_branch_id">
|
||||
<option value="0" selected>Select Insurer Branch</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-12" style="display: true;" id="date_div">
|
||||
<label>Statement Month<span class="text-danger"></span></label>
|
||||
<div class="input-icon">
|
||||
<input type="text" id="reportrange" class="form-control" readonly style="caret-color: transparent;">
|
||||
<i class="mdi mdi-calendar-blank-outline additional-icon"></i>
|
||||
</div>
|
||||
<input type="hidden" id="startDate">
|
||||
<input type="hidden" id="endDate">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="filter-sidebar-footer">
|
||||
<a href="<?= base_url("/policy_tranction/report/report-outstanding-list"); ?>"
|
||||
class="btn btn-secondary" id="clear-filters">Clear</a>
|
||||
<a href="<?= base_url("/policy_tranction/report/report-outstanding-list"); ?>"
|
||||
class="btn btn-primary" id="get-emp-list"
|
||||
onclick="fetchEmpolyeeList(event);closeFilterNav();">Submit</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12" id="outstanding_list">
|
||||
@ -172,6 +200,14 @@
|
||||
|
||||
|
||||
<script>
|
||||
function openOutstandingFilterNav() {
|
||||
document.getElementById("filter-sidebar").style.width = "350px";
|
||||
}
|
||||
|
||||
function closeFilterNav() {
|
||||
document.getElementById("filter-sidebar").style.width = "0";
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
var ticketsTable = $('#scroll-horizontal-datatable');
|
||||
@ -186,7 +222,18 @@
|
||||
"<'row'<'col-sm-12'tr>>" +
|
||||
"<'row align-items-center'<'col-5 text-start'i><'col-7 d-flex justify-content-end align-items-center'<'me-2'l>p>>",
|
||||
lengthMenu: [[10, 20, 50, 100], [10, 20, 50, 100]],
|
||||
buttons: [{
|
||||
buttons: [
|
||||
{
|
||||
text: '<i class="mdi mdi-filter"></i><span class="btn-custom"> Filter </span>',
|
||||
className: 'btn app-btn-primary mr-2',
|
||||
action: function (e, dt, node, config) {
|
||||
openOutstandingFilterNav();
|
||||
},
|
||||
attr: {
|
||||
id: 'openOutstandingFilterNav'
|
||||
}
|
||||
},
|
||||
{
|
||||
extend: 'collection',
|
||||
text: '<span class=" btn-custom"> Export </span><i class="mdi mdi-menu-down"></i>',
|
||||
className: 'btn app-btn-secondary ',
|
||||
|
||||
@ -313,6 +313,13 @@
|
||||
"<'row align-items-center'<'col-5 text-start'i><'col-7 d-flex justify-content-end align-items-center'<'me-2'l>p>>",
|
||||
lengthMenu: [[10, 20, 50, 100], [10, 20, 50, 100]],
|
||||
buttons: [
|
||||
{
|
||||
text: '<i class="mdi mdi-filter" ></i><span class=" btn-custom"> Filter </span>',
|
||||
className: 'btn app-btn-primary mr-2',
|
||||
action: function(e, dt, node, config) {
|
||||
openPayoutFilterNav();
|
||||
}
|
||||
},
|
||||
// {
|
||||
// text: 'Add',
|
||||
// className: 'btn app-btn-primary mr-2',
|
||||
|
||||
@ -1,78 +1,105 @@
|
||||
<div class="container-fluid-min">
|
||||
<div class="col-12" id="bds_filter">
|
||||
<div class="card-body">
|
||||
<div id="accordion" class="mb-3">
|
||||
<div class="card mb-1">
|
||||
<h4 class="m-1">
|
||||
<span>Filter</span>
|
||||
<a id="toggleIcon" class="text-dark float-right" data-toggle="collapse" href="#collapseOne"
|
||||
aria-expanded="true">
|
||||
<i id="icon" class="mdi mdi-chevron-down mr-1 text-primary" style="font-size: 28px;"></i>
|
||||
</a>
|
||||
</h4>
|
||||
<div id="collapseOne" class="collapse show" aria-labelledby="headingOne" data-parent="#accordion">
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<div class="form-row">
|
||||
<!-- <div class="form-group col-md-3">
|
||||
<label for="client_branch">Agents<span class="text-danger"></span></label>
|
||||
<select class="form-control" id="agent_id" name="agent_id">
|
||||
<option value="">Select Agent</option>
|
||||
<?php if (isset($agent_list) && !empty($agent_list)) : ?>
|
||||
<?php foreach ($agent_list as $agent) : ?>
|
||||
<option value="<?= $agent['id']; ?>"><?= $agent['name']; ?></option>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</select>
|
||||
</div> -->
|
||||
<div class="form-group col-md-3">
|
||||
<label for="pos_id">POS<span class="text-danger"></span></label>
|
||||
<select class="form-control" id="pos_id" name="pos_id">
|
||||
<option value="">Select POS (Manager)</option>
|
||||
<?php if (isset($pos_list) && !empty($pos_list)) : ?>
|
||||
<?php foreach ($pos_list as $pos) : ?>
|
||||
<option value="<?= $pos['id']; ?>"><?= htmlspecialchars($pos['name'] . ' (' . $pos['manager_name'] . ')'); ?></option>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</select>
|
||||
</div>
|
||||
<style>
|
||||
.filter-sidebar {
|
||||
height: 100%;
|
||||
width: 0;
|
||||
position: fixed;
|
||||
z-index: 1001;
|
||||
top: 0;
|
||||
right: 0;
|
||||
background-color: #f8f9fa;
|
||||
overflow-x: hidden;
|
||||
transition: 0.5s;
|
||||
box-shadow: -2px 0 5px rgba(0,0,0,0.1);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
<div class="form-group col-md-3">
|
||||
<label for="client_branch_id">Status<span class="text-danger"></span></label>
|
||||
<select class="form-control" id="status_id" name="status_id">
|
||||
<option value="">Select status</option>
|
||||
<?php if (isset($payout_status) && !empty($payout_status)) : ?>
|
||||
<?php foreach ($payout_status as $id => $status) : ?>
|
||||
<option value="<?= $id; ?>"><?= $status; ?></option>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</select>
|
||||
</div>
|
||||
.filter-sidebar-header {
|
||||
padding: 15px 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
<div class="form-group col-md-3" style="display: true;" id="date_div">
|
||||
<label>Date<span class="text-danger"></span></label>
|
||||
<div class="input-icon">
|
||||
<input type="text" id="reportrange" class="form-control" readonly style="caret-color: transparent;">
|
||||
<i class="mdi mdi-calendar-blank-outline additional-icon"></i>
|
||||
</div>
|
||||
<input type="hidden" id="startDate">
|
||||
<input type="hidden" id="endDate">
|
||||
</div>
|
||||
.filter-sidebar-header .closebtn {
|
||||
font-size: 28px;
|
||||
text-decoration: none;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
<div class="form-group col-md-3 text-right" style="margin-top: 29px;">
|
||||
<a class="btn btn-secondary" id="clear-filters">Clear</a>
|
||||
<a class="btn btn-primary" id="get-emp-list" onclick="fetchPayoutList(this);">Submit</a>
|
||||
</div>
|
||||
.filter-sidebar .closebtn:hover {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
</div>
|
||||
.filter-sidebar-content {
|
||||
padding: 20px;
|
||||
flex-grow: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
.filter-sidebar-footer {
|
||||
padding: 15px 20px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
border-top: 1px solid #dee2e6;
|
||||
flex-shrink: 0;
|
||||
gap: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="payout-filter-sidebar" class="filter-sidebar">
|
||||
<div class="filter-sidebar-header">
|
||||
<h4 class="m-0">Filter</h4>
|
||||
<a href="javascript:void(0)" class="closebtn" onclick="closePayoutFilterNav()">×</a>
|
||||
</div>
|
||||
<div class="filter-sidebar-content">
|
||||
<div class="form-group">
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-12">
|
||||
<label for="pos_id">POS<span class="text-danger"></span></label>
|
||||
<select class="form-control" id="pos_id" name="pos_id">
|
||||
<option value="">Select POS (Manager)</option>
|
||||
<?php if (isset($pos_list) && !empty($pos_list)) : ?>
|
||||
<?php foreach ($pos_list as $pos) : ?>
|
||||
<option value="<?= $pos['id']; ?>"><?= htmlspecialchars($pos['name'] . ' (' . $pos['manager_name'] . ')'); ?></option>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-12">
|
||||
<label for="client_branch_id">Status<span class="text-danger"></span></label>
|
||||
<select class="form-control" id="status_id" name="status_id">
|
||||
<option value="">Select status</option>
|
||||
<?php if (isset($payout_status) && !empty($payout_status)) : ?>
|
||||
<?php foreach ($payout_status as $id => $status) : ?>
|
||||
<option value="<?= $id; ?>"><?= $status; ?></option>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-12" style="display: true;" id="date_div">
|
||||
<label>Date<span class="text-danger"></span></label>
|
||||
<div class="input-icon">
|
||||
<input type="text" id="reportrange" class="form-control" readonly style="caret-color: transparent;">
|
||||
<i class="mdi mdi-calendar-blank-outline additional-icon"></i>
|
||||
</div>
|
||||
<input type="hidden" id="startDate">
|
||||
<input type="hidden" id="endDate">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="filter-sidebar-footer">
|
||||
<a href="#" class="btn btn-secondary" id="clear-filters">Clear</a>
|
||||
<a class="btn btn-primary" id="get-emp-list" onclick="fetchPayoutList(this); closePayoutFilterNav();">Submit</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container-fluid-min">
|
||||
<!-- end page title -->
|
||||
<div id="payout_list">
|
||||
<?php if(isset($payout_list) && !empty($payout_list)) { echo $payout_list; }else{ } ?>
|
||||
@ -80,6 +107,14 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function openPayoutFilterNav() {
|
||||
document.getElementById("payout-filter-sidebar").style.width = "350px";
|
||||
}
|
||||
|
||||
function closePayoutFilterNav() {
|
||||
document.getElementById("payout-filter-sidebar").style.width = "0";
|
||||
}
|
||||
|
||||
let utrHasBeenChanged = false;
|
||||
|
||||
$(document).ready(function(){
|
||||
|
||||
@ -156,7 +156,7 @@
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="row" style="margin-bottom:1rem;">
|
||||
<!-- <div class="row">
|
||||
<div class="col-6" style="align-self: center;">
|
||||
<h4 style="position: relative;">Add Endorsement</h4>
|
||||
</div>
|
||||
@ -164,7 +164,7 @@
|
||||
<a href="<?= base_url('policy_tranction/endorsement/list') ?>" id="btnAdd" class="btn btn-primary waves-effect waves-light"
|
||||
>Back</a>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
<form role="form" class="parsley-examples" method="post" id="endorsement_form_id" enctype="multipart/form-data">
|
||||
|
||||
<input type="hidden" name="id" id="policy_tranction_primarykey">
|
||||
@ -1052,6 +1052,12 @@
|
||||
if(res.status == true){
|
||||
|
||||
hide_list_show_add()
|
||||
var page_title = 'Edit Endorsement' + (res.data.client_short_name || res.data.policy_type || res.data.policy_no ?
|
||||
' - ' + [res.data.client_short_name, res.data.policy_type, res.data.policy_no]
|
||||
.filter(Boolean).join('-') : '');
|
||||
var backUrl = '<?= base_url("policy_tranction/endorsement/list"); ?>';
|
||||
var backButton = `<a href="${backUrl}" aria-label="Back to list"><i class="mdi mdi-arrow-left" style="font-size: 17px;"></i></a>`;
|
||||
updateNavTitle(page_title, backButton);
|
||||
|
||||
$('#bro_payable_by').val(res.data.bro_payable_by)
|
||||
|
||||
|
||||
@ -8,20 +8,12 @@
|
||||
padding: 4px 4px !important;
|
||||
}
|
||||
|
||||
/*
|
||||
table.dataTable thead th {
|
||||
padding: 4px 4px !important;
|
||||
} */
|
||||
|
||||
|
||||
|
||||
.dataTables_filter {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.column-header {
|
||||
margin-right: 10px;
|
||||
/* Adjust this value as needed */
|
||||
}
|
||||
|
||||
.form-section {
|
||||
@ -78,7 +70,6 @@ table.dataTable thead th {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Ensure the dropdown menu has a solid background */
|
||||
.custom-dropdown-menu::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
@ -90,7 +81,6 @@ table.dataTable thead th {
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
/* Add a subtle backdrop effect */
|
||||
.custom-dropdown-menu::after {
|
||||
content: '';
|
||||
position: fixed;
|
||||
@ -102,6 +92,54 @@ table.dataTable thead th {
|
||||
z-index: -2;
|
||||
pointer-events: none;
|
||||
}
|
||||
.filter-sidebar {
|
||||
height: 100%;
|
||||
width: 0;
|
||||
position: fixed;
|
||||
z-index: 1001;
|
||||
top: 0;
|
||||
right: 0;
|
||||
background-color: #f8f9fa;
|
||||
overflow-x: hidden;
|
||||
transition: 0.5s;
|
||||
box-shadow: -2px 0 5px rgba(0,0,0,0.1);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.filter-sidebar-header {
|
||||
padding: 15px 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.filter-sidebar-header .closebtn {
|
||||
font-size: 28px;
|
||||
text-decoration: none;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.filter-sidebar .closebtn:hover {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.filter-sidebar-content {
|
||||
padding: 20px;
|
||||
flex-grow: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.filter-sidebar-footer {
|
||||
padding: 15px 20px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
border-top: 1px solid #dee2e6;
|
||||
flex-shrink: 0;
|
||||
gap: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
@ -168,142 +206,115 @@ table.dataTable thead th {
|
||||
</style>
|
||||
|
||||
<div class="container-fluid-min">
|
||||
<div class="col-12" id="endorsement_filter">
|
||||
<div class="card-body">
|
||||
<div id="accordion" class="mb-3">
|
||||
<div class="card mb-1">
|
||||
<h4 class="m-1">
|
||||
<span>Filter</span>
|
||||
<a id="toggleIcon" class="text-dark float-right" data-toggle="collapse" href="#collapseOne"
|
||||
aria-expanded="true">
|
||||
<i id="icon" class="mdi mdi-chevron-down mr-1 text-primary" style="font-size: 28px;"></i>
|
||||
</a>
|
||||
</h4>
|
||||
<div id="collapseOne" class="collapse show" aria-labelledby="headingOne" data-parent="#accordion">
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<div class="form-row">
|
||||
<div id="filter-sidebar" class="filter-sidebar">
|
||||
<div class="filter-sidebar-header">
|
||||
<h4 class="m-0">Filter</h4>
|
||||
<a href="javascript:void(0)" class="closebtn" onclick="closeFilterNav()">×</a>
|
||||
</div>
|
||||
<div class="filter-sidebar-content">
|
||||
<div class="form-group">
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-12">
|
||||
<label for="client_branch">Client<span class="text-danger"></span></label>
|
||||
<select class="form-control" id="client_id_for_search" name="client_id">
|
||||
<option value="0">Select Client</option>
|
||||
<?php
|
||||
if (isset($client) && count($client)) {
|
||||
foreach ($client as $key => $value) {
|
||||
echo "<option value='" . $value['id'] . "'>" . $value['client_name'] . "</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-3">
|
||||
<label for="client_branch">Client<span class="text-danger"></span></label>
|
||||
<select class="form-control" id="client_id_for_search" name="client_id">
|
||||
<option value="0">Select Client</option>
|
||||
<?php
|
||||
if (isset($client) && count($client)) {
|
||||
foreach ($client as $key => $value) {
|
||||
echo "<option value='" . $value['id'] . "'>" . $value['client_name'] . "</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group col-md-12">
|
||||
<label for="addon_policy"> Insurer <span id="base_danger" class="text-danger"></span></label>
|
||||
<select class="form-control" id="insurer_id_for_search" name="insurer_id">
|
||||
<option value="0" selected>Select Insurer</option>
|
||||
<?php foreach ($insurer as $value) { ?>
|
||||
<option value="<?= $value['id'] ?>"><?= $value['name'] ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-3">
|
||||
<label for="addon_policy"> Insurer <span id="base_danger" class="text-danger"></span></label>
|
||||
<select class="form-control" id="insurer_id_for_search" name="insurer_id">
|
||||
<option value="0" selected>Select Insurer</option>
|
||||
<?php foreach ($insurer as $value) { ?>
|
||||
<option value="<?= $value['id'] ?>"><?= $value['name'] ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group col-md-12">
|
||||
<label for="email"> Policy Type <span class="text-danger"></span></label>
|
||||
<select class="form-control" id="policy_type_for_search" name="policy_type_id">
|
||||
<option value="0">Select Policy Type</option>
|
||||
<?php
|
||||
if (isset($policy_types) && count($policy_types)) {
|
||||
foreach ($policy_types as $key => $value) {
|
||||
echo "<option value='" . $value['id'] . "'>" . $value['policy_type'] . "</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-3">
|
||||
<label for="email"> Policy Type <span class="text-danger"></span></label>
|
||||
<select class="form-control" id="policy_type_for_search" name="policy_type_id">
|
||||
<option value="0">Select Policy Type</option>
|
||||
<?php
|
||||
if (isset($policy_types) && count($policy_types)) {
|
||||
foreach ($policy_types as $key => $value) {
|
||||
echo "<option value='" . $value['id'] . "'>" . $value['policy_type'] . "</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group col-md-12">
|
||||
<label for="client_branch">Issuer<span class="text-danger">*</span></label>
|
||||
<select class="form-control" id="issuer_for_search" name="issuer" required>
|
||||
<option value="0">Select Issure</option>
|
||||
<?php
|
||||
if (isset($issuer) && count($issuer)) {
|
||||
foreach ($issuer as $key => $value) {
|
||||
echo "<option value=" . $key . ">" . $value . "</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-3">
|
||||
<label for="client_branch">Issuer<span class="text-danger">*</span></label>
|
||||
<select class="form-control" id="issuer_for_search" name="issuer" required>
|
||||
<option value="0">Select Issure</option>
|
||||
<?php
|
||||
if (isset($issuer) && count($issuer)) {
|
||||
foreach ($issuer as $key => $value) {
|
||||
echo "<option value=" . $key . ">" . $value . "</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-12">
|
||||
<label for="email"> Status <span class="text-danger"></span></label>
|
||||
<select class="form-control" id="policy_status_for_search" name="policy_type_id">
|
||||
<option value="0">Select Status</option>
|
||||
<?php
|
||||
if (isset($policy_status) && count($policy_status)) {
|
||||
foreach ($policy_status as $key => $value) {
|
||||
echo "<option value='" . $key . "'>" . $value . "</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-12">
|
||||
<label>Date Type<span class="text-danger"></span></label>
|
||||
<select class="form-control" id="date_type" name="date_type" onchange="hideDateField()">
|
||||
<option value="0">Select Date Type</option>
|
||||
<?php
|
||||
if (isset($date_type) && count($date_type)) {
|
||||
foreach ($date_type as $key => $value) {
|
||||
echo "<option value='" . $key . "'>" . $value . "</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-3">
|
||||
<label for="email"> Status <span class="text-danger"></span></label>
|
||||
<select class="form-control" id="policy_status_for_search" name="policy_type_id">
|
||||
<option value="0">Select Status</option>
|
||||
<?php
|
||||
if (isset($policy_status) && count($policy_status)) {
|
||||
foreach ($policy_status as $key => $value) {
|
||||
echo "<option value='" . $key . "'>" . $value . "</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-3">
|
||||
<label>Date Type<span class="text-danger"></span></label>
|
||||
<select class="form-control" id="date_type" name="date_type" onchange="hideDateField()">
|
||||
<option value="0">Select Date Type</option>
|
||||
<?php
|
||||
if (isset($date_type) && count($date_type)) {
|
||||
foreach ($date_type as $key => $value) {
|
||||
echo "<option value='" . $key . "'>" . $value . "</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-3" style="display: none;" id="date_div">
|
||||
<label>Date<span class="text-danger"></span></label>
|
||||
<div id="reportrange" class="form-control" style="background: #fff; cursor: pointer; padding: 5px 10px; border: 1px solid #ccc; width: 100%">
|
||||
<i class="mdi mdi-calendar-blank"></i>
|
||||
<span></span> <i class="mdi mdi-menu-down"></i>
|
||||
</div>
|
||||
<input type="hidden" id="startDate">
|
||||
<input type="hidden" id="endDate">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="form-group text-right m-b-0">
|
||||
<a href="<?= base_url("/policy_tranction/endorsement/list"); ?>" class="btn btn-secondary" id="clear-filters">Clear</a>
|
||||
<a href="<?= base_url("/policy_tranction/endorsement/list"); ?>" class="btn btn-primary" id="get-emp-list" onclick="fetchEmpolyeeList(event);">Submit</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="form-group col-md-12" style="display: none;" id="date_div">
|
||||
<label>Date<span class="text-danger"></span></label>
|
||||
<div id="reportrange" class="form-control" style="background: #fff; cursor: pointer; padding: 5px 10px; border: 1px solid #ccc; width: 100%">
|
||||
<i class="mdi mdi-calendar-blank"></i>
|
||||
<span></span> <i class="mdi mdi-menu-down"></i>
|
||||
</div>
|
||||
<input type="hidden" id="startDate">
|
||||
<input type="hidden" id="endDate">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="filter-sidebar-footer">
|
||||
<a href="<?= base_url("/policy_tranction/endorsement/list"); ?>" class="btn btn-secondary" id="clear-filters">Clear</a>
|
||||
<a href="<?= base_url("/policy_tranction/endorsement/list"); ?>" class="btn btn-primary" id="get-emp-list" onclick="fetchEmpolyeeList(event);closeFilterNav();">Submit</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12" id="endorsement_list">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<!-- <div class="row" style="margin-bottom:1rem;">
|
||||
<div class="col-6" style="align-self: center;">
|
||||
<h4 style="position: relative;">Endorsement List</h4>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<button type="button" id="btnAdd" class="btn btn-primary waves-effect waves-light" onclick="hide_list_show_add()">Add</button>
|
||||
</div>
|
||||
</div> -->
|
||||
<table data-custom-table-css="table" class="table table-striped mb-0 nowrap" cellspacing="0" id="tickets-table">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
@ -367,6 +378,16 @@ table.dataTable thead th {
|
||||
<?php include('pt_endorsement_form_file_upload_tab.php'); ?>
|
||||
|
||||
<script>
|
||||
/* Modified by Gemini Code Assist */
|
||||
function openEndorsementFilterNav() {
|
||||
document.getElementById("filter-sidebar").style.width = "350px";
|
||||
}
|
||||
|
||||
/* Modified by Gemini Code Assist */
|
||||
function closeFilterNav() {
|
||||
document.getElementById("filter-sidebar").style.width = "0";
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
const table = document.getElementById("tickets-table");
|
||||
|
||||
@ -497,15 +518,18 @@ table.dataTable thead th {
|
||||
if (ticketsTable.length) {
|
||||
ticketsTable.DataTable({
|
||||
scrollX: true,
|
||||
// dom: "<'row'<'col-sm-1'f><'col-sm-11 text-right'B>>" + // Filter left, buttons right
|
||||
// dom: "<'row'<'col-12 d-flex justify-content-between'<'datatable-search dataTables_filter'f><'datatable-buttons dt-buttons'B>>>" +
|
||||
// "<'row'<'col-sm-12'tr>>" +
|
||||
// "<'row'<'col-sm-5'i><'col-sm-7'p>>", // Pagination at the bottom, without page length selector
|
||||
dom: "<'row'<'col-12 d-flex justify-content-between'<'datatable-search dataTables_filter'f><'datatable-buttons dt-buttons'B>>>" +
|
||||
dom: "<'row'<'col-sm-7'f><'col-sm-5 text-right'B>>" + // Filter left, buttons right
|
||||
"<'row'<'col-sm-12'tr>>" +
|
||||
"<'row align-items-center'<'col-5 text-start'i><'col-7 d-flex justify-content-end align-items-center'<'me-2'l>p>>",
|
||||
lengthMenu: [[10, 20, 50, 100], [10, 20, 50, 100]],
|
||||
buttons: [
|
||||
buttons: [
|
||||
{
|
||||
text: '<i class="mdi mdi-filter"></i><span class="btn-custom"> Filter </span>',
|
||||
className: 'btn app-btn-primary mr-2',
|
||||
action: function (e, dt, node, config) {
|
||||
openEndorsementFilterNav();
|
||||
}
|
||||
},
|
||||
{
|
||||
text: '<i class="mdi mdi-plus" ></i><span class=" btn-custom"> Add </span>',
|
||||
className: 'btn app-btn-primary mr-2',
|
||||
@ -614,7 +638,37 @@ table.dataTable thead th {
|
||||
|
||||
});
|
||||
|
||||
var pageName = "<?= esc($page_name ?? '') ?>";
|
||||
var userName = "<?= esc(get_session_userdata()->first_name ?? 'NOT SET') ?>";
|
||||
|
||||
function updateNavTitle(subTitle, backButton) {
|
||||
var titleHtml = "";
|
||||
var $titleEl = $(".top-navbar-title");
|
||||
var $navDiv = $(".top-navbar-div");
|
||||
|
||||
if (pageName && backButton && subTitle) {
|
||||
titleHtml = `<h3 style="margin-right:5px !important;">${pageName}</h3>`;
|
||||
titleHtml += `<h6>${backButton} ${subTitle}</h6>`;
|
||||
$titleEl.css({ display: "block", bottom: "12px" });
|
||||
$navDiv.css({ marginTop: "0px" });
|
||||
} else if (pageName && subTitle) {
|
||||
titleHtml = `<h3 style="margin-right:5px !important;">${pageName}</h3>`;
|
||||
titleHtml += `<h6>${subTitle}</h6>`;
|
||||
$titleEl.css({ display: "block", bottom: "12px" });
|
||||
$navDiv.css({ marginTop: "0px" });
|
||||
} else {
|
||||
titleHtml = `<h3 style="margin-right:5px !important;">${pageName}</h3>`;
|
||||
$titleEl.css({ display: "flex", bottom: "0px" });
|
||||
$navDiv.css({ marginTop: "20px" });
|
||||
}
|
||||
|
||||
$titleEl.html(titleHtml);
|
||||
}
|
||||
|
||||
function hide_list_show_add() {
|
||||
var backUrl = '<?= base_url("policy_tranction/endorsement/list"); ?>';
|
||||
var backButton = `<a href="${backUrl}" aria-label="Back to list"><i class="mdi mdi-arrow-left" style="font-size: 17px;"></i></a>`;
|
||||
updateNavTitle('Add Endorsement', backButton);
|
||||
removeAllColumnsExceptFirst('insurerTable');
|
||||
$('.hideter').hide()
|
||||
$('.hidetp').hide()
|
||||
@ -630,6 +684,7 @@ table.dataTable thead th {
|
||||
}
|
||||
|
||||
function show_list_hide_add() {
|
||||
updateNavTitle(false, false);
|
||||
$('#pt_onboarding').hide()
|
||||
$('#endorsement_list').show()
|
||||
$('#endorsement_filter').show()
|
||||
@ -909,19 +964,6 @@ table.dataTable thead th {
|
||||
|
||||
hideDateField(date_type)
|
||||
|
||||
// console.log('startDate', startDate)
|
||||
// console.log('endDate', endDate)
|
||||
// console.log('client_id', client_id)
|
||||
// console.log('insurer_id', insurer_id)
|
||||
// console.log('policy_type_id', policy_type_id)
|
||||
// console.log('date_type', date_type)
|
||||
// console.log('issuer', issuer)
|
||||
// console.log('status', status)
|
||||
|
||||
// Log the values or use them as needed
|
||||
// console.log('Start Date:', startDate);
|
||||
// console.log('End Date:', endDate);
|
||||
|
||||
if (startDate != 0 && endDate != 0) {
|
||||
setTimeout(function() {
|
||||
$('#start_date').val(startDate)
|
||||
@ -951,9 +993,6 @@ table.dataTable thead th {
|
||||
var start = moment(startDateParam, 'DD/MM/YYYY');
|
||||
var end = moment(endDateParam, 'DD/MM/YYYY');
|
||||
|
||||
// var start = moment().subtract(29, 'days');
|
||||
// var end = moment();
|
||||
|
||||
function cb(start, end) {
|
||||
$('#reportrange span').html(start.format('D/MM/YYYY') + ' - ' + end.format('D/MM/YYYY'));
|
||||
$('#startDate').val(start.format('DD/MM/YYYY'));
|
||||
@ -1107,12 +1146,6 @@ table.dataTable thead th {
|
||||
row.append($('<td>').text(item.doc_name));
|
||||
row.append($('<td>').text(item.file_name));
|
||||
|
||||
// var url = "<?= base_url('/downloadGdriveFile'); ?>" +
|
||||
// "?client_id=" + item.client_id +
|
||||
// "&file_type=policy" +
|
||||
// "&file_name=" + item.file_name +
|
||||
// "&client_policy_id=" + item.client_policy_id;
|
||||
|
||||
var url = '<?= base_url('download-kyc-docs/') ?>' + item.file_name;
|
||||
|
||||
|
||||
|
||||
@ -293,7 +293,10 @@
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
var pageSubTitle = undefined;
|
||||
var pageBackButton = undefined;
|
||||
</script>
|
||||
<div class="tab-pane fade active show" id="form">
|
||||
<input type="hidden" id="entity_type_id">
|
||||
<div class="row" id="inception_form">
|
||||
@ -302,16 +305,16 @@
|
||||
<div class="card-body">
|
||||
<div class="row" style="margin-bottom:1rem;">
|
||||
|
||||
<div class="col-6" style="align-self: center;">
|
||||
<!-- <div class="col-6" style="align-self: center;">
|
||||
<h4 id="page_title" style="position: relative;">Add Policy</h4>
|
||||
</div>
|
||||
<div class="col-3" style="text-align: right; position: relative; left: 211px;">
|
||||
</div> -->
|
||||
<!-- <div class="col-3" style="text-align: right; position: relative; left: 211px;"> -->
|
||||
<!-- <button class="btn btn-primary waves-effect waves-light" onclick="fileupload(this)">file upload</button> -->
|
||||
</div>
|
||||
<div class="col-1" style="text-align: right; position: relative; left: 193px;">
|
||||
<!-- </div> -->
|
||||
<!-- <div class="col-1" style="text-align: right; position: relative; left: 193px;">
|
||||
<a href="<?= base_url('policy_tranction/inception/list') ?>" type="button" id="btnAdd" class="btn btn-primary waves-effect waves-light"
|
||||
>Back</a>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
<form role="form" class="parsley-examples" method="post" id="inception_form_id" enctype="multipart/form-data">
|
||||
|
||||
@ -2255,8 +2258,9 @@
|
||||
var page_title = 'Edit Policy' + (res.data.client_short_name || res.data.policy_type || res.data.policy_no ?
|
||||
' - ' + [res.data.client_short_name, res.data.policy_type, res.data.policy_no]
|
||||
.filter(Boolean).join('-') : '');
|
||||
|
||||
$('#page_title').text(page_title);
|
||||
var backUrl = '<?= base_url("policy_tranction/inception/list"); ?>';
|
||||
var backButton = `<a href="${backUrl}" aria-label="Back to list"><i class="mdi mdi-arrow-left" style="font-size: 17px;"></i></a>`;
|
||||
updateNavTitle(page_title, backButton);
|
||||
$('#client_id_kyc').val(res.data.client_id);
|
||||
$('#policy_tranction_primarykey_for_file_upload').val(res.data.id);
|
||||
$('#client_id_for_vehicle_file_upload').val(res.data.client_id);
|
||||
|
||||
@ -71,6 +71,54 @@ table.dataTable tbody td {
|
||||
color: #16181b !important;
|
||||
cursor: pointer !important;
|
||||
}
|
||||
.filter-sidebar {
|
||||
height: 100%;
|
||||
width: 0;
|
||||
position: fixed;
|
||||
z-index: 1001;
|
||||
top: 0;
|
||||
right: 0;
|
||||
background-color: #f8f9fa;
|
||||
overflow-x: hidden;
|
||||
transition: 0.5s;
|
||||
box-shadow: -2px 0 5px rgba(0,0,0,0.1);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.filter-sidebar-header {
|
||||
padding: 15px 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.filter-sidebar-header .closebtn {
|
||||
font-size: 28px;
|
||||
text-decoration: none;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.filter-sidebar .closebtn:hover {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.filter-sidebar-content {
|
||||
padding: 20px;
|
||||
flex-grow: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.filter-sidebar-footer {
|
||||
padding: 15px 20px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
border-top: 1px solid #dee2e6;
|
||||
flex-shrink: 0;
|
||||
gap: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
@ -139,156 +187,117 @@ table.dataTable tbody td {
|
||||
margin-left:30px !important;
|
||||
}
|
||||
|
||||
#inception_list{
|
||||
margin-left:20px !important;
|
||||
}
|
||||
.dataTables_length label {height: 21px !important;}
|
||||
|
||||
</style>
|
||||
|
||||
<div class="row" id="inception_filter">
|
||||
<div class="col-12">
|
||||
<div id="accordion" class="mb-3">
|
||||
<div class="card mb-1">
|
||||
<h4 class="m-1">
|
||||
<span>Filter</span>
|
||||
<a id="toggleIcon" class="text-dark float-right" data-toggle="collapse" href="#collapseOne"
|
||||
aria-expanded="true">
|
||||
<i id="icon" class="mdi mdi-chevron-down mr-1 text-primary" style="font-size: 28px;"></i>
|
||||
</a>
|
||||
</h4>
|
||||
<div id="collapseOne" class="collapse show" aria-labelledby="headingOne" data-parent="#accordion">
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<div class="form-row">
|
||||
<div id="filter-sidebar" class="filter-sidebar">
|
||||
<div class="filter-sidebar-header">
|
||||
<h4 class="m-0">Filter</h4>
|
||||
<a href="javascript:void(0)" class="closebtn" onclick="closeFilterNav()">×</a>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-3">
|
||||
<label for="client_branch">Client<span class="text-danger"></span></label>
|
||||
<select class="form-control" id="client_id_for_search" name="client_id" >
|
||||
<option value="0">Select Client</option>
|
||||
<?php
|
||||
if (isset($client) && count($client)) {
|
||||
foreach ($client as $key => $value) {
|
||||
echo "<option value='" . $value['id'] . "'>" . $value['client_name'] . "</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-sidebar-content">
|
||||
<div class="form-group">
|
||||
<div class="form-row">
|
||||
|
||||
<div class="form-group col-md-3">
|
||||
<label for="addon_policy"> Insurer <span id="base_danger" class="text-danger"></span></label>
|
||||
<select class="form-control" id="insurer_id_for_search" name="insurer_id" >
|
||||
<option value="0" selected>Select Insurer</option>
|
||||
<?php foreach ($insurer as $value) { ?>
|
||||
<option value="<?= $value['id']?>"><?= $value['name']?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group col-md-12">
|
||||
<label for="client_branch">Client<span class="text-danger"></span></label>
|
||||
<select class="form-control" id="client_id_for_search" name="client_id" >
|
||||
<option value="0">Select Client</option>
|
||||
<?php
|
||||
if (isset($client) && count($client)) {
|
||||
foreach ($client as $key => $value) {
|
||||
echo "<option value='" . $value['id'] . "'>" . $value['client_name'] . "</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-3">
|
||||
<label for="email"> Policy Type <span class="text-danger"></span></label>
|
||||
<select class="form-control" id="policy_type_for_search" name="policy_type_id" >
|
||||
<option value="0">Select Policy Type</option>
|
||||
<?php
|
||||
if (isset($policy_types) && count($policy_types)) {
|
||||
foreach ($policy_types as $key => $value) {
|
||||
echo "<option value='" . $value['id'] . "'>" . $value['policy_type'] . "</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group col-md-12">
|
||||
<label for="addon_policy"> Insurer <span id="base_danger" class="text-danger"></span></label>
|
||||
<select class="form-control" id="insurer_id_for_search" name="insurer_id" >
|
||||
<option value="0" selected>Select Insurer</option>
|
||||
<?php foreach ($insurer as $value) { ?>
|
||||
<option value="<?= $value['id']?>"><?= $value['name']?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- <div class="form-group col-md-3" style="display: none;">
|
||||
<label for="client_branch">Issuer<span class="text-danger">*</span></label>
|
||||
<select class="form-control" id="issuer_for_search" name="issuer">
|
||||
<option value="0">Select Issuer</option>
|
||||
<?php
|
||||
if (isset($issuer) && count($issuer)) {
|
||||
foreach ($issuer as $key => $value) {
|
||||
echo "<option value=" . $key . ">" . $value . "</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div> -->
|
||||
<div class="form-group col-md-12">
|
||||
<label for="email"> Policy Type <span class="text-danger"></span></label>
|
||||
<select class="form-control" id="policy_type_for_search" name="policy_type_id" >
|
||||
<option value="0">Select Policy Type</option>
|
||||
<?php
|
||||
if (isset($policy_types) && count($policy_types)) {
|
||||
foreach ($policy_types as $key => $value) {
|
||||
echo "<option value='" . $value['id'] . "'>" . $value['policy_type'] . "</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-3">
|
||||
<label for="email"> Status <span class="text-danger"></span></label>
|
||||
<select class="form-control" id="policy_status_for_search" name="policy_type_id" >
|
||||
<option value="0">Select Status</option>
|
||||
<?php
|
||||
if (isset($policy_status) && count($policy_status)) {
|
||||
foreach ($policy_status as $key => $value) {
|
||||
echo "<option value='" . $key . "'>" . $value . "</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group col-md-12">
|
||||
<label for="email"> Status <span class="text-danger"></span></label>
|
||||
<select class="form-control" id="policy_status_for_search" name="policy_type_id" >
|
||||
<option value="0">Select Status</option>
|
||||
<?php
|
||||
if (isset($policy_status) && count($policy_status)) {
|
||||
foreach ($policy_status as $key => $value) {
|
||||
echo "<option value='" . $key . "'>" . $value . "</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="form-group col-md-12">
|
||||
<label>Date Type<span class="text-danger"></span></label>
|
||||
<select class="form-control" id="date_type" name="date_type" onchange="hideDateField()">
|
||||
<option value="0">Select Date Type</option>
|
||||
<?php
|
||||
if (isset($date_type) && count($date_type)) {
|
||||
foreach ($date_type as $key => $value) {
|
||||
echo "<option value='" . $key . "'>" . $value . "</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
|
||||
|
||||
|
||||
<div class="form-group col-md-3">
|
||||
<label>Date Type<span class="text-danger"></span></label>
|
||||
<select class="form-control" id="date_type" name="date_type" onchange="hideDateField()">
|
||||
<option value="0">Select Date Type</option>
|
||||
<?php
|
||||
if (isset($date_type) && count($date_type)) {
|
||||
foreach ($date_type as $key => $value) {
|
||||
echo "<option value='" . $key . "'>" . $value . "</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-3" style="display: none;" id="date_div">
|
||||
<label>Date<span class="text-danger"></span></label>
|
||||
<div id="reportrange" class="form-control" style="background: #fff; cursor: pointer; padding: 5px 10px; border: 1px solid #ccc; width: 100%">
|
||||
<i class="mdi mdi-calendar-blank"></i>
|
||||
<span></span> <i class="mdi mdi-menu-down"></i>
|
||||
</div>
|
||||
<input type="hidden" id="startDate">
|
||||
<input type="hidden" id="endDate">
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="form-row">
|
||||
|
||||
<div class="form-group col-md-12 text-right m-b-0">
|
||||
<a href="<?= base_url("/policy_tranction/inception/list"); ?>" class="btn btn-secondary" id="clear-filters">Clear</a>
|
||||
<a href="<?= base_url("/policy_tranction/inception/list"); ?>" class="btn btn-primary" id="get-emp-list" onclick="fetchEmpolyeeList(event);">Submit</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-12" style="display: none;" id="date_div">
|
||||
<label>Date<span class="text-danger"></span></label>
|
||||
<div id="reportrange" class="form-control" style="background: #fff; cursor: pointer; padding: 5px 10px; border: 1px solid #ccc; width: 100%">
|
||||
<i class="mdi mdi-calendar-blank"></i>
|
||||
<span></span> <i class="mdi mdi-menu-down"></i>
|
||||
</div>
|
||||
<input type="hidden" id="startDate">
|
||||
<input type="hidden" id="endDate">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="filter-sidebar-footer">
|
||||
<a href="<?= base_url("/policy_tranction/inception/list"); ?>" class="btn btn-secondary" id="clear-filters">Clear</a>
|
||||
<a href="<?= base_url("/policy_tranction/inception/list"); ?>" class="btn btn-primary" id="get-emp-list" onclick="fetchEmpolyeeList(event); closeFilterNav();">Submit</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" id="inception_list">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="row" style="margin-bottom:1rem;">
|
||||
<!-- <div class="row" style="margin-bottom:1rem;">
|
||||
<div class="col-6" style="align-self: center;">
|
||||
<h4 style="position: relative;">Policy List</h4>
|
||||
</div>
|
||||
|
||||
<div class="col-3" id="status_change" style="text-align: right; position: relative;top: 56px; left: 291px;">
|
||||
<!-- <button type="button" id="btnAdd" class="btn btn-primary waves-effect waves-light" onclick="hide_list_show_add();">Add</button> -->
|
||||
<button type="button" id="btnAdd" class="btn btn-primary waves-effect waves-light" onclick="hide_list_show_add();">Add</button>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
<div>
|
||||
<table data-custom-table-css="table" class="table table-striped mb-0 nowrap" cellspacing="0" id="tickets-table">
|
||||
<thead class="bg-light">
|
||||
@ -386,7 +395,37 @@ table.dataTable tbody td {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="policyNoAddModal" tabindex="-1" role="dialog" aria-labelledby="policyNoAddModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="policyNoAddModalLabel">Add policy</h5>
|
||||
<button type="button" id="close_btn" class="close" data-dismiss="modal" aria-hidden="true">x</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<label for="modal_policy_no">Policy number</label>
|
||||
<input type="text" class="form-control" id="modal_policy_no" name="modal_policy_no" autocomplete="off" placeholder="Enter policy number">
|
||||
<small class="text-danger d-block mt-1" id="policy_no_add_modal_feedback"></small>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" onclick="hidePolicyNoAddModal()">Cancel</button>
|
||||
<button type="button" class="btn btn-outline-primary" onclick="runInceptionAddFlowFromList()">Proceed to Add</button>
|
||||
<button type="button" class="btn btn-primary" id="policyNoModalCheck">Check</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
/* Modified by Gemini Code Assist */
|
||||
function openPolicyFilterNav() {
|
||||
document.getElementById("filter-sidebar").style.width = "350px";
|
||||
}
|
||||
|
||||
/* Modified by Gemini Code Assist */
|
||||
function closeFilterNav() {
|
||||
document.getElementById("filter-sidebar").style.width = "0";
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const table = document.getElementById("tickets-table");
|
||||
@ -529,6 +568,7 @@ $(document).ready(function(){
|
||||
getClientAndBranchAndPolicy()
|
||||
addHTMLInput();
|
||||
addHTMLInputForVehicleFileUpload()
|
||||
getAddPage();
|
||||
|
||||
<?php if (session()->has('create_failed')) : ?>
|
||||
toastr.error('<?= session()->getFlashdata('create_failed') ?>', 'Failed');
|
||||
@ -559,9 +599,87 @@ $(document).ready(function(){
|
||||
$('#ppteam').select2();
|
||||
})
|
||||
|
||||
function openPolicyNoAddModal() {
|
||||
|
||||
var myModal = new bootstrap.Modal(document.getElementById('policyNoAddModal'));
|
||||
myModal.show();
|
||||
}
|
||||
|
||||
function runInceptionAddFlowFromList() {
|
||||
hide_list_show_add();
|
||||
addInsurerColumn();
|
||||
addHTMLInput(null, 'policy_docs_div');
|
||||
$('#policy_docs').show();
|
||||
$('.close').click();
|
||||
}
|
||||
|
||||
function validatePolicyNoForAdd() {
|
||||
|
||||
var pn = ($('#modal_policy_no').val() || '').trim();
|
||||
$('#policy_no_add_modal_feedback').text('');
|
||||
if (!pn) {
|
||||
$('#policy_no_add_modal_feedback').text('Please enter a policy number.');
|
||||
return;
|
||||
}
|
||||
|
||||
$('.loader').fadeIn();
|
||||
$('.loader-mask').fadeIn();
|
||||
|
||||
$.ajax({
|
||||
url: '<?= base_url('/util/get_client_policy_data_using_policy_no') ?>',
|
||||
type: 'GET',
|
||||
data: { policy_no: pn },
|
||||
dataType: 'json',
|
||||
success: function (res) {
|
||||
$('.loader').fadeOut();
|
||||
$('.loader-mask').delay(350).fadeOut('slow');
|
||||
|
||||
var msg = (res && res.message) ? String(res.message) : 'Unable to validate policy number.';
|
||||
|
||||
if ("pt_id" in res) {
|
||||
console.log("PT ID exists:", res.pt_id);
|
||||
getPolicyTransactionDataForEdit(res.pt_id);
|
||||
$('.close').click();
|
||||
} else if("is_dublicate" in res){
|
||||
runInceptionAddFlowFromList();
|
||||
toastr.success(msg,'SUCCESS');
|
||||
} else {
|
||||
toastr.warning(msg,'WARNING');
|
||||
}
|
||||
|
||||
},
|
||||
error: function () {
|
||||
$('.loader').fadeOut();
|
||||
$('.loader-mask').delay(350).fadeOut('slow');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getAddPage(){
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const view = params.get('view') ?? 0;
|
||||
|
||||
if(view == 1){
|
||||
runInceptionAddFlowFromList();
|
||||
}
|
||||
}
|
||||
|
||||
// Datatable document ready
|
||||
$(document).ready(function() {
|
||||
|
||||
$('#policyNoModalCheck').on('click', function () {
|
||||
validatePolicyNoForAdd();
|
||||
});
|
||||
$('#policyNoModalProceedAdd').on('click', function () {
|
||||
proceedToAddFromPolicyNoModal();
|
||||
});
|
||||
$(document).on('keydown', '#modal_policy_no', function (e) {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
validatePolicyNoForAdd();
|
||||
}
|
||||
});
|
||||
|
||||
var ticketsTable = $('#tickets-table');
|
||||
|
||||
if (ticketsTable.length) {
|
||||
@ -571,19 +689,23 @@ $(document).ready(function() {
|
||||
// dom: "<'row'<'col-12 d-flex justify-content-between'<'datatable-search dataTables_filter'f><'datatable-buttons dt-buttons'B>>>" +
|
||||
// "<'row'<'col-sm-12'tr>>" +
|
||||
// "<'row'<'col-sm-5'i><'col-sm-7'p>>", // Pagination at the bottom, without page length selector
|
||||
dom: "<'row'<'col-12 d-flex justify-content-between'<'datatable-search dataTables_filter'f><'datatable-buttons dt-buttons'B>>>" +
|
||||
dom: "<'row'<'col-sm-7'f><'col-sm-5 text-right'B>>" + // Filter left, buttons right
|
||||
"<'row'<'col-sm-12'tr>>" +
|
||||
"<'row align-items-center'<'col-5 text-start'i><'col-7 d-flex justify-content-end align-items-center'<'me-2'l>p>>",
|
||||
lengthMenu: [[10, 20, 50, 100], [10, 20, 50, 100]],
|
||||
buttons: [
|
||||
{
|
||||
text: '<i class="mdi mdi-filter" ></i><span class=" btn-custom"> Filter </span>',
|
||||
className: 'btn app-btn-primary mr-2',
|
||||
action: function(e, dt, node, config) {
|
||||
openPolicyFilterNav();
|
||||
}
|
||||
},
|
||||
{
|
||||
text: '<i class="mdi mdi-plus" ></i><span class=" btn-custom"> Add </span>',
|
||||
className: 'btn app-btn-primary mr-2',
|
||||
action: function (e, dt, node, config) {
|
||||
hide_list_show_add();
|
||||
addInsurerColumn();
|
||||
addHTMLInput(null, 'policy_docs_div');
|
||||
$('#policy_docs').show()
|
||||
openPolicyNoAddModal();
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -695,10 +817,39 @@ $(document).ready(function(){
|
||||
})
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------
|
||||
var pageName = "<?= esc($page_name ?? '') ?>";
|
||||
var userName = "<?= esc(get_session_userdata()->first_name ?? 'NOT SET') ?>";
|
||||
|
||||
function updateNavTitle(subTitle, backButton) {
|
||||
var titleHtml = "";
|
||||
var $titleEl = $(".top-navbar-title");
|
||||
var $navDiv = $(".top-navbar-div");
|
||||
|
||||
if (pageName && backButton && subTitle) {
|
||||
titleHtml = `<h3 style="margin-right:5px !important;">${pageName}</h3>`;
|
||||
titleHtml += `<h6>${backButton} ${subTitle}</h6>`;
|
||||
$titleEl.css({ display: "block", bottom: "12px" });
|
||||
$navDiv.css({ marginTop: "0px" });
|
||||
} else if (pageName && subTitle) {
|
||||
titleHtml = `<h3 style="margin-right:5px !important;">${pageName}</h3>`;
|
||||
titleHtml += `<h6>${subTitle}</h6>`;
|
||||
$titleEl.css({ display: "block", bottom: "12px" });
|
||||
$navDiv.css({ marginTop: "0px" });
|
||||
} else {
|
||||
titleHtml = `<h3 style="margin-right:5px !important;">${pageName}</h3>`;
|
||||
$titleEl.css({ display: "flex", bottom: "0px" });
|
||||
$navDiv.css({ marginTop: "20px" });
|
||||
}
|
||||
|
||||
$titleEl.html(titleHtml);
|
||||
}
|
||||
|
||||
function hide_list_show_add()
|
||||
{
|
||||
$('#page_title').text('Add Policy')
|
||||
// $('#page_title').text('Add Policy')
|
||||
var backUrl = '<?= base_url("policy_tranction/inception/list"); ?>';
|
||||
var backButton = `<a href="${backUrl}" aria-label="Back to list"><i class="mdi mdi-arrow-left" style="font-size: 17px;"></i></a>`;
|
||||
updateNavTitle('Add Policy', backButton);
|
||||
$('#inception_form_id')[0].reset();
|
||||
$('#client_id').val('').change().prop('disabled', false);
|
||||
$('#tpa').val('').change().prop('disabled', false);
|
||||
@ -729,6 +880,9 @@ function hide_list_show_add()
|
||||
|
||||
function show_list_hide_add()
|
||||
{
|
||||
pageSubTitle = undefined;
|
||||
pageBackButton = undefined;
|
||||
updateNavTitle(false, false);
|
||||
$('#pt_onboarding').hide()
|
||||
$('#inception_list').show()
|
||||
$('#inception_filter').show();
|
||||
@ -1523,6 +1677,7 @@ $('#cd_ac_no_data').change(function(){
|
||||
|
||||
// if(policy_type_id == 3 || policy_type_id == 4 || policy_type_id == 5) {
|
||||
// $('#base_policy_id').show();
|
||||
.
|
||||
// }else{
|
||||
// $('#base_policy_id').hide();
|
||||
// }
|
||||
|
||||
@ -72,7 +72,7 @@
|
||||
<div class="row" id="policy_list">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body maincard">
|
||||
<div class="card-body">
|
||||
<!-- <div class="row" style="margin-bottom:1rem;">
|
||||
<div class="col-6" style="align-self: center;">
|
||||
<h4 style="position: relative;">Policy Type List</h4>
|
||||
|
||||
@ -28,11 +28,14 @@
|
||||
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
var pageSubTitle = 'Policy Type';
|
||||
var pageBackButton = '<a href="#" onclick="history.back(); return false;" data-toggle="tooltip" data-placement="top" title="Back"><i class="mdi mdi-arrow-left" style="font-size: 17px;"></i></a>';
|
||||
</script>
|
||||
<div id="policy_add">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body maincard">
|
||||
<div class="card-body">
|
||||
<!-- <div class="row" style="padding-bottom: 10px;">
|
||||
<div class="col-6" style="align-self: center;">
|
||||
<h4 style="position: relative;">Policy Type</h4>
|
||||
@ -41,14 +44,14 @@
|
||||
<a href="<?= base_url("master/policy/list"); ?>"><i class="mdi mdi-arrow-left" style="font-size: 17px;"></i> </a>
|
||||
</div>
|
||||
</div> -->
|
||||
<div class="row" style="padding-bottom: 10px;">
|
||||
<!-- <div class="row" style="padding-bottom: 10px;">
|
||||
<h4 class="d-flex align-items-center" style="gap: 8px;">
|
||||
<span onclick="history.back()" style="cursor: pointer;">
|
||||
<i class="mdi mdi-chevron-left" style="font-size: 43px;"></i>
|
||||
</span>
|
||||
<span class="title-text ">Policy Type</span>
|
||||
</h4>
|
||||
</div>
|
||||
</div> -->
|
||||
<div class="tab-wrapper position-relative">
|
||||
<ul class="nav nav-pills navtab-bg" id="myTab">
|
||||
<li class="nav-item d-flex justify-content-center align-items-center">
|
||||
|
||||
@ -35,7 +35,7 @@
|
||||
<div class="row" id="List-page">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body maincard">
|
||||
<div class="card-body">
|
||||
<table data-custom-table-css="table" class="table table-sm m-0 table-centered dt-responsive nowrap w-100" cellspacing="a" id="user-table">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
@ -244,8 +244,8 @@
|
||||
lengthMenu: [[10, 20, 50, 100], [10, 20, 50, 100]],
|
||||
buttons: [
|
||||
{
|
||||
text: 'Add',
|
||||
className: 'btn app-btn-primary mr-2',
|
||||
text: '<i class="mdi mdi-plus"></i><span class="btn-custom"> Add </span>',
|
||||
action: function(e, dt, node, config) {
|
||||
resetValues();openModal();
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<div class="row" id="pt_onboarding" style="position: relative; bottom: 25px; display:none;">
|
||||
<div class="row" id="pt_onboarding" style="bottom: 25px; display:none;">
|
||||
<div class="col-xl-12">
|
||||
<div class="card-body">
|
||||
<div class="tab-wrapper position-relative">
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<div class="row" id="pt_onboarding" style="position: relative; bottom: 25px; display:none;">
|
||||
<div class="row" id="pt_onboarding"> <!-- style="position: relative; bottom: 25px; display:none;" -->
|
||||
<div class="col-xl-12">
|
||||
<div class="card-body">
|
||||
<div class="tab-wrapper position-relative">
|
||||
|
||||
@ -394,6 +394,13 @@ $(document).ready(function() {
|
||||
"<'row align-items-center'<'col-5 text-start'i><'col-7 d-flex justify-content-end align-items-center'<'me-2'l>p>>",
|
||||
lengthMenu: [[10, 20, 50, 100], [10, 20, 50, 100]],
|
||||
buttons: [
|
||||
{
|
||||
text: '<i class="mdi mdi-filter" ></i><span class=" btn-custom"> Filter </span>',
|
||||
className: 'btn app-btn-primary mr-2',
|
||||
action: function(e, dt, node, config) {
|
||||
openBDSFilterNav();
|
||||
}
|
||||
},
|
||||
{
|
||||
extend: 'collection',
|
||||
text: '<span class=" btn-custom"> Export </span><i class="mdi mdi-menu-down"></i>',
|
||||
|
||||
@ -1,150 +1,175 @@
|
||||
<style>
|
||||
.filter-sidebar {
|
||||
height: 100%;
|
||||
width: 0;
|
||||
position: fixed;
|
||||
z-index: 1001;
|
||||
top: 0;
|
||||
right: 0;
|
||||
background-color: #f8f9fa;
|
||||
overflow-x: hidden;
|
||||
transition: 0.5s;
|
||||
box-shadow: -2px 0 5px rgba(0,0,0,0.1);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.filter-sidebar-header {
|
||||
padding: 15px 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.filter-sidebar-header .closebtn {
|
||||
font-size: 28px;
|
||||
text-decoration: none;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.filter-sidebar .closebtn:hover {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.filter-sidebar-content {
|
||||
padding: 20px;
|
||||
flex-grow: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.filter-sidebar-footer {
|
||||
padding: 15px 20px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
border-top: 1px solid #dee2e6;
|
||||
flex-shrink: 0;
|
||||
gap: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<div class="container-fluid-min">
|
||||
<div class="col-12" id="bds_filter">
|
||||
<div class="card-body">
|
||||
<div id="accordion" class="mb-3">
|
||||
|
||||
<div class="card mb-1">
|
||||
<h4 class="m-1">
|
||||
<span>Filter</span>
|
||||
<a id="toggleIcon" class="text-dark float-right" data-toggle="collapse" href="#collapseOne"
|
||||
aria-expanded="true">
|
||||
<i id="icon" class="mdi mdi-chevron-down mr-1 text-primary" style="font-size: 28px;"></i>
|
||||
</a>
|
||||
</h4>
|
||||
<div id="collapseOne" class="collapse show" aria-labelledby="headingOne" data-parent="#accordion">
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-3">
|
||||
<label for="client_branch">Client<span class="text-danger"></span></label>
|
||||
<select class="form-control" id="client_id" name="client_id" onchange="getClientPolicyDataBasedOnClientAndInsuer()">
|
||||
<option value="0">Select Client</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-3">
|
||||
<label for="client_branch_id">Client Branch<span class="text-danger"></span></label>
|
||||
<select class="form-control" id="client_branch_id" name="client_branch_id" onchange="getClientPolicyDataBasedOnClientAndInsuer()">
|
||||
<option value="0">Select Client Branch</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-3">
|
||||
<label for="insurer_id"> Insurer <span id="base_danger" class="text-danger"></span></label>
|
||||
<select class="form-control" id="insurer_id" name="insurer_id" onchange="getClientPolicyDataBasedOnClientAndInsuer()">
|
||||
<option value="0" selected>Select Insurer</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-3">
|
||||
<label for="insurer_branch_id"> Insurer Branch <span id="base_danger" class="text-danger"></span></label>
|
||||
<select class="form-control" id="insurer_branch_id" name="insurer_branch_id" onchange="getClientPolicyDataBasedOnClientAndInsuer()">
|
||||
<option value="0" selected>Select Insurer Branch</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-3">
|
||||
<label for="email"> Policy Type <span class="text-danger"></span></label>
|
||||
<select class="form-control" id="policy_type_id" name="policy_type_id" onchange="getClientPolicyDataBasedOnClientAndInsuer()">
|
||||
<option value="0">Select Policy Type</option>
|
||||
<?php
|
||||
if (isset($policy_types) && count($policy_types)) {
|
||||
foreach ($policy_types as $key => $value) {
|
||||
echo "<option value='" . $value['id'] . "'>" . $value['policy_type'] . "</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-3">
|
||||
<label for="client_policy_id"> Client Policy <span id="base_danger" class="text-danger"></span></label>
|
||||
<select class="form-control" id="client_policy_id" name="client_policy_id">
|
||||
<option value="0" selected>Select client policy</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-3">
|
||||
<label for="client_branch">Issuer<span class="text-danger">*</span></label>
|
||||
<select class="form-control" id="issuer" name="issuer" required>
|
||||
<option value="0">Select Issure</option>
|
||||
<?php
|
||||
if (isset($issuer) && count($issuer)) {
|
||||
foreach ($issuer as $key => $value) {
|
||||
echo "<option value=" . $key . ">" . $value . "</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-3">
|
||||
<label for="user_id">Users<span class="text-danger"></span></label>
|
||||
<select class="form-control" id="user_id" name="user_id">
|
||||
<option value="0">Select user</option>
|
||||
<?php
|
||||
if (isset($users) && count($users)) {
|
||||
foreach ($users as $key => $value) {
|
||||
echo "<option value='" . $value['id'] . "'>" . $value['first_name'] . "</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-3">
|
||||
<label>Date Type<span class="text-danger"></span></label>
|
||||
<select class="form-control" id="date_type" name="date_type" onchange="hideDateField(); changeTitle();">
|
||||
<option value="0">Select Date Type</option>
|
||||
<?php
|
||||
if (isset($date_type) && count($date_type)) {
|
||||
foreach ($date_type as $key => $value) {
|
||||
echo "<option value='" . $key . "'>" . $value . "</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-3" style="display: none;" id="date_div">
|
||||
<label>Date<span class="text-danger"></span></label>
|
||||
<div class="input-icon">
|
||||
<input type="text" id="reportrange" class="form-control" readonly style="caret-color: transparent;">
|
||||
<i class="mdi mdi-calendar-blank-outline additional-icon"></i>
|
||||
</div>
|
||||
<input type="hidden" id="startDate">
|
||||
<input type="hidden" id="endDate">
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-12 text-right m-b-0" style="margin-top: 29px;">
|
||||
<a href="<?= base_url("/policy_tranction/report/list"); ?>" class="btn btn-secondary" id="clear-filters">Clear</a>
|
||||
<a href="<?= base_url("/policy_tranction/report/list"); ?>" class="btn btn-primary" id="get-emp-list" onclick="fetchEmpolyeeList(event);">Submit</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- <div class="form-group text-right m-b-0">
|
||||
<button type="button" class="btn btn-secondary" id="clear-filters">Clear</button>
|
||||
<a href="<?= base_url("/policy_tranction/report/list"); ?>"
|
||||
class="btn btn-primary waves-effect waves-light" id="get-emp-list"
|
||||
onclick="fetchEmpolyeeList(event);">Submit</a>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="bds-filter-sidebar" class="filter-sidebar">
|
||||
<div class="filter-sidebar-header">
|
||||
<h4 class="m-0">Filter</h4>
|
||||
<a href="javascript:void(0)" class="closebtn" onclick="closeBDSFilterNav()">×</a>
|
||||
</div>
|
||||
<div class="filter-sidebar-content">
|
||||
<div class="form-group">
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-12">
|
||||
<label for="client_branch">Client<span class="text-danger"></span></label>
|
||||
<select class="form-control" id="client_id" name="client_id" onchange="getClientPolicyDataBasedOnClientAndInsuer()">
|
||||
<option value="0">Select Client</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-12">
|
||||
<label for="client_branch_id">Client Branch<span class="text-danger"></span></label>
|
||||
<select class="form-control" id="client_branch_id" name="client_branch_id" onchange="getClientPolicyDataBasedOnClientAndInsuer()">
|
||||
<option value="0">Select Client Branch</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-12">
|
||||
<label for="insurer_id"> Insurer <span id="base_danger" class="text-danger"></span></label>
|
||||
<select class="form-control" id="insurer_id" name="insurer_id" onchange="getClientPolicyDataBasedOnClientAndInsuer()">
|
||||
<option value="0" selected>Select Insurer</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-12">
|
||||
<label for="insurer_branch_id"> Insurer Branch <span id="base_danger" class="text-danger"></span></label>
|
||||
<select class="form-control" id="insurer_branch_id" name="insurer_branch_id" onchange="getClientPolicyDataBasedOnClientAndInsuer()">
|
||||
<option value="0" selected>Select Insurer Branch</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-12">
|
||||
<label for="email"> Policy Type <span class="text-danger"></span></label>
|
||||
<select class="form-control" id="policy_type_id" name="policy_type_id" onchange="getClientPolicyDataBasedOnClientAndInsuer()">
|
||||
<option value="0">Select Policy Type</option>
|
||||
<?php
|
||||
if (isset($policy_types) && count($policy_types)) {
|
||||
foreach ($policy_types as $key => $value) {
|
||||
echo "<option value='" . $value['id'] . "'>" . $value['policy_type'] . "</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-12">
|
||||
<label for="client_policy_id"> Client Policy <span id="base_danger" class="text-danger"></span></label>
|
||||
<select class="form-control" id="client_policy_id" name="client_policy_id">
|
||||
<option value="0" selected>Select client policy</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-12">
|
||||
<label for="client_branch">Issuer<span class="text-danger">*</span></label>
|
||||
<select class="form-control" id="issuer" name="issuer" required>
|
||||
<option value="0">Select Issure</option>
|
||||
<?php
|
||||
if (isset($issuer) && count($issuer)) {
|
||||
foreach ($issuer as $key => $value) {
|
||||
echo "<option value=" . $key . ">" . $value . "</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-12">
|
||||
<label for="user_id">Users<span class="text-danger"></span></label>
|
||||
<select class="form-control" id="user_id" name="user_id">
|
||||
<option value="0">Select user</option>
|
||||
<?php
|
||||
if (isset($users) && count($users)) {
|
||||
foreach ($users as $key => $value) {
|
||||
echo "<option value='" . $value['id'] . "'>" . $value['first_name'] . "</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-12">
|
||||
<label>Date Type<span class="text-danger"></span></label>
|
||||
<select class="form-control" id="date_type" name="date_type" onchange="hideDateField(); changeTitle();">
|
||||
<option value="0">Select Date Type</option>
|
||||
<?php
|
||||
if (isset($date_type) && count($date_type)) {
|
||||
foreach ($date_type as $key => $value) {
|
||||
echo "<option value='" . $key . "'>" . $value . "</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-12" style="display: none;" id="date_div">
|
||||
<label>Date<span class="text-danger"></span></label>
|
||||
<div class="input-icon">
|
||||
<input type="text" id="reportrange" class="form-control" readonly style="caret-color: transparent;">
|
||||
<i class="mdi mdi-calendar-blank-outline additional-icon"></i>
|
||||
</div>
|
||||
<input type="hidden" id="startDate">
|
||||
<input type="hidden" id="endDate">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="filter-sidebar-footer">
|
||||
<a href="<?= base_url("/policy_tranction/report/list"); ?>" class="btn btn-secondary" id="clear-filters">Clear</a>
|
||||
<a href="<?= base_url("/policy_tranction/report/list"); ?>" class="btn btn-primary" id="get-emp-list" onclick="fetchEmpolyeeList(event);">Submit</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="container-fluid-min">
|
||||
<!-- end page title -->
|
||||
<div id="file_list">
|
||||
<?php
|
||||
@ -160,6 +185,14 @@
|
||||
|
||||
|
||||
<script>
|
||||
function openBDSFilterNav() {
|
||||
document.getElementById("bds-filter-sidebar").style.width = "350px";
|
||||
}
|
||||
|
||||
function closeBDSFilterNav() {
|
||||
document.getElementById("bds-filter-sidebar").style.width = "0";
|
||||
}
|
||||
|
||||
let client_list = [];
|
||||
let branch_list = [];
|
||||
let policy_list = [];
|
||||
@ -183,6 +216,7 @@
|
||||
|
||||
function fetchEmpolyeeList(event) {
|
||||
event.preventDefault(); // Prevent default action
|
||||
closeBDSFilterNav();
|
||||
|
||||
var start_date = $('#startDate').val();
|
||||
var end_date = $('#endDate').val();
|
||||
|
||||
@ -29,86 +29,115 @@
|
||||
transform: scale(1.5);
|
||||
}
|
||||
.dataTables_length label {height: 21px !important;}
|
||||
.filter-sidebar {
|
||||
height: 100%;
|
||||
width: 0;
|
||||
position: fixed;
|
||||
z-index: 1001;
|
||||
top: 0;
|
||||
right: 0;
|
||||
background-color: #f8f9fa;
|
||||
overflow-x: hidden;
|
||||
transition: 0.5s;
|
||||
box-shadow: -2px 0 5px rgba(0,0,0,0.1);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.filter-sidebar-header {
|
||||
padding: 15px 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.filter-sidebar-header .closebtn {
|
||||
font-size: 28px;
|
||||
text-decoration: none;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.filter-sidebar .closebtn:hover {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.filter-sidebar-content {
|
||||
padding: 20px;
|
||||
flex-grow: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.filter-sidebar-footer {
|
||||
padding: 15px 20px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
border-top: 1px solid #dee2e6;
|
||||
flex-shrink: 0;
|
||||
gap: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="container-fluid-min">
|
||||
<div class="row">
|
||||
<div class="col-xl-12">
|
||||
<div class="card-body">
|
||||
<div id="accordion" class="mb-3">
|
||||
<div class="card mb-1">
|
||||
<h4>
|
||||
Filter
|
||||
<a id="toggleIcon" class="text-dark float-right" data-toggle="collapse" href="#collapseOne"
|
||||
aria-expanded="true">
|
||||
<i id="icon" class="mdi mdi-chevron-down mr-1 text-primary" style="font-size: 28px;"></i>
|
||||
</a>
|
||||
</h4>
|
||||
<div id="collapseOne" class="collapse show" aria-labelledby="headingOne" data-parent="#accordion">
|
||||
<div class="card-body">
|
||||
<!-- <div class="text-center"> -->
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-4">
|
||||
<label>Client</label> <br />
|
||||
<select class="form-control" id="clients">
|
||||
<option value="0">Select</option>
|
||||
<?php foreach ($client as $key => $value) { ?>
|
||||
<option value="<?= $value['id'] ?>" <?= (($getData['client_id'] ?? '') == $value['id']) ? 'selected' : '' ?>>
|
||||
<?= $value['client_name'] . (!empty($value['short_name']) ? ' ( ' . $value['short_name'] . ' )' : '') ?>
|
||||
</option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
<div id="filter-sidebar" class="filter-sidebar">
|
||||
<div class="filter-sidebar-header">
|
||||
<h4 class="m-0">Filter</h4>
|
||||
<a href="javascript:void(0)" class="closebtn" onclick="closeFilterNav()">×</a>
|
||||
</div>
|
||||
<div class="filter-sidebar-content">
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-12">
|
||||
<label>Client</label> <br />
|
||||
<select class="form-control" id="clients">
|
||||
<option value="0">Select</option>
|
||||
<?php foreach ($client as $key => $value) { ?>
|
||||
<option value="<?= $value['id'] ?>" <?= (($getData['client_id'] ?? '') == $value['id']) ? 'selected' : '' ?>>
|
||||
<?= $value['client_name'] . (!empty($value['short_name']) ? ' ( ' . $value['short_name'] . ' )' : '') ?>
|
||||
</option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-4">
|
||||
<label>Insurer</label> <br />
|
||||
<select class="form-control" id="insurer">
|
||||
<option value="0">Select</option>
|
||||
<?php foreach ($insurer as $key => $value) { ?>
|
||||
<option value="<?= $value['id'] ?>" <?= (($getData['insurer_id'] ?? '') == $value['id']) ? 'selected' : '' ?>>
|
||||
<?= $value['name'] . (!empty($value['short_name']) ? ' ( ' . $value['short_name'] . ' )' : '') ?>
|
||||
</option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group col-md-12">
|
||||
<label>Insurer</label> <br />
|
||||
<select class="form-control" id="insurer">
|
||||
<option value="0">Select</option>
|
||||
<?php foreach ($insurer as $key => $value) { ?>
|
||||
<option value="<?= $value['id'] ?>" <?= (($getData['insurer_id'] ?? '') == $value['id']) ? 'selected' : '' ?>>
|
||||
<?= $value['name'] . (!empty($value['short_name']) ? ' ( ' . $value['short_name'] . ' )' : '') ?>
|
||||
</option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-4">
|
||||
<label>Status</label> <br />
|
||||
<select class="form-control" id="status1">
|
||||
<option value="0">Select</option>
|
||||
<?php foreach ($status as $key => $value) { ?>
|
||||
<option value="<?= $key ?>" <?= (($getData['status'] ?? '') == $key) ? 'selected' : '' ?>>
|
||||
<?= $value ?>
|
||||
</option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12" style="text-align: right;">
|
||||
<a href="<?= base_url("employee/retail-endorsement-list"); ?>" class="btn btn-primary waves-effect waves-light" id="get-emp-list" onclick="fetchEmpolyeeList(event);">Submit</a>
|
||||
</div>
|
||||
</div>
|
||||
<!-- </div> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-12">
|
||||
<label>Status</label> <br />
|
||||
<select class="form-control" id="status1">
|
||||
<option value="0">Select</option>
|
||||
<?php foreach ($status as $key => $value) { ?>
|
||||
<option value="<?= $key ?>" <?= (($getData['status'] ?? '') == $key) ? 'selected' : '' ?>>
|
||||
<?= $value ?>
|
||||
</option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="filter-sidebar-footer">
|
||||
<a href="<?= base_url("employee/retail-endorsement-list"); ?>" class="btn btn-secondary" >Clear</a>
|
||||
<a href="<?= base_url("employee/retail-endorsement-list"); ?>" class="btn btn-primary" id="get-emp-list" onclick="fetchEmpolyeeList(event);closeFilterNav();">Submit</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" id="client_list">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="row" style="padding-bottom: 10px;">
|
||||
<!-- <div class="row" style="padding-bottom: 10px;">
|
||||
<div class="col-6" style="align-self: center;">
|
||||
<h4 style="position: relative;">Endorsement</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
<table data-custom-table-css="table" class="table table-hover m-0 table-centered dt-responsive w-100" cellspacing="0" id="tickets-table">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
@ -168,10 +197,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="modal fade font-color-black " id="centermodal" tabindex="-1" role="dialog" aria-hidden="true" data-backdrop="static">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
@ -217,6 +242,14 @@
|
||||
|
||||
|
||||
<script>
|
||||
function openRetailEndrosementFilterNav() {
|
||||
document.getElementById("filter-sidebar").style.width = "350px";
|
||||
}
|
||||
|
||||
function closeFilterNav() {
|
||||
document.getElementById("filter-sidebar").style.width = "0";
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
// Initialize select2
|
||||
$("#clients").select2();
|
||||
@ -257,13 +290,6 @@
|
||||
|
||||
}
|
||||
|
||||
document.getElementById('toggleIcon').addEventListener('click', function() {
|
||||
var icon = document.getElementById('icon');
|
||||
icon.classList.toggle('mdi-chevron-down');
|
||||
icon.classList.toggle('mdi-chevron-up');
|
||||
});
|
||||
|
||||
|
||||
$('.emp_data_model').click(function() {
|
||||
var id = $(this).data('id');
|
||||
var eno = $(this).data('eno');
|
||||
@ -373,32 +399,22 @@
|
||||
$(document).ready(function() {
|
||||
|
||||
$('#tickets-table').DataTable({
|
||||
// dom: "<'row'<'col-12 d-flex justify-content-between align-items-center'<'datatable-search dataTables_filter'f><'datatable-buttons dt-buttons'B>>>" +
|
||||
// "<'row'<'col-sm-12'tr>>" +
|
||||
// "<'row'<'col-sm-5'i><'col-sm-7'p>>",
|
||||
// buttons: [{
|
||||
// extend: 'csv',
|
||||
// text: 'CSV',
|
||||
// title: 'Retail-Endorsement-List',
|
||||
// className: 'my_class',
|
||||
// exportOptions: {
|
||||
// columns: ':not(:last-child)',
|
||||
// format: {
|
||||
// body: function (data, row, column, node) {
|
||||
// // Convert data to string to avoid scientific notation in CSV
|
||||
// if (!isNaN(data) && parseFloat(data) > 1e20) {
|
||||
// return `'${data}`;
|
||||
// }
|
||||
// return data.toString();// Ensures all data is treated as strings
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }],
|
||||
dom: "<'row'<'col-12 d-flex justify-content-between align-items-center'<'datatable-search dataTables_filter'f><'datatable-buttons dt-buttons'B>>>" +
|
||||
"<'row'<'col-sm-12'tr>>" +
|
||||
"<'row align-items-center'<'col-5 text-start'i><'col-7 d-flex justify-content-end align-items-center'<'me-2'l>p>>",
|
||||
lengthMenu: [[10, 20, 50, 100], [10, 20, 50, 100]],
|
||||
buttons: [{
|
||||
buttons: [
|
||||
{
|
||||
text: '<i class="mdi mdi-filter"></i><span class="btn-custom"> Filter </span>',
|
||||
className: 'btn app-btn-primary mr-2',
|
||||
action: function (e, dt, node, config) {
|
||||
openRetailEndrosementFilterNav();
|
||||
},
|
||||
attr: {
|
||||
id: 'openRetailEndrosementFilterNav'
|
||||
}
|
||||
},
|
||||
{
|
||||
extend: 'collection',
|
||||
text: '<span class=" btn-custom"> Export </span><i class="mdi mdi-menu-down"></i>',
|
||||
className: 'btn app-btn-secondary ',
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
<div class="form-row renewalCalculation">
|
||||
|
||||
<div class="form-group col-md-3 renewalFields" style="display: none;">
|
||||
<label for="incurred_claim_date">Incurred Claim Date<span class="text-danger"></span></label>
|
||||
<label for="incurred_claim_date">Incurred Claim Date<span class="text-danger">*</span></label>
|
||||
<input value="<?= isset($lead_edit_data['incurred_claims_date']) ? $lead_edit_data['incurred_claims_date'] : '' ?>" type="text" class="form-control incurred_claim" id="incurred_claim_date<?= $increment ?>"
|
||||
name="incurred_claim_date[]" placeholder="Enter DOE" oninput="calculatePolicyMetrics(this)">
|
||||
</div>
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<div class="row" id="List-page">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body maincard">
|
||||
<div class="card-body">
|
||||
<table data-custom-table-css="table" class="table table-sm m-0 table-centered dt-responsive nowrap w-100" cellspacing="a" id="user-table">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
@ -125,8 +125,8 @@
|
||||
lengthMenu: [[10, 20, 50, 100], [10, 20, 50, 100]],
|
||||
buttons: [
|
||||
{
|
||||
text: 'Add',
|
||||
className: 'btn app-btn-primary mr-2',
|
||||
text: '<i class="mdi mdi-plus"></i><span class="btn-custom"> Add </span>',
|
||||
action: function(e, dt, node, config) {
|
||||
openModal()
|
||||
}
|
||||
|
||||
@ -123,7 +123,7 @@
|
||||
.timeline-item:last-child::before { display: none !important;}
|
||||
|
||||
/* opportunities Styling */
|
||||
.opportunities-list { display: grid; gap: 15px;}
|
||||
.opportunities-list { display: grid; gap: 15px; margin-bottom: 12px; }
|
||||
.opportunity-card { background: white; border-radius: 12px; padding: 20px; border: 1px solid #e0e0e0; transition: all 0.2s;}
|
||||
.opportunity-header { display: flex; justify-content: space-between; align-items: start; margin-bottom: 15px;}
|
||||
.opportunity-title { font-weight: 600; font-size: 16px; margin-bottom: 5px;}
|
||||
@ -171,7 +171,7 @@
|
||||
</style>
|
||||
|
||||
<div class="main-content">
|
||||
<hr class="my-0">
|
||||
<hr style="margin-bottom: 0 !important;">
|
||||
<div class="lead-header">
|
||||
|
||||
<!-- LEFT SIDE -->
|
||||
@ -213,7 +213,7 @@
|
||||
<div class="modal-content" style="max-width: 850px;">
|
||||
<div class="modal-header">
|
||||
<div>
|
||||
<h2 id="det_company">Lead Detail</h2>
|
||||
<h4 class="modal-title" id="det_company" style="margin-top: 5px;" >Lead Detail</h2>
|
||||
<span id="det_status_badge" class="lead-status" style="margin-top: unset;"></span>
|
||||
</div>
|
||||
<button class="btn-close" title="Close" onclick="closeModal('leadDetailModal')">×</button>
|
||||
@ -408,7 +408,7 @@
|
||||
<div class="modal" id="opportunityModal">
|
||||
<div class="modal-content" style="max-width: 600px;">
|
||||
<div class="modal-header">
|
||||
<h2>Select Opportunity Type</h2>
|
||||
<h4 class="modal-title" style="margin-top: 15px;margin-bottom: 15px;">Select Opportunity Type</h4>
|
||||
<button class="btn-close" title="Close" onclick="closeModal('opportunityModal')">×</button>
|
||||
</div>
|
||||
<hr class="my-0">
|
||||
@ -683,7 +683,7 @@ async function fetchActivities(isLoadMore = false) {
|
||||
${activityIcons[a.activity_type]} ${a.activity_type}
|
||||
</span>
|
||||
<span class="activity-status-badge status-${a.status.toLowerCase().replace(' ', '-')}"">
|
||||
${a.status}
|
||||
${capitalize(a.status)}
|
||||
</span>
|
||||
</div>
|
||||
<div class="activity-details">
|
||||
@ -814,7 +814,7 @@ function renderCard(opps) {
|
||||
// ${o.client_short_name}
|
||||
return `
|
||||
<div class="opportunities-list" id="leadOpportunitiesList">
|
||||
<div class="opportunity-card">
|
||||
<div class="opportunity-card" style="bottom:2px;">
|
||||
<div class="opportunity-header">
|
||||
<div class="opportunity-title">${lead_type}</div>
|
||||
</div>
|
||||
@ -940,6 +940,11 @@ function openOpportunityModal() {
|
||||
openModal('opportunityModal');
|
||||
}
|
||||
|
||||
function capitalize(str) {
|
||||
if (!str) return '';
|
||||
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
|
||||
}
|
||||
|
||||
function switchTab(tabName) {
|
||||
//Default Activity Tab how to resset here
|
||||
// 1. Hide all tab content
|
||||
|
||||
@ -353,7 +353,7 @@
|
||||
$colors = ['#ff6b35','#667eea','#48bb78','#ed8936','#9f7aea'];
|
||||
foreach ($team as $member):
|
||||
$firstLetter = strtoupper(substr($member['first_name'], 0, 1));
|
||||
$fullName = $member['first_name'] . ' ' . $member['last_name'];
|
||||
$fullName = $member['first_name'];
|
||||
$color = $colors[abs(crc32($member['first_name'])) % count($colors)];
|
||||
?>
|
||||
<div class="team-member">
|
||||
@ -567,7 +567,7 @@ function renderList() {
|
||||
+ '<div class="member-cell">'
|
||||
+ '<div style="font-size:14px;font-weight:800;color:#cbd5e1;min-width:26px;">#' + (i+1) + '</div>'
|
||||
+ '<div class="m-avatar" style="background:' + grad + ';">' + (m.first_name||'?')[0] + '</div>'
|
||||
+ '<div><div class="m-name">' + m.first_name + ' ' + m.last_name + '</div></div>'
|
||||
+ '<div><div class="m-name">' + m.first_name + '</div></div>'
|
||||
+ '</div>'
|
||||
+ '<div style="padding-right:20px;">'
|
||||
+ '<div class="prog-row"><span class="prog-pct" style="color:' + color + ';">' + p + '%</span><span class="prog-amts">' + fmt(m.achieved_amt) + ' / ' + fmt(m.target_amt) + '</span></div>'
|
||||
@ -599,7 +599,7 @@ function openModal(id) {
|
||||
|
||||
document.getElementById('modalAva').style.background = grad;
|
||||
document.getElementById('modalAva').textContent = (m.first_name || '?')[0];
|
||||
document.getElementById('modalName').textContent = m.first_name + ' ' + m.last_name;
|
||||
document.getElementById('modalName').textContent = m.first_name;
|
||||
document.getElementById('modalFyBadge').textContent = fyLabel;
|
||||
|
||||
document.getElementById('modalSummary').innerHTML =
|
||||
|
||||
526
app/Views/sales/branch_level_dashboard_view_2mar.php
Normal file
526
app/Views/sales/branch_level_dashboard_view_2mar.php
Normal file
@ -0,0 +1,526 @@
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap');
|
||||
|
||||
.dash-container {
|
||||
padding: 28px;
|
||||
background: #f4f6fb;
|
||||
font-family: 'Plus Jakarta Sans', 'Segoe UI', sans-serif;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* ── Stat Cards ── */
|
||||
.stat-cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 18px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.card-hero {
|
||||
background: white;
|
||||
padding: 22px 24px;
|
||||
border-radius: 14px;
|
||||
border: 1px solid #e8edf5;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.04);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
.card-hero::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0; left: 0;
|
||||
width: 100%; height: 4px;
|
||||
}
|
||||
.card-hero:nth-child(1)::before { background: linear-gradient(90deg, #667eea, #764ba2); }
|
||||
.card-hero:nth-child(2)::before { background: linear-gradient(90deg, #f093fb, #f5576c); }
|
||||
.card-hero:nth-child(3)::before { background: linear-gradient(90deg, #fda085, #f6d365); }
|
||||
.card-hero:nth-child(4)::before { background: linear-gradient(90deg, #43e97b, #38f9d7); }
|
||||
.card-hero:hover { transform: translateY(-4px); box-shadow: 0 12px 24px rgba(0,0,0,0.09); }
|
||||
|
||||
.stat-icon {
|
||||
width: 42px; height: 42px;
|
||||
border-radius: 10px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
font-size: 18px; margin-bottom: 14px;
|
||||
}
|
||||
.stat-val { font-size: 30px; font-weight: 800; color: #1a202c; line-height: 1; }
|
||||
.stat-label { color: #8896aa; font-size: 13px; margin-top: 6px; font-weight: 600; }
|
||||
|
||||
/* ── Shared Card ── */
|
||||
.card {
|
||||
background: white;
|
||||
padding: 24px;
|
||||
border-radius: 14px;
|
||||
border: 1px solid #e8edf5;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.04);
|
||||
}
|
||||
|
||||
/* ── Main Grid ── */
|
||||
.main-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr;
|
||||
gap: 20px;
|
||||
margin-bottom: 24px;
|
||||
align-items: start;
|
||||
}
|
||||
.main-grid > .card {
|
||||
height: 520px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.table-scroll-area { flex: 1; overflow-y: auto; }
|
||||
.table-scroll-area thead th { position: sticky; top: 0; background: white; z-index: 1; }
|
||||
|
||||
/* ── Right Panel ── */
|
||||
.right-panel {
|
||||
height: 520px;
|
||||
background: white;
|
||||
border-radius: 14px;
|
||||
border: 1px solid #e8edf5;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.04);
|
||||
padding: 22px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
.team-members-scroll { flex: 1; overflow-y: auto; margin-top: 10px; }
|
||||
.team-member {
|
||||
display: flex; align-items: center;
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px solid #f4f6fb;
|
||||
}
|
||||
.team-member:last-child { border-bottom: none; }
|
||||
.member-img {
|
||||
width: 38px; height: 38px; border-radius: 50%;
|
||||
color: white; margin-right: 12px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
font-weight: 700; font-size: 15px; flex-shrink: 0;
|
||||
}
|
||||
.act-stats { text-align: right; }
|
||||
.act-stats .total { font-weight: 700; font-size: 13px; color: #1a202c; }
|
||||
.act-stats .done { font-size: 11px; color: #38a169; font-weight: 600; margin-top: 2px; }
|
||||
|
||||
/* ── Activity Breakdown ── */
|
||||
.breakdown-section {
|
||||
flex-shrink: 0;
|
||||
margin-top: 12px;
|
||||
border: 1px solid #f0f4fa;
|
||||
border-radius: 12px;
|
||||
padding: 14px;
|
||||
background: #fafbfd;
|
||||
}
|
||||
.breakdown-item {
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
margin-bottom: 9px; font-size: 13px;
|
||||
}
|
||||
.breakdown-item:last-child { margin-bottom: 0; }
|
||||
.breakdown-bar-wrap {
|
||||
flex: 1; height: 5px; background: #eef0f5;
|
||||
border-radius: 99px; margin: 0 10px; overflow: hidden;
|
||||
}
|
||||
.breakdown-bar { height: 100%; border-radius: 99px; }
|
||||
|
||||
/* ── Table ── */
|
||||
.table-header {
|
||||
display: flex; justify-content: space-between;
|
||||
align-items: center; margin-bottom: 16px;
|
||||
}
|
||||
table { width: 100%; border-collapse: collapse; }
|
||||
th {
|
||||
text-align: left; padding: 10px 12px;
|
||||
color: #8896aa; font-size: 11px;
|
||||
text-transform: uppercase; letter-spacing: 0.07em;
|
||||
border-bottom: 1px solid #edf2f7; background: white; font-weight: 700;
|
||||
}
|
||||
td { padding: 13px 12px; border-bottom: 1px solid #f4f6fb; font-size: 14px; vertical-align: middle; }
|
||||
tbody tr:hover { background: #fafbfe; }
|
||||
|
||||
/* ── Status Pills ── */
|
||||
.status-pill { padding: 4px 12px; border-radius: 20px; font-size: 11px; font-weight: 700; display: inline-block; }
|
||||
.status-completed { background: #f0fff4; color: #38a169; }
|
||||
.status-pending { background: #fffaf0; color: #dd6b20; }
|
||||
.status-new { background: #e3f2fd; color: #1976d2; }
|
||||
.status-potential { background: #fff3e0; color: #f57c00; }
|
||||
.status-prospects { background: #e8f5e9; color: #388e3c; }
|
||||
.status-not-a-prospects { background: #ffebee; color: #d32f2f; }
|
||||
|
||||
.leads-table-wrap { max-height: 480px; overflow-y: auto; }
|
||||
.leads-table-wrap thead th { position: sticky; top: 0; background: white; z-index: 1; }
|
||||
|
||||
.empty-state { text-align: center; padding: 50px 20px; color: #aaa; }
|
||||
.empty-icon { width: 70px; height: 70px; margin: 0 auto 16px; background: #f5f5f5; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 32px; }
|
||||
|
||||
/* ══════════════════════════════════════════
|
||||
TEAM ACHIEVEMENT SECTION (NEW)
|
||||
══════════════════════════════════════════ */
|
||||
.achievement-section { margin-bottom: 24px; }
|
||||
|
||||
.achievement-header {
|
||||
display: flex; justify-content: space-between; align-items: flex-start;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.achievement-cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.achieve-card {
|
||||
background: white;
|
||||
border-radius: 14px;
|
||||
border: 1px solid #e8edf5;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.04);
|
||||
padding: 20px;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.achieve-card:hover { transform: translateY(-3px); box-shadow: 0 10px 20px rgba(0,0,0,0.08); }
|
||||
|
||||
.achieve-card-top {
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.achieve-avatar {
|
||||
width: 44px; height: 44px; border-radius: 12px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
font-weight: 800; font-size: 17px; color: white; flex-shrink: 0;
|
||||
}
|
||||
.achieve-name { font-weight: 700; font-size: 15px; color: #1a202c; }
|
||||
.achieve-role { font-size: 11px; color: #8896aa; font-weight: 600; margin-top: 2px; }
|
||||
|
||||
.achieve-pct { font-size: 22px; font-weight: 800; position: absolute; top: 18px; right: 20px; }
|
||||
|
||||
.achieve-progress-wrap {
|
||||
background: #f0f4fa; height: 8px;
|
||||
border-radius: 99px; overflow: hidden; margin-bottom: 10px;
|
||||
}
|
||||
.achieve-progress-bar { height: 100%; border-radius: 99px; }
|
||||
|
||||
.achieve-stats-row {
|
||||
display: grid; grid-template-columns: repeat(3, 1fr);
|
||||
gap: 8px; margin-top: 12px;
|
||||
}
|
||||
.achieve-stat-box { background: #f8fafd; border-radius: 8px; padding: 8px 10px; text-align: center; }
|
||||
.achieve-stat-num { font-size: 16px; font-weight: 800; color: #1a202c; }
|
||||
.achieve-stat-lbl { font-size: 10px; color: #8896aa; font-weight: 600; margin-top: 2px; }
|
||||
|
||||
.achieve-badge {
|
||||
display: inline-block; padding: 3px 10px;
|
||||
border-radius: 99px; font-size: 10px; font-weight: 700; letter-spacing: 0.04em;
|
||||
}
|
||||
.badge-achieved { background: #f0fff4; color: #38a169; }
|
||||
.badge-on-track { background: #e3f2fd; color: #1976d2; }
|
||||
.badge-behind { background: #fff3e0; color: #dd6b20; }
|
||||
.badge-at-risk { background: #ffebee; color: #d32f2f; }
|
||||
|
||||
/* ── Responsive ── */
|
||||
@media (max-width: 1200px) { .stat-cards { grid-template-columns: repeat(2, 1fr); } }
|
||||
@media (max-width: 992px) {
|
||||
.main-grid { grid-template-columns: 1fr; }
|
||||
.main-grid > .card, .right-panel { height: 450px; }
|
||||
.stat-cards { grid-template-columns: repeat(2, 1fr); }
|
||||
.achievement-cards { grid-template-columns: 1fr 1fr; }
|
||||
}
|
||||
@media (max-width: 576px) {
|
||||
.dash-container { padding: 14px; }
|
||||
.stat-cards { grid-template-columns: 1fr 1fr; gap: 10px; }
|
||||
.stat-val { font-size: 24px; }
|
||||
.main-grid > .card, .right-panel { height: 400px; }
|
||||
table th, table td { padding: 10px 8px; font-size: 12px; }
|
||||
.achievement-cards { grid-template-columns: 1fr; }
|
||||
.achievement-header { flex-direction: column; gap: 14px; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="dash-container">
|
||||
|
||||
<!-- ── Stat Cards ── -->
|
||||
<div class="stat-cards">
|
||||
<div class="card-hero">
|
||||
<div class="stat-icon" style="background:#ede9fe;">👥</div>
|
||||
<div class="stat-val"><?php echo $total_leads; ?></div>
|
||||
<div class="stat-label">Total Leads</div>
|
||||
</div>
|
||||
<div class="card-hero">
|
||||
<div class="stat-icon" style="background:#fce7f3;">⚡</div>
|
||||
<div class="stat-val"><?php echo $total_acts ?></div>
|
||||
<div class="stat-label">Total Activities</div>
|
||||
</div>
|
||||
<div class="card-hero">
|
||||
<div class="stat-icon" style="background:#fef3c7;">⏳</div>
|
||||
<div class="stat-val"><?php echo $total_pending_acts ?></div>
|
||||
<div class="stat-label">Pending Activities</div>
|
||||
</div>
|
||||
<div class="card-hero">
|
||||
<div class="stat-icon" style="background:#d1fae5;">✅</div>
|
||||
<div class="stat-val"><?php echo $total_completed_acts ?></div>
|
||||
<div class="stat-label">Completed Activities</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ══════════════════════════════════════════
|
||||
🏆 TEAM ACHIEVEMENT SECTION (NEW)
|
||||
══════════════════════════════════════════ -->
|
||||
<div class="achievement-section">
|
||||
<div class="achievement-header">
|
||||
<div>
|
||||
<h3 style="font-size:17px; font-weight:700; color:#1a202c; margin:0;">🏆 Team Achievement Overview</h3>
|
||||
<div style="font-size:12px; color:#8896aa; font-weight:600; margin-top:4px;">Individual target vs. actual performance this period</div>
|
||||
</div>
|
||||
<?php
|
||||
$branch_total_target = array_sum(array_column($team, 'target_amt'));
|
||||
$branch_total_achiev = array_sum(array_column($team, 'achieved_amt'));
|
||||
$branch_pct = $branch_total_target > 0
|
||||
? min(100, round($branch_total_achiev / $branch_total_target * 100)) : 0;
|
||||
?>
|
||||
<div style="text-align:right;">
|
||||
<div style="font-size:12px; color:#8896aa; font-weight:600;">Branch Total</div>
|
||||
<div style="font-size:22px; font-weight:800; color:#1a202c;">
|
||||
₹<?= number_format($branch_total_achiev / 100000, 1) ?>L
|
||||
<span style="font-size:13px; color:#8896aa; font-weight:600;">/ ₹<?= number_format($branch_total_target / 100000, 1) ?>L</span>
|
||||
</div>
|
||||
<div style="margin-top:6px;">
|
||||
<div style="background:#f0f4fa; height:6px; border-radius:99px; overflow:hidden; width:200px; margin-left:auto;">
|
||||
<div style="background:linear-gradient(90deg,#667eea,#764ba2); height:100%; width:<?= $branch_pct ?>%; border-radius:99px;"></div>
|
||||
</div>
|
||||
<div style="font-size:12px; color:#667eea; font-weight:700; margin-top:4px;"><?= $branch_pct ?>% of branch target</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="achievement-cards">
|
||||
<?php
|
||||
if (!empty($team)):
|
||||
$gradients = [
|
||||
'linear-gradient(135deg,#667eea,#764ba2)',
|
||||
'linear-gradient(135deg,#f093fb,#f5576c)',
|
||||
'linear-gradient(135deg,#43e97b,#38f9d7)',
|
||||
'linear-gradient(135deg,#fda085,#f6d365)',
|
||||
'linear-gradient(135deg,#4facfe,#00f2fe)',
|
||||
'linear-gradient(135deg,#ff6b35,#fda085)',
|
||||
'linear-gradient(135deg,#a18cd1,#fbc2eb)',
|
||||
];
|
||||
$accent_colors = ['#667eea','#f5576c','#43e97b','#fda085','#4facfe','#ff6b35','#a18cd1'];
|
||||
|
||||
foreach ($team as $member):
|
||||
$firstLetter = strtoupper(substr($member['first_name'], 0, 1));
|
||||
$fullName = $member['first_name'] . ' ' . $member['last_name'];
|
||||
$totalActs = $member['total_acts'];
|
||||
$doneActs = $member['done_acts'];
|
||||
$pendingActs = $totalActs - $doneActs;
|
||||
$role = $member['role'];
|
||||
$idx = abs(crc32($member['first_name'])) % count($gradients);
|
||||
$grad = $gradients[$idx];
|
||||
$color = $accent_colors[$idx];
|
||||
|
||||
/* These fields must be added to your $team query */
|
||||
$target_amt = $member['target_amt'] ?? 0;
|
||||
$achieved_amt = $member['achieved_amt'] ?? 0;
|
||||
$remaining = max(0, $target_amt - $achieved_amt);
|
||||
$pct = $target_amt > 0 ? min(100, round($achieved_amt / $target_amt * 100)) : 0;
|
||||
|
||||
if ($pct >= 100) { $badge_class = 'badge-achieved'; $badge_text = '🏆 Achieved'; }
|
||||
elseif ($pct >= 75) { $badge_class = 'badge-on-track'; $badge_text = '🎯 On Track'; }
|
||||
elseif ($pct >= 50) { $badge_class = 'badge-behind'; $badge_text = '⚡ Behind'; }
|
||||
else { $badge_class = 'badge-at-risk'; $badge_text = '⚠️ At Risk'; }
|
||||
?>
|
||||
<div class="achieve-card">
|
||||
<!-- % overlay -->
|
||||
<div class="achieve-pct" style="color:<?= $color ?>;"><?= $pct ?>%</div>
|
||||
|
||||
<div class="achieve-card-top">
|
||||
<div class="achieve-avatar" style="background:<?= $grad ?>;"><?= $firstLetter ?></div>
|
||||
<div style="flex:1; min-width:0; padding-right:50px;">
|
||||
<div class="achieve-name"><?= esc($fullName) ?></div>
|
||||
<div class="achieve-role"><?= esc($role) ?></div>
|
||||
<span class="achieve-badge <?= $badge_class ?>" style="margin-top:4px; display:inline-block;"><?= $badge_text ?></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display:flex; justify-content:space-between; font-size:12px; color:#8896aa; font-weight:600; margin-bottom:6px;">
|
||||
<span>Achieved <strong style="color:#1a202c;">₹<?= number_format($achieved_amt / 100000, 1) ?>L</strong></span>
|
||||
<span>Target <strong style="color:#1a202c;">₹<?= number_format($target_amt / 100000, 1) ?>L</strong></span>
|
||||
</div>
|
||||
|
||||
<div class="achieve-progress-wrap">
|
||||
<div class="achieve-progress-bar" style="width:<?= $pct ?>%; background:<?= $grad ?>;"></div>
|
||||
</div>
|
||||
|
||||
<div style="font-size:11px; color:#8896aa; font-weight:600; margin-bottom:10px;">
|
||||
Remaining: <strong style="color:#dd6b20;">₹<?= number_format($remaining / 100000, 1) ?>L</strong>
|
||||
</div>
|
||||
|
||||
<div class="achieve-stats-row">
|
||||
<div class="achieve-stat-box">
|
||||
<div class="achieve-stat-num"><?= $totalActs ?></div>
|
||||
<div class="achieve-stat-lbl">Activities</div>
|
||||
</div>
|
||||
<div class="achieve-stat-box">
|
||||
<div class="achieve-stat-num" style="color:#38a169;"><?= $doneActs ?></div>
|
||||
<div class="achieve-stat-lbl">Completed</div>
|
||||
</div>
|
||||
<div class="achieve-stat-box">
|
||||
<div class="achieve-stat-num" style="color:#dd6b20;"><?= $pendingActs ?></div>
|
||||
<div class="achieve-stat-lbl">Pending</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; else: ?>
|
||||
<div class="card" style="grid-column:1/-1;">
|
||||
<div class="empty-state"><div class="empty-icon">🏆</div>No team data available</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── Main Grid ── -->
|
||||
<div class="main-grid">
|
||||
|
||||
<!-- LEFT: Pending Activities -->
|
||||
<div class="card">
|
||||
<div class="table-header">
|
||||
<h3 style="font-size:15px; font-weight:700; margin:0;">Pending Activities — All Team</h3>
|
||||
<span style="color:#8896aa; font-size:12px; font-weight:600;">
|
||||
<?= count($pending_acts) . ' ' . (count($pending_acts) == 1 ? 'activity' : 'activities') ?>
|
||||
</span>
|
||||
</div>
|
||||
<div class="table-scroll-area">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Company</th>
|
||||
<th>Activity & Assigned</th>
|
||||
<th>Date</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (!empty($pending_acts)): ?>
|
||||
<?php foreach ($pending_acts as $a): ?>
|
||||
<?php
|
||||
$activityIcons = ['Call'=>'📞','Email'=>'✉️','Meeting'=>'📅','Visit'=>'🚗','Demo'=>'🖥️','Share Docs'=>'📄','To Do'=>'✓'];
|
||||
$icon = $activityIcons[$a['activity_type']] ?? '📌';
|
||||
$statusClass = strtolower(str_replace(' ', '-', $a['status']));
|
||||
$formattedscheduledDate = date('M d, Y, h:i A', strtotime($a['scheduled_date']));
|
||||
?>
|
||||
<tr>
|
||||
<td><strong><?= esc($a['company_name']) ?></strong></td>
|
||||
<td>
|
||||
<div style="color:#ff6b35; font-weight:700; font-size:11px;"><?= $icon ?> <?= strtoupper(esc($a['activity_type'])) ?></div>
|
||||
<div style="font-size:12px; color:#8896aa;"><?= esc($a['assigned_to_name'] ?? 'ID: ' . $a['assigned_to']) ?></div>
|
||||
</td>
|
||||
<td style="color:#4a5568; font-weight:500; font-size:13px;"><?= $formattedscheduledDate ?></td>
|
||||
<td><span class="status-pill status-<?= $statusClass ?>"><?= ucfirst(esc($a['status'])) ?></span></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<tr><td colspan="4"><div class="empty-state"><div class="empty-icon">✓</div>No pending activities</div></td></tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- RIGHT: Sales Team Performance + Activity Breakdown -->
|
||||
<div class="right-panel">
|
||||
<div style="font-size:15px; font-weight:700; padding-bottom:10px; border-bottom:1px solid #f0f4fa; flex-shrink:0;">
|
||||
Sales Team Performance
|
||||
</div>
|
||||
|
||||
<div class="team-members-scroll">
|
||||
<?php
|
||||
if (!empty($team)):
|
||||
$colors = ['#ff6b35','#667eea','#48bb78','#ed8936','#9f7aea'];
|
||||
foreach ($team as $member):
|
||||
$firstLetter = strtoupper(substr($member['first_name'], 0, 1));
|
||||
$fullName = $member['first_name'] . ' ' . $member['last_name'];
|
||||
$color = $colors[abs(crc32($member['first_name'])) % count($colors)];
|
||||
?>
|
||||
<div class="team-member">
|
||||
<div class="member-img" style="background:<?= $color ?>;"><?= $firstLetter ?></div>
|
||||
<div style="flex:1;">
|
||||
<div style="font-weight:700; font-size:14px;"><?= esc($fullName) ?></div>
|
||||
<div style="font-size:11px; color:#8896aa;"><?= esc($member['role']) ?></div>
|
||||
</div>
|
||||
<div class="act-stats">
|
||||
<div class="total"><?= $member['total_acts'] ?> acts</div>
|
||||
<div class="done"><?= $member['done_acts'] ?> done</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; else: ?>
|
||||
<div class="empty-state">No team data found</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="breakdown-section">
|
||||
<h3 style="font-size:13px; font-weight:700; margin:0 0 12px 0; color:#1a202c;">Activity Breakdown</h3>
|
||||
<?php
|
||||
$activityConfig = [
|
||||
'Call' => ['color'=>'#667eea','icon'=>'📞'],
|
||||
'Email' => ['color'=>'#48bb78','icon'=>'✉️'],
|
||||
'Meeting' => ['color'=>'#4299e1','icon'=>'📅'],
|
||||
'Visit' => ['color'=>'#ecc94b','icon'=>'🚗'],
|
||||
'Demo' => ['color'=>'#9f7aea','icon'=>'🖥️'],
|
||||
'Share Docs' => ['color'=>'#ed8936','icon'=>'📄'],
|
||||
'To Do' => ['color'=>'#718096','icon'=>'✓'],
|
||||
];
|
||||
?>
|
||||
<?php if (!empty($activity_breakdown)): ?>
|
||||
<?php foreach ($activity_breakdown as $item): ?>
|
||||
<?php $cfg = $activityConfig[$item['activity_type']] ?? ['color'=>'#718096','icon'=>'📌']; ?>
|
||||
<div class="breakdown-item">
|
||||
<span style="min-width:90px;"><?= $cfg['icon'] ?> <?= esc($item['activity_type']) ?></span>
|
||||
<div class="breakdown-bar-wrap">
|
||||
<div class="breakdown-bar" style="width:<?= $item['percentage'] ?>%; background:<?= $cfg['color'] ?>;"></div>
|
||||
</div>
|
||||
<strong style="min-width:34px; text-align:right; color:#1a202c;"><?= $item['percentage'] ?>%</strong>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<div style="text-align:center; color:#aaa; font-size:13px;">No activities found</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- end .main-grid -->
|
||||
|
||||
<!-- ── All Leads Overview ── -->
|
||||
<div class="card" style="width:100%; box-sizing:border-box;">
|
||||
<div class="table-header">
|
||||
<h3 style="font-size:15px; font-weight:700; margin:0;">All Leads Overview</h3>
|
||||
</div>
|
||||
<div class="leads-table-wrap">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Company</th>
|
||||
<th>Status</th>
|
||||
<th>Assigned To</th>
|
||||
<th style="text-align:center;">Activities</th>
|
||||
<th style="text-align:center;">Opportunities</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (!empty($leads_overview)): ?>
|
||||
<?php foreach ($leads_overview as $lead): ?>
|
||||
<?php $statusClass = strtolower(str_replace(' ', '-', $lead['status'])); ?>
|
||||
<tr>
|
||||
<td><strong><?= esc($lead['company_name']) ?></strong></td>
|
||||
<td><span class="status-pill status-<?= $statusClass ?>"><?= esc($lead['status']) ?></span></td>
|
||||
<td><?= esc($lead['assigned_to'] ?? 'Unassigned') ?></td>
|
||||
<td><div style="text-align:center; font-weight:700;"><?= $lead['activities'] ?></div></td>
|
||||
<td><div style="text-align:center; font-weight:700;"><?= $lead['opportunities'] ?></div></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<tr><td colspan="5"><div class="empty-state"><div class="empty-icon">👤</div>No leads found</div></td></tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- end .dash-container -->
|
||||
592
app/Views/sales/branch_level_dashboard_view_3mar.php
Normal file
592
app/Views/sales/branch_level_dashboard_view_3mar.php
Normal file
@ -0,0 +1,592 @@
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap');
|
||||
|
||||
.dash-container {
|
||||
padding: 28px;
|
||||
background: #f4f6fb;
|
||||
font-family: 'Plus Jakarta Sans', 'Segoe UI', sans-serif;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* ── Stat Cards ── */
|
||||
.stat-cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 18px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.card-hero {
|
||||
background: white;
|
||||
padding: 22px 24px;
|
||||
border-radius: 14px;
|
||||
border: 1px solid #e8edf5;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.04);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
.card-hero::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0; left: 0;
|
||||
width: 100%; height: 4px;
|
||||
}
|
||||
.card-hero:nth-child(1)::before { background: linear-gradient(90deg, #667eea, #764ba2); }
|
||||
.card-hero:nth-child(2)::before { background: linear-gradient(90deg, #f093fb, #f5576c); }
|
||||
.card-hero:nth-child(3)::before { background: linear-gradient(90deg, #fda085, #f6d365); }
|
||||
.card-hero:nth-child(4)::before { background: linear-gradient(90deg, #43e97b, #38f9d7); }
|
||||
.card-hero:hover { transform: translateY(-4px); box-shadow: 0 12px 24px rgba(0,0,0,0.09); }
|
||||
|
||||
.stat-icon {
|
||||
width: 42px; height: 42px;
|
||||
border-radius: 10px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
font-size: 18px; margin-bottom: 14px;
|
||||
}
|
||||
.stat-val { font-size: 30px; font-weight: 800; color: #1a202c; line-height: 1; }
|
||||
.stat-label { color: #8896aa; font-size: 13px; margin-top: 6px; font-weight: 600; }
|
||||
|
||||
/* ── Shared Card ── */
|
||||
.card {
|
||||
background: white;
|
||||
padding: 24px;
|
||||
border-radius: 14px;
|
||||
border: 1px solid #e8edf5;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.04);
|
||||
}
|
||||
|
||||
/* ── Main Grid ── */
|
||||
.main-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr;
|
||||
gap: 20px;
|
||||
margin-bottom: 24px;
|
||||
align-items: start;
|
||||
}
|
||||
.main-grid > .card {
|
||||
height: 520px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.table-scroll-area { flex: 1; overflow-y: auto; }
|
||||
.table-scroll-area thead th { position: sticky; top: 0; background: white; z-index: 1; }
|
||||
|
||||
/* ── Right Panel ── */
|
||||
.right-panel {
|
||||
height: 520px;
|
||||
background: white;
|
||||
border-radius: 14px;
|
||||
border: 1px solid #e8edf5;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.04);
|
||||
padding: 22px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
.team-members-scroll { flex: 1; overflow-y: auto; margin-top: 10px; }
|
||||
.team-member {
|
||||
display: flex; align-items: center;
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px solid #f4f6fb;
|
||||
}
|
||||
.team-member:last-child { border-bottom: none; }
|
||||
.member-img {
|
||||
width: 38px; height: 38px; border-radius: 50%;
|
||||
color: white; margin-right: 12px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
font-weight: 700; font-size: 15px; flex-shrink: 0;
|
||||
}
|
||||
.act-stats { text-align: right; }
|
||||
.act-stats .total { font-weight: 700; font-size: 13px; color: #1a202c; }
|
||||
.act-stats .done { font-size: 11px; color: #38a169; font-weight: 600; margin-top: 2px; }
|
||||
|
||||
/* ── Activity Breakdown ── */
|
||||
.breakdown-section {
|
||||
flex-shrink: 0;
|
||||
margin-top: 12px;
|
||||
border: 1px solid #f0f4fa;
|
||||
border-radius: 12px;
|
||||
padding: 14px;
|
||||
background: #fafbfd;
|
||||
}
|
||||
.breakdown-item {
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
margin-bottom: 9px; font-size: 13px;
|
||||
}
|
||||
.breakdown-item:last-child { margin-bottom: 0; }
|
||||
.breakdown-bar-wrap {
|
||||
flex: 1; height: 5px; background: #eef0f5;
|
||||
border-radius: 99px; margin: 0 10px; overflow: hidden;
|
||||
}
|
||||
.breakdown-bar { height: 100%; border-radius: 99px; }
|
||||
|
||||
/* ── Table ── */
|
||||
.table-header {
|
||||
display: flex; justify-content: space-between;
|
||||
align-items: center; margin-bottom: 16px;
|
||||
}
|
||||
table { width: 100%; border-collapse: collapse; }
|
||||
th {
|
||||
text-align: left; padding: 10px 12px;
|
||||
color: #8896aa; font-size: 11px;
|
||||
text-transform: uppercase; letter-spacing: 0.07em;
|
||||
border-bottom: 1px solid #edf2f7; background: white; font-weight: 700;
|
||||
}
|
||||
td { padding: 13px 12px; border-bottom: 1px solid #f4f6fb; font-size: 14px; vertical-align: middle; }
|
||||
tbody tr:hover { background: #fafbfe; }
|
||||
|
||||
/* ── Status Pills ── */
|
||||
.status-pill { padding: 4px 12px; border-radius: 20px; font-size: 11px; font-weight: 700; display: inline-block; }
|
||||
.status-completed { background: #f0fff4; color: #38a169; }
|
||||
.status-pending { background: #fffaf0; color: #dd6b20; }
|
||||
.status-new { background: #e3f2fd; color: #1976d2; }
|
||||
.status-potential { background: #fff3e0; color: #f57c00; }
|
||||
.status-prospects { background: #e8f5e9; color: #388e3c; }
|
||||
.status-not-a-prospects { background: #ffebee; color: #d32f2f; }
|
||||
|
||||
.leads-table-wrap { max-height: 480px; overflow-y: auto; }
|
||||
.leads-table-wrap thead th { position: sticky; top: 0; background: white; z-index: 1; }
|
||||
|
||||
.empty-state { text-align: center; padding: 50px 20px; color: #aaa; }
|
||||
.empty-icon { width: 70px; height: 70px; margin: 0 auto 16px; background: #f5f5f5; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 32px; }
|
||||
|
||||
/* ── Responsive ── */
|
||||
@media (max-width: 1200px) { .stat-cards { grid-template-columns: repeat(2, 1fr); } }
|
||||
@media (max-width: 992px) {
|
||||
.main-grid { grid-template-columns: 1fr; }
|
||||
.main-grid > .card, .right-panel { height: 450px; }
|
||||
.stat-cards { grid-template-columns: repeat(2, 1fr); }
|
||||
.achievement-cards { grid-template-columns: 1fr 1fr; }
|
||||
}
|
||||
@media (max-width: 576px) {
|
||||
.dash-container { padding: 14px; }
|
||||
.stat-cards { grid-template-columns: 1fr 1fr; gap: 10px; }
|
||||
.stat-val { font-size: 24px; }
|
||||
.main-grid > .card, .right-panel { height: 400px; }
|
||||
table th, table td { padding: 10px 8px; font-size: 12px; }
|
||||
.achievement-cards { grid-template-columns: 1fr; }
|
||||
.achievement-header { flex-direction: column; gap: 14px; }
|
||||
}
|
||||
|
||||
/* ══════════════════════════════════════════
|
||||
TEAM ACHIEVEMENT SECTION (NEW)
|
||||
══════════════════════════════════════════ */
|
||||
|
||||
.section-head{display:flex;justify-content:space-between;align-items:flex-end;margin-bottom:16px;}
|
||||
.section-title{font-size:16px;font-weight:800;color:#0f172a;}
|
||||
.section-sub{font-size:12px;color:#7c8db0;font-weight:600;margin-top:3px;}
|
||||
.page-title{font-size:16px;font-weight:800;color:#0f172a;}
|
||||
.branch-summary{text-align:right;}
|
||||
.branch-amt{font-size:20px;font-weight:800;color:#0f172a;}
|
||||
.branch-bar-wrap{width:180px;height:5px;background:#e4eaf5;border-radius:99px;overflow:hidden;margin:6px 0 4px auto;}
|
||||
.branch-bar-fill{height:100%;border-radius:99px;background:linear-gradient(90deg,#4f46e5,#818cf8);}
|
||||
.branch-pct-lbl{font-size:11px;color:#4f46e5;font-weight:700;}
|
||||
.achieve-list{background:#fff;border-radius:14px;border:1px solid #e4eaf5;box-shadow:0 2px 12px rgba(15,23,42,0.06);overflow:hidden;}
|
||||
.list-col-head{display:grid;grid-template-columns:260px 1fr 130px 130px 130px 110px 80px;padding:10px 22px;background:#f8fafd;border-bottom:1px solid #e4eaf5;}
|
||||
.col-lbl{font-size:10px;font-weight:700;color:#7c8db0;letter-spacing:.07em;text-transform:uppercase;font-family:'DM Mono',monospace;display:flex;align-items:center;}
|
||||
.col-lbl.right{justify-content:flex-end;}
|
||||
.col-lbl.center{justify-content:center;}
|
||||
.achieve-row{display:grid;grid-template-columns:260px 1fr 130px 130px 130px 110px 80px;padding:16px 22px;border-bottom:1px solid #f1f5f9;align-items:center;cursor:pointer;transition:background .15s;position:relative;}
|
||||
.achieve-row:last-child{border-bottom:none;}
|
||||
.achieve-row:hover{background:#f8fafd;}
|
||||
.achieve-row:hover .row-arrow{opacity:1;transform:translateY(-50%) translateX(0);}
|
||||
.member-cell{display:flex;align-items:center;gap:12px;}
|
||||
.m-avatar{width:42px;height:42px;border-radius:12px;display:flex;align-items:center;justify-content:center;font-weight:800;font-size:16px;color:#fff;flex-shrink:0;}
|
||||
.m-name{font-weight:700;font-size:14px;color:#0f172a;}
|
||||
.m-role{font-size:11px;color:#7c8db0;font-weight:600;margin-top:2px;}
|
||||
.prog-row{display:flex;justify-content:space-between;align-items:center;margin-bottom:5px;}
|
||||
.prog-pct{font-size:13px;font-weight:800;}
|
||||
.prog-amts{font-size:11px;color:#7c8db0;font-weight:600;}
|
||||
.prog-bar-wrap{height:6px;background:#f0f3fa;border-radius:99px;overflow:hidden;}
|
||||
.prog-bar{height:100%;border-radius:99px;}
|
||||
.num-cell{text-align:right;}
|
||||
.num-val{font-size:15px;font-weight:800;}
|
||||
.num-lbl{font-size:10px;color:#7c8db0;font-weight:700;margin-top:2px;letter-spacing:.04em;}
|
||||
.num-cell.center-col{text-align:center;}
|
||||
.badge{display:inline-flex;align-items:center;gap:5px;padding:4px 12px;border-radius:99px;font-size:11px;font-weight:700;white-space:nowrap;}
|
||||
.b-achieved{background:#ecfdf5;color:#059669;}
|
||||
.b-ontrack{background:#eff6ff;color:#2563eb;}
|
||||
.b-behind{background:#fff7ed;color:#c2410c;}
|
||||
.b-atrisk{background:#fef2f2;color:#b91c1c;}
|
||||
.row-arrow{position:absolute;right:18px;top:50%;transform:translateY(-50%) translateX(4px);color:#cbd5e1;font-size:16px;opacity:0;transition:opacity .15s,transform .15s;}
|
||||
.modal-overlay{display:none;position:fixed;inset:0;background:rgba(10,15,30,0.55);backdrop-filter:blur(6px);z-index:1000;align-items:center;justify-content:center;padding:20px;}
|
||||
.modal-overlay.open{display:flex;animation:fadeIn .2s ease;}
|
||||
@keyframes fadeIn{from{opacity:0}to{opacity:1}}
|
||||
.modal-box{background:#fff;border-radius:20px;width:100%;max-width:720px;max-height:90vh;overflow:hidden;display:flex;flex-direction:column;box-shadow:0 32px 80px rgba(15,23,42,0.25);animation:slideUp .25s ease;}
|
||||
@keyframes slideUp{from{transform:translateY(30px);opacity:0}to{transform:translateY(0);opacity:1}}
|
||||
.modal-header{padding:20px 24px 16px;border-bottom:1px solid #e4eaf5;display:flex;align-items:center;gap:14px;flex-shrink:0;}
|
||||
.modal-ava{width:50px;height:50px;border-radius:14px;display:flex;align-items:center;justify-content:center;font-size:21px;font-weight:800;color:#fff;flex-shrink:0;}
|
||||
.modal-name{font-size:17px;font-weight:800;}
|
||||
.modal-role{font-size:12px;color:#7c8db0;font-weight:600;margin-top:2px;}
|
||||
.modal-close{margin-left:auto;background:#f1f5f9;border:none;width:34px;height:34px;border-radius:50%;cursor:pointer;font-size:18px;display:flex;align-items:center;justify-content:center;color:#7c8db0;transition:background .2s;}
|
||||
.modal-close:hover{background:#e2e8f0;color:#0f172a;}
|
||||
.modal-summary{display:grid;grid-template-columns:repeat(4,1fr);background:#f8fafd;border-bottom:1px solid #e4eaf5;flex-shrink:0;}
|
||||
.ms-box{padding:14px 18px;text-align:center;border-right:1px solid #e4eaf5;}
|
||||
.ms-box:last-child{border-right:none;}
|
||||
.ms-num{font-size:19px;font-weight:800;}
|
||||
.ms-lbl{font-size:10px;color:#7c8db0;font-weight:700;letter-spacing:.05em;margin-top:2px;}
|
||||
.modal-body{padding:22px 24px;overflow-y:auto;flex:1;}
|
||||
.overall-prog{background:linear-gradient(135deg,#0f172a,#1e293b);border-radius:14px;padding:18px 20px;color:#fff;margin-bottom:20px;}
|
||||
.op-label{font-size:10px;font-weight:700;letter-spacing:.08em;color:#94a3b8;}
|
||||
.op-row{display:flex;justify-content:space-between;align-items:flex-end;margin:6px 0 12px;}
|
||||
.op-achieved{font-size:24px;font-weight:800;}
|
||||
.op-target{font-size:12px;color:#94a3b8;font-weight:600;}
|
||||
.op-pct{font-size:26px;font-weight:800;color:#818cf8;}
|
||||
.op-bar-wrap{background:rgba(255,255,255,0.12);height:7px;border-radius:99px;overflow:hidden;}
|
||||
.op-bar-fill{height:100%;border-radius:99px;background:linear-gradient(90deg,#818cf8,#67e8f9);}
|
||||
.op-foot{display:flex;justify-content:space-between;font-size:11px;color:#94a3b8;font-weight:600;margin-top:7px;}
|
||||
.split-section-title{font-size:13px;font-weight:800;margin-bottom:12px;}
|
||||
.split-table{width:100%;border-collapse:collapse;font-size:13px;}
|
||||
.split-table thead th{background:#f8fafd;padding:9px 12px;text-align:left;font-size:10px;text-transform:uppercase;letter-spacing:.07em;color:#7c8db0;font-family:'DM Mono',monospace;font-weight:500;border-bottom:2px solid #e4eaf5;}
|
||||
.split-table thead th:last-child{text-align:right;}
|
||||
.split-table tbody tr{border-bottom:1px solid #f1f5f9;}
|
||||
.split-table tbody tr:hover{background:#f8fafd;}
|
||||
.split-table td{padding:12px;vertical-align:middle;}
|
||||
.split-table td:last-child{text-align:right;}
|
||||
.split-name{font-weight:700;font-size:13px;}
|
||||
.split-dates{font-size:11px;color:#7c8db0;font-weight:600;margin-top:1px;}
|
||||
.mini-prog-wrap{width:80px;height:4px;background:#e4eaf5;border-radius:99px;overflow:hidden;margin-top:4px;}
|
||||
.mini-prog-bar{height:100%;border-radius:99px;}
|
||||
.split-pct-badge{display:inline-block;padding:3px 10px;border-radius:99px;font-size:11px;font-weight:700;}
|
||||
.act-chips{display:flex;gap:8px;flex-wrap:wrap;margin-top:14px;}
|
||||
.act-chip{display:flex;align-items:center;gap:6px;background:#f1f5f9;border-radius:99px;padding:5px 13px;font-size:12px;font-weight:700;}
|
||||
.act-dot{width:7px;height:7px;border-radius:50%;}
|
||||
@media(max-width:900px){.list-col-head,.achieve-row{grid-template-columns:220px 1fr 110px 110px 110px;}.col-lbl:nth-child(6),.col-lbl:nth-child(7),.achieve-row>*:nth-child(6),.achieve-row>*:nth-child(7){display:none;}}
|
||||
@media(max-width:640px){body{padding:14px;}.list-col-head{display:none;}.achieve-row{grid-template-columns:1fr auto;gap:12px;padding:14px 16px;}.achieve-row>*:not(:nth-child(1)):not(:nth-child(7)){display:none;}.section-head{flex-direction:column;align-items:flex-start;gap:12px;}.modal-summary{grid-template-columns:repeat(2,1fr);}}
|
||||
</style>
|
||||
|
||||
<div class="dash-container">
|
||||
|
||||
<div>
|
||||
<div class="page-title">Branch Dashboard </div>
|
||||
<div class="section-sub"><span>FY : {financial_year dropdown selected value here}</span></div>
|
||||
<div style="position: relative;">
|
||||
<select id="financial_year" name="financial_year" onchange="onFinancialYearChange(this)" style="background:#f5f5f5; padding:10px 20px; border-radius:8px; border:none; width: 250px; font-size: 13px; cursor: pointer;">
|
||||
<?php if(!empty($fin_years)): ?>
|
||||
<?php foreach($fin_years as $year): ?>
|
||||
<option value="<?= $year; ?>"><?= $year; ?></option>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── Stat Cards ── -->
|
||||
<div class="stat-cards">
|
||||
<div class="card-hero">
|
||||
<div class="stat-icon" style="background:#ede9fe;">👥</div>
|
||||
<div class="stat-val"><?php echo $total_leads; ?></div>
|
||||
<div class="stat-label">Total Leads</div>
|
||||
</div>
|
||||
<div class="card-hero">
|
||||
<div class="stat-icon" style="background:#fce7f3;">⚡</div>
|
||||
<div class="stat-val"><?php echo $total_acts ?></div>
|
||||
<div class="stat-label">Total Activities</div>
|
||||
</div>
|
||||
<div class="card-hero">
|
||||
<div class="stat-icon" style="background:#fef3c7;">⏳</div>
|
||||
<div class="stat-val"><?php echo $total_pending_acts ?></div>
|
||||
<div class="stat-label">Pending Activities</div>
|
||||
</div>
|
||||
<div class="card-hero">
|
||||
<div class="stat-icon" style="background:#d1fae5;">✅</div>
|
||||
<div class="stat-val"><?php echo $total_completed_acts ?></div>
|
||||
<div class="stat-label">Completed Activities</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ══════════════════════════════════════════
|
||||
🏆 TEAM ACHIEVEMENT SECTION (NEW)
|
||||
══════════════════════════════════════════ -->
|
||||
<div class="section-head">
|
||||
<div>
|
||||
<div class="section-title">Team Achievement Overview</div>
|
||||
<div class="section-sub">Click any row to view split-wise achievement history</div>
|
||||
</div>
|
||||
<div class="branch-summary">
|
||||
<div style="font-size:11px;color:#7c8db0;font-weight:700;">Branch Total</div>
|
||||
<div class="branch-amt">₹62.5L <span style="font-size:13px;color:#7c8db0;font-weight:600;">/ ₹100L</span></div>
|
||||
<div class="branch-bar-wrap"><div class="branch-bar-fill" style="width:62.5%;"></div></div>
|
||||
<div class="branch-pct-lbl">62.5% of branch target</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="achieve-list">
|
||||
<div class="list-col-head">
|
||||
<div class="col-lbl">Member</div>
|
||||
<div class="col-lbl">Progress</div>
|
||||
<div class="col-lbl right">Achieved</div>
|
||||
<div class="col-lbl right">Target</div>
|
||||
<div class="col-lbl right">Remaining</div>
|
||||
<div class="col-lbl center">Status</div>
|
||||
<div class="col-lbl center">Acts</div>
|
||||
</div>
|
||||
<div id="achieveList"></div>
|
||||
</div>
|
||||
<br>
|
||||
|
||||
<!-- ── Main Grid ── -->
|
||||
<div class="main-grid">
|
||||
|
||||
<!-- LEFT: Pending Activities -->
|
||||
<div class="card">
|
||||
<div class="table-header">
|
||||
<h3 style="font-size:15px; font-weight:700; margin:0;">Pending Activities — All Team</h3>
|
||||
<span style="color:#8896aa; font-size:12px; font-weight:600;">
|
||||
<?= count($pending_acts) . ' ' . (count($pending_acts) == 1 ? 'activity' : 'activities') ?>
|
||||
</span>
|
||||
</div>
|
||||
<div class="table-scroll-area">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Company</th>
|
||||
<th>Activity & Assigned</th>
|
||||
<th>Date</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (!empty($pending_acts)): ?>
|
||||
<?php foreach ($pending_acts as $a): ?>
|
||||
<?php
|
||||
$activityIcons = ['Call'=>'📞','Email'=>'✉️','Meeting'=>'📅','Visit'=>'🚗','Demo'=>'🖥️','Share Docs'=>'📄','To Do'=>'✓'];
|
||||
$icon = $activityIcons[$a['activity_type']] ?? '📌';
|
||||
$statusClass = strtolower(str_replace(' ', '-', $a['status']));
|
||||
$formattedscheduledDate = date('M d, Y, h:i A', strtotime($a['scheduled_date']));
|
||||
?>
|
||||
<tr>
|
||||
<td><strong><?= esc($a['company_name']) ?></strong></td>
|
||||
<td>
|
||||
<div style="color:#ff6b35; font-weight:700; font-size:11px;"><?= $icon ?> <?= strtoupper(esc($a['activity_type'])) ?></div>
|
||||
<div style="font-size:12px; color:#8896aa;"><?= esc($a['assigned_to_name'] ?? 'ID: ' . $a['assigned_to']) ?></div>
|
||||
</td>
|
||||
<td style="color:#4a5568; font-weight:500; font-size:13px;"><?= $formattedscheduledDate ?></td>
|
||||
<td><span class="status-pill status-<?= $statusClass ?>"><?= ucfirst(esc($a['status'])) ?></span></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<tr><td colspan="4"><div class="empty-state"><div class="empty-icon">✓</div>No pending activities</div></td></tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- RIGHT: Sales Team Performance + Activity Breakdown -->
|
||||
<div class="right-panel">
|
||||
<div style="font-size:15px; font-weight:700; padding-bottom:10px; border-bottom:1px solid #f0f4fa; flex-shrink:0;">
|
||||
Sales Team Performance
|
||||
</div>
|
||||
|
||||
<div class="team-members-scroll">
|
||||
<?php
|
||||
if (!empty($team)):
|
||||
$colors = ['#ff6b35','#667eea','#48bb78','#ed8936','#9f7aea'];
|
||||
foreach ($team as $member):
|
||||
$firstLetter = strtoupper(substr($member['first_name'], 0, 1));
|
||||
$fullName = $member['first_name'] . ' ' . $member['last_name'];
|
||||
$color = $colors[abs(crc32($member['first_name'])) % count($colors)];
|
||||
?>
|
||||
<div class="team-member">
|
||||
<div class="member-img" style="background:<?= $color ?>;"><?= $firstLetter ?></div>
|
||||
<div style="flex:1;">
|
||||
<div style="font-weight:700; font-size:14px;"><?= esc($fullName) ?></div>
|
||||
<div style="font-size:11px; color:#8896aa;"><?= esc($member['role']) ?></div>
|
||||
</div>
|
||||
<div class="act-stats">
|
||||
<div class="total"><?= $member['total_acts'] ?> acts</div>
|
||||
<div class="done"><?= $member['done_acts'] ?> done</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; else: ?>
|
||||
<div class="empty-state">No team data found</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="breakdown-section">
|
||||
<h3 style="font-size:13px; font-weight:700; margin:0 0 12px 0; color:#1a202c;">Activity Breakdown</h3>
|
||||
<?php
|
||||
$activityConfig = [
|
||||
'Call' => ['color'=>'#667eea','icon'=>'📞'],
|
||||
'Email' => ['color'=>'#48bb78','icon'=>'✉️'],
|
||||
'Meeting' => ['color'=>'#4299e1','icon'=>'📅'],
|
||||
'Visit' => ['color'=>'#ecc94b','icon'=>'🚗'],
|
||||
'Demo' => ['color'=>'#9f7aea','icon'=>'🖥️'],
|
||||
'Share Docs' => ['color'=>'#ed8936','icon'=>'📄'],
|
||||
'To Do' => ['color'=>'#718096','icon'=>'✓'],
|
||||
];
|
||||
?>
|
||||
<?php if (!empty($activity_breakdown)): ?>
|
||||
<?php foreach ($activity_breakdown as $item): ?>
|
||||
<?php $cfg = $activityConfig[$item['activity_type']] ?? ['color'=>'#718096','icon'=>'📌']; ?>
|
||||
<div class="breakdown-item">
|
||||
<span style="min-width:90px;"><?= $cfg['icon'] ?> <?= esc($item['activity_type']) ?></span>
|
||||
<div class="breakdown-bar-wrap">
|
||||
<div class="breakdown-bar" style="width:<?= $item['percentage'] ?>%; background:<?= $cfg['color'] ?>;"></div>
|
||||
</div>
|
||||
<strong style="min-width:34px; text-align:right; color:#1a202c;"><?= $item['percentage'] ?>%</strong>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<div style="text-align:center; color:#aaa; font-size:13px;">No activities found</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- end .main-grid -->
|
||||
|
||||
<!-- ── All Leads Overview ── -->
|
||||
<div class="card" style="width:100%; box-sizing:border-box;">
|
||||
<div class="table-header">
|
||||
<h3 style="font-size:15px; font-weight:700; margin:0;">All Leads Overview</h3>
|
||||
</div>
|
||||
<div class="leads-table-wrap">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Company</th>
|
||||
<th>Status</th>
|
||||
<th>Assigned To</th>
|
||||
<th style="text-align:center;">Activities</th>
|
||||
<th style="text-align:center;">Opportunities</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (!empty($leads_overview)): ?>
|
||||
<?php foreach ($leads_overview as $lead): ?>
|
||||
<?php $statusClass = strtolower(str_replace(' ', '-', $lead['status'])); ?>
|
||||
<tr>
|
||||
<td><strong><?= esc($lead['company_name']) ?></strong></td>
|
||||
<td><span class="status-pill status-<?= $statusClass ?>"><?= esc($lead['status']) ?></span></td>
|
||||
<td><?= esc($lead['assigned_to'] ?? 'Unassigned') ?></td>
|
||||
<td><div style="text-align:center; font-weight:700;"><?= $lead['activities'] ?></div></td>
|
||||
<td><div style="text-align:center; font-weight:700;"><?= $lead['opportunities'] ?></div></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<tr><td colspan="5"><div class="empty-state"><div class="empty-icon">👤</div>No leads found</div></td></tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-overlay" id="modalOverlay">
|
||||
<div class="modal-box">
|
||||
<div class="modal-header">
|
||||
<div class="modal-ava" id="modalAva"></div>
|
||||
<div><div class="modal-name" id="modalName"></div><div class="modal-role" id="modalRole"></div></div>
|
||||
<button class="modal-close" onclick="closeModal()">✕</button>
|
||||
</div>
|
||||
<div class="modal-summary" id="modalSummary"></div>
|
||||
<div class="modal-body" id="modalBody"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- end .dash-container -->
|
||||
<script>
|
||||
const TEAM=[{id:1,first:'Divya',last:'Rao',role:'Senior Sales Executive',total_acts:88,done_acts:82,target_amt:2500000,achieved_amt:2510000,grad:'linear-gradient(135deg,#10b981,#34d399)',color:'#10b981',splits:[{name:'Q1 (Apr-Jun 2024)',start:'Apr 2024',end:'Jun 2024',target:625000,achieved:650000,acts:22,done:21,leads:16},{name:'Q2 (Jul-Sep 2024)',start:'Jul 2024',end:'Sep 2024',target:625000,achieved:610000,acts:22,done:20,leads:15},{name:'Q3 (Oct-Dec 2024)',start:'Oct 2024',end:'Dec 2024',target:625000,achieved:640000,acts:24,done:22,leads:17},{name:'Q4 (Jan-Mar 2025)',start:'Jan 2025',end:'Mar 2025',target:625000,achieved:610000,acts:20,done:19,leads:14}],activities:{Call:35,Email:20,Meeting:19,Visit:9,Demo:5}},{id:2,first:'Priya',last:'Nair',role:'Sales Executive',total_acts:74,done_acts:68,target_amt:2000000,achieved_amt:1940000,grad:'linear-gradient(135deg,#06b6d4,#67e8f9)',color:'#06b6d4',splits:[{name:'Q1 (Apr-Jun 2024)',start:'Apr 2024',end:'Jun 2024',target:500000,achieved:495000,acts:19,done:18,leads:12},{name:'Q2 (Jul-Sep 2024)',start:'Jul 2024',end:'Sep 2024',target:500000,achieved:510000,acts:18,done:17,leads:11},{name:'Q3 (Oct-Dec 2024)',start:'Oct 2024',end:'Dec 2024',target:500000,achieved:487000,acts:20,done:18,leads:13},{name:'Q4 (Jan-Mar 2025)',start:'Jan 2025',end:'Mar 2025',target:500000,achieved:448000,acts:17,done:15,leads:10}],activities:{Call:28,Email:22,Meeting:15,Visit:6,Demo:3}},{id:3,first:'Arjun',last:'Sharma',role:'Senior Sales Executive',total_acts:92,done_acts:78,target_amt:2500000,achieved_amt:1975000,grad:'linear-gradient(135deg,#4f46e5,#818cf8)',color:'#4f46e5',splits:[{name:'Q1 (Apr-Jun 2024)',start:'Apr 2024',end:'Jun 2024',target:625000,achieved:582000,acts:24,done:21,leads:18},{name:'Q2 (Jul-Sep 2024)',start:'Jul 2024',end:'Sep 2024',target:625000,achieved:510000,acts:23,done:20,leads:14},{name:'Q3 (Oct-Dec 2024)',start:'Oct 2024',end:'Dec 2024',target:625000,achieved:498000,acts:22,done:19,leads:16},{name:'Q4 (Jan-Mar 2025)',start:'Jan 2025',end:'Mar 2025',target:625000,achieved:385000,acts:23,done:18,leads:12}],activities:{Call:32,Email:18,Meeting:22,Visit:12,Demo:8}},{id:4,first:'Rahul',last:'Verma',role:'Sales Executive',total_acts:67,done_acts:56,target_amt:1500000,achieved_amt:1005000,grad:'linear-gradient(135deg,#ec4899,#f9a8d4)',color:'#ec4899',splits:[{name:'Q1 (Apr-Jun 2024)',start:'Apr 2024',end:'Jun 2024',target:375000,achieved:285000,acts:17,done:14,leads:10},{name:'Q2 (Jul-Sep 2024)',start:'Jul 2024',end:'Sep 2024',target:375000,achieved:262000,acts:17,done:15,leads:9},{name:'Q3 (Oct-Dec 2024)',start:'Oct 2024',end:'Dec 2024',target:375000,achieved:250000,acts:18,done:15,leads:11},{name:'Q4 (Jan-Mar 2025)',start:'Jan 2025',end:'Mar 2025',target:375000,achieved:208000,acts:15,done:12,leads:8}],activities:{Call:22,Email:16,Meeting:17,Visit:7,Demo:5}},{id:5,first:'Kiran',last:'Menon',role:'Junior Sales Executive',total_acts:65,done_acts:44,target_amt:1500000,achieved_amt:820000,grad:'linear-gradient(135deg,#f97316,#fbbf24)',color:'#f97316',splits:[{name:'Q1 (Apr-Jun 2024)',start:'Apr 2024',end:'Jun 2024',target:375000,achieved:240000,acts:17,done:11,leads:9},{name:'Q2 (Jul-Sep 2024)',start:'Jul 2024',end:'Sep 2024',target:375000,achieved:195000,acts:16,done:10,leads:8},{name:'Q3 (Oct-Dec 2024)',start:'Oct 2024',end:'Dec 2024',target:375000,achieved:210000,acts:18,done:13,leads:10},{name:'Q4 (Jan-Mar 2025)',start:'Jan 2025',end:'Mar 2025',target:375000,achieved:175000,acts:14,done:10,leads:7}],activities:{Call:20,Email:14,Meeting:18,Visit:8,Demo:5}}];
|
||||
const ACT_ICONS={Call:'📞',Email:'✉️',Meeting:'📅',Visit:'🚗',Demo:'🖥️','Share Docs':'📄','To Do':'✓'};
|
||||
const ACT_COLORS={Call:'#4f46e5',Email:'#10b981',Meeting:'#06b6d4',Visit:'#f59e0b',Demo:'#ec4899','Share Docs':'#f97316','To Do':'#64748b'};
|
||||
const fmt=v=>'₹'+(v/100000).toFixed(1)+'L';
|
||||
const pct=(a,t)=>t>0?Math.min(100,Math.round(a/t*100)):0;
|
||||
const badge=p=>p>=100?['b-achieved','🏆 Achieved']:p>=75?['b-ontrack','🎯 On Track']:p>=50?['b-behind','⚡ Behind']:['b-atrisk','⚠️ At Risk'];
|
||||
const progColor=p=>p>=100?'linear-gradient(90deg,#10b981,#34d399)':p>=75?'linear-gradient(90deg,#06b6d4,#67e8f9)':p>=50?'linear-gradient(90deg,#f97316,#fbbf24)':'linear-gradient(90deg,#ef4444,#fca5a5)';
|
||||
|
||||
function renderList(){
|
||||
const sorted=[...TEAM].sort((a,b)=>pct(b.achieved_amt,b.target_amt)-pct(a.achieved_amt,a.target_amt));
|
||||
document.getElementById('achieveList').innerHTML=sorted.map((m,i)=>{
|
||||
const p=pct(m.achieved_amt,m.target_amt);
|
||||
const rem=Math.max(0,m.target_amt-m.achieved_amt);
|
||||
const [bc,bt]=badge(p);
|
||||
// const rankLabel=i===0?'🥇':i===1?'🥈':i===2?'🥉':'#'+(i+1);
|
||||
const rankLabel = '#'+(i+1);
|
||||
return `<div class="achieve-row" onclick="openModal(${m.id})">
|
||||
<div class="member-cell">
|
||||
<div style="font-size:14px;font-weight:800;color:#cbd5e1;min-width:26px;font-family:'DM Mono',monospace;">${rankLabel}</div>
|
||||
<div class="m-avatar" style="background:${m.grad};">${m.first[0]}</div>
|
||||
<div><div class="m-name">${m.first} ${m.last}</div></div>
|
||||
</div>
|
||||
<div style="padding-right:20px;">
|
||||
<div class="prog-row"><span class="prog-pct" style="color:${m.color};">${p}%</span><span class="prog-amts">${fmt(m.achieved_amt)} / ${fmt(m.target_amt)}</span></div>
|
||||
<div class="prog-bar-wrap"><div class="prog-bar" style="width:${p}%;background:${m.grad};"></div></div>
|
||||
</div>
|
||||
<div class="num-cell"><div class="num-val" style="color:#0f172a;">${fmt(m.achieved_amt)}</div></div>
|
||||
<div class="num-cell"><div class="num-val" style="color:#7c8db0;">${fmt(m.target_amt)}</div></div>
|
||||
<div class="num-cell"><div class="num-val" style="color:${rem>0?'#f97316':'#10b981'};">${rem>0?fmt(rem):'—'}</div></div>
|
||||
<div style="display:flex;justify-content:center;"><span class="badge ${bc}">${bt}</span></div>
|
||||
<div class="num-cell center-col"><div class="num-val">${m.done_acts}<span style="color:#cbd5e1;font-size:12px;font-weight:600;">/${m.total_acts}</span></div><div class="num-lbl">DONE/TOTAL</div></div>
|
||||
<span class="row-arrow">›</span>
|
||||
</div>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function openModal(id){
|
||||
const m=TEAM.find(x=>x.id===id);if(!m)return;
|
||||
const p=pct(m.achieved_amt,m.target_amt);
|
||||
const rem=Math.max(0,m.target_amt-m.achieved_amt);
|
||||
const [bc,bt]=badge(p);
|
||||
document.getElementById('modalAva').style.background=m.grad;
|
||||
document.getElementById('modalAva').textContent=m.first[0];
|
||||
document.getElementById('modalName').textContent=m.first+' '+m.last
|
||||
document.getElementById('modalRole').textContent=m.role;
|
||||
document.getElementById('modalSummary').innerHTML=`
|
||||
<div class="ms-box"><div class="ms-num" style="color:#4f46e5;">${fmt(m.achieved_amt)}</div><div class="ms-lbl">ACHIEVED</div></div>
|
||||
<div class="ms-box"><div class="ms-num">${fmt(m.target_amt)}</div><div class="ms-lbl">TARGET</div></div>
|
||||
<div class="ms-box"><div class="ms-num" style="color:#f97316;">${fmt(rem)}</div><div class="ms-lbl">REMAINING</div></div>
|
||||
<div class="ms-box"><div class="ms-num" style="color:${p>=75?'#10b981':p>=50?'#f97316':'#ef4444'};">${p}%</div><div class="ms-lbl">ACHIEVED %</div></div>`;
|
||||
const splitRows=m.splits.map((s,i)=>{
|
||||
const sp=pct(s.achieved,s.target);
|
||||
const [sbc,sbt]=badge(sp);
|
||||
const srem=Math.max(0,s.target-s.achieved);
|
||||
return `<tr>
|
||||
<td><div class="split-name">Split ${i+1}: ${s.name}</div><div class="split-dates">📅 ${s.start} → ${s.end}</div></td>
|
||||
<td><div style="font-weight:800;">${fmt(s.achieved)}</div><div class="mini-prog-wrap"><div class="mini-prog-bar" style="width:${sp}%;background:${progColor(sp)};"></div></div></td>
|
||||
<td style="font-weight:700;color:#7c8db0;">${fmt(s.target)}</td>
|
||||
<td style="font-weight:700;color:#f97316;">${srem>0?fmt(srem):'—'}</td>
|
||||
<td><span class="split-pct-badge ${sbc}">${sp}% · ${sbt}</span></td>
|
||||
<td style="font-weight:700;">${s.done}<span style="color:#cbd5e1;">/${s.acts}</span></td>
|
||||
<td style="font-weight:700;">${s.leads}</td>
|
||||
</tr>`;
|
||||
}).join('');
|
||||
const actChips=Object.entries(m.activities).map(([k,v])=>`<div class="act-chip"><div class="act-dot" style="background:${ACT_COLORS[k]||'#64748b'};"></div>${ACT_ICONS[k]||'📌'} ${k}: <strong>${v}</strong></div>`).join('');
|
||||
document.getElementById('modalBody').innerHTML=`
|
||||
<div class="overall-prog">
|
||||
<div class="op-label">OVERALL TARGET ACHIEVEMENT</div>
|
||||
<div class="op-row"><div><div class="op-achieved">${fmt(m.achieved_amt)}</div><div class="op-target">of ${fmt(m.target_amt)} target</div></div><div class="op-pct">${p}%</div></div>
|
||||
<div class="op-bar-wrap"><div class="op-bar-fill" style="width:${p}%;"></div></div>
|
||||
<div class="op-foot"><span>Remaining: <strong style="color:#fbbf24;">${fmt(rem)}</strong></span><span class="badge ${bc}" style="background:rgba(255,255,255,.1);color:#e2e8f0;">${bt}</span></div>
|
||||
</div>
|
||||
<div class="split-section-title">📊 Split-wise Achievement Breakdown</div>
|
||||
<div style="overflow-x:auto;margin-bottom:20px;">
|
||||
<table class="split-table">
|
||||
<thead><tr><th>Split Period</th><th>Achieved</th><th>Target</th><th>Remaining</th><th>Status</th><th>Activities</th><th>Leads</th></tr></thead>
|
||||
<tbody>${splitRows}</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="split-section-title">⚡ Activity Breakdown</div>
|
||||
<div class="act-chips">${actChips}</div>`;
|
||||
document.getElementById('modalOverlay').classList.add('open');
|
||||
document.body.style.overflow='hidden';
|
||||
}
|
||||
function closeModal(){document.getElementById('modalOverlay').classList.remove('open');document.body.style.overflow='';}
|
||||
document.getElementById('modalOverlay').addEventListener('click',e=>{if(e.target===e.currentTarget)closeModal();});
|
||||
document.addEventListener('keydown',e=>{if(e.key==='Escape')closeModal();});
|
||||
renderList();
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const fy = params.get('fy');
|
||||
if (fy) {
|
||||
document.getElementById('financial_year').value = fy;
|
||||
}
|
||||
});
|
||||
|
||||
function onFinancialYearChange(select) {
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.set('fy', select.value);
|
||||
window.location.href = url.toString();
|
||||
}
|
||||
</script>
|
||||
294
app/Views/sales/branch_level_dashboard_view_mar.php
Normal file
294
app/Views/sales/branch_level_dashboard_view_mar.php
Normal file
@ -0,0 +1,294 @@
|
||||
<style>
|
||||
.dash-container { padding: 25px; background: #f8f9fa; font-family: 'Segoe UI', sans-serif; }
|
||||
.top-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 25px; }
|
||||
|
||||
.stat-cards { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; margin-bottom: 30px; }
|
||||
.card { background: white; padding: 25px; border-radius: 12px; border: 1px solid #edf2f7; box-shadow: 0 2px 4px rgba(0,0,0,0.02); }
|
||||
.stat-val { font-size: 28px; font-weight: 700; color: #1a202c; }
|
||||
.stat-label { color: #718096; font-size: 14px; margin-top: 4px; font-weight: 500; }
|
||||
.stat-change { font-size: 11px; margin-top: 8px; font-weight: 600; }
|
||||
.text-success { color: #48bb78; }
|
||||
|
||||
.card-hero {
|
||||
background: white;
|
||||
padding: 25px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid #edf2f7;
|
||||
box-shadow: 0 4px 6px rgba(0,0,0,0.05);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
/* The Gradient Overlay Effect */
|
||||
.card-hero::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 4px; /* Thin line at the top */
|
||||
background: linear-gradient(90deg, #4facfe 0%, #00f2fe 100%);
|
||||
}
|
||||
|
||||
.card-hero:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 10px 15px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
/* Optional: Subtle Background Gradient */
|
||||
.card-hero.gradient-bg {
|
||||
background: linear-gradient(135deg, #ffffff 0%, #f8faff 100%);
|
||||
}
|
||||
|
||||
.main-grid { display: grid; grid-template-columns: 2fr 1fr; gap: 20px; margin-bottom: 30px; }
|
||||
.table-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; }
|
||||
|
||||
.team-member { display: flex; align-items: center; padding: 15px 0; border-bottom: 1px solid #f1f5f9; }
|
||||
.member-img { width: 40px; height: 40px; border-radius: 50%; background: #ff6b35; color: white; margin-right: 12px; display: flex; align-items: center; justify-content: center; font-weight: bold; font-size: 16px; }
|
||||
.act-stats { text-align: right; }
|
||||
.act-stats .total { font-weight: 700; font-size: 13px; color: #1a202c; }
|
||||
.act-stats .done { font-size: 11px; color: #38a169; font-weight: 600; margin-top: 2px; }
|
||||
|
||||
.breakdown-card { margin-top: 20px; }
|
||||
.breakdown-item { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; font-size: 13px; }
|
||||
.dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; margin-right: 8px; }
|
||||
|
||||
table { width: 100%; border-collapse: collapse; }
|
||||
th { text-align: left; padding: 12px; color: #718096; font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; border-bottom: 1px solid #edf2f7; }
|
||||
td { padding: 15px 12px; border-bottom: 1px solid #f1f5f9; font-size: 14px; vertical-align: middle; }
|
||||
|
||||
.status-pill { padding: 4px 12px; border-radius: 20px; font-size: 11px; font-weight: 700; display: inline-block; }
|
||||
.status-completed { background: #f0fff4; color: #38a169; }
|
||||
.status-pending { background: #fffaf0; color: #dd6b20; }
|
||||
.status-new { background: #e3f2fd; color: #1976d2; }
|
||||
.status-potential { background: #fff3e0; color: #f57c00; }
|
||||
.status-prospects { background: #e8f5e9; color: #388e3c; }
|
||||
.status-not-a-prospects { background: #ffebee; color: #d32f2f; }
|
||||
|
||||
.empty-state { text-align: center; padding: 60px 20px; color: #999; }
|
||||
.empty-icon { width: 80px; height: 80px; margin: 0 auto 20px; background: #f5f5f5; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 36px; }
|
||||
|
||||
</style>
|
||||
|
||||
<div class="dash-container">
|
||||
<!-- <div class="top-header">
|
||||
<div>
|
||||
<h2 style="font-weight: 800; color: #1a202c; font-size: 24px;">Dashboard</h2>
|
||||
<p style="color: #718096; font-size: 14px; margin-top: 4px;">Overview of your branch</p>
|
||||
</div>
|
||||
<div style="position: relative;">
|
||||
<input type="text" placeholder="Search leads, activities....." style="background:white; padding:12px 20px; border-radius:10px; border:1px solid #e2e8f0; width: 350px; font-size: 13px;">
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<div class="stat-cards">
|
||||
<div class="card-hero">
|
||||
<div class="stat-val"><?php echo $total_leads; ?></div>
|
||||
<div class="stat-label">Total Leads</div>
|
||||
<!-- <div class="stat-change text-success">↑ 12% this month</div> -->
|
||||
</div>
|
||||
<div class="card-hero">
|
||||
<div class="stat-val"><?php echo $total_acts ?></div>
|
||||
<div class="stat-label">Total Activities</div>
|
||||
<!-- <div class="stat-change text-success">↑ 5% this month</div> -->
|
||||
</div>
|
||||
<div class="card-hero">
|
||||
<div class="stat-val"><?php echo $total_pending_acts ?></div>
|
||||
<div class="stat-label">Pending Activities</div>
|
||||
<!-- <div class="stat-change text-success">↑ 15% this month</div> -->
|
||||
</div>
|
||||
<div class="card-hero">
|
||||
<div class="stat-val"><?php echo $total_completed_acts ?></div>
|
||||
<div class="stat-label">Completed Activities</div>
|
||||
<!-- <div class="stat-change text-success">↑ 15% this month</div> -->
|
||||
</div>
|
||||
<!-- <div class="card">
|
||||
<div class="stat-val">₹15.0L</div>
|
||||
<div class="stat-label">Pipeline Value</div>
|
||||
<div class="stat-change text-success">↑ 18% this month</div>
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
<div class="main-grid">
|
||||
<div class="card">
|
||||
<div class="table-header">
|
||||
<h3 style="font-size: 16px; font-weight: 700;">Pending Activities - All Team</h3>
|
||||
<span style="color: #718096; font-size: 12px; font-weight: 600;"><?= count($pending_acts) . ' ' . (count($pending_acts) == 1 ? 'activity' : 'activities') ?></span>
|
||||
</div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Company</th>
|
||||
<th>Activity & Assigned</th>
|
||||
<th>Date</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (!empty($pending_acts)): ?>
|
||||
<?php foreach ($pending_acts as $a): ?>
|
||||
<?php
|
||||
$activityIcons = [
|
||||
'Call' => '📞',
|
||||
'Email' => '✉️',
|
||||
'Meeting' => '📅',
|
||||
'Visit' => '🚗',
|
||||
'Demo' => '🖥️',
|
||||
'Share Docs' => '📄',
|
||||
'To Do' => '✓'
|
||||
];
|
||||
$icon = $activityIcons[$a['activity_type']] ?? '📌';
|
||||
$statusClass = strtolower(str_replace(' ', '-', $a['status']));
|
||||
$formattedscheduledDate = date('M d, Y, h:i A', strtotime($a['scheduled_date']));
|
||||
?>
|
||||
<tr>
|
||||
<td><strong><?= esc($a['company_name']) ?></strong></td>
|
||||
<td>
|
||||
<div style="color: #ff6b35; font-weight: 700; font-size: 11px;">
|
||||
<?= $icon ?> <?= strtoupper(esc($a['activity_type'])) ?>
|
||||
</div>
|
||||
<div style="font-size: 12px; color: #718096;">
|
||||
<?= esc($a['assigned_to_name'] ?? 'ID: ' . $a['assigned_to']) ?>
|
||||
</div>
|
||||
<!-- <div style="font-size: 12px; color: #718096;">
|
||||
<?= esc($a['notes'] ?? 'ID: ' . $a['assigned_to']) ?>
|
||||
</div> -->
|
||||
</td>
|
||||
<td style="color: #4a5568; font-weight: 500;"><?= $formattedscheduledDate ?></td>
|
||||
<td><span class="status-pill status-<?= $statusClass ?>"><?= ucfirst(esc($a['status'])) ?></span></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<tr>
|
||||
<td colspan="4">
|
||||
<div class="empty-state">
|
||||
<div class="empty-icon">✓</div> No activities found
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="card">
|
||||
|
||||
<h3 style="font-size: 16px; font-weight: 700; margin-bottom: 15px;">Sales Team Performance</h3>
|
||||
<?php foreach ($team as $member) {
|
||||
$firstLetter = strtoupper(substr($member['first_name'], 0, 1));
|
||||
$fullName = $member['first_name'] . ' ' . $member['last_name'];
|
||||
$totalActs = $member['total_acts'];
|
||||
$doneActs = $member['done_acts'];
|
||||
$role = $member['role']; // fix your DB first
|
||||
|
||||
// Random or consistent color based on name
|
||||
$colors = ['#ff6b35', '#667eea', '#48bb78', '#ed8936', '#9f7aea'];
|
||||
$colorIndex = abs(crc32($member['first_name'])) % count($colors);
|
||||
$color = $colors[$colorIndex];
|
||||
|
||||
echo "
|
||||
<div class='team-member'>
|
||||
<div class='member-img' style='background: {$color};'>{$firstLetter}</div>
|
||||
<div style='flex: 1;'>
|
||||
<div style='font-weight: 700; font-size: 14px;'>{$fullName}</div>
|
||||
<div style='font-size: 11px; color: #718096;'>{$role}</div>
|
||||
</div>
|
||||
<div class='act-stats'>
|
||||
<div class='total'>{$totalActs} acts</div>
|
||||
<div class='done'>{$doneActs} done</div>
|
||||
</div>
|
||||
</div>";
|
||||
} ?>
|
||||
|
||||
|
||||
<div class="card breakdown-card">
|
||||
<h3 style="font-size: 15px; font-weight: 700; margin-bottom: 20px;">Activity Breakdown</h3>
|
||||
<!-- <div class="breakdown-item"><span><span class="dot" style="background: #ff6b35;"></span> Call</span><strong>42%</strong></div>
|
||||
<div class="breakdown-item"><span><span class="dot" style="background: #48bb78;"></span> Email</span><strong>25%</strong></div>
|
||||
<div class="breakdown-item"><span><span class="dot" style="background: #4299e1;"></span> Meeting</span><strong>18%</strong></div>
|
||||
<div class="breakdown-item"><span><span class="dot" style="background: #ecc94b;"></span> Visit</span><strong>10%</strong></div>
|
||||
<div class="breakdown-item"><span><span class="dot" style="background: #9f7aea;"></span> Demo</span><strong>5%</strong></div> -->
|
||||
<?php
|
||||
$activityConfig = [
|
||||
'Call' => ['color' => '#ff6b35', 'icon' => '📞'],
|
||||
'Email' => ['color' => '#48bb78', 'icon' => '✉️'],
|
||||
'Meeting' => ['color' => '#4299e1', 'icon' => '📅'],
|
||||
'Visit' => ['color' => '#ecc94b', 'icon' => '🚗'],
|
||||
'Demo' => ['color' => '#9f7aea', 'icon' => '🖥️'],
|
||||
'Share Docs' => ['color' => '#ed8936', 'icon' => '📄'],
|
||||
'To Do' => ['color' => '#718096', 'icon' => '✓'],
|
||||
];
|
||||
?>
|
||||
|
||||
<?php if (!empty($activity_breakdown)): ?>
|
||||
<?php foreach ($activity_breakdown as $item): ?>
|
||||
<?php
|
||||
$type = $item['activity_type'];
|
||||
$color = $activityConfig[$type]['color'] ?? '#718096';
|
||||
$icon = $activityConfig[$type]['icon'] ?? '📌';
|
||||
?>
|
||||
<div class="breakdown-item">
|
||||
<span>
|
||||
<span style="background: <?= $color ?>;"></span>
|
||||
<?= $icon ?> <?= esc($type) ?>
|
||||
</span>
|
||||
<strong><?= $item['percentage'] ?>%</strong>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<div class="empty-state">
|
||||
<div class="empty-icon">✓</div> No activities found
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card" style="grid-column: 1 / -1; width: 100%; box-sizing: border-box;">
|
||||
<div class="table-header">
|
||||
<h3 style="font-size: 16px; font-weight: 700;">All Leads Overview </h3>
|
||||
<!-- <span style="color: #718096; font-size: 12px; font-weight: 600;">Click a lead to see details</span>
|
||||
<a href="<?= base_url('sales') ?>"
|
||||
style="color: #718096; font-size: 12px; font-weight: 600; text-decoration: none; padding: 4px 8px; transition: color 0.2s ease; display: inline-block; cursor: pointer;"
|
||||
onmouseover="this.style.color='#ff6b35';"
|
||||
onmouseout="this.style.color='#718096';">
|
||||
Click a lead to see details
|
||||
</a> -->
|
||||
</div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Company</th>
|
||||
<th>Status</th>
|
||||
<th>Assigned To</th>
|
||||
<th style="text-align: center !important;">Activities</th>
|
||||
<th style="text-align: center !important;">Opportunities</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (!empty($leads_overview)): ?>
|
||||
<?php foreach ($leads_overview as $lead): ?>
|
||||
<?php $statusClass = strtolower(str_replace(' ', '-', $lead['status'])); ?>
|
||||
<tr>
|
||||
<td><strong><?= esc($lead['company_name']) ?></strong></td>
|
||||
<td><span class="status-pill status-<?= $statusClass ?>"><?= esc($lead['status']) ?></span></td>
|
||||
<td><?= esc($lead['assigned_to'] ?? 'Unassigned') ?></td>
|
||||
<td><div style="text-align: center; font-weight: 700;"><?= $lead['activities'] ?></div></td>
|
||||
<td><div style="text-align: center; font-weight: 700;"><?= $lead['opportunities'] ?></div></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<tr>
|
||||
<td colspan="5">
|
||||
<div class="empty-state">
|
||||
<div class="empty-icon">👤</div> No leads found
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user