@extends('layouts.adminlte') @section('title', 'Dashboard') @section('page_title', 'Dashboard') @push('styles') @endpush @push('scripts') @endpush @section('content') @php $hideOnboardingForm = true; @endphp @if ($builderMode)
@if (!empty($profileEdit?->id)) Preview Tab Baru @else @endif
Trash Widget (drag widget ke sini untuk nonaktif)
0
Drop widget di area ini
@endif
@if (!$hideOnboardingForm) @if (!($onboardingState['is_completed'] ?? false))
Setup awal belum selesai.
@endif @endif Guide Setup @if (!$hideOnboardingForm) @endif

{{ number_format($usersCount) }}

Users

{{ number_format($rolesCount) }}

Roles

{{ number_format($menusCount) }}

Menus ({{ $activeMenusCount }} active)

{{ number_format($permissionsCount) }}

Permissions

@if ($canSeeLoginReset ?? false)

{{ number_format((int) ($pendingLoginResetCount ?? 0)) }}

Pending Login Reset

@endif

Range Date

@if ($builderMode)
@endif
@foreach (request()->except(['range_from', 'range_to']) as $k => $v) @if (is_scalar($v)) @endif @endforeach
Grafik dan total mengikuti range ini.

Karyawan Perlu Kontrak Baru

@if ($builderMode)
@endif
@php $cr = $contractRenewal ?? []; $daysTh = (int) ($cr['days_threshold'] ?? 30); $expired = (int) ($cr['expired'] ?? 0); $soon = (int) ($cr['expiring_soon'] ?? 0); $total = (int) ($cr['total_need_action'] ?? 0); $top = is_array($cr['top_expiring'] ?? null) ? $cr['top_expiring'] : []; @endphp @if (!empty($top))
Akan habis (top)
@foreach ($top as $r) @endforeach
Karyawan End Sisa
{{ $r['employee_code'] ?? '-' }} - {{ $r['full_name'] ?? '-' }} {{ $r['contract_end_date'] ?? '-' }} {{ isset($r['days_left']) ? ($r['days_left'] . ' hari') : '-' }}
@endif
Kriteria: karyawan aktif dengan contract_end_date sudah lewat (expired) atau akan habis dalam {{ $daysTh }} hari.

Perbandingan Bulanan Request (6 Bulan)

@if ($builderMode)
@endif
@php $mLabels = is_array($monthlyCompare['labels'] ?? null) ? $monthlyCompare['labels'] : []; $mLembur = is_array($monthlyCompare['lembur'] ?? null) ? $monthlyCompare['lembur'] : []; $mFc = is_array($monthlyCompare['forgetting_clock'] ?? null) ? $monthlyCompare['forgetting_clock'] : []; $mLeave = is_array($monthlyCompare['leave'] ?? null) ? $monthlyCompare['leave'] : []; $mMax = 0; foreach ($mLabels as $i => $_) { $t = (int) ($mLembur[$i] ?? 0) + (int) ($mFc[$i] ?? 0) + (int) ($mLeave[$i] ?? 0); $mMax = max($mMax, $t); } if ($mMax <= 0) $mMax = 1; @endphp
Lembur FC Leave
@foreach ($mLabels as $i => $lab) @php $l = (int) ($mLembur[$i] ?? 0); $f = (int) ($mFc[$i] ?? 0); $lv = (int) ($mLeave[$i] ?? 0); $t = $l + $f + $lv; $lp = max(0, min(100, round(($l / $mMax) * 100, 2))); $fp = max(0, min(100, round(($f / $mMax) * 100, 2))); $lvp = max(0, min(100, round(($lv / $mMax) * 100, 2))); $barPayload = ['kind' => 'monthly_requests', 'month' => $lab, 'lembur' => $l, 'forgetting_clock' => $f, 'leave' => $lv, 'max_total' => $mMax]; @endphp
{{ $t > 0 ? number_format($t) : '' }}
@endforeach
{{ $mLabels[0] ?? '-' }} Maks: {{ $mMax }} {{ $mLabels[count($mLabels) - 1] ?? '-' }}

Karyawan Bulan Ini vs Bulan Lalu

@if ($builderMode)
@endif
@php $cur = $profilKaryawanCompare['current'] ?? ['code' => '', 'count' => 0]; $prev = $profilKaryawanCompare['previous'] ?? ['code' => '', 'count' => 0]; $delta = (int) ($profilKaryawanCompare['delta'] ?? 0); $hireThis = (int) ($newHires['this_month'] ?? 0); $hirePrev = (int) ($newHires['prev_month'] ?? 0); $hireDelta = (int) ($newHires['delta'] ?? 0); @endphp
Profil Bulan
{{ $cur['code'] ?: 'Current' }}
{{ number_format((int) ($cur['count'] ?? 0)) }}
{{ $prev['code'] ?: 'Previous' }}
{{ number_format((int) ($prev['count'] ?? 0)) }}
Delta: {{ $delta >= 0 ? '+' : '' }}{{ $delta }}

Tambahan Karyawan (Hire Date)
Bulan ini
{{ number_format($hireThis) }}
Bulan lalu
{{ number_format($hirePrev) }}
Delta: {{ $hireDelta >= 0 ? '+' : '' }}{{ $hireDelta }}

Perbandingan Bulan Ini vs Bulan Lalu

@if ($builderMode)
@endif
@php $mc = $monthCompare ?? []; $mThis = $mc['metrics']['this'] ?? null; $mPrev = $mc['metrics']['prev'] ?? null; $mm = $mc['metrics'] ?? []; @endphp
@foreach (request()->except(['compare_from', 'compare_to']) as $k => $v) @if (is_scalar($v)) @endif @endforeach
Bulan lalu otomatis: periode yang sama -1 bulan.
Periode: {{ $mc['this']['from'] ?? '-' }} s/d {{ $mc['this']['to'] ?? '-' }} dibanding {{ $mc['prev']['from'] ?? '-' }} s/d {{ $mc['prev']['to'] ?? '-' }}
@php $rows = [ ['key' => 'telat', 'label' => 'Keterlambatan (count)'], ['key' => 'hadir', 'label' => 'Kehadiran (count)'], ['key' => 'alpha', 'label' => 'Alpha (count)'], ['key' => 'cuti', 'label' => 'Cuti (request count)'], ['key' => 'hours', 'label' => 'Total Jam'], ['key' => 'salary_net', 'label' => 'Total Salary Net'], ]; @endphp @foreach ($rows as $r) @php $k = $r['key']; $thisVal = $mm[$k]['this'] ?? 0; $prevVal = $mm[$k]['prev'] ?? 0; $deltaVal = (is_numeric($thisVal) && is_numeric($prevVal)) ? ($thisVal - $prevVal) : 0; $isMoney = $k === 'salary_net'; $fmt = function ($v) use ($isMoney) { if ($isMoney) return 'Rp ' . number_format((float) $v, 0, ',', '.'); if (is_float($v) || (is_numeric($v) && str_contains((string) $v, '.'))) return number_format((float) $v, 2); return number_format((int) $v); }; @endphp @endforeach
Metric Bulan Ini Bulan Lalu Delta
{{ $r['label'] }} {{ $fmt($thisVal) }} {{ $fmt($prevVal) }} {{ $deltaVal >= 0 ? '+' : '' }}{{ $isMoney ? ('Rp ' . number_format((float) $deltaVal, 0, ',', '.')) : (is_float($deltaVal) ? number_format((float) $deltaVal, 2) : (int) $deltaVal) }}

Grafik Keterlambatan

@if ($builderMode)
@endif
@php $dates = is_array($dashSeries['dates'] ?? null) ? $dashSeries['dates'] : []; $telat = is_array($dashSeries['telat'] ?? null) ? $dashSeries['telat'] : []; $maxTelat = 0; foreach ($telat as $v) { $maxTelat = max($maxTelat, (int) $v); } if ($maxTelat <= 0) $maxTelat = 1; @endphp
@foreach ($dates as $idx => $d) @php $val = (int) ($telat[$idx] ?? 0); $pct = max(0, min(100, round(($val / $maxTelat) * 100, 2))); $label = is_string($d) && strlen($d) >= 10 ? substr($d, 8, 2) : $d; $barPayload = ['kind' => 'telat', 'date' => $d, 'telat' => $val, 'max' => $maxTelat]; @endphp
{{ $val > 0 ? number_format($val) : '' }}
@endforeach
{{ $dates[0] ?? '-' }} Maks: {{ $maxTelat }} {{ $dates[count($dates) - 1] ?? '-' }}

Grafik Kehadiran, Alpha, Cuti

@if ($builderMode)
@endif
@php $dates = is_array($dashSeries['dates'] ?? null) ? $dashSeries['dates'] : []; $areaPayload = is_array($dashAreaSeries ?? null) ? $dashAreaSeries : []; $workdayCount = (int) ($areaPayload['workday_count'] ?? 0); $weekendCount = (int) ($areaPayload['weekend_count'] ?? 0); $holidayCount = (int) ($areaPayload['holiday_count'] ?? 0); $holidaySpecialCount = (int) ($areaPayload['holiday_special_count'] ?? 0); @endphp
Per Tanggal
Mode pagi menghitung check-in saja (tidak menunggu checkout), cocok untuk monitoring realtime.
{{ $dates[0] ?? '-' }} {{ $dates[count($dates) - 1] ?? '-' }}

Klik bar pada tanggal di grafik untuk menampilkan detail per-area.
Workday per Area (Alpha, Cuti, Sakit, Hadir, Telat)
Off-day per Area: Kehadiran dianggap Lembur
Catatan: weekend/holiday tidak dihitung sebagai alpha/cuti/sakit. Jika ada kehadiran di off-day, itu masuk lembur.
@foreach ((array) ($areaPayload['areas'] ?? []) as $r) @php $w = is_array($r['workday'] ?? null) ? $r['workday'] : []; $we = is_array($r['weekend'] ?? null) ? $r['weekend'] : []; $h = is_array($r['holiday'] ?? null) ? $r['holiday'] : []; $hs = is_array($r['holiday_special'] ?? null) ? $r['holiday_special'] : []; @endphp @endforeach
Area Karyawan Shift (Planned) Alpha (WD) Cuti (WD) Sakit (WD) Hadir Shift (WD) Telat Precheck (WD) Telat Grace (WD) Hadir NonShift (WD) Lembur Weekend Telat Weekend (P/G) Lembur Holiday Telat Holiday (P/G) Lembur Holiday Special Telat H-Special (P/G)
{{ $r['label'] ?? '-' }} {{ number_format((int) ($r['employees'] ?? 0)) }} {{ number_format((int) ($w['shift_planned'] ?? 0)) }} {{ number_format((int) ($w['alpha'] ?? 0)) }} {{ number_format((int) ($w['cuti'] ?? 0)) }} {{ number_format((int) ($w['sick'] ?? 0)) }} {{ number_format((int) ($w['hadir_shift'] ?? 0)) }} {{ number_format((int) ($w['late_precheck'] ?? 0)) }} {{ number_format((int) ($w['late_grace'] ?? 0)) }} {{ number_format((int) ($w['hadir_nonshift'] ?? 0)) }} {{ number_format((int) ($we['hadir'] ?? 0)) }} {{ number_format((int) ($we['late_precheck'] ?? 0)) }}/{{ number_format((int) ($we['late_grace'] ?? 0)) }} {{ number_format((int) ($h['hadir'] ?? 0)) }} {{ number_format((int) ($h['late_precheck'] ?? 0)) }}/{{ number_format((int) ($h['late_grace'] ?? 0)) }} {{ number_format((int) ($hs['hadir'] ?? 0)) }} {{ number_format((int) ($hs['late_precheck'] ?? 0)) }}/{{ number_format((int) ($hs['late_grace'] ?? 0)) }}
Level 1: Karyawan per Area / Group Cetak
Klik bar untuk memuat data.
Area Kerja Client Sub Area Client Group Cetak Jabatan ID Finger Nama Karyawan Status
Klik salah satu bar tanggal untuk melihat detail karyawan.

Total Jam

@if ($builderMode)
@endif
@php $hours = is_array($dashSeries['hours'] ?? null) ? $dashSeries['hours'] : []; $maxHours = 0.0; foreach ($hours as $v) { $maxHours = max($maxHours, (float) $v); } if ($maxHours <= 0) $maxHours = 1.0; @endphp
{{ number_format((float) ($dashTotals['hours'] ?? 0), 2) }}
jam
@foreach ($hours as $idx => $v) @php $val = (float) $v; $pct = max(0, min(100, round(($val / $maxHours) * 100, 2))); $barPayload = ['kind' => 'hours', 'date' => $dates[$idx] ?? null, 'hours' => $val, 'max' => $maxHours]; @endphp
{{ $val > 0 ? number_format($val, 2) : '' }}
@endforeach

Total Salary (Net)

@if ($builderMode)
@endif
@php $salary = is_array($dashSeries['salary'] ?? null) ? $dashSeries['salary'] : []; $maxSalary = 0.0; foreach ($salary as $v) { $maxSalary = max($maxSalary, (float) $v); } if ($maxSalary <= 0) $maxSalary = 1.0; @endphp
Rp {{ number_format((float) ($dashTotals['salary_net'] ?? 0), 0, ',', '.') }}
@php $expenseEstimateNet = max(0.0, (float) ($payrollExpenseWidget['estimate_net'] ?? 0)); $expensePaidNet = max(0.0, (float) ($payrollExpenseWidget['period_net'] ?? 0)); $expenseUnpaidNet = max(0.0, (float) ($payrollExpenseWidget['unpaid_net'] ?? max(0, $expenseEstimateNet - $expensePaidNet))); $expenseTotalForChart = max(1.0, $expensePaidNet + $expenseUnpaidNet, $expenseEstimateNet); $expensePaidPct = min(100, round(($expensePaidNet / $expenseTotalForChart) * 100, 2)); $expenseUnpaidPct = min(100, round(($expenseUnpaidNet / $expenseTotalForChart) * 100, 2)); $expenseSource = (string) ($payrollExpenseWidget['estimate_source'] ?? 'paid_only'); $expenseDailyBudget = is_array($payrollExpenseWidget['daily_budget'] ?? null) ? $payrollExpenseWidget['daily_budget'] : []; $expenseDailyMax = 1.0; foreach ($expenseDailyBudget as $dayBudget) { $expenseDailyMax = max($expenseDailyMax, (float) ($dayBudget['net'] ?? 0)); } $expenseActualRunRate = (float) ($payrollExpenseWidget['daily_actual_total'] ?? 0); $expenseEstimateRunRate = (float) ($payrollExpenseWidget['daily_estimate_total'] ?? 0); $expenseOvertimeSaving = (float) ($payrollExpenseWidget['daily_overtime_removed_total'] ?? 0); $expensePlanningNet = !empty($expenseDailyBudget) ? ($expenseActualRunRate + $expenseEstimateRunRate) : $expenseEstimateNet; $expenseTodayBudget = []; foreach ($expenseDailyBudget as $dayBudget) { if (!empty($dayBudget['is_today'])) { $expenseTodayBudget = $dayBudget; break; } } @endphp

Pengeluaran Payroll

{{ $payrollExpenseWidget['profil_code'] ?? '-' }} @if (!empty($payrollExpenseWidget['period_start']) && !empty($payrollExpenseWidget['period_end'])) | {{ $payrollExpenseWidget['period_start'] }} s/d {{ $payrollExpenseWidget['period_end'] }} @endif
@if (!empty($payrollProfilOptions))
@foreach (request()->except('payroll_profil_id') as $key => $value) @if (is_scalar($value)) @endif @endforeach
@endif
@if ($builderMode)
@else @if ($expenseSource === 'payroll_snapshot') Snapshot payroll @elseif ($expenseSource === 'profil_gaji_fast_estimate') Estimasi profil gaji @else Paid only @endif @endif
Budget planning periode berjalan
Rp {{ number_format($expensePlanningNet, 0, ',', '.') }}
Actual H-1 + estimasi H-0 sampai akhir periode tanpa lembur.
Actual H-1
Rp {{ number_format($expenseActualRunRate, 0, ',', '.') }}
H-0 dst tanpa lembur
Rp {{ number_format($expenseEstimateRunRate, 0, ',', '.') }}
Paid Rp {{ number_format($expensePaidNet, 0, ',', '.') }} Belum Rp {{ number_format($expenseUnpaidNet, 0, ',', '.') }}
Estimate bruto
Rp {{ number_format((float) ($payrollExpenseWidget['estimate_gross'] ?? 0), 0, ',', '.') }}
Estimate reimburse
Rp {{ number_format((float) ($payrollExpenseWidget['estimate_reimburse'] ?? 0), 0, ',', '.') }}
Estimate PPh21
Rp {{ number_format((float) ($payrollExpenseWidget['estimate_pph21'] ?? 0), 0, ',', '.') }}
Estimate BPJS
Rp {{ number_format((float) ($payrollExpenseWidget['estimate_bpjs'] ?? 0), 0, ',', '.') }}
Budget Hari Ini
{{ $payrollExpenseWidget['today_date'] ?? now()->format('Y-m-d') }}
Rp {{ number_format((float) ($expenseTodayBudget['net'] ?? $payrollExpenseWidget['today_net'] ?? 0), 0, ',', '.') }}
H-0 estimasi tanpa lembur untuk {{ number_format((int) ($expenseTodayBudget['employee_count'] ?? 0), 0, ',', '.') }} karyawan
Bruto Rp {{ number_format((float) ($expenseTodayBudget['gross'] ?? $payrollExpenseWidget['today_gross'] ?? 0), 0, ',', '.') }}
PPh21 Rp {{ number_format((float) ($expenseTodayBudget['pph21'] ?? $payrollExpenseWidget['today_pph21'] ?? 0), 0, ',', '.') }}
BPJS Rp {{ number_format((float) ($expenseTodayBudget['bpjs'] ?? $payrollExpenseWidget['today_bpjs'] ?? 0), 0, ',', '.') }}
Lembur dikeluarkan Rp {{ number_format((float) ($expenseTodayBudget['overtime_removed'] ?? 0), 0, ',', '.') }}
Estimasi Bayar Harian Satu Periode
Biru = actual s/d H-1, kuning = H-0 dan ke depan tanpa lembur.
Potensi tekan OT Rp {{ number_format($expenseOvertimeSaving, 0, ',', '.') }}
@if (!empty($expenseDailyBudget))
@foreach ($expenseDailyBudget as $dayBudget) @php $dayNet = max(0.0, (float) ($dayBudget['net'] ?? 0)); $dayHeight = min(100, max(0, round(($dayNet / $expenseDailyMax) * 100, 2))); $dayMode = (string) ($dayBudget['mode'] ?? 'estimate_no_overtime'); $dayColor = $dayMode === 'actual' ? '#67e8f9' : '#fbbf24'; $payload = array_merge([ 'kind' => 'payroll_daily_budget', 'profil_bulan_id' => (int) ($payrollExpenseWidget['profil_id'] ?? 0), ], $dayBudget); @endphp
{{ $dayBudget['day'] ?? '' }}
@endforeach
@else
Belum ada data payroll/estimasi untuk membuat grafik harian. Pastikan Profil Bulan punya Profil Gaji aktif, atau buka menu Penggajian lalu klik Tampilkan agar snapshot final periode terbentuk.
@endif
Sumber estimate: @if ($expenseSource === 'payroll_snapshot') snapshot payroll terakhir. @elseif ($expenseSource === 'profil_gaji_fast_estimate') estimasi cepat dari Profil Gaji aktif, belum memasukkan hasil attendance final/lembur real-time. @else data paid saja, snapshot belum tersedia. @endif @if (!empty($payrollExpenseWidget['estimate_generated_at'])) Generate: {{ $payrollExpenseWidget['estimate_generated_at'] }}. @endif Grafik planning memakai rata-rata hari kerja efektif: H-1 sudah mengikuti snapshot aktual, H-0/H+1 dikurangi komponen lembur.

Department Distribution

@if ($builderMode)
@endif
@php $deptItems = collect($departmentDistribution ?? [])->values(); $deptTop = $deptItems->take(8); $deptMax = (int) ($deptItems->max('total') ?? 0); if ($deptMax <= 0) $deptMax = 1; @endphp
Top Department (Users)
@forelse ($deptTop as $item) @php $val = (int) ($item->total ?? 0); $pct = max(0, min(100, round(($val / $deptMax) * 100, 2))); @endphp
{{ $item->department_label }}
{{ number_format($val) }}
@empty
No data
@endforelse
@forelse ($departmentDistribution as $item) @empty @endforelse
Department Users
{{ $item->department_label }} {{ number_format($item->total) }}
No data

Role Access Summary

@if ($builderMode)
@endif
@php $roleItems = collect($roleAccess ?? [])->values(); $roleTop = $roleItems->take(8); $roleMax = (int) ($roleItems->max('users_count') ?? 0); if ($roleMax <= 0) $roleMax = 1; @endphp
Top Role (Users)
@forelse ($roleTop as $role) @php $val = (int) ($role->users_count ?? 0); $pct = max(0, min(100, round(($val / $roleMax) * 100, 2))); @endphp
{{ $role->name }}
{{ number_format($val) }}
@empty
No roles
@endforelse
@forelse ($roleAccess as $role) @empty @endforelse
Role Users Permissions
{{ $role->name }} {{ number_format($role->users_count) }} {{ number_format($role->permissions_count) }}
No roles

Recent Users

@if ($builderMode)
@endif
@forelse ($recentUsers as $user) @empty @endforelse
Name Email Department Roles
{{ $user->name }} {{ $user->email }} {{ $user->department ?: '-' }} {{ $user->roles->pluck('name')->implode(', ') ?: '-' }}
No users

Access Health

@if ($builderMode)
@endif
Mapped Menus {{ $mappedMenusCount }} / {{ $menusCount }}
@php $mappedPercent = $menusCount > 0 ? (int) round(($mappedMenusCount / $menusCount) * 100) : 0; @endphp
{{ $mappedPercent }}%
{{ $menusWithoutPermission > 0 ? $menusWithoutPermission . ' menus have no permissions mapped yet.' : 'All menus already have mapped permissions.' }}
@if ($builderMode) @endif

Menu Management (Module per Department)

@forelse ($menuModules as $menu) @empty @endforelse
Department Module Status
{{ $menu->department ?: '-' }} {{ $menu->name }} {{ $menu->is_active ? 'Active' : 'Inactive' }}
No modules
@if ($moduleLibraries->isNotEmpty())
@if ($builderMode) @endif

Module Library (Auto by Department)

Department: {{ $authUser?->department ?: 'All' }}
@foreach ($moduleLibraries as $library)
{{ $library->name }} @if ($library->department) {{ $library->department }} @endif
@includeIf($library->view_path, ['authUser' => $authUser, 'moduleLibrary' => $library])
@endforeach
@endif
@if ($builderMode) @endif @if ($builderMode) @endif @if (!$hideOnboardingForm) @endif @endsection @push('scripts') @endpush @push('scripts') @endpush @push('scripts') @endpush