first commit

This commit is contained in:
2026-04-11 14:08:27 +07:00
parent e86e5d2c46
commit 6b7655aa16
389 changed files with 5387 additions and 60861 deletions

View File

@@ -0,0 +1,124 @@
<div class="page-title-area">
<div>
<h1>Create Qualification</h1>
<p class="subtitle">Register a new degree / qualification record</p>
</div>
<a href="/admin/qualification" class="btn btn-outline-secondary"><i class="fas fa-arrow-left"></i> Back</a>
</div>
<% if (typeof error !== 'undefined' && error) { %>
<div class="alert d-flex align-items-center gap-2 mb-3" style="background:var(--danger-soft);color:var(--danger-color);border:none;border-radius:var(--border-radius-sm);">
<i class="fas fa-exclamation-circle"></i> <%= error %>
</div>
<% } %>
<form method="POST" action="/admin/qualification/create" enctype="multipart/form-data">
<div class="row g-3">
<div class="col-lg-8">
<div class="card border-0 mb-3">
<div class="card-header"><h5 class="card-header-title"><i class="fas fa-id-card"></i> Basic Information</h5></div>
<div class="card-body">
<div class="row g-3">
<div class="col-md-6">
<label class="form-label">Qualification No. <span class="text-danger">*</span></label>
<input type="text" class="form-control" name="qualification_number" required
value="<%= (typeof formData !== 'undefined' && formData) ? formData.qualification_number || '' : '' %>">
</div>
<div class="col-md-6">
<label class="form-label">Student Full Name <span class="text-danger">*</span></label>
<input type="text" class="form-control" name="student_name" required
value="<%= (typeof formData !== 'undefined' && formData) ? formData.student_name || '' : '' %>">
</div>
<div class="col-md-6">
<label class="form-label">Program Name <span class="text-danger">*</span></label>
<input type="text" class="form-control" name="program_name" required
value="<%= (typeof formData !== 'undefined' && formData) ? formData.program_name || '' : '' %>">
</div>
<div class="col-md-6">
<label class="form-label">Passport No.</label>
<input type="text" class="form-control" name="passport_number"
value="<%= (typeof formData !== 'undefined' && formData) ? formData.passport_number || '' : '' %>">
</div>
<div class="col-12">
<label class="form-label">Address</label>
<input type="text" class="form-control" name="address"
value="<%= (typeof formData !== 'undefined' && formData) ? formData.address || '' : '' %>">
</div>
</div>
</div>
</div>
<div class="card border-0 mb-3">
<div class="card-header"><h5 class="card-header-title"><i class="fas fa-tags"></i> Classification</h5></div>
<div class="card-body">
<div class="row g-3">
<div class="col-md-4">
<label class="form-label">Department <span class="text-danger">*</span></label>
<select class="form-select" name="department" required>
<option value="">-- Select --</option>
<% departments.forEach(d => { %>
<option value="<%= d._id %>" <%= (typeof formData !== 'undefined' && formData && formData.department === d._id.toString()) ? 'selected' : '' %>><%= d.name %></option>
<% }); %>
</select>
</div>
<div class="col-md-4">
<label class="form-label">Level <span class="text-danger">*</span></label>
<select class="form-select" name="level" required>
<option value="">-- Select --</option>
<% levels.forEach(l => { %>
<option value="<%= l._id %>" <%= (typeof formData !== 'undefined' && formData && formData.level === l._id.toString()) ? 'selected' : '' %>><%= l.type %></option>
<% }); %>
</select>
</div>
<div class="col-md-4">
<label class="form-label">Issue Date <span class="text-danger">*</span></label>
<input type="date" class="form-control" name="issued_date" required
value="<%= (typeof formData !== 'undefined' && formData && formData.issued_date) ? formData.issued_date.toString().substring(0,10) : '' %>">
</div>
<div class="col-md-4">
<label class="form-label">Status</label>
<select class="form-select" name="status">
<option value="active">Active</option>
<option value="revoked" <%= (typeof formData !== 'undefined' && formData && formData.status === 'revoked') ? 'selected' : '' %>>Revoked</option>
</select>
</div>
</div>
</div>
</div>
<div class="card border-0 mb-3">
<div class="card-header"><h5 class="card-header-title"><i class="fas fa-book-open"></i> Thesis / Topic <span style="font-size:0.75rem;color:var(--text-muted);font-weight:400">(fill for PhD — leave empty for MBA/Master)</span></h5></div>
<div class="card-body">
<div class="row g-3">
<div class="col-12">
<label class="form-label">Topic Title</label>
<input type="text" class="form-control" name="topic_name"
value="<%= (typeof formData !== 'undefined' && formData) ? formData.topic_name || '' : '' %>">
</div>
<div class="col-12">
<label class="form-label">Short Description</label>
<textarea class="form-control" name="topic_short_desc" rows="3"><%= (typeof formData !== 'undefined' && formData) ? formData.topic_short_desc || '' : '' %></textarea>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="card border-0 mb-3">
<div class="card-header"><h5 class="card-header-title"><i class="fas fa-image"></i> Degree Image</h5></div>
<div class="card-body">
<input type="file" class="form-control" name="degree_image" accept="image/*">
</div>
</div>
<div class="card border-0">
<div class="card-body d-flex flex-column gap-2">
<button type="submit" class="btn btn-primary w-100"><i class="fas fa-save"></i> Create Qualification</button>
<a href="/admin/qualification" class="btn btn-outline-secondary w-100">Cancel</a>
</div>
</div>
</div>
</div>
</form>

View File

@@ -0,0 +1,123 @@
<div class="page-title-area">
<div>
<h1>Edit Qualification</h1>
<p class="subtitle"><code style="font-size:0.8rem;color:var(--accent-color)"><%= qual.qualification_number %></code></p>
</div>
<a href="/admin/qualification" class="btn btn-outline-secondary"><i class="fas fa-arrow-left"></i> Back</a>
</div>
<% if (typeof error !== 'undefined' && error) { %>
<div class="alert d-flex align-items-center gap-2 mb-3" style="background:var(--danger-soft);color:var(--danger-color);border:none;border-radius:var(--border-radius-sm);">
<i class="fas fa-exclamation-circle"></i> <%= error %>
</div>
<% } %>
<form method="POST" action="/admin/qualification/<%= qual._id %>/edit" enctype="multipart/form-data">
<div class="row g-3">
<div class="col-lg-8">
<div class="card border-0 mb-3">
<div class="card-header"><h5 class="card-header-title"><i class="fas fa-id-card"></i> Basic Information</h5></div>
<div class="card-body">
<div class="row g-3">
<div class="col-md-6">
<label class="form-label">Qualification No. <span class="text-danger">*</span></label>
<input type="text" class="form-control" name="qualification_number" required value="<%= qual.qualification_number %>">
</div>
<div class="col-md-6">
<label class="form-label">Student Full Name <span class="text-danger">*</span></label>
<input type="text" class="form-control" name="student_name" required value="<%= qual.student_name %>">
</div>
<div class="col-md-6">
<label class="form-label">Program Name <span class="text-danger">*</span></label>
<input type="text" class="form-control" name="program_name" required value="<%= qual.program_name %>">
</div>
<div class="col-md-6">
<label class="form-label">Passport No.</label>
<input type="text" class="form-control" name="passport_number" value="<%= qual.passport_number || '' %>">
</div>
<div class="col-12">
<label class="form-label">Address</label>
<input type="text" class="form-control" name="address" value="<%= qual.address || '' %>">
</div>
</div>
</div>
</div>
<div class="card border-0 mb-3">
<div class="card-header"><h5 class="card-header-title"><i class="fas fa-tags"></i> Classification</h5></div>
<div class="card-body">
<div class="row g-3">
<div class="col-md-4">
<label class="form-label">Department <span class="text-danger">*</span></label>
<select class="form-select" name="department" required>
<option value="">-- Select --</option>
<% departments.forEach(d => { %>
<option value="<%= d._id %>" <%= (qual.department && qual.department._id && qual.department._id.toString() === d._id.toString()) ? 'selected' : '' %>><%= d.name %></option>
<% }); %>
</select>
</div>
<div class="col-md-4">
<label class="form-label">Level <span class="text-danger">*</span></label>
<select class="form-select" name="level" required>
<option value="">-- Select --</option>
<% levels.forEach(l => { %>
<option value="<%= l._id %>" <%= (qual.level && qual.level._id && qual.level._id.toString() === l._id.toString()) ? 'selected' : '' %>><%= l.type %></option>
<% }); %>
</select>
</div>
<div class="col-md-4">
<label class="form-label">Issue Date <span class="text-danger">*</span></label>
<input type="date" class="form-control" name="issued_date" required value="<%= qual.issued_date ? new Date(qual.issued_date).toISOString().substring(0,10) : '' %>">
</div>
<div class="col-md-4">
<label class="form-label">Status</label>
<select class="form-select" name="status">
<option value="active" <%= qual.status === 'active' ? 'selected' : '' %>>Active</option>
<option value="revoked" <%= qual.status === 'revoked' ? 'selected' : '' %>>Revoked</option>
</select>
</div>
</div>
</div>
</div>
<div class="card border-0 mb-3">
<div class="card-header"><h5 class="card-header-title"><i class="fas fa-book-open"></i> Thesis / Topic</h5></div>
<div class="card-body">
<div class="row g-3">
<div class="col-12">
<label class="form-label">Topic Title</label>
<input type="text" class="form-control" name="topic_name" value="<%= qual.topic_name || '' %>">
</div>
<div class="col-12">
<label class="form-label">Short Description</label>
<textarea class="form-control" name="topic_short_desc" rows="3"><%= qual.topic_short_desc || '' %></textarea>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="card border-0 mb-3">
<div class="card-header"><h5 class="card-header-title"><i class="fas fa-image"></i> Degree Image</h5></div>
<div class="card-body">
<% if (qual.degree_image) { %>
<div class="mb-2">
<img src="/admin/files/<%= qual.degree_image %>" alt="Degree image" class="img-thumbnail" style="max-height:110px;border-radius:var(--border-radius-sm);">
</div>
<% } %>
<input type="file" class="form-control" name="degree_image" accept="image/*">
<div class="form-text">Leave empty to keep current image.</div>
</div>
</div>
<div class="card border-0">
<div class="card-body d-flex flex-column gap-2">
<button type="submit" class="btn btn-primary w-100"><i class="fas fa-save"></i> Save Changes</button>
<a href="/admin/qualification" class="btn btn-outline-secondary w-100">Cancel</a>
</div>
</div>
</div>
</div>
</form>

View File

@@ -0,0 +1,110 @@
<div class="page-title-area">
<div>
<h1>Qualifications</h1>
<p class="subtitle">Degree / qualification records</p>
</div>
<a href="/admin/qualification/create" class="btn btn-primary">
<i class="fas fa-plus"></i> New Qualification
</a>
</div>
<!-- Filter -->
<div class="card border-0 mb-3">
<div class="card-body" style="padding:1rem 1.25rem;">
<form method="GET" action="/admin/qualification">
<div class="row g-2 align-items-end">
<div class="col-md-4">
<div class="position-relative search-bar">
<i class="fas fa-search search-icon"></i>
<input type="text" class="form-control" name="search" placeholder="Search name, number..." value="<%= query.search || '' %>">
</div>
</div>
<div class="col-md-2">
<select class="form-select" name="status">
<option value="">All Status</option>
<option value="active" <%= query.status === 'active' ? 'selected' : '' %>>Active</option>
<option value="revoked" <%= query.status === 'revoked' ? 'selected' : '' %>>Revoked</option>
</select>
</div>
<div class="col-md-1 d-flex gap-1">
<button type="submit" class="btn btn-primary flex-fill"><i class="fas fa-search"></i></button>
<% if (query.search || query.status) { %>
<a href="/admin/qualification" class="btn btn-outline-secondary"><i class="fas fa-times"></i></a>
<% } %>
</div>
</div>
</form>
</div>
</div>
<!-- Table -->
<div class="card border-0">
<div class="card-body p-0">
<% if (qualifications && qualifications.length > 0) { %>
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead>
<tr>
<th style="width:40px">#</th>
<th>Qualification No.</th>
<th>Full Name</th>
<th>Program</th>
<th>Department</th>
<th>Level</th>
<th>Issue Date</th>
<th>Type</th>
<th>Status</th>
<th style="width:90px">Actions</th>
</tr>
</thead>
<tbody>
<% qualifications.forEach((q, i) => { %>
<tr>
<td style="color:var(--text-muted)"><%= i + 1 %></td>
<td><code style="font-size:0.8rem;color:var(--primary-color)"><%= q.qualification_number %></code></td>
<td style="font-weight:500"><%= q.student_name %></td>
<td style="font-size:0.8125rem;color:var(--text-muted)"><%= q.program_name %></td>
<td style="font-size:0.8125rem"><%= q.department ? q.department.name : '—' %></td>
<td style="font-size:0.8125rem"><%= q.level ? q.level.type : '—' %></td>
<td style="font-size:0.8125rem;color:var(--text-muted)"><%= q.issued_date ? new Date(q.issued_date).toLocaleDateString('en-GB') : '—' %></td>
<td>
<% if (q.topic_name) { %>
<span class="badge badge-soft-primary">PhD</span>
<% } else { %>
<span class="badge bg-soft-info">MBA/Master</span>
<% } %>
</td>
<td>
<% if (q.status === 'active') { %>
<span class="badge bg-soft-success">Active</span>
<% } else { %>
<span class="badge bg-soft-danger">Revoked</span>
<% } %>
</td>
<td>
<div class="table-actions">
<a href="/admin/qualification/<%= q._id %>/edit" class="btn btn-sm btn-outline-primary btn-icon" title="Edit">
<i class="fas fa-pen"></i>
</a>
<form method="POST" action="/admin/qualification/<%= q._id %>/delete" style="display:inline;" onsubmit="return confirm('Delete this qualification?')">
<button type="submit" class="btn btn-sm btn-outline-danger btn-icon" title="Delete">
<i class="fas fa-trash"></i>
</button>
</form>
</div>
</td>
</tr>
<% }); %>
</tbody>
</table>
</div>
<% } else { %>
<div class="empty-state">
<div class="empty-state-icon"><i class="fas fa-graduation-cap"></i></div>
<h5>No qualifications found</h5>
<p>Create the first qualification record.</p>
<a href="/admin/qualification/create" class="btn btn-primary"><i class="fas fa-plus"></i> Create</a>
</div>
<% } %>
</div>
</div>