added leave request, profile and staff members controllers
This commit is contained in:
@@ -1,413 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Nexus ERP - Client Management</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
|
||||
<style>
|
||||
body { background-color: #f8f9fc; }
|
||||
|
||||
/* Sidebar Styling */
|
||||
#sidebar { width: 260px; background-color: #111425; position: fixed; height: 100vh; overflow-y: auto; z-index: 1000; }
|
||||
.sidebar-brand { color: #fff; padding: 1.5rem 1.25rem; font-weight: 700; }
|
||||
.sidebar-brand-icon { background: #5c4df0; padding: 0.5rem; border-radius: 8px; margin-right: 10px; }
|
||||
.sidebar-nav-item { color: #8a90a5; text-decoration: none; padding: 0.75rem 1.25rem; display: block; border-radius: 8px; margin: 0.2rem 1rem; font-size: 0.9rem; }
|
||||
.sidebar-nav-item:hover, .sidebar-nav-item.active { background-color: #5c4df0; color: #fff; }
|
||||
.sidebar-heading { color: #5a617a; font-size: 0.75rem; text-transform: uppercase; padding: 1rem 1.25rem 0.5rem; font-weight: 600; }
|
||||
|
||||
/* Main Layout */
|
||||
#main-content { margin-left: 260px; min-height: 100vh; }
|
||||
.topbar { background: #fff; height: 70px; border-bottom: 1px solid #eaedf1; padding: 0 1.5rem; }
|
||||
|
||||
/* Component Styling */
|
||||
.content-card { background: #fff; border: 1px solid #eaedf1; border-radius: 12px; box-shadow: 0 2px 4px rgba(0,0,0,0.02); }
|
||||
.stat-card { border-left: 4px solid transparent; }
|
||||
.stat-card.primary { border-left-color: #5c4df0; }
|
||||
.stat-card.success { border-left-color: #10b981; }
|
||||
.stat-card.warning { border-left-color: #f59e0b; }
|
||||
|
||||
/* Table Styling */
|
||||
.table-custom th { background-color: #f8f9fc; color: #5a617a; font-weight: 600; font-size: 0.8rem; text-transform: uppercase; border-bottom: 2px solid #eaedf1; padding: 1rem; }
|
||||
.table-custom td { padding: 1rem; vertical-align: middle; font-size: 0.9rem; border-bottom: 1px solid #eaedf1; }
|
||||
.avatar-circle { width: 36px; height: 36px; display: flex; align-items: center; justify-content: center; border-radius: 50%; font-weight: bold; font-size: 0.85rem; }
|
||||
|
||||
/* Modal Customization */
|
||||
.modal-content { border-radius: 12px; border: none; box-shadow: 0 10px 30px rgba(0,0,0,0.1); }
|
||||
.modal-header { border-bottom: 1px solid #eaedf1; background-color: #f8f9fc; border-radius: 12px 12px 0 0; }
|
||||
.modal-footer { border-top: 1px solid #eaedf1; }
|
||||
.form-control:focus, .form-select:focus { border-color: #5c4df0; box-shadow: 0 0 0 0.25rem rgba(92, 77, 240, 0.25); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<aside id="sidebar">
|
||||
<div class="sidebar-brand d-flex align-items-center">
|
||||
<span class="sidebar-brand-icon"><i class="bi bi-buildings"></i></span>
|
||||
<div>
|
||||
<div class="mb-0 fs-6">NEXUS <span class="badge bg-secondary ms-1" style="font-size: 0.6rem;">ERP</span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="px-3 mb-3">
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-text bg-dark border-secondary text-secondary"><i class="bi bi-search"></i></span>
|
||||
<input type="text" class="form-control bg-dark border-secondary text-light placeholder-secondary" placeholder="Quick search...">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-heading mt-2">Core Entities</div>
|
||||
<a href="#" class="sidebar-nav-item"><i class="bi bi-person-badge me-2"></i> Staff Directory</a>
|
||||
<a href="#" class="sidebar-nav-item active"><i class="bi bi-people me-2"></i> Clients</a>
|
||||
<a href="#" class="sidebar-nav-item"><i class="bi bi-broadcast-pin me-2"></i> MNO Partners</a>
|
||||
<div class="sidebar-heading mt-2">Resources</div>
|
||||
<a href="#" class="sidebar-nav-item"><i class="bi bi-folder2-open me-2"></i> Document Vault</a>
|
||||
</aside>
|
||||
|
||||
<main id="main-content">
|
||||
<header class="topbar d-flex justify-content-between align-items-center">
|
||||
<div class="d-flex align-items-center">
|
||||
<a href="#" class="text-secondary text-decoration-none hover-primary"><i class="bi bi-house me-2"></i></a>
|
||||
<i class="bi bi-chevron-right text-secondary me-2" style="font-size: 0.8rem;"></i>
|
||||
<a href="#" class="text-secondary text-decoration-none me-2" style="font-size: 0.95rem;">Entities</a>
|
||||
<i class="bi bi-chevron-right text-secondary me-2" style="font-size: 0.8rem;"></i>
|
||||
<span class="fw-bold" style="font-size: 0.95rem;">Clients</span>
|
||||
</div>
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="text-secondary me-4" style="font-size: 0.9rem;">
|
||||
<i class="bi bi-clock me-1"></i> <span id="current-time">--:--:--</span>
|
||||
</div>
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="rounded-circle bg-light d-flex justify-content-center align-items-center me-2 border text-dark fw-bold" style="width: 32px; height: 32px; font-size: 0.8rem;">AU</div>
|
||||
<span class="fw-bold" style="font-size: 0.9rem;">Admin User</span>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="container-fluid p-4">
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<h4 class="fw-bold mb-1">Client Management</h4>
|
||||
<p class="text-secondary mb-0" style="font-size: 0.9rem;">Manage corporate accounts, billing types, and service provisions.</p>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<button class="btn btn-outline-secondary d-flex align-items-center" style="border-radius: 8px;">
|
||||
<i class="bi bi-download me-2"></i> Export
|
||||
</button>
|
||||
<button class="btn text-white fw-bold d-flex align-items-center" style="background-color: #5c4df0; border-radius: 8px;" id="btnOpenClientModal">
|
||||
<i class="bi bi-plus-lg me-2"></i> Add New Client
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-4 mb-4">
|
||||
<div class="col-md-4">
|
||||
<div class="content-card stat-card primary p-3 h-100 border-0">
|
||||
<div class="text-secondary fw-bold mb-2" style="font-size: 0.75rem; letter-spacing: 0.5px;">TOTAL CLIENTS</div>
|
||||
<h3 class="fw-bold mb-0">24</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="content-card stat-card success p-3 h-100 border-0">
|
||||
<div class="text-secondary fw-bold mb-2" style="font-size: 0.75rem; letter-spacing: 0.5px;">ACTIVE SIP TRUNKS</div>
|
||||
<h3 class="fw-bold mb-0">18</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="content-card stat-card warning p-3 h-100 border-0">
|
||||
<div class="text-secondary fw-bold mb-2" style="font-size: 0.75rem; letter-spacing: 0.5px;">PREPAID ACCOUNTS</div>
|
||||
<h3 class="fw-bold mb-0">12</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content-card">
|
||||
<div class="p-3 border-bottom bg-light bg-opacity-50 rounded-top" style="border-top-left-radius: 12px; border-top-right-radius: 12px;">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-4">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text bg-white border-end-0 text-secondary"><i class="bi bi-search"></i></span>
|
||||
<input type="text" class="form-control bg-white border-start-0 ps-0" placeholder="Search clients, email, or contact...">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<select class="form-select bg-white">
|
||||
<option value="">All Service Types</option>
|
||||
<option value="voip">PBX / SIP Trunking</option>
|
||||
<option value="gis">GIS Mapping Services</option>
|
||||
<option value="web">Web Infrastructure</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<select class="form-select bg-white">
|
||||
<option value="">Billing: All</option>
|
||||
<option value="prepaid">Prepaid</option>
|
||||
<option value="postpaid">Postpaid</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-custom table-hover mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Client Details</th>
|
||||
<th>Primary Contact</th>
|
||||
<th>Services</th>
|
||||
<th>Billing</th>
|
||||
<th>Status</th>
|
||||
<th class="text-end">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="avatar-circle bg-primary bg-opacity-10 text-primary me-3">CT</div>
|
||||
<div>
|
||||
<div class="fw-bold text-dark">Click Tech Team</div>
|
||||
<div class="text-secondary" style="font-size: 0.8rem;"><i class="bi bi-geo-alt me-1"></i>Accra, GH</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="fw-semibold">Admin Desk</div>
|
||||
<div class="text-secondary" style="font-size: 0.8rem;">hello@clicktechteam.com</div>
|
||||
</td>
|
||||
<td><span class="badge bg-secondary bg-opacity-10 text-secondary border">Web Infrastructure</span></td>
|
||||
<td><span class="badge bg-info bg-opacity-10 text-info">Postpaid</span></td>
|
||||
<td><span class="badge bg-success bg-opacity-10 text-success px-2 py-1"><i class="bi bi-check-circle me-1"></i>Active</span></td>
|
||||
<td class="text-end">
|
||||
<button class="btn btn-sm btn-light text-secondary me-1"><i class="bi bi-eye"></i></button>
|
||||
<button class="btn btn-sm btn-light text-primary me-1 btn-edit-client"
|
||||
data-id="1" data-name="Click Tech Team" data-email="hello@clicktechteam.com" data-contact="Admin Desk" data-phone="+233 20 000 0000" data-location="Accra, GH" data-service="web" data-billing="postpaid" data-status="active">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="avatar-circle bg-success bg-opacity-10 text-success me-3">AT</div>
|
||||
<div>
|
||||
<div class="fw-bold text-dark">Alpha Telecom Services</div>
|
||||
<div class="text-secondary" style="font-size: 0.8rem;"><i class="bi bi-geo-alt me-1"></i>Kumasi, GH</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="fw-semibold">Michael Osei</div>
|
||||
<div class="text-secondary" style="font-size: 0.8rem;">m.osei@alphatel.com.gh</div>
|
||||
</td>
|
||||
<td><span class="badge bg-secondary bg-opacity-10 text-secondary border">SIP Trunking / PBX</span></td>
|
||||
<td><span class="badge bg-warning bg-opacity-10 text-warning text-dark">Prepaid</span></td>
|
||||
<td><span class="badge bg-success bg-opacity-10 text-success px-2 py-1"><i class="bi bi-check-circle me-1"></i>Active</span></td>
|
||||
<td class="text-end">
|
||||
<button class="btn btn-sm btn-light text-secondary me-1"><i class="bi bi-eye"></i></button>
|
||||
<button class="btn btn-sm btn-light text-primary me-1 btn-edit-client"
|
||||
data-id="2" data-name="Alpha Telecom Services" data-email="m.osei@alphatel.com.gh" data-contact="Michael Osei" data-phone="+233 24 000 0000" data-location="Kumasi, GH" data-service="pbx" data-billing="prepaid" data-status="active">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="avatar-circle bg-warning bg-opacity-10 text-warning me-3">LS</div>
|
||||
<div>
|
||||
<div class="fw-bold text-dark">LiveScores Media ZM</div>
|
||||
<div class="text-secondary" style="font-size: 0.8rem;"><i class="bi bi-geo-alt me-1"></i>Lusaka, ZM</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="fw-semibold">David Phiri</div>
|
||||
<div class="text-secondary" style="font-size: 0.8rem;">david@livescores.co.zm</div>
|
||||
</td>
|
||||
<td><span class="badge bg-secondary bg-opacity-10 text-secondary border">USSD App / API</span></td>
|
||||
<td><span class="badge bg-info bg-opacity-10 text-info">Postpaid</span></td>
|
||||
<td><span class="badge bg-warning bg-opacity-10 text-warning px-2 py-1"><i class="bi bi-clock me-1"></i>Onboarding</span></td>
|
||||
<td class="text-end">
|
||||
<button class="btn btn-sm btn-light text-secondary me-1"><i class="bi bi-eye"></i></button>
|
||||
<button class="btn btn-sm btn-light text-primary me-1 btn-edit-client"
|
||||
data-id="3" data-name="LiveScores Media ZM" data-email="david@livescores.co.zm" data-contact="David Phiri" data-phone="+260 97 000 0000" data-location="Lusaka, ZM" data-service="api" data-billing="postpaid" data-status="onboarding">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="p-3 d-flex justify-content-between align-items-center">
|
||||
<span class="text-secondary" style="font-size: 0.85rem;">Showing 1 to 3 of 24 entries</span>
|
||||
<nav>
|
||||
<ul class="pagination pagination-sm mb-0">
|
||||
<li class="page-item disabled"><a class="page-link" href="#">Previous</a></li>
|
||||
<li class="page-item active"><a class="page-link" href="#" style="background-color: #5c4df0; border-color: #5c4df0;">1</a></li>
|
||||
<li class="page-item"><a class="page-link text-dark" href="#">2</a></li>
|
||||
<li class="page-item"><a class="page-link text-dark" href="#">Next</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<div class="modal fade" id="clientModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title fw-bold" id="clientModalTitle">Add New Client</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
|
||||
<form id="clientForm">
|
||||
<div class="modal-body p-4">
|
||||
<input type="hidden" id="clientId" name="id">
|
||||
|
||||
<h6 class="fw-bold mb-3 text-secondary text-uppercase" style="font-size: 0.75rem;">Company Details</h6>
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label text-secondary fw-semibold" style="font-size: 0.85rem;">Company Name *</label>
|
||||
<input type="text" class="form-control" id="clientName" name="name" required placeholder="e.g. Acme Corp">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label text-secondary fw-semibold" style="font-size: 0.85rem;">Location / Region</label>
|
||||
<input type="text" class="form-control" id="clientLocation" name="location" placeholder="e.g. Accra, GH">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h6 class="fw-bold mb-3 text-secondary text-uppercase" style="font-size: 0.75rem;">Primary Contact</h6>
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label text-secondary fw-semibold" style="font-size: 0.85rem;">Full Name</label>
|
||||
<input type="text" class="form-control" id="clientContact" name="contact_name" placeholder="Contact person">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label text-secondary fw-semibold" style="font-size: 0.85rem;">Email Address *</label>
|
||||
<input type="email" class="form-control" id="clientEmail" name="email" required placeholder="name@company.com">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label text-secondary fw-semibold" style="font-size: 0.85rem;">Phone Number</label>
|
||||
<input type="text" class="form-control" id="clientPhone" name="phone" placeholder="+233 ...">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h6 class="fw-bold mb-3 text-secondary text-uppercase" style="font-size: 0.75rem;">Service & Billing Configuration</h6>
|
||||
<div class="row g-3">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label text-secondary fw-semibold" style="font-size: 0.85rem;">Primary Service</label>
|
||||
<select class="form-select" id="clientService" name="service_type">
|
||||
<option value="pbx">SIP Trunking / PBX</option>
|
||||
<option value="api">USSD App / API</option>
|
||||
<option value="gis">GIS Data / Mapping</option>
|
||||
<option value="web">Web Infrastructure</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label text-secondary fw-semibold" style="font-size: 0.85rem;">Billing Type</label>
|
||||
<select class="form-select" id="clientBilling" name="billing_type">
|
||||
<option value="postpaid">Postpaid (Invoice)</option>
|
||||
<option value="prepaid">Prepaid (Balance)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label text-secondary fw-semibold" style="font-size: 0.85rem;">Account Status</label>
|
||||
<select class="form-select" id="clientStatus" name="status">
|
||||
<option value="active">Active</option>
|
||||
<option value="onboarding">Onboarding</option>
|
||||
<option value="suspended">Suspended</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer bg-light">
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="submit" class="btn text-white fw-bold px-4" style="background-color: #5c4df0;" id="btnSubmitClient">
|
||||
Save Client
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
// Reusable Clock Update
|
||||
function updateClock() {
|
||||
const now = new Date();
|
||||
$('#current-time').text(now.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: true }));
|
||||
}
|
||||
updateClock();
|
||||
setInterval(updateClock, 1000);
|
||||
|
||||
const clientModal = new bootstrap.Modal(document.getElementById('clientModal'));
|
||||
const $form = $('#clientForm');
|
||||
|
||||
// --- OPEN MODAL FOR CREATE ---
|
||||
$('#btnOpenClientModal').on('click', function() {
|
||||
$form[0].reset();
|
||||
$('#clientId').val('');
|
||||
$('#clientModalTitle').text('Add New Client');
|
||||
$('#btnSubmitClient').html('<i class="bi bi-save me-2"></i>Save Client');
|
||||
clientModal.show();
|
||||
});
|
||||
|
||||
// --- OPEN MODAL FOR EDIT ---
|
||||
$('.btn-edit-client').on('click', function() {
|
||||
// Fetch data attributes from the clicked button
|
||||
const data = $(this).data();
|
||||
|
||||
// Populate the form
|
||||
$('#clientId').val(data.id);
|
||||
$('#clientName').val(data.name);
|
||||
$('#clientLocation').val(data.location);
|
||||
$('#clientContact').val(data.contact);
|
||||
$('#clientEmail').val(data.email);
|
||||
$('#clientPhone').val(data.phone);
|
||||
$('#clientService').val(data.service);
|
||||
$('#clientBilling').val(data.billing);
|
||||
$('#clientStatus').val(data.status);
|
||||
|
||||
// Update UI text
|
||||
$('#clientModalTitle').text('Edit Client: ' + data.name);
|
||||
$('#btnSubmitClient').html('<i class="bi bi-check-circle me-2"></i>Update Client');
|
||||
|
||||
clientModal.show();
|
||||
});
|
||||
|
||||
// --- HANDLE FORM SUBMISSION ---
|
||||
$form.on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
const $btn = $('#btnSubmitClient');
|
||||
const originalText = $btn.html();
|
||||
const isEdit = $('#clientId').val() !== '';
|
||||
|
||||
$btn.html('<span class="spinner-border spinner-border-sm me-2"></span> Processing...');
|
||||
$btn.prop('disabled', true);
|
||||
|
||||
// Simulate AJAX Request to Laravel Backend
|
||||
setTimeout(() => {
|
||||
const formData = $(this).serializeArray();
|
||||
console.log('Submitted Client Data:', formData);
|
||||
|
||||
$btn.html(originalText);
|
||||
$btn.prop('disabled', false);
|
||||
clientModal.hide();
|
||||
|
||||
alert(isEdit ? 'Client updated successfully!' : 'New client created successfully!');
|
||||
// Normally you would reload the datatable here or append the new row
|
||||
}, 800);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -578,6 +578,4 @@
|
||||
<script src="{{ asset('public/assets/js/client-show-modal.js') }}"></script>
|
||||
<script>
|
||||
</script>
|
||||
@endpush
|
||||
|
||||
0546907824
|
||||
@endpush
|
||||
@@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
<title>CML ERP</title>
|
||||
<title>Click ERP</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
|
||||
<style>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@extends('layouts.masterbeta')
|
||||
|
||||
@section('title', 'Nexus ERP - Document Vault')
|
||||
@section('title', 'Click ERP - Document Vault')
|
||||
|
||||
@push('styles')
|
||||
<style>
|
||||
@@ -122,7 +122,7 @@
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="file-icon icon-pdf me-3"><i class="bi bi-file-earmark-pdf-fill"></i></div>
|
||||
<div>
|
||||
<div class="fw-bold text-dark">MTN_SIP_Trunk_SLA_2026.pdf</div>
|
||||
<div class="fw-bold text-dark">MTN_SMS_SLA_2026.pdf</div>
|
||||
<div class="text-secondary" style="font-size: 0.75rem;">ID: DOC-9942</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -151,12 +151,12 @@
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="file-icon icon-word me-3"><i class="bi bi-file-earmark-word-fill"></i></div>
|
||||
<div>
|
||||
<div class="fw-bold text-dark">ClickTech_Hosting_Proposal_Draft.docx</div>
|
||||
<div class="fw-bold text-dark">ClickTech_SMS_Proposal_Draft.docx</div>
|
||||
<div class="text-secondary" style="font-size: 0.75rem;">ID: DOC-9943</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="badge bg-info bg-opacity-10 text-info border border-info-subtle"><i class="bi bi-people me-1"></i>Client: Click Tech</span></td>
|
||||
<td><span class="badge bg-info bg-opacity-10 text-info border border-info-subtle"><i class="bi bi-people me-1"></i>Client: Gleaner Solutions</span></td>
|
||||
<td><span class="badge bg-secondary bg-opacity-10 text-secondary">Draft / Proposal</span></td>
|
||||
<td>
|
||||
<div class="text-dark fw-semibold" style="font-size: 0.85rem;">850 KB</div>
|
||||
|
||||
195
resources/views/leave/index.blade.php
Normal file
195
resources/views/leave/index.blade.php
Normal file
@@ -0,0 +1,195 @@
|
||||
@extends('layouts.masterbeta')
|
||||
|
||||
@section('title', 'Click ERP - Leave Management')
|
||||
|
||||
@section('breadcrumbs')
|
||||
<a href="{{ url('/') }}" class="text-secondary text-decoration-none"><i class="bi bi-house me-2"></i></a>
|
||||
<i class="bi bi-chevron-right text-secondary me-2" style="font-size: 0.8rem;"></i>
|
||||
<span class="fw-bold" style="font-size: 0.95rem;">Leave Administration</span>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<h4 class="fw-bold mb-1">Leave Administration</h4>
|
||||
<p class="text-secondary mb-0" style="font-size: 0.9rem;">Review and manage employee time-off requests.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- KPIs -->
|
||||
<div class="row g-4 mb-4">
|
||||
<div class="col-md-6">
|
||||
<div class="content-card p-4 h-100 border-start border-4 border-0 shadow-sm" style="border-left-color: #f59e0b !important;">
|
||||
<div class="text-secondary fw-bold mb-1" style="font-size: 0.75rem; letter-spacing: 0.5px;">PENDING APPROVALS</div>
|
||||
<h2 class="fw-bold mb-0 text-dark">{{ $pendingCount }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="content-card p-4 h-100 border-start border-4 border-0 shadow-sm" style="border-left-color: #10b981 !important;">
|
||||
<div class="text-secondary fw-bold mb-1" style="font-size: 0.75rem; letter-spacing: 0.5px;">APPROVED THIS MONTH</div>
|
||||
<h2 class="fw-bold mb-0 text-dark">{{ $approvedThisMonth }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Data Table & Filter -->
|
||||
<div class="content-card shadow-sm">
|
||||
<div class="p-3 border-bottom bg-light bg-opacity-50">
|
||||
<form action="{{ route('leave.index') }}" method="GET">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text bg-white border-end-0 text-secondary"><i class="bi bi-search"></i></span>
|
||||
<input type="text" name="search" value="{{ request('search') }}" class="form-control bg-white border-start-0 ps-0" placeholder="Search by staff name...">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<select name="status" class="form-select bg-white" onchange="this.form.submit()">
|
||||
<option value="">Status: All</option>
|
||||
<option value="PENDING" {{ request('status') == 'PENDING' ? 'selected' : '' }}>Pending</option>
|
||||
<option value="APPROVED" {{ request('status') == 'APPROVED' ? 'selected' : '' }}>Approved</option>
|
||||
<option value="REJECTED" {{ request('status') == 'REJECTED' ? 'selected' : '' }}>Rejected</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button type="submit" class="btn text-white w-100" style="background-color: #5c4df0;">
|
||||
<i class="bi bi-funnel me-1"></i> Filter
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-custom table-hover mb-0 align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Employee</th>
|
||||
<th>Leave Type</th>
|
||||
<th>Duration</th>
|
||||
<th>Reason</th>
|
||||
<th>Status</th>
|
||||
<th class="text-end">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($leaveRequests as $request)
|
||||
<tr>
|
||||
<td>
|
||||
<div class="fw-bold text-dark">{{ $request->staff->name ?? 'Unknown Staff' }}</div>
|
||||
<div class="text-secondary small">Bal: {{ $request->staff->annual_leave_balance ?? 0 }} Days</div>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge bg-secondary bg-opacity-10 text-dark border">{{ $request->leave_type }}</span>
|
||||
<div class="fw-bold mt-1 text-dark">{{ $request->total_days }} Days</div>
|
||||
</td>
|
||||
<td class="small text-secondary">
|
||||
<div><i class="bi bi-calendar-event me-1"></i> {{ $request->start_date->format('M d, Y') }}</div>
|
||||
<div><i class="bi bi-calendar-check me-1"></i> {{ $request->end_date->format('M d, Y') }}</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="text-truncate small text-secondary" style="max-width: 250px;" title="{{ $request->reason }}">
|
||||
{{ $request->reason }}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
@if($request->status == 'APPROVED')
|
||||
<span class="badge bg-success bg-opacity-10 text-success border border-success-subtle px-2 py-1">Approved</span>
|
||||
@elseif($request->status == 'REJECTED')
|
||||
<span class="badge bg-danger bg-opacity-10 text-danger border border-danger-subtle px-2 py-1">Rejected</span>
|
||||
@else
|
||||
<span class="badge bg-warning bg-opacity-10 text-warning border border-warning-subtle px-2 py-1">Pending</span>
|
||||
@endif
|
||||
|
||||
@if($request->admin_remarks)
|
||||
<i class="bi bi-chat-text text-secondary ms-1" data-bs-toggle="tooltip" title="Notes: {{ $request->admin_remarks }}"></i>
|
||||
@endif
|
||||
</td>
|
||||
<td class="text-end">
|
||||
@if($request->status == 'PENDING')
|
||||
<button class="btn btn-sm btn-light text-success border-success border-opacity-25 me-1 btn-action-leave"
|
||||
data-id="{{ $request->id }}" data-action="APPROVED" title="Approve">
|
||||
<i class="bi bi-check-lg"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-light text-danger border-danger border-opacity-25 btn-action-leave"
|
||||
data-id="{{ $request->id }}" data-action="REJECTED" title="Reject">
|
||||
<i class="bi bi-x-lg"></i>
|
||||
</button>
|
||||
@else
|
||||
<span class="text-secondary small"><i class="bi bi-lock"></i> Processed</span>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="6" class="text-center py-4 text-secondary">No leave requests found.</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="p-3 border-top">
|
||||
{{ $leaveRequests->links('pagination::bootstrap-5') }}
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
// Initialize tooltips
|
||||
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
|
||||
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
|
||||
return new bootstrap.Tooltip(tooltipTriggerEl)
|
||||
});
|
||||
|
||||
// Handle Approve / Reject actions
|
||||
$('.btn-action-leave').on('click', function() {
|
||||
const requestId = $(this).data('id');
|
||||
const action = $(this).data('action');
|
||||
|
||||
const actionText = action === 'APPROVED' ? 'approve' : 'reject';
|
||||
const confirmColor = action === 'APPROVED' ? '#10b981' : '#ef4444';
|
||||
|
||||
Swal.fire({
|
||||
title: `Are you sure you want to ${actionText} this request?`,
|
||||
input: 'text',
|
||||
inputLabel: 'Add a remark (Optional)',
|
||||
inputPlaceholder: 'e.g., Have a great vacation! / Insufficient coverage...',
|
||||
icon: 'question',
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: confirmColor,
|
||||
cancelButtonColor: '#6c757d',
|
||||
confirmButtonText: `Yes, ${actionText} it!`,
|
||||
showLoaderOnConfirm: true,
|
||||
preConfirm: (remark) => {
|
||||
return $.ajax({
|
||||
url: `{{ url('leave-requests') }}/${requestId}/status`,
|
||||
type: 'POST',
|
||||
data: {
|
||||
_token: '{{ csrf_token() }}',
|
||||
_method: 'PUT',
|
||||
status: action,
|
||||
admin_remarks: remark
|
||||
}
|
||||
}).catch(error => {
|
||||
Swal.showValidationMessage(`Request failed: ${error.responseJSON?.message || 'Server error'}`);
|
||||
});
|
||||
},
|
||||
allowOutsideClick: () => !Swal.isLoading()
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Success!',
|
||||
text: 'Leave request status updated.',
|
||||
confirmButtonColor: '#5c4df0'
|
||||
}).then(() => {
|
||||
location.reload();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
235
resources/views/leave/my_leave.blade.php
Normal file
235
resources/views/leave/my_leave.blade.php
Normal file
@@ -0,0 +1,235 @@
|
||||
@extends('layouts.masterbeta')
|
||||
|
||||
@section('title', 'Click ERP - My Leave')
|
||||
|
||||
@push('styles')
|
||||
<style>
|
||||
.balance-card { background: linear-gradient(135deg, #5c4df0 0%, #4338ca 100%); color: white; border-radius: 12px; }
|
||||
.status-badge { font-size: 0.75rem; padding: 0.35em 0.65em; font-weight: 600; }
|
||||
</style>
|
||||
@endpush
|
||||
|
||||
@section('breadcrumbs')
|
||||
<a href="{{ url('/') }}" class="text-secondary text-decoration-none"><i class="bi bi-house me-2"></i></a>
|
||||
<i class="bi bi-chevron-right text-secondary me-2" style="font-size: 0.8rem;"></i>
|
||||
<span class="fw-bold" style="font-size: 0.95rem;">My Leave</span>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<h4 class="fw-bold mb-1">My Leave Dashboard</h4>
|
||||
<p class="text-secondary mb-0" style="font-size: 0.9rem;">Manage your time off and track request statuses.</p>
|
||||
</div>
|
||||
<button class="btn text-white fw-bold d-flex align-items-center" style="background-color: #5c4df0; border-radius: 8px;" id="btnOpenLeaveModal">
|
||||
<i class="bi bi-calendar-plus me-2"></i> Request Leave
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-4">
|
||||
<div class="content-card balance-card p-4 h-100 d-flex flex-column justify-content-center">
|
||||
<h6 class="text-white-50 text-uppercase fw-bold mb-2" style="letter-spacing: 1px; font-size: 0.8rem;">Annual Leave Balance</h6>
|
||||
<div class="d-flex align-items-baseline">
|
||||
<h1 class="display-4 fw-bold mb-0 me-2">{{ $staff->annual_leave_balance }}</h1>
|
||||
<span class="text-white-50 fs-5">Days</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="content-card p-4 h-100 border-start border-4 border-0" style="border-left-color: #10b981 !important;">
|
||||
<h6 class="text-secondary text-uppercase fw-bold mb-3" style="letter-spacing: 1px; font-size: 0.8rem;">Leave Policy Quick Guide</h6>
|
||||
<ul class="text-secondary mb-0 small" style="line-height: 1.8;">
|
||||
<li><strong>Policy 1</strong></li>
|
||||
<li><strong>Policy 2</strong></li>
|
||||
<li><strong>Policy 3</strong></li>
|
||||
<li><strong><!-- Annual Leave:</strong> Deducted from your balance. Weekends are automatically excluded from the calculation.</li>
|
||||
<li><strong>Sick Leave:</strong> Requires a medical certificate for absences exceeding 2 consecutive days.</li>
|
||||
<li><strong> -->Pending Requests:</strong> Can be canceled before HR approval. Once approved, contact HR to amend.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Leave History Table -->
|
||||
<div class="content-card">
|
||||
<div class="p-3 border-bottom bg-light bg-opacity-50">
|
||||
<h6 class="fw-bold mb-0">My Request History</h6>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-custom table-hover mb-0 align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date Submitted</th>
|
||||
<th>Leave Type</th>
|
||||
<th>Duration</th>
|
||||
<th>Total Days</th>
|
||||
<th>Reason</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($leaveRequests as $request)
|
||||
<tr>
|
||||
<td class="text-secondary small">{{ $request->created_at->format('M d, Y') }}</td>
|
||||
<td class="fw-medium">{{ $request->leave_type }}</td>
|
||||
<td class="small">
|
||||
{{ $request->start_date->format('M d, Y') }} <i class="bi bi-arrow-right mx-1 text-secondary"></i> {{ $request->end_date->format('M d, Y') }}
|
||||
</td>
|
||||
<td class="fw-bold">{{ $request->total_days }}</td>
|
||||
<td class="text-secondary small text-truncate" style="max-width: 200px;">
|
||||
{{ $request->reason }}
|
||||
</td>
|
||||
<td>
|
||||
@if($request->status == 'APPROVED')
|
||||
<span class="badge bg-success bg-opacity-10 text-success border border-success-subtle status-badge">Approved</span>
|
||||
@elseif($request->status == 'REJECTED')
|
||||
<span class="badge bg-danger bg-opacity-10 text-danger border border-danger-subtle status-badge">Rejected</span>
|
||||
@else
|
||||
<span class="badge bg-warning bg-opacity-10 text-warning border border-warning-subtle status-badge">Pending</span>
|
||||
@endif
|
||||
|
||||
@if($request->admin_remarks)
|
||||
<i class="bi bi-info-circle text-secondary ms-2" data-bs-toggle="tooltip" title="HR Note: {{ $request->admin_remarks }}"></i>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="6" class="text-center py-4 text-secondary">You haven't submitted any leave requests yet.</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="p-3 border-top">
|
||||
{{ $leaveRequests->links('pagination::bootstrap-5') }}
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('modals')
|
||||
<!-- REQUEST LEAVE MODAL -->
|
||||
<div class="modal fade" id="leaveModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-light">
|
||||
<h5 class="modal-title fw-bold">Request Time Off</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
|
||||
<form id="leaveForm">
|
||||
@csrf
|
||||
<input type="hidden" name="staff_member_id" value="{{ $staff->id }}">
|
||||
|
||||
<div class="modal-body p-4">
|
||||
<div id="leaveModalAlert"></div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-secondary fw-semibold small">Leave Type *</label>
|
||||
<select name="leave_type" class="form-select" required>
|
||||
<option value="">Select Type...</option>
|
||||
<option value="Annual">Annual Leave</option>
|
||||
<option value="Sick">Sick Leave</option>
|
||||
<option value="Maternity">Maternity/Paternity Leave</option>
|
||||
<option value="Unpaid">Unpaid Leave</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="row g-3 mb-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label text-secondary fw-semibold small">Start Date *</label>
|
||||
<!-- Set min date to today using standard HTML5 -->
|
||||
<input type="date" name="start_date" class="form-control" min="{{ date('Y-m-d') }}" required>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label text-secondary fw-semibold small">End Date *</label>
|
||||
<input type="date" name="end_date" class="form-control" min="{{ date('Y-m-d') }}" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-secondary fw-semibold small">Reason / Notes *</label>
|
||||
<textarea name="reason" class="form-control" rows="3" required placeholder="Briefly explain your request..."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer bg-light">
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="submit" id="btnSubmitLeave" class="btn text-white btn-sm fw-bold px-4" style="background-color: #5c4df0;">Submit Request</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endpush
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
// Initialize Bootstrap tooltips for the HR notes
|
||||
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
|
||||
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
|
||||
return new bootstrap.Tooltip(tooltipTriggerEl)
|
||||
});
|
||||
|
||||
const leaveModal = new bootstrap.Modal(document.getElementById('leaveModal'));
|
||||
const $form = $('#leaveForm');
|
||||
|
||||
$('#btnOpenLeaveModal').on('click', function() {
|
||||
$form[0].reset();
|
||||
$('#leaveModalAlert').html('');
|
||||
leaveModal.show();
|
||||
});
|
||||
|
||||
$form.on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const $btn = $('#btnSubmitLeave');
|
||||
const originalText = $btn.html();
|
||||
const $alertBox = $('#leaveModalAlert');
|
||||
|
||||
$btn.html('<span class="spinner-border spinner-border-sm me-2"></span> Processing...');
|
||||
$btn.prop('disabled', true);
|
||||
$alertBox.html('');
|
||||
|
||||
$.ajax({
|
||||
url: "{{ route('leave.store') }}",
|
||||
type: 'POST',
|
||||
data: $form.serialize(),
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
leaveModal.hide();
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Submitted!',
|
||||
text: response.message,
|
||||
confirmButtonColor: '#5c4df0'
|
||||
}).then(() => {
|
||||
location.reload();
|
||||
});
|
||||
}
|
||||
},
|
||||
error: function(xhr) {
|
||||
let errorMessage = 'An unexpected error occurred.';
|
||||
|
||||
if (xhr.status === 422) {
|
||||
// If it's the insufficient balance custom error or validation error
|
||||
errorMessage = xhr.responseJSON.message || 'Please check the form for errors.';
|
||||
}
|
||||
|
||||
$alertBox.html(`
|
||||
<div class="alert alert-danger d-flex align-items-center py-2 px-3 small" role="alert">
|
||||
<i class="bi bi-exclamation-triangle-fill me-2"></i>
|
||||
<div>${errorMessage}</div>
|
||||
</div>
|
||||
`);
|
||||
},
|
||||
complete: function() {
|
||||
$btn.html(originalText).prop('disabled', false);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
399
resources/views/profile/index.blade.php
Normal file
399
resources/views/profile/index.blade.php
Normal file
@@ -0,0 +1,399 @@
|
||||
@extends('layouts.masterbeta')
|
||||
|
||||
@section('title', 'Click ERP - My Profile')
|
||||
|
||||
@push('styles')
|
||||
<style>
|
||||
.profile-avatar-wrapper { position: relative; width: 120px; height: 120px; margin: 0 auto; }
|
||||
.profile-avatar { width: 100%; height: 100%; border-radius: 50%; object-fit: cover; border: 4px solid #fff; box-shadow: 0 4px 10px rgba(0,0,0,0.1); }
|
||||
.profile-avatar-placeholder { width: 100%; height: 100%; border-radius: 50%; background-color: #f8f9fc; display: flex; align-items: center; justify-content: center; font-size: 2.5rem; font-weight: bold; color: #5c4df0; border: 4px solid #fff; box-shadow: 0 4px 10px rgba(0,0,0,0.1); }
|
||||
.btn-upload-avatar { position: absolute; bottom: 0; right: 0; background: #5c4df0; color: #fff; width: 32px; height: 32px; border-radius: 50%; display: flex; align-items: center; justify-content: center; cursor: pointer; border: 2px solid #fff; transition: all 0.2s; }
|
||||
.btn-upload-avatar:hover { background: #4338ca; transform: scale(1.1); }
|
||||
</style>
|
||||
@endpush
|
||||
|
||||
@section('breadcrumbs')
|
||||
<a href="{{ url('/') }}" class="text-secondary text-decoration-none"><i class="bi bi-house me-2"></i></a>
|
||||
<i class="bi bi-chevron-right text-secondary me-2" style="font-size: 0.8rem;"></i>
|
||||
<span class="fw-bold" style="font-size: 0.95rem;">My Profile</span>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="mb-4">
|
||||
<h4 class="fw-bold mb-1">Personal Settings</h4>
|
||||
<p class="text-secondary mb-0" style="font-size: 0.9rem;">Manage your profile information and emergency contacts.</p>
|
||||
</div>
|
||||
|
||||
<div class="row g-4">
|
||||
<!-- Left Column: Organizational Info & Quick Links -->
|
||||
<div class="col-lg-4">
|
||||
<div class="content-card p-4 text-center mb-4">
|
||||
<form id="avatarForm" enctype="multipart/form-data">
|
||||
<div class="profile-avatar-wrapper mb-3">
|
||||
@if($staff->profile_pic)
|
||||
<img src="{{ asset('public/storage/' . $staff->profile_pic) }}" class="profile-avatar" id="avatarPreviewDisplay" alt="{{ $staff->name }}">
|
||||
@else
|
||||
@php
|
||||
$words = explode(' ', $staff->name);
|
||||
$initials = strtoupper(substr($words[0], 0, 1) . (isset($words[1]) ? substr($words[1], 0, 1) : ''));
|
||||
@endphp
|
||||
<div class="profile-avatar-placeholder" id="avatarPreviewDisplay">{{ $initials }}</div>
|
||||
@endif
|
||||
<label for="profilePhoto" class="btn-upload-avatar" title="Update Photo">
|
||||
<i class="bi bi-camera-fill" style="font-size: 0.9rem;"></i>
|
||||
</label>
|
||||
<input type="file" id="profilePhoto" name="photo" class="d-none" accept=".jpg,.jpeg,.png">
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<h5 class="fw-bold mb-1 text-dark">{{ $staff->name }}</h5>
|
||||
<p class="text-secondary small mb-3">{{ $staff->designation ?? 'Staff Member' }}</p>
|
||||
|
||||
<span class="badge {{ $staff->status == 'ACTIVE' ? 'bg-success' : 'bg-warning' }} bg-opacity-10 text-{{ $staff->status == 'ACTIVE' ? 'success' : 'warning' }} px-3 py-2 rounded-pill mb-4">
|
||||
{{ $staff->status }}
|
||||
</span>
|
||||
|
||||
<div class="border-top pt-4 text-start">
|
||||
<h6 class="fw-bold text-secondary mb-3 text-uppercase" style="font-size: 0.75rem; letter-spacing: 1px;">Work Information</h6>
|
||||
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<div class="bg-light p-2 rounded me-3 text-secondary"><i class="bi bi-envelope"></i></div>
|
||||
<div>
|
||||
<div class="small text-secondary fw-semibold" style="font-size: 0.7rem;">WORK EMAIL</div>
|
||||
<div class="fw-medium text-dark small">{{ $staff->email }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<div class="bg-light p-2 rounded me-3 text-secondary"><i class="bi bi-person-badge"></i></div>
|
||||
<div>
|
||||
<div class="small text-secondary fw-semibold" style="font-size: 0.7rem;">STAFF ID</div>
|
||||
<div class="fw-medium text-dark small">{{ $staff->staff_id ?? 'N/A' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center mb-4">
|
||||
<div class="bg-light p-2 rounded me-3 text-secondary"><i class="bi bi-calendar-check"></i></div>
|
||||
<div>
|
||||
<div class="small text-secondary fw-semibold" style="font-size: 0.7rem;">HIRE DATE</div>
|
||||
<div class="fw-medium text-dark small">{{ $staff->hire_date ? date('M d, Y', strtotime($staff->hire_date)) : 'N/A' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a href="{{ route('leave.my_leave') }}" class="btn w-100 fw-bold border text-primary d-flex justify-content-between align-items-center" style="background-color: #f8f9fc;">
|
||||
<span><i class="bi bi-airplane me-2"></i> Leave Management</span>
|
||||
<span class="badge bg-primary rounded-pill">{{ $staff->annual_leave_balance }} Days</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right Column: Editable Form & Emergency Contacts -->
|
||||
<div class="col-lg-8">
|
||||
<!-- Personal Details Form -->
|
||||
<div class="content-card p-4 mb-4">
|
||||
<h5 class="fw-bold mb-4 border-bottom pb-3">Edit Profile Details</h5>
|
||||
|
||||
<form id="profileForm">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
|
||||
<div class="row g-4 mb-4">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label text-secondary fw-semibold small">Full Name</label>
|
||||
<input type="text" class="form-control" name="name" value="{{ $staff->name }}" required>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label text-secondary fw-semibold small">Phone Number</label>
|
||||
<input type="text" class="form-control" name="phone" value="{{ $staff->phone }}">
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label text-secondary fw-semibold small">Personal Email</label>
|
||||
<input type="email" class="form-control" name="personal_email" value="{{ $staff->personal_email }}" placeholder="Alternate contact email">
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label text-secondary fw-semibold small">Location / Country</label>
|
||||
<input type="text" class="form-control" name="location_country" value="{{ $staff->location_country }}">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label text-secondary fw-semibold small">Date of Birth (Month & Day)</label>
|
||||
<div class="row g-2">
|
||||
@php
|
||||
// Extract current month and day if they exist in the DB (e.g., "12-25" or "2026-12-25")
|
||||
$currentDob = $staff->dob ? date('m-d', strtotime($staff->dob)) : '--';
|
||||
[$currMonth, $currDay] = explode('-', $currentDob) + [null, null];
|
||||
@endphp
|
||||
|
||||
<!-- Month Dropdown -->
|
||||
<div class="col-7">
|
||||
<select class="form-select" name="birth_month">
|
||||
<option value="">Select Month</option>
|
||||
<option value="01" {{ $currMonth == '01' ? 'selected' : '' }}>January</option>
|
||||
<option value="02" {{ $currMonth == '02' ? 'selected' : '' }}>February</option>
|
||||
<option value="03" {{ $currMonth == '03' ? 'selected' : '' }}>March</option>
|
||||
<option value="04" {{ $currMonth == '04' ? 'selected' : '' }}>April</option>
|
||||
<option value="05" {{ $currMonth == '05' ? 'selected' : '' }}>May</option>
|
||||
<option value="06" {{ $currMonth == '06' ? 'selected' : '' }}>June</option>
|
||||
<option value="07" {{ $currMonth == '07' ? 'selected' : '' }}>July</option>
|
||||
<option value="08" {{ $currMonth == '08' ? 'selected' : '' }}>August</option>
|
||||
<option value="09" {{ $currMonth == '09' ? 'selected' : '' }}>September</option>
|
||||
<option value="10" {{ $currMonth == '10' ? 'selected' : '' }}>October</option>
|
||||
<option value="11" {{ $currMonth == '11' ? 'selected' : '' }}>November</option>
|
||||
<option value="12" {{ $currMonth == '12' ? 'selected' : '' }}>December</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Day Dropdown -->
|
||||
<div class="col-5">
|
||||
<select class="form-select" name="birth_day">
|
||||
<option value="">Day</option>
|
||||
@for($d = 1; $d <= 31; $d++)
|
||||
@php $dayFormatted = str_pad($d, 2, '0', STR_PAD_LEFT); @endphp
|
||||
<option value="{{ $dayFormatted }}" {{ $currDay == $dayFormatted ? 'selected' : '' }}>{{ $d }}</option>
|
||||
@endfor
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-end border-top pt-3">
|
||||
<button type="submit" id="btnSubmitProfile" class="btn text-white fw-bold px-4" style="background-color: #5c4df0;">
|
||||
Save Changes
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Emergency Contacts / Dependents Section -->
|
||||
<div class="content-card p-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4 border-bottom pb-3">
|
||||
<h5 class="fw-bold mb-0">Emergency Contacts & Dependents</h5>
|
||||
<button class="btn btn-sm text-white fw-bold d-flex align-items-center" style="background-color: #5c4df0; border-radius: 6px;" id="btnOpenDependentModal">
|
||||
<i class="bi bi-plus-lg me-1"></i> Add Contact
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-custom table-hover mb-0 align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Full Name</th>
|
||||
<th>Relationship</th>
|
||||
<th>Phone</th>
|
||||
<th>Medical Notes</th>
|
||||
<th class="text-end">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($staff->dependents as $dependent)
|
||||
<tr>
|
||||
<td class="fw-bold text-dark">{{ $dependent->fullname }}</td>
|
||||
<td><span class="badge bg-secondary bg-opacity-10 text-dark border">{{ $dependent->relationship }}</span></td>
|
||||
<td class="small">{{ $dependent->phone ?? 'N/A' }}</td>
|
||||
<td class="small text-secondary text-truncate" style="max-width: 150px;">{{ $dependent->medical_details ?? 'None' }}</td>
|
||||
<td class="text-end">
|
||||
<div class="d-flex justify-content-end align-items-center"> <!-- Add this wrapper -->
|
||||
<button class="btn btn-sm btn-light text-primary me-1 btn-edit-dependent"
|
||||
data-id="{{ $dependent->id }}"
|
||||
data-fullname="{{ $dependent->fullname }}"
|
||||
data-relationship="{{ $dependent->relationship }}"
|
||||
data-phone="{{ $dependent->phone }}"
|
||||
data-medical_details="{{ $dependent->medical_details }}">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</button>
|
||||
<form action="{{ route('profile.dependent.destroy', $dependent->id) }}" method="POST" class="d-inline">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" class="btn btn-sm btn-light text-danger btn-delete-dependent">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="5" class="text-center py-4 text-secondary">No emergency contacts added yet.</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('modals')
|
||||
<!-- ADD / EDIT EMERGENCY CONTACT MODAL -->
|
||||
<div class="modal fade" id="dependentModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-light">
|
||||
<h5 class="modal-title fw-bold" id="dependentModalTitle">Add Emergency Contact</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
|
||||
<form id="dependentForm">
|
||||
@csrf
|
||||
<input type="hidden" id="dependentRecordId" name="id">
|
||||
<div class="modal-body p-4">
|
||||
<div id="dependentModalAlert"></div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-secondary fw-semibold small">Full Name *</label>
|
||||
<input type="text" class="form-control" id="dep_fullname" name="fullname" required placeholder="e.g. Jane Doe">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-secondary fw-semibold small">Relationship *</label>
|
||||
<input type="text" class="form-control" id="dep_relationship" name="relationship" required placeholder="e.g. Spouse, Parent, Sibling">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-secondary fw-semibold small">Phone Number</label>
|
||||
<input type="text" class="form-control" id="dep_phone" name="phone" placeholder="e.g. +233 24 123 4567">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-secondary fw-semibold small">Medical Details / Allergies</label>
|
||||
<textarea class="form-control" id="dep_medical" name="medical_details" rows="2" placeholder="Optional context..."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer bg-light">
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="submit" id="btnSubmitDependent" class="btn text-white btn-sm fw-bold px-4" style="background-color: #5c4df0;">Save Contact</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endpush
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
// Profile Photo Preview
|
||||
$('#profilePhoto').on('change', function(e) {
|
||||
if(e.target.files && e.target.files[0]) {
|
||||
let reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
$('#avatarPreviewDisplay').replaceWith(`<img src="${e.target.result}" class="profile-avatar" id="avatarPreviewDisplay">`);
|
||||
}
|
||||
reader.readAsDataURL(e.target.files[0]);
|
||||
}
|
||||
});
|
||||
|
||||
// Profile Form Update
|
||||
$('#profileForm').on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
const $btn = $('#btnSubmitProfile');
|
||||
const originalText = $btn.html();
|
||||
$btn.html('<span class="spinner-border spinner-border-sm me-2"></span> Saving...');
|
||||
$btn.prop('disabled', true);
|
||||
|
||||
let formData = new FormData(this);
|
||||
let photoFile = $('#profilePhoto')[0].files[0];
|
||||
if (photoFile) formData.append('photo', photoFile);
|
||||
|
||||
$.ajax({
|
||||
url: "{{ route('profile.update') }}",
|
||||
type: 'POST',
|
||||
data: formData,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Updated!',
|
||||
text: response.message,
|
||||
confirmButtonColor: '#5c4df0'
|
||||
}).then(() => location.reload());
|
||||
}
|
||||
},
|
||||
complete: function() {
|
||||
$btn.html(originalText).prop('disabled', false);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// --- EMERGENCY CONTACT MODAL SCRIPTS ---
|
||||
const dependentModal = new bootstrap.Modal(document.getElementById('dependentModal'));
|
||||
const $depForm = $('#dependentForm');
|
||||
|
||||
$('#btnOpenDependentModal').on('click', function() {
|
||||
$depForm[0].reset();
|
||||
$('#dependentRecordId').val('');
|
||||
$('#dependentModalTitle').text('Add Emergency Contact');
|
||||
$('#btnSubmitDependent').text('Save Contact');
|
||||
dependentModal.show();
|
||||
});
|
||||
|
||||
$('.btn-edit-dependent').on('click', function() {
|
||||
const data = $(this).data();
|
||||
$depForm[0].reset();
|
||||
$('#dependentRecordId').val(data.id);
|
||||
$('#dep_fullname').val(data.fullname);
|
||||
$('#dep_relationship').val(data.relationship);
|
||||
$('#dep_phone').val(data.phone);
|
||||
$('#dep_medical').val(data.medical_details);
|
||||
|
||||
$('#dependentModalTitle').text('Edit Emergency Contact');
|
||||
$('#btnSubmitDependent').text('Update Contact');
|
||||
dependentModal.show();
|
||||
});
|
||||
|
||||
$depForm.on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
const $btn = $('#btnSubmitDependent');
|
||||
const originalText = $btn.html();
|
||||
const recordId = $('#dependentRecordId').val();
|
||||
|
||||
const url = recordId ? "{{ url('profile/dependents') }}/" + recordId : "{{ route('profile.dependent.store') }}";
|
||||
let formData = $depForm.serialize();
|
||||
if (recordId) formData += '&_method=PUT';
|
||||
|
||||
$btn.html('<span class="spinner-border spinner-border-sm me-2"></span> Saving...');
|
||||
$btn.prop('disabled', true);
|
||||
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: 'POST',
|
||||
data: formData,
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
dependentModal.hide();
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Success!',
|
||||
text: response.message,
|
||||
confirmButtonColor: '#5c4df0'
|
||||
}).then(() => location.reload());
|
||||
}
|
||||
},
|
||||
complete: function() {
|
||||
$btn.html(originalText).prop('disabled', false);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('.btn-delete-dependent').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
const $form = $(this).closest('form');
|
||||
Swal.fire({
|
||||
title: 'Remove Contact?',
|
||||
text: "This emergency contact will be deleted.",
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: '#ef4444',
|
||||
cancelButtonColor: '#6c757d',
|
||||
confirmButtonText: 'Yes, remove!'
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) $form.submit();
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
@@ -41,7 +41,7 @@
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<h4 class="fw-bold mb-1">SMS Sender IDs</h4>
|
||||
<p class="text-secondary mb-0" style="font-size: 0.9rem;">Manage alphanumeric masks, shortcodes, and MNO registration statuses.</p>
|
||||
<p class="text-secondary mb-0" style="font-size: 0.9rem;">Manage Sender IDs</p>
|
||||
</div>
|
||||
<button class="btn text-white fw-bold d-flex align-items-center" style="background-color: #5c4df0; border-radius: 8px;" id="btnOpenSenderModal">
|
||||
<i class="bi bi-plus-lg me-2"></i> Add Sender ID
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<h4 class="fw-bold mb-1">Short Codes Management</h4>
|
||||
<p class="text-secondary mb-0" style="font-size: 0.9rem;">Manage SMS, USSD, and Voice short codes, toll-free lines, and validity periods.</p>
|
||||
<p class="text-secondary mb-0" style="font-size: 0.9rem;">Manage SMS, USSD, and Voice short codes and validity periods.</p>
|
||||
</div>
|
||||
<button class="btn text-white fw-bold d-flex align-items-center" style="background-color: #5c4df0; border-radius: 8px;" id="btnOpenShortCodeModal">
|
||||
<i class="bi bi-plus-lg me-2"></i> Add Short Code
|
||||
|
||||
@@ -1,66 +1,28 @@
|
||||
@extends('layouts.masterbeta')
|
||||
|
||||
@section('title', 'CML ERP - Staff Directory')
|
||||
@section('title', 'Click ERP - Staff Directory')
|
||||
|
||||
@push('styles')
|
||||
<!-- Select2 CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
|
||||
<style>
|
||||
/* Staff Card Specific Styling */
|
||||
.staff-card {
|
||||
background: #fff;
|
||||
border: 1px solid #eaedf1;
|
||||
border-radius: 12px;
|
||||
transition: all 0.2s ease-in-out;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.staff-card:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 8px 15px rgba(0,0,0,0.05);
|
||||
border-color: #d1d5db;
|
||||
}
|
||||
.staff-card { background: #fff; border: 1px solid #eaedf1; border-radius: 12px; transition: all 0.2s ease-in-out; position: relative; overflow: hidden; }
|
||||
.staff-card:hover { transform: translateY(-3px); box-shadow: 0 8px 15px rgba(0,0,0,0.05); border-color: #d1d5db; }
|
||||
.card-top-bar { height: 4px; width: 100%; position: absolute; top: 0; left: 0; }
|
||||
|
||||
/* The decorative colored top border */
|
||||
.card-top-bar {
|
||||
height: 4px;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
.dept-engineering { background-color: #5c4df0; }
|
||||
.dept-sales { background-color: #10b981; }
|
||||
.dept-support { background-color: #f59e0b; }
|
||||
.staff-avatar { width: 64px; height: 64px; border-radius: 12px; object-fit: cover; background-color: #f8f9fc; display: flex; align-items: center; justify-content: center; font-size: 1.5rem; font-weight: bold; color: #5c4df0; }
|
||||
.contact-row { font-size: 0.85rem; padding: 0.4rem 0; border-bottom: 1px solid #f8f9fc; }
|
||||
.contact-row:last-child { border-bottom: none; }
|
||||
|
||||
.staff-avatar {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
border-radius: 12px;
|
||||
object-fit: cover;
|
||||
background-color: #f8f9fc;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
color: #5c4df0;
|
||||
}
|
||||
|
||||
.contact-row {
|
||||
font-size: 0.85rem;
|
||||
padding: 0.4rem 0;
|
||||
border-bottom: 1px solid #f8f9fc;
|
||||
}
|
||||
.contact-row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
/* Select2 Bootstrap 5 tweaks */
|
||||
.select2-container .select2-selection--single { height: 38px; border: 1px solid #dee2e6; border-radius: 0.375rem; display: flex; align-items: center; }
|
||||
.select2-container--default .select2-selection--single .select2-selection__arrow { height: 36px; }
|
||||
</style>
|
||||
@endpush
|
||||
|
||||
@section('breadcrumbs')
|
||||
<a href="{{ url('/') }}" class="text-secondary text-decoration-none"><i class="bi bi-house me-2"></i></a>
|
||||
<i class="bi bi-chevron-right text-secondary me-2" style="font-size: 0.8rem;"></i>
|
||||
<a href="#" class="text-secondary text-decoration-none me-2" style="font-size: 0.95rem;">Core Entities</a>
|
||||
<i class="bi bi-chevron-right text-secondary me-2" style="font-size: 0.8rem;"></i>
|
||||
<span class="fw-bold" style="font-size: 0.95rem;">Staff Directory</span>
|
||||
@endsection
|
||||
|
||||
@@ -71,164 +33,139 @@
|
||||
<p class="text-secondary mb-0" style="font-size: 0.9rem;">Manage employee profiles, department roles, and system access.</p>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<button class="btn btn-outline-secondary d-flex align-items-center" style="border-radius: 8px;">
|
||||
<i class="bi bi-diagram-3 me-2"></i> Org Chart
|
||||
</button>
|
||||
<button class="btn text-white fw-bold d-flex align-items-center" style="background-color: #5c4df0; border-radius: 8px;" id="btnOpenStaffModal">
|
||||
<i class="bi bi-person-plus me-2"></i> Add Employee
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Filters -->
|
||||
<div class="content-card p-3 mb-4">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-5">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text bg-transparent border-end-0 text-secondary"><i class="bi bi-search"></i></span>
|
||||
<input type="text" class="form-control border-start-0 ps-0" placeholder="Search by name, role, or extension...">
|
||||
<form action="{{ route('staff.index') }}" method="GET">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-5">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text bg-transparent border-end-0 text-secondary"><i class="bi bi-search"></i></span>
|
||||
<input type="text" name="search" value="{{ request('search') }}" class="form-control border-start-0 ps-0" placeholder="Search by name, role, or phone...">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<select class="form-select" name="department_id" onchange="this.form.submit()">
|
||||
<option value="">All Departments</option>
|
||||
@foreach($departments as $dept)
|
||||
<option value="{{ $dept->id }}" {{ request('department_id') == $dept->id ? 'selected' : '' }}>{{ $dept->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<select class="form-select" name="status" onchange="this.form.submit()">
|
||||
<option value="">Status: All</option>
|
||||
<option value="ACTIVE" {{ request('status') == 'ACTIVE' ? 'selected' : '' }}>Active</option>
|
||||
<option value="ON-LEAVE" {{ request('status') == 'ON-LEAVE' ? 'selected' : '' }}>On Leave</option>
|
||||
<option value="INACTIVE" {{ request('status') == 'INACTIVE' ? 'selected' : '' }}>Inactive</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button type="submit" class="btn btn-secondary w-100" style="border-radius: 8px;">Filter</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<select class="form-select">
|
||||
<option value="">All Departments</option>
|
||||
<option value="engineering">Engineering & IT</option>
|
||||
<option value="sales">Sales & Accounts</option>
|
||||
<option value="support">Technical Support</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<select class="form-select">
|
||||
<option value="active">Active</option>
|
||||
<option value="on-leave">On Leave</option>
|
||||
<option value="inactive">Inactive</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button class="btn btn-secondary w-100" style="border-radius: 8px;">Filter</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Staff Grid -->
|
||||
<div class="row g-4">
|
||||
|
||||
<div class="col-xl-3 col-lg-4 col-md-6">
|
||||
<div class="staff-card p-4 h-100">
|
||||
<div class="card-top-bar dept-engineering"></div>
|
||||
@php
|
||||
// Define an array of Bootstrap background classes for the card top bars
|
||||
$colors = ['bg-primary', 'bg-success', 'bg-warning', 'bg-danger', 'bg-info'];
|
||||
@endphp
|
||||
|
||||
@forelse($staffMembers as $staff)
|
||||
@php
|
||||
// Generate Initials
|
||||
$words = explode(' ', $staff->name);
|
||||
$initials = strtoupper(substr($words[0], 0, 1) . (isset($words[1]) ? substr($words[1], 0, 1) : ''));
|
||||
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<div class="staff-avatar bg-primary bg-opacity-10 me-3">AD</div>
|
||||
<div>
|
||||
<h6 class="fw-bold mb-1 text-dark">Admin Desk</h6>
|
||||
<span class="badge bg-primary bg-opacity-10 text-primary border border-primary-subtle px-2 py-1" style="font-size: 0.7rem;">Systems Admin</span>
|
||||
</div>
|
||||
</div>
|
||||
// Match the department ID to the collection
|
||||
$deptName = $departments->firstWhere('id', $staff->department_id)->name ?? 'Unassigned';
|
||||
|
||||
// Cycle through colors based on the loop index
|
||||
$topBarColor = $colors[$loop->index % count($colors)];
|
||||
@endphp
|
||||
|
||||
<div class="mb-3">
|
||||
<div class="contact-row d-flex justify-content-between align-items-center">
|
||||
<span class="text-secondary"><i class="bi bi-buildings me-2"></i>Engineering</span>
|
||||
<span class="badge bg-success bg-opacity-10 text-success" style="font-size: 0.65rem;">ACTIVE</span>
|
||||
<div class="col-xl-3 col-lg-4 col-md-6">
|
||||
<div class="staff-card p-4 h-100 d-flex flex-column">
|
||||
<div class="card-top-bar {{ $topBarColor }}"></div>
|
||||
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
@if($staff->profile_pic)
|
||||
<img src="{{ asset('public/storage/' . $staff->profile_pic) }}" class="staff-avatar me-3" alt="{{ $staff->name }}">
|
||||
@else
|
||||
<div class="staff-avatar bg-primary bg-opacity-10 text-primary me-3">{{ $initials }}</div>
|
||||
@endif
|
||||
|
||||
<div>
|
||||
<h6 class="fw-bold mb-1 text-dark">{{ $staff->name }}</h6>
|
||||
<span class="badge bg-primary bg-opacity-10 text-primary border border-primary-subtle px-2 py-1" style="font-size: 0.7rem;">
|
||||
{{ $staff->designation ?? 'N/A' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="contact-row d-flex justify-content-between align-items-center">
|
||||
<span class="text-secondary"><i class="bi bi-envelope me-2"></i>admin@nexus.io</span>
|
||||
<a href="mailto:admin@nexus.io" class="text-secondary hover-primary"><i class="bi bi-box-arrow-up-right" style="font-size: 0.7rem;"></i></a>
|
||||
</div>
|
||||
<div class="contact-row d-flex align-items-center">
|
||||
<span class="text-secondary"><i class="bi bi-telephone me-2"></i>Ext: 1001</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2 mt-auto pt-2 border-top">
|
||||
<button class="btn btn-sm btn-light text-secondary flex-grow-1 fw-semibold btn-edit-staff"
|
||||
data-id="1" data-name="Admin Desk" data-email="admin@nexus.io" data-ext="1001" data-role="Systems Admin" data-dept="engineering" data-status="active">
|
||||
<i class="bi bi-pencil me-1"></i> Edit
|
||||
</button>
|
||||
<button class="btn btn-sm btn-light text-secondary flex-grow-1 fw-semibold">
|
||||
<i class="bi bi-shield-lock me-1"></i> Access
|
||||
</button>
|
||||
<div class="mb-3">
|
||||
<div class="contact-row d-flex justify-content-between align-items-center">
|
||||
<span class="text-secondary text-truncate" style="max-width: 140px;"><i class="bi bi-buildings me-2"></i>{{ $deptName }}</span>
|
||||
<span class="badge {{ $staff->status == 'ACTIVE' ? 'bg-success text-success' : 'bg-warning text-warning' }} bg-opacity-10" style="font-size: 0.65rem;">
|
||||
{{ $staff->status }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="contact-row d-flex justify-content-between align-items-center">
|
||||
<span class="text-secondary text-truncate" style="max-width: 180px;"><i class="bi bi-envelope me-2"></i>{{ $staff->email }}</span>
|
||||
<a href="mailto:{{ $staff->email }}" class="text-secondary"><i class="bi bi-box-arrow-up-right" style="font-size: 0.7rem;"></i></a>
|
||||
</div>
|
||||
<div class="contact-row d-flex align-items-center">
|
||||
<span class="text-secondary"><i class="bi bi-geo-alt me-2"></i>{{ $staff->location_country ?? 'Not Specified' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2 mt-auto pt-2 border-top">
|
||||
<button class="btn btn-sm btn-light text-primary flex-grow-1 fw-semibold btn-edit-staff"
|
||||
data-id="{{ $staff->id }}"
|
||||
data-name="{{ $staff->name }}"
|
||||
data-email="{{ $staff->email }}"
|
||||
data-phone="{{ $staff->phone }}"
|
||||
data-designation="{{ $staff->designation }}"
|
||||
data-department_id="{{ $staff->department_id }}"
|
||||
data-location_country="{{ $staff->location_country }}"
|
||||
data-status="{{ $staff->status }}"
|
||||
data-pic="{{ $staff->profile_pic ? asset('public/storage/' . $staff->profile_pic) : '' }}">
|
||||
<i class="bi bi-pencil me-1"></i> Edit
|
||||
</button>
|
||||
|
||||
<form action="{{ route('staff.destroy', $staff->id) }}" method="POST" class="flex-grow-1 m-0">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="button" class="btn btn-sm btn-light text-danger w-100 fw-semibold btn-delete-staff">
|
||||
<i class="bi bi-trash me-1"></i> Delete
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xl-3 col-lg-4 col-md-6">
|
||||
<div class="staff-card p-4 h-100">
|
||||
<div class="card-top-bar dept-sales"></div>
|
||||
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<div class="staff-avatar bg-success bg-opacity-10 text-success me-3">MO</div>
|
||||
<div>
|
||||
<h6 class="fw-bold mb-1 text-dark">Mansa Olympio</h6>
|
||||
<span class="badge bg-success bg-opacity-10 text-success border border-success-subtle px-2 py-1" style="font-size: 0.7rem;">Account Executive</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<div class="contact-row d-flex justify-content-between align-items-center">
|
||||
<span class="text-secondary"><i class="bi bi-buildings me-2"></i>Sales</span>
|
||||
<span class="badge bg-success bg-opacity-10 text-success" style="font-size: 0.65rem;">ACTIVE</span>
|
||||
</div>
|
||||
<div class="contact-row d-flex justify-content-between align-items-center">
|
||||
<span class="text-secondary text-truncate" style="max-width: 150px;"><i class="bi bi-envelope me-2"></i>m.osei@nexus.io</span>
|
||||
<a href="mailto:m.osei@nexus.io" class="text-secondary hover-primary"><i class="bi bi-box-arrow-up-right" style="font-size: 0.7rem;"></i></a>
|
||||
</div>
|
||||
<div class="contact-row d-flex align-items-center">
|
||||
<span class="text-secondary"><i class="bi bi-telephone me-2"></i>Ext: 1045</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2 mt-auto pt-2 border-top">
|
||||
<button class="btn btn-sm btn-light text-secondary flex-grow-1 fw-semibold btn-edit-staff"
|
||||
data-id="2" data-name="Michael Osei" data-email="m.osei@nexus.io" data-ext="1045" data-role="Account Executive" data-dept="sales" data-status="active">
|
||||
<i class="bi bi-pencil me-1"></i> Edit
|
||||
</button>
|
||||
<button class="btn btn-sm btn-light text-secondary flex-grow-1 fw-semibold">
|
||||
<i class="bi bi-shield-lock me-1"></i> Access
|
||||
</button>
|
||||
</div>
|
||||
@empty
|
||||
<div class="col-12 text-center py-5 text-secondary">
|
||||
<i class="bi bi-people mb-3 d-block" style="font-size: 3rem;"></i>
|
||||
No staff members found.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xl-3 col-lg-4 col-md-6">
|
||||
<div class="staff-card p-4 h-100">
|
||||
<div class="card-top-bar dept-support"></div>
|
||||
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<img src="https://ui-avatars.com/api/?name=David+Kumwenda&background=f59e0b&color=fff" class="staff-avatar me-3" alt="David Kumwenda">
|
||||
<div>
|
||||
<h6 class="fw-bold mb-1 text-dark">David Kumwenda</h6>
|
||||
<span class="badge bg-warning bg-opacity-10 text-dark border border-warning-subtle px-2 py-1" style="font-size: 0.7rem;">Software Developer</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<div class="contact-row d-flex justify-content-between align-items-center">
|
||||
<span class="text-secondary"><i class="bi bi-buildings me-2"></i>Tech</span>
|
||||
<span class="badge bg-secondary bg-opacity-10 text-secondary" style="font-size: 0.65rem;">ON LEAVE</span>
|
||||
</div>
|
||||
<div class="contact-row d-flex justify-content-between align-items-center">
|
||||
<span class="text-secondary text-truncate" style="max-width: 150px;"><i class="bi bi-envelope me-2"></i>s.mensah@nexus.io</span>
|
||||
<a href="mailto:s.mensah@nexus.io" class="text-secondary hover-primary"><i class="bi bi-box-arrow-up-right" style="font-size: 0.7rem;"></i></a>
|
||||
</div>
|
||||
<div class="contact-row d-flex align-items-center">
|
||||
<span class="text-secondary"><i class="bi bi-telephone me-2"></i>Ext: 1022</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2 mt-auto pt-2 border-top">
|
||||
<button class="btn btn-sm btn-light text-secondary flex-grow-1 fw-semibold btn-edit-staff"
|
||||
data-id="3" data-name="Sarah Mensah" data-email="s.mensah@nexus.io" data-ext="1022" data-role="VoIP Specialist" data-dept="support" data-status="on-leave">
|
||||
<i class="bi bi-pencil me-1"></i> Edit
|
||||
</button>
|
||||
<button class="btn btn-sm btn-light text-secondary flex-grow-1 fw-semibold">
|
||||
<i class="bi bi-shield-lock me-1"></i> Access
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@endforelse
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
{{ $staffMembers->links('pagination::bootstrap-5') }}
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('modals')
|
||||
<!-- ADD / EDIT MODAL -->
|
||||
<div class="modal fade" id="staffModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg modal-dialog-centered">
|
||||
<div class="modal-content" style="border-radius: 12px; border: none;">
|
||||
@@ -239,36 +176,39 @@
|
||||
|
||||
<form id="staffForm" enctype="multipart/form-data">
|
||||
<div class="modal-body p-4">
|
||||
<div id="staffModalAlert"></div>
|
||||
<input type="hidden" id="staffId" name="id">
|
||||
<input type="hidden" id="staffMethod" name="_method" value="POST">
|
||||
|
||||
<div class="row mb-4">
|
||||
<!-- Avatar Column -->
|
||||
<div class="col-md-3 text-center border-end pe-4">
|
||||
<div class="staff-avatar mx-auto mb-3" style="width: 100px; height: 100px; font-size: 2rem;" id="avatarPreview">
|
||||
<i class="bi bi-person text-secondary"></i>
|
||||
</div>
|
||||
<label for="staffPhoto" class="btn btn-sm btn-outline-secondary w-100">Upload Photo</label>
|
||||
<input type="file" id="staffPhoto" name="photo" class="d-none" accept="image/*">
|
||||
<input type="file" id="staffPhoto" name="photo" class="d-none" accept=".jpg,.jpeg,.png">
|
||||
<div class="form-text mt-1" style="font-size: 0.7rem;">Max size: 2MB</div>
|
||||
</div>
|
||||
|
||||
<!-- Identity Column -->
|
||||
<div class="col-md-9 ps-4">
|
||||
<h6 class="fw-bold mb-3 text-secondary text-uppercase" style="font-size: 0.75rem;">Identity</h6>
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label text-secondary fw-semibold" style="font-size: 0.85rem;">Full Name *</label>
|
||||
<label class="form-label text-secondary fw-semibold small">Full Name *</label>
|
||||
<input type="text" class="form-control" id="staffName" name="name" required placeholder="Jane Doe">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label text-secondary fw-semibold" style="font-size: 0.85rem;">Job Title *</label>
|
||||
<input type="text" class="form-control" id="staffRole" name="role" required placeholder="e.g. Network Engineer">
|
||||
<label class="form-label text-secondary fw-semibold small">Job Title *</label>
|
||||
<input type="text" class="form-control" id="staffDesignation" name="designation" required placeholder="e.g. Network Engineer">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label text-secondary fw-semibold" style="font-size: 0.85rem;">Email Address *</label>
|
||||
<input type="email" class="form-control" id="staffEmail" name="email" required placeholder="jane@nexus.io">
|
||||
<label class="form-label text-secondary fw-semibold small">Email Address *</label>
|
||||
<input type="email" class="form-control" id="staffEmail" name="email" required placeholder="jane@company.com">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label text-secondary fw-semibold" style="font-size: 0.85rem;">PBX Extension</label>
|
||||
<input type="text" class="form-control" id="staffExt" name="extension" placeholder="e.g. 1010">
|
||||
<label class="form-label text-secondary fw-semibold small">Phone / Extension</label>
|
||||
<input type="text" class="form-control" id="staffPhone" name="phone" placeholder="e.g. +233 55 123 4567">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -278,20 +218,30 @@
|
||||
|
||||
<h6 class="fw-bold mb-3 text-secondary text-uppercase" style="font-size: 0.75rem;">Organizational Assignment</h6>
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label text-secondary fw-semibold" style="font-size: 0.85rem;">Department</label>
|
||||
<select class="form-select" id="staffDept" name="department_id">
|
||||
<option value="engineering">Engineering & IT</option>
|
||||
<option value="sales">Sales & Accounts</option>
|
||||
<option value="support">Technical Support</option>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label text-secondary fw-semibold small">Department *</label>
|
||||
<select class="form-select" id="staffDept" name="department_id" required>
|
||||
<option value="">Select Department...</option>
|
||||
@foreach($departments as $dept)
|
||||
<option value="{{ $dept->id }}">{{ $dept->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label text-secondary fw-semibold" style="font-size: 0.85rem;">Status</label>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label text-secondary fw-semibold small">Location / Country</label>
|
||||
<select class="form-select select2-field" id="staffCountry" name="location_country">
|
||||
<option value="">Select Country</option>
|
||||
@foreach($countries as $country)
|
||||
<option value="{{ $country->en_short_name }}">{{ $country->en_short_name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label text-secondary fw-semibold small">Status</label>
|
||||
<select class="form-select" id="staffStatus" name="status">
|
||||
<option value="active">Active</option>
|
||||
<option value="on-leave">On Leave</option>
|
||||
<option value="inactive">Inactive</option>
|
||||
<option value="ACTIVE">Active</option>
|
||||
<option value="ON-LEAVE">On Leave</option>
|
||||
<option value="INACTIVE">Inactive</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@@ -309,19 +259,41 @@
|
||||
@endpush
|
||||
|
||||
@push('scripts')
|
||||
<!-- Select2 JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
const staffModal = new bootstrap.Modal(document.getElementById('staffModal'));
|
||||
const $form = $('#staffForm');
|
||||
|
||||
// Initialize Select2 inside the modal for Country
|
||||
$('#staffCountry').select2({
|
||||
placeholder: "Select Country",
|
||||
allowClear: true,
|
||||
width: '100%',
|
||||
dropdownParent: $('#staffModal')
|
||||
});
|
||||
|
||||
// Handle Photo Preview
|
||||
$('#staffPhoto').on('change', function(e) {
|
||||
if(e.target.files && e.target.files[0]) {
|
||||
let reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
$('#avatarPreview').html(`<img src="${e.target.result}" style="width:100%; height:100%; object-fit:cover; border-radius:12px;">`);
|
||||
}
|
||||
reader.readAsDataURL(e.target.files[0]);
|
||||
}
|
||||
});
|
||||
|
||||
// --- OPEN MODAL FOR CREATE ---
|
||||
$('#btnOpenStaffModal').on('click', function() {
|
||||
$form[0].reset();
|
||||
$('#staffId').val('');
|
||||
$('#staffMethod').val('POST');
|
||||
$('#avatarPreview').html('<i class="bi bi-person text-secondary"></i>');
|
||||
$('#staffCountry').val(null).trigger('change');
|
||||
|
||||
$('#staffModalTitle').text('Add New Employee');
|
||||
$('#btnSubmitStaff').html('<i class="bi bi-save me-2"></i>Save Employee');
|
||||
$('#avatarPreview').html('<i class="bi bi-person text-secondary"></i>');
|
||||
staffModal.show();
|
||||
});
|
||||
|
||||
@@ -331,44 +303,93 @@
|
||||
|
||||
$form[0].reset();
|
||||
$('#staffId').val(data.id);
|
||||
$('#staffMethod').val('PUT');
|
||||
|
||||
$('#staffName').val(data.name);
|
||||
$('#staffRole').val(data.role);
|
||||
$('#staffDesignation').val(data.designation);
|
||||
$('#staffEmail').val(data.email);
|
||||
$('#staffExt').val(data.ext);
|
||||
$('#staffDept').val(data.dept);
|
||||
$('#staffPhone').val(data.phone);
|
||||
$('#staffDept').val(data.department_id);
|
||||
$('#staffCountry').val(data.location_country).trigger('change');
|
||||
$('#staffStatus').val(data.status);
|
||||
|
||||
// Show initials in preview for demo purposes
|
||||
const initials = data.name.split(' ').map(n => n[0]).join('').substring(0,2);
|
||||
$('#avatarPreview').html(initials).addClass('bg-primary bg-opacity-10 text-primary');
|
||||
if (data.pic) {
|
||||
$('#avatarPreview').html(`<img src="${data.pic}" style="width:100%; height:100%; object-fit:cover; border-radius:12px;">`);
|
||||
} else {
|
||||
$('#avatarPreview').html('<i class="bi bi-person text-secondary"></i>');
|
||||
}
|
||||
|
||||
$('#staffModalTitle').text('Edit Employee: ' + data.name);
|
||||
$('#btnSubmitStaff').html('<i class="bi bi-check-circle me-2"></i>Update Employee');
|
||||
|
||||
staffModal.show();
|
||||
});
|
||||
|
||||
// --- HANDLE FORM SUBMISSION ---
|
||||
// --- HANDLE FORM SUBMISSION (AJAX) ---
|
||||
$form.on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
const $btn = $('#btnSubmitStaff');
|
||||
const originalText = $btn.html();
|
||||
const $alertBox = $('#staffModalAlert');
|
||||
|
||||
$btn.html('<span class="spinner-border spinner-border-sm me-2"></span> Saving...');
|
||||
$btn.prop('disabled', true);
|
||||
$alertBox.html('');
|
||||
$form.find('.is-invalid').removeClass('is-invalid');
|
||||
|
||||
// Use FormData to allow file uploads (profile picture)
|
||||
let formData = new FormData(this);
|
||||
const recordId = $('#staffId').val();
|
||||
const submitUrl = recordId ? "{{ url('staff') }}/" + recordId : "{{ url('staff') }}";
|
||||
|
||||
// Simulate AJAX Request
|
||||
setTimeout(() => {
|
||||
console.log('Staff Data Saved');
|
||||
$btn.html(originalText).prop('disabled', false);
|
||||
staffModal.hide();
|
||||
alert('Employee record saved successfully!');
|
||||
}, 800);
|
||||
if (recordId) formData.append('_method', 'PUT');
|
||||
|
||||
$.ajax({
|
||||
url: submitUrl,
|
||||
type: 'POST',
|
||||
data: formData,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
staffModal.hide();
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Success!',
|
||||
text: response.message,
|
||||
confirmButtonColor: '#5c4df0'
|
||||
}).then(() => location.reload());
|
||||
}
|
||||
},
|
||||
error: function(xhr) {
|
||||
if (xhr.status === 422) {
|
||||
let errors = xhr.responseJSON.errors;
|
||||
$alertBox.html('<div class="alert alert-danger py-2 px-3 small"><i class="bi bi-exclamation-triangle-fill me-2"></i>Please check the highlighted fields below.</div>');
|
||||
$.each(errors, function(key, value) {
|
||||
let $input = $form.find('[name="' + key + '"]');
|
||||
$input.addClass('is-invalid');
|
||||
});
|
||||
} else {
|
||||
$alertBox.html('<div class="alert alert-danger py-2 px-3 small"><i class="bi bi-x-circle-fill me-2"></i>An unexpected error occurred.</div>');
|
||||
}
|
||||
},
|
||||
complete: function() {
|
||||
$btn.html(originalText).prop('disabled', false);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// --- SWEETALERT FOR DELETE ---
|
||||
$('.btn-delete-staff').on('click', function() {
|
||||
const $form = $(this).closest('form');
|
||||
Swal.fire({
|
||||
title: 'Remove Staff Member?',
|
||||
text: "You are about to delete this employee's profile. This cannot be undone.",
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: '#ef4444',
|
||||
cancelButtonColor: '#6c757d',
|
||||
confirmButtonText: '<i class="bi bi-trash me-1"></i> Yes, remove them!'
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) $form.submit();
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user