Initial commit

This commit is contained in:
Porawit Dongwang
2026-09-16 23:20:08 +07:00
commit 0041668dbb
32577 changed files with 3687927 additions and 0 deletions
@@ -0,0 +1,8 @@
<IfModule mod_rewrite.c>
RewriteEngine On
# Send all requests to index.php if they are not a real file or directory
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]
</IfModule>
@@ -0,0 +1,94 @@
/* Modern Hospital Theme - Glassmorphism & Gradient */
:root {
--primary-color: #0d6efd;
--secondary-color: #6c757d;
--background-start: #e0eafc;
--background-end: #cfdef3;
--glass-bg: rgba(255, 255, 255, 0.25);
--glass-border: rgba(255, 255, 255, 0.18);
--glass-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
}
body {
background: linear-gradient(135deg, var(--background-start) 0%, var(--background-end) 100%);
min-height: 100vh;
font-family: 'Inter', sans-serif; /* Recommended modern font */
}
/* Glassmorphism Card Utility */
.glass-card {
background: var(--glass-bg);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border: 1px solid var(--glass-border);
box-shadow: var(--glass-shadow);
border-radius: 15px;
padding: 20px;
}
/* Loading button animation */
.btn-loading {
position: relative;
pointer-events: none;
opacity: 0.8;
}
.btn-loading::after {
content: '';
position: absolute;
width: 1rem;
height: 1rem;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
border: 2px solid transparent;
border-top-color: currentColor;
border-radius: 50%;
animation: button-loading-spinner 1s ease infinite;
}
@keyframes button-loading-spinner {
from { transform: rotate(0turn); }
to { transform: rotate(1turn); }
}
/* Sidebar & Wrapper Layout */
#wrapper {
overflow-x: hidden;
min-height: 100vh;
}
.glass-sidebar {
min-height: 100vh;
width: 250px;
background: rgba(255, 255, 255, 0.4);
backdrop-filter: blur(15px);
-webkit-backdrop-filter: blur(15px);
border-right: 1px solid var(--glass-border);
transition: margin 0.25s ease-out;
}
.glass-sidebar .list-group-item {
transition: all 0.2s ease;
}
.glass-sidebar .list-group-item:hover, .active-menu {
background: rgba(13, 110, 253, 0.1) !important;
color: var(--primary-color) !important;
transform: translateX(5px);
}
#page-content-wrapper {
min-width: 100vw;
}
@media (min-width: 992px) {
#page-content-wrapper {
min-width: 0;
width: 100%;
}
}
#wrapper.toggled #sidebar-wrapper {
margin-left: -250px;
}
@@ -0,0 +1,24 @@
/**
* Main JavaScript File for Hospital Payroll Management System
*/
document.addEventListener('DOMContentLoaded', () => {
console.log('Hospital Payroll Management System - Initialized');
// Setup global CSRF token for AJAX requests if available
const csrfToken = document.querySelector('meta[name="csrf-token"]');
if (csrfToken) {
// Axios or fetch interceptor can be setup here
}
});
/**
* Utility to toggle loading state on buttons
*/
function toggleButtonLoading(buttonElement, isLoading) {
if (isLoading) {
buttonElement.classList.add('btn-loading');
} else {
buttonElement.classList.remove('btn-loading');
}
}
@@ -0,0 +1,232 @@
<?php
session_start();
/**
* Entry Point for Hospital Payroll Management System
*/
// Define application root
define('APP_ROOT', dirname(__DIR__));
// Require basic configuration
require_once APP_ROOT . '/config/app.php';
require_once APP_ROOT . '/config/database.php';
// Very basic autoloader for our MVC structure
spl_autoload_register(function ($className) {
$path = APP_ROOT . '/' . str_replace('\\', '/', $className) . '.php';
if (file_exists($path)) {
require_once $path;
}
});
// Simple Router
$requestUri = $_SERVER['REQUEST_URI'];
$requestMethod = $_SERVER['REQUEST_METHOD'];
// Remove the base directory from the request URI to get the relative path
$baseDir = parse_url(BASE_URL, PHP_URL_PATH) ?? '';
if ($baseDir !== '' && strpos($requestUri, $baseDir) === 0) {
$uri = substr($requestUri, strlen($baseDir));
} else {
$uri = $requestUri;
}
$uri = strtok($uri, '?'); // Remove query string
$uri = trim($uri, '/');
$controllerName = 'app\\Controllers\\AuthController';
$methodName = 'index';
if ($uri === '' || $uri === 'login') {
if ($requestMethod === 'POST') {
$controllerName = 'app\\Controllers\\AuthController';
$methodName = 'login';
} else {
$controllerName = 'app\\Controllers\\AuthController';
$methodName = 'index';
}
} elseif ($uri === 'logout') {
$controllerName = 'app\\Controllers\\AuthController';
$methodName = 'logout';
} elseif ($uri === 'dashboard') {
$controllerName = 'app\\Controllers\\DashboardController';
$methodName = 'index';
} elseif ($uri === 'payroll') {
$controllerName = 'app\\Controllers\\PayrollController';
$methodName = 'index';
} elseif ($uri === 'payroll/store') {
$controllerName = 'app\\Controllers\\PayrollController';
$methodName = 'store';
} elseif ($uri === 'payroll/clear') {
$controllerName = 'app\\Controllers\\PayrollController';
$methodName = 'clearData';
} elseif (preg_match('#^payroll/details(?:/(\d+))?$#', $uri, $matches)) {
$_GET['id'] = $matches[1] ?? ($_GET['id'] ?? null);
$controllerName = 'app\\Controllers\\PayrollController';
$methodName = 'details';
} elseif ($uri === 'payroll/copy-previous') {
$controllerName = 'app\\Controllers\\PayrollController';
$methodName = 'copyPreviousMonth';
} elseif ($uri === 'payroll/employee/add') {
$controllerName = 'app\\Controllers\\PayrollController';
$methodName = 'addEmployee';
} elseif ($uri === 'payroll/employee/delete') {
$controllerName = 'app\\Controllers\\PayrollController';
$methodName = 'deleteEmployee';
} elseif (preg_match('#^payroll/employee/edit(?:/(\d+))?$#', $uri, $matches)) {
$_GET['id'] = $matches[1] ?? ($_GET['id'] ?? null);
$controllerName = 'app\\Controllers\\PayrollController';
$methodName = 'editEmployee';
} elseif ($uri === 'payroll/saveEmployee') {
$controllerName = 'app\\Controllers\\PayrollController';
$methodName = 'saveEmployee';
} elseif ($uri === 'payroll/updateStatus' && $_SERVER['REQUEST_METHOD'] === 'POST') {
$controllerName = 'app\\Controllers\\PayrollController';
$methodName = 'updateStatus';
} elseif (preg_match('#^payroll/deleteEmployee/(\d+)$#', $uri, $matches)) {
$_GET['id'] = $matches[1] ?? ($_GET['id'] ?? null);
$controllerName = 'app\\Controllers\\PayrollController';
$methodName = 'editEmployee';
} elseif ($uri === 'payroll/employee/update') {
$controllerName = 'app\\Controllers\\PayrollController';
$methodName = 'updateEmployee';
} elseif (preg_match('#^employee/image(?:/([\w\d_-]+))?$#', $uri, $matches)) {
$_GET['national_id'] = $matches[1] ?? null;
$controllerName = 'app\\Controllers\\HrController';
$methodName = 'image';
} elseif ($uri === 'settings/income') {
$controllerName = 'app\\Controllers\\SettingController';
if ($requestMethod === 'POST') {
$methodName = 'storeIncome';
} else {
$methodName = 'income';
}
} elseif ($uri === 'settings/income/update') {
$controllerName = 'app\\Controllers\\SettingController';
$methodName = 'updateIncome';
} elseif ($uri === 'settings/deduction') {
$controllerName = 'app\\Controllers\\SettingController';
if ($requestMethod === 'POST') {
$methodName = 'storeDeduction';
} else {
$methodName = 'deduction';
}
} elseif ($uri === 'settings/deduction/update') {
$controllerName = 'app\\Controllers\\SettingController';
$methodName = 'updateDeduction';
} elseif ($uri === 'settings/officer-types') {
$controllerName = 'app\\Controllers\\SettingController';
if ($requestMethod === 'POST') {
$methodName = 'storeOfficerType';
} else {
$methodName = 'officerTypes';
}
} elseif ($uri === 'settings/officer-types/update') {
$controllerName = 'app\\Controllers\\SettingController';
$methodName = 'updateOfficerType';
} elseif ($uri === 'settings/officer-types/toggle') {
$controllerName = 'app\\Controllers\\SettingController';
$methodName = 'toggleOfficerTypeStatus';
} elseif ($uri === 'settings/officer-types/delete') {
$controllerName = 'app\\Controllers\\SettingController';
$methodName = 'deleteOfficerType';
} elseif ($uri === 'settings/system/search-officer') {
$controllerName = 'app\\Controllers\\SettingController';
$methodName = 'searchOfficer';
} elseif ($uri === 'settings/system/save') {
$controllerName = 'app\\Controllers\\SettingController';
$methodName = 'saveSystemSettings';
} elseif ($uri === 'settings/system/upload-tax-signature') {
$controllerName = 'app\\Controllers\\SettingController';
$methodName = 'uploadTaxSignature';
} elseif ($uri === 'settings/system') {
$controllerName = 'app\\Controllers\\SettingController';
if ($requestMethod === 'POST') {
$methodName = 'uploadSignature';
} else {
$methodName = 'system';
}
} elseif ($uri === 'users') {
$controllerName = 'app\\Controllers\\UserController';
if ($requestMethod === 'POST') {
$methodName = 'store';
} else {
$methodName = 'index';
}
} elseif ($uri === 'users/update') {
$controllerName = 'app\\Controllers\\UserController';
$methodName = 'update';
} elseif ($uri === 'users/toggle_status') {
$controllerName = 'app\\Controllers\\UserController';
$methodName = 'toggle_status';
} elseif ($uri === 'users/logs') {
$controllerName = 'app\\Controllers\\UserController';
$methodName = 'logs';
} elseif ($uri === 'profile') {
$controllerName = 'app\\Controllers\\ProfileController';
$methodName = 'index';
} elseif ($uri === 'profile/update-password') {
$controllerName = 'app\\Controllers\\ProfileController';
$methodName = 'updatePassword';
} elseif ($uri === 'manual') {
$controllerName = 'app\\Controllers\\ManualController';
$methodName = 'index';
} elseif ($uri === 'reports') {
$controllerName = 'app\\Controllers\\ReportController';
$methodName = 'index';
} elseif (preg_match('#^reports/slip/([\w\d_-]+)/(\d{2})/(\d{4})$#', $uri, $matches)) {
$_GET['national_id'] = $matches[1];
$_GET['month'] = $matches[2];
$_GET['year'] = $matches[3];
$controllerName = 'app\\Controllers\\ReportController';
$methodName = 'printSlip';
} elseif ($uri === 'reports/tax-bulk') {
$controllerName = 'app\\Controllers\\ReportController';
$methodName = 'printTaxBulk';
} elseif ($uri === 'reports/salary') {
$controllerName = 'app\\Controllers\\ReportController';
$methodName = 'salaryReport';
} elseif ($uri === 'reports/salary/export') {
$controllerName = 'app\\Controllers\\ReportController';
$methodName = 'exportExcel';
} elseif (preg_match('#^reports/tax/([\w\d_-]+)/(\d{4})$#', $uri, $matches)) {
$_GET['national_id'] = $matches[1];
$_GET['year'] = $matches[2];
$controllerName = 'app\\Controllers\\ReportController';
$methodName = 'printTax';
} elseif ($uri === 'import') {
$controllerName = 'app\\Controllers\\ImportController';
$methodName = 'index';
} elseif ($uri === 'import/upload') {
$controllerName = 'app\\Controllers\\ImportController';
$methodName = 'upload';
} elseif ($uri === 'kiosk') {
$controllerName = 'app\\Controllers\\KioskController';
$methodName = 'index';
} elseif ($uri === 'kiosk/search') {
$controllerName = 'app\\Controllers\\KioskController';
$methodName = 'search';
} elseif (preg_match('#^kiosk/print/slip/([\w\d_-]+)/(\d{2})/(\d{4})$#', $uri, $matches)) {
$_GET['national_id'] = $matches[1];
$_GET['month'] = $matches[2];
$_GET['year'] = $matches[3];
$controllerName = 'app\\Controllers\\KioskController';
$methodName = 'printSlip';
} elseif ($uri === 'kiosk/print/slipBulk') {
$controllerName = 'app\\Controllers\\KioskController';
$methodName = 'printSlipBulk';
} elseif (preg_match('#^kiosk/print/tax/([\w\d_-]+)/(\d{4})$#', $uri, $matches)) {
$_GET['national_id'] = $matches[1];
$_GET['year'] = $matches[2];
$controllerName = 'app\\Controllers\\KioskController';
$methodName = 'printTax';
} else {
// Basic 404
http_response_code(404);
echo "<h1>404 Not Found</h1>";
echo "<p>The page you are looking for does not exist.</p>";
exit;
}
$controller = new $controllerName();
$controller->$methodName();
?>
@@ -0,0 +1,21 @@
{
"name": "Samui Hospital Payroll",
"short_name": "Payroll",
"description": "ระบบจัดการข้อมูลเงินเดือนเจ้าหน้าที่โรงพยาบาลเกาะสมุย",
"start_url": "/dashboard",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#0ea5e9",
"icons": [
{
"src": "assets/icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "assets/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
@@ -0,0 +1,43 @@
const CACHE_NAME = 'payroll-app-v1';
const urlsToCache = [
'/',
'/public/assets/css/style.css',
'/public/assets/js/main.js'
];
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => {
return cache.addAll(urlsToCache);
})
);
});
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(response => {
// Cache hit - return response
if (response) {
return response;
}
return fetch(event.request);
})
);
});
self.addEventListener('activate', event => {
const cacheWhitelist = [CACHE_NAME];
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (cacheWhitelist.indexOf(cacheName) === -1) {
return caches.delete(cacheName);
}
})
);
})
);
});