699 lines
38 KiB
PHP
699 lines
38 KiB
PHP
@extends('layouts.masterbeta')
|
|
|
|
@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 { 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; }
|
|
|
|
.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>
|
|
<span class="fw-bold" style="font-size: 0.95rem;">Staff Directory</span>
|
|
@endsection
|
|
|
|
@section('content')
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<div>
|
|
<h4 class="fw-bold mb-1">Staff Directory</h4>
|
|
<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">
|
|
@if(strtolower(Auth::user()->department->name) == 'directors')
|
|
<button class="btn btn-warning btn-sm fw-bold" data-bs-toggle="modal" data-bs-target="#overrideModal">
|
|
<i class="bi bi-unlock-fill me-1"></i> Grant Report Override
|
|
</button>
|
|
@endif
|
|
@if(in_array(strtolower(Auth::user()->department->name) , ['directors', 'human resource']))
|
|
<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>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Filters -->
|
|
<div class="content-card p-3 mb-4">
|
|
<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>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- Staff Grid -->
|
|
<div class="row g-4">
|
|
@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) : ''));
|
|
|
|
// 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="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="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-success flex-grow-1 btn-view-staff"
|
|
data-id="{{ $staff->id }}"
|
|
title="View Profile Details">
|
|
<i class="bi bi-eye"></i> View
|
|
</button>
|
|
<button class="btn btn-sm btn-light text-primary me-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>
|
|
@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>
|
|
@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;">
|
|
<div class="modal-header bg-light" style="border-radius: 12px 12px 0 0;">
|
|
<h5 class="modal-title fw-bold" id="staffModalTitle">Add New Employee</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
|
|
<form id="staffForm" enctype="multipart/form-data">
|
|
<div class="modal-body p-4">
|
|
<div id="staffModalAlert"></div>
|
|
<input type="hidden" id="staffId" name="id">
|
|
|
|
<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=".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 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 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 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 small">Phone / Extension</label>
|
|
<input type="text" class="form-control" id="staffPhone" name="phone" placeholder="e.g. +233 55 123 4567">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<hr class="text-secondary opacity-25">
|
|
|
|
<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-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-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>
|
|
</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="btnSubmitStaff">
|
|
Save Employee
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- VIEW STAFF PROFILE MODAL -->
|
|
<div class="modal fade" id="viewStaffModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-lg modal-dialog-centered">
|
|
<div class="modal-content">
|
|
<div class="modal-header bg-light">
|
|
<h5 class="modal-title fw-bold" id="viewStaffTitle">Staff Profile Details</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
|
|
<div class="modal-body p-4">
|
|
<!-- Basic Info Header -->
|
|
<div class="d-flex align-items-center mb-4 pb-3 border-bottom">
|
|
<div id="viewStaffAvatarContainer" class="me-3">
|
|
<!-- Dynamic Avatar inserted via JS -->
|
|
</div>
|
|
<div>
|
|
<h4 class="fw-bold mb-1 text-dark" id="viewStaffName">---</h4>
|
|
<p class="text-secondary mb-1 small" id="viewStaffDesignation">---</p>
|
|
<span id="viewStaffStatusBadge" class="badge">---</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Info Grid -->
|
|
<div class="row g-3 mb-4">
|
|
<div class="col-md-4">
|
|
<div class="text-secondary small fw-semibold">WORK EMAIL</div>
|
|
<div class="fw-medium text-dark small" id="viewStaffEmail">---</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="text-secondary small fw-semibold">PHONE NUMBER</div>
|
|
<div class="fw-medium text-dark small" id="viewStaffPhone">---</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="text-secondary small fw-semibold">STAFF ID</div>
|
|
<div class="fw-medium text-dark small" id="viewStaffCode">---</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="text-secondary small fw-semibold">PERSONAL EMAIL</div>
|
|
<div class="fw-medium text-dark small" id="viewStaffPersonalEmail">---</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="text-secondary small fw-semibold">LOCATION / COUNTRY</div>
|
|
<div class="fw-medium text-dark small" id="viewStaffLocation">---</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="text-secondary small fw-semibold">ANNUAL LEAVE BALANCE</div>
|
|
<div class="fw-medium text-dark small"><span id="viewStaffLeaveBalance">0</span> Days</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Emergency Contacts Table Section -->
|
|
<h6 class="fw-bold text-dark mb-3"><i class="bi bi-shield-exclamation me-2 text-danger"></i>Emergency Contacts & Dependents</h6>
|
|
<div class="table-responsive">
|
|
<table class="table table-bordered table-sm align-middle mb-0">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th>Full Name</th>
|
|
<th>Relationship</th>
|
|
<th>Phone</th>
|
|
<th>Medical Details</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="viewStaffDependentsList">
|
|
<tr>
|
|
<td colspan="4" class="text-center text-secondary py-3">Loading emergency contacts...</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="modal-footer bg-light">
|
|
<button type="button" class="btn btn-outline-secondary btn-sm" data-bs-dismiss="modal">Close</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@push('modals')
|
|
|
|
|
|
<!-- OVERRIDE MODAL) -->
|
|
<div class="modal fade" id="overrideModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered">
|
|
<div class="modal-content">
|
|
<div class="modal-header bg-light border-bottom-0">
|
|
<h5 class="modal-title fw-bold">Grant Report Override</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
|
|
<form id="overrideForm">
|
|
@csrf
|
|
<div class="modal-body p-4">
|
|
<div class="alert alert-warning d-flex align-items-center p-3 mb-4" style="font-size: 0.85rem;">
|
|
<i class="bi bi-exclamation-triangle-fill me-2 fs-5 text-warning"></i>
|
|
<div>Temporarily unlock the submission window for a staff member to file a past daily report.</div>
|
|
</div>
|
|
|
|
<div id="overrideModalAlert"></div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label text-secondary fw-semibold small">Staff Member *</label>
|
|
<select class="form-select select2-staff" id="override_user_id" name="user_id" required style="width: 100%;">
|
|
<option value=""></option>
|
|
<!-- Assuming you pass $staffMembers from the controller -->
|
|
@foreach($allstaffMembers as $staff)
|
|
<option value="{{ $staff->id }}">{{ $staff->name }}</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
|
|
<div class="row g-3">
|
|
<div class="col-md-6">
|
|
<label class="form-label text-secondary fw-semibold small">Locked Report Date *</label>
|
|
<input type="date" class="form-control" name="report_date" required max="{{ now()->format('Y-m-d') }}">
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label text-secondary fw-semibold small">Validity Window</label>
|
|
<div class="input-group">
|
|
<input type="number" class="form-control" name="hours_valid" value="24" min="1" max="72">
|
|
<span class="input-group-text bg-light text-secondary">Hours</span>
|
|
</div>
|
|
<div class="form-text text-muted" style="font-size: 0.75rem;">Time given to submit before locking again.</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer bg-light border-top-0">
|
|
<button type="button" class="btn btn-outline-secondary btn-sm" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="submit" id="btnSubmitOverride" class="btn text-white btn-sm fw-bold px-4" style="background-color: #5c4df0;">
|
|
Grant Access
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endpush
|
|
@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('');
|
|
$('#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');
|
|
staffModal.show();
|
|
});
|
|
|
|
// --- OPEN MODAL FOR EDIT ---
|
|
$('.btn-edit-staff').on('click', function() {
|
|
const data = $(this).data();
|
|
|
|
$form[0].reset();
|
|
$('#staffId').val(data.id);
|
|
|
|
$('#staffName').val(data.name);
|
|
$('#staffDesignation').val(data.designation);
|
|
$('#staffEmail').val(data.email);
|
|
$('#staffPhone').val(data.phone);
|
|
$('#staffDept').val(data.department_id);
|
|
$('#staffCountry').val(data.location_country).trigger('change');
|
|
$('#staffStatus').val(data.status);
|
|
|
|
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 (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');
|
|
|
|
let formData = new FormData(this);
|
|
const recordId = $('#staffId').val();
|
|
const submitUrl = recordId ? "{{ url('staff') }}/" + recordId : "{{ url('staff') }}";
|
|
|
|
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();
|
|
});
|
|
});
|
|
});
|
|
|
|
$(document).ready(function() {
|
|
const viewModal = new bootstrap.Modal(document.getElementById('viewStaffModal'));
|
|
|
|
$('.btn-view-staff').on('click', function() {
|
|
const staffId = $(this).data('id');
|
|
|
|
// Reset fields to loading state
|
|
$('#viewStaffName').text('Loading...');
|
|
$('#viewStaffDesignation').text('');
|
|
$('#viewStaffEmail').text('');
|
|
$('#viewStaffPhone').text('');
|
|
$('#viewStaffCode').text('');
|
|
$('#viewStaffPersonalEmail').text('');
|
|
$('#viewStaffLocation').text('');
|
|
$('#viewStaffLeaveBalance').text('');
|
|
$('#viewStaffDependentsList').html('<tr><td colspan="4" class="text-center text-secondary py-3">Loading...</td></tr>');
|
|
|
|
viewModal.show();
|
|
|
|
// Fetch staff details via AJAX
|
|
$.ajax({
|
|
url: "{{ url('staff') }}/" + staffId + "/details", // Match your route endpoint
|
|
type: 'GET',
|
|
success: function(response) {
|
|
if (response.success) {
|
|
let staff = response.staff;
|
|
|
|
$('#viewStaffName').text(staff.name);
|
|
$('#viewStaffDesignation').text(staff.designation || 'Staff Member');
|
|
$('#viewStaffEmail').text(staff.email || 'N/A');
|
|
$('#viewStaffPhone').text(staff.phone || 'N/A');
|
|
$('#viewStaffCode').text(staff.staff_id || 'N/A');
|
|
$('#viewStaffPersonalEmail').text(staff.personal_email || 'N/A');
|
|
$('#viewStaffLocation').text(staff.location_country || 'N/A');
|
|
$('#viewStaffLeaveBalance').text(staff.annual_leave_balance ?? 0);
|
|
|
|
// Status Badge
|
|
let badgeClass = staff.status === 'ACTIVE' ? 'bg-success bg-opacity-10 text-success' : 'bg-warning bg-opacity-10 text-warning';
|
|
$('#viewStaffStatusBadge').attr('class', `badge ${badgeClass} px-3 py-1`).text(staff.status);
|
|
|
|
// Avatar setup
|
|
if (staff.profile_pic) {
|
|
$('#viewStaffAvatarContainer').html(`<img src="{{ asset('public/storage') }}/${staff.profile_pic}" class="rounded-circle border" style="width: 70px; height: 70px; object-fit: cover;">`);
|
|
} else {
|
|
let initials = staff.name.split(' ').map(n => n[0]).join('').substring(0, 2).toUpperCase();
|
|
$('#viewStaffAvatarContainer').html(`<div class="rounded-circle bg-light text-primary fw-bold d-flex align-items-center justify-content-center border" style="width: 70px; height: 70px; font-size: 1.5rem;">${initials}</div>`);
|
|
}
|
|
|
|
// Dependents Table population
|
|
let dependentsHtml = '';
|
|
if (response.dependents && response.dependents.length > 0) {
|
|
response.dependents.forEach(dep => {
|
|
dependentsHtml += `
|
|
<tr>
|
|
<td class="fw-bold">${dep.fullname}</td>
|
|
<td><span class="badge bg-secondary bg-opacity-10 text-dark border">${dep.relationship}</span></td>
|
|
<td>${dep.phone || 'N/A'}</td>
|
|
<td class="text-secondary">${dep.medical_details || 'None'}</td>
|
|
</tr>
|
|
`;
|
|
});
|
|
} else {
|
|
dependentsHtml = `<tr><td colspan="4" class="text-center text-secondary py-3">No emergency contacts found.</td></tr>`;
|
|
}
|
|
$('#viewStaffDependentsList').html(dependentsHtml);
|
|
}
|
|
},
|
|
error: function() {
|
|
$('#viewStaffName').text('Error loading profile details.');
|
|
}
|
|
});
|
|
});
|
|
|
|
});
|
|
|
|
// OVERRIDE
|
|
$(document).ready(function() {
|
|
|
|
// Initialize Select2 for the staff dropdown
|
|
$('.select2-staff').select2({
|
|
theme: 'bootstrap-5',
|
|
dropdownParent: $('#overrideModal'),
|
|
placeholder: 'Search staff member...',
|
|
allowClear: true
|
|
});
|
|
|
|
$('#overrideForm').on('submit', function(e) {
|
|
e.preventDefault();
|
|
|
|
let $form = $(this);
|
|
let $submitBtn = $('#btnSubmitOverride');
|
|
let originalText = $submitBtn.html();
|
|
let $alertBox = $('#overrideModalAlert');
|
|
|
|
$submitBtn.html('<span class="spinner-border spinner-border-sm me-2"></span>Processing...').prop('disabled', true);
|
|
$alertBox.html('');
|
|
$form.find('.is-invalid').removeClass('is-invalid');
|
|
$form.find('.invalid-feedback').remove();
|
|
|
|
$.ajax({
|
|
url: base_url + '/overrides', // Ensure you mapped this route in web.php to ReportOverrideController@store
|
|
type: 'POST',
|
|
data: $form.serialize(),
|
|
success: function(response) {
|
|
if(response.success) {
|
|
$alertBox.html(`
|
|
<div class="alert alert-success d-flex align-items-center p-3" role="alert">
|
|
<i class="bi bi-check-circle-fill me-2 fs-5 text-success"></i>
|
|
<div style="font-size: 0.85rem;">${response.message}</div>
|
|
</div>
|
|
`);
|
|
|
|
setTimeout(() => {
|
|
$('#overrideModal').modal('hide');
|
|
}, 1500);
|
|
}
|
|
},
|
|
error: function(xhr) {
|
|
if (xhr.status === 422) {
|
|
let errors = xhr.responseJSON.errors;
|
|
$.each(errors, function(key, value) {
|
|
let $input = $form.find('[name="' + key + '"]');
|
|
if ($input.length) {
|
|
$input.addClass('is-invalid');
|
|
$input.parent().append('<div class="invalid-feedback d-block fw-medium" style="font-size: 0.75rem;">' + value[0] + '</div>');
|
|
}
|
|
});
|
|
} else {
|
|
$alertBox.html(`
|
|
<div class="alert alert-danger d-flex align-items-center p-3" role="alert">
|
|
<i class="bi bi-x-circle-fill me-2 fs-5 text-danger"></i>
|
|
<div style="font-size: 0.85rem;">Server error. Please try again.</div>
|
|
</div>
|
|
`);
|
|
}
|
|
},
|
|
complete: function() {
|
|
$submitBtn.html(originalText).prop('disabled', false);
|
|
}
|
|
});
|
|
});
|
|
|
|
// Clean up modal on close
|
|
$('#overrideModal').on('hidden.bs.modal', function () {
|
|
$('#overrideForm')[0].reset();
|
|
$('#override_user_id').val(null).trigger('change');
|
|
$('#overrideModalAlert').html('');
|
|
$('#overrideForm').find('.is-invalid').removeClass('is-invalid');
|
|
$('#overrideForm').find('.invalid-feedback').remove();
|
|
});
|
|
});
|
|
|
|
</script>
|
|
@endpush |