feat(user-management): enhance user management, permission handling, and charges invoice workflows
- Add UserProfileModel and migration for user profile data - Enhance User Management views, controller, and doctor/nurse search integration - Refactor handleAuthError to separate 401 session expiry from 403 access restriction - Apply granular permission checks to sidebar navigation links - Open Bed Status view for all authenticated users - Support GET and POST on charges item routes and exempt billing charges from CSRF desync
This commit is contained in:
parent
81f3501157
commit
a3c544a53c
|
|
@ -345,6 +345,7 @@ class AuthGroups extends ShieldAuthGroups
|
||||||
'ipd_ot.schedule.manage',
|
'ipd_ot.schedule.manage',
|
||||||
'ipd_ot.status.manage',
|
'ipd_ot.status.manage',
|
||||||
'ipd_ot.postop.manage',
|
'ipd_ot.postop.manage',
|
||||||
|
'settings.bed_status.view',
|
||||||
],
|
],
|
||||||
'nurse' => [
|
'nurse' => [
|
||||||
'ipd_nursing.view',
|
'ipd_nursing.view',
|
||||||
|
|
@ -356,9 +357,11 @@ class AuthGroups extends ShieldAuthGroups
|
||||||
'ipd_ot.request.manage',
|
'ipd_ot.request.manage',
|
||||||
'ipd_ot.status.manage',
|
'ipd_ot.status.manage',
|
||||||
'ipd_ot.postop.manage',
|
'ipd_ot.postop.manage',
|
||||||
|
'settings.bed_status.view',
|
||||||
],
|
],
|
||||||
'pharmacy_admin' => [
|
'pharmacy_admin' => [
|
||||||
'pharmacy.*',
|
'pharmacy.*',
|
||||||
|
'settings.bed_status.view',
|
||||||
],
|
],
|
||||||
'stock_manager' => [
|
'stock_manager' => [
|
||||||
'hospital_stock.access',
|
'hospital_stock.access',
|
||||||
|
|
@ -366,28 +369,33 @@ class AuthGroups extends ShieldAuthGroups
|
||||||
'hospital_stock.indent.approve',
|
'hospital_stock.indent.approve',
|
||||||
'hospital_stock.purchase.manage',
|
'hospital_stock.purchase.manage',
|
||||||
'hospital_stock.report.view',
|
'hospital_stock.report.view',
|
||||||
|
'settings.bed_status.view',
|
||||||
],
|
],
|
||||||
'stock_requester' => [
|
'stock_requester' => [
|
||||||
'hospital_stock.access',
|
'hospital_stock.access',
|
||||||
'hospital_stock.indent.create',
|
'hospital_stock.indent.create',
|
||||||
'hospital_stock.report.view',
|
'hospital_stock.report.view',
|
||||||
|
'settings.bed_status.view',
|
||||||
],
|
],
|
||||||
'stock_issuer' => [
|
'stock_issuer' => [
|
||||||
'hospital_stock.access',
|
'hospital_stock.access',
|
||||||
'hospital_stock.issue',
|
'hospital_stock.issue',
|
||||||
'hospital_stock.purchase.manage',
|
'hospital_stock.purchase.manage',
|
||||||
'hospital_stock.report.view',
|
'hospital_stock.report.view',
|
||||||
|
'settings.bed_status.view',
|
||||||
],
|
],
|
||||||
'department_head' => [
|
'department_head' => [
|
||||||
'hospital_stock.access',
|
'hospital_stock.access',
|
||||||
'hospital_stock.indent.create',
|
'hospital_stock.indent.create',
|
||||||
'hospital_stock.report.view',
|
'hospital_stock.report.view',
|
||||||
|
'settings.bed_status.view',
|
||||||
],
|
],
|
||||||
'storekeeper' => [
|
'storekeeper' => [
|
||||||
'hospital_stock.access',
|
'hospital_stock.access',
|
||||||
'hospital_stock.issue',
|
'hospital_stock.issue',
|
||||||
'hospital_stock.purchase.manage',
|
'hospital_stock.purchase.manage',
|
||||||
'hospital_stock.report.view',
|
'hospital_stock.report.view',
|
||||||
|
'settings.bed_status.view',
|
||||||
],
|
],
|
||||||
'billing_cashier' => [
|
'billing_cashier' => [
|
||||||
'finance.workflow.view',
|
'finance.workflow.view',
|
||||||
|
|
@ -399,6 +407,7 @@ class AuthGroups extends ShieldAuthGroups
|
||||||
'billing.refund.view',
|
'billing.refund.view',
|
||||||
'billing.refund.manage',
|
'billing.refund.manage',
|
||||||
'ipd_discharge.view',
|
'ipd_discharge.view',
|
||||||
|
'settings.bed_status.view',
|
||||||
],
|
],
|
||||||
'accounts_officer' => [
|
'accounts_officer' => [
|
||||||
'finance.workflow.view',
|
'finance.workflow.view',
|
||||||
|
|
@ -407,8 +416,11 @@ class AuthGroups extends ShieldAuthGroups
|
||||||
'finance.bank.audit',
|
'finance.bank.audit',
|
||||||
'finance.bank.statement.update',
|
'finance.bank.statement.update',
|
||||||
'finance.payment.edit',
|
'finance.payment.edit',
|
||||||
|
'settings.bed_status.view',
|
||||||
|
],
|
||||||
|
'user' => [
|
||||||
|
'settings.bed_status.view',
|
||||||
],
|
],
|
||||||
'user' => [],
|
|
||||||
'beta' => [
|
'beta' => [
|
||||||
'beta.access',
|
'beta.access',
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,8 @@ class Filters extends BaseFilters
|
||||||
'HealthplixGateway/*',
|
'HealthplixGateway/*',
|
||||||
'stt-proxy',
|
'stt-proxy',
|
||||||
'stt-proxy/*',
|
'stt-proxy/*',
|
||||||
|
'billing/charges',
|
||||||
|
'billing/charges/*',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
// 'honeypot',
|
// 'honeypot',
|
||||||
|
|
|
||||||
|
|
@ -632,16 +632,16 @@ $routes->post('patient/abdm_content_request_custom/(:num)', 'Patient::abdm_conte
|
||||||
$routes->get('charges/add/(:num)/(:num)', 'Billing\\Charges::add/$1/$2');
|
$routes->get('charges/add/(:num)/(:num)', 'Billing\\Charges::add/$1/$2');
|
||||||
$routes->get('charges/add/(:num)/(:num)/(:num)', 'Billing\\Charges::add/$1/$2/$3');
|
$routes->get('charges/add/(:num)/(:num)/(:num)', 'Billing\\Charges::add/$1/$2/$3');
|
||||||
$routes->get('charges/edit/(:num)', 'Billing\\Charges::edit/$1');
|
$routes->get('charges/edit/(:num)', 'Billing\\Charges::edit/$1');
|
||||||
$routes->post('charges/list-by-type', 'Billing\\Charges::listByType');
|
$routes->match(['GET', 'POST'], 'charges/list-by-type', 'Billing\\Charges::listByType');
|
||||||
$routes->post('charges/item/(:num)', 'Billing\\Charges::item/$1');
|
$routes->match(['GET', 'POST'], 'charges/item/(:num)', 'Billing\\Charges::item/$1');
|
||||||
$routes->post('charges/update-refer', 'Billing\\Charges::updateReferDoc');
|
$routes->match(['GET', 'POST'], 'charges/update-refer', 'Billing\\Charges::updateReferDoc');
|
||||||
$routes->post('charges/confirm-payment', 'Billing\\Charges::confirm_payment');
|
$routes->post('charges/confirm-payment', 'Billing\\Charges::confirm_payment');
|
||||||
$routes->post('charges/cancel-invoice/(:num)', 'Billing\\Charges::cancel_invoice/$1');
|
$routes->post('charges/cancel-invoice/(:num)', 'Billing\\Charges::cancel_invoice/$1');
|
||||||
$routes->post('charges/update-correction', 'Billing\\Charges::update_correction_charges');
|
$routes->post('charges/update-correction', 'Billing\\Charges::update_correction_charges');
|
||||||
$routes->get('charges/show/(:num)', 'Billing\\Charges::show/$1');
|
$routes->get('charges/show/(:num)', 'Billing\\Charges::show/$1');
|
||||||
$routes->get('charges/print_invoice/(:num)/(:num)', 'Billing\\Charges::print_invoice/$1/$2');
|
$routes->get('charges/print_invoice/(:num)/(:num)', 'Billing\\Charges::print_invoice/$1/$2');
|
||||||
$routes->post('charges/delete', 'Billing\\Charges::delete');
|
$routes->match(['GET', 'POST'], 'charges/delete', 'Billing\\Charges::delete');
|
||||||
$routes->post('charges/update-discount', 'Billing\\Charges::updateDiscount');
|
$routes->match(['GET', 'POST'], 'charges/update-discount', 'Billing\\Charges::updateDiscount');
|
||||||
});
|
});
|
||||||
|
|
||||||
$routes->get('ipd/patient', 'IpdPatient::index', ['filter' => 'permission:ipd_nursing.view']);
|
$routes->get('ipd/patient', 'IpdPatient::index', ['filter' => 'permission:ipd_nursing.view']);
|
||||||
|
|
@ -1355,6 +1355,7 @@ $routes->group('setting', static function($routes) {
|
||||||
$routes->get('admin/user-management/sessions', 'Setting\\UserManagement::sessions', ['filter' => 'permission:users.edit,users.manage-admins']);
|
$routes->get('admin/user-management/sessions', 'Setting\\UserManagement::sessions', ['filter' => 'permission:users.edit,users.manage-admins']);
|
||||||
$routes->post('admin/user-management/sessions/force-logout/(:num)', 'Setting\\UserManagement::forceLogout/$1', ['filter' => ['permission:users.edit,users.manage-admins', 'csrf']]);
|
$routes->post('admin/user-management/sessions/force-logout/(:num)', 'Setting\\UserManagement::forceLogout/$1', ['filter' => ['permission:users.edit,users.manage-admins', 'csrf']]);
|
||||||
$routes->post('admin/user-management/delete/(:num)', 'Setting\\UserManagement::delete/$1', ['filter' => ['permission:users.delete,users.manage-admins', 'csrf']]);
|
$routes->post('admin/user-management/delete/(:num)', 'Setting\\UserManagement::delete/$1', ['filter' => ['permission:users.delete,users.manage-admins', 'csrf']]);
|
||||||
|
$routes->post('admin/user-management/toggle-status/(:num)', 'Setting\\UserManagement::toggleStatus/$1', ['filter' => ['permission:users.edit,users.manage-admins', 'csrf']]);
|
||||||
$routes->get('admin/roles', 'Setting\\RoleManagement::index', ['filter' => 'permission:users.manage-admins']);
|
$routes->get('admin/roles', 'Setting\\RoleManagement::index', ['filter' => 'permission:users.manage-admins']);
|
||||||
$routes->get('admin/roles/new', 'Setting\\RoleManagement::create', ['filter' => 'permission:users.manage-admins']);
|
$routes->get('admin/roles/new', 'Setting\\RoleManagement::create', ['filter' => 'permission:users.manage-admins']);
|
||||||
$routes->post('admin/roles', 'Setting\\RoleManagement::store', ['filter' => ['permission:users.manage-admins', 'csrf']]);
|
$routes->post('admin/roles', 'Setting\\RoleManagement::store', ['filter' => ['permission:users.manage-admins', 'csrf']]);
|
||||||
|
|
@ -1462,7 +1463,7 @@ $routes->group('setting', static function($routes) {
|
||||||
$routes->post('admin/departments/save', 'Setting\\BedManagement::saveDepartment', ['filter' => $settingsBedFilter]);
|
$routes->post('admin/departments/save', 'Setting\\BedManagement::saveDepartment', ['filter' => $settingsBedFilter]);
|
||||||
$routes->post('admin/departments/delete', 'Setting\\BedManagement::deleteDepartment', ['filter' => $settingsBedFilter]);
|
$routes->post('admin/departments/delete', 'Setting\\BedManagement::deleteDepartment', ['filter' => $settingsBedFilter]);
|
||||||
$routes->get('admin/wards', 'Setting\\BedManagement::wards', ['filter' => $settingsBedFilter]);
|
$routes->get('admin/wards', 'Setting\\BedManagement::wards', ['filter' => $settingsBedFilter]);
|
||||||
$routes->get('admin/bed-status', 'Setting\\BedManagement::bedStatus', ['filter' => $settingsBedFilter]);
|
$routes->get('admin/bed-status', 'Setting\\BedManagement::bedStatus');
|
||||||
$routes->post('admin/wards/save', 'Setting\\BedManagement::saveWard', ['filter' => $settingsBedFilter]);
|
$routes->post('admin/wards/save', 'Setting\\BedManagement::saveWard', ['filter' => $settingsBedFilter]);
|
||||||
$routes->post('admin/wards/delete', 'Setting\\BedManagement::deleteWard', ['filter' => $settingsBedFilter]);
|
$routes->post('admin/wards/delete', 'Setting\\BedManagement::deleteWard', ['filter' => $settingsBedFilter]);
|
||||||
$routes->get('admin/bed-categories', 'Setting\\BedManagement::categories', ['filter' => $settingsBedFilter]);
|
$routes->get('admin/bed-categories', 'Setting\\BedManagement::categories', ['filter' => $settingsBedFilter]);
|
||||||
|
|
|
||||||
|
|
@ -267,8 +267,8 @@ class Charges extends BaseController
|
||||||
return $resp;
|
return $resp;
|
||||||
}
|
}
|
||||||
|
|
||||||
$tid = (int) $this->request->getPost('itype_idv');
|
$tid = (int) ($this->request->getVar('itype_idv') ?? 0);
|
||||||
$insId = (int) $this->request->getPost('ins_id');
|
$insId = (int) ($this->request->getVar('ins_id') ?? 0);
|
||||||
|
|
||||||
if ($insId > 1) {
|
if ($insId > 1) {
|
||||||
$sql = "select m.id,m.itype,m.idesc,m.amount,t.itype_id,t.is_ipd_opd,t.group_desc,
|
$sql = "select m.id,m.itype,m.idesc,m.amount,t.itype_id,t.is_ipd_opd,t.group_desc,
|
||||||
|
|
@ -292,6 +292,7 @@ class Charges extends BaseController
|
||||||
echo '<option value=' . $row->id . '>' . $row->sdesc . '</option>';
|
echo '<option value=' . $row->id . '>' . $row->sdesc . '</option>';
|
||||||
}
|
}
|
||||||
echo '</select></div>';
|
echo '</select></div>';
|
||||||
|
echo '<input type="hidden" name="' . csrf_token() . '" value="' . csrf_hash() . '" class="ajax-csrf-token" />';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function updateReferDoc()
|
public function updateReferDoc()
|
||||||
|
|
@ -300,9 +301,9 @@ class Charges extends BaseController
|
||||||
return $resp;
|
return $resp;
|
||||||
}
|
}
|
||||||
|
|
||||||
$invoiceId = (int) $this->request->getPost('inv_id');
|
$invoiceId = (int) ($this->request->getVar('inv_id') ?? 0);
|
||||||
$docId = (int) $this->request->getPost('doc_id');
|
$docId = (int) ($this->request->getVar('doc_id') ?? 0);
|
||||||
$rawDate = (string) $this->request->getPost('inv_date');
|
$rawDate = (string) ($this->request->getVar('inv_date') ?? '');
|
||||||
$invDate = str_to_MysqlDate($rawDate);
|
$invDate = str_to_MysqlDate($rawDate);
|
||||||
if (strpos($rawDate, '-') !== false && strlen($rawDate) === 10) {
|
if (strpos($rawDate, '-') !== false && strlen($rawDate) === 10) {
|
||||||
$invDate = $rawDate;
|
$invDate = $rawDate;
|
||||||
|
|
@ -318,7 +319,7 @@ class Charges extends BaseController
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($docId < 1) {
|
if ($docId < 1) {
|
||||||
$referName = (string) $this->request->getPost('refername');
|
$referName = (string) ($this->request->getVar('refername') ?? '');
|
||||||
} else {
|
} else {
|
||||||
$sql = "select * from doctor_master where id=" . $docId;
|
$sql = "select * from doctor_master where id=" . $docId;
|
||||||
$query = $this->db->query($sql);
|
$query = $this->db->query($sql);
|
||||||
|
|
@ -346,8 +347,8 @@ class Charges extends BaseController
|
||||||
$invoiceModel = new InvoiceModel();
|
$invoiceModel = new InvoiceModel();
|
||||||
|
|
||||||
if ($type === 1) {
|
if ($type === 1) {
|
||||||
$insId = (int) $this->request->getPost('ins_id');
|
$insId = (int) ($this->request->getVar('ins_id') ?? 0);
|
||||||
$itemId = (int) $this->request->getPost('itype_name_id');
|
$itemId = (int) ($this->request->getVar('itype_name_id') ?? 0);
|
||||||
|
|
||||||
if ($insId > 1) {
|
if ($insId > 1) {
|
||||||
$sql = "select m.id,m.itype,m.idesc as sdesc,m.amount,t.itype_id,t.is_ipd_opd,t.group_desc,i.isdelete,
|
$sql = "select m.id,m.itype,m.idesc as sdesc,m.amount,t.itype_id,t.is_ipd_opd,t.group_desc,i.isdelete,
|
||||||
|
|
@ -361,11 +362,11 @@ class Charges extends BaseController
|
||||||
|
|
||||||
$itemRate = $itemlist[0]->amount1 ?? 0;
|
$itemRate = $itemlist[0]->amount1 ?? 0;
|
||||||
$orgCode = $itemlist[0]->org_code ?? '';
|
$orgCode = $itemlist[0]->org_code ?? '';
|
||||||
$customRate = (float) $this->request->getPost('input_rate');
|
$customRate = (float) ($this->request->getVar('input_rate') ?? 0);
|
||||||
if ($customRate > 0) {
|
if ($customRate > 0) {
|
||||||
$itemRate = $customRate;
|
$itemRate = $customRate;
|
||||||
}
|
}
|
||||||
$amountValue = (float) $this->request->getPost('input_qty') * $itemRate;
|
$amountValue = (float) ($this->request->getVar('input_qty') ?? 1) * $itemRate;
|
||||||
} else {
|
} else {
|
||||||
$sql = "select id,idesc as sdesc,amount from hc_items where id=" . $itemId;
|
$sql = "select id,idesc as sdesc,amount from hc_items where id=" . $itemId;
|
||||||
$query = $this->db->query($sql);
|
$query = $this->db->query($sql);
|
||||||
|
|
@ -373,27 +374,27 @@ class Charges extends BaseController
|
||||||
|
|
||||||
$itemRate = $itemlist[0]->amount ?? 0;
|
$itemRate = $itemlist[0]->amount ?? 0;
|
||||||
$orgCode = '';
|
$orgCode = '';
|
||||||
$customRate = (float) $this->request->getPost('input_rate');
|
$customRate = (float) ($this->request->getVar('input_rate') ?? 0);
|
||||||
if ($customRate > 0) {
|
if ($customRate > 0) {
|
||||||
$itemRate = $customRate;
|
$itemRate = $customRate;
|
||||||
}
|
}
|
||||||
$amountValue = (float) $this->request->getPost('input_qty') * $itemRate;
|
$amountValue = (float) ($this->request->getVar('input_qty') ?? 1) * $itemRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
$insertInvoiceItem = [
|
$insertInvoiceItem = [
|
||||||
'inv_master_id' => (int) $this->request->getPost('lab_invoice_id'),
|
'inv_master_id' => (int) ($this->request->getVar('lab_invoice_id') ?? 0),
|
||||||
'item_type' => (int) $this->request->getPost('itype_idv'),
|
'item_type' => (int) ($this->request->getVar('itype_idv') ?? 0),
|
||||||
'item_id' => $itemId,
|
'item_id' => $itemId,
|
||||||
'item_name' => $itemlist[0]->sdesc ?? '',
|
'item_name' => $itemlist[0]->sdesc ?? '',
|
||||||
'item_rate' => $itemRate,
|
'item_rate' => $itemRate,
|
||||||
'item_added_date' => str_to_MysqlDate(date('d/m/Y')),
|
'item_added_date' => str_to_MysqlDate(date('d/m/Y')),
|
||||||
'item_qty' => (float) $this->request->getPost('input_qty'),
|
'item_qty' => (float) ($this->request->getVar('input_qty') ?? 1),
|
||||||
'item_amount' => $amountValue,
|
'item_amount' => $amountValue,
|
||||||
'ins_id' => $insId,
|
'ins_id' => $insId,
|
||||||
'org_code' => $orgCode,
|
'org_code' => $orgCode,
|
||||||
];
|
];
|
||||||
|
|
||||||
$sql = "select * from invoice_item where inv_master_id=" . (int) $this->request->getPost('lab_invoice_id') . " and item_id=" . $itemId;
|
$sql = "select * from invoice_item where inv_master_id=" . (int) ($this->request->getVar('lab_invoice_id') ?? 0) . " and item_id=" . $itemId;
|
||||||
$query = $this->db->query($sql);
|
$query = $this->db->query($sql);
|
||||||
$itemCheck = $query->getResult();
|
$itemCheck = $query->getResult();
|
||||||
|
|
||||||
|
|
@ -401,18 +402,18 @@ class Charges extends BaseController
|
||||||
$invoiceModel->addInvoiceItem($insertInvoiceItem);
|
$invoiceModel->addInvoiceItem($insertInvoiceItem);
|
||||||
}
|
}
|
||||||
} elseif ($type === 2) {
|
} elseif ($type === 2) {
|
||||||
$amountValue = (float) $this->request->getPost('update_qty') * (float) $this->request->getPost('item_rate');
|
$amountValue = (float) ($this->request->getVar('update_qty') ?? 1) * (float) ($this->request->getVar('item_rate') ?? 0);
|
||||||
$updateInvoiceItem = [
|
$updateInvoiceItem = [
|
||||||
'item_added_date' => str_to_MysqlDate(date('d/m/Y')),
|
'item_added_date' => str_to_MysqlDate(date('d/m/Y')),
|
||||||
'item_qty' => (float) $this->request->getPost('update_qty'),
|
'item_qty' => (float) ($this->request->getVar('update_qty') ?? 1),
|
||||||
'item_amount' => $amountValue,
|
'item_amount' => $amountValue,
|
||||||
];
|
];
|
||||||
$invoiceModel->updateItem($updateInvoiceItem, (int) $this->request->getPost('itemid'));
|
$invoiceModel->updateItem($updateInvoiceItem, (int) ($this->request->getVar('itemid') ?? 0));
|
||||||
} else {
|
} else {
|
||||||
$invoiceModel->deleteInvoiceItem((int) $this->request->getPost('itemid'));
|
$invoiceModel->deleteInvoiceItem((int) ($this->request->getVar('itemid') ?? 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
$invoiceId = (int) $this->request->getPost('lab_invoice_id');
|
$invoiceId = (int) ($this->request->getVar('lab_invoice_id') ?? 0);
|
||||||
$sql = "select i.*,t.group_desc from invoice_item i join hc_item_type t on i.item_type=t.itype_id where i.inv_master_id=" . $invoiceId . " Order by i.id";
|
$sql = "select i.*,t.group_desc from invoice_item i join hc_item_type t on i.item_type=t.itype_id where i.inv_master_id=" . $invoiceId . " Order by i.id";
|
||||||
$query = $this->db->query($sql);
|
$query = $this->db->query($sql);
|
||||||
$invoiceDetails = $query->getResult();
|
$invoiceDetails = $query->getResult();
|
||||||
|
|
|
||||||
|
|
@ -250,6 +250,15 @@ class IpdPatient extends BaseController
|
||||||
$nurseModel = new \App\Models\NurseModel();
|
$nurseModel = new \App\Models\NurseModel();
|
||||||
$nurseList = $nurseModel->getActiveNurses();
|
$nurseList = $nurseModel->getActiveNurses();
|
||||||
|
|
||||||
|
$defaultNurseId = 0;
|
||||||
|
$user = function_exists('auth') ? auth()->user() : null;
|
||||||
|
if ($user && ! empty($user->id)) {
|
||||||
|
$profile = (new \App\Models\UserProfileModel())->getProfile((int) $user->id);
|
||||||
|
if (! empty($profile['nurse_id'])) {
|
||||||
|
$defaultNurseId = (int) $profile['nurse_id'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return view('ipd_nursing/workspace', [
|
return view('ipd_nursing/workspace', [
|
||||||
'ipd_info' => $panelData['ipd_info'] ?? null,
|
'ipd_info' => $panelData['ipd_info'] ?? null,
|
||||||
'person_info' => $person,
|
'person_info' => $person,
|
||||||
|
|
@ -260,6 +269,7 @@ class IpdPatient extends BaseController
|
||||||
'doctor_visit_fee_map' => $this->ipdBillingModel->getDoctorVisitFeeMap($doctorIds),
|
'doctor_visit_fee_map' => $this->ipdBillingModel->getDoctorVisitFeeMap($doctorIds),
|
||||||
'doc_list' => $docList,
|
'doc_list' => $docList,
|
||||||
'nurse_list' => $nurseList,
|
'nurse_list' => $nurseList,
|
||||||
|
'default_nurse_id' => $defaultNurseId,
|
||||||
'history_fields' => $historyFields,
|
'history_fields' => $historyFields,
|
||||||
'patient_history_row' => $patientHistoryRow,
|
'patient_history_row' => $patientHistoryRow,
|
||||||
'opd_history_snapshot' => $opdHistorySnapshot,
|
'opd_history_snapshot' => $opdHistorySnapshot,
|
||||||
|
|
@ -412,6 +422,12 @@ class IpdPatient extends BaseController
|
||||||
$recordedById = $user->id ?? null;
|
$recordedById = $user->id ?? null;
|
||||||
|
|
||||||
$nurseIdPost = (int) ($this->request->getPost('recorded_by_nurse_id') ?? 0);
|
$nurseIdPost = (int) ($this->request->getPost('recorded_by_nurse_id') ?? 0);
|
||||||
|
if ($nurseIdPost <= 0 && $user && ! empty($user->id)) {
|
||||||
|
$profile = (new \App\Models\UserProfileModel())->getProfile((int) $user->id);
|
||||||
|
if (! empty($profile['nurse_id'])) {
|
||||||
|
$nurseIdPost = (int) $profile['nurse_id'];
|
||||||
|
}
|
||||||
|
}
|
||||||
if ($nurseIdPost > 0) {
|
if ($nurseIdPost > 0) {
|
||||||
$nurseModel = new \App\Models\NurseModel();
|
$nurseModel = new \App\Models\NurseModel();
|
||||||
$nurseRow = $nurseModel->getNurseById($nurseIdPost);
|
$nurseRow = $nurseModel->getNurseById($nurseIdPost);
|
||||||
|
|
|
||||||
|
|
@ -1520,6 +1520,16 @@ class Opd extends BaseController
|
||||||
$data['org_case_master'] = $query->getResult();
|
$data['org_case_master'] = $query->getResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$defaultDocId = 0;
|
||||||
|
$user = function_exists('auth') ? auth()->user() : null;
|
||||||
|
if ($user && ! empty($user->id)) {
|
||||||
|
$profile = (new \App\Models\UserProfileModel())->getProfile((int) $user->id);
|
||||||
|
if (! empty($profile['doctor_id'])) {
|
||||||
|
$defaultDocId = (int) $profile['doctor_id'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$data['default_doc_id'] = $defaultDocId;
|
||||||
|
|
||||||
return view('billing/opd_appointment_V', $data);
|
return view('billing/opd_appointment_V', $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,11 +41,21 @@ class Opdcase extends BaseController
|
||||||
$insuranceId = (int) $row->insurance_id;
|
$insuranceId = (int) $row->insurance_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$defaultDocId = 0;
|
||||||
|
$user = function_exists('auth') ? auth()->user() : null;
|
||||||
|
if ($user && ! empty($user->id)) {
|
||||||
|
$profile = (new \App\Models\UserProfileModel())->getProfile((int) $user->id);
|
||||||
|
if (! empty($profile['doctor_id'])) {
|
||||||
|
$defaultDocId = (int) $profile['doctor_id'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return view('billing/opd_app_insurance_V', [
|
return view('billing/opd_app_insurance_V', [
|
||||||
'person_info' => $data['person_info'],
|
'person_info' => $data['person_info'],
|
||||||
'doc_spec_l' => $data['doc_spec_l'],
|
'doc_spec_l' => $data['doc_spec_l'],
|
||||||
'insurance_cards' => $cards,
|
'insurance_cards' => $cards,
|
||||||
'insurance_id' => $insuranceId,
|
'insurance_id' => $insuranceId,
|
||||||
|
'default_doc_id' => $defaultDocId,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -329,8 +329,16 @@ class BedManagement extends BaseController
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function bedStatus(): string
|
public function bedStatus()
|
||||||
{
|
{
|
||||||
|
if (function_exists('auth') && ! auth()->loggedIn()) {
|
||||||
|
if ($this->request->isAJAX()) {
|
||||||
|
return $this->response->setStatusCode(401)->setBody('Unauthorized');
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->to(base_url('login'));
|
||||||
|
}
|
||||||
|
|
||||||
// ── Stats counters ────────────────────────────────────────────────
|
// ── Stats counters ────────────────────────────────────────────────
|
||||||
$stats = [
|
$stats = [
|
||||||
'total' => 0,
|
'total' => 0,
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,16 @@ namespace App\Controllers\Setting;
|
||||||
|
|
||||||
use App\Controllers\BaseController;
|
use App\Controllers\BaseController;
|
||||||
use App\Libraries\UserSessionRegistry;
|
use App\Libraries\UserSessionRegistry;
|
||||||
|
use App\Models\UserProfileModel;
|
||||||
use CodeIgniter\Shield\Entities\User;
|
use CodeIgniter\Shield\Entities\User;
|
||||||
use CodeIgniter\Shield\Models\UserModel;
|
use CodeIgniter\Shield\Models\UserModel;
|
||||||
|
|
||||||
class UserManagement extends BaseController
|
class UserManagement extends BaseController
|
||||||
{
|
{
|
||||||
|
private function profileModel(): UserProfileModel
|
||||||
|
{
|
||||||
|
return model(UserProfileModel::class);
|
||||||
|
}
|
||||||
private function currentUserId(): int
|
private function currentUserId(): int
|
||||||
{
|
{
|
||||||
return function_exists('auth') ? (int) (auth()->user()->id ?? 0) : 0;
|
return function_exists('auth') ? (int) (auth()->user()->id ?? 0) : 0;
|
||||||
|
|
@ -38,41 +43,60 @@ class UserManagement extends BaseController
|
||||||
|
|
||||||
public function index(): string
|
public function index(): string
|
||||||
{
|
{
|
||||||
$userModel = model(UserModel::class);
|
$profileModel = $this->profileModel();
|
||||||
|
$filters = [
|
||||||
|
'role' => (string) ($this->request->getGet('role') ?? ''),
|
||||||
|
'active' => (string) ($this->request->getGet('active') ?? ''),
|
||||||
|
'search' => (string) ($this->request->getGet('search') ?? ''),
|
||||||
|
];
|
||||||
|
|
||||||
$users = $userModel
|
$users = $profileModel->getUsersList($filters);
|
||||||
->withIdentities()
|
|
||||||
->withGroups()
|
|
||||||
->findAll();
|
|
||||||
|
|
||||||
return view('Setting/Admin/UserManagement/index', [
|
return view('Setting/Admin/UserManagement/index', [
|
||||||
'users' => $users,
|
'users' => $users,
|
||||||
|
'roles' => $this->availableRoles(),
|
||||||
|
'filters' => $filters,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function create(): string
|
public function create(): string
|
||||||
{
|
{
|
||||||
|
$profileModel = $this->profileModel();
|
||||||
|
|
||||||
return view('Setting/Admin/UserManagement/create', [
|
return view('Setting/Admin/UserManagement/create', [
|
||||||
'roles' => $this->availableRoles(),
|
'roles' => $this->availableRoles(),
|
||||||
|
'doctors' => $profileModel->getActiveDoctors(),
|
||||||
|
'nurses' => $profileModel->getActiveNurses(),
|
||||||
|
'selected_doctor_id' => (int) ($this->request->getGet('doctor_id') ?? 0),
|
||||||
|
'selected_nurse_id' => (int) ($this->request->getGet('nurse_id') ?? 0),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function store()
|
public function store()
|
||||||
{
|
{
|
||||||
$rules = [
|
$rules = [
|
||||||
'username' => 'required|min_length[3]|max_length[30]|regex_match[/^[a-zA-Z0-9.]+$/]',
|
'username' => 'required|min_length[3]|max_length[30]|regex_match[/^[a-zA-Z0-9.]+$/]',
|
||||||
'email' => 'required|valid_email|max_length[254]',
|
'email' => 'required|valid_email|max_length[254]',
|
||||||
'password' => 'required|min_length[8]',
|
'password' => 'required|min_length[8]',
|
||||||
'role' => 'required',
|
'role' => 'required',
|
||||||
'person_name' => 'permit_empty|max_length[120]',
|
'person_name' => 'permit_empty|max_length[120]',
|
||||||
'phone_no' => 'permit_empty|regex_match[/^[0-9+()\\s-]{7,20}$/]',
|
'phone_no' => 'permit_empty|regex_match[/^[0-9+()\\s-]{7,20}$/]',
|
||||||
|
'doctor_id' => 'permit_empty|is_natural_no_zero',
|
||||||
|
'nurse_id' => 'permit_empty|is_natural_no_zero',
|
||||||
|
'department' => 'permit_empty|max_length[100]',
|
||||||
|
'designation' => 'permit_empty|max_length[100]',
|
||||||
|
'employee_code' => 'permit_empty|max_length[50]',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
$profileModel = $this->profileModel();
|
||||||
|
|
||||||
if (! $this->validate($rules)) {
|
if (! $this->validate($rules)) {
|
||||||
if ($this->request->isAJAX()) {
|
if ($this->request->isAJAX()) {
|
||||||
return view('Setting/Admin/UserManagement/create', [
|
return view('Setting/Admin/UserManagement/create', [
|
||||||
'roles' => $this->availableRoles(),
|
'roles' => $this->availableRoles(),
|
||||||
'errors' => $this->validator->getErrors(),
|
'doctors' => $profileModel->getActiveDoctors(),
|
||||||
|
'nurses' => $profileModel->getActiveNurses(),
|
||||||
|
'errors' => $this->validator->getErrors(),
|
||||||
'formData' => $this->request->getPost(),
|
'formData' => $this->request->getPost(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
@ -86,8 +110,10 @@ class UserManagement extends BaseController
|
||||||
$errors = ['role' => 'Invalid role selected.'];
|
$errors = ['role' => 'Invalid role selected.'];
|
||||||
if ($this->request->isAJAX()) {
|
if ($this->request->isAJAX()) {
|
||||||
return view('Setting/Admin/UserManagement/create', [
|
return view('Setting/Admin/UserManagement/create', [
|
||||||
'roles' => $this->availableRoles(),
|
'roles' => $this->availableRoles(),
|
||||||
'errors' => $errors,
|
'doctors' => $profileModel->getActiveDoctors(),
|
||||||
|
'nurses' => $profileModel->getActiveNurses(),
|
||||||
|
'errors' => $errors,
|
||||||
'formData' => $this->request->getPost(),
|
'formData' => $this->request->getPost(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
@ -108,8 +134,10 @@ class UserManagement extends BaseController
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return view('Setting/Admin/UserManagement/create', [
|
return view('Setting/Admin/UserManagement/create', [
|
||||||
'roles' => $this->availableRoles(),
|
'roles' => $this->availableRoles(),
|
||||||
'errors' => ['user' => $e->getMessage() !== '' ? $e->getMessage() : 'Unable to create user.'],
|
'doctors' => $profileModel->getActiveDoctors(),
|
||||||
|
'nurses' => $profileModel->getActiveNurses(),
|
||||||
|
'errors' => ['user' => $e->getMessage() !== '' ? $e->getMessage() : 'Unable to create user.'],
|
||||||
'formData' => $this->request->getPost(),
|
'formData' => $this->request->getPost(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
@ -117,26 +145,41 @@ class UserManagement extends BaseController
|
||||||
$savedUser = $userModel->find($userModel->getInsertID());
|
$savedUser = $userModel->find($userModel->getInsertID());
|
||||||
if ($savedUser !== null) {
|
if ($savedUser !== null) {
|
||||||
$savedUser->addGroup($role);
|
$savedUser->addGroup($role);
|
||||||
$this->syncIdentityMeta(
|
|
||||||
(int) $savedUser->id,
|
$personName = trim((string) $this->request->getPost('person_name'));
|
||||||
trim((string) $this->request->getPost('person_name')),
|
$phoneNo = trim((string) $this->request->getPost('phone_no'));
|
||||||
trim((string) $this->request->getPost('phone_no'))
|
$doctorId = (int) $this->request->getPost('doctor_id');
|
||||||
);
|
$nurseId = (int) $this->request->getPost('nurse_id');
|
||||||
|
$department = trim((string) $this->request->getPost('department'));
|
||||||
|
$designation = trim((string) $this->request->getPost('designation'));
|
||||||
|
$employeeCode = trim((string) $this->request->getPost('employee_code'));
|
||||||
|
|
||||||
|
$profileModel->saveProfile((int) $savedUser->id, [
|
||||||
|
'full_name' => $personName,
|
||||||
|
'phone_no' => $phoneNo,
|
||||||
|
'doctor_id' => $doctorId > 0 ? $doctorId : null,
|
||||||
|
'nurse_id' => $nurseId > 0 ? $nurseId : null,
|
||||||
|
'department' => $department,
|
||||||
|
'designation' => $designation,
|
||||||
|
'employee_code' => $employeeCode,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->syncIdentityMeta((int) $savedUser->id, $personName, $phoneNo);
|
||||||
$this->auditClinicalUpdate('user_management', 'create', $savedUser->id, null, [
|
$this->auditClinicalUpdate('user_management', 'create', $savedUser->id, null, [
|
||||||
'username' => $savedUser->username,
|
'username' => $savedUser->username,
|
||||||
'role' => $role,
|
'role' => $role,
|
||||||
'active' => 1,
|
'active' => 1,
|
||||||
|
'doctor_id' => $doctorId > 0 ? $doctorId : null,
|
||||||
|
'nurse_id' => $nurseId > 0 ? $nurseId : null,
|
||||||
|
'department' => $department,
|
||||||
|
'employee_code' => $employeeCode,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->request->isAJAX()) {
|
if ($this->request->isAJAX()) {
|
||||||
$users = $userModel
|
|
||||||
->withIdentities()
|
|
||||||
->withGroups()
|
|
||||||
->findAll();
|
|
||||||
|
|
||||||
return view('Setting/Admin/UserManagement/index', [
|
return view('Setting/Admin/UserManagement/index', [
|
||||||
'users' => $users,
|
'users' => $profileModel->getUsersList(),
|
||||||
|
'roles' => $this->availableRoles(),
|
||||||
'message' => 'User created successfully.',
|
'message' => 'User created successfully.',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
@ -180,36 +223,42 @@ class UserManagement extends BaseController
|
||||||
->withGroups()
|
->withGroups()
|
||||||
->find($userId);
|
->find($userId);
|
||||||
|
|
||||||
if ($user === null) {
|
$profileModel = $this->profileModel();
|
||||||
$users = $userModel
|
|
||||||
->withIdentities()
|
|
||||||
->withGroups()
|
|
||||||
->findAll();
|
|
||||||
|
|
||||||
|
if ($user === null) {
|
||||||
return view('Setting/Admin/UserManagement/index', [
|
return view('Setting/Admin/UserManagement/index', [
|
||||||
'users' => $users,
|
'users' => $profileModel->getUsersList(),
|
||||||
'errors' => ['User not found.'],
|
'roles' => $this->availableRoles(),
|
||||||
|
'errors' => ['User not found.'],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->isPrivilegedUser($user) && ! $this->canManageAdmins()) {
|
if ($this->isPrivilegedUser($user) && ! $this->canManageAdmins()) {
|
||||||
return view('Setting/Admin/UserManagement/index', [
|
return view('Setting/Admin/UserManagement/index', [
|
||||||
'users' => $userModel->withIdentities()->withGroups()->findAll(),
|
'users' => $profileModel->getUsersList(),
|
||||||
'errors' => ['You cannot edit an administrator account.'],
|
'roles' => $this->availableRoles(),
|
||||||
|
'errors' => ['You cannot edit an administrator account.'],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$emailIdentity = $user->getEmailIdentity();
|
$emailIdentity = $user->getEmailIdentity();
|
||||||
$email = $emailIdentity ? (string) ($emailIdentity->secret ?? '') : '';
|
$email = $emailIdentity ? (string) ($emailIdentity->secret ?? '') : '';
|
||||||
$meta = $this->decodeIdentityExtra($emailIdentity->extra ?? null);
|
$profile = $profileModel->getProfile($userId);
|
||||||
|
|
||||||
return view('Setting/Admin/UserManagement/edit', [
|
return view('Setting/Admin/UserManagement/edit', [
|
||||||
'user' => $user,
|
'user' => $user,
|
||||||
'email' => $email,
|
'email' => $email,
|
||||||
'person_name' => trim((string) ($meta['full_name'] ?? '')),
|
'person_name' => trim((string) ($profile['full_name'] ?? '')),
|
||||||
'phone_no' => trim((string) ($meta['phone_no'] ?? '')),
|
'phone_no' => trim((string) ($profile['phone_no'] ?? '')),
|
||||||
'roles' => $this->availableRoles(),
|
'doctor_id' => (int) ($profile['doctor_id'] ?? 0),
|
||||||
'current_role' => (string) (($user->getGroups() ?? [])[0] ?? ''),
|
'nurse_id' => (int) ($profile['nurse_id'] ?? 0),
|
||||||
|
'department' => trim((string) ($profile['department'] ?? '')),
|
||||||
|
'designation' => trim((string) ($profile['designation'] ?? '')),
|
||||||
|
'employee_code' => trim((string) ($profile['employee_code'] ?? '')),
|
||||||
|
'roles' => $this->availableRoles(),
|
||||||
|
'current_role' => (string) (($user->getGroups() ?? [])[0] ?? ''),
|
||||||
|
'doctors' => $profileModel->getActiveDoctors(),
|
||||||
|
'nurses' => $profileModel->getActiveNurses(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -217,22 +266,21 @@ class UserManagement extends BaseController
|
||||||
{
|
{
|
||||||
$userModel = model(UserModel::class);
|
$userModel = model(UserModel::class);
|
||||||
$user = $userModel->withGroups()->find($userId);
|
$user = $userModel->withGroups()->find($userId);
|
||||||
if ($user === null) {
|
$profileModel = $this->profileModel();
|
||||||
$users = $userModel
|
|
||||||
->withIdentities()
|
|
||||||
->withGroups()
|
|
||||||
->findAll();
|
|
||||||
|
|
||||||
|
if ($user === null) {
|
||||||
return view('Setting/Admin/UserManagement/index', [
|
return view('Setting/Admin/UserManagement/index', [
|
||||||
'users' => $users,
|
'users' => $profileModel->getUsersList(),
|
||||||
'errors' => ['User not found.'],
|
'roles' => $this->availableRoles(),
|
||||||
|
'errors' => ['User not found.'],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->isPrivilegedUser($user) && ! $this->canManageAdmins()) {
|
if ($this->isPrivilegedUser($user) && ! $this->canManageAdmins()) {
|
||||||
return view('Setting/Admin/UserManagement/index', [
|
return view('Setting/Admin/UserManagement/index', [
|
||||||
'users' => $userModel->withIdentities()->withGroups()->findAll(),
|
'users' => $profileModel->getUsersList(),
|
||||||
'errors' => ['You cannot edit an administrator account.'],
|
'roles' => $this->availableRoles(),
|
||||||
|
'errors' => ['You cannot edit an administrator account.'],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -240,14 +288,19 @@ class UserManagement extends BaseController
|
||||||
$email = trim((string) $this->request->getPost('email'));
|
$email = trim((string) $this->request->getPost('email'));
|
||||||
$personName = trim((string) $this->request->getPost('person_name'));
|
$personName = trim((string) $this->request->getPost('person_name'));
|
||||||
$phoneNo = trim((string) $this->request->getPost('phone_no'));
|
$phoneNo = trim((string) $this->request->getPost('phone_no'));
|
||||||
|
$doctorId = (int) $this->request->getPost('doctor_id');
|
||||||
|
$nurseId = (int) $this->request->getPost('nurse_id');
|
||||||
|
$department = trim((string) $this->request->getPost('department'));
|
||||||
|
$designation = trim((string) $this->request->getPost('designation'));
|
||||||
|
$employeeCode = trim((string) $this->request->getPost('employee_code'));
|
||||||
$password = (string) $this->request->getPost('password');
|
$password = (string) $this->request->getPost('password');
|
||||||
$active = $this->request->getPost('active') ? 1 : 0;
|
$active = $this->request->getPost('active') ? 1 : 0;
|
||||||
$role = strtolower(trim((string) $this->request->getPost('role')));
|
$role = strtolower(trim((string) $this->request->getPost('role')));
|
||||||
$oldGroups = $user->getGroups() ?? [];
|
$oldGroups = $user->getGroups() ?? [];
|
||||||
$oldState = [
|
$oldState = [
|
||||||
'username' => (string) ($user->username ?? ''),
|
'username' => (string) ($user->username ?? ''),
|
||||||
'groups' => $oldGroups,
|
'groups' => $oldGroups,
|
||||||
'active' => (int) ($user->active ?? 0),
|
'active' => (int) ($user->active ?? 0),
|
||||||
];
|
];
|
||||||
|
|
||||||
$errors = [];
|
$errors = [];
|
||||||
|
|
@ -302,20 +355,32 @@ class UserManagement extends BaseController
|
||||||
|
|
||||||
if (! empty($errors)) {
|
if (! empty($errors)) {
|
||||||
return view('Setting/Admin/UserManagement/edit', [
|
return view('Setting/Admin/UserManagement/edit', [
|
||||||
'user' => $userModel->withIdentities()->find($userId),
|
'user' => $userModel->withIdentities()->find($userId),
|
||||||
'email' => $email,
|
'email' => $email,
|
||||||
'person_name' => $personName,
|
'person_name' => $personName,
|
||||||
'phone_no' => $phoneNo,
|
'phone_no' => $phoneNo,
|
||||||
'roles' => $this->availableRoles(),
|
'doctor_id' => $doctorId,
|
||||||
'current_role' => $role,
|
'nurse_id' => $nurseId,
|
||||||
'errors' => $errors,
|
'department' => $department,
|
||||||
'formData' => [
|
'designation' => $designation,
|
||||||
'username' => $username,
|
'employee_code' => $employeeCode,
|
||||||
'email' => $email,
|
'roles' => $this->availableRoles(),
|
||||||
'person_name' => $personName,
|
'current_role' => $role,
|
||||||
'phone_no' => $phoneNo,
|
'doctors' => $profileModel->getActiveDoctors(),
|
||||||
'active' => $active,
|
'nurses' => $profileModel->getActiveNurses(),
|
||||||
'role' => $role,
|
'errors' => $errors,
|
||||||
|
'formData' => [
|
||||||
|
'username' => $username,
|
||||||
|
'email' => $email,
|
||||||
|
'person_name' => $personName,
|
||||||
|
'phone_no' => $phoneNo,
|
||||||
|
'doctor_id' => $doctorId,
|
||||||
|
'nurse_id' => $nurseId,
|
||||||
|
'department' => $department,
|
||||||
|
'designation' => $designation,
|
||||||
|
'employee_code' => $employeeCode,
|
||||||
|
'active' => $active,
|
||||||
|
'role' => $role,
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
@ -329,24 +394,35 @@ class UserManagement extends BaseController
|
||||||
|
|
||||||
$userModel->save($user);
|
$userModel->save($user);
|
||||||
$user->syncGroups($role);
|
$user->syncGroups($role);
|
||||||
|
|
||||||
|
$profileModel->saveProfile($userId, [
|
||||||
|
'full_name' => $personName,
|
||||||
|
'phone_no' => $phoneNo,
|
||||||
|
'doctor_id' => $doctorId > 0 ? $doctorId : null,
|
||||||
|
'nurse_id' => $nurseId > 0 ? $nurseId : null,
|
||||||
|
'department' => $department,
|
||||||
|
'designation' => $designation,
|
||||||
|
'employee_code' => $employeeCode,
|
||||||
|
]);
|
||||||
|
|
||||||
$this->syncIdentityMeta($userId, $personName, $phoneNo);
|
$this->syncIdentityMeta($userId, $personName, $phoneNo);
|
||||||
$this->auditClinicalUpdate('user_management', 'update', $userId, $oldState, [
|
$this->auditClinicalUpdate('user_management', 'update', $userId, $oldState, [
|
||||||
'username' => $username,
|
'username' => $username,
|
||||||
'groups' => [$role],
|
'groups' => [$role],
|
||||||
'active' => $active,
|
'active' => $active,
|
||||||
|
'doctor_id' => $doctorId > 0 ? $doctorId : null,
|
||||||
|
'nurse_id' => $nurseId > 0 ? $nurseId : null,
|
||||||
|
'department' => $department,
|
||||||
|
'employee_code' => $employeeCode,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($active !== 1) {
|
if ($active !== 1) {
|
||||||
(new UserSessionRegistry($this->db))->revokeUser($userId, $this->currentUserId(), 'account_deactivated');
|
(new UserSessionRegistry($this->db))->revokeUser($userId, $this->currentUserId(), 'account_deactivated');
|
||||||
}
|
}
|
||||||
|
|
||||||
$users = $userModel
|
|
||||||
->withIdentities()
|
|
||||||
->withGroups()
|
|
||||||
->findAll();
|
|
||||||
|
|
||||||
return view('Setting/Admin/UserManagement/index', [
|
return view('Setting/Admin/UserManagement/index', [
|
||||||
'users' => $users,
|
'users' => $profileModel->getUsersList(),
|
||||||
|
'roles' => $this->availableRoles(),
|
||||||
'message' => 'User updated successfully.',
|
'message' => 'User updated successfully.',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
@ -707,21 +783,79 @@ class UserManagement extends BaseController
|
||||||
return $this->response->setStatusCode(403)->setJSON(['update' => 0, 'error_text' => 'You cannot delete an administrator.']);
|
return $this->response->setStatusCode(403)->setJSON(['update' => 0, 'error_text' => 'You cannot delete an administrator.']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deactivate first to ensure no lingering access
|
||||||
|
$user->active = 0;
|
||||||
|
$userModel->save($user);
|
||||||
|
|
||||||
(new UserSessionRegistry($this->db))->revokeUser($userId, $this->currentUserId(), 'account_deleted');
|
(new UserSessionRegistry($this->db))->revokeUser($userId, $this->currentUserId(), 'account_deleted');
|
||||||
$this->auditClinicalUpdate('user_management', 'delete', $userId, [
|
$this->auditClinicalUpdate('user_management', 'delete', $userId, [
|
||||||
'username' => $user->username,
|
'username' => $user->username,
|
||||||
'groups' => $user->getGroups() ?? [],
|
'groups' => $user->getGroups() ?? [],
|
||||||
], ['deleted' => true]);
|
], ['deleted' => true, 'soft_delete' => true]);
|
||||||
|
|
||||||
if (! $userModel->delete($userId)) {
|
if (! $userModel->delete($userId)) {
|
||||||
return $this->response->setJSON(['update' => 0, 'error_text' => 'Unable to delete user.']);
|
return $this->response->setJSON(['update' => 0, 'error_text' => 'Unable to delete user.']);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->response->setJSON([
|
return $this->response->setJSON([
|
||||||
'update' => 1,
|
'update' => 1,
|
||||||
'error_text' => 'User deleted successfully.',
|
'error_text' => 'User deactivated and removed from active list.',
|
||||||
'csrfName' => csrf_token(),
|
'csrfName' => csrf_token(),
|
||||||
'csrfHash' => csrf_hash(),
|
'csrfHash' => csrf_hash(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toggleStatus(int $userId)
|
||||||
|
{
|
||||||
|
if ($userId === $this->currentUserId()) {
|
||||||
|
return $this->response->setStatusCode(400)->setJSON([
|
||||||
|
'success' => 0,
|
||||||
|
'error_text' => 'You cannot deactivate your own account.',
|
||||||
|
'csrfName' => csrf_token(),
|
||||||
|
'csrfHash' => csrf_hash(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$userModel = model(UserModel::class);
|
||||||
|
$user = $userModel->withGroups()->find($userId);
|
||||||
|
if ($user === null) {
|
||||||
|
return $this->response->setStatusCode(404)->setJSON([
|
||||||
|
'success' => 0,
|
||||||
|
'error_text' => 'User not found.',
|
||||||
|
'csrfName' => csrf_token(),
|
||||||
|
'csrfHash' => csrf_hash(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->isPrivilegedUser($user) && ! $this->canManageAdmins()) {
|
||||||
|
return $this->response->setStatusCode(403)->setJSON([
|
||||||
|
'success' => 0,
|
||||||
|
'error_text' => 'You cannot change the status of an administrator account.',
|
||||||
|
'csrfName' => csrf_token(),
|
||||||
|
'csrfHash' => csrf_hash(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$newActive = (int) ($user->active ?? 0) === 1 ? 0 : 1;
|
||||||
|
$user->active = $newActive;
|
||||||
|
$userModel->save($user);
|
||||||
|
|
||||||
|
if ($newActive === 0) {
|
||||||
|
(new UserSessionRegistry($this->db))->revokeUser($userId, $this->currentUserId(), 'account_deactivated');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->auditClinicalUpdate('user_management', 'status_toggle', $userId, [
|
||||||
|
'active' => 1 - $newActive,
|
||||||
|
], [
|
||||||
|
'active' => $newActive,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $this->response->setJSON([
|
||||||
|
'success' => 1,
|
||||||
|
'active' => $newActive,
|
||||||
|
'message' => $newActive === 1 ? 'User activated successfully.' : 'User deactivated and active session revoked.',
|
||||||
|
'csrfName' => csrf_token(),
|
||||||
|
'csrfHash' => csrf_hash(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,132 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Database\Migrations;
|
||||||
|
|
||||||
|
use CodeIgniter\Database\Migration;
|
||||||
|
|
||||||
|
class CreateUserProfilesTable extends Migration
|
||||||
|
{
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
if (! $this->db->tableExists('user_profiles')) {
|
||||||
|
$this->forge->addField([
|
||||||
|
'id' => [
|
||||||
|
'type' => 'BIGINT',
|
||||||
|
'unsigned' => true,
|
||||||
|
'auto_increment' => true,
|
||||||
|
],
|
||||||
|
'user_id' => [
|
||||||
|
'type' => 'INT',
|
||||||
|
'unsigned' => true,
|
||||||
|
],
|
||||||
|
'full_name' => [
|
||||||
|
'type' => 'VARCHAR',
|
||||||
|
'constraint' => 120,
|
||||||
|
'null' => true,
|
||||||
|
],
|
||||||
|
'phone_no' => [
|
||||||
|
'type' => 'VARCHAR',
|
||||||
|
'constraint' => 20,
|
||||||
|
'null' => true,
|
||||||
|
],
|
||||||
|
'doctor_id' => [
|
||||||
|
'type' => 'INT',
|
||||||
|
'unsigned' => true,
|
||||||
|
'null' => true,
|
||||||
|
],
|
||||||
|
'nurse_id' => [
|
||||||
|
'type' => 'INT',
|
||||||
|
'unsigned' => true,
|
||||||
|
'null' => true,
|
||||||
|
],
|
||||||
|
'department' => [
|
||||||
|
'type' => 'VARCHAR',
|
||||||
|
'constraint' => 100,
|
||||||
|
'null' => true,
|
||||||
|
],
|
||||||
|
'designation' => [
|
||||||
|
'type' => 'VARCHAR',
|
||||||
|
'constraint' => 100,
|
||||||
|
'null' => true,
|
||||||
|
],
|
||||||
|
'employee_code' => [
|
||||||
|
'type' => 'VARCHAR',
|
||||||
|
'constraint' => 50,
|
||||||
|
'null' => true,
|
||||||
|
],
|
||||||
|
'avatar' => [
|
||||||
|
'type' => 'VARCHAR',
|
||||||
|
'constraint' => 255,
|
||||||
|
'null' => true,
|
||||||
|
],
|
||||||
|
'created_at' => [
|
||||||
|
'type' => 'DATETIME',
|
||||||
|
'null' => true,
|
||||||
|
],
|
||||||
|
'updated_at' => [
|
||||||
|
'type' => 'DATETIME',
|
||||||
|
'null' => true,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->forge->addPrimaryKey('id');
|
||||||
|
$this->forge->addUniqueKey('user_id', 'uk_user_profiles_user_id');
|
||||||
|
$this->forge->addKey('doctor_id', false, false, 'idx_user_profiles_doctor_id');
|
||||||
|
$this->forge->addKey('nurse_id', false, false, 'idx_user_profiles_nurse_id');
|
||||||
|
$this->forge->addKey('phone_no', false, false, 'idx_user_profiles_phone_no');
|
||||||
|
$this->forge->addKey('employee_code', false, false, 'idx_user_profiles_employee_code');
|
||||||
|
$this->forge->createTable('user_profiles', true, ['ENGINE' => 'InnoDB']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backfill existing users from auth_identities.extra
|
||||||
|
$users = $this->db->table('users')->get()->getResultArray();
|
||||||
|
$now = date('Y-m-d H:i:s');
|
||||||
|
|
||||||
|
foreach ($users as $user) {
|
||||||
|
$userId = (int) $user['id'];
|
||||||
|
$existing = $this->db->table('user_profiles')->where('user_id', $userId)->get(1)->getRowArray();
|
||||||
|
if ($existing !== null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$identity = $this->db->table('auth_identities')
|
||||||
|
->where('user_id', $userId)
|
||||||
|
->where('type', 'email_password')
|
||||||
|
->get(1)
|
||||||
|
->getRowArray();
|
||||||
|
|
||||||
|
$fullName = null;
|
||||||
|
$phoneNo = null;
|
||||||
|
$avatar = null;
|
||||||
|
|
||||||
|
if (! empty($identity['extra'])) {
|
||||||
|
$extra = $identity['extra'];
|
||||||
|
if (is_string($extra)) {
|
||||||
|
$decoded = json_decode($extra, true);
|
||||||
|
if (is_array($decoded)) {
|
||||||
|
$extra = $decoded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is_array($extra)) {
|
||||||
|
$fullName = ! empty($extra['full_name']) ? trim((string) $extra['full_name']) : null;
|
||||||
|
$phoneNo = ! empty($extra['phone_no']) ? trim((string) $extra['phone_no']) : null;
|
||||||
|
$avatar = ! empty($extra['profile_photo']) ? trim((string) $extra['profile_photo']) : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->db->table('user_profiles')->insert([
|
||||||
|
'user_id' => $userId,
|
||||||
|
'full_name' => $fullName,
|
||||||
|
'phone_no' => $phoneNo,
|
||||||
|
'avatar' => $avatar,
|
||||||
|
'created_at' => $now,
|
||||||
|
'updated_at' => $now,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
$this->forge->dropTable('user_profiles', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -27,7 +27,16 @@ class DoctorModel
|
||||||
|
|
||||||
public function getDoctors(): array
|
public function getDoctors(): array
|
||||||
{
|
{
|
||||||
return $this->db->table('doctor_master')->get()->getResult();
|
$builder = $this->db->table('doctor_master d');
|
||||||
|
if ($this->db->tableExists('user_profiles')) {
|
||||||
|
$builder->select('d.*, u.id as linked_user_id, u.username as linked_username, u.active as linked_user_active')
|
||||||
|
->join('user_profiles up', 'up.doctor_id = d.id', 'left')
|
||||||
|
->join('users u', 'u.id = up.user_id AND u.deleted_at IS NULL', 'left');
|
||||||
|
} else {
|
||||||
|
$builder->select('d.*');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $builder->get()->getResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDoctorById(int $id): array
|
public function getDoctorById(int $id): array
|
||||||
|
|
|
||||||
|
|
@ -61,18 +61,27 @@ class NurseModel
|
||||||
|
|
||||||
public function getNurses(string $q = ''): array
|
public function getNurses(string $q = ''): array
|
||||||
{
|
{
|
||||||
$builder = $this->db->table('nurse_master');
|
$builder = $this->db->table('nurse_master n');
|
||||||
|
if ($this->db->tableExists('user_profiles')) {
|
||||||
|
$builder->select('n.*, u.id as linked_user_id, u.username as linked_username, u.active as linked_user_active')
|
||||||
|
->join('user_profiles up', 'up.nurse_id = n.id', 'left')
|
||||||
|
->join('users u', 'u.id = up.user_id AND u.deleted_at IS NULL', 'left');
|
||||||
|
} else {
|
||||||
|
$builder->select('n.*');
|
||||||
|
}
|
||||||
|
|
||||||
if ($q !== '') {
|
if ($q !== '') {
|
||||||
$builder->groupStart()
|
$builder->groupStart()
|
||||||
->like('name', $q)
|
->like('n.name', $q)
|
||||||
->orLike('nurse_code', $q)
|
->orLike('n.nurse_code', $q)
|
||||||
->orLike('hpr_id', $q)
|
->orLike('n.hpr_id', $q)
|
||||||
->orLike('registration_no', $q)
|
->orLike('n.registration_no', $q)
|
||||||
->orLike('qualification', $q)
|
->orLike('n.qualification', $q)
|
||||||
->orLike('contact_no', $q)
|
->orLike('n.contact_no', $q)
|
||||||
->groupEnd();
|
->groupEnd();
|
||||||
}
|
}
|
||||||
return $builder->orderBy('name', 'ASC')->get()->getResultArray();
|
|
||||||
|
return $builder->orderBy('n.name', 'ASC')->get()->getResultArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getActiveNurses(): array
|
public function getActiveNurses(): array
|
||||||
|
|
|
||||||
212
app/Models/UserProfileModel.php
Normal file
212
app/Models/UserProfileModel.php
Normal file
|
|
@ -0,0 +1,212 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use CodeIgniter\Model;
|
||||||
|
|
||||||
|
class UserProfileModel extends Model
|
||||||
|
{
|
||||||
|
protected $table = 'user_profiles';
|
||||||
|
protected $primaryKey = 'id';
|
||||||
|
protected $useAutoIncrement = true;
|
||||||
|
protected $returnType = 'array';
|
||||||
|
protected $useSoftDeletes = false;
|
||||||
|
protected $protectFields = true;
|
||||||
|
protected $allowedFields = [
|
||||||
|
'user_id',
|
||||||
|
'full_name',
|
||||||
|
'phone_no',
|
||||||
|
'doctor_id',
|
||||||
|
'nurse_id',
|
||||||
|
'department',
|
||||||
|
'designation',
|
||||||
|
'employee_code',
|
||||||
|
'avatar',
|
||||||
|
'created_at',
|
||||||
|
'updated_at',
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $useTimestamps = true;
|
||||||
|
protected $createdField = 'created_at';
|
||||||
|
protected $updatedField = 'updated_at';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get profile for a specific user.
|
||||||
|
*/
|
||||||
|
public function getProfile(int $userId): ?array
|
||||||
|
{
|
||||||
|
if ($userId <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->where('user_id', $userId)->first();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save or update user profile.
|
||||||
|
*/
|
||||||
|
public function saveProfile(int $userId, array $data): bool
|
||||||
|
{
|
||||||
|
if ($userId <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$existing = $this->where('user_id', $userId)->first();
|
||||||
|
$fields = [
|
||||||
|
'full_name' => isset($data['full_name']) ? trim((string) $data['full_name']) : null,
|
||||||
|
'phone_no' => isset($data['phone_no']) ? trim((string) $data['phone_no']) : null,
|
||||||
|
'doctor_id' => ! empty($data['doctor_id']) ? (int) $data['doctor_id'] : null,
|
||||||
|
'nurse_id' => ! empty($data['nurse_id']) ? (int) $data['nurse_id'] : null,
|
||||||
|
'department' => isset($data['department']) ? trim((string) $data['department']) : null,
|
||||||
|
'designation' => isset($data['designation']) ? trim((string) $data['designation']) : null,
|
||||||
|
'employee_code' => isset($data['employee_code']) ? trim((string) $data['employee_code']) : null,
|
||||||
|
];
|
||||||
|
|
||||||
|
if (isset($data['avatar'])) {
|
||||||
|
$fields['avatar'] = trim((string) $data['avatar']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($existing) {
|
||||||
|
return (bool) $this->update($existing['id'], $fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
$fields['user_id'] = $userId;
|
||||||
|
return (bool) $this->insert($fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete profile for a specific user.
|
||||||
|
*/
|
||||||
|
public function deleteProfile(int $userId): bool
|
||||||
|
{
|
||||||
|
if ($userId <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (bool) $this->where('user_id', $userId)->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get users with full profile, clinical link, and identity information.
|
||||||
|
*/
|
||||||
|
public function getUsersList(array $filters = []): array
|
||||||
|
{
|
||||||
|
$builder = $this->db->table('users u')
|
||||||
|
->select('u.id, u.username, u.active, u.status, u.created_at, u.deleted_at')
|
||||||
|
->select('p.full_name, p.phone_no, p.doctor_id, p.nurse_id, p.department, p.designation, p.employee_code, p.avatar')
|
||||||
|
->select('ai.secret as email, ai.force_reset')
|
||||||
|
->select('(CASE WHEN p.doctor_id IS NOT NULL AND d.id IS NOT NULL THEN TRIM(CONCAT(COALESCE(d.p_title, "Dr."), " ", COALESCE(d.p_fname, ""), " ", COALESCE(d.p_lname, ""))) ELSE NULL END) as linked_doctor_name')
|
||||||
|
->select('d.doctor_reg_no as linked_doctor_reg')
|
||||||
|
->select('(CASE WHEN p.nurse_id IS NOT NULL AND n.id IS NOT NULL THEN n.name ELSE NULL END) as linked_nurse_name, n.nurse_code as linked_nurse_code')
|
||||||
|
->join('user_profiles p', 'p.user_id = u.id', 'left')
|
||||||
|
->join('auth_identities ai', 'ai.user_id = u.id AND ai.type = "email_password"', 'left')
|
||||||
|
->join('doctor_master d', 'd.id = p.doctor_id', 'left')
|
||||||
|
->join('nurse_master n', 'n.id = p.nurse_id', 'left');
|
||||||
|
|
||||||
|
// By default show non-deleted users
|
||||||
|
if (! empty($filters['include_deleted'])) {
|
||||||
|
// Include soft-deleted
|
||||||
|
} else {
|
||||||
|
$builder->where('u.deleted_at IS NULL');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($filters['active']) && $filters['active'] !== '') {
|
||||||
|
$builder->where('u.active', (int) $filters['active']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! empty($filters['search'])) {
|
||||||
|
$s = trim((string) $filters['search']);
|
||||||
|
$builder->groupStart()
|
||||||
|
->like('u.username', $s)
|
||||||
|
->orLike('p.full_name', $s)
|
||||||
|
->orLike('p.phone_no', $s)
|
||||||
|
->orLike('ai.secret', $s)
|
||||||
|
->orLike('p.employee_code', $s)
|
||||||
|
->orLike('p.department', $s)
|
||||||
|
->groupEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
$builder->orderBy('u.id', 'ASC');
|
||||||
|
$users = $builder->get()->getResultArray();
|
||||||
|
|
||||||
|
// Attach groups/roles
|
||||||
|
$groups = $this->db->table('auth_groups_users')->get()->getResultArray();
|
||||||
|
$userGroups = [];
|
||||||
|
foreach ($groups as $g) {
|
||||||
|
$userGroups[(int) $g['user_id']][] = $g['group'];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($users as &$user) {
|
||||||
|
$uid = (int) $user['id'];
|
||||||
|
$user['groups'] = $userGroups[$uid] ?? [];
|
||||||
|
}
|
||||||
|
unset($user);
|
||||||
|
|
||||||
|
// Filter by role if requested
|
||||||
|
if (! empty($filters['role'])) {
|
||||||
|
$roleFilter = strtolower(trim((string) $filters['role']));
|
||||||
|
$users = array_values(array_filter($users, fn($u) => in_array($roleFilter, $u['groups'], true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $users;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get active doctors for dropdown mapping.
|
||||||
|
*/
|
||||||
|
public function getActiveDoctors(): array
|
||||||
|
{
|
||||||
|
if (! $this->db->tableExists('doctor_master')) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->db->table('doctor_master')
|
||||||
|
->select('id, p_title, p_fname, p_lname, email1, mphone1, doctor_reg_no, active')
|
||||||
|
->where('active', '1')
|
||||||
|
->orWhere('active', 1)
|
||||||
|
->orderBy('p_fname', 'ASC')
|
||||||
|
->get()
|
||||||
|
->getResultArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get active nurses for dropdown mapping.
|
||||||
|
*/
|
||||||
|
public function getActiveNurses(): array
|
||||||
|
{
|
||||||
|
if (! $this->db->tableExists('nurse_master')) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->db->table('nurse_master')
|
||||||
|
->select('id, nurse_code, name, email, contact_no, designation, is_active')
|
||||||
|
->where('is_active', 1)
|
||||||
|
->orderBy('name', 'ASC')
|
||||||
|
->get()
|
||||||
|
->getResultArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get user profile by linked doctor ID.
|
||||||
|
*/
|
||||||
|
public function getByDoctorId(int $doctorId): ?array
|
||||||
|
{
|
||||||
|
if ($doctorId <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->where('doctor_id', $doctorId)->first();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get user profile by linked nurse ID.
|
||||||
|
*/
|
||||||
|
public function getByNurseId(int $nurseId): ?array
|
||||||
|
{
|
||||||
|
if ($nurseId <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->where('nurse_id', $nurseId)->first();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,88 +1,239 @@
|
||||||
|
<div class="card shadow-sm border-0">
|
||||||
|
<div class="card-header bg-white py-3 d-flex align-items-center justify-content-between flex-wrap gap-2 border-bottom">
|
||||||
<div class="card">
|
<div>
|
||||||
<div class="card-header d-flex align-items-center justify-content-between flex-wrap gap-2">
|
<h4 class="card-title mb-0 fw-bold text-dark">New User & Staff Account</h4>
|
||||||
<h3 class="card-title mb-0">New Users</h3>
|
<div class="text-muted small">Create credentials, assign baseline role, and optionally link to a Doctor or Nurse profile</div>
|
||||||
<div class="card-tools ms-auto">
|
|
||||||
<button class="btn btn-light" type="button" onclick="load_form_div('<?= base_url('setting/admin/user-management') ?>','maindiv','User Management');">
|
|
||||||
<i class="bi bi-arrow-left"></i>
|
|
||||||
Back to User List
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-tools ms-auto">
|
||||||
<?php $errors = $errors ?? session('errors'); ?>
|
<button class="btn btn-outline-secondary" type="button" onclick="load_form_div('<?= base_url('setting/admin/user-management') ?>','maindiv','User Management');">
|
||||||
<?php $formData = $formData ?? []; ?>
|
<i class="bi bi-arrow-left"></i> Back to User List
|
||||||
<?php foreach ((array) $errors as $error) : ?><span class="d-none" data-page-notification data-notification-type="error" data-notification-title="Create User" data-notification-message="<?= esc((string) $error, 'attr') ?>"></span><?php endforeach ?>
|
</button>
|
||||||
<form class="needs-validation" novalidate action="<?= base_url('setting/admin/user-management/new') ?>" method="post">
|
</div>
|
||||||
<?= csrf_field() ?>
|
</div>
|
||||||
|
<div class="card-body p-4">
|
||||||
|
<?php $errors = $errors ?? session('errors'); ?>
|
||||||
|
<?php $formData = $formData ?? []; ?>
|
||||||
|
<?php foreach ((array) $errors as $error) : ?><span class="d-none" data-page-notification data-notification-type="error" data-notification-title="Create User" data-notification-message="<?= esc((string) $error, 'attr') ?>"></span><?php endforeach ?>
|
||||||
|
|
||||||
|
<form id="frm_user_create" class="needs-validation" novalidate action="<?= base_url('setting/admin/user-management/new') ?>" method="post">
|
||||||
|
<?= csrf_field() ?>
|
||||||
|
|
||||||
|
<!-- Section 1: Account Credentials -->
|
||||||
|
<div class="mb-4">
|
||||||
|
<h6 class="fw-bold text-primary text-uppercase border-bottom pb-2 mb-3">
|
||||||
|
<i class="bi bi-shield-lock"></i> Account Credentials & Baseline Role
|
||||||
|
</h6>
|
||||||
<div class="row g-3">
|
<div class="row g-3">
|
||||||
<div class="col-md-6">
|
<div class="col-md-4">
|
||||||
<label class="form-label" for="username">Username</label>
|
<label class="form-label fw-semibold" for="username">Login ID / Username <span class="text-danger">*</span></label>
|
||||||
<input class="form-control" id="username" name="username" type="text" value="<?= esc($formData['username'] ?? old('username')) ?>" required>
|
<input class="form-control" id="username" name="username" type="text" maxlength="30" placeholder="e.g. dr.sharma or rajesh.k" value="<?= esc($formData['username'] ?? old('username')) ?>" required>
|
||||||
<div class="invalid-feedback">Username is required.</div>
|
<div class="form-text">3-30 characters, letters, numbers, and dot.</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-4">
|
||||||
<label class="form-label" for="email">Email</label>
|
<label class="form-label fw-semibold" for="email">Email Address <span class="text-danger">*</span></label>
|
||||||
<input class="form-control" id="email" name="email" type="email" value="<?= esc($formData['email'] ?? old('email')) ?>" required>
|
<input class="form-control" id="email" name="email" type="email" placeholder="staff@hospital.com" value="<?= esc($formData['email'] ?? old('email')) ?>" required>
|
||||||
<div class="invalid-feedback">Email is required.</div>
|
<div class="form-text">Used for notifications and password recovery.</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-4">
|
||||||
<label class="form-label" for="person_name">Person Name</label>
|
<label class="form-label fw-semibold" for="password">Password <span class="text-danger">*</span></label>
|
||||||
<input class="form-control" id="person_name" name="person_name" type="text" maxlength="120" value="<?= esc($formData['person_name'] ?? old('person_name')) ?>">
|
<input class="form-control" id="password" name="password" type="password" minlength="8" placeholder="Minimum 8 characters" required>
|
||||||
|
<div class="form-text">At least 8 characters.</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
|
||||||
<label class="form-label" for="phone_no">Phone No.</label>
|
<?php
|
||||||
<input class="form-control" id="phone_no" name="phone_no" type="text" maxlength="20" value="<?= esc($formData['phone_no'] ?? old('phone_no')) ?>">
|
$defaultRole = $formData['role'] ?? old('role');
|
||||||
</div>
|
if (empty($defaultRole) && ! empty($selected_doctor_id)) {
|
||||||
<div class="col-md-6">
|
$defaultRole = 'doctor';
|
||||||
<label class="form-label" for="password">Password</label>
|
} elseif (empty($defaultRole) && ! empty($selected_nurse_id)) {
|
||||||
<input class="form-control" id="password" name="password" type="password" required>
|
$defaultRole = 'nurse';
|
||||||
<div class="invalid-feedback">Password is required.</div>
|
}
|
||||||
</div>
|
$defaultDoctorId = (int) ($formData['doctor_id'] ?? old('doctor_id') ?? $selected_doctor_id ?? 0);
|
||||||
<div class="col-md-6">
|
$defaultNurseId = (int) ($formData['nurse_id'] ?? old('nurse_id') ?? $selected_nurse_id ?? 0);
|
||||||
<label class="form-label" for="role">Role</label>
|
?>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label class="form-label fw-semibold" for="role">Assigned Role <span class="text-danger">*</span></label>
|
||||||
<select class="form-select" id="role" name="role" required>
|
<select class="form-select" id="role" name="role" required>
|
||||||
<option value="">Select role</option>
|
<option value="">Select role</option>
|
||||||
<?php if (! empty($roles)) : ?>
|
<?php if (! empty($roles)) : ?>
|
||||||
<?php foreach ($roles as $roleKey => $roleInfo) : ?>
|
<?php foreach ($roles as $roleKey => $roleInfo) : ?>
|
||||||
<option value="<?= esc($roleKey) ?>" <?= ($formData['role'] ?? old('role')) === $roleKey ? 'selected' : '' ?>>
|
<option value="<?= esc($roleKey) ?>" <?= $defaultRole === $roleKey ? 'selected' : '' ?>>
|
||||||
<?= esc($roleInfo['title'] ?? $roleKey) ?>
|
<?= esc($roleInfo['title'] ?? $roleKey) ?>
|
||||||
</option>
|
</option>
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
</select>
|
</select>
|
||||||
<div class="invalid-feedback">Role is required.</div>
|
<div class="form-text">Provides baseline module permissions.</div>
|
||||||
<div class="form-text">Choose the closest job role for baseline access. Add exceptional access later from Additional User Permissions.</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Dynamic Clinical Linkage for Doctor -->
|
||||||
|
<div class="col-md-4 clinical-link-field clinical-doctor-link" style="display:none;">
|
||||||
|
<label class="form-label fw-semibold text-info-emphasis" for="doctor_id">
|
||||||
|
<i class="bi bi-hospital"></i> Link to Doctor Master
|
||||||
|
</label>
|
||||||
|
<select class="form-select border-info" id="doctor_id" name="doctor_id">
|
||||||
|
<option value="">-- No Doctor Linked --</option>
|
||||||
|
<?php if (! empty($doctors)) : ?>
|
||||||
|
<?php foreach ($doctors as $doc) : ?>
|
||||||
|
<?php
|
||||||
|
$docName = trim(($doc['p_title'] ?? 'Dr.') . ' ' . ($doc['p_fname'] ?? '') . ' ' . ($doc['p_lname'] ?? ''));
|
||||||
|
?>
|
||||||
|
<option value="<?= (int) $doc['id'] ?>" <?= ($defaultDoctorId === (int) $doc['id']) ? 'selected' : '' ?>>
|
||||||
|
<?= esc($docName) ?> <?= ! empty($doc['doctor_reg_no']) ? '(Reg: ' . esc($doc['doctor_reg_no']) . ')' : '' ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach ?>
|
||||||
|
<?php endif ?>
|
||||||
|
</select>
|
||||||
|
<div class="form-text text-info">Auto-fills doctor details & sets doctor context in OPD/IPD.</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Dynamic Clinical Linkage for Nurse -->
|
||||||
|
<div class="col-md-4 clinical-link-field clinical-nurse-link" style="display:none;">
|
||||||
|
<label class="form-label fw-semibold text-purple" for="nurse_id">
|
||||||
|
<i class="bi bi-heart-pulse"></i> Link to Nurse Master
|
||||||
|
</label>
|
||||||
|
<select class="form-select border-primary" id="nurse_id" name="nurse_id">
|
||||||
|
<option value="">-- No Nurse Linked --</option>
|
||||||
|
<?php if (! empty($nurses)) : ?>
|
||||||
|
<?php foreach ($nurses as $nurse) : ?>
|
||||||
|
<option value="<?= (int) $nurse['id'] ?>" <?= ($defaultNurseId === (int) $nurse['id']) ? 'selected' : '' ?>>
|
||||||
|
<?= esc($nurse['name']) ?> (<?= esc($nurse['nurse_code'] ?? 'Staff') ?>)
|
||||||
|
</option>
|
||||||
|
<?php endforeach ?>
|
||||||
|
<?php endif ?>
|
||||||
|
</select>
|
||||||
|
<div class="form-text">Auto-fills nursing details & logs bedside notes.</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="mt-4 d-flex gap-2">
|
<!-- Section 2: Staff Details & Hospital Profile -->
|
||||||
<button class="btn btn-primary" type="submit">Create User</button>
|
<div class="mb-4">
|
||||||
<button class="btn btn-light" type="button" onclick="load_form_div('<?= base_url('setting/admin/user-management') ?>','maindiv','User Management');">Cancel</button>
|
<h6 class="fw-bold text-secondary text-uppercase border-bottom pb-2 mb-3">
|
||||||
|
<i class="bi bi-person-lines-fill"></i> Staff Profile & Hospital Details
|
||||||
|
</h6>
|
||||||
|
<div class="row g-3">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label class="form-label" for="person_name">Person / Staff Full Name</label>
|
||||||
|
<input class="form-control" id="person_name" name="person_name" type="text" maxlength="120" placeholder="e.g. Dr. Rajesh Sharma" value="<?= esc($formData['person_name'] ?? old('person_name')) ?>">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label class="form-label" for="phone_no">Mobile Phone No.</label>
|
||||||
|
<input class="form-control" id="phone_no" name="phone_no" type="text" maxlength="20" placeholder="e.g. 9876543210" value="<?= esc($formData['phone_no'] ?? old('phone_no')) ?>">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label class="form-label" for="employee_code">Employee / Staff Code</label>
|
||||||
|
<input class="form-control" id="employee_code" name="employee_code" type="text" maxlength="50" placeholder="e.g. EMP-104" value="<?= esc($formData['employee_code'] ?? old('employee_code')) ?>">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label" for="department">Department</label>
|
||||||
|
<input class="form-control" id="department" name="department" type="text" maxlength="100" placeholder="e.g. General Medicine, Nursing Station 1, Billing Counter" value="<?= esc($formData['department'] ?? old('department')) ?>">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label" for="designation">Designation</label>
|
||||||
|
<input class="form-control" id="designation" name="designation" type="text" maxlength="100" placeholder="e.g. Consultant Physician, Staff Nurse, Head Cashier" value="<?= esc($formData['designation'] ?? old('designation')) ?>">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
</div>
|
|
||||||
|
<div class="d-flex gap-2 pt-3 border-top">
|
||||||
|
<button class="btn btn-primary px-4" type="submit">
|
||||||
|
<i class="bi bi-check-circle"></i> Create User
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-light px-3" type="button" onclick="load_form_div('<?= base_url('setting/admin/user-management') ?>','maindiv','User Management');">
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
(function() {
|
(function() {
|
||||||
var form = document.querySelector('form[action="<?= base_url('setting/admin/user-management/new') ?>"]');
|
var form = document.getElementById('frm_user_create');
|
||||||
if (!form || !window.jQuery) {
|
if (!form || !window.jQuery) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$(form).off('submit.userCreate').on('submit.userCreate', function(event) {
|
var doctorsData = <?= json_encode($doctors ?? []) ?>;
|
||||||
event.preventDefault();
|
var nursesData = <?= json_encode($nurses ?? []) ?>;
|
||||||
|
|
||||||
$.post($(form).attr('action'), $(form).serialize())
|
function handleRoleChange() {
|
||||||
.done(function(html) {
|
var role = ($('#role').val() || '').toLowerCase();
|
||||||
$('#maindiv').html(html);
|
$('.clinical-link-field').hide();
|
||||||
showPageNotifications(document.getElementById('maindiv'));
|
|
||||||
})
|
if (role === 'doctor') {
|
||||||
.fail(function(xhr) {
|
$('.clinical-doctor-link').fadeIn(150);
|
||||||
notify('error', 'Create User', (xhr.responseJSON && xhr.responseJSON.error_text) || 'Request failed. Please try again.');
|
} else if (role === 'nurse') {
|
||||||
});
|
$('.clinical-nurse-link').fadeIn(150);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#role').off('change.roleSelect').on('change.roleSelect', handleRoleChange);
|
||||||
|
handleRoleChange();
|
||||||
|
|
||||||
|
// Auto-populate when selecting a Doctor
|
||||||
|
$('#doctor_id').off('change.docSelect').on('change.docSelect', function() {
|
||||||
|
var docId = parseInt($(this).val() || '0', 10);
|
||||||
|
if (docId <= 0) return;
|
||||||
|
|
||||||
|
var doc = doctorsData.find(function(d) { return parseInt(d.id, 10) === docId; });
|
||||||
|
if (!doc) return;
|
||||||
|
|
||||||
|
var title = (doc.p_title || 'Dr.').trim();
|
||||||
|
var fname = (doc.p_fname || '').trim();
|
||||||
|
var lname = (doc.p_lname || '').trim();
|
||||||
|
var fullName = (title + ' ' + fname + ' ' + lname).trim();
|
||||||
|
|
||||||
|
if (!$('#person_name').val()) $('#person_name').val(fullName);
|
||||||
|
if (!$('#email').val() && doc.email1) $('#email').val(doc.email1);
|
||||||
|
if (!$('#phone_no').val() && doc.mphone1) $('#phone_no').val(doc.mphone1);
|
||||||
|
if (!$('#designation').val()) $('#designation').val('Doctor / Consultant');
|
||||||
|
if (!$('#department').val()) $('#department').val('Clinical / OPD');
|
||||||
|
if (!$('#username').val() && fname) {
|
||||||
|
var suggestedUser = 'dr.' + fname.toLowerCase();
|
||||||
|
$('#username').val(suggestedUser);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Auto-populate when selecting a Nurse
|
||||||
|
$('#nurse_id').off('change.nurseSelect').on('change.nurseSelect', function() {
|
||||||
|
var nurseId = parseInt($(this).val() || '0', 10);
|
||||||
|
if (nurseId <= 0) return;
|
||||||
|
|
||||||
|
var nurse = nursesData.find(function(n) { return parseInt(n.id, 10) === nurseId; });
|
||||||
|
if (!nurse) return;
|
||||||
|
|
||||||
|
if (!$('#person_name').val() && nurse.name) $('#person_name').val(nurse.name);
|
||||||
|
if (!$('#email').val() && nurse.email) $('#email').val(nurse.email);
|
||||||
|
if (!$('#phone_no').val() && nurse.contact_no) $('#phone_no').val(nurse.contact_no);
|
||||||
|
if (!$('#employee_code').val() && nurse.nurse_code) $('#employee_code').val(nurse.nurse_code);
|
||||||
|
if (!$('#designation').val()) $('#designation').val(nurse.designation || 'Staff Nurse');
|
||||||
|
if (!$('#department').val()) $('#department').val(nurse.department || 'Nursing');
|
||||||
|
if (!$('#username').val() && nurse.nurse_code) {
|
||||||
|
$('#username').val(nurse.nurse_code.toLowerCase());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
<?php if (! empty($selected_doctor_id)) : ?>
|
||||||
|
$('#doctor_id').trigger('change.docSelect');
|
||||||
|
<?php elseif (! empty($selected_nurse_id)) : ?>
|
||||||
|
$('#nurse_id').trigger('change.nurseSelect');
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
// AJAX Form submission
|
||||||
|
$(form).off('submit.userCreate').on('submit.userCreate', function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
$.post($(form).attr('action'), $(form).serialize())
|
||||||
|
.done(function(html) {
|
||||||
|
$('#maindiv').html(html);
|
||||||
|
if (typeof showPageNotifications === 'function') {
|
||||||
|
showPageNotifications(document.getElementById('maindiv'));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.fail(function(xhr) {
|
||||||
|
notify('error', 'Create User', (xhr.responseJSON && xhr.responseJSON.error_text) || 'Request failed. Please check form validation.');
|
||||||
});
|
});
|
||||||
})();
|
});
|
||||||
</script>
|
})();
|
||||||
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,64 +1,141 @@
|
||||||
<div class="card">
|
<div class="card shadow-sm border-0">
|
||||||
<div class="card-header d-flex align-items-center justify-content-between flex-wrap gap-2">
|
<div class="card-header bg-white py-3 d-flex align-items-center justify-content-between flex-wrap gap-2 border-bottom">
|
||||||
<h3 class="card-title mb-0">Edit User</h3>
|
<div>
|
||||||
|
<h4 class="card-title mb-0 fw-bold text-dark">Edit User & Staff Account</h4>
|
||||||
|
<div class="text-muted small">Update login credentials, role assignments, staff metadata, and clinical links</div>
|
||||||
|
</div>
|
||||||
<div class="card-tools ms-auto">
|
<div class="card-tools ms-auto">
|
||||||
<button class="btn btn-light" type="button" onclick="load_form_div('<?= base_url('setting/admin/user-management') ?>','maindiv','User Management');">
|
<button class="btn btn-outline-secondary" type="button" onclick="load_form_div('<?= base_url('setting/admin/user-management') ?>','maindiv','User Management');">
|
||||||
<i class="bi bi-arrow-left"></i>
|
<i class="bi bi-arrow-left"></i> Back to User List
|
||||||
Back to User List
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body p-4">
|
||||||
<?php $errors = $errors ?? session('errors'); ?>
|
<?php $errors = $errors ?? session('errors'); ?>
|
||||||
<?php $formData = $formData ?? []; ?>
|
<?php $formData = $formData ?? []; ?>
|
||||||
<?php foreach ((array) $errors as $error) : ?><span class="d-none" data-page-notification data-notification-type="error" data-notification-title="Edit User" data-notification-message="<?= esc((string) $error, 'attr') ?>"></span><?php endforeach ?>
|
<?php foreach ((array) $errors as $error) : ?><span class="d-none" data-page-notification data-notification-type="error" data-notification-title="Edit User" data-notification-message="<?= esc((string) $error, 'attr') ?>"></span><?php endforeach ?>
|
||||||
|
|
||||||
<form id="frm_user_edit" class="needs-validation" novalidate action="<?= base_url('setting/admin/user-management/edit/' . (int) ($user->id ?? 0)) ?>" method="post">
|
<form id="frm_user_edit" class="needs-validation" novalidate action="<?= base_url('setting/admin/user-management/edit/' . (int) ($user->id ?? 0)) ?>" method="post">
|
||||||
<?= csrf_field() ?>
|
<?= csrf_field() ?>
|
||||||
<div class="row g-3">
|
|
||||||
<div class="col-md-6">
|
<!-- Section 1: Account Credentials -->
|
||||||
<label class="form-label" for="username">Login ID</label>
|
<div class="mb-4">
|
||||||
<input class="form-control" id="username" name="username" type="text" maxlength="30" value="<?= esc($formData['username'] ?? ($user->username ?? '')) ?>" required>
|
<h6 class="fw-bold text-primary text-uppercase border-bottom pb-2 mb-3">
|
||||||
</div>
|
<i class="bi bi-shield-lock"></i> Account Credentials & Role
|
||||||
<div class="col-md-6">
|
</h6>
|
||||||
<label class="form-label" for="person_name">Person Name</label>
|
<div class="row g-3">
|
||||||
<input class="form-control" id="person_name" name="person_name" type="text" maxlength="120" value="<?= esc($formData['person_name'] ?? ($person_name ?? '')) ?>">
|
<div class="col-md-4">
|
||||||
</div>
|
<label class="form-label fw-semibold" for="username">Login ID / Username <span class="text-danger">*</span></label>
|
||||||
<div class="col-md-6">
|
<input class="form-control" id="username" name="username" type="text" maxlength="30" value="<?= esc($formData['username'] ?? ($user->username ?? '')) ?>" required>
|
||||||
<label class="form-label" for="email">Email</label>
|
</div>
|
||||||
<input class="form-control" id="email" name="email" type="email" value="<?= esc($formData['email'] ?? ($email ?? '')) ?>" required>
|
<div class="col-md-4">
|
||||||
</div>
|
<label class="form-label fw-semibold" for="email">Email Address <span class="text-danger">*</span></label>
|
||||||
<div class="col-md-6">
|
<input class="form-control" id="email" name="email" type="email" value="<?= esc($formData['email'] ?? ($email ?? '')) ?>" required>
|
||||||
<label class="form-label" for="phone_no">Phone No.</label>
|
</div>
|
||||||
<input class="form-control" id="phone_no" name="phone_no" type="text" maxlength="20" value="<?= esc($formData['phone_no'] ?? ($phone_no ?? '')) ?>">
|
<div class="col-md-4">
|
||||||
</div>
|
<label class="form-label fw-semibold" for="password">New Password (Optional)</label>
|
||||||
<div class="col-md-6">
|
<input class="form-control" id="password" name="password" type="password" minlength="8" autocomplete="new-password" placeholder="Leave blank to keep current">
|
||||||
<label class="form-label" for="role">Role</label>
|
</div>
|
||||||
<select class="form-select" id="role" name="role" required>
|
|
||||||
<option value="">Select role</option>
|
<div class="col-md-4">
|
||||||
<?php foreach (($roles ?? []) as $roleKey => $roleInfo) : ?>
|
<label class="form-label fw-semibold" for="role">Assigned Role <span class="text-danger">*</span></label>
|
||||||
<option value="<?= esc($roleKey) ?>" <?= (string) ($formData['role'] ?? ($current_role ?? '')) === (string) $roleKey ? 'selected' : '' ?>>
|
<select class="form-select" id="role" name="role" required>
|
||||||
<?= esc($roleInfo['title'] ?? $roleKey) ?>
|
<option value="">Select role</option>
|
||||||
</option>
|
<?php foreach (($roles ?? []) as $roleKey => $roleInfo) : ?>
|
||||||
<?php endforeach ?>
|
<option value="<?= esc($roleKey) ?>" <?= (string) ($formData['role'] ?? ($current_role ?? '')) === (string) $roleKey ? 'selected' : '' ?>>
|
||||||
</select>
|
<?= esc($roleInfo['title'] ?? $roleKey) ?>
|
||||||
<div class="form-text">The role provides baseline access. Use Additional User Permissions only when this user needs extra access beyond the role.</div>
|
</option>
|
||||||
</div>
|
<?php endforeach ?>
|
||||||
<div class="col-md-6">
|
</select>
|
||||||
<label class="form-label" for="password">New Password (Optional)</label>
|
</div>
|
||||||
<input class="form-control" id="password" name="password" type="password" autocomplete="new-password" placeholder="Leave blank to keep current password">
|
|
||||||
</div>
|
<!-- Clinical Linkage for Doctor -->
|
||||||
<div class="col-md-6 d-flex align-items-center">
|
<div class="col-md-4 clinical-link-field clinical-doctor-link" style="display:none;">
|
||||||
<div class="form-check mt-4">
|
<label class="form-label fw-semibold text-info-emphasis" for="doctor_id">
|
||||||
<input class="form-check-input" id="active" name="active" type="checkbox" value="1" <?= (int) ($formData['active'] ?? ($user->active ?? 0)) === 1 ? 'checked' : '' ?>>
|
<i class="bi bi-hospital"></i> Link to Doctor Master
|
||||||
<label class="form-check-label" for="active">Active User</label>
|
</label>
|
||||||
|
<select class="form-select border-info" id="doctor_id" name="doctor_id">
|
||||||
|
<option value="">-- No Doctor Linked --</option>
|
||||||
|
<?php if (! empty($doctors)) : ?>
|
||||||
|
<?php foreach ($doctors as $doc) : ?>
|
||||||
|
<?php
|
||||||
|
$docName = trim(($doc['p_title'] ?? 'Dr.') . ' ' . ($doc['p_fname'] ?? '') . ' ' . ($doc['p_lname'] ?? ''));
|
||||||
|
$selectedDoc = (int) ($formData['doctor_id'] ?? ($doctor_id ?? 0));
|
||||||
|
?>
|
||||||
|
<option value="<?= (int) $doc['id'] ?>" <?= ($selectedDoc === (int) $doc['id']) ? 'selected' : '' ?>>
|
||||||
|
<?= esc($docName) ?> <?= ! empty($doc['doctor_reg_no']) ? '(Reg: ' . esc($doc['doctor_reg_no']) . ')' : '' ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach ?>
|
||||||
|
<?php endif ?>
|
||||||
|
</select>
|
||||||
|
<div class="form-text text-info">Locks doctor context in OPD and prescriptions.</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Clinical Linkage for Nurse -->
|
||||||
|
<div class="col-md-4 clinical-link-field clinical-nurse-link" style="display:none;">
|
||||||
|
<label class="form-label fw-semibold text-purple" for="nurse_id">
|
||||||
|
<i class="bi bi-heart-pulse"></i> Link to Nurse Master
|
||||||
|
</label>
|
||||||
|
<select class="form-select border-primary" id="nurse_id" name="nurse_id">
|
||||||
|
<option value="">-- No Nurse Linked --</option>
|
||||||
|
<?php if (! empty($nurses)) : ?>
|
||||||
|
<?php foreach ($nurses as $nurse) : ?>
|
||||||
|
<?php $selectedNurse = (int) ($formData['nurse_id'] ?? ($nurse_id ?? 0)); ?>
|
||||||
|
<option value="<?= (int) $nurse['id'] ?>" <?= ($selectedNurse === (int) $nurse['id']) ? 'selected' : '' ?>>
|
||||||
|
<?= esc($nurse['name']) ?> (<?= esc($nurse['nurse_code'] ?? 'Staff') ?>)
|
||||||
|
</option>
|
||||||
|
<?php endforeach ?>
|
||||||
|
<?php endif ?>
|
||||||
|
</select>
|
||||||
|
<div class="form-text">Used for nursing chart signing and bedside records.</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-4 d-flex align-items-center">
|
||||||
|
<div class="form-check form-switch mt-3">
|
||||||
|
<input class="form-check-input" id="active" name="active" type="checkbox" role="switch" value="1" <?= (int) ($formData['active'] ?? ($user->active ?? 0)) === 1 ? 'checked' : '' ?>>
|
||||||
|
<label class="form-check-label fw-semibold" for="active">Account Active</label>
|
||||||
|
<div class="form-text">Deactivating terminates all active sessions immediately.</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-4 d-flex gap-2">
|
<!-- Section 2: Staff Details -->
|
||||||
<button class="btn btn-primary" type="submit">Save Changes</button>
|
<div class="mb-4">
|
||||||
<button class="btn btn-light" type="button" onclick="load_form_div('<?= base_url('setting/admin/user-management') ?>','maindiv','User Management');">Cancel</button>
|
<h6 class="fw-bold text-secondary text-uppercase border-bottom pb-2 mb-3">
|
||||||
|
<i class="bi bi-person-lines-fill"></i> Staff Profile Details
|
||||||
|
</h6>
|
||||||
|
<div class="row g-3">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label class="form-label" for="person_name">Person / Staff Full Name</label>
|
||||||
|
<input class="form-control" id="person_name" name="person_name" type="text" maxlength="120" value="<?= esc($formData['person_name'] ?? ($person_name ?? '')) ?>">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label class="form-label" for="phone_no">Mobile Phone No.</label>
|
||||||
|
<input class="form-control" id="phone_no" name="phone_no" type="text" maxlength="20" value="<?= esc($formData['phone_no'] ?? ($phone_no ?? '')) ?>">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label class="form-label" for="employee_code">Employee / Staff Code</label>
|
||||||
|
<input class="form-control" id="employee_code" name="employee_code" type="text" maxlength="50" placeholder="e.g. EMP-104" value="<?= esc($formData['employee_code'] ?? ($employee_code ?? '')) ?>">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label" for="department">Department</label>
|
||||||
|
<input class="form-control" id="department" name="department" type="text" maxlength="100" placeholder="e.g. General Medicine, Nursing Station 1, Billing Counter" value="<?= esc($formData['department'] ?? ($department ?? '')) ?>">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label" for="designation">Designation</label>
|
||||||
|
<input class="form-control" id="designation" name="designation" type="text" maxlength="100" placeholder="e.g. Consultant Physician, Staff Nurse, Head Cashier" value="<?= esc($formData['designation'] ?? ($designation ?? '')) ?>">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex gap-2 pt-3 border-top">
|
||||||
|
<button class="btn btn-primary px-4" type="submit">
|
||||||
|
<i class="bi bi-check2-circle"></i> Save Changes
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-light px-3" type="button" onclick="load_form_div('<?= base_url('setting/admin/user-management') ?>','maindiv','User Management');">
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -71,12 +148,65 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var doctorsData = <?= json_encode($doctors ?? []) ?>;
|
||||||
|
var nursesData = <?= json_encode($nurses ?? []) ?>;
|
||||||
|
|
||||||
|
function handleRoleChange() {
|
||||||
|
var role = ($('#role').val() || '').toLowerCase();
|
||||||
|
$('.clinical-link-field').hide();
|
||||||
|
|
||||||
|
if (role === 'doctor') {
|
||||||
|
$('.clinical-doctor-link').fadeIn(150);
|
||||||
|
} else if (role === 'nurse') {
|
||||||
|
$('.clinical-nurse-link').fadeIn(150);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#role').off('change.roleSelect').on('change.roleSelect', handleRoleChange);
|
||||||
|
handleRoleChange();
|
||||||
|
|
||||||
|
// Auto-fill when choosing doctor
|
||||||
|
$('#doctor_id').off('change.docSelect').on('change.docSelect', function() {
|
||||||
|
var docId = parseInt($(this).val() || '0', 10);
|
||||||
|
if (docId <= 0) return;
|
||||||
|
|
||||||
|
var doc = doctorsData.find(function(d) { return parseInt(d.id, 10) === docId; });
|
||||||
|
if (!doc) return;
|
||||||
|
|
||||||
|
var title = (doc.p_title || 'Dr.').trim();
|
||||||
|
var fname = (doc.p_fname || '').trim();
|
||||||
|
var lname = (doc.p_lname || '').trim();
|
||||||
|
var fullName = (title + ' ' + fname + ' ' + lname).trim();
|
||||||
|
|
||||||
|
if (!$('#person_name').val()) $('#person_name').val(fullName);
|
||||||
|
if (!$('#phone_no').val() && doc.mphone1) $('#phone_no').val(doc.mphone1);
|
||||||
|
if (!$('#designation').val()) $('#designation').val('Doctor / Consultant');
|
||||||
|
if (!$('#department').val()) $('#department').val('Clinical / OPD');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Auto-fill when choosing nurse
|
||||||
|
$('#nurse_id').off('change.nurseSelect').on('change.nurseSelect', function() {
|
||||||
|
var nurseId = parseInt($(this).val() || '0', 10);
|
||||||
|
if (nurseId <= 0) return;
|
||||||
|
|
||||||
|
var nurse = nursesData.find(function(n) { return parseInt(n.id, 10) === nurseId; });
|
||||||
|
if (!nurse) return;
|
||||||
|
|
||||||
|
if (!$('#person_name').val() && nurse.name) $('#person_name').val(nurse.name);
|
||||||
|
if (!$('#phone_no').val() && nurse.contact_no) $('#phone_no').val(nurse.contact_no);
|
||||||
|
if (!$('#employee_code').val() && nurse.nurse_code) $('#employee_code').val(nurse.nurse_code);
|
||||||
|
if (!$('#designation').val()) $('#designation').val(nurse.designation || 'Staff Nurse');
|
||||||
|
if (!$('#department').val()) $('#department').val(nurse.department || 'Nursing');
|
||||||
|
});
|
||||||
|
|
||||||
$(form).off('submit.userEdit').on('submit.userEdit', function(event) {
|
$(form).off('submit.userEdit').on('submit.userEdit', function(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
$.post($(form).attr('action'), $(form).serialize())
|
$.post($(form).attr('action'), $(form).serialize())
|
||||||
.done(function(html) {
|
.done(function(html) {
|
||||||
$('#maindiv').html(html);
|
$('#maindiv').html(html);
|
||||||
showPageNotifications(document.getElementById('maindiv'));
|
if (typeof showPageNotifications === 'function') {
|
||||||
|
showPageNotifications(document.getElementById('maindiv'));
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.fail(function(xhr) {
|
.fail(function(xhr) {
|
||||||
notify('error', 'Edit User', (xhr.responseJSON && xhr.responseJSON.error_text) || 'Request failed. Please try again.');
|
notify('error', 'Edit User', (xhr.responseJSON && xhr.responseJSON.error_text) || 'Request failed. Please try again.');
|
||||||
|
|
|
||||||
|
|
@ -1,108 +1,208 @@
|
||||||
<div id="maindiv">
|
<div id="maindiv">
|
||||||
<div class="card">
|
<div class="card shadow-sm border-0">
|
||||||
<div class="card-header d-flex align-items-center justify-content-between flex-wrap gap-2">
|
<div class="card-header bg-white py-3 d-flex align-items-center justify-content-between flex-wrap gap-2 border-bottom">
|
||||||
<h3 class="card-title mb-0">Users</h3>
|
<div>
|
||||||
|
<h4 class="card-title mb-0 fw-bold text-dark">User Management</h4>
|
||||||
|
<div class="text-muted small">Manage hospital staff accounts, roles, clinical linkages, and credentials</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="card-tools ms-auto">
|
<div class="card-tools ms-auto d-flex align-items-center gap-2 flex-wrap">
|
||||||
<button class="btn btn-primary" type="button" onclick="load_form_div('<?= base_url('setting/admin/user-management/new') ?>','maindiv','New User');">
|
<button class="btn btn-outline-info" type="button" onclick="load_form_div('<?= base_url('setting/admin/user-management/sessions') ?>','maindiv','Active Sessions');">
|
||||||
<i class="bi bi-person-plus"></i>
|
<i class="bi bi-broadcast"></i> Active Sessions
|
||||||
New User
|
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-outline-dark" type="button" onclick="load_form_div('<?= base_url('setting/admin/roles') ?>','maindiv','Role Master');">
|
<button class="btn btn-outline-dark" type="button" onclick="load_form_div('<?= base_url('setting/admin/roles') ?>','maindiv','Role Master');">
|
||||||
<i class="bi bi-person-badge"></i>
|
<i class="bi bi-shield-check"></i> Role Master
|
||||||
Role Master
|
</button>
|
||||||
|
<button class="btn btn-primary" type="button" onclick="load_form_div('<?= base_url('setting/admin/user-management/new') ?>','maindiv','New User');">
|
||||||
|
<i class="bi bi-person-plus-fill"></i> New User
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
|
||||||
|
<div class="card-body p-3">
|
||||||
<?php $message = $message ?? session('message'); ?>
|
<?php $message = $message ?? session('message'); ?>
|
||||||
<?php $errors = $errors ?? session('errors'); ?>
|
<?php $errors = $errors ?? session('errors'); ?>
|
||||||
<?php if (! empty($message)) : ?><span class="d-none" data-page-notification data-notification-type="success" data-notification-title="User Management" data-notification-message="<?= esc((string) $message, 'attr') ?>"></span><?php endif ?>
|
<?php if (! empty($message)) : ?><span class="d-none" data-page-notification data-notification-type="success" data-notification-title="User Management" data-notification-message="<?= esc((string) $message, 'attr') ?>"></span><?php endif ?>
|
||||||
<?php foreach ((array) $errors as $error) : ?><span class="d-none" data-page-notification data-notification-type="error" data-notification-title="User Management" data-notification-message="<?= esc((string) $error, 'attr') ?>"></span><?php endforeach ?>
|
<?php foreach ((array) $errors as $error) : ?><span class="d-none" data-page-notification data-notification-type="error" data-notification-title="User Management" data-notification-message="<?= esc((string) $error, 'attr') ?>"></span><?php endforeach ?>
|
||||||
<div class="row g-2 mb-3">
|
|
||||||
<div class="col-md-4">
|
<!-- Advanced Filter Bar -->
|
||||||
<label class="form-label mb-1" for="um_filter_person">Filter by Person Name</label>
|
<div class="row g-2 mb-3 align-items-end bg-light p-2 rounded border">
|
||||||
<input type="text" id="um_filter_person" class="form-control form-control-sm" placeholder="Type person name...">
|
<div class="col-md-4 col-sm-6">
|
||||||
|
<label class="form-label small fw-semibold text-secondary mb-1" for="um_filter_search">Search Staff / User</label>
|
||||||
|
<div class="input-group input-group-sm">
|
||||||
|
<span class="input-group-text bg-white"><i class="bi bi-search"></i></span>
|
||||||
|
<input type="text" id="um_filter_search" class="form-control" placeholder="Search by name, login ID, phone, email, emp code...">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4">
|
<div class="col-md-3 col-sm-6">
|
||||||
<label class="form-label mb-1" for="um_filter_phone">Filter by Phone No.</label>
|
<label class="form-label small fw-semibold text-secondary mb-1" for="um_filter_role">Filter by Role</label>
|
||||||
<input type="text" id="um_filter_phone" class="form-control form-control-sm" placeholder="Type phone no...">
|
<select id="um_filter_role" class="form-select form-select-sm">
|
||||||
|
<option value="">All Roles</option>
|
||||||
|
<?php if (! empty($roles)) : ?>
|
||||||
|
<?php foreach ($roles as $roleKey => $roleInfo) : ?>
|
||||||
|
<option value="<?= esc($roleKey) ?>"><?= esc($roleInfo['title'] ?? $roleKey) ?></option>
|
||||||
|
<?php endforeach ?>
|
||||||
|
<?php endif ?>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4 d-flex align-items-end">
|
<div class="col-md-3 col-sm-6">
|
||||||
<button type="button" class="btn btn-outline-secondary btn-sm" id="um_filter_reset">Clear Filters</button>
|
<label class="form-label small fw-semibold text-secondary mb-1" for="um_filter_status">Filter by Status</label>
|
||||||
|
<select id="um_filter_status" class="form-select form-select-sm">
|
||||||
|
<option value="">All Statuses</option>
|
||||||
|
<option value="active">Active Only</option>
|
||||||
|
<option value="inactive">Inactive Only</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2 col-sm-6 d-flex gap-2">
|
||||||
|
<button type="button" class="btn btn-outline-secondary btn-sm w-100" id="um_filter_reset">
|
||||||
|
<i class="bi bi-x-circle"></i> Reset
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-striped datatable">
|
<table class="table table-hover table-striped align-middle mb-0" id="tbl_user_list">
|
||||||
<thead>
|
<thead class="table-light">
|
||||||
<tr>
|
<tr>
|
||||||
<th>#</th>
|
<th style="width: 50px;">#</th>
|
||||||
<th>Login ID</th>
|
<th>Staff Member</th>
|
||||||
<th>Person Name</th>
|
<th>Clinical / Staff Link</th>
|
||||||
<th>Phone No.</th>
|
<th>Contact Details</th>
|
||||||
<th>Email</th>
|
|
||||||
<th>Role</th>
|
<th>Role</th>
|
||||||
<th>Status</th>
|
<th style="width: 140px;">Status</th>
|
||||||
<th>Action</th>
|
<th style="width: 170px;" class="text-end">Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<?php if (! empty($users)) : ?>
|
<?php if (! empty($users)) : ?>
|
||||||
<?php $rolesConfig = setting('AuthGroups.groups'); ?>
|
<?php
|
||||||
|
$rolesConfig = setting('AuthGroups.groups');
|
||||||
|
$currentUid = function_exists('auth') ? (int) (auth()->user()->id ?? 0) : 0;
|
||||||
|
?>
|
||||||
<?php foreach ($users as $index => $user) : ?>
|
<?php foreach ($users as $index => $user) : ?>
|
||||||
<?php
|
<?php
|
||||||
$emailIdentity = $user->getEmailIdentity();
|
$userId = (int) ($user['id'] ?? 0);
|
||||||
$email = $emailIdentity ? $emailIdentity->secret : '';
|
$isSelf = ($userId === $currentUid);
|
||||||
$personName = '';
|
$groups = (array) ($user['groups'] ?? []);
|
||||||
$phoneNo = '';
|
$personName = trim((string) ($user['full_name'] ?? ''));
|
||||||
if ($emailIdentity && isset($emailIdentity->extra)) {
|
$phoneNo = trim((string) ($user['phone_no'] ?? ''));
|
||||||
$extra = $emailIdentity->extra;
|
$email = trim((string) ($user['email'] ?? ''));
|
||||||
if (is_string($extra) && trim($extra) !== '') {
|
$empCode = trim((string) ($user['employee_code'] ?? ''));
|
||||||
$decodedExtra = json_decode($extra, true);
|
$dept = trim((string) ($user['department'] ?? ''));
|
||||||
if (is_array($decodedExtra)) {
|
$desig = trim((string) ($user['designation'] ?? ''));
|
||||||
$personName = trim((string) ($decodedExtra['full_name'] ?? ''));
|
$isActive = ! empty($user['active']);
|
||||||
$phoneNo = trim((string) ($decodedExtra['phone_no'] ?? ''));
|
|
||||||
}
|
// Clinical mapping
|
||||||
} elseif (is_array($extra)) {
|
$linkedDoctor = trim((string) ($user['linked_doctor_name'] ?? ''));
|
||||||
$personName = trim((string) ($extra['full_name'] ?? ''));
|
$linkedNurse = trim((string) ($user['linked_nurse_name'] ?? ''));
|
||||||
$phoneNo = trim((string) ($extra['phone_no'] ?? ''));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$groups = $user->getGroups() ?? [];
|
|
||||||
$roleLabels = [];
|
|
||||||
foreach ($groups as $group) {
|
|
||||||
$roleLabels[] = $rolesConfig[$group]['title'] ?? $group;
|
|
||||||
}
|
|
||||||
$roleText = $roleLabels !== [] ? implode(', ', $roleLabels) : '-';
|
|
||||||
?>
|
?>
|
||||||
<tr>
|
<tr data-user-id="<?= $userId ?>"
|
||||||
<td><?= $index + 1 ?></td>
|
data-username="<?= esc(strtolower($user['username'] ?? ''), 'attr') ?>"
|
||||||
<td><?= esc($user->username ?? '') ?></td>
|
data-person-name="<?= esc(strtolower($personName), 'attr') ?>"
|
||||||
<td><?= esc($personName !== '' ? $personName : '-') ?></td>
|
data-phone="<?= esc($phoneNo, 'attr') ?>"
|
||||||
<td><?= esc($phoneNo !== '' ? $phoneNo : '-') ?></td>
|
data-email="<?= esc(strtolower($email), 'attr') ?>"
|
||||||
<td><?= esc($email) ?></td>
|
data-emp-code="<?= esc(strtolower($empCode), 'attr') ?>"
|
||||||
<td><?= esc($roleText) ?></td>
|
data-roles="<?= esc(strtolower(implode(' ', $groups)), 'attr') ?>"
|
||||||
|
data-status="<?= $isActive ? 'active' : 'inactive' ?>">
|
||||||
|
<td class="text-muted small"><?= $index + 1 ?></td>
|
||||||
<td>
|
<td>
|
||||||
<?php if (! empty($user->active)) : ?>
|
<div class="d-flex align-items-center gap-2">
|
||||||
<span class="badge bg-success">Active</span>
|
<div class="avatar-circle d-flex align-items-center justify-content-center fw-bold bg-primary text-white rounded-circle" style="width:34px; height:34px; font-size:13px;">
|
||||||
|
<?= strtoupper(substr($personName !== '' ? $personName : ($user['username'] ?? 'U'), 0, 1)) ?>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="fw-bold text-dark">
|
||||||
|
<?= esc($personName !== '' ? $personName : $user['username']) ?>
|
||||||
|
<?php if ($isSelf) : ?>
|
||||||
|
<span class="badge bg-secondary ms-1" style="font-size:10px;">You</span>
|
||||||
|
<?php endif ?>
|
||||||
|
</div>
|
||||||
|
<div class="small text-muted d-flex align-items-center gap-2">
|
||||||
|
<code><?= esc($user['username'] ?? '') ?></code>
|
||||||
|
<?php if ($empCode !== '') : ?>
|
||||||
|
<span class="badge bg-light text-secondary border py-0">Emp: <?= esc($empCode) ?></span>
|
||||||
|
<?php endif ?>
|
||||||
|
<?php if ($desig !== '' || $dept !== '') : ?>
|
||||||
|
<span>• <?= esc(implode(' / ', array_filter([$desig, $dept]))) ?></span>
|
||||||
|
<?php endif ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<?php if ($linkedDoctor !== '') : ?>
|
||||||
|
<span class="badge rounded-pill" style="background-color:#0d9488; color:#fff;" title="Linked Doctor">
|
||||||
|
<i class="bi bi-hospital"></i> <?= esc($linkedDoctor) ?>
|
||||||
|
</span>
|
||||||
|
<?php if (! empty($user['linked_doctor_reg'])) : ?>
|
||||||
|
<div class="text-muted" style="font-size:10px;">Reg: <?= esc($user['linked_doctor_reg']) ?></div>
|
||||||
|
<?php endif ?>
|
||||||
|
<?php elseif ($linkedNurse !== '') : ?>
|
||||||
|
<span class="badge rounded-pill" style="background-color:#d946ef; color:#fff;" title="Linked Nurse">
|
||||||
|
<i class="bi bi-heart-pulse"></i> <?= esc($linkedNurse) ?>
|
||||||
|
</span>
|
||||||
<?php else : ?>
|
<?php else : ?>
|
||||||
<span class="badge bg-secondary">Inactive</span>
|
<span class="text-muted small">—</span>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="d-flex gap-1">
|
<div><?= esc($phoneNo !== '' ? $phoneNo : '-') ?></div>
|
||||||
<button type="button" class="btn btn-sm btn-outline-primary" title="Edit user" aria-label="Edit user" onclick="load_form_div('<?= base_url('setting/admin/user-management/edit/' . (int) ($user->id ?? 0)) ?>','maindiv','Edit User');">
|
<div class="small text-muted"><?= esc($email !== '' ? $email : '-') ?></div>
|
||||||
<i class="bi bi-pencil-square" aria-hidden="true"></i>
|
</td>
|
||||||
|
<td>
|
||||||
|
<?php if (! empty($groups)) : ?>
|
||||||
|
<?php foreach ($groups as $g) : ?>
|
||||||
|
<?php
|
||||||
|
$roleTitle = $rolesConfig[$g]['title'] ?? $g;
|
||||||
|
$badgeStyle = 'background-color:#64748b; color:#fff;';
|
||||||
|
if (in_array($g, ['superadmin', 'admin'], true)) {
|
||||||
|
$badgeStyle = 'background-color:#dc2626; color:#fff;';
|
||||||
|
} elseif ($g === 'doctor') {
|
||||||
|
$badgeStyle = 'background-color:#0284c7; color:#fff;';
|
||||||
|
} elseif ($g === 'nurse') {
|
||||||
|
$badgeStyle = 'background-color:#7c3aed; color:#fff;';
|
||||||
|
} elseif (strpos($g, 'billing') !== false || strpos($g, 'cashier') !== false) {
|
||||||
|
$badgeStyle = 'background-color:#16a34a; color:#fff;';
|
||||||
|
} elseif (strpos($g, 'pharmacy') !== false) {
|
||||||
|
$badgeStyle = 'background-color:#ea580c; color:#fff;';
|
||||||
|
} elseif (strpos($g, 'stock') !== false) {
|
||||||
|
$badgeStyle = 'background-color:#475569; color:#fff;';
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<span class="badge rounded-pill mb-1" style="<?= $badgeStyle ?>"><?= esc($roleTitle) ?></span>
|
||||||
|
<?php endforeach ?>
|
||||||
|
<?php else : ?>
|
||||||
|
<span class="badge bg-light text-muted border">No role</span>
|
||||||
|
<?php endif ?>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="form-check form-switch d-inline-flex align-items-center gap-1 mb-0">
|
||||||
|
<input class="form-check-input btn-toggle-user-status"
|
||||||
|
type="checkbox"
|
||||||
|
role="switch"
|
||||||
|
data-user-id="<?= $userId ?>"
|
||||||
|
data-username="<?= esc((string) ($user['username'] ?? ''), 'attr') ?>"
|
||||||
|
<?= $isActive ? 'checked' : '' ?>
|
||||||
|
<?= $isSelf ? 'disabled title="You cannot deactivate your own account"' : '' ?>>
|
||||||
|
<span class="status-label small fw-semibold <?= $isActive ? 'text-success' : 'text-secondary' ?>">
|
||||||
|
<?= $isActive ? 'Active' : 'Inactive' ?>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="text-end">
|
||||||
|
<div class="btn-group btn-group-sm">
|
||||||
|
<button type="button" class="btn btn-outline-primary" title="Edit user" aria-label="Edit user" onclick="load_form_div('<?= base_url('setting/admin/user-management/edit/' . $userId) ?>','maindiv','Edit User');">
|
||||||
|
<i class="bi bi-pencil-square"></i>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="btn btn-sm btn-outline-warning" title="Reset password" aria-label="Reset password" onclick="load_form_div('<?= base_url('setting/admin/user-management/reset-password/' . (int) ($user->id ?? 0)) ?>','maindiv','Reset Password');">
|
<button type="button" class="btn btn-outline-warning" title="Set Temporary PIN" aria-label="Reset password" onclick="load_form_div('<?= base_url('setting/admin/user-management/reset-password/' . $userId) ?>','maindiv','Reset Password');">
|
||||||
<i class="bi bi-key" aria-hidden="true"></i>
|
<i class="bi bi-key-fill"></i>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="btn btn-sm btn-outline-info" title="Additional permissions" aria-label="Additional permissions" onclick="load_form_div('<?= base_url('setting/admin/user-management/permissions?user_id=' . (int) ($user->id ?? 0)) ?>','maindiv','Additional Permissions');">
|
<button type="button" class="btn btn-outline-info" title="Additional permissions" aria-label="Additional permissions" onclick="load_form_div('<?= base_url('setting/admin/user-management/permissions?user_id=' . $userId) ?>','maindiv','Additional Permissions');">
|
||||||
<i class="bi bi-shield-lock" aria-hidden="true"></i>
|
<i class="bi bi-shield-lock"></i>
|
||||||
</button>
|
</button>
|
||||||
<?php if ((int) ($user->id ?? 0) !== (int) (auth()->user()->id ?? 0)) : ?>
|
<?php if (! $isSelf) : ?>
|
||||||
<button type="button" class="btn btn-sm btn-outline-danger btn-delete-user" title="Delete user" aria-label="Delete user" data-user-id="<?= (int) ($user->id ?? 0) ?>" data-username="<?= esc((string) ($user->username ?? ''), 'attr') ?>">
|
<button type="button" class="btn btn-outline-danger btn-delete-user" title="Deactivate & remove user" aria-label="Delete user" data-user-id="<?= $userId ?>" data-username="<?= esc((string) ($user['username'] ?? ''), 'attr') ?>">
|
||||||
<i class="bi bi-trash" aria-hidden="true"></i>
|
<i class="bi bi-trash"></i>
|
||||||
</button>
|
</button>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -111,7 +211,7 @@
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
<?php else : ?>
|
<?php else : ?>
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="8" class="text-center text-muted">No users loaded yet.</td>
|
<td colspan="7" class="text-center text-muted py-4">No users found. Click <strong>New User</strong> to add your first staff member.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
@ -122,49 +222,110 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
(function() {
|
(function() {
|
||||||
function applyUserFilters() {
|
function filterUsersTable() {
|
||||||
var personNeedle = ($('#um_filter_person').val() || '').toString().trim().toLowerCase();
|
var query = ($('#um_filter_search').val() || '').toString().trim().toLowerCase();
|
||||||
var phoneNeedle = ($('#um_filter_phone').val() || '').toString().trim().toLowerCase();
|
var selectedRole = ($('#um_filter_role').val() || '').toString().trim().toLowerCase();
|
||||||
|
var selectedStatus = ($('#um_filter_status').val() || '').toString().trim().toLowerCase();
|
||||||
|
|
||||||
$('table.datatable tbody tr').each(function() {
|
$('#tbl_user_list tbody tr').each(function() {
|
||||||
var $row = $(this);
|
var $row = $(this);
|
||||||
var personText = ($row.find('td').eq(2).text() || '').toString().trim().toLowerCase();
|
if ($row.find('td').length <= 1) return; // skip empty state row
|
||||||
var phoneText = ($row.find('td').eq(3).text() || '').toString().trim().toLowerCase();
|
|
||||||
|
|
||||||
var personOk = personNeedle === '' || personText.indexOf(personNeedle) !== -1;
|
var username = ($row.data('username') || '').toString();
|
||||||
var phoneOk = phoneNeedle === '' || phoneText.indexOf(phoneNeedle) !== -1;
|
var personName = ($row.data('person-name') || '').toString();
|
||||||
|
var phone = ($row.data('phone') || '').toString();
|
||||||
|
var email = ($row.data('email') || '').toString();
|
||||||
|
var empCode = ($row.data('emp-code') || '').toString();
|
||||||
|
var roles = ($row.data('roles') || '').toString();
|
||||||
|
var status = ($row.data('status') || '').toString();
|
||||||
|
|
||||||
$row.toggle(personOk && phoneOk);
|
var matchesQuery = (query === '') ||
|
||||||
|
(username.indexOf(query) !== -1) ||
|
||||||
|
(personName.indexOf(query) !== -1) ||
|
||||||
|
(phone.indexOf(query) !== -1) ||
|
||||||
|
(email.indexOf(query) !== -1) ||
|
||||||
|
(empCode.indexOf(query) !== -1);
|
||||||
|
|
||||||
|
var matchesRole = (selectedRole === '') || (roles.indexOf(selectedRole) !== -1);
|
||||||
|
var matchesStatus = (selectedStatus === '') || (status === selectedStatus);
|
||||||
|
|
||||||
|
$row.toggle(matchesQuery && matchesRole && matchesStatus);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).off('input.umFilter', '#um_filter_person,#um_filter_phone').on('input.umFilter', '#um_filter_person,#um_filter_phone', function() {
|
$(document).off('input.umFilter', '#um_filter_search').on('input.umFilter', '#um_filter_search', filterUsersTable);
|
||||||
applyUserFilters();
|
$(document).off('change.umFilter', '#um_filter_role,#um_filter_status').on('change.umFilter', '#um_filter_role,#um_filter_status', filterUsersTable);
|
||||||
|
|
||||||
|
$(document).off('click.umReset', '#um_filter_reset').on('click.umReset', '#um_filter_reset', function() {
|
||||||
|
$('#um_filter_search,#um_filter_role,#um_filter_status').val('');
|
||||||
|
filterUsersTable();
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document).off('click.umFilterReset', '#um_filter_reset').on('click.umFilterReset', '#um_filter_reset', function() {
|
// Quick Active / Inactive Status Switcher
|
||||||
$('#um_filter_person,#um_filter_phone').val('');
|
$(document).off('change.umToggleStatus', '.btn-toggle-user-status').on('change.umToggleStatus', '.btn-toggle-user-status', function(e) {
|
||||||
applyUserFilters();
|
var $checkbox = $(this);
|
||||||
});
|
var userId = parseInt($checkbox.data('user-id') || '0', 10);
|
||||||
|
var username = ($checkbox.data('username') || 'user').toString();
|
||||||
|
var targetChecked = $checkbox.is(':checked');
|
||||||
|
|
||||||
$(document).off('click.userDelete', '.btn-delete-user').on('click.userDelete', '.btn-delete-user', function() {
|
var confirmMsg = targetChecked ?
|
||||||
var userId = parseInt($(this).data('user-id') || '0', 10);
|
('Activate account for ' + username + '?') :
|
||||||
var username = ($(this).data('username') || 'this user').toString();
|
('Deactivate ' + username + '? Any active login sessions will be terminated immediately.');
|
||||||
if (userId <= 0 || !window.confirm('Delete ' + username + '? The account will no longer be able to sign in.')) {
|
|
||||||
|
if (!window.confirm(confirmMsg)) {
|
||||||
|
$checkbox.prop('checked', !targetChecked);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$.post('<?= base_url('setting/admin/user-management/delete') ?>/' + userId, {
|
var postData = {};
|
||||||
'<?= csrf_token() ?>': $('input[name="<?= csrf_token() ?>"]').first().val() || '<?= csrf_hash() ?>'
|
postData['<?= csrf_token() ?>'] = $('input[name="<?= csrf_token() ?>"]').first().val() || '<?= csrf_hash() ?>';
|
||||||
}, function(data) {
|
|
||||||
if (!data || parseInt(data.update || '0', 10) !== 1) {
|
$.post('<?= base_url('setting/admin/user-management/toggle-status') ?>/' + userId, postData, function(res) {
|
||||||
notify('error', 'Delete User', (data && data.error_text) || 'Unable to delete user.');
|
if (!res || parseInt(res.success || '0', 10) !== 1) {
|
||||||
|
notify('error', 'Status Toggle', (res && res.error_text) || 'Failed to update user status.');
|
||||||
|
$checkbox.prop('checked', !targetChecked);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
notify('success', 'Delete User', data.error_text || 'User deleted successfully.');
|
|
||||||
|
var newActive = parseInt(res.active || '0', 10) === 1;
|
||||||
|
$checkbox.prop('checked', newActive);
|
||||||
|
var $row = $checkbox.closest('tr');
|
||||||
|
$row.attr('data-status', newActive ? 'active' : 'inactive');
|
||||||
|
|
||||||
|
var $label = $checkbox.siblings('.status-label');
|
||||||
|
if (newActive) {
|
||||||
|
$label.removeClass('text-secondary').addClass('text-success').text('Active');
|
||||||
|
} else {
|
||||||
|
$label.removeClass('text-success').addClass('text-secondary').text('Inactive');
|
||||||
|
}
|
||||||
|
|
||||||
|
notify('success', 'User Status', res.message || 'Status updated successfully.');
|
||||||
|
}, 'json').fail(function(xhr) {
|
||||||
|
notify('error', 'Status Toggle', (xhr.responseJSON && xhr.responseJSON.error_text) || 'Request failed.');
|
||||||
|
$checkbox.prop('checked', !targetChecked);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Delete (Deactivate) User
|
||||||
|
$(document).off('click.userDelete', '.btn-delete-user').on('click.userDelete', '.btn-delete-user', function() {
|
||||||
|
var userId = parseInt($(this).data('user-id') || '0', 10);
|
||||||
|
var username = ($(this).data('username') || 'this user').toString();
|
||||||
|
if (userId <= 0 || !window.confirm('Deactivate and archive account for ' + username + '? The account will be disabled immediately.')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var postData = {};
|
||||||
|
postData['<?= csrf_token() ?>'] = $('input[name="<?= csrf_token() ?>"]').first().val() || '<?= csrf_hash() ?>';
|
||||||
|
|
||||||
|
$.post('<?= base_url('setting/admin/user-management/delete') ?>/' + userId, postData, function(data) {
|
||||||
|
if (!data || parseInt(data.update || '0', 10) !== 1) {
|
||||||
|
notify('error', 'User Deactivation', (data && data.error_text) || 'Unable to deactivate user.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
notify('success', 'User Deactivation', data.error_text || 'User deactivated successfully.');
|
||||||
load_form_div('<?= base_url('setting/admin/user-management') ?>', 'maindiv', 'User Management');
|
load_form_div('<?= base_url('setting/admin/user-management') ?>', 'maindiv', 'User Management');
|
||||||
}, 'json').fail(function(xhr) {
|
}, 'json').fail(function(xhr) {
|
||||||
notify('error', 'Delete User', (xhr.responseJSON && xhr.responseJSON.error_text) || 'Unable to delete user.');
|
notify('error', 'User Deactivation', (xhr.responseJSON && xhr.responseJSON.error_text) || 'Unable to deactivate user.');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<th>Phone Number</th>
|
<th>Phone Number</th>
|
||||||
<th>Email</th>
|
<th>Email</th>
|
||||||
|
<th>User Account</th>
|
||||||
<th>Action</th>
|
<th>Action</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
@ -34,6 +35,17 @@
|
||||||
<td><?= esc(($row->p_title ?? '') . ' ' . ($row->p_fname ?? '')) ?></td>
|
<td><?= esc(($row->p_title ?? '') . ' ' . ($row->p_fname ?? '')) ?></td>
|
||||||
<td><?= esc($row->mphone1 ?? '') ?></td>
|
<td><?= esc($row->mphone1 ?? '') ?></td>
|
||||||
<td><?= esc($row->email1 ?? '') ?></td>
|
<td><?= esc($row->email1 ?? '') ?></td>
|
||||||
|
<td>
|
||||||
|
<?php if (! empty($row->linked_username)) : ?>
|
||||||
|
<span class="badge bg-success-subtle text-success border border-success-subtle" title="Linked User Account">
|
||||||
|
<i class="bi bi-person-check me-1"></i><?= esc($row->linked_username) ?>
|
||||||
|
</span>
|
||||||
|
<?php else : ?>
|
||||||
|
<button type="button" onclick="load_form_div('<?= base_url('setting/admin/user-management/new?doctor_id=' . (int)$row->id) ?>','maindiv','Create User');" class="btn btn-outline-secondary btn-sm py-0 px-2" style="font-size: 11px;">
|
||||||
|
<i class="bi bi-person-plus me-1"></i>Link User
|
||||||
|
</button>
|
||||||
|
<?php endif; ?>
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<button type="button" class="btn btn-outline-dark btn-sm me-1 btn-qr-doctor" data-id="<?= (int)$row->id ?>" data-name="<?= esc(($row->p_title ?? '') . ' ' . ($row->p_fname ?? '')) ?>">
|
<button type="button" class="btn btn-outline-dark btn-sm me-1 btn-qr-doctor" data-id="<?= (int)$row->id ?>" data-name="<?= esc(($row->p_title ?? '') . ' ' . ($row->p_fname ?? '')) ?>">
|
||||||
<i class="bi bi-qr-code"></i> QR
|
<i class="bi bi-qr-code"></i> QR
|
||||||
|
|
@ -44,7 +56,7 @@
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
<?php else : ?>
|
<?php else : ?>
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="4" class="text-center text-muted">No doctors found.</td>
|
<td colspan="5" class="text-center text-muted">No doctors found.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@
|
||||||
<th style="width: 120px;">Designation</th>
|
<th style="width: 120px;">Designation</th>
|
||||||
<th>Qualification</th>
|
<th>Qualification</th>
|
||||||
<th>Contact / Mobile</th>
|
<th>Contact / Mobile</th>
|
||||||
|
<th style="width: 120px;">User Account</th>
|
||||||
<th style="width: 90px;">Status</th>
|
<th style="width: 90px;">Status</th>
|
||||||
<th style="width: 110px;" class="text-end pe-3">Actions</th>
|
<th style="width: 110px;" class="text-end pe-3">Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
@ -48,7 +49,7 @@
|
||||||
<tbody id="nurse_table_body">
|
<tbody id="nurse_table_body">
|
||||||
<?php if (empty($nurses)): ?>
|
<?php if (empty($nurses)): ?>
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="10" class="text-center py-4 text-muted">
|
<td colspan="11" class="text-center py-4 text-muted">
|
||||||
<i class="bi bi-person-x fs-3 d-block mb-1"></i>
|
<i class="bi bi-person-x fs-3 d-block mb-1"></i>
|
||||||
No nursing staff records found. Click <strong>"Add New Nurse"</strong> to create one.
|
No nursing staff records found. Click <strong>"Add New Nurse"</strong> to create one.
|
||||||
</td>
|
</td>
|
||||||
|
|
@ -75,6 +76,17 @@
|
||||||
<td><span class="badge bg-secondary-subtle text-dark border"><?= esc($nurse['designation'] ?: 'Staff Nurse') ?></span></td>
|
<td><span class="badge bg-secondary-subtle text-dark border"><?= esc($nurse['designation'] ?: 'Staff Nurse') ?></span></td>
|
||||||
<td><?= esc($nurse['qualification'] ?: '-') ?></td>
|
<td><?= esc($nurse['qualification'] ?: '-') ?></td>
|
||||||
<td><?= esc($nurse['contact_no'] ?: '-') ?></td>
|
<td><?= esc($nurse['contact_no'] ?: '-') ?></td>
|
||||||
|
<td>
|
||||||
|
<?php if (!empty($nurse['linked_username'])): ?>
|
||||||
|
<span class="badge bg-success-subtle text-success border border-success-subtle" title="Linked User Account">
|
||||||
|
<i class="bi bi-person-check me-1"></i><?= esc($nurse['linked_username']) ?>
|
||||||
|
</span>
|
||||||
|
<?php else: ?>
|
||||||
|
<button type="button" onclick="load_form_div('<?= base_url('setting/admin/user-management/new?nurse_id=' . (int)$nurse['id']) ?>','maindiv','Create User');" class="btn btn-outline-secondary btn-sm py-0 px-2" style="font-size: 11px;">
|
||||||
|
<i class="bi bi-person-plus me-1"></i>Link User
|
||||||
|
</button>
|
||||||
|
<?php endif; ?>
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<?php if ((int)$nurse['is_active'] === 1): ?>
|
<?php if ((int)$nurse['is_active'] === 1): ?>
|
||||||
<span class="badge bg-success">Active</span>
|
<span class="badge bg-success">Active</span>
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
</ol>
|
</ol>
|
||||||
</section>
|
</section>
|
||||||
<section class="content">
|
<section class="content">
|
||||||
<form role="form" class="form1">
|
<form role="form" class="form1" onsubmit="return false;">
|
||||||
<?= csrf_field() ?>
|
<?= csrf_field() ?>
|
||||||
<?php
|
<?php
|
||||||
$user = auth()->user();
|
$user = auth()->user();
|
||||||
|
|
@ -215,51 +215,135 @@ $(document).ready(function(){
|
||||||
load_form('<?= base_url('billing/charges/show') ?>/' + inv_id);
|
load_form('<?= base_url('billing/charges/show') ?>/' + inv_id);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('#input_qty, #input_rate').on('keydown', function(e) {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
e.preventDefault();
|
||||||
|
add_item_invoice();
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function getChargesCsrf() {
|
||||||
|
return $('input[name="<?= csrf_token() ?>"]').last().val() || '<?= csrf_hash() ?>';
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateChargesCsrf(data) {
|
||||||
|
if (!data) return;
|
||||||
|
try {
|
||||||
|
var $parsed = $('<div>').append($.parseHTML(data));
|
||||||
|
var newCsrf = $parsed.find('input[name="<?= csrf_token() ?>"]').val();
|
||||||
|
if (newCsrf) {
|
||||||
|
$('input[name="<?= csrf_token() ?>"]').val(newCsrf);
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
|
||||||
function add_item_invoice() {
|
function add_item_invoice() {
|
||||||
var csrf_value = $('input[name="<?= csrf_token() ?>"]').val();
|
var csrf_value = getChargesCsrf();
|
||||||
if ($('#input_qty').val() > 0 && $('#itype_name_id').val() > 0) {
|
var qty = parseFloat($('#input_qty').val()) || 0;
|
||||||
$.post('<?= base_url('billing/charges/item') ?>/1', {
|
var itemId = parseInt($('#itype_name_id').val(), 10) || 0;
|
||||||
"itype_name_id": $('#itype_name_id').val(),
|
var labInvoiceId = parseInt($('#lab_invoice_id').val(), 10) || 0;
|
||||||
|
|
||||||
|
if (itemId <= 0) {
|
||||||
|
alert('Please select a Charge Name before adding.');
|
||||||
|
$('#itype_name_id').select2('open');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (qty <= 0) {
|
||||||
|
alert('Quantity must be greater than 0.');
|
||||||
|
$('#input_qty').focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var $btn = $('#additem');
|
||||||
|
$btn.prop('disabled', true);
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: '<?= base_url('billing/charges/item') ?>/1',
|
||||||
|
type: 'POST',
|
||||||
|
data: {
|
||||||
|
"itype_name_id": itemId,
|
||||||
"itype_idv": $('#itype_idv').val(),
|
"itype_idv": $('#itype_idv').val(),
|
||||||
"lab_invoice_id": $('#lab_invoice_id').val(),
|
"lab_invoice_id": labInvoiceId,
|
||||||
"ins_id": $('#ins_id').val(),
|
"ins_id": $('#ins_id').val(),
|
||||||
"input_qty": $('#input_qty').val(),
|
"input_qty": $('#input_qty').val(),
|
||||||
"input_rate": $('#input_rate').val(),
|
"input_rate": $('#input_rate').val(),
|
||||||
"<?= csrf_token() ?>": csrf_value
|
"<?= csrf_token() ?>": csrf_value
|
||||||
}, function(data){
|
},
|
||||||
|
success: function(data){
|
||||||
$('#show_item_list').html(data);
|
$('#show_item_list').html(data);
|
||||||
});
|
updateChargesCsrf(data);
|
||||||
}
|
$('#input_rate').val('0.00');
|
||||||
|
$('#input_qty').val('1');
|
||||||
|
$('#itype_name_id').focus();
|
||||||
|
},
|
||||||
|
error: function(jqXHR, textStatus, errorThrown) {
|
||||||
|
console.error('add_item_invoice error', jqXHR.status, errorThrown);
|
||||||
|
var errMsg = 'Failed to add item.';
|
||||||
|
if (jqXHR.status === 403) {
|
||||||
|
errMsg = 'Permission Denied: Your account does not have permission to add or edit charges.';
|
||||||
|
} else if (jqXHR.responseText && jqXHR.responseText.indexOf('route') === -1) {
|
||||||
|
errMsg = jqXHR.responseText.replace(/<[^>]*>/g, ' ').substring(0, 150);
|
||||||
|
}
|
||||||
|
if (typeof window.notify === 'function') {
|
||||||
|
window.notify('danger', 'Charges Error: ', errMsg);
|
||||||
|
} else {
|
||||||
|
alert(errMsg);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
complete: function() {
|
||||||
|
$btn.prop('disabled', false);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function remove_item_invoice(itemid) {
|
function remove_item_invoice(itemid) {
|
||||||
var csrf_value = $('input[name="<?= csrf_token() ?>"]').val();
|
var csrf_value = getChargesCsrf();
|
||||||
if (confirm('Are you sure Remove this item')) {
|
if (confirm('Are you sure you want to remove this item?')) {
|
||||||
$.post('<?= base_url('billing/charges/item') ?>/0', {
|
$.ajax({
|
||||||
"itemid": itemid,
|
url: '<?= base_url('billing/charges/item') ?>/0',
|
||||||
"lab_invoice_id": $('#lab_invoice_id').val(),
|
type: 'POST',
|
||||||
"<?= csrf_token() ?>": csrf_value
|
data: {
|
||||||
}, function(data){
|
"itemid": itemid,
|
||||||
$('#show_item_list').html(data);
|
"lab_invoice_id": $('#lab_invoice_id').val(),
|
||||||
|
"<?= csrf_token() ?>": csrf_value
|
||||||
|
},
|
||||||
|
success: function(data){
|
||||||
|
$('#show_item_list').html(data);
|
||||||
|
updateChargesCsrf(data);
|
||||||
|
},
|
||||||
|
error: function(jqXHR, textStatus, errorThrown) {
|
||||||
|
console.error('remove_item_invoice error', jqXHR.status, errorThrown);
|
||||||
|
alert('Error removing item: ' + (jqXHR.statusText || 'Failed'));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function update_qty(itemid) {
|
function update_qty(itemid) {
|
||||||
var csrf_value = $('input[name="<?= csrf_token() ?>"]').val();
|
var csrf_value = getChargesCsrf();
|
||||||
if (confirm('Are you sure Update this item')) {
|
if (confirm('Are you sure you want to update this item?')) {
|
||||||
var update_qty = $('#input_qty_' + itemid).val();
|
var update_qty_val = $('#input_qty_' + itemid).val();
|
||||||
var item_rate = $('#hidden_rate_' + itemid).val();
|
var item_rate_val = $('#hidden_rate_' + itemid).val();
|
||||||
$.post('<?= base_url('billing/charges/item') ?>/2', {
|
$.ajax({
|
||||||
"itemid": itemid,
|
url: '<?= base_url('billing/charges/item') ?>/2',
|
||||||
"lab_invoice_id": $('#lab_invoice_id').val(),
|
type: 'POST',
|
||||||
"update_qty": update_qty,
|
data: {
|
||||||
"item_rate": item_rate,
|
"itemid": itemid,
|
||||||
"<?= csrf_token() ?>": csrf_value
|
"lab_invoice_id": $('#lab_invoice_id').val(),
|
||||||
}, function(data){
|
"update_qty": update_qty_val,
|
||||||
$('#show_item_list').html(data);
|
"item_rate": item_rate_val,
|
||||||
|
"<?= csrf_token() ?>": csrf_value
|
||||||
|
},
|
||||||
|
success: function(data){
|
||||||
|
$('#show_item_list').html(data);
|
||||||
|
updateChargesCsrf(data);
|
||||||
|
},
|
||||||
|
error: function(jqXHR, textStatus, errorThrown) {
|
||||||
|
console.error('update_qty error', jqXHR.status, errorThrown);
|
||||||
|
alert('Error updating item: ' + (jqXHR.statusText || 'Failed'));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@
|
||||||
<div class="border rounded p-2">
|
<div class="border rounded p-2">
|
||||||
<?php foreach ($doc_spec_l as $row) : ?>
|
<?php foreach ($doc_spec_l as $row) : ?>
|
||||||
<label class="d-block">
|
<label class="d-block">
|
||||||
<input type="radio" id="rdoc_id" name="rdoc_id" class="form-check-input me-1" value="<?= esc($row->id) ?>">
|
<input type="radio" id="rdoc_id" name="rdoc_id" class="form-check-input me-1" value="<?= esc($row->id) ?>" <?= (! empty($default_doc_id) && (int) $default_doc_id === (int) $row->id) ? 'checked' : '' ?>>
|
||||||
<?= esc($row->p_fname) ?> [<i><?= esc($row->SpecName) ?></i>]
|
<?= esc($row->p_fname) ?> [<i><?= esc($row->SpecName) ?></i>]
|
||||||
</label>
|
</label>
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@
|
||||||
<div class="border rounded p-2">
|
<div class="border rounded p-2">
|
||||||
<?php foreach ($doc_spec_l as $row) : ?>
|
<?php foreach ($doc_spec_l as $row) : ?>
|
||||||
<label class="d-block">
|
<label class="d-block">
|
||||||
<input type="radio" id="rdoc_id" name="rdoc_id" class="form-check-input me-1" value="<?= esc($row->id) ?>">
|
<input type="radio" id="rdoc_id" name="rdoc_id" class="form-check-input me-1" value="<?= esc($row->id) ?>" <?= (! empty($default_doc_id) && (int) $default_doc_id === (int) $row->id) ? 'checked' : '' ?>>
|
||||||
<?= esc($row->p_fname) ?> [<i><?= esc($row->SpecName) ?></i>]
|
<?= esc($row->p_fname) ?> [<i><?= esc($row->SpecName) ?></i>]
|
||||||
</label>
|
</label>
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,9 @@ $admRecordedAt = !empty($latestAdmissionEntry['recorded_at'])
|
||||||
? date('Y-m-d\TH:i', strtotime($latestAdmissionEntry['recorded_at']))
|
? date('Y-m-d\TH:i', strtotime($latestAdmissionEntry['recorded_at']))
|
||||||
: date('Y-m-d\TH:i');
|
: date('Y-m-d\TH:i');
|
||||||
$admNurseId = (int) ($latestAdmissionEntry['recorded_by_id'] ?? 0);
|
$admNurseId = (int) ($latestAdmissionEntry['recorded_by_id'] ?? 0);
|
||||||
|
if ($admNurseId === 0 && ! empty($default_nurse_id)) {
|
||||||
|
$admNurseId = (int) $default_nurse_id;
|
||||||
|
}
|
||||||
$admComplaints = (string) ($latestAdmissionEntry['treatment_text'] ?? '');
|
$admComplaints = (string) ($latestAdmissionEntry['treatment_text'] ?? '');
|
||||||
$admTempF = (isset($latestAdmissionEntry['temperature_c']) && $latestAdmissionEntry['temperature_c'] !== null && $latestAdmissionEntry['temperature_c'] !== '')
|
$admTempF = (isset($latestAdmissionEntry['temperature_c']) && $latestAdmissionEntry['temperature_c'] !== null && $latestAdmissionEntry['temperature_c'] !== '')
|
||||||
? $toFahrenheit($latestAdmissionEntry['temperature_c'])
|
? $toFahrenheit($latestAdmissionEntry['temperature_c'])
|
||||||
|
|
@ -560,7 +563,7 @@ $treatmentEntries = array_values(array_filter($entries, static fn ($r) => ($r['e
|
||||||
<select name="recorded_by_nurse_id" class="form-select">
|
<select name="recorded_by_nurse_id" class="form-select">
|
||||||
<option value="0">-- Select Nursing Staff --</option>
|
<option value="0">-- Select Nursing Staff --</option>
|
||||||
<?php foreach (($nurse_list ?? []) as $nRow): ?>
|
<?php foreach (($nurse_list ?? []) as $nRow): ?>
|
||||||
<option value="<?= (int) $nRow['id'] ?>">
|
<option value="<?= (int) $nRow['id'] ?>" <?= (! empty($default_nurse_id) && (int) $default_nurse_id === (int) $nRow['id']) ? 'selected' : '' ?>>
|
||||||
<?= esc(($nRow['nurse_code'] ? '[' . $nRow['nurse_code'] . '] ' : '') . $nRow['name']) ?>
|
<?= esc(($nRow['nurse_code'] ? '[' . $nRow['nurse_code'] . '] ' : '') . $nRow['name']) ?>
|
||||||
</option>
|
</option>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
|
|
@ -653,7 +656,7 @@ $treatmentEntries = array_values(array_filter($entries, static fn ($r) => ($r['e
|
||||||
<select name="recorded_by_nurse_id" class="form-select">
|
<select name="recorded_by_nurse_id" class="form-select">
|
||||||
<option value="0">-- Select Nursing Staff --</option>
|
<option value="0">-- Select Nursing Staff --</option>
|
||||||
<?php foreach (($nurse_list ?? []) as $nRow): ?>
|
<?php foreach (($nurse_list ?? []) as $nRow): ?>
|
||||||
<option value="<?= (int) $nRow['id'] ?>">
|
<option value="<?= (int) $nRow['id'] ?>" <?= (! empty($default_nurse_id) && (int) $default_nurse_id === (int) $nRow['id']) ? 'selected' : '' ?>>
|
||||||
<?= esc(($nRow['nurse_code'] ? '[' . $nRow['nurse_code'] . '] ' : '') . $nRow['name']) ?>
|
<?= esc(($nRow['nurse_code'] ? '[' . $nRow['nurse_code'] . '] ' : '') . $nRow['name']) ?>
|
||||||
</option>
|
</option>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
|
|
@ -757,7 +760,7 @@ $treatmentEntries = array_values(array_filter($entries, static fn ($r) => ($r['e
|
||||||
<select name="recorded_by_nurse_id" class="form-select">
|
<select name="recorded_by_nurse_id" class="form-select">
|
||||||
<option value="0">-- Select Nursing Staff --</option>
|
<option value="0">-- Select Nursing Staff --</option>
|
||||||
<?php foreach (($nurse_list ?? []) as $nRow): ?>
|
<?php foreach (($nurse_list ?? []) as $nRow): ?>
|
||||||
<option value="<?= (int) $nRow['id'] ?>">
|
<option value="<?= (int) $nRow['id'] ?>" <?= (! empty($default_nurse_id) && (int) $default_nurse_id === (int) $nRow['id']) ? 'selected' : '' ?>>
|
||||||
<?= esc(($nRow['nurse_code'] ? '[' . $nRow['nurse_code'] . '] ' : '') . $nRow['name']) ?>
|
<?= esc(($nRow['nurse_code'] ? '[' . $nRow['nurse_code'] . '] ' : '') . $nRow['name']) ?>
|
||||||
</option>
|
</option>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,11 @@
|
||||||
<?php
|
<?php
|
||||||
$user = function_exists('auth') ? auth()->user() : null;
|
$user = function_exists('auth') ? auth()->user() : null;
|
||||||
|
|
||||||
|
$isAdmin = false;
|
||||||
|
if ($user && method_exists($user, 'inGroup')) {
|
||||||
|
$isAdmin = $user->inGroup('superadmin', 'admin', 'developer');
|
||||||
|
}
|
||||||
|
|
||||||
$canTemplate = false;
|
$canTemplate = false;
|
||||||
if ($user && method_exists($user, 'can')) {
|
if ($user && method_exists($user, 'can')) {
|
||||||
$canTemplate = $user->can('template.pathology')
|
$canTemplate = $user->can('template.pathology')
|
||||||
|
|
@ -11,18 +17,10 @@ if ($user && method_exists($user, 'can')) {
|
||||||
|| $user->can('template.*');
|
|| $user->can('template.*');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! $canTemplate && $user && method_exists($user, 'inGroup')) {
|
if (! $canTemplate && $isAdmin) {
|
||||||
$canTemplate = $user->inGroup('superadmin', 'admin', 'developer');
|
$canTemplate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$canIpdBilling = $user && method_exists($user, 'can')
|
|
||||||
? ($user->can('billing.ipd.access')
|
|
||||||
|| $user->can('billing.ipd.current-admission')
|
|
||||||
|| $user->can('billing.ipd.invoice')
|
|
||||||
|| $user->can('billing.ipd.cash-balance')
|
|
||||||
|| $user->can('billing.access'))
|
|
||||||
: false;
|
|
||||||
|
|
||||||
$canIpdNursing = $user && method_exists($user, 'can')
|
$canIpdNursing = $user && method_exists($user, 'can')
|
||||||
? ($user->can('ipd_nursing.view') || $user->can('ipd_nursing.*'))
|
? ($user->can('ipd_nursing.view') || $user->can('ipd_nursing.*'))
|
||||||
: false;
|
: false;
|
||||||
|
|
@ -32,43 +30,34 @@ $canIpdDischarge = $user && method_exists($user, 'can')
|
||||||
$canIpdOt = $user && method_exists($user, 'can')
|
$canIpdOt = $user && method_exists($user, 'can')
|
||||||
? ($user->can('ipd_ot.view') || $user->can('ipd_ot.request.manage') || $user->can('ipd_ot.schedule.manage') || $user->can('ipd_ot.status.manage') || $user->can('ipd_ot.postop.manage') || $user->can('ipd_ot.*'))
|
? ($user->can('ipd_ot.view') || $user->can('ipd_ot.request.manage') || $user->can('ipd_ot.schedule.manage') || $user->can('ipd_ot.status.manage') || $user->can('ipd_ot.postop.manage') || $user->can('ipd_ot.*'))
|
||||||
: false;
|
: false;
|
||||||
if ($user && method_exists($user, 'inGroup') && $user->inGroup('superadmin', 'admin', 'developer')) {
|
if ($isAdmin) {
|
||||||
$canIpdNursing = true;
|
$canIpdNursing = true;
|
||||||
$canIpdDischarge = true;
|
$canIpdDischarge = true;
|
||||||
$canIpdOt = true;
|
$canIpdOt = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$canBilling = false;
|
// Granular billing links visibility:
|
||||||
if ($user && method_exists($user, 'can')) {
|
$canPatientList = $isAdmin || ($user && method_exists($user, 'can') && ($user->can('billing.access') || $user->can('billing.opd.edit') || $user->can('billing.*')));
|
||||||
$canBilling = $user->can('billing.access')
|
$canOpdInvoice = $isAdmin || ($user && method_exists($user, 'can') && ($user->can('billing.access') || $user->can('billing.opd.edit') || $user->can('billing.*')));
|
||||||
|| $user->can('billing.opd.edit')
|
$canChargesInvoice = $isAdmin || ($user && method_exists($user, 'can') && ($user->can('billing.charges.view') || $user->can('billing.access') || $user->can('billing.*')));
|
||||||
|| $user->can('billing.opd.pay')
|
$canIpdBilling = $isAdmin || ($user && method_exists($user, 'can') && ($user->can('billing.ipd.access') || $user->can('billing.ipd.current-admission') || $user->can('billing.ipd.invoice') || $user->can('billing.ipd.cash-balance') || $user->can('billing.access') || $user->can('billing.*')));
|
||||||
|| $user->can('billing.charges.view')
|
$canOrgOpdInvoice = $isAdmin || ($user && method_exists($user, 'can') && ($user->can('billing.access') || $user->can('billing.opd.edit') || $user->can('billing.*')));
|
||||||
|| $user->can('billing.charges.edit')
|
$canPaymentRequest = $isAdmin || ($user && method_exists($user, 'can') && ($user->can('billing.payment_request.view') || $user->can('billing.payment_request.manage') || $user->can('billing.*')));
|
||||||
|| $user->can('billing.charges.date-edit')
|
$canRefundRequest = $isAdmin || ($user && method_exists($user, 'can') && ($user->can('billing.refund.view') || $user->can('billing.refund.manage') || $user->can('billing.*')));
|
||||||
|| $user->can('billing.charges.pay')
|
$canReportPanel = $isAdmin || ($user && method_exists($user, 'can') && ($user->can('billing.access') || $user->can('reports.access') || $user->can('billing.*')));
|
||||||
|| $user->can('billing.charges.cancel')
|
$canOrgPacking = $isAdmin || ($user && method_exists($user, 'can') && ($user->can('billing.access') || $user->can('billing.charges.view') || $user->can('billing.*')));
|
||||||
|| $user->can('billing.charges.correct')
|
$canFinanceBilling = $isAdmin || ($user && method_exists($user, 'can') && ($user->can('finance.cash.billing.submit') || $user->can('finance.*')));
|
||||||
|| $user->can('billing.ipd.access')
|
|
||||||
|| $user->can('billing.ipd.current-admission')
|
|
||||||
|| $user->can('billing.ipd.invoice')
|
|
||||||
|| $user->can('billing.ipd.cash-balance')
|
|
||||||
|| $user->can('billing.ipd.export')
|
|
||||||
|| $user->can('billing.*');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! $canBilling && $user && method_exists($user, 'inGroup')) {
|
$canBilling = $canPatientList || $canOpdInvoice || $canChargesInvoice || $canIpdBilling || $canOrgOpdInvoice || $canPaymentRequest || $canRefundRequest || $canReportPanel || $canOrgPacking || $canFinanceBilling;
|
||||||
$canBilling = $user->inGroup('superadmin', 'admin', 'developer');
|
|
||||||
}
|
|
||||||
|
|
||||||
$canPharmacy = true;
|
$canPharmacy = false;
|
||||||
if ($user && method_exists($user, 'can')) {
|
if ($user && method_exists($user, 'can')) {
|
||||||
$canPharmacy = $user->can('pharmacy.access')
|
$canPharmacy = $user->can('pharmacy.access')
|
||||||
|| $user->can('billing.access');
|
|| $user->can('pharmacy.*');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! $canPharmacy && $user && method_exists($user, 'inGroup')) {
|
if (! $canPharmacy && ($isAdmin || ($user && method_exists($user, 'inGroup') && $user->inGroup('pharmacy_admin')))) {
|
||||||
$canPharmacy = $user->inGroup('superadmin', 'admin', 'developer');
|
$canPharmacy = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$canHospitalStock = false;
|
$canHospitalStock = false;
|
||||||
|
|
@ -150,30 +139,18 @@ if (! $canAbdm && $user && method_exists($user, 'inGroup')) {
|
||||||
$canAbdm = $user->inGroup('superadmin', 'admin', 'developer');
|
$canAbdm = $user->inGroup('superadmin', 'admin', 'developer');
|
||||||
}
|
}
|
||||||
|
|
||||||
$canBedStatus = false;
|
// Bed Status is open for all logged-in users
|
||||||
if ($user && method_exists($user, 'can')) {
|
$canBedStatus = ($user !== null);
|
||||||
$canBedStatus = $user->can('settings.bed_status.view')
|
|
||||||
|| $user->can('admin.settings')
|
|
||||||
|| $user->can('admin.access');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! $canBedStatus && $user && method_exists($user, 'inGroup')) {
|
|
||||||
$canBedStatus = $user->inGroup('superadmin', 'admin', 'developer');
|
|
||||||
}
|
|
||||||
|
|
||||||
$canReports = false;
|
// Granular reports permissions:
|
||||||
if ($user && method_exists($user, 'can')) {
|
$canCollectionReport = $isAdmin || ($user && method_exists($user, 'can') && ($user->can('reports.collection.view') || $user->can('reports.access') || $user->can('reports.*')));
|
||||||
$canReports = $user->can('reports.access')
|
$canOldPaymentReport = $isAdmin || ($user && method_exists($user, 'can') && ($user->can('reports.collection.view') || $user->can('reports.access') || $user->can('reports.*')));
|
||||||
|| $user->can('reports.collection.view')
|
$canOpdTotalReport = $isAdmin || ($user && method_exists($user, 'can') && ($user->can('reports.access') || $user->can('reports.*') || $user->can('billing.access')));
|
||||||
|| $user->can('reports.insurance_credit.view')
|
$canInsuranceCreditReport = $isAdmin || ($user && method_exists($user, 'can') && ($user->can('reports.insurance_credit.view') || $user->can('reports.access') || $user->can('reports.*')));
|
||||||
|| $user->can('reports.nabh_audit.view')
|
$canNabhAuditReport = $isAdmin || ($user && method_exists($user, 'can') && ($user->can('reports.nabh_audit.view') || $user->can('reports.access') || $user->can('reports.*')));
|
||||||
|| $user->can('diagnosis.report.view')
|
|
||||||
|| $user->can('diagnosis.access');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! $canReports && $user && method_exists($user, 'inGroup')) {
|
$canReports = $canCollectionReport || $canOldPaymentReport || $canOpdTotalReport || $canDiagnosis || $canInsuranceCreditReport || $canNabhAuditReport;
|
||||||
$canReports = $user->inGroup('superadmin', 'admin', 'developer');
|
|
||||||
}
|
|
||||||
|
|
||||||
$canAdminPanel = false;
|
$canAdminPanel = false;
|
||||||
if ($user && method_exists($user, 'can')) {
|
if ($user && method_exists($user, 'can')) {
|
||||||
|
|
@ -212,63 +189,79 @@ if (! $canChargesSettings && $user && method_exists($user, 'inGroup')) {
|
||||||
</li>
|
</li>
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
<?php if ($canBilling) { ?>
|
<?php if ($canBilling) { ?>
|
||||||
|
|
||||||
<li class="nav-heading">Billing</li>
|
<li class="nav-heading">Billing</li>
|
||||||
|
<?php if ($canPatientList) { ?>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('/billing/patient') ?>','Patient List')">
|
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('/billing/patient') ?>','Patient List')">
|
||||||
<i class="bi bi-person-lines-fill"></i>
|
<i class="bi bi-person-lines-fill"></i>
|
||||||
<span>Patient List</span>
|
<span>Patient List</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<?php } ?>
|
||||||
|
<?php if ($canOpdInvoice) { ?>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('Invoice/opdlist') ?>','OPD Invoice')">
|
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('Invoice/opdlist') ?>','OPD Invoice')">
|
||||||
<i class="bi bi-receipt"></i>
|
<i class="bi bi-receipt"></i>
|
||||||
<span>OPD Invoice</span>
|
<span>OPD Invoice</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<?php } ?>
|
||||||
|
<?php if ($canChargesInvoice) { ?>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('Invoice/chargeslist') ?>','Charges Invoice')">
|
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('Invoice/chargeslist') ?>','Charges Invoice')">
|
||||||
<i class="bi bi-journal-text"></i>
|
<i class="bi bi-journal-text"></i>
|
||||||
<span>Charges Invoice</span>
|
<span>Charges Invoice</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<?php } ?>
|
||||||
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('/billing/ipd') ?>','IPD List')">
|
<?php if ($canIpdBilling) { ?>
|
||||||
<i class="bi bi-hospital"></i>
|
<li class="nav-item">
|
||||||
<span>IPD Billing</span>
|
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('/billing/ipd') ?>','IPD List')">
|
||||||
</a>
|
<i class="bi bi-hospital"></i>
|
||||||
</li>
|
<span>IPD Billing</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<?php } ?>
|
||||||
|
<?php if ($canOrgOpdInvoice) { ?>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('Orgcase/search_all') ?>','Org. OPD\'s Invoice')">
|
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('Orgcase/search_all') ?>','Org. OPD\'s Invoice')">
|
||||||
<i class="bi bi-receipt-cutoff"></i>
|
<i class="bi bi-receipt-cutoff"></i>
|
||||||
<span>Org. OPD's Invoice</span>
|
<span>Org. OPD's Invoice</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<?php } ?>
|
||||||
|
<?php if ($canPaymentRequest) { ?>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('Invoice/list_req_payment') ?>','Payment Request')">
|
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('Invoice/list_req_payment') ?>','Payment Request')">
|
||||||
<i class="bi bi-cash-coin"></i>
|
<i class="bi bi-cash-coin"></i>
|
||||||
<span>Payment Request</span>
|
<span>Payment Request</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<?php } ?>
|
||||||
|
<?php if ($canRefundRequest) { ?>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('Invoice/list_refund') ?>','Refund Amount Request')">
|
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('Invoice/list_refund') ?>','Refund Amount Request')">
|
||||||
<i class="bi bi-arrow-counterclockwise"></i>
|
<i class="bi bi-arrow-counterclockwise"></i>
|
||||||
<span>Refund Amount Request</span>
|
<span>Refund Amount Request</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<?php } ?>
|
||||||
|
<?php if ($canReportPanel) { ?>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('Report/index') ?>','Report Panel')">
|
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('Report/index') ?>','Report Panel')">
|
||||||
<i class="bi bi-grid-1x2"></i>
|
<i class="bi bi-grid-1x2"></i>
|
||||||
<span>Report Panel</span>
|
<span>Report Panel</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<?php } ?>
|
||||||
|
<?php if ($canOrgPacking) { ?>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('org-packing') ?>','Organization Packing')">
|
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('org-packing') ?>','Organization Packing')">
|
||||||
<i class="bi bi-box-seam"></i>
|
<i class="bi bi-box-seam"></i>
|
||||||
<span>Org Packing </span>
|
<span>Org Packing </span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<?php } ?>
|
||||||
<?php if ($canFinanceBilling) { ?>
|
<?php if ($canFinanceBilling) { ?>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('billing/cash-submission/create') ?>','Cash Submission')">
|
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('billing/cash-submission/create') ?>','Cash Submission')">
|
||||||
|
|
@ -489,24 +482,30 @@ if (! $canChargesSettings && $user && method_exists($user, 'inGroup')) {
|
||||||
<?php if ($canReports) { ?>
|
<?php if ($canReports) { ?>
|
||||||
<li class="nav-heading">Reports</li>
|
<li class="nav-heading">Reports</li>
|
||||||
|
|
||||||
|
<?php if ($canCollectionReport) { ?>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('Report/collection_report') ?>','Collection Report')">
|
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('Report/collection_report') ?>','Collection Report')">
|
||||||
<i class="bi bi-graph-up"></i>
|
<i class="bi bi-graph-up"></i>
|
||||||
<span>Collection Report</span>
|
<span>Collection Report</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<?php } ?>
|
||||||
|
<?php if ($canOldPaymentReport) { ?>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('Report/old_payment_received_report') ?>','Old Payment Received Report')">
|
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('Report/old_payment_received_report') ?>','Old Payment Received Report')">
|
||||||
<i class="bi bi-clock-history"></i>
|
<i class="bi bi-clock-history"></i>
|
||||||
<span>Old Payment Received</span>
|
<span>Old Payment Received</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<?php } ?>
|
||||||
|
<?php if ($canOpdTotalReport) { ?>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('Report/report_opd_total') ?>','OPD Total Report')">
|
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('Report/report_opd_total') ?>','OPD Total Report')">
|
||||||
<i class="bi bi-bar-chart"></i>
|
<i class="bi bi-bar-chart"></i>
|
||||||
<span>OPD Total Report</span>
|
<span>OPD Total Report</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<?php } ?>
|
||||||
<?php if ($canDiagnosis) { ?>
|
<?php if ($canDiagnosis) { ?>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('Report/diagnosis_report') ?>','Diagnosis Report')">
|
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('Report/diagnosis_report') ?>','Diagnosis Report')">
|
||||||
|
|
@ -515,18 +514,22 @@ if (! $canChargesSettings && $user && method_exists($user, 'inGroup')) {
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
|
<?php if ($canInsuranceCreditReport) { ?>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('Report/insurance_credit_main') ?>','Insurance Credit')">
|
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('Report/insurance_credit_main') ?>','Insurance Credit')">
|
||||||
<i class="bi bi-file-medical"></i>
|
<i class="bi bi-file-medical"></i>
|
||||||
<span>Insurance Credit </span>
|
<span>Insurance Credit </span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<?php } ?>
|
||||||
|
<?php if ($canNabhAuditReport) { ?>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('Report/nabh_audit_report') ?>','NABH Audit Report')">
|
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('Report/nabh_audit_report') ?>','NABH Audit Report')">
|
||||||
<i class="bi bi-clipboard2-check"></i>
|
<i class="bi bi-clipboard2-check"></i>
|
||||||
<span>NABH Audit Report</span>
|
<span>NABH Audit Report</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<?php } ?>
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -295,12 +295,74 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleAuthError(jqXHR) {
|
function handleAuthError(jqXHR, containerId) {
|
||||||
if (jqXHR.status === 401 || jqXHR.status === 403) {
|
if (!jqXHR) {
|
||||||
alert('You have been logged out. Please login again.');
|
return false;
|
||||||
window.location.href = '/login';
|
}
|
||||||
|
|
||||||
|
// HTTP 401: Unauthorized -> User session is genuinely lost or expired
|
||||||
|
if (jqXHR.status === 401) {
|
||||||
|
alert('Your session has expired or you have been logged out. Please login again.');
|
||||||
|
window.location.href = '<?= base_url('login') ?>';
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HTTP 403: Forbidden -> User IS logged in, but lacks permission for this action/link
|
||||||
|
if (jqXHR.status === 403) {
|
||||||
|
var raw = (jqXHR.responseText ? String(jqXHR.responseText) : '');
|
||||||
|
var detailMsg = '';
|
||||||
|
if (raw && !/<!DOCTYPE/i.test(raw)) {
|
||||||
|
detailMsg = raw.replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ').trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
var accessDeniedHtml =
|
||||||
|
'<div class="container-fluid py-5">' +
|
||||||
|
'<div class="row justify-content-center">' +
|
||||||
|
'<div class="col-12 col-md-8 col-lg-6">' +
|
||||||
|
'<div class="card shadow-sm border-0 text-center p-4" style="border-radius: 12px; background: #fff;">' +
|
||||||
|
'<div class="card-body p-3">' +
|
||||||
|
'<div class="mb-3">' +
|
||||||
|
'<span class="d-inline-flex align-items-center justify-content-center rounded-circle bg-danger-subtle text-danger" style="width: 76px; height: 76px; font-size: 36px;">' +
|
||||||
|
'<i class="bi bi-shield-lock-fill"></i>' +
|
||||||
|
'</span>' +
|
||||||
|
'</div>' +
|
||||||
|
'<h4 class="fw-bold text-dark mb-2">Access Restricted</h4>' +
|
||||||
|
'<p class="text-muted mb-3" style="font-size: 14px;">You do not have permission to access this module or view this information with your current user account.</p>' +
|
||||||
|
'<div class="alert alert-warning border-0 bg-warning-subtle text-warning-emphasis text-start p-3 mb-4 rounded-3 small">' +
|
||||||
|
'<div class="fw-bold mb-1"><i class="bi bi-info-circle-fill me-1"></i> How to get access:</div>' +
|
||||||
|
'<div>Please communicate with your <strong>Hospital Administrator</strong> or IT Department to assign the required permissions or roles to your account.</div>' +
|
||||||
|
(detailMsg ? '<div class="mt-2 pt-2 border-top border-warning-subtle text-muted" style="font-size: 11px;">Notice: ' + detailMsg.substring(0, 160) + '</div>' : '') +
|
||||||
|
'</div>' +
|
||||||
|
'<div class="d-flex justify-content-center gap-2 flex-wrap">' +
|
||||||
|
'<button type="button" class="btn btn-outline-secondary px-3 btn-sm" onclick="if(window.history.length > 1){ window.history.back(); } else { load_form(\'<?= base_url('dashboard') ?>\', \'Dashboard\'); }">' +
|
||||||
|
'<i class="bi bi-arrow-left me-1"></i> Go Back' +
|
||||||
|
'</button>' +
|
||||||
|
'<button type="button" class="btn btn-primary px-3 btn-sm" onclick="load_form(\'<?= base_url('dashboard') ?>\', \'Dashboard\');">' +
|
||||||
|
'<i class="bi bi-grid me-1"></i> Back to Dashboard' +
|
||||||
|
'</button>' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>';
|
||||||
|
|
||||||
|
if (containerId) {
|
||||||
|
var targetEl = document.getElementById(String(containerId));
|
||||||
|
if (targetEl) {
|
||||||
|
targetEl.innerHTML = accessDeniedHtml;
|
||||||
|
} else if (window.jQuery) {
|
||||||
|
$('#' + containerId).html(accessDeniedHtml);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof window.notify === 'function') {
|
||||||
|
window.notify('danger', 'Access Restricted: ', 'You do not have permission to access this module. Please communicate with your Hospital Administrator.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -377,7 +439,7 @@
|
||||||
})
|
})
|
||||||
.fail(function(jqXHR, textStatus, errorThrown) {
|
.fail(function(jqXHR, textStatus, errorThrown) {
|
||||||
$("#wait").css("display", "none");
|
$("#wait").css("display", "none");
|
||||||
if (handleAuthError(jqXHR)) return;
|
if (handleAuthError(jqXHR, 'main')) return;
|
||||||
console.error('load_form failed', {
|
console.error('load_form failed', {
|
||||||
url: normalizedUrl,
|
url: normalizedUrl,
|
||||||
status: jqXHR.status,
|
status: jqXHR.status,
|
||||||
|
|
@ -428,7 +490,7 @@
|
||||||
if (top_title !== '') document.title = top_title;
|
if (top_title !== '') document.title = top_title;
|
||||||
})
|
})
|
||||||
.fail(function(jqXHR, textStatus, errorThrown) {
|
.fail(function(jqXHR, textStatus, errorThrown) {
|
||||||
if (handleAuthError(jqXHR)) return;
|
if (handleAuthError(jqXHR, xdiv)) return;
|
||||||
console.error('load_form_div failed', {
|
console.error('load_form_div failed', {
|
||||||
url: ourl,
|
url: ourl,
|
||||||
status: jqXHR.status,
|
status: jqXHR.status,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user