261 lines
13 KiB
PHP
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'; ?>
|