135 lines
7.3 KiB
PHP
135 lines
7.3 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
require_once __DIR__ . '/includes/app.php';
|
|
|
|
$errors = [];
|
|
$old = [
|
|
'employee_name' => '',
|
|
'employee_email' => '',
|
|
'department' => 'Operations',
|
|
'leave_type' => 'Annual leave',
|
|
'start_date' => date('Y-m-d', strtotime('+1 day')),
|
|
'end_date' => date('Y-m-d', strtotime('+1 day')),
|
|
'reason' => '',
|
|
];
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
$old = array_merge($old, $_POST);
|
|
$result = createLeaveRequest($_POST);
|
|
if (!empty($result['success'])) {
|
|
flash('success', 'Leave request submitted and added to the tenant-scoped approval queue.');
|
|
redirectWithCompany('/leave_request.php', ['id' => (int)$result['id'], 'created' => 1]);
|
|
}
|
|
$errors = $result['errors'] ?? [];
|
|
}
|
|
|
|
renderPageStart(
|
|
'Create Leave Request',
|
|
'Submit a leave request with validation, tenant scoping, and approval-ready review status.',
|
|
'new'
|
|
);
|
|
?>
|
|
<section class="row g-4">
|
|
<div class="col-xl-7">
|
|
<article class="panel-card">
|
|
<div class="panel-head align-items-start">
|
|
<div>
|
|
<span class="section-kicker">Request workflow</span>
|
|
<h2 class="panel-title mb-1">Submit leave for approval</h2>
|
|
<p class="text-secondary mb-0">Every request is stored with <code>company_id</code> and only appears inside the active tenant workspace.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<?php if ($errors): ?>
|
|
<div class="alert alert-danger" role="alert">
|
|
Please fix the highlighted fields and resubmit.
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<form method="post" class="row g-3" novalidate>
|
|
<div class="col-md-6">
|
|
<label class="form-label" for="employee_name">Employee name</label>
|
|
<input type="text" class="form-control <?= isset($errors['employee_name']) ? 'is-invalid' : '' ?>" id="employee_name" name="employee_name" value="<?= htmlspecialchars((string)$old['employee_name']) ?>" required>
|
|
<?php if (isset($errors['employee_name'])): ?><div class="invalid-feedback"><?= htmlspecialchars($errors['employee_name']) ?></div><?php endif; ?>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label" for="employee_email">Work email</label>
|
|
<input type="email" class="form-control <?= isset($errors['employee_email']) ? 'is-invalid' : '' ?>" id="employee_email" name="employee_email" value="<?= htmlspecialchars((string)$old['employee_email']) ?>" required>
|
|
<?php if (isset($errors['employee_email'])): ?><div class="invalid-feedback"><?= htmlspecialchars($errors['employee_email']) ?></div><?php endif; ?>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label" for="department">Department</label>
|
|
<select class="form-select <?= isset($errors['department']) ? 'is-invalid' : '' ?>" id="department" name="department">
|
|
<?php foreach (departmentOptions() as $department): ?>
|
|
<option value="<?= htmlspecialchars($department) ?>" <?= $old['department'] === $department ? 'selected' : '' ?>><?= htmlspecialchars($department) ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
<?php if (isset($errors['department'])): ?><div class="invalid-feedback"><?= htmlspecialchars($errors['department']) ?></div><?php endif; ?>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label" for="leave_type">Leave type</label>
|
|
<select class="form-select <?= isset($errors['leave_type']) ? 'is-invalid' : '' ?>" id="leave_type" name="leave_type">
|
|
<?php foreach (leaveTypeOptions() as $leaveType): ?>
|
|
<option value="<?= htmlspecialchars($leaveType) ?>" <?= $old['leave_type'] === $leaveType ? 'selected' : '' ?>><?= htmlspecialchars($leaveType) ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
<?php if (isset($errors['leave_type'])): ?><div class="invalid-feedback"><?= htmlspecialchars($errors['leave_type']) ?></div><?php endif; ?>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label" for="start_date">Start date</label>
|
|
<input type="date" class="form-control <?= isset($errors['dates']) ? 'is-invalid' : '' ?> js-date-input" id="start_date" name="start_date" value="<?= htmlspecialchars((string)$old['start_date']) ?>" required>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label" for="end_date">End date</label>
|
|
<input type="date" class="form-control <?= isset($errors['dates']) ? 'is-invalid' : '' ?> js-date-input" id="end_date" name="end_date" value="<?= htmlspecialchars((string)$old['end_date']) ?>" required>
|
|
<?php if (isset($errors['dates'])): ?><div class="invalid-feedback d-block"><?= htmlspecialchars($errors['dates']) ?></div><?php endif; ?>
|
|
</div>
|
|
<div class="col-12">
|
|
<label class="form-label" for="reason">Business reason</label>
|
|
<textarea class="form-control <?= isset($errors['reason']) ? 'is-invalid' : '' ?> js-reason-input" id="reason" name="reason" rows="5" maxlength="500" required><?= htmlspecialchars((string)$old['reason']) ?></textarea>
|
|
<div class="form-hint d-flex justify-content-between mt-2">
|
|
<span>Keep this short, factual, and audit-friendly.</span>
|
|
<span class="text-secondary"><span class="js-reason-count"><?= textLength((string)$old['reason']) ?></span>/500</span>
|
|
</div>
|
|
<?php if (isset($errors['reason'])): ?><div class="invalid-feedback d-block"><?= htmlspecialchars($errors['reason']) ?></div><?php endif; ?>
|
|
</div>
|
|
<div class="col-12 d-flex flex-column flex-md-row justify-content-between align-items-md-center gap-3 pt-2 border-top">
|
|
<div class="request-preview">
|
|
<div class="small text-secondary text-uppercase">Calculated duration</div>
|
|
<div class="fw-semibold"><span class="js-days-preview">1</span> business days requested</div>
|
|
</div>
|
|
<div class="d-flex gap-2">
|
|
<a href="<?= htmlspecialchars(currentCompanyFilterQuery('/leave_requests.php')) ?>" class="btn btn-outline-secondary">Cancel</a>
|
|
<button type="submit" class="btn btn-dark">Submit request</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</article>
|
|
</div>
|
|
|
|
<div class="col-xl-5">
|
|
<article class="panel-card mb-4">
|
|
<span class="section-kicker">What this proves</span>
|
|
<h3 class="panel-title">Thin but complete SaaS slice</h3>
|
|
<ul class="feature-list">
|
|
<li>Tenant switcher demonstrates isolated data views.</li>
|
|
<li>Server-side validation protects the workflow.</li>
|
|
<li>Approval-ready status starts at pending.</li>
|
|
<li>Dashboard and list update immediately after submit.</li>
|
|
</ul>
|
|
</article>
|
|
|
|
<article class="panel-card">
|
|
<span class="section-kicker">Next modules</span>
|
|
<h3 class="panel-title">Planned expansion</h3>
|
|
<div class="roadmap-grid">
|
|
<div class="roadmap-card"><strong>Attendance</strong><span>Clock in/out + GPS capture</span></div>
|
|
<div class="roadmap-card"><strong>Employees</strong><span>Directory, roles, and contracts</span></div>
|
|
<div class="roadmap-card"><strong>Notifications</strong><span>Email + database events</span></div>
|
|
<div class="roadmap-card"><strong>AI assistant</strong><span>Natural-language HR insights</span></div>
|
|
</div>
|
|
</article>
|
|
</div>
|
|
</section>
|
|
<?php renderPageEnd(); ?>
|