added leave request, profile and staff members controllers
This commit is contained in:
195
resources/views/leave/index.blade.php
Normal file
195
resources/views/leave/index.blade.php
Normal file
@@ -0,0 +1,195 @@
|
||||
@extends('layouts.masterbeta')
|
||||
|
||||
@section('title', 'Click ERP - Leave Management')
|
||||
|
||||
@section('breadcrumbs')
|
||||
<a href="{{ url('/') }}" class="text-secondary text-decoration-none"><i class="bi bi-house me-2"></i></a>
|
||||
<i class="bi bi-chevron-right text-secondary me-2" style="font-size: 0.8rem;"></i>
|
||||
<span class="fw-bold" style="font-size: 0.95rem;">Leave Administration</span>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<h4 class="fw-bold mb-1">Leave Administration</h4>
|
||||
<p class="text-secondary mb-0" style="font-size: 0.9rem;">Review and manage employee time-off requests.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- KPIs -->
|
||||
<div class="row g-4 mb-4">
|
||||
<div class="col-md-6">
|
||||
<div class="content-card p-4 h-100 border-start border-4 border-0 shadow-sm" style="border-left-color: #f59e0b !important;">
|
||||
<div class="text-secondary fw-bold mb-1" style="font-size: 0.75rem; letter-spacing: 0.5px;">PENDING APPROVALS</div>
|
||||
<h2 class="fw-bold mb-0 text-dark">{{ $pendingCount }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="content-card p-4 h-100 border-start border-4 border-0 shadow-sm" style="border-left-color: #10b981 !important;">
|
||||
<div class="text-secondary fw-bold mb-1" style="font-size: 0.75rem; letter-spacing: 0.5px;">APPROVED THIS MONTH</div>
|
||||
<h2 class="fw-bold mb-0 text-dark">{{ $approvedThisMonth }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Data Table & Filter -->
|
||||
<div class="content-card shadow-sm">
|
||||
<div class="p-3 border-bottom bg-light bg-opacity-50">
|
||||
<form action="{{ route('leave.index') }}" method="GET">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text bg-white border-end-0 text-secondary"><i class="bi bi-search"></i></span>
|
||||
<input type="text" name="search" value="{{ request('search') }}" class="form-control bg-white border-start-0 ps-0" placeholder="Search by staff name...">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<select name="status" class="form-select bg-white" onchange="this.form.submit()">
|
||||
<option value="">Status: All</option>
|
||||
<option value="PENDING" {{ request('status') == 'PENDING' ? 'selected' : '' }}>Pending</option>
|
||||
<option value="APPROVED" {{ request('status') == 'APPROVED' ? 'selected' : '' }}>Approved</option>
|
||||
<option value="REJECTED" {{ request('status') == 'REJECTED' ? 'selected' : '' }}>Rejected</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button type="submit" class="btn text-white w-100" style="background-color: #5c4df0;">
|
||||
<i class="bi bi-funnel me-1"></i> Filter
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-custom table-hover mb-0 align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Employee</th>
|
||||
<th>Leave Type</th>
|
||||
<th>Duration</th>
|
||||
<th>Reason</th>
|
||||
<th>Status</th>
|
||||
<th class="text-end">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($leaveRequests as $request)
|
||||
<tr>
|
||||
<td>
|
||||
<div class="fw-bold text-dark">{{ $request->staff->name ?? 'Unknown Staff' }}</div>
|
||||
<div class="text-secondary small">Bal: {{ $request->staff->annual_leave_balance ?? 0 }} Days</div>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge bg-secondary bg-opacity-10 text-dark border">{{ $request->leave_type }}</span>
|
||||
<div class="fw-bold mt-1 text-dark">{{ $request->total_days }} Days</div>
|
||||
</td>
|
||||
<td class="small text-secondary">
|
||||
<div><i class="bi bi-calendar-event me-1"></i> {{ $request->start_date->format('M d, Y') }}</div>
|
||||
<div><i class="bi bi-calendar-check me-1"></i> {{ $request->end_date->format('M d, Y') }}</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="text-truncate small text-secondary" style="max-width: 250px;" title="{{ $request->reason }}">
|
||||
{{ $request->reason }}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
@if($request->status == 'APPROVED')
|
||||
<span class="badge bg-success bg-opacity-10 text-success border border-success-subtle px-2 py-1">Approved</span>
|
||||
@elseif($request->status == 'REJECTED')
|
||||
<span class="badge bg-danger bg-opacity-10 text-danger border border-danger-subtle px-2 py-1">Rejected</span>
|
||||
@else
|
||||
<span class="badge bg-warning bg-opacity-10 text-warning border border-warning-subtle px-2 py-1">Pending</span>
|
||||
@endif
|
||||
|
||||
@if($request->admin_remarks)
|
||||
<i class="bi bi-chat-text text-secondary ms-1" data-bs-toggle="tooltip" title="Notes: {{ $request->admin_remarks }}"></i>
|
||||
@endif
|
||||
</td>
|
||||
<td class="text-end">
|
||||
@if($request->status == 'PENDING')
|
||||
<button class="btn btn-sm btn-light text-success border-success border-opacity-25 me-1 btn-action-leave"
|
||||
data-id="{{ $request->id }}" data-action="APPROVED" title="Approve">
|
||||
<i class="bi bi-check-lg"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-light text-danger border-danger border-opacity-25 btn-action-leave"
|
||||
data-id="{{ $request->id }}" data-action="REJECTED" title="Reject">
|
||||
<i class="bi bi-x-lg"></i>
|
||||
</button>
|
||||
@else
|
||||
<span class="text-secondary small"><i class="bi bi-lock"></i> Processed</span>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="6" class="text-center py-4 text-secondary">No leave requests found.</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="p-3 border-top">
|
||||
{{ $leaveRequests->links('pagination::bootstrap-5') }}
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
// Initialize tooltips
|
||||
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
|
||||
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
|
||||
return new bootstrap.Tooltip(tooltipTriggerEl)
|
||||
});
|
||||
|
||||
// Handle Approve / Reject actions
|
||||
$('.btn-action-leave').on('click', function() {
|
||||
const requestId = $(this).data('id');
|
||||
const action = $(this).data('action');
|
||||
|
||||
const actionText = action === 'APPROVED' ? 'approve' : 'reject';
|
||||
const confirmColor = action === 'APPROVED' ? '#10b981' : '#ef4444';
|
||||
|
||||
Swal.fire({
|
||||
title: `Are you sure you want to ${actionText} this request?`,
|
||||
input: 'text',
|
||||
inputLabel: 'Add a remark (Optional)',
|
||||
inputPlaceholder: 'e.g., Have a great vacation! / Insufficient coverage...',
|
||||
icon: 'question',
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: confirmColor,
|
||||
cancelButtonColor: '#6c757d',
|
||||
confirmButtonText: `Yes, ${actionText} it!`,
|
||||
showLoaderOnConfirm: true,
|
||||
preConfirm: (remark) => {
|
||||
return $.ajax({
|
||||
url: `{{ url('leave-requests') }}/${requestId}/status`,
|
||||
type: 'POST',
|
||||
data: {
|
||||
_token: '{{ csrf_token() }}',
|
||||
_method: 'PUT',
|
||||
status: action,
|
||||
admin_remarks: remark
|
||||
}
|
||||
}).catch(error => {
|
||||
Swal.showValidationMessage(`Request failed: ${error.responseJSON?.message || 'Server error'}`);
|
||||
});
|
||||
},
|
||||
allowOutsideClick: () => !Swal.isLoading()
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Success!',
|
||||
text: 'Leave request status updated.',
|
||||
confirmButtonColor: '#5c4df0'
|
||||
}).then(() => {
|
||||
location.reload();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
235
resources/views/leave/my_leave.blade.php
Normal file
235
resources/views/leave/my_leave.blade.php
Normal file
@@ -0,0 +1,235 @@
|
||||
@extends('layouts.masterbeta')
|
||||
|
||||
@section('title', 'Click ERP - My Leave')
|
||||
|
||||
@push('styles')
|
||||
<style>
|
||||
.balance-card { background: linear-gradient(135deg, #5c4df0 0%, #4338ca 100%); color: white; border-radius: 12px; }
|
||||
.status-badge { font-size: 0.75rem; padding: 0.35em 0.65em; font-weight: 600; }
|
||||
</style>
|
||||
@endpush
|
||||
|
||||
@section('breadcrumbs')
|
||||
<a href="{{ url('/') }}" class="text-secondary text-decoration-none"><i class="bi bi-house me-2"></i></a>
|
||||
<i class="bi bi-chevron-right text-secondary me-2" style="font-size: 0.8rem;"></i>
|
||||
<span class="fw-bold" style="font-size: 0.95rem;">My Leave</span>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<h4 class="fw-bold mb-1">My Leave Dashboard</h4>
|
||||
<p class="text-secondary mb-0" style="font-size: 0.9rem;">Manage your time off and track request statuses.</p>
|
||||
</div>
|
||||
<button class="btn text-white fw-bold d-flex align-items-center" style="background-color: #5c4df0; border-radius: 8px;" id="btnOpenLeaveModal">
|
||||
<i class="bi bi-calendar-plus me-2"></i> Request Leave
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-4">
|
||||
<div class="content-card balance-card p-4 h-100 d-flex flex-column justify-content-center">
|
||||
<h6 class="text-white-50 text-uppercase fw-bold mb-2" style="letter-spacing: 1px; font-size: 0.8rem;">Annual Leave Balance</h6>
|
||||
<div class="d-flex align-items-baseline">
|
||||
<h1 class="display-4 fw-bold mb-0 me-2">{{ $staff->annual_leave_balance }}</h1>
|
||||
<span class="text-white-50 fs-5">Days</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="content-card p-4 h-100 border-start border-4 border-0" style="border-left-color: #10b981 !important;">
|
||||
<h6 class="text-secondary text-uppercase fw-bold mb-3" style="letter-spacing: 1px; font-size: 0.8rem;">Leave Policy Quick Guide</h6>
|
||||
<ul class="text-secondary mb-0 small" style="line-height: 1.8;">
|
||||
<li><strong>Policy 1</strong></li>
|
||||
<li><strong>Policy 2</strong></li>
|
||||
<li><strong>Policy 3</strong></li>
|
||||
<li><strong><!-- Annual Leave:</strong> Deducted from your balance. Weekends are automatically excluded from the calculation.</li>
|
||||
<li><strong>Sick Leave:</strong> Requires a medical certificate for absences exceeding 2 consecutive days.</li>
|
||||
<li><strong> -->Pending Requests:</strong> Can be canceled before HR approval. Once approved, contact HR to amend.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Leave History Table -->
|
||||
<div class="content-card">
|
||||
<div class="p-3 border-bottom bg-light bg-opacity-50">
|
||||
<h6 class="fw-bold mb-0">My Request History</h6>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-custom table-hover mb-0 align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date Submitted</th>
|
||||
<th>Leave Type</th>
|
||||
<th>Duration</th>
|
||||
<th>Total Days</th>
|
||||
<th>Reason</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($leaveRequests as $request)
|
||||
<tr>
|
||||
<td class="text-secondary small">{{ $request->created_at->format('M d, Y') }}</td>
|
||||
<td class="fw-medium">{{ $request->leave_type }}</td>
|
||||
<td class="small">
|
||||
{{ $request->start_date->format('M d, Y') }} <i class="bi bi-arrow-right mx-1 text-secondary"></i> {{ $request->end_date->format('M d, Y') }}
|
||||
</td>
|
||||
<td class="fw-bold">{{ $request->total_days }}</td>
|
||||
<td class="text-secondary small text-truncate" style="max-width: 200px;">
|
||||
{{ $request->reason }}
|
||||
</td>
|
||||
<td>
|
||||
@if($request->status == 'APPROVED')
|
||||
<span class="badge bg-success bg-opacity-10 text-success border border-success-subtle status-badge">Approved</span>
|
||||
@elseif($request->status == 'REJECTED')
|
||||
<span class="badge bg-danger bg-opacity-10 text-danger border border-danger-subtle status-badge">Rejected</span>
|
||||
@else
|
||||
<span class="badge bg-warning bg-opacity-10 text-warning border border-warning-subtle status-badge">Pending</span>
|
||||
@endif
|
||||
|
||||
@if($request->admin_remarks)
|
||||
<i class="bi bi-info-circle text-secondary ms-2" data-bs-toggle="tooltip" title="HR Note: {{ $request->admin_remarks }}"></i>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="6" class="text-center py-4 text-secondary">You haven't submitted any leave requests yet.</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="p-3 border-top">
|
||||
{{ $leaveRequests->links('pagination::bootstrap-5') }}
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('modals')
|
||||
<!-- REQUEST LEAVE MODAL -->
|
||||
<div class="modal fade" id="leaveModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-light">
|
||||
<h5 class="modal-title fw-bold">Request Time Off</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
|
||||
<form id="leaveForm">
|
||||
@csrf
|
||||
<input type="hidden" name="staff_member_id" value="{{ $staff->id }}">
|
||||
|
||||
<div class="modal-body p-4">
|
||||
<div id="leaveModalAlert"></div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-secondary fw-semibold small">Leave Type *</label>
|
||||
<select name="leave_type" class="form-select" required>
|
||||
<option value="">Select Type...</option>
|
||||
<option value="Annual">Annual Leave</option>
|
||||
<option value="Sick">Sick Leave</option>
|
||||
<option value="Maternity">Maternity/Paternity Leave</option>
|
||||
<option value="Unpaid">Unpaid Leave</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="row g-3 mb-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label text-secondary fw-semibold small">Start Date *</label>
|
||||
<!-- Set min date to today using standard HTML5 -->
|
||||
<input type="date" name="start_date" class="form-control" min="{{ date('Y-m-d') }}" required>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label text-secondary fw-semibold small">End Date *</label>
|
||||
<input type="date" name="end_date" class="form-control" min="{{ date('Y-m-d') }}" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-secondary fw-semibold small">Reason / Notes *</label>
|
||||
<textarea name="reason" class="form-control" rows="3" required placeholder="Briefly explain your request..."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer bg-light">
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="submit" id="btnSubmitLeave" class="btn text-white btn-sm fw-bold px-4" style="background-color: #5c4df0;">Submit Request</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endpush
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
// Initialize Bootstrap tooltips for the HR notes
|
||||
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
|
||||
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
|
||||
return new bootstrap.Tooltip(tooltipTriggerEl)
|
||||
});
|
||||
|
||||
const leaveModal = new bootstrap.Modal(document.getElementById('leaveModal'));
|
||||
const $form = $('#leaveForm');
|
||||
|
||||
$('#btnOpenLeaveModal').on('click', function() {
|
||||
$form[0].reset();
|
||||
$('#leaveModalAlert').html('');
|
||||
leaveModal.show();
|
||||
});
|
||||
|
||||
$form.on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const $btn = $('#btnSubmitLeave');
|
||||
const originalText = $btn.html();
|
||||
const $alertBox = $('#leaveModalAlert');
|
||||
|
||||
$btn.html('<span class="spinner-border spinner-border-sm me-2"></span> Processing...');
|
||||
$btn.prop('disabled', true);
|
||||
$alertBox.html('');
|
||||
|
||||
$.ajax({
|
||||
url: "{{ route('leave.store') }}",
|
||||
type: 'POST',
|
||||
data: $form.serialize(),
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
leaveModal.hide();
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Submitted!',
|
||||
text: response.message,
|
||||
confirmButtonColor: '#5c4df0'
|
||||
}).then(() => {
|
||||
location.reload();
|
||||
});
|
||||
}
|
||||
},
|
||||
error: function(xhr) {
|
||||
let errorMessage = 'An unexpected error occurred.';
|
||||
|
||||
if (xhr.status === 422) {
|
||||
// If it's the insufficient balance custom error or validation error
|
||||
errorMessage = xhr.responseJSON.message || 'Please check the form for errors.';
|
||||
}
|
||||
|
||||
$alertBox.html(`
|
||||
<div class="alert alert-danger d-flex align-items-center py-2 px-3 small" role="alert">
|
||||
<i class="bi bi-exclamation-triangle-fill me-2"></i>
|
||||
<div>${errorMessage}</div>
|
||||
</div>
|
||||
`);
|
||||
},
|
||||
complete: function() {
|
||||
$btn.html(originalText).prop('disabled', false);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
Reference in New Issue
Block a user