Roles HRD, SPV, MNGR, STAFF

This commit is contained in:
jouel88 2026-03-20 14:38:59 +07:00
parent d6c09fe1ae
commit 99dfe31f64
13 changed files with 332 additions and 339 deletions

View File

@ -19,7 +19,7 @@ public function index(Request $request)
{
$query = User::with(['divisi', 'jabatan', 'kantor', 'roles'])
->whereDoesntHave('roles', function ($q) {
$q->where('nama_role', 'admin');
$q->where('nama_role', 'hrd');
});
if ($request->filled('filter_jabatan')) {
@ -49,7 +49,7 @@ public function index(Request $request)
$allKantor = Kantor::withCount([
'users as total_pegawai' => function ($q) {
$q->whereDoesntHave('roles', function ($sq) {
$sq->where('nama_role', 'admin');
$sq->where('nama_role', 'hrd');
});
}
])->get();
@ -57,11 +57,11 @@ public function index(Request $request)
$stats = [
'kantor_list' => $allKantor,
'total' => User::whereDoesntHave('roles', function ($q) {
$q->where('nama_role', 'admin');
$q->where('nama_role', 'hrd');
})->count(),
'active' => User::where('status_aktif', 1)
->whereDoesntHave('roles', function ($q) {
$q->where('nama_role', 'admin');
$q->where('nama_role', 'hrd');
})->count(),
];

View File

@ -26,7 +26,7 @@ public function index()
public function create()
{
$pegawai = User::with('kantor')->whereDoesntHave('roles', function ($q) {
$q->where('nama_role', 'admin')->orWhere('nama_role', 'Admin');
$q->where('nama_role', 'hrd');
})->get();
$kantor = \App\Models\Kantor::orderBy('nama_kantor', 'asc')->get();

View File

@ -12,7 +12,7 @@ public function authorize(): bool
{
return $this->user()->roles->contains(function ($role) {
return in_array(strtolower($role->nama_role), ['admin', 'super admin', 'super_admin']);
return strtolower($role->nama_role) === 'hrd';
});
}

View File

@ -111,14 +111,14 @@ public function hasPermission($permissionSlug): bool
public function isGlobalAdmin(): bool
{
return $this->roles->contains(function ($role) {
return in_array(strtolower($role->nama_role), ['admin', 'super admin', 'super_admin']);
return strtolower($role->nama_role) === 'hrd';
});
}
public function isSuperAdmin(): bool
{
return $this->roles->contains(function ($role) {
return in_array(strtolower($role->nama_role), ['super admin', 'super_admin']);
return strtolower($role->nama_role) === 'hrd';
});
}

View File

@ -54,6 +54,8 @@ public function enrollFace($userId, array $photos)
->where('id', $userId)
->update(['is_face_registered' => 1]);
$trainingSuccess = false;
try {
$pythonPath = env('PYTHON_PATH', 'python');
$scriptPath = base_path('python_scripts/train_face.py');
@ -73,22 +75,26 @@ public function enrollFace($userId, array $photos)
$process->run();
if (!$process->isSuccessful()) {
\Illuminate\Support\Facades\Log::warning("Face Training Failed (silently caught): " . $process->getErrorOutput());
\Illuminate\Support\Facades\Log::warning("Face Training Failed: " . $process->getErrorOutput());
} else {
$output = json_decode($process->getOutput(), true);
if (!isset($output['status']) || $output['status'] !== 'success') {
\Illuminate\Support\Facades\Log::warning("Face Training Failed (silently caught): " . ($output['message'] ?? 'Unknown Error'));
if (isset($output['status']) && $output['status'] === 'success') {
$trainingSuccess = true;
} else {
\Illuminate\Support\Facades\Log::warning("Face Training Failed: " . ($output['message'] ?? 'Unknown Error'));
}
}
} catch (\Exception $trainEx) {
\Illuminate\Support\Facades\Log::warning("Face Training Error (silently caught): " . $trainEx->getMessage());
\Illuminate\Support\Facades\Log::warning("Face Training Error: " . $trainEx->getMessage());
}
$relativeModelPath = "face_models/user_{$userId}.yml";
// Jika training Python berhasil → otomatis verified
// Jika training gagal → tetap pending (is_verified = 0), butuh approval manual HRD
DB::table('data_wajah')->where('id_user', $userId)->update([
'path_model_yml' => $relativeModelPath,
'is_verified' => 0,
'is_verified' => $trainingSuccess ? 1 : 0,
]);
DB::commit();

View File

@ -2,46 +2,27 @@
namespace Database\Seeders;
use App\Models\User;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
class DatabaseSeeder extends Seeder
{
public function run(): void
{
// Panggil MasterDataSeeder terlebih dahulu untuk data dasar (Divisi, Jabatan, Kantor, Role, User Admin)
$this->call([
// 1. Data master: Role, Divisi, Jabatan, Kantor, User HRD & contoh staff
MasterDataSeeder::class,
]);
// Tambahan data demo jika diperlukan (Opsional)
$kantor = \App\Models\Kantor::first();
$divisi = \App\Models\Divisi::first();
$jabatan = \App\Models\Jabatan::where('nama_jabatan', 'Staff')->first() ?? \App\Models\Jabatan::first();
$users = User::factory(5)->create([
'id_kantor' => $kantor->id_kantor,
'id_divisi' => $divisi->id_divisi,
'id_jabatan' => $jabatan->id_jabatan,
]);
$rolePegawai = \App\Models\Role::where('nama_role', 'pegawai')->first();
if ($rolePegawai) {
foreach ($users as $user) {
$user->roles()->attach($rolePegawai->id_role);
}
}
// Panggil seeder fitur lainnya
$this->call([
// 2. Permission per role
PermissionSeeder::class,
// 3. Data lookup / referensi
PresensiStatusSeeder::class,
PresensiSeeder::class,
JenisIzinSeeder::class,
StatusPengajuanSeeder::class,
PengajuanIzinSeeder::class,
SuperAdminSeeder::class, // Menambah super administrator dengan role admin
// 4. Data dummy (opsional, comment jika tidak diperlukan di production)
// PresensiSeeder::class,
// PengajuanIzinSeeder::class,
]);
}
}

View File

@ -3,31 +3,12 @@
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use App\Models\User;
use App\Models\Role;
class FixLegacyRolesSeeder extends Seeder
{
public function run()
{
$roleAdmin = Role::firstOrCreate(['nama_role' => 'admin']);
$rolePegawai = Role::firstOrCreate(['nama_role' => 'pegawai']);
$oldAdmins = User::where('role', 'admin')->get();
foreach ($oldAdmins as $user) {
if (!$user->roles()->where('nama_role', 'admin')->exists()) {
$user->roles()->attach($roleAdmin->id_role ?? $roleAdmin->id);
$this->command->info("Migrated legacy admin: {$user->email}");
}
}
$oldStaff = User::where('role', 'pegawai')->get();
foreach ($oldStaff as $user) {
if (!$user->roles()->where('nama_role', 'pegawai')->exists()) {
$user->roles()->attach($rolePegawai->id_role ?? $rolePegawai->id);
$this->command->info("Migrated legacy staff: {$user->email}");
}
}
$this->command->info('Tidak ada migrasi legacy yang perlu dilakukan (sistem sudah menggunakan pivot roles).');
}
}

View File

@ -26,61 +26,62 @@ public function run()
$kPusat = Kantor::firstOrCreate(
['nama_kantor' => 'Kantor Pusat'],
[
'alamat' => 'Jl. Jendral Sudirman No. 1, Jakarta',
'latitude' => -6.2088,
'alamat' => 'Jl. Jendral Sudirman No. 1, Jakarta',
'latitude' => -6.2088,
'longitude' => 106.8456,
'radius' => 200
'radius' => 200
]
);
$kCabang = Kantor::firstOrCreate(
['nama_kantor' => 'Curah Manis Studio'],
[
'alamat' => 'Jl. Curah Manis, Malang',
'latitude' => -7.9839,
'alamat' => 'Jl. Curah Manis, Malang',
'latitude' => -7.9839,
'longitude' => 112.6214,
'radius' => 200
'radius' => 200
]
);
// Roles
$roleAdmin = \App\Models\Role::firstOrCreate(['nama_role' => 'admin']);
$roleHrd = \App\Models\Role::firstOrCreate(['nama_role' => 'hrd']);
$roleManager = \App\Models\Role::firstOrCreate(['nama_role' => 'manager']);
$rolePegawai = \App\Models\Role::firstOrCreate(['nama_role' => 'pegawai']);
$roleHrd = \App\Models\Role::firstOrCreate(['nama_role' => 'hrd']);
$roleManager = \App\Models\Role::firstOrCreate(['nama_role' => 'manager']);
$roleSupervisor = \App\Models\Role::firstOrCreate(['nama_role' => 'supervisor']);
$roleStaff = \App\Models\Role::firstOrCreate(['nama_role' => 'staff']);
$admin = User::updateOrCreate(
['email' => 'admin@mpg.co.id'],
$hrd = User::updateOrCreate(
['nik' => '999999999'],
[
'nama_lengkap' => 'Super Administrator',
'password' => Hash::make('admin123'),
'nik' => '999999999',
'status_aktif' => 1,
'id_divisi' => $divIT->id_divisi,
'id_jabatan' => $jabMgr->id_jabatan,
'id_kantor' => $kPusat->id_kantor,
'nama_lengkap' => 'HRD Manager',
'email' => 'hrd@mpg.co.id',
'password' => Hash::make('Mpg123!'),
'status_aktif' => 1,
'id_divisi' => $divHR->id_divisi,
'id_jabatan' => $jabMgr->id_jabatan,
'id_kantor' => $kPusat->id_kantor,
'tgl_bergabung' => now(),
'sisa_cuti' => 12,
]
);
if (!$admin->roles()->where('nama_role', 'admin')->exists()) {
$admin->roles()->attach($roleAdmin->id_role);
if (!$hrd->roles()->where('nama_role', 'hrd')->exists()) {
$hrd->roles()->attach($roleHrd->id_role);
}
$pegawai = User::updateOrCreate(
['email' => 'budi@mpg.co.id'],
[
'nama_lengkap' => 'Budi Santoso',
'password' => Hash::make('Mpg123!'),
'nik' => '350712345678',
'status_aktif' => 1,
'id_divisi' => $divHR->id_divisi,
'id_jabatan' => $jabStf->id_jabatan,
'id_kantor' => $kCabang->id_kantor,
'nama_lengkap' => 'Budi Santoso',
'password' => Hash::make('Mpg123!'),
'nik' => '350712345678',
'status_aktif' => 1,
'id_divisi' => $divHR->id_divisi,
'id_jabatan' => $jabStf->id_jabatan,
'id_kantor' => $kCabang->id_kantor,
'tgl_bergabung' => now(),
'sisa_cuti' => 12,
]
);
if (!$pegawai->roles()->where('nama_role', 'pegawai')->exists()) {
$pegawai->roles()->attach($rolePegawai->id_role);
if (!$pegawai->roles()->where('nama_role', 'staff')->exists()) {
$pegawai->roles()->attach($roleStaff->id_role);
}
}
}

View File

@ -5,30 +5,34 @@
use Illuminate\Database\Seeder;
use App\Models\Permission;
use App\Models\Role;
use Illuminate\Support\Str;
class PermissionSeeder extends Seeder
{
public function run(): void
{
$permissions = [
'view_dashboard',
// Data Master
'manage_divisi',
'manage_jabatan',
'manage_kantor',
'manage_shift',
'manage_hari_libur',
'manage_cuti',
// Data Pegawai
'view_pegawai',
'create_pegawai',
'edit_pegawai',
'delete_pegawai',
'view_pegawai_detail',
// Penjadwalan
'view_jadwal',
'manage_jadwal',
// Presensi
'view_presensi_all',
'view_presensi_team',
'view_presensi_self',
@ -36,20 +40,37 @@ public function run(): void
'approve_presensi',
'correction_presensi',
// Izin
'view_izin_all',
'view_izin_team',
'create_izin',
'approve_izin',
// Surat Izin
'view_surat_izin',
'approve_surat_izin',
// Lembur
'view_lembur_all',
'view_lembur_team',
'create_lembur',
'approve_lembur',
// Poin
'manage_poin',
// Laporan
'view_laporan', // laporan kehadiran
'view_laporan_izin', // laporan izin/cuti
'view_laporan_lembur', // laporan lembur
// Pengumuman
'manage_pengumuman',
// Face Recognition
'manage_face_data',
'view_laporan',
// Sistem
'manage_users',
'manage_roles',
'manage_permissions',
@ -62,49 +83,50 @@ public function run(): void
);
}
$adminRole = Role::where('nama_role', 'admin')->first();
if ($adminRole) {
$allPermissions = Permission::all();
$adminRole->permissions()->sync($allPermissions);
}
// ─── HRD ─── akses penuh ke semua permission
$hrdRole = Role::where('nama_role', 'hrd')->first();
if ($hrdRole) {
$hrdPermissions = Permission::whereIn('nama_permission', [
'view_dashboard',
'manage_divisi', 'manage_jabatan', 'manage_kantor', 'manage_shift', 'manage_hari_libur',
'view_pegawai', 'create_pegawai', 'edit_pegawai', 'view_pegawai_detail',
'view_presensi_all', 'approve_presensi', 'correction_presensi',
'view_izin_all', 'approve_izin',
'view_lembur_all', 'approve_lembur',
'manage_face_data',
'view_laporan'
])->get();
$hrdRole->permissions()->sync($hrdPermissions);
$hrdRole->permissions()->sync(Permission::all());
}
// ─── MANAGER ─── approve presensi & izin, lihat laporan kehadiran & izin
$managerRole = Role::where('nama_role', 'manager')->first();
if ($managerRole) {
$managerPermissions = Permission::whereIn('nama_permission', [
$perms = Permission::whereIn('nama_permission', [
'view_dashboard',
'view_pegawai', 'view_pegawai_detail',
'view_presensi_team',
'view_jadwal',
'view_presensi_team', 'approve_presensi',
'view_izin_team', 'approve_izin',
'view_lembur_team', 'approve_lembur'
'view_surat_izin', 'approve_surat_izin',
'view_laporan',
'view_laporan_izin',
])->get();
$managerRole->permissions()->sync($managerPermissions);
$managerRole->permissions()->sync($perms);
}
$pegawaiRole = Role::where('nama_role', 'pegawai')->first();
if ($pegawaiRole) {
$pegawaiPermissions = Permission::whereIn('nama_permission', [
// ─── SUPERVISOR ─── approve presensi, lihat laporan kehadiran & penjadwalan
$supervisorRole = Role::where('nama_role', 'supervisor')->first();
if ($supervisorRole) {
$perms = Permission::whereIn('nama_permission', [
'view_dashboard',
'view_jadwal',
'view_presensi_team', 'approve_presensi',
'view_laporan',
])->get();
$supervisorRole->permissions()->sync($perms);
}
// ─── STAFF ─── hanya mobile (tidak ada view_dashboard = tidak bisa akses web)
$staffRole = Role::where('nama_role', 'staff')->first();
if ($staffRole) {
$perms = Permission::whereIn('nama_permission', [
'view_presensi_self',
'create_presensi',
'create_izin',
'create_lembur'
'create_lembur',
])->get();
$pegawaiRole->permissions()->sync($pegawaiPermissions);
$staffRole->permissions()->sync($perms);
}
}
}

View File

@ -3,53 +3,17 @@
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use App\Models\User;
use App\Models\Role;
use App\Models\Divisi;
use App\Models\Jabatan;
use App\Models\Kantor;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\DB;
/**
* SuperAdminSeeder sudah tidak digunakan.
* Fungsinya telah dipindahkan ke MasterDataSeeder (user HRD dibuat di sana).
* File ini dipertahankan agar tidak ada error jika masih dirujuk,
* namun tidak melakukan apa-apa.
*/
class SuperAdminSeeder extends Seeder
{
public function run()
public function run(): void
{
$divisi = Divisi::first() ?? Divisi::create(['nama_divisi' => 'IT Dept']);
$jabatan = Jabatan::first() ?? Jabatan::create(['nama_jabatan' => 'Super Admin']);
$kantor = Kantor::first() ?? Kantor::create(['nama_kantor' => 'Headquarters', 'alamat' => 'Cloud', 'tipe' => 'Pusat']);
$roleAdmin = Role::firstOrCreate(
['nama_role' => 'admin'],
['keterangan' => 'Administrator with Full Access']
);
$user = User::updateOrCreate(
['email' => 'superadmin@mpg.co.id'],
[
'nama_lengkap' => 'Super Administrator',
'password' => Hash::make('password'),
'nik' => '77777777',
'no_telp' => '081234567890',
'alamat' => 'Server Room',
'status_aktif' => 1,
'tgl_bergabung' => now(),
'id_divisi' => $divisi->id_divisi ?? $divisi->id,
'id_jabatan' => $jabatan->id_jabatan ?? $jabatan->id,
'id_kantor' => $kantor->id_kantor ?? $kantor->id,
'sisa_cuti' => 12,
]
);
if (!$user->roles()->where('nama_role', 'admin')->exists()) {
$user->roles()->attach($roleAdmin->id_role ?? $roleAdmin->id);
$this->command->info('Role admin attached to user.');
}
$this->command->info('Super Admin created successfully!');
$this->command->info('Email: superadmin@mpg.co.id');
$this->command->info('Password: password');
$this->command->info('SuperAdminSeeder: tidak melakukan apa-apa (lihat MasterDataSeeder).');
}
}

View File

@ -11,15 +11,27 @@ class="fixed inset-y-0 left-0 w-72 bg-white border-r border-slate-200 flex flex-
</div>
</div>
@php
$isAdmin = Auth::user()->roles->contains(function ($role) {
return strtolower($role->nama_role) === 'admin';
});
$isSuperAdmin = Auth::user()->isSuperAdmin();
$isAdminOrSuperAdmin = Auth::user()->isGlobalAdmin();
@endphp
@php
$roleNama = strtolower(Auth::user()->roles->first()?->nama_role ?? '');
$isHrd = $roleNama === 'hrd';
$isManager = $roleNama === 'manager';
$isSupervisor = $roleNama === 'supervisor';
$isStaff = $roleNama === 'staff';
// Alias untuk kondisi gabungan
$canManage = $isHrd; // hanya HRD
$canViewPegawai = $isHrd || $isManager; // HRD & Manager
$canViewPresensi = $isHrd || $isManager || $isSupervisor;
$canApproveIzin = $isHrd || $isManager;
$canViewLaporan = $isHrd || $isManager || $isSupervisor; // laporan kehadiran
$canViewLaporanIzin = $isHrd || $isManager;
$canViewJadwal = $isHrd || $isManager || $isSupervisor;
$canAccessWeb = $isHrd || $isManager || $isSupervisor;
@endphp
<nav class="flex-1 px-4 py-6 space-y-2 overflow-y-auto no-scrollbar">
{{-- ── Dashboard ── --}}
<div class="space-y-1">
<a href="{{ route('dashboard') }}"
class="flex items-center px-4 py-3 rounded-xl transition-all duration-200 group {{ request()->routeIs('dashboard') ? 'bg-[#130F26] text-white shadow-lg shadow-[#130F26]/30' : 'text-slate-500 hover:bg-slate-50 hover:text-slate-900' }}">
@ -33,7 +45,8 @@ class="flex items-center px-4 py-3 rounded-xl transition-all duration-200 group
</a>
</div>
@if($isAdminOrSuperAdmin)
{{-- ════════════════════════════════ SEKSI: MANAJEMEN ════════════════════════════════ --}}
@if($canManage || $canViewPegawai)
<div class="pt-6 pb-3 px-4 flex items-center gap-3">
<div class="h-px bg-slate-200 flex-1"></div>
<span class="text-[10px] font-bold text-slate-400 uppercase tracking-widest">Manajemen</span>
@ -41,47 +54,56 @@ class="flex items-center px-4 py-3 rounded-xl transition-all duration-200 group
</div>
<div class="space-y-1">
@if($isSuperAdmin)
<a href="{{ route('face.index') }}"
class="flex items-center px-4 py-3 rounded-xl transition-all duration-200 group {{ request()->routeIs('face.*') ? 'bg-[#130F26] text-white shadow-lg shadow-[#130F26]/30' : 'text-slate-500 hover:bg-slate-50 hover:text-slate-900' }}">
<svg class="w-5 h-5 mr-3 {{ request()->routeIs('face.*') ? 'text-white' : 'text-slate-400 group-hover:text-slate-600' }} group-hover:scale-110 transition-transform duration-300"
fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M14.828 14.828a4 4 0 01-5.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span class="font-semibold text-sm">Verifikasi Wajah</span>
@php $pendingFace = \App\Models\User::where('is_face_registered', 0)->whereHas('dataWajah')->count(); @endphp
@if($pendingFace > 0)
<span
class="ml-auto bg-red-500 text-white text-[10px] font-bold px-2 py-0.5 rounded-full">{{ $pendingFace }}</span>
{{-- Verifikasi Wajah HRD only --}}
@if($isHrd)
<a href="{{ route('face.index') }}"
class="flex items-center px-4 py-3 rounded-xl transition-all duration-200 group {{ request()->routeIs('face.*') ? 'bg-[#130F26] text-white shadow-lg shadow-[#130F26]/30' : 'text-slate-500 hover:bg-slate-50 hover:text-slate-900' }}">
<svg class="w-5 h-5 mr-3 {{ request()->routeIs('face.*') ? 'text-white' : 'text-slate-400 group-hover:text-slate-600' }} group-hover:scale-110 transition-transform duration-300"
fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M14.828 14.828a4 4 0 01-5.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span class="font-semibold text-sm">Verifikasi Wajah</span>
@php $pendingFace = \App\Models\User::where('is_face_registered', 0)->whereHas('dataWajah')->count(); @endphp
@if($pendingFace > 0)
<span class="ml-auto bg-red-500 text-white text-[10px] font-bold px-2 py-0.5 rounded-full">{{ $pendingFace }}</span>
@endif
</a>
@endif
</a>
@endif
<a href="{{ route('pegawai.index') }}"
class="flex items-center px-4 py-3 rounded-xl transition-all duration-200 group {{ request()->routeIs('pegawai.*') ? 'bg-[#130F26] text-white shadow-lg shadow-[#130F26]/30' : 'text-slate-500 hover:bg-slate-50 hover:text-slate-900' }}">
<svg class="w-5 h-5 mr-3 {{ request()->routeIs('pegawai.*') ? 'text-white' : 'text-slate-400 group-hover:text-slate-600' }} group-hover:scale-110 transition-transform duration-300"
fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z">
</path>
</svg>
<span class="font-semibold text-sm">Data Karyawan</span>
</a>
{{-- Data Karyawan HRD & Manager --}}
@if($canViewPegawai)
<a href="{{ route('pegawai.index') }}"
class="flex items-center px-4 py-3 rounded-xl transition-all duration-200 group {{ request()->routeIs('pegawai.*') ? 'bg-[#130F26] text-white shadow-lg shadow-[#130F26]/30' : 'text-slate-500 hover:bg-slate-50 hover:text-slate-900' }}">
<svg class="w-5 h-5 mr-3 {{ request()->routeIs('pegawai.*') ? 'text-white' : 'text-slate-400 group-hover:text-slate-600' }} group-hover:scale-110 transition-transform duration-300"
fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z">
</path>
</svg>
<span class="font-semibold text-sm">Data Karyawan</span>
</a>
@endif
{{-- Penjadwalan HRD, Manager, Supervisor --}}
@if($canViewJadwal)
<a href="{{ route('jadwal.index') }}"
class="flex items-center px-4 py-3 rounded-xl transition-all duration-200 group {{ request()->routeIs('jadwal.*') ? 'bg-[#130F26] text-white shadow-lg shadow-[#130F26]/30' : 'text-slate-500 hover:bg-slate-50 hover:text-slate-900' }}">
<svg class="w-5 h-5 mr-3 {{ request()->routeIs('jadwal.*') ? 'text-white' : 'text-slate-400 group-hover:text-slate-600' }} group-hover:scale-110 transition-transform duration-300"
fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
</svg>
<span class="font-semibold text-sm">Penjadwalan</span>
</a>
@endif
<a href="{{ route('jadwal.index') }}"
class="flex items-center px-4 py-3 rounded-xl transition-all duration-200 group {{ request()->routeIs('jadwal.*') ? 'bg-[#130F26] text-white shadow-lg shadow-[#130F26]/30' : 'text-slate-500 hover:bg-slate-50 hover:text-slate-900' }}">
<svg class="w-5 h-5 mr-3 {{ request()->routeIs('jadwal.*') ? 'text-white' : 'text-slate-400 group-hover:text-slate-600' }} group-hover:scale-110 transition-transform duration-300"
fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
</svg>
<span class="font-semibold text-sm">Penjadwalan</span>
</a>
</div>
@endif
@if($isAdminOrSuperAdmin)
{{-- ════════════════════════════════ SEKSI: ABSENSI ════════════════════════════════ --}}
@if($canViewLaporan || $canViewPresensi)
<div class="pt-6 pb-3 px-4 flex items-center gap-3">
<div class="h-px bg-slate-200 flex-1"></div>
<span class="text-[10px] font-bold text-slate-400 uppercase tracking-widest">Absensi</span>
@ -89,126 +111,145 @@ class="flex items-center px-4 py-3 rounded-xl transition-all duration-200 group
</div>
<div class="space-y-1">
<a href="{{ route('laporan.index') }}"
class="flex items-center px-4 py-3 rounded-xl transition-all duration-200 group {{ request()->routeIs('laporan.index') ? 'bg-[#130F26] text-white shadow-lg shadow-[#130F26]/30' : 'text-slate-500 hover:bg-slate-50 hover:text-slate-900' }}">
<svg class="w-5 h-5 mr-3 {{ request()->routeIs('laporan.index') ? 'text-white' : 'text-slate-400 group-hover:text-slate-600' }} group-hover:scale-110 transition-transform duration-300"
fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9 17v-2m3 2v-4m3 4v-6m2 10H7a2 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>
<span class="font-semibold text-sm">Laporan Kehadiran</span>
</a>
@if($isAdminOrSuperAdmin)
<a href="{{ route('laporan-lembur.index') }}"
class="flex items-center px-4 py-3 rounded-xl transition-all duration-200 group {{ request()->routeIs('laporan-lembur.*') ? 'bg-[#130F26] text-white shadow-lg shadow-[#130F26]/30' : 'text-slate-500 hover:bg-slate-50 hover:text-slate-900' }}">
<svg class="w-5 h-5 mr-3 {{ request()->routeIs('laporan-lembur.*') ? 'text-white' : 'text-slate-400 group-hover:text-slate-600' }} group-hover:scale-110 transition-transform duration-300"
fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span class="font-semibold text-sm">Laporan Lembur</span>
</a>
{{-- Laporan Kehadiran HRD, Manager, Supervisor --}}
@if($canViewLaporan)
<a href="{{ route('laporan.index') }}"
class="flex items-center px-4 py-3 rounded-xl transition-all duration-200 group {{ request()->routeIs('laporan.index') ? 'bg-[#130F26] text-white shadow-lg shadow-[#130F26]/30' : 'text-slate-500 hover:bg-slate-50 hover:text-slate-900' }}">
<svg class="w-5 h-5 mr-3 {{ request()->routeIs('laporan.index') ? 'text-white' : 'text-slate-400 group-hover:text-slate-600' }} group-hover:scale-110 transition-transform duration-300"
fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9 17v-2m3 2v-4m3 4v-6m2 10H7a2 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>
<span class="font-semibold text-sm">Laporan Kehadiran</span>
</a>
@endif
{{-- Laporan Lembur HRD only --}}
@if($isHrd)
<a href="{{ route('laporan-lembur.index') }}"
class="flex items-center px-4 py-3 rounded-xl transition-all duration-200 group {{ request()->routeIs('laporan-lembur.*') ? 'bg-[#130F26] text-white shadow-lg shadow-[#130F26]/30' : 'text-slate-500 hover:bg-slate-50 hover:text-slate-900' }}">
<svg class="w-5 h-5 mr-3 {{ request()->routeIs('laporan-lembur.*') ? 'text-white' : 'text-slate-400 group-hover:text-slate-600' }} group-hover:scale-110 transition-transform duration-300"
fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span class="font-semibold text-sm">Laporan Lembur</span>
</a>
@endif
{{-- Laporan Izin/Cuti HRD & Manager --}}
@if($canViewLaporanIzin)
<a href="{{ route('laporan.izin') }}"
class="flex items-center px-4 py-3 rounded-xl transition-all duration-200 group {{ request()->routeIs('laporan.izin') ? 'bg-[#130F26] text-white shadow-lg shadow-[#130F26]/30' : 'text-slate-500 hover:bg-slate-50 hover:text-slate-900' }}">
<svg class="w-5 h-5 mr-3 {{ request()->routeIs('laporan.izin') ? 'text-white' : 'text-slate-400 group-hover:text-slate-600' }} group-hover:scale-110 transition-transform duration-300"
fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
</svg>
<span class="font-semibold text-sm">Laporan Izin/Cuti</span>
</a>
@endif
{{-- Monitoring Presensi HRD, Manager, Supervisor --}}
@if($canViewPresensi)
<a href="{{ route('presensi.index') }}"
class="flex items-center px-4 py-3 rounded-xl transition-all duration-200 group {{ request()->routeIs('presensi.*') ? 'bg-[#130F26] text-white shadow-lg shadow-[#130F26]/30' : 'text-slate-500 hover:bg-slate-50 hover:text-slate-900' }}">
<svg class="w-5 h-5 mr-3 {{ request()->routeIs('presensi.*') ? 'text-white' : 'text-slate-400 group-hover:text-slate-600' }} group-hover:scale-110 transition-transform duration-300"
fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
<span class="flex-1 font-semibold text-sm">Monitoring Presensi</span>
@if(isset($pendingPresensi) && $pendingPresensi > 0)
<span class="ml-auto bg-red-500 text-white text-[10px] font-bold px-2 py-0.5 rounded-full">{{ $pendingPresensi }}</span>
@endif
</a>
@endif
{{-- Manajemen Izin HRD & Manager --}}
@if($canApproveIzin)
<a href="{{ route('izin.index') }}"
class="flex items-center px-4 py-3 rounded-xl transition-all duration-200 group {{ request()->routeIs('izin.*') ? 'bg-[#130F26] text-white shadow-lg shadow-[#130F26]/30' : 'text-slate-500 hover:bg-slate-50 hover:text-slate-900' }}">
<svg class="w-5 h-5 mr-3 {{ request()->routeIs('izin.*') ? 'text-white' : 'text-slate-400 group-hover:text-slate-600' }} group-hover:scale-110 transition-transform duration-300"
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">
</path>
</svg>
<span class="flex-1 font-semibold text-sm">Manajemen Izin</span>
@php $pendingIzin = \App\Models\PengajuanIzin::where('id_status', 1)->count(); @endphp
@if($pendingIzin > 0)
<span class="ml-auto bg-orange-500 text-white text-[10px] font-bold px-2 py-0.5 rounded-full">{{ $pendingIzin }}</span>
@endif
</a>
{{-- Surat Izin HRD & Manager --}}
<a href="{{ route('surat-izin.index') }}"
class="flex items-center px-4 py-3 rounded-xl transition-all duration-200 group {{ request()->routeIs('surat-izin.*') ? 'bg-[#130F26] text-white shadow-lg shadow-[#130F26]/30' : 'text-slate-500 hover:bg-slate-50 hover:text-slate-900' }}">
<svg class="w-5 h-5 mr-3 {{ request()->routeIs('surat-izin.*') ? 'text-white' : 'text-slate-400 group-hover:text-slate-600' }} group-hover:scale-110 transition-transform duration-300"
fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z">
</path>
</svg>
<span class="flex-1 font-semibold text-sm">Surat Izin</span>
@php $pendingSurat = \App\Models\SuratIzin::whereIn('status_surat', ['menunggu_manajer', 'menunggu_hrd'])->count(); @endphp
@if($pendingSurat > 0)
<span class="ml-auto bg-orange-500 text-white text-[10px] font-bold px-2 py-0.5 rounded-full">{{ $pendingSurat }}</span>
@endif
</a>
@endif
{{-- Lembur & Poin HRD only --}}
@if($isHrd)
<a href="{{ route('lembur.index') }}"
class="flex items-center px-4 py-3 rounded-xl transition-all duration-200 group {{ request()->routeIs('lembur.*') ? 'bg-[#130F26] text-white shadow-lg shadow-[#130F26]/30' : 'text-slate-500 hover:bg-slate-50 hover:text-slate-900' }}">
<svg class="w-5 h-5 mr-3 {{ request()->routeIs('lembur.*') ? 'text-white' : 'text-slate-400 group-hover:text-slate-600' }} group-hover:scale-110 transition-transform duration-300"
fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z">
</path>
</svg>
<span class="flex-1 font-semibold text-sm">Lembur & Poin</span>
@php $pendingLembur = \App\Models\Lembur::where('id_status', 1)->count(); @endphp
@if($pendingLembur > 0)
<span class="ml-auto bg-orange-500 text-white text-[10px] font-bold px-2 py-0.5 rounded-full">{{ $pendingLembur }}</span>
@endif
</a>
<a href="{{ route('penggunaan-poin.index') }}"
class="flex items-center px-4 py-3 rounded-xl transition-all duration-200 group {{ request()->routeIs('penggunaan-poin.*') ? 'bg-[#130F26] text-white shadow-lg shadow-[#130F26]/30' : 'text-slate-500 hover:bg-slate-50 hover:text-slate-900' }}">
<svg class="w-5 h-5 mr-3 {{ request()->routeIs('penggunaan-poin.*') ? 'text-white' : 'text-slate-400 group-hover:text-slate-600' }} group-hover:scale-110 transition-transform duration-300"
fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9 12l2 2 4-4M7.835 4.697a3.42 3.42 0 001.946-.806 3.42 3.42 0 014.438 0 3.42 3.42 0 001.946.806 3.42 3.42 0 013.138 3.138 3.42 3.42 0 00.806 1.946 3.42 3.42 0 010 4.438 3.42 3.42 0 00-.806 1.946 3.42 3.42 0 01-3.138 3.138 3.42 3.42 0 00-1.946.806 3.42 3.42 0 01-4.438 0 3.42 3.42 0 00-1.946-.806 3.42 3.42 0 01-3.138-3.138 3.42 3.42 0 00-.806-1.946 3.42 3.42 0 010-4.438 3.42 3.42 0 00.806-1.946 3.42 3.42 0 013.138-3.138z">
</path>
</svg>
<span class="flex-1 font-semibold text-sm">Persetujuan Poin</span>
@php $pendingPoin = \App\Models\PenggunaanPoin::where('id_status', 1)->count(); @endphp
@if($pendingPoin > 0)
<span class="ml-auto bg-orange-500 text-white text-[10px] font-bold px-2 py-0.5 rounded-full">{{ $pendingPoin }}</span>
@endif
</a>
{{-- Pengumuman HRD only --}}
<a href="{{ route('pengumuman.index') }}"
class="flex items-center px-4 py-3 rounded-xl transition-all duration-200 group {{ request()->routeIs('pengumuman.*') ? 'bg-[#130F26] text-white shadow-lg shadow-[#130F26]/30' : 'text-slate-500 hover:bg-slate-50 hover:text-slate-900' }}">
<svg class="w-5 h-5 mr-3 {{ request()->routeIs('pengumuman.*') ? 'text-white' : 'text-slate-400 group-hover:text-slate-600' }} group-hover:scale-110 transition-transform duration-300"
fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M11 5.882V19.24a1.76 1.76 0 01-3.417.592l-2.147-6.15M18 13a3 3 0 100-6M5.436 13.683A4.001 4.001 0 017 6h1.832c4.1 0 7.625-1.234 9.168-3v14c-1.543-1.766-5.067-3-9.168-3H7a3.988 3.988 0 01-1.564-.317z">
</path>
</svg>
<span class="font-semibold text-sm">Pengumuman</span>
</a>
@endif
</div>
@endif
<a href="{{ route('laporan.izin') }}"
class="flex items-center px-4 py-3 rounded-xl transition-all duration-200 group {{ request()->routeIs('laporan.izin') ? 'bg-[#130F26] text-white shadow-lg shadow-[#130F26]/30' : 'text-slate-500 hover:bg-slate-50 hover:text-slate-900' }}">
<svg class="w-5 h-5 mr-3 {{ request()->routeIs('laporan.izin') ? 'text-white' : 'text-slate-400 group-hover:text-slate-600' }} group-hover:scale-110 transition-transform duration-300"
fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
</svg>
<span class="font-semibold text-sm">Laporan Izin/Cuti</span>
</a>
<a href="{{ route('presensi.index') }}"
class="flex items-center px-4 py-3 rounded-xl transition-all duration-200 group {{ request()->routeIs('presensi.*') ? 'bg-[#130F26] text-white shadow-lg shadow-[#130F26]/30' : 'text-slate-500 hover:bg-slate-50 hover:text-slate-900' }}">
<svg class="w-5 h-5 mr-3 {{ request()->routeIs('presensi.*') ? 'text-white' : 'text-slate-400 group-hover:text-slate-600' }} group-hover:scale-110 transition-transform duration-300"
fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
<span class="flex-1 font-semibold text-sm">Monitoring Presensi</span>
@if(isset($pendingPresensi) && $pendingPresensi > 0)
<span class="ml-auto bg-red-500 text-white text-[10px] font-bold px-2 py-0.5 rounded-full">
{{ $pendingPresensi }}
</span>
@endif
</a>
<a href="{{ route('izin.index') }}"
class="flex items-center px-4 py-3 rounded-xl transition-all duration-200 group {{ request()->routeIs('izin.*') ? 'bg-[#130F26] text-white shadow-lg shadow-[#130F26]/30' : 'text-slate-500 hover:bg-slate-50 hover:text-slate-900' }}">
<svg class="w-5 h-5 mr-3 {{ request()->routeIs('izin.*') ? 'text-white' : 'text-slate-400 group-hover:text-slate-600' }} group-hover:scale-110 transition-transform duration-300"
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">
</path>
</svg>
<span class="flex-1 font-semibold text-sm">Manajemen Izin</span>
@php $pendingIzin = \App\Models\PengajuanIzin::where('id_status', 1)->count(); @endphp
@if($pendingIzin > 0)
<span class="ml-auto bg-orange-500 text-white text-[10px] font-bold px-2 py-0.5 rounded-full">{{ $pendingIzin }}</span>
@endif
</a>
<a href="{{ route('surat-izin.index') }}"
class="flex items-center px-4 py-3 rounded-xl transition-all duration-200 group {{ request()->routeIs('surat-izin.*') ? 'bg-[#130F26] text-white shadow-lg shadow-[#130F26]/30' : 'text-slate-500 hover:bg-slate-50 hover:text-slate-900' }}">
<svg class="w-5 h-5 mr-3 {{ request()->routeIs('surat-izin.*') ? 'text-white' : 'text-slate-400 group-hover:text-slate-600' }} group-hover:scale-110 transition-transform duration-300"
fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z">
</path>
</svg>
<span class="flex-1 font-semibold text-sm">Surat Izin</span>
@php $pendingSurat = \App\Models\SuratIzin::whereIn('status_surat', ['menunggu_manajer', 'menunggu_hrd'])->count(); @endphp
@if($pendingSurat > 0)
<span class="ml-auto bg-orange-500 text-white text-[10px] font-bold px-2 py-0.5 rounded-full">{{ $pendingSurat }}</span>
@endif
</a>
<a href="{{ route('pengumuman.index') }}"
class="flex items-center px-4 py-3 rounded-xl transition-all duration-200 group {{ request()->routeIs('pengumuman.*') ? 'bg-[#130F26] text-white shadow-lg shadow-[#130F26]/30' : 'text-slate-500 hover:bg-slate-50 hover:text-slate-900' }}">
<svg class="w-5 h-5 mr-3 {{ request()->routeIs('pengumuman.*') ? 'text-white' : 'text-slate-400 group-hover:text-slate-600' }} group-hover:scale-110 transition-transform duration-300"
fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M11 5.882V19.24a1.76 1.76 0 01-3.417.592l-2.147-6.15M18 13a3 3 0 100-6M5.436 13.683A4.001 4.001 0 017 6h1.832c4.1 0 7.625-1.234 9.168-3v14c-1.543-1.766-5.067-3-9.168-3H7a3.988 3.988 0 01-1.564-.317z">
</path>
</svg>
<span class="font-semibold text-sm">Pengumuman</span>
</a>
<a href="{{ route('lembur.index') }}"
class="flex items-center px-4 py-3 rounded-xl transition-all duration-200 group {{ request()->routeIs('lembur.*') ? 'bg-[#130F26] text-white shadow-lg shadow-[#130F26]/30' : 'text-slate-500 hover:bg-slate-50 hover:text-slate-900' }}">
<svg class="w-5 h-5 mr-3 {{ request()->routeIs('lembur.*') ? 'text-white' : 'text-slate-400 group-hover:text-slate-600' }} group-hover:scale-110 transition-transform duration-300"
fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z">
</path>
</svg>
<span class="flex-1 font-semibold text-sm">Lembur & Poin</span>
@php $pendingLembur = \App\Models\Lembur::where('id_status', 1)->count(); @endphp
@if($pendingLembur > 0)
<span class="ml-auto bg-orange-500 text-white text-[10px] font-bold px-2 py-0.5 rounded-full">{{ $pendingLembur }}</span>
@endif
</a>
<a href="{{ route('penggunaan-poin.index') }}"
class="flex items-center px-4 py-3 rounded-xl transition-all duration-200 group {{ request()->routeIs('penggunaan-poin.*') ? 'bg-[#130F26] text-white shadow-lg shadow-[#130F26]/30' : 'text-slate-500 hover:bg-slate-50 hover:text-slate-900' }}">
<svg class="w-5 h-5 mr-3 {{ request()->routeIs('penggunaan-poin.*') ? 'text-white' : 'text-slate-400 group-hover:text-slate-600' }} group-hover:scale-110 transition-transform duration-300"
fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9 12l2 2 4-4M7.835 4.697a3.42 3.42 0 001.946-.806 3.42 3.42 0 014.438 0 3.42 3.42 0 001.946.806 3.42 3.42 0 013.138 3.138 3.42 3.42 0 00.806 1.946 3.42 3.42 0 010 4.438 3.42 3.42 0 00-.806 1.946 3.42 3.42 0 01-3.138 3.138 3.42 3.42 0 00-1.946.806 3.42 3.42 0 01-4.438 0 3.42 3.42 0 00-1.946-.806 3.42 3.42 0 01-3.138-3.138 3.42 3.42 0 00-.806-1.946 3.42 3.42 0 010-4.438 3.42 3.42 0 00.806-1.946 3.42 3.42 0 013.138-3.138z">
</path>
</svg>
<span class="flex-1 font-semibold text-sm">Persetujuan Poin</span>
@php $pendingPoin = \App\Models\PenggunaanPoin::where('id_status', 1)->count(); @endphp
@if($pendingPoin > 0)
<span class="ml-auto bg-orange-500 text-white text-[10px] font-bold px-2 py-0.5 rounded-full">{{ $pendingPoin }}</span>
@endif
</a>
</div>
{{-- ════════════════════════════════ SEKSI: DATA MASTER (HRD only) ════════════════════════════════ --}}
@if($isHrd)
<div x-data="{ open: {{ request()->routeIs('divisi.*', 'jabatan.*', 'kantor.*', 'role.*', 'shift.*', 'hari-libur.*', 'cuti.*') ? 'true' : 'false' }} }"
class="space-y-1">
@ -250,12 +291,10 @@ class="block py-2.5 px-4 text-sm rounded-lg transition-all {{ request()->routeIs
Data Jabatan
</a>
@if($isSuperAdmin)
<a href="{{ route('role.index') }}"
class="block py-2.5 px-4 text-sm rounded-lg transition-all {{ request()->routeIs('role.*') ? 'bg-slate-100 text-[#130F26] font-bold border-l-4 border-[#130F26]' : 'text-slate-500 hover:text-slate-900 hover:bg-slate-50' }}">
Data Role
</a>
@endif
<a href="{{ route('shift.index') }}"
class="block py-2.5 px-4 text-sm rounded-lg transition-all {{ request()->routeIs('shift.*') ? 'bg-slate-100 text-[#130F26] font-bold border-l-4 border-[#130F26]' : 'text-slate-500 hover:text-slate-900 hover:bg-slate-50' }}">
@ -266,7 +305,7 @@ class="block py-2.5 px-4 text-sm rounded-lg transition-all {{ request()->routeIs
class="block py-2.5 px-4 text-sm rounded-lg transition-all {{ request()->routeIs('hari-libur.*') ? 'bg-slate-100 text-[#130F26] font-bold border-l-4 border-[#130F26]' : 'text-slate-500 hover:text-slate-900 hover:bg-slate-50' }}">
Hari Libur
</a>
<a href="{{ route('cuti.index') }}"
class="block py-2.5 px-4 text-sm rounded-lg transition-all {{ request()->routeIs('cuti.*') ? 'bg-slate-100 text-[#130F26] font-bold border-l-4 border-[#130F26]' : 'text-slate-500 hover:text-slate-900 hover:bg-slate-50' }}">
Sisa Cuti Pegawai
@ -276,6 +315,8 @@ class="block py-2.5 px-4 text-sm rounded-lg transition-all {{ request()->routeIs
@endif
</nav>
{{-- ── Footer: Pengaturan Akun & Logout ── --}}
<div class="px-8 py-6 border-t border-slate-100 mt-auto bg-slate-50/50">
<a href="{{ route('profile.edit') }}"
class="flex items-center px-4 py-3 rounded-xl transition-all duration-200 group {{ request()->routeIs('profile.edit') ? 'bg-[#130F26] text-white shadow-lg shadow-[#130F26]/30' : 'text-slate-500 hover:bg-slate-50 hover:text-slate-900' }}">
@ -310,7 +351,7 @@ class="flex w-full items-center px-4 py-3 rounded-xl text-red-500 hover:bg-red-5
display: none;
}
.no-scrollbar {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none;
scrollbar-width: none;
}
</style>

View File

@ -1,4 +1,4 @@
@extends('layouts.app')
@extends('layouts.app')
@section('title', 'Monitoring Presensi')
@ -24,7 +24,7 @@
<x-page-header title="Monitoring Presensi" subtitle="Pantau kehadiran pegawai harian.">
<div class="flex gap-2">
@if(Auth::user()->roles->contains(fn($role) => in_array(strtolower($role->nama_role), ['admin', 'super admin', 'super_admin'])))
@if(Auth::user()->roles->contains(fn($role) => strtolower($role->nama_role) === 'hrd'))
<a href="{{ route('presensi.create') }}" class="inline-flex items-center justify-center px-4 py-2 border border-transparent font-medium rounded-lg text-white bg-slate-800 hover:bg-slate-900 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-slate-900 transition-colors shadow-sm text-sm h-[42px]">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
@ -212,7 +212,7 @@ class="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-bold
</button>
@endif
@if(Auth::user()->roles->contains(fn($role) => in_array(strtolower($role->nama_role), ['admin', 'super admin', 'super_admin'])))
@if(Auth::user()->roles->contains(fn($role) => strtolower($role->nama_role) === 'hrd'))
<a href="{{ route('presensi.edit', $p->id_presensi) }}" class="p-1.5 text-blue-600 hover:text-blue-800 bg-blue-50 hover:bg-blue-100 rounded-lg transition-colors border border-blue-100" title="Koreksi Jam/Status">
<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="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"></path></svg>
</a>

View File

@ -74,11 +74,10 @@
Route::post('/tanda-tangan', [SignatureController::class, 'store'])->name('signature.store');
});
Route::middleware(['auth', 'role:admin'])->group(function () {
Route::middleware(['auth', 'role:hrd'])->group(function () {
Route::resource('divisi', DivisiController::class)->except(['create', 'edit', 'show']);
Route::resource('jabatan', JabatanController::class);
Route::resource('kantor', KantorController::class)->except(['create', 'show', 'edit']);
// Role route moved to super admin group
Route::resource('shift', \App\Http\Controllers\ShiftController::class)->except(['create', 'edit', 'show']);
Route::resource('jadwal', \App\Http\Controllers\JadwalController::class)->except(['edit', 'show']);
Route::resource('pegawai', PegawaiController::class);
@ -99,9 +98,7 @@
Route::get('/tukar-shift/jadwal-user', [\App\Http\Controllers\TukarShiftController::class, 'getJadwalUser'])->name('tukar-shift.jadwal-user');
Route::resource('tukar-shift', \App\Http\Controllers\TukarShiftController::class)->only(['index', 'create', 'store']);
});
Route::middleware(['auth', 'role:super admin,super_admin'])->group(function () {
Route::get('/permissions', [PermissionController::class, 'index'])->name('permission.index');
Route::post('/permissions', [PermissionController::class, 'store'])->name('permission.store');
Route::post('/permissions/sync/{id_role}', [PermissionController::class, 'sync'])->name('permission.sync');