180 lines
12 KiB
PHP
180 lines
12 KiB
PHP
@extends('layouts.app')
|
|
|
|
@section('content')
|
|
<div class="space-y-6">
|
|
<!-- Page Header -->
|
|
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
|
|
<h1 class="text-2xl font-bold text-gray-900">{{ __('payroll.payroll') }}</h1>
|
|
<div class="flex gap-2 flex-wrap">
|
|
<a href="{{ route('payroll.calculate') }}" class="inline-flex items-center gap-2 px-4 py-2 bg-teal-600 text-white text-sm font-medium rounded-lg hover:bg-teal-700 transition-colors">
|
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"/></svg>
|
|
{{ __('payroll.new_entry') }}
|
|
</a>
|
|
<a href="{{ route('payroll.settlement') }}" class="inline-flex items-center gap-2 px-4 py-2 bg-amber-600 text-white text-sm font-medium rounded-lg hover:bg-amber-700 transition-colors">
|
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/></svg>
|
|
{{ __('payroll.final_settlement') }}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Statistics Cards -->
|
|
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
|
|
<div class="bg-white rounded-xl border border-gray-200 p-4">
|
|
<div class="text-xs font-medium text-gray-500 mb-1">{{ __('payroll.total_gross') }}</div>
|
|
<div class="text-lg font-bold text-gray-900">{{ format_currency($totalGross) }}</div>
|
|
</div>
|
|
<div class="bg-white rounded-xl border border-gray-200 p-4">
|
|
<div class="text-xs font-medium text-gray-500 mb-1">{{ __('payroll.total_deductions') }}</div>
|
|
<div class="text-lg font-bold text-red-600">{{ format_currency($totalDeductions + $totalAdvances) }}</div>
|
|
</div>
|
|
<div class="bg-white rounded-xl border border-gray-200 p-4">
|
|
<div class="text-xs font-medium text-gray-500 mb-1">{{ __('payroll.total_net') }}</div>
|
|
<div class="text-lg font-bold text-teal-600">{{ format_currency($totalNet) }}</div>
|
|
</div>
|
|
<div class="bg-white rounded-xl border border-gray-200 p-4">
|
|
<div class="text-xs font-medium text-gray-500 mb-1">{{ __('payroll.unpaid_employees') }}</div>
|
|
<div class="text-lg font-bold text-amber-600">{{ $unpaidEmployeeCount }} / {{ $activeEmployeeCount }}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Paid/Unpaid Progress -->
|
|
<div class="bg-white rounded-xl border border-gray-200 p-4">
|
|
<div class="flex items-center justify-between mb-2">
|
|
<span class="text-sm font-medium text-gray-700">{{ __('payroll.payment_status') }}</span>
|
|
<span class="text-sm text-gray-500">{{ __('payroll.total_paid') }}: {{ format_currency($totalPaid) }} | {{ __('payroll.total_unpaid') }}: {{ format_currency($totalUnpaid) }}</span>
|
|
</div>
|
|
@php $paidPercent = $totalGross > 0 ? round(($totalPaid / $totalGross) * 100) : 0; @endphp
|
|
<div class="w-full bg-gray-200 rounded-full h-3">
|
|
<div class="bg-teal-500 h-3 rounded-full transition-all" style="width: {{ $paidPercent }}%"></div>
|
|
</div>
|
|
<div class="text-xs text-gray-500 mt-1">{{ $paidPercent }}% {{ __('payroll.paid_percent') }}</div>
|
|
</div>
|
|
|
|
<!-- Month/Year Filter -->
|
|
<div class="bg-white rounded-xl border border-gray-200 p-4">
|
|
<form method="GET" action="{{ route('payroll.index') }}" class="flex flex-wrap gap-3 items-end">
|
|
<div class="w-44">
|
|
<label class="block text-xs font-medium text-gray-500 mb-1">{{ __('payroll.month') }}</label>
|
|
<select name="month" class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-teal-500">
|
|
@foreach($months as $m)
|
|
<option value="{{ $m }}" {{ $month == $m ? 'selected' : '' }}>{{ $persianMonths[$m] ?? $m }}</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
<div class="w-32">
|
|
<label class="block text-xs font-medium text-gray-500 mb-1">{{ __('payroll.year') }}</label>
|
|
<select name="year" class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-teal-500">
|
|
@foreach($years as $y)
|
|
<option value="{{ $y }}" {{ $year == $y ? 'selected' : '' }}>{{ $y }}</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
<button type="submit" class="px-4 py-2 bg-gray-100 text-gray-700 text-sm font-medium rounded-lg hover:bg-gray-200 transition-colors">{{ __('common.filter') }}</button>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- Bulk Pay Form -->
|
|
<form method="POST" action="{{ route('payroll.pay-bulk') }}" id="bulkPayForm">
|
|
@csrf
|
|
|
|
<!-- Financial Records Table -->
|
|
<div class="bg-white rounded-xl border border-gray-200 overflow-hidden">
|
|
@if($financials->count() > 0)
|
|
<div class="px-5 py-3 border-b border-gray-200 bg-gray-50 flex items-center justify-between">
|
|
<span class="text-sm font-medium text-gray-700">{{ __('payroll.financial_records') }} — {{ $persianMonths[$month] ?? $month }} {{ $year }}</span>
|
|
<button type="submit" class="px-3 py-1.5 bg-teal-600 text-white text-xs font-medium rounded-lg hover:bg-teal-700 transition-colors" onclick="return confirm('{{ __('payroll.confirm_bulk_pay') }}')">
|
|
{{ __('payroll.pay_bulk') }}
|
|
</button>
|
|
</div>
|
|
@endif
|
|
<div class="overflow-x-auto">
|
|
<table class="w-full">
|
|
<thead class="bg-gray-50 border-b border-gray-200">
|
|
<tr>
|
|
<th class="px-3 py-3 text-start text-xs font-medium text-gray-500 w-10">
|
|
<input type="checkbox" id="selectAll" class="rounded border-gray-300 text-teal-600 focus:ring-teal-500">
|
|
</th>
|
|
<th class="px-5 py-3 text-start text-xs font-medium text-gray-500">{{ __('payroll.employee') }}</th>
|
|
<th class="px-5 py-3 text-start text-xs font-medium text-gray-500">{{ __('payroll.type') }}</th>
|
|
<th class="px-5 py-3 text-start text-xs font-medium text-gray-500">{{ __('payroll.amount') }}</th>
|
|
<th class="px-5 py-3 text-start text-xs font-medium text-gray-500">{{ __('payroll.effective_date') }}</th>
|
|
<th class="px-5 py-3 text-start text-xs font-medium text-gray-500">{{ __('common.status') }}</th>
|
|
<th class="px-5 py-3 text-start text-xs font-medium text-gray-500">{{ __('common.description') }}</th>
|
|
<th class="px-5 py-3 text-start text-xs font-medium text-gray-500"></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="divide-y divide-gray-100">
|
|
@forelse($financials as $financial)
|
|
<tr class="hover:bg-gray-50 transition-colors">
|
|
<td class="px-3 py-3">
|
|
@if(!$financial->is_paid)
|
|
<input type="checkbox" name="financial_ids[]" value="{{ $financial->id }}" class="financial-checkbox rounded border-gray-300 text-teal-600 focus:ring-teal-500">
|
|
@endif
|
|
</td>
|
|
<td class="px-5 py-3">
|
|
<a href="{{ route('payroll.show', $financial->employee_id) }}" class="text-sm font-medium text-teal-700 hover:text-teal-900 hover:underline">
|
|
{{ $financial->employee->first_name ?? '' }} {{ $financial->employee->last_name ?? '' }}
|
|
</a>
|
|
</td>
|
|
<td class="px-5 py-3">
|
|
<span class="inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium
|
|
{{ $financial->type === 'salary' ? 'bg-teal-50 text-teal-700' :
|
|
($financial->type === 'overtime' ? 'bg-blue-50 text-blue-700' :
|
|
($financial->type === 'bonus' ? 'bg-emerald-50 text-emerald-700' :
|
|
($financial->type === 'allowance' ? 'bg-cyan-50 text-cyan-700' :
|
|
($financial->type === 'deduction' ? 'bg-red-50 text-red-700' :
|
|
'bg-amber-50 text-amber-700')))) }}">
|
|
{{ __('payroll.type_' . $financial->type) }}
|
|
</span>
|
|
</td>
|
|
<td class="px-5 py-3 text-sm font-medium {{ in_array($financial->type, ['deduction', 'advance']) ? 'text-red-600' : 'text-gray-900' }}">
|
|
{{ in_array($financial->type, ['deduction', 'advance']) ? '-' : '' }}{{ number_format($financial->amount) }}
|
|
</td>
|
|
<td class="px-5 py-3 text-sm text-gray-500">{{ $financial->effective_date ? calendar_date($financial->effective_date) : '-' }}</td>
|
|
<td class="px-5 py-3">
|
|
@if($financial->is_paid)
|
|
<span class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium bg-green-50 text-green-700">
|
|
<svg class="w-3 h-3" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/></svg>
|
|
{{ __('payroll.paid') }}
|
|
</span>
|
|
@else
|
|
<span class="inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium bg-amber-50 text-amber-700">
|
|
{{ __('payroll.unpaid') }}
|
|
</span>
|
|
@endif
|
|
</td>
|
|
<td class="px-5 py-3 text-sm text-gray-500 max-w-[200px] truncate">{{ $financial->description ?? '-' }}</td>
|
|
<td class="px-5 py-3">
|
|
<div class="flex items-center gap-1">
|
|
@if(!$financial->is_paid)
|
|
<a href="{{ route('payroll.pay', $financial) }}" class="p-1.5 text-green-500 hover:text-green-700 hover:bg-green-50 rounded-lg transition-colors" title="{{ __('payroll.pay') }}" onclick="return confirm('{{ __('payroll.confirm_pay') }}')">
|
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 9V7a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2m2 4h10a2 2 0 002-2v-6a2 2 0 00-2-2H9a2 2 0 00-2 2v6a2 2 0 002 2zm7-5a2 2 0 11-4 0 2 2 0 014 0z"/></svg>
|
|
</a>
|
|
@endif
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
@empty
|
|
<tr>
|
|
<td colspan="8" class="text-center py-12 text-gray-500">{{ __('payroll.no_records') }}</td>
|
|
</tr>
|
|
@endforelse
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
|
|
<!-- Pagination -->
|
|
<div class="flex justify-center">
|
|
{{ $financials->withQueryString()->links() }}
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
document.getElementById('selectAll')?.addEventListener('change', function() {
|
|
document.querySelectorAll('.financial-checkbox').forEach(cb => cb.checked = this.checked);
|
|
});
|
|
</script>
|
|
@endsection
|