38438-vm/employees.php
2026-02-15 16:51:04 +00:00

261 lines
13 KiB
PHP

<?php
declare(strict_types=1);
require_once __DIR__ . '/db/config.php';
$tenant_id = 1;
// Handle Add Employee
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_employee'])) {
$first_name = $_POST['first_name'] ?? '';
$last_name = $_POST['last_name'] ?? '';
$email = $_POST['email'] ?? '';
$position = $_POST['position'] ?? '';
$start_date = $_POST['start_date'] ?? date('Y-m-d');
$is_limited = isset($_POST['is_limited']) ? 1 : 0;
$phone = $_POST['phone'] ?? '';
$password = $_POST['password'] ?? '';
$force_password_change = isset($_POST['force_password_change']) ? 1 : 0;
$initial_wage = (float)($_POST['initial_wage'] ?? 0);
$team_ids = $_POST['teams'] ?? [];
if ($first_name && $last_name) {
$user_id = null;
if (!$is_limited && $email) {
$hashed_password = $password ? password_hash($password, PASSWORD_DEFAULT) : null;
$stmt = db()->prepare("INSERT INTO users (tenant_id, name, email, phone, password, require_password_change, role)
VALUES (?, ?, ?, ?, ?, ?, 'staff')
ON DUPLICATE KEY UPDATE
phone = VALUES(phone),
password = COALESCE(VALUES(password), password),
require_password_change = VALUES(require_password_change)");
$stmt->execute([$tenant_id, "$first_name $last_name", $email, $phone, $hashed_password, $force_password_change]);
$stmt = db()->prepare("SELECT id FROM users WHERE email = ? AND tenant_id = ?");
$stmt->execute([$email, $tenant_id]);
$user_id = (int)($stmt->fetchColumn() ?: null);
}
$stmt = db()->prepare("INSERT INTO employees (tenant_id, first_name, last_name, email, phone, position, start_date, is_limited, user_id, name) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->execute([$tenant_id, $first_name, $last_name, $email, $phone, $position, $start_date, $is_limited, $user_id, "$first_name $last_name"]);
$employee_id = (int)db()->lastInsertId();
if ($initial_wage > 0) {
$stmt = db()->prepare("INSERT INTO employee_wages (tenant_id, employee_id, hourly_rate, effective_date) VALUES (?, ?, ?, ?)");
$stmt->execute([$tenant_id, $employee_id, $initial_wage, $start_date]);
}
if (!empty($team_ids)) {
foreach ($team_ids as $tid) {
$stmt = db()->prepare("INSERT INTO employee_teams (tenant_id, employee_id, team_id) VALUES (?, ?, ?)");
$stmt->execute([$tenant_id, $employee_id, $tid]);
}
}
$stmt = db()->prepare("INSERT INTO activity_log (tenant_id, action, details) VALUES (?, ?, ?)");
$stmt->execute([$tenant_id, 'Employee Created', "Added employee: $first_name $last_name"]);
header("Location: employees.php?success=1");
exit;
}
}
// Fetch Data
$stmt = db()->prepare("SELECT pref_key, pref_value FROM system_preferences WHERE tenant_id = ?");
$stmt->execute([$tenant_id]);
$prefs = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
$employees = db()->prepare("
SELECT e.*,
(SELECT hourly_rate FROM employee_wages WHERE employee_id = e.id ORDER BY effective_date DESC LIMIT 1) as current_wage
FROM employees e
WHERE e.tenant_id = ?
ORDER BY e.first_name, e.last_name
");
$employees->execute([$tenant_id]);
$employeeList = $employees->fetchAll();
$teams = db()->prepare("SELECT * FROM teams WHERE tenant_id = ? ORDER BY name");
$teams->execute([$tenant_id]);
$teamList = $teams->fetchAll();
$pageTitle = "SR&ED Manager - Employees";
include __DIR__ . '/includes/header.php';
?>
<div class="container-fluid py-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2 class="fw-bold mb-0">Employees</h2>
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addEmployeeModal">+ New Employee</button>
</div>
<?php if (isset($_GET['success'])): ?>
<div class="alert alert-success alert-dismissible fade show border-0 shadow-sm mb-4" role="alert">
Employee record successfully created.
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
<?php endif; ?>
<div class="card border-0 shadow-sm">
<div class="table-responsive">
<table class="table align-middle mb-0">
<thead class="bg-light">
<tr>
<th>Name</th>
<th>Position</th>
<th>Teams</th>
<th>Wage</th>
<th>Access</th>
<th class="text-end">Actions</th>
</tr>
</thead>
<tbody>
<?php if (empty($employeeList)): ?>
<tr><td colspan="6" class="text-center py-5 text-muted">No employees found.</td></tr>
<?php endif; ?>
<?php foreach ($employeeList as $e): ?>
<tr>
<td>
<a href="employee_detail.php?id=<?= $e['id'] ?>" class="text-decoration-none fw-bold text-dark">
<?= htmlspecialchars($e['first_name'] . ' ' . $e['last_name']) ?>
</a>
</td>
<td class="small text-muted"><?= htmlspecialchars($e['position']) ?></td>
<td>
<?php
$e_teams = db()->prepare("SELECT t.name FROM teams t JOIN employee_teams et ON t.id = et.team_id WHERE et.employee_id = ?");
$e_teams->execute([$e['id']]);
$t_names = $e_teams->fetchAll(PDO::FETCH_COLUMN);
foreach ($t_names as $tn) {
echo '<span class="badge bg-light text-dark border me-1">' . htmlspecialchars($tn) . '</span>';
}
?>
</td>
<td><span class="fw-bold text-success">$<?= number_format((float)($e['current_wage'] ?? 0), 2) ?>/h</span></td>
<td><span class="badge <?= $e['is_limited'] ? 'bg-secondary' : 'bg-primary' ?>"><?= $e['is_limited'] ? 'Limited' : 'Regular' ?></span></td>
<td class="text-end">
<a href="employee_detail.php?id=<?= $e['id'] ?>" class="btn btn-sm btn-primary me-1">View</a>
<button class="btn btn-sm btn-secondary">Edit</button>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
<!-- Modal -->
<div class="modal fade" id="addEmployeeModal" tabindex="-1">
<div class="modal-dialog modal-lg modal-dialog-centered">
<div class="modal-content border-0 shadow">
<div class="modal-header">
<h5 class="modal-title fw-bold">Add New Employee</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<form method="POST">
<div class="modal-body">
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label small fw-bold">First Name</label>
<input type="text" name="first_name" class="form-control" required>
</div>
<div class="col-md-6 mb-3">
<label class="form-label small fw-bold">Last Name</label>
<input type="text" name="last_name" class="form-control" required>
</div>
<div class="col-md-6 mb-3">
<label class="form-label small fw-bold">Email</label>
<input type="email" name="email" class="form-control">
</div>
<div class="col-md-6 mb-3">
<label class="form-label small fw-bold">Telephone (for 2FA)</label>
<input type="text" name="phone" class="form-control" placeholder="+1234567890">
</div>
<div class="col-md-6 mb-3">
<label class="form-label small fw-bold">Position</label>
<input type="text" name="position" class="form-control">
</div>
<div class="col-md-6 mb-3">
<label class="form-label small fw-bold">Start Date</label>
<input type="date" name="start_date" class="form-control" value="<?= date('Y-m-d') ?>">
</div>
<div class="col-md-6 mb-3">
<label class="form-label small fw-bold">Hourly Wage ($)</label>
<input type="number" name="initial_wage" class="form-control" step="0.01">
</div>
<div class="col-md-12 mb-3">
<label class="form-label small fw-bold d-block">Teams</label>
<div class="row px-2">
<?php foreach ($teamList as $t): ?>
<div class="col-md-4 form-check">
<input class="form-check-input" type="checkbox" name="teams[]" value="<?= $t['id'] ?>" id="teamCheck<?= $t['id'] ?>">
<label class="form-check-label small" for="teamCheck<?= $t['id'] ?>"><?= htmlspecialchars($t['name']) ?></label>
</div>
<?php endforeach; ?>
</div>
</div>
<div class="col-12 mb-3">
<div class="form-check form-switch p-3 border rounded">
<input class="form-check-input ms-0 me-2" type="checkbox" name="is_limited" id="limitedCheck" checked onchange="togglePasswordFields()">
<label class="form-check-label small fw-bold" for="limitedCheck">Limited Web Reporting (Cannot Login)</label>
</div>
</div>
<div id="passwordSection" style="display: none;">
<div class="row p-3 border rounded bg-light mx-1 mb-3">
<div class="col-md-8 mb-3">
<label class="form-label small fw-bold">Password</label>
<div class="input-group">
<input type="text" name="password" id="employeePassword" class="form-control" placeholder="Set or generate password">
<button type="button" class="btn btn-outline-secondary" onclick="generatePassword()">Generate</button>
</div>
</div>
<div class="col-md-4 mb-3 d-flex align-items-end">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="force_password_change" id="forceChange" checked>
<label class="form-check-label small fw-bold" for="forceChange">Require change</label>
</div>
</div>
<div class="col-12">
<div class="extra-small text-muted">Passwords must meet complexity requirements defined in System Preferences.</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer border-0">
<button type="submit" name="add_employee" class="btn btn-primary px-4">Create Employee</button>
</div>
</form>
</div>
</div>
</div>
<script>
function togglePasswordFields() {
const isLimited = document.getElementById('limitedCheck').checked;
document.getElementById('passwordSection').style.display = isLimited ? 'none' : 'block';
if (!isLimited) {
document.getElementById('employeePassword').required = true;
} else {
document.getElementById('employeePassword').required = false;
document.getElementById('employeePassword').value = '';
}
}
function generatePassword() {
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+";
let retVal = "";
for (let i = 0, n = charset.length; i < 12; ++i) {
retVal += charset.charAt(Math.floor(Math.random() * n));
}
document.getElementById('employeePassword').value = retVal;
}
// Initialize on load
document.addEventListener('DOMContentLoaded', togglePasswordFields);
</script>
<?php include __DIR__ . '/includes/footer.php'; ?>