added leave request, profile and staff members controllers

This commit is contained in:
Kwesi Banson Jnr
2026-08-20 09:30:43 +00:00
parent 1c4d32833a
commit 81d26dc138
152 changed files with 1627 additions and 152101 deletions

View File

@@ -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>

View File

@@ -578,6 +578,4 @@
<script src="{{ asset('public/assets/js/client-show-modal.js') }}"></script>
<script>
</script>
@endpush
0546907824
@endpush

View File

@@ -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>

View File

@@ -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>

View 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

View 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

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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>