perbaikan
This commit is contained in:
parent
1f881a6c69
commit
0216c572cf
|
|
@ -110,6 +110,10 @@ public function registerEvents(): array
|
||||||
$lastRow = $this->data->count() + 3;
|
$lastRow = $this->data->count() + 3;
|
||||||
$dataRange = "A3:I{$lastRow}";
|
$dataRange = "A3:I{$lastRow}";
|
||||||
|
|
||||||
|
$sheet->getParent()->getDefaultStyle()->getFont()
|
||||||
|
->setName('Calibri')
|
||||||
|
->setSize(10);
|
||||||
|
|
||||||
// Merge judul
|
// Merge judul
|
||||||
$sheet->mergeCells('A1:I1');
|
$sheet->mergeCells('A1:I1');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -150,6 +150,10 @@ public function registerEvents(): array
|
||||||
$lastRow = count($this->pegawai) + 6;
|
$lastRow = count($this->pegawai) + 6;
|
||||||
$dataStart = 7;
|
$dataStart = 7;
|
||||||
|
|
||||||
|
$sheet->getParent()->getDefaultStyle()->getFont()
|
||||||
|
->setName('Calibri')
|
||||||
|
->setSize(10);
|
||||||
|
|
||||||
$sheet->mergeCells('A1:I1');
|
$sheet->mergeCells('A1:I1');
|
||||||
$sheet->mergeCells('A2:I2');
|
$sheet->mergeCells('A2:I2');
|
||||||
$sheet->mergeCells('A3:I3');
|
$sheet->mergeCells('A3:I3');
|
||||||
|
|
|
||||||
|
|
@ -154,6 +154,10 @@ public function registerEvents(): array
|
||||||
$lastRow = count($this->rekap) + 6;
|
$lastRow = count($this->rekap) + 6;
|
||||||
$dataStart = 7;
|
$dataStart = 7;
|
||||||
|
|
||||||
|
$sheet->getParent()->getDefaultStyle()->getFont()
|
||||||
|
->setName('Calibri')
|
||||||
|
->setSize(10);
|
||||||
|
|
||||||
// Merge judul
|
// Merge judul
|
||||||
$sheet->mergeCells('A1:K1');
|
$sheet->mergeCells('A1:K1');
|
||||||
$sheet->mergeCells('A2:K2');
|
$sheet->mergeCells('A2:K2');
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ public function index()
|
||||||
'description' => $item->isi,
|
'description' => $item->isi,
|
||||||
'tanggal' => $item->tanggal?->format('Y-m-d'),
|
'tanggal' => $item->tanggal?->format('Y-m-d'),
|
||||||
'jabatan' => $item->pembuat?->jabatan?->nama_jabatan ?? 'Admin',
|
'jabatan' => $item->pembuat?->jabatan?->nama_jabatan ?? 'Admin',
|
||||||
|
'nama_pembuat' => $item->pembuat?->nama_lengkap ?? 'Admin',
|
||||||
'avatar_url' => $item->pembuat?->foto,
|
'avatar_url' => $item->pembuat?->foto,
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
@ -31,4 +32,23 @@ public function index()
|
||||||
return ApiResponse::error('Gagal memuat pengumuman: ' . $e->getMessage(), 500);
|
return ApiResponse::error('Gagal memuat pengumuman: ' . $e->getMessage(), 500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function show($id)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$item = Pengumuman::with('pembuat.jabatan')->findOrFail($id);
|
||||||
|
|
||||||
|
return ApiResponse::success([
|
||||||
|
'id' => $item->id_pengumuman,
|
||||||
|
'title' => $item->judul,
|
||||||
|
'description' => $item->isi,
|
||||||
|
'tanggal' => $item->tanggal?->format('Y-m-d'),
|
||||||
|
'jabatan' => $item->pembuat?->jabatan?->nama_jabatan ?? 'Admin',
|
||||||
|
'nama_pembuat' => $item->pembuat?->nama_lengkap ?? 'Admin',
|
||||||
|
'avatar_url' => $item->pembuat?->foto,
|
||||||
|
], 'Detail pengumuman berhasil dimuat');
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return ApiResponse::error('Pengumuman tidak ditemukan', 404);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,61 @@ public function index()
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function checkRadius(Request $request)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$request->validate([
|
||||||
|
'latitude' => 'required|numeric',
|
||||||
|
'longitude' => 'required|numeric',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$user = Auth::user();
|
||||||
|
$kantor = $user->kantor;
|
||||||
|
|
||||||
|
if (!$kantor) {
|
||||||
|
return response()->json([
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Data kantor belum disetting.',
|
||||||
|
], 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
$distance = $this->calculateDistance(
|
||||||
|
$request->latitude,
|
||||||
|
$request->longitude,
|
||||||
|
$kantor->latitude,
|
||||||
|
$kantor->longitude,
|
||||||
|
);
|
||||||
|
|
||||||
|
$isWithinRadius = $distance <= $kantor->radius;
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'status' => 'success',
|
||||||
|
'data' => [
|
||||||
|
'is_within_radius' => $isWithinRadius,
|
||||||
|
'distance' => round($distance, 2),
|
||||||
|
'radius' => $kantor->radius,
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return response()->json([
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => $e->getMessage(),
|
||||||
|
], 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function calculateDistance($lat1, $lon1, $lat2, $lon2)
|
||||||
|
{
|
||||||
|
$earthRadius = 6371000;
|
||||||
|
$dLat = deg2rad($lat2 - $lat1);
|
||||||
|
$dLon = deg2rad($lon2 - $lon1);
|
||||||
|
$a = sin($dLat / 2) * sin($dLat / 2) +
|
||||||
|
cos(deg2rad($lat1)) * cos(deg2rad($lat2)) *
|
||||||
|
sin($dLon / 2) * sin($dLon / 2);
|
||||||
|
$c = 2 * atan2(sqrt($a), sqrt(1 - $a));
|
||||||
|
return $earthRadius * $c;
|
||||||
|
}
|
||||||
|
|
||||||
public function store(Request $request)
|
public function store(Request $request)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
|
@ -138,11 +193,16 @@ public function history(Request $request)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$userId = Auth::id();
|
$userId = Auth::id();
|
||||||
$history = Presensi::where('id_user', $userId)
|
$month = $request->get('month', Carbon::now('Asia/Jakarta')->month);
|
||||||
->latest('tanggal')
|
$year = $request->get('year', Carbon::now('Asia/Jakarta')->year);
|
||||||
->paginate(10);
|
|
||||||
|
|
||||||
$enriched = $history->getCollection()->map(function ($item) use ($userId) {
|
$history = Presensi::where('id_user', $userId)
|
||||||
|
->whereMonth('tanggal', $month)
|
||||||
|
->whereYear('tanggal', $year)
|
||||||
|
->latest('tanggal')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
$enriched = $history->map(function ($item) use ($userId) {
|
||||||
$jadwal = \App\Models\JadwalKerja::with('shift')
|
$jadwal = \App\Models\JadwalKerja::with('shift')
|
||||||
->where('id_user', $userId)
|
->where('id_user', $userId)
|
||||||
->where('tanggal', $item->tanggal)
|
->where('tanggal', $item->tanggal)
|
||||||
|
|
@ -220,9 +280,11 @@ public function history(Request $request)
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
$history->setCollection($enriched);
|
return ApiResponse::success([
|
||||||
|
'data' => $enriched->values(),
|
||||||
return ApiResponse::success($history);
|
'month' => (int) $month,
|
||||||
|
'year' => (int) $year,
|
||||||
|
]);
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
return ApiResponse::error($e->getMessage(), 500);
|
return ApiResponse::error($e->getMessage(), 500);
|
||||||
|
|
|
||||||
|
|
@ -30,14 +30,23 @@ public function store(\App\Http\Requests\StorePengajuanIzinRequest $request)
|
||||||
|
|
||||||
$jenisIzin = JenisIzin::find($request->id_jenis_izin);
|
$jenisIzin = JenisIzin::find($request->id_jenis_izin);
|
||||||
if ($jenisIzin && $jenisIzin->nama_izin == 'Cuti') {
|
if ($jenisIzin && $jenisIzin->nama_izin == 'Cuti') {
|
||||||
// Validasi sisa cuti
|
$tanggalMulai = Carbon::parse($request->tanggal_mulai);
|
||||||
|
$tanggalSelesai = Carbon::parse($request->tanggal_selesai);
|
||||||
|
$jumlahHari = $tanggalMulai->diffInDays($tanggalSelesai) + 1;
|
||||||
|
|
||||||
if ($user->sisa_cuti <= 0) {
|
if ($user->sisa_cuti <= 0) {
|
||||||
return ApiResponse::error('Sisa cuti Anda sudah habis (0 hari). Pengajuan cuti tidak dapat dilanjutkan.', 400);
|
return ApiResponse::error('Sisa cuti Anda sudah habis (0 hari). Pengajuan cuti tidak dapat dilanjutkan.', 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
$tanggalMulai = Carbon::parse($request->tanggal_mulai);
|
if ($jumlahHari > 3) {
|
||||||
$minDate = Carbon::now()->addDays(7)->startOfDay();
|
return ApiResponse::error('Pengajuan cuti maksimal 3 hari berturut-turut dalam satu pengajuan.', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($jumlahHari > $user->sisa_cuti) {
|
||||||
|
return ApiResponse::error("Sisa cuti Anda tidak mencukupi. Sisa: {$user->sisa_cuti} hari, diajukan: {$jumlahHari} hari.", 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
$minDate = Carbon::now()->addDays(7)->startOfDay();
|
||||||
if ($tanggalMulai->lt($minDate)) {
|
if ($tanggalMulai->lt($minDate)) {
|
||||||
return ApiResponse::error('Pengajuan Cuti minimal H-7!', 400);
|
return ApiResponse::error('Pengajuan Cuti minimal H-7!', 400);
|
||||||
}
|
}
|
||||||
|
|
@ -255,7 +264,7 @@ public function update(\App\Http\Requests\UpdatePengajuanIzinRequest $request, $
|
||||||
public function history(Request $request)
|
public function history(Request $request)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$query = PengajuanIzin::with(['jenisIzin', 'statusPengajuan'])
|
$query = PengajuanIzin::with(['jenisIzin', 'statusPengajuan', 'suratIzin'])
|
||||||
->where('id_user', Auth::id());
|
->where('id_user', Auth::id());
|
||||||
|
|
||||||
if ($request->has('status')) {
|
if ($request->has('status')) {
|
||||||
|
|
@ -275,6 +284,16 @@ public function history(Request $request)
|
||||||
|
|
||||||
$history = $query->orderBy('created_at', 'desc')->paginate(10);
|
$history = $query->orderBy('created_at', 'desc')->paginate(10);
|
||||||
|
|
||||||
|
$history->getCollection()->transform(function ($item) {
|
||||||
|
$item->bukti_file_url = $item->bukti_file
|
||||||
|
? asset('storage/' . $item->bukti_file)
|
||||||
|
: null;
|
||||||
|
$item->has_surat = $item->suratIzin !== null;
|
||||||
|
$item->id_surat = $item->suratIzin?->id_surat;
|
||||||
|
unset($item->suratIzin);
|
||||||
|
return $item;
|
||||||
|
});
|
||||||
|
|
||||||
return ApiResponse::success($history);
|
return ApiResponse::success($history);
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
|
|
|
||||||
|
|
@ -200,12 +200,22 @@ public function show($id)
|
||||||
|
|
||||||
$historyPoin = $historyTambah->concat($historyKurang)->sortByDesc('tanggal')->take(10);
|
$historyPoin = $historyTambah->concat($historyKurang)->sortByDesc('tanggal')->take(10);
|
||||||
|
|
||||||
$riwayatPresensi = \App\Models\Presensi::with(['status', 'jadwal.shift'])
|
$riwayatPresensi = \App\Models\Presensi::with(['status'])
|
||||||
->where('id_user', $id)
|
->where('id_user', $id)
|
||||||
->whereDate('tanggal', '>=', now()->subDays(30)->toDateString())
|
->whereDate('tanggal', '>=', now()->subDays(30)->toDateString())
|
||||||
->orderBy('tanggal', 'desc')
|
->orderBy('tanggal', 'desc')
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
|
$jadwalKerja = \App\Models\JadwalKerja::with('shift')
|
||||||
|
->where('id_user', $id)
|
||||||
|
->whereIn('tanggal', $riwayatPresensi->pluck('tanggal'))
|
||||||
|
->get()
|
||||||
|
->keyBy('tanggal');
|
||||||
|
|
||||||
|
foreach ($riwayatPresensi as $presensi) {
|
||||||
|
$presensi->setRelation('jadwal', $jadwalKerja->get($presensi->tanggal));
|
||||||
|
}
|
||||||
|
|
||||||
$riwayatIzin = \App\Models\PengajuanIzin::with('jenisIzin')
|
$riwayatIzin = \App\Models\PengajuanIzin::with('jenisIzin')
|
||||||
->where('id_user', $id)
|
->where('id_user', $id)
|
||||||
->whereDate('tanggal_mulai', '>=', now()->subDays(30)->toDateString())
|
->whereDate('tanggal_mulai', '>=', now()->subDays(30)->toDateString())
|
||||||
|
|
@ -238,4 +248,18 @@ public function destroy($id)
|
||||||
|
|
||||||
return redirect()->route('pegawai.index')->with('success', 'Status pegawai telah diubah menjadi Non-Aktif (Resigned). Histori data tetap tersimpan.');
|
return redirect()->route('pegawai.index')->with('success', 'Status pegawai telah diubah menjadi Non-Aktif (Resigned). Histori data tetap tersimpan.');
|
||||||
}
|
}
|
||||||
|
public function resetPassword($id)
|
||||||
|
{
|
||||||
|
$pegawai = User::findOrFail($id);
|
||||||
|
|
||||||
|
$defaultPassword = 'Mpg123!';
|
||||||
|
$pegawai->update([
|
||||||
|
'password' => Hash::make($defaultPassword),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$pegawai->tokens()->delete();
|
||||||
|
|
||||||
|
return redirect()->route('pegawai.show', $id)
|
||||||
|
->with('success', "Password {$pegawai->nama_lengkap} berhasil direset ke default (Mpg123!).");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,18 @@ public function store(Request $request)
|
||||||
|
|
||||||
$jenisIzin = JenisIzin::find($request->id_jenis_izin);
|
$jenisIzin = JenisIzin::find($request->id_jenis_izin);
|
||||||
if ($jenisIzin && $jenisIzin->nama_izin == 'Cuti') {
|
if ($jenisIzin && $jenisIzin->nama_izin == 'Cuti') {
|
||||||
$diffParams = Carbon::parse($request->tanggal_mulai, 'Asia/Jakarta')->diffInDays(Carbon::now('Asia/Jakarta'));
|
$pegawai = User::find($request->id_user);
|
||||||
|
$tanggalMulai = Carbon::parse($request->tanggal_mulai);
|
||||||
|
$tanggalSelesai = Carbon::parse($request->tanggal_selesai);
|
||||||
|
$jumlahHari = $tanggalMulai->diffInDays($tanggalSelesai) + 1;
|
||||||
|
|
||||||
|
if ($jumlahHari > 3) {
|
||||||
|
return back()->withInput()->with('error', 'Pengajuan cuti maksimal 3 hari berturut-turut dalam satu pengajuan.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($pegawai && $pegawai->sisa_cuti < $jumlahHari) {
|
||||||
|
return back()->withInput()->with('error', "Sisa cuti pegawai tidak mencukupi. Sisa: {$pegawai->sisa_cuti} hari, diajukan: {$jumlahHari} hari.");
|
||||||
|
}
|
||||||
|
|
||||||
if (Carbon::parse($request->tanggal_mulai, 'Asia/Jakarta')->diffInDays(Carbon::now('Asia/Jakarta')) < 7) {
|
if (Carbon::parse($request->tanggal_mulai, 'Asia/Jakarta')->diffInDays(Carbon::now('Asia/Jakarta')) < 7) {
|
||||||
return back()->withInput()->with('error', 'Pengajuan Cuti minimal H-7!');
|
return back()->withInput()->with('error', 'Pengajuan Cuti minimal H-7!');
|
||||||
|
|
@ -207,7 +218,8 @@ public function approve($id)
|
||||||
if ($izin->id_jenis_izin == 2) {
|
if ($izin->id_jenis_izin == 2) {
|
||||||
$user = \App\Models\User::find($izin->id_user);
|
$user = \App\Models\User::find($izin->id_user);
|
||||||
$jumlahHari = Carbon::parse($izin->tanggal_mulai)->diffInDays(Carbon::parse($izin->tanggal_selesai)) + 1;
|
$jumlahHari = Carbon::parse($izin->tanggal_mulai)->diffInDays(Carbon::parse($izin->tanggal_selesai)) + 1;
|
||||||
$user->decrement('sisa_cuti', $jumlahHari);
|
$newSisa = max(0, ($user->sisa_cuti ?? 0) - $jumlahHari);
|
||||||
|
$user->update(['sisa_cuti' => $newSisa]);
|
||||||
}
|
}
|
||||||
|
|
||||||
DB::commit();
|
DB::commit();
|
||||||
|
|
|
||||||
|
|
@ -64,10 +64,13 @@ public function show($id)
|
||||||
$canApprove = false;
|
$canApprove = false;
|
||||||
$tahapApproval = null;
|
$tahapApproval = null;
|
||||||
|
|
||||||
if ($surat->status_surat === 'menunggu_manajer' && ($user->roles->contains('nama_role', 'manajer') || $isGlobalAdmin)) {
|
// Cegah user yang sama menandatangani di dua tahap berbeda
|
||||||
|
$sudahApprove = $surat->approvals->contains('id_approver', $user->id);
|
||||||
|
|
||||||
|
if (!$sudahApprove && $surat->status_surat === 'menunggu_manajer' && ($user->roles->contains('nama_role', 'manajer') || $isGlobalAdmin)) {
|
||||||
$canApprove = true;
|
$canApprove = true;
|
||||||
$tahapApproval = 1;
|
$tahapApproval = 1;
|
||||||
} elseif ($surat->status_surat === 'menunggu_hrd' && ($user->roles->contains('nama_role', 'hrd') || $isGlobalAdmin)) {
|
} elseif (!$sudahApprove && $surat->status_surat === 'menunggu_hrd' && ($user->roles->contains('nama_role', 'hrd') || $isGlobalAdmin)) {
|
||||||
$canApprove = true;
|
$canApprove = true;
|
||||||
$tahapApproval = 2;
|
$tahapApproval = 2;
|
||||||
}
|
}
|
||||||
|
|
@ -95,6 +98,15 @@ public function approve(Request $request, $id)
|
||||||
|
|
||||||
$tahap = null;
|
$tahap = null;
|
||||||
|
|
||||||
|
// Cegah approver yang sama menandatangani di dua posisi berbeda
|
||||||
|
$sudahApprove = ApprovalSurat::where('id_surat', $surat->id_surat)
|
||||||
|
->where('id_approver', $user->id)
|
||||||
|
->exists();
|
||||||
|
|
||||||
|
if ($sudahApprove) {
|
||||||
|
return redirect()->back()->with('error', 'Anda sudah menyetujui surat ini sebelumnya dan tidak dapat menandatangani di posisi lain.');
|
||||||
|
}
|
||||||
|
|
||||||
if ($surat->status_surat === 'menunggu_manajer' && ($user->roles->contains('nama_role', 'manajer') || $isGlobalAdmin)) {
|
if ($surat->status_surat === 'menunggu_manajer' && ($user->roles->contains('nama_role', 'manajer') || $isGlobalAdmin)) {
|
||||||
$tahap = 1;
|
$tahap = 1;
|
||||||
} elseif ($surat->status_surat === 'menunggu_hrd' && ($user->roles->contains('nama_role', 'hrd') || $isGlobalAdmin)) {
|
} elseif ($surat->status_surat === 'menunggu_hrd' && ($user->roles->contains('nama_role', 'hrd') || $isGlobalAdmin)) {
|
||||||
|
|
@ -176,6 +188,15 @@ public function reject(Request $request, $id)
|
||||||
|
|
||||||
$tahap = null;
|
$tahap = null;
|
||||||
|
|
||||||
|
// Cegah approver yang sama menandatangani di dua posisi berbeda
|
||||||
|
$sudahApprove = ApprovalSurat::where('id_surat', $surat->id_surat)
|
||||||
|
->where('id_approver', $user->id)
|
||||||
|
->exists();
|
||||||
|
|
||||||
|
if ($sudahApprove) {
|
||||||
|
return redirect()->back()->with('error', 'Anda sudah menyetujui surat ini sebelumnya dan tidak dapat menolak di posisi lain.');
|
||||||
|
}
|
||||||
|
|
||||||
if ($surat->status_surat === 'menunggu_manajer' && ($user->roles->contains('nama_role', 'manajer') || $isGlobalAdmin)) {
|
if ($surat->status_surat === 'menunggu_manajer' && ($user->roles->contains('nama_role', 'manajer') || $isGlobalAdmin)) {
|
||||||
$tahap = 1;
|
$tahap = 1;
|
||||||
} elseif ($surat->status_surat === 'menunggu_hrd' && ($user->roles->contains('nama_role', 'hrd') || $isGlobalAdmin)) {
|
} elseif ($surat->status_surat === 'menunggu_hrd' && ($user->roles->contains('nama_role', 'hrd') || $isGlobalAdmin)) {
|
||||||
|
|
@ -258,7 +279,8 @@ private function prosesPersetujuanFinal(SuratIzin $surat)
|
||||||
if ($izin->id_jenis_izin == 2) {
|
if ($izin->id_jenis_izin == 2) {
|
||||||
$user = \App\Models\User::find($izin->id_user, ['*']);
|
$user = \App\Models\User::find($izin->id_user, ['*']);
|
||||||
$jumlahHari = Carbon::parse($izin->tanggal_mulai)->diffInDays(Carbon::parse($izin->tanggal_selesai)) + 1;
|
$jumlahHari = Carbon::parse($izin->tanggal_mulai)->diffInDays(Carbon::parse($izin->tanggal_selesai)) + 1;
|
||||||
$user->decrement('sisa_cuti', $jumlahHari);
|
$newSisa = max(0, ($user->sisa_cuti ?? 0) - $jumlahHari);
|
||||||
|
$user->update(['sisa_cuti' => $newSisa]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -94,4 +94,15 @@ public function user()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(User::class, 'id_user');
|
return $this->belongsTo(User::class, 'id_user');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function status()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(StatusPengajuan::class, 'id_status', 'id_status');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function jadwal()
|
||||||
|
{
|
||||||
|
return $this->hasOne(JadwalKerja::class, 'id_user', 'id_user')
|
||||||
|
->whereColumn('jadwal_kerja.tanggal', 'presensi.tanggal');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -20,6 +20,7 @@ public function run(): void
|
||||||
JenisIzinSeeder::class,
|
JenisIzinSeeder::class,
|
||||||
StatusPengajuanSeeder::class,
|
StatusPengajuanSeeder::class,
|
||||||
JenisPenguranganSeeder::class,
|
JenisPenguranganSeeder::class,
|
||||||
|
JenisKompensasiSeeder::class,
|
||||||
|
|
||||||
// 4. Data dummy (opsional, comment jika tidak diperlukan di production)
|
// 4. Data dummy (opsional, comment jika tidak diperlukan di production)
|
||||||
// PresensiSeeder::class,
|
// PresensiSeeder::class,
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,16 @@ class JenisIzinSeeder extends Seeder
|
||||||
{
|
{
|
||||||
public function run(): void
|
public function run(): void
|
||||||
{
|
{
|
||||||
$types = ['Sakit', 'Cuti', 'Izin'];
|
$types = [
|
||||||
|
['id_jenis_izin' => 1, 'nama_izin' => 'Sakit'],
|
||||||
|
['id_jenis_izin' => 2, 'nama_izin' => 'Cuti'],
|
||||||
|
['id_jenis_izin' => 3, 'nama_izin' => 'Izin'],
|
||||||
|
];
|
||||||
|
|
||||||
foreach ($types as $type) {
|
foreach ($types as $type) {
|
||||||
DB::table('jenis_izin')->updateOrInsert(
|
DB::table('jenis_izin')->updateOrInsert(
|
||||||
['nama_izin' => $type],
|
['id_jenis_izin' => $type['id_jenis_izin']],
|
||||||
['updated_at' => now(), 'created_at' => now()]
|
['nama_izin' => $type['nama_izin'], 'updated_at' => now(), 'created_at' => now()]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use App\Models\JenisKompensasi;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
|
class JenisKompensasiSeeder extends Seeder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the database seeds.
|
||||||
|
*/
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
$kompensasi = [
|
||||||
|
['id_kompensasi' => 1, 'nama_kompensasi' => 'Uang Lembur'],
|
||||||
|
['id_kompensasi' => 2, 'nama_kompensasi' => 'Tambahan Poin'],
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($kompensasi as $k) {
|
||||||
|
JenisKompensasi::updateOrCreate(
|
||||||
|
['id_kompensasi' => $k['id_kompensasi']],
|
||||||
|
['nama_kompensasi' => $k['nama_kompensasi']]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -11,20 +11,33 @@ class PresensiStatusSeeder extends Seeder
|
||||||
public function run(): void
|
public function run(): void
|
||||||
{
|
{
|
||||||
$statuses = [
|
$statuses = [
|
||||||
'Tepat Waktu',
|
['id_status' => 1, 'nama_status' => 'Tepat Waktu'],
|
||||||
'Terlambat',
|
['id_status' => 2, 'nama_status' => 'Terlambat'],
|
||||||
'Izin',
|
['id_status' => 3, 'nama_status' => 'Izin'],
|
||||||
'Sakit',
|
['id_status' => 4, 'nama_status' => 'Sakit'],
|
||||||
'Alpha',
|
['id_status' => 5, 'nama_status' => 'Alpha'],
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($statuses as $status) {
|
foreach ($statuses as $status) {
|
||||||
DB::table('status_presensi')->updateOrInsert(
|
DB::table('status_presensi')->updateOrInsert(
|
||||||
['nama_status' => $status],
|
['id_status' => $status['id_status']],
|
||||||
['created_at' => now(), 'updated_at' => now()]
|
['nama_status' => $status['nama_status'], 'created_at' => now(), 'updated_at' => now()]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->command->info('Tabel status_presensi berhasil diisi!');
|
$validasiStatuses = [
|
||||||
|
['id_status' => 1, 'nama_status' => 'Valid'],
|
||||||
|
['id_status' => 2, 'nama_status' => 'Pending'],
|
||||||
|
['id_status' => 3, 'nama_status' => 'Ditolak'],
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($validasiStatuses as $status) {
|
||||||
|
DB::table('status_validasi_presensi')->updateOrInsert(
|
||||||
|
['id_status' => $status['id_status']],
|
||||||
|
['nama_status' => $status['nama_status'], 'created_at' => now(), 'updated_at' => now()]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->command->info('Tabel status_presensi & status_validasi_presensi berhasil diisi!');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,16 @@ class StatusPengajuanSeeder extends Seeder
|
||||||
{
|
{
|
||||||
public function run(): void
|
public function run(): void
|
||||||
{
|
{
|
||||||
$statuses = ['Disetujui', 'Pending', 'Ditolak'];
|
$statuses = [
|
||||||
|
['id_status' => 1, 'nama_status' => 'Pending'],
|
||||||
|
['id_status' => 2, 'nama_status' => 'Disetujui'],
|
||||||
|
['id_status' => 3, 'nama_status' => 'Ditolak'],
|
||||||
|
];
|
||||||
|
|
||||||
foreach ($statuses as $status) {
|
foreach ($statuses as $status) {
|
||||||
DB::table('status_pengajuan')->updateOrInsert(
|
DB::table('status_pengajuan')->updateOrInsert(
|
||||||
['nama_status' => $status],
|
['id_status' => $status['id_status']],
|
||||||
['updated_at' => now(), 'created_at' => now()]
|
['nama_status' => $status['nama_status'], 'updated_at' => now(), 'created_at' => now()]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,18 +11,10 @@
|
||||||
|
|
||||||
<div class="p-6 border-b border-slate-100 flex flex-col md:flex-row md:items-center justify-between gap-4">
|
<div class="p-6 border-b border-slate-100 flex flex-col md:flex-row md:items-center justify-between gap-4">
|
||||||
<form action="{{ route('cuti.index') }}" method="GET" class="w-full md:w-1/3">
|
<form action="{{ route('cuti.index') }}" method="GET" class="w-full md:w-1/3">
|
||||||
<div class="relative">
|
<x-search-input name="search" :value="$search" placeholder="Cari NIK atau Nama..." class="!mb-0 h-10" />
|
||||||
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
|
||||||
<svg class="h-5 w-5 text-slate-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<input type="text" name="search" value="{{ $search }}" placeholder="Cari NIK atau Nama..."
|
|
||||||
class="block w-full pl-10 pr-3 py-2 border border-slate-300 rounded-lg focus:ring-primary focus:border-primary sm:text-sm transition-colors">
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<button onclick="toggleModal('modalReset')" 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">
|
<button x-data @click="$dispatch('open-modal', 'reset-cuti')" 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">
|
||||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<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="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
@ -31,11 +23,11 @@ class="block w-full pl-10 pr-3 py-2 border border-slate-300 rounded-lg focus:rin
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<x-table>
|
<x-table>
|
||||||
<x-slot name="header">
|
<x-slot:header>
|
||||||
<th class="px-6 py-4 text-xs font-bold text-slate-500 uppercase tracking-wider text-left">Pegawai</th>
|
<th class="px-6 py-4 text-xs font-bold text-slate-500 uppercase tracking-wider text-left">Pegawai</th>
|
||||||
<th class="px-6 py-4 text-xs font-bold text-slate-500 uppercase tracking-wider text-center">Sisa Cuti</th>
|
<th class="px-6 py-4 text-xs font-bold text-slate-500 uppercase tracking-wider text-center">Sisa Cuti</th>
|
||||||
<th class="px-6 py-4 text-xs font-bold text-slate-500 uppercase tracking-wider text-right">Aksi</th>
|
<th class="px-6 py-4 text-xs font-bold text-slate-500 uppercase tracking-wider text-right">Aksi</th>
|
||||||
</x-slot>
|
</x-slot:header>
|
||||||
|
|
||||||
@forelse ($pegawai as $p)
|
@forelse ($pegawai as $p)
|
||||||
<tr class="hover:bg-slate-50 transition-colors group">
|
<tr class="hover:bg-slate-50 transition-colors group">
|
||||||
|
|
@ -65,147 +57,79 @@ class="block w-full pl-10 pr-3 py-2 border border-slate-300 rounded-lg focus:rin
|
||||||
@endforelse
|
@endforelse
|
||||||
</x-table>
|
</x-table>
|
||||||
|
|
||||||
@if($pegawai->hasPages())
|
<x-pagination :paginator="$pegawai" />
|
||||||
<div class="border-t border-slate-100 p-4">
|
|
||||||
{{ $pegawai->links() }}
|
|
||||||
</div>
|
|
||||||
@endif
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="modalEdit" class="fixed inset-0 z-[100] hidden overflow-y-auto" aria-labelledby="modal-title" role="dialog" aria-modal="true">
|
<x-modal name="edit-cuti" title="Edit Sisa Cuti">
|
||||||
<div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:p-0">
|
<form id="formEditCuti" method="POST" action="" class="space-y-4">
|
||||||
<div class="fixed inset-0 bg-gray-500/50 transition-opacity" aria-hidden="true" onclick="toggleModal('modalEdit')"></div>
|
@csrf
|
||||||
<div class="relative bg-white rounded-2xl text-left overflow-hidden shadow-2xl transform transition-all sm:my-8 sm:max-w-md w-full border border-slate-200">
|
@method('PUT')
|
||||||
|
|
||||||
<form id="formEditCuti" method="POST" action="">
|
<div>
|
||||||
@csrf
|
<label class="block text-sm font-semibold text-slate-700 mb-1">Pegawai</label>
|
||||||
@method('PUT')
|
<input type="text" id="editNama" disabled class="w-full bg-slate-50 border border-slate-200 rounded-lg px-3 py-2 text-slate-600 font-medium">
|
||||||
|
|
||||||
<div class="bg-white px-6 pt-6 pb-6">
|
|
||||||
<div class="flex items-center justify-between mb-5">
|
|
||||||
<h3 class="text-xl font-bold text-slate-900" id="modal-title">Edit Sisa Cuti</h3>
|
|
||||||
<button type="button" onclick="toggleModal('modalEdit')" class="text-slate-400 hover:text-slate-500">
|
|
||||||
<span class="sr-only">Close</span>
|
|
||||||
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="space-y-4">
|
|
||||||
<div>
|
|
||||||
<label class="block text-sm font-semibold text-slate-700 mb-1">Pegawai</label>
|
|
||||||
<input type="text" id="editNama" disabled class="w-full bg-slate-50 border border-slate-200 rounded-lg px-3 py-2 text-slate-600 font-medium">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<x-input label="Jumlah Sisa Cuti (Hari)" name="sisa_cuti" type="number" id="editSisa" required min="0" placeholder="0" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="p-3 bg-blue-50 text-blue-800 rounded-lg text-xs leading-relaxed border border-blue-100">
|
|
||||||
<span class="font-bold block mb-1">Catatan:</span>
|
|
||||||
Perubahan ini langsung memengaruhi jatah cuti pegawai yang dapat diajukan di aplikasi mobile. Pastikan jumlahnya tepat.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="bg-slate-50 px-6 py-4 flex flex-row-reverse border-t border-slate-100 gap-2">
|
|
||||||
<x-button type="submit" variant="primary" id="submitBtnEdit" class="w-full sm:w-auto">
|
|
||||||
Simpan Perubahan
|
|
||||||
</x-button>
|
|
||||||
<button type="button" class="w-full sm:w-auto inline-flex justify-center rounded-lg border border-slate-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-slate-700 hover:bg-slate-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary sm:text-sm" onclick="toggleModal('modalEdit')">
|
|
||||||
Batal
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
<div>
|
||||||
|
<x-input label="Jumlah Sisa Cuti (Hari)" name="sisa_cuti" type="number" id="editSisa" required min="0" placeholder="0" />
|
||||||
<div id="modalReset" class="fixed inset-0 z-[100] hidden overflow-y-auto" aria-labelledby="modal-title" role="dialog" aria-modal="true">
|
|
||||||
<div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:p-0">
|
|
||||||
<div class="fixed inset-0 bg-gray-500/50 transition-opacity" aria-hidden="true" onclick="toggleModal('modalReset')"></div>
|
|
||||||
<div class="relative bg-white rounded-2xl text-left overflow-hidden shadow-2xl transform transition-all sm:my-8 sm:max-w-lg w-full border border-slate-200">
|
|
||||||
|
|
||||||
<form method="POST" action="{{ route('cuti.reset') }}">
|
|
||||||
@csrf
|
|
||||||
|
|
||||||
<div class="bg-white px-6 pt-6 pb-6">
|
|
||||||
<div class="flex items-center justify-between mb-5">
|
|
||||||
<div class="flex items-center">
|
|
||||||
<div class="flex-shrink-0 flex items-center justify-center h-10 w-10 rounded-full bg-slate-100 mr-3">
|
|
||||||
<svg class="h-5 w-5 text-slate-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<h3 class="text-xl font-bold text-slate-900" id="modal-title">Reset Cuti Massal</h3>
|
|
||||||
</div>
|
|
||||||
<button type="button" onclick="toggleModal('modalReset')" class="text-slate-400 hover:text-slate-500">
|
|
||||||
<span class="sr-only">Close</span>
|
|
||||||
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="space-y-4">
|
|
||||||
<p class="text-sm text-slate-600">
|
|
||||||
Fungsi ini biasanya digunakan di awal tahun untuk memberikan jatah cuti baru ke <strong>seluruh pegawai</strong>. Apakah Anda yakin ingin menetapkan ulang sisa cuti massal?
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<x-input label="Jatah Cuti Baru (Hari)" name="jumlah_hari" type="number" required min="1" value="12" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="p-3 bg-amber-50 text-amber-800 rounded-lg text-xs leading-relaxed border border-amber-200 flex items-start">
|
|
||||||
<svg class="w-5 h-5 mr-2 flex-shrink-0 text-amber-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path></svg>
|
|
||||||
<div>
|
|
||||||
<span class="font-bold block mb-0.5">Peringatan:</span>
|
|
||||||
Aksi ini <strong>menimpa dan menghilangkan</strong> semua sisa cuti pegawai sebelumnya tanpa pandang bulu. Aksi ini tidak bisa dibatalkan!
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label class="block text-sm font-semibold text-slate-700 mb-1">
|
|
||||||
Ketik <span class="font-mono text-rose-600 bg-rose-50 px-1 rounded">RESET</span> untuk mengkonfirmasi
|
|
||||||
</label>
|
|
||||||
<input type="text" id="konfirmasiReset" placeholder="Ketik RESET di sini..."
|
|
||||||
oninput="toggleResetBtn(this.value)"
|
|
||||||
class="block w-full border border-slate-300 rounded-lg px-3 py-2 text-sm focus:ring-rose-500 focus:border-rose-500 transition-colors">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="bg-slate-50 px-6 py-4 flex flex-row-reverse border-t border-slate-100 gap-2">
|
|
||||||
<button type="submit" id="btnResetSubmit" disabled
|
|
||||||
class="w-full sm:w-auto inline-flex justify-center rounded-lg border border-transparent shadow-sm px-4 py-2 bg-rose-600 text-base font-medium text-white hover:bg-rose-700 disabled:opacity-40 disabled:cursor-not-allowed focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-rose-600 sm:text-sm transition-colors">
|
|
||||||
<span id="submitBtnReset">Ya, Reset Sekarang</span>
|
|
||||||
</button>
|
|
||||||
<button type="button" class="w-full sm:w-auto inline-flex justify-center rounded-lg border border-slate-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-slate-700 hover:bg-slate-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary sm:text-sm" onclick="toggleModal('modalReset')">
|
|
||||||
Batal
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
<div class="p-3 bg-blue-50 text-blue-800 rounded-lg text-xs leading-relaxed border border-blue-100">
|
||||||
|
<span class="font-bold block mb-1">Catatan:</span>
|
||||||
|
Perubahan ini langsung memengaruhi jatah cuti pegawai yang dapat diajukan di aplikasi mobile. Pastikan jumlahnya tepat.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex justify-end gap-3 pt-4 border-t border-slate-100">
|
||||||
|
<button type="button" x-data @click="$dispatch('close-modal', 'edit-cuti')"
|
||||||
|
class="px-4 py-2 text-sm font-medium text-slate-600 bg-white border border-slate-300 rounded-xl hover:bg-slate-50">Batal</button>
|
||||||
|
<button type="submit"
|
||||||
|
class="px-4 py-2 text-sm font-medium text-white bg-primary rounded-xl hover:bg-primary/90">Simpan Perubahan</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</x-modal>
|
||||||
|
|
||||||
|
<x-modal name="reset-cuti" title="Reset Cuti Massal">
|
||||||
|
<form method="POST" action="{{ route('cuti.reset') }}" class="space-y-4">
|
||||||
|
@csrf
|
||||||
|
|
||||||
|
<p class="text-sm text-slate-600">
|
||||||
|
Fungsi ini biasanya digunakan di awal tahun untuk memberikan jatah cuti baru ke <strong>seluruh pegawai</strong>. Apakah Anda yakin ingin menetapkan ulang sisa cuti massal?
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<x-input label="Jatah Cuti Baru (Hari)" name="jumlah_hari" type="number" required min="1" value="12" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="p-3 bg-amber-50 text-amber-800 rounded-lg text-xs leading-relaxed border border-amber-200 flex items-start">
|
||||||
|
<svg class="w-5 h-5 mr-2 flex-shrink-0 text-amber-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path></svg>
|
||||||
|
<div>
|
||||||
|
<span class="font-bold block mb-0.5">Peringatan:</span>
|
||||||
|
Aksi ini <strong>menimpa dan menghilangkan</strong> semua sisa cuti pegawai sebelumnya tanpa pandang bulu. Aksi ini tidak bisa dibatalkan!
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-slate-700 mb-1">
|
||||||
|
Ketik <span class="font-mono text-rose-600 bg-rose-50 px-1 rounded">RESET</span> untuk mengkonfirmasi
|
||||||
|
</label>
|
||||||
|
<input type="text" id="konfirmasiReset" placeholder="Ketik RESET di sini..."
|
||||||
|
oninput="toggleResetBtn(this.value)"
|
||||||
|
class="block w-full border border-slate-300 rounded-lg px-3 py-2 text-sm focus:ring-rose-500 focus:border-rose-500 transition-colors">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex justify-end gap-3 pt-4 border-t border-slate-100">
|
||||||
|
<button type="button" x-data @click="$dispatch('close-modal', 'reset-cuti')"
|
||||||
|
class="px-4 py-2 text-sm font-medium text-slate-600 bg-white border border-slate-300 rounded-xl hover:bg-slate-50">Batal</button>
|
||||||
|
<button type="submit" id="btnResetSubmit" disabled
|
||||||
|
class="px-4 py-2 text-sm font-medium text-white bg-rose-600 rounded-xl hover:bg-rose-700 disabled:opacity-40 disabled:cursor-not-allowed transition-colors">Ya, Reset Sekarang</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</x-modal>
|
||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
@section('script')
|
@section('script')
|
||||||
<script>
|
<script>
|
||||||
function toggleModal(modalID) {
|
|
||||||
document.getElementById(modalID).classList.toggle("hidden");
|
|
||||||
|
|
||||||
// Reset field konfirmasi reset saat modal ditutup
|
|
||||||
if (modalID === 'modalReset') {
|
|
||||||
const konfirmasi = document.getElementById('konfirmasiReset');
|
|
||||||
const btn = document.getElementById('btnResetSubmit');
|
|
||||||
if (konfirmasi) konfirmasi.value = '';
|
|
||||||
if (btn) btn.disabled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleResetBtn(value) {
|
function toggleResetBtn(value) {
|
||||||
const btn = document.getElementById('btnResetSubmit');
|
const btn = document.getElementById('btnResetSubmit');
|
||||||
btn.disabled = value !== 'RESET';
|
btn.disabled = value !== 'RESET';
|
||||||
|
|
@ -218,7 +142,7 @@ function openEditModal(id, nama, sisa) {
|
||||||
let form = document.getElementById('formEditCuti');
|
let form = document.getElementById('formEditCuti');
|
||||||
form.action = `/cuti/${id}`;
|
form.action = `/cuti/${id}`;
|
||||||
|
|
||||||
toggleModal('modalEdit');
|
window.dispatchEvent(new CustomEvent('open-modal', { detail: 'edit-cuti' }));
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@endsection
|
@endsection
|
||||||
|
|
|
||||||
|
|
@ -3,15 +3,9 @@
|
||||||
@section('title', 'Dashboard')
|
@section('title', 'Dashboard')
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
<div class="mb-8">
|
<x-page-header
|
||||||
<h1 class="text-2xl font-bold text-slate-800">
|
title="{{ Carbon\Carbon::now('Asia/Jakarta')->hour < 12 ? 'Selamat Pagi' : (Carbon\Carbon::now('Asia/Jakarta')->hour < 15 ? 'Selamat Siang' : 'Selamat Sore') }}, {{ Auth::user()->nama_lengkap }}! 👋"
|
||||||
{{ \Carbon\Carbon::now('Asia/Jakarta')->hour < 12 ? 'Selamat Pagi' : (\Carbon\Carbon::now('Asia/Jakarta')->hour < 15 ? 'Selamat Siang' : 'Selamat Sore') }},
|
subtitle="{{ Carbon\Carbon::now('Asia/Jakarta')->translatedFormat('l, d F Y') }}" />
|
||||||
{{ Auth::user()->nama_lengkap }}! 👋
|
|
||||||
</h1>
|
|
||||||
<p class="text-slate-500 mt-1 text-sm font-medium">
|
|
||||||
{{ \Carbon\Carbon::now('Asia/Jakarta')->translatedFormat('l, d F Y') }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-4 md:gap-6 mb-8">
|
<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-4 md:gap-6 mb-8">
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
@extends('layouts.app')
|
@extends('layouts.app')
|
||||||
|
|
||||||
@section('title', 'Generate Jadwal Kerja')
|
@section('title', 'Generate Jadwal Kerja')
|
||||||
|
|
||||||
|
|
@ -13,19 +13,75 @@
|
||||||
<form action="{{ route('jadwal.store') }}" method="POST">
|
<form action="{{ route('jadwal.store') }}" method="POST">
|
||||||
@csrf
|
@csrf
|
||||||
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-6">
|
<style>
|
||||||
<x-date-input label="Tanggal Mulai" name="tanggal_mulai" :value="now()->format('Y-m-d')" required />
|
input[type="date"]::-webkit-calendar-picker-indicator,
|
||||||
<x-date-input label="Tanggal Selesai" name="tanggal_selesai" :value="now()->addDays(6)->format('Y-m-d')"
|
input[type="date"]::-webkit-inner-spin-button {
|
||||||
required />
|
display: none;
|
||||||
<x-select label="Shift" name="id_shift" required>
|
-webkit-appearance: none;
|
||||||
<option value="">-- Pilih Shift --</option>
|
}
|
||||||
@foreach($shifts as $shift)
|
</style>
|
||||||
<option value="{{ $shift->id_shift }}">{{ $shift->nama_shift }}
|
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.5rem;" class="mb-6">
|
||||||
({{ \Carbon\Carbon::parse($shift->jam_mulai)->format('H:i') }} -
|
<div class="space-y-1">
|
||||||
{{ \Carbon\Carbon::parse($shift->jam_selesai)->format('H:i') }})
|
<label class="text-sm font-semibold text-slate-700">Tanggal Mulai</label>
|
||||||
</option>
|
<div class="relative">
|
||||||
@endforeach
|
<input type="date" name="tanggal_mulai" value="{{ old('tanggal_mulai', now()->format('Y-m-d')) }}" required
|
||||||
</x-select>
|
onclick="try{this.showPicker()}catch(e){}"
|
||||||
|
onfocus="try{this.showPicker()}catch(e){}"
|
||||||
|
class="w-full pl-4 pr-10 py-2.5 bg-white border border-slate-300 rounded-xl text-sm outline-none focus:ring-2 focus:ring-[#130F26] focus:border-[#130F26] transition-all text-slate-600 cursor-pointer appearance-none"
|
||||||
|
style="-webkit-appearance: none;">
|
||||||
|
<div class="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none text-slate-500">
|
||||||
|
<svg class="w-5 h-5" 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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@error('tanggal_mulai')
|
||||||
|
<p class="text-red-500 text-xs mt-1">{{ $message }}</p>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="space-y-1">
|
||||||
|
<label class="text-sm font-semibold text-slate-700">Tanggal Selesai</label>
|
||||||
|
<div class="relative">
|
||||||
|
<input type="date" name="tanggal_selesai" value="{{ old('tanggal_selesai', now()->addDays(6)->format('Y-m-d')) }}" required
|
||||||
|
onclick="try{this.showPicker()}catch(e){}"
|
||||||
|
onfocus="try{this.showPicker()}catch(e){}"
|
||||||
|
class="w-full pl-4 pr-10 py-2.5 bg-white border border-slate-300 rounded-xl text-sm outline-none focus:ring-2 focus:ring-[#130F26] focus:border-[#130F26] transition-all text-slate-600 cursor-pointer appearance-none"
|
||||||
|
style="-webkit-appearance: none;">
|
||||||
|
<div class="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none text-slate-500">
|
||||||
|
<svg class="w-5 h-5" 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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@error('tanggal_selesai')
|
||||||
|
<p class="text-red-500 text-xs mt-1">{{ $message }}</p>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="space-y-1">
|
||||||
|
<label class="text-sm font-semibold text-slate-700">Shift</label>
|
||||||
|
<div class="relative">
|
||||||
|
<select name="id_shift" id="id_shift" required
|
||||||
|
class="w-full pl-4 pr-10 py-2.5 bg-white border border-slate-300 rounded-xl text-sm outline-none focus:ring-2 focus:ring-[#130F26] focus:border-[#130F26] transition-all text-slate-600 cursor-pointer appearance-none">
|
||||||
|
<option value="">-- Pilih Shift --</option>
|
||||||
|
@foreach($shifts as $shift)
|
||||||
|
<option value="{{ $shift->id_shift }}">{{ $shift->nama_shift }}
|
||||||
|
({{ \Carbon\Carbon::parse($shift->jam_mulai)->format('H:i') }} -
|
||||||
|
{{ \Carbon\Carbon::parse($shift->jam_selesai)->format('H:i') }})
|
||||||
|
</option>
|
||||||
|
@endforeach
|
||||||
|
</select>
|
||||||
|
<div class="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none text-slate-500">
|
||||||
|
<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="M19 9l-7 7-7-7"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@error('id_shift')
|
||||||
|
<p class="text-red-500 text-xs mt-1">{{ $message }}</p>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-6">
|
<div class="mb-6">
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
@extends('layouts.app')
|
@extends('layouts.app')
|
||||||
|
|
||||||
@section('title', 'Lembur')
|
@section('title', 'Lembur')
|
||||||
|
|
||||||
|
|
@ -26,22 +26,22 @@
|
||||||
</x-slot:header>
|
</x-slot:header>
|
||||||
|
|
||||||
@forelse ($lembur as $index => $item)
|
@forelse ($lembur as $index => $item)
|
||||||
<tr class="hover:bg-gray-50 transition-colors">
|
<tr class="hover:bg-slate-50 transition-colors">
|
||||||
<td class="px-6 py-4 text-sm text-gray-700">
|
<td class="px-6 py-4 text-sm text-slate-700">
|
||||||
{{ $lembur->firstItem() + $index }}
|
{{ $lembur->firstItem() + $index }}
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-4 text-sm text-gray-700">
|
<td class="px-6 py-4 text-sm text-slate-700">
|
||||||
{{ \Carbon\Carbon::parse($item->tanggal_lembur)->translatedFormat('d F Y') }}
|
{{ \Carbon\Carbon::parse($item->tanggal_lembur)->translatedFormat('d F Y') }}
|
||||||
<div class="text-xs text-gray-500">
|
<div class="text-xs text-slate-500">
|
||||||
{{ \Carbon\Carbon::parse($item->jam_mulai)->format('H:i') }} -
|
{{ \Carbon\Carbon::parse($item->jam_mulai)->format('H:i') }} -
|
||||||
{{ \Carbon\Carbon::parse($item->jam_selesai)->format('H:i') }}
|
{{ \Carbon\Carbon::parse($item->jam_selesai)->format('H:i') }}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-4">
|
<td class="px-6 py-4">
|
||||||
<div class="text-sm font-medium text-gray-900">{{ $item->user->nama_lengkap ?? '-' }}</div>
|
<div class="text-sm font-medium text-slate-900">{{ $item->user->nama_lengkap ?? '-' }}</div>
|
||||||
<div class="text-xs text-gray-500">{{ $item->user->jabatan->nama_jabatan ?? '' }}</div>
|
<div class="text-xs text-slate-500">{{ $item->user->jabatan->nama_jabatan ?? '' }}</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-4 text-sm text-gray-700">
|
<td class="px-6 py-4 text-sm text-slate-700">
|
||||||
{{ $item->durasi_menit }} Menit
|
{{ $item->durasi_menit }} Menit
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-4">
|
<td class="px-6 py-4">
|
||||||
|
|
@ -51,7 +51,7 @@
|
||||||
<x-badge color="green">💵 Uang</x-badge>
|
<x-badge color="green">💵 Uang</x-badge>
|
||||||
@endif
|
@endif
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-4 text-sm text-gray-700">
|
<td class="px-6 py-4 text-sm text-slate-700">
|
||||||
{{ Str::limit($item->keterangan, 30) }}
|
{{ Str::limit($item->keterangan, 30) }}
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-4">
|
<td class="px-6 py-4">
|
||||||
|
|
@ -91,7 +91,7 @@ class="text-red-600 hover:text-red-900 bg-red-50 hover:bg-red-100 p-2 rounded-lg
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@else
|
@else
|
||||||
<span class="text-xs text-gray-400 italic">Selesai</span>
|
<span class="text-xs text-slate-400 italic">Selesai</span>
|
||||||
@endif
|
@endif
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
@ -114,7 +114,7 @@ class="text-red-600 hover:text-red-900 bg-red-50 hover:bg-red-100 p-2 rounded-lg
|
||||||
placeholder="Jelaskan alasan kenapa pengajuan ini ditolak..." required rows="3" />
|
placeholder="Jelaskan alasan kenapa pengajuan ini ditolak..." required rows="3" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex justify-end gap-3 pt-4 border-t border-gray-100 mt-4">
|
<div class="flex justify-end gap-3 pt-4 border-t border-slate-100 mt-4">
|
||||||
<x-button type="button" variant="secondary" x-data
|
<x-button type="button" variant="secondary" x-data
|
||||||
@click="$dispatch('close-modal', 'reject-modal')">Batal</x-button>
|
@click="$dispatch('close-modal', 'reject-modal')">Batal</x-button>
|
||||||
<x-button type="submit" class="bg-red-600 hover:bg-red-700 text-white">Tolak Pengajuan</x-button>
|
<x-button type="submit" class="bg-red-600 hover:bg-red-700 text-white">Tolak Pengajuan</x-button>
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,7 @@
|
||||||
@section('title', 'Notifikasi')
|
@section('title', 'Notifikasi')
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
<div class="mb-6 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
|
<x-page-header title="Notifikasi" subtitle="Daftar semua notifikasi Anda">
|
||||||
<div>
|
|
||||||
<h1 class="text-2xl font-bold text-slate-800">Notifikasi</h1>
|
|
||||||
<p class="text-sm text-slate-500 mt-1">Daftar semua notifikasi Anda</p>
|
|
||||||
</div>
|
|
||||||
@if($unreadCount > 0)
|
@if($unreadCount > 0)
|
||||||
<form id="mark-all-read-form" onsubmit="return false;">
|
<form id="mark-all-read-form" onsubmit="return false;">
|
||||||
<x-button type="button" variant="secondary" onclick="markAllReadPage()">
|
<x-button type="button" variant="secondary" onclick="markAllReadPage()">
|
||||||
|
|
@ -15,7 +11,7 @@
|
||||||
</x-button>
|
</x-button>
|
||||||
</form>
|
</form>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</x-page-header>
|
||||||
|
|
||||||
<div class="bg-white rounded-2xl border border-slate-200/60 shadow-sm overflow-hidden">
|
<div class="bg-white rounded-2xl border border-slate-200/60 shadow-sm overflow-hidden">
|
||||||
@forelse($notifikasi as $item)
|
@forelse($notifikasi as $item)
|
||||||
|
|
@ -62,11 +58,7 @@
|
||||||
@endforelse
|
@endforelse
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if($notifikasi->hasPages())
|
<x-pagination :paginator="$notifikasi" />
|
||||||
<div class="mt-6">
|
|
||||||
{{ $notifikasi->links() }}
|
|
||||||
</div>
|
|
||||||
@endif
|
|
||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
@section('script')
|
@section('script')
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
@extends('layouts.app')
|
@extends('layouts.app')
|
||||||
|
|
||||||
@section('title', 'Data Karyawan')
|
@section('title', 'Data Karyawan')
|
||||||
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
<div class="max-w-7xl mx-auto space-y-8">
|
<div class="max-w-7xl mx-auto space-y-8">
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h1 class="text-2xl font-bold text-slate-800 mb-6">Data Karyawan</h1>
|
<x-page-header title="Data Karyawan" subtitle="Kelola data pegawai perusahaan." class="mb-6" />
|
||||||
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||||
@foreach($stats['kantor_list'] as $kantor)
|
@foreach($stats['kantor_list'] as $kantor)
|
||||||
|
|
@ -201,7 +201,3 @@ class="p-2 text-blue-500 hover:bg-blue-50 rounded-lg transition" title="Lihat De
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
@section('script')
|
|
||||||
<script src="{{ asset('js/notifications.js') }}"></script>
|
|
||||||
@endsection
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
@extends('layouts.app')
|
@extends('layouts.app')
|
||||||
|
|
||||||
@section('title', 'Detail Pegawai')
|
@section('title', 'Detail Pegawai')
|
||||||
|
|
||||||
|
|
@ -104,6 +104,23 @@ class="w-24 h-24 rounded-full bg-slate-100 flex items-center justify-center text
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-white rounded-2xl shadow-sm border border-slate-200 p-6">
|
||||||
|
<h4 class="text-sm font-bold text-slate-800 uppercase tracking-wider mb-4">Aksi Cepat</h4>
|
||||||
|
<div class="space-y-3">
|
||||||
|
<button type="button"
|
||||||
|
onclick="confirmAction(event, 'reset-password-form', 'Password akan direset ke default (Mpg123!). Pegawai harus login ulang setelah reset.', '#f59e0b', 'Ya, Reset Password')"
|
||||||
|
class="w-full flex items-center gap-3 px-4 py-3 bg-amber-50 text-amber-700 border border-amber-200 rounded-xl hover:bg-amber-100 transition text-sm font-semibold">
|
||||||
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z"></path>
|
||||||
|
</svg>
|
||||||
|
Reset Password
|
||||||
|
</button>
|
||||||
|
<form id="reset-password-form" action="{{ route('pegawai.reset-password', $pegawai->id) }}" method="POST" class="hidden">
|
||||||
|
@csrf
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="lg:col-span-2 space-y-6">
|
<div class="lg:col-span-2 space-y-6">
|
||||||
|
|
@ -238,11 +255,11 @@ class="inline-flex py-1 px-3 rounded-full text-xs font-bold bg-red-100 text-red-
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-4 text-sm text-slate-600 text-center whitespace-nowrap">
|
<td class="px-6 py-4 text-sm text-slate-600 text-center whitespace-nowrap">
|
||||||
{{ $presensi->jadwal->shift->nama_shift ?? 'Shift Default' }}<br>
|
{{ $presensi->jadwal->shift->nama_shift ?? 'Shift Default' }}<br>
|
||||||
<span class="text-xs text-slate-400">{{ substr($presensi->jadwal->jam_masuk ?? '08:00', 0, 5) }} - {{ substr($presensi->jadwal->jam_pulang ?? '17:00', 0, 5) }}</span>
|
<span class="text-xs text-slate-400">{{ substr($presensi->jadwal->shift->jam_mulai ?? '08:00', 0, 5) }} - {{ substr($presensi->jadwal->shift->jam_selesai ?? '17:00', 0, 5) }}</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-4 text-center whitespace-nowrap">
|
<td class="px-6 py-4 text-center whitespace-nowrap">
|
||||||
<div class="flex flex-col text-sm items-center justify-center">
|
<div class="flex flex-col text-sm items-center justify-center">
|
||||||
<span class="font-mono font-bold {{ $presensi->jam_masuk > ($presensi->jadwal->jam_masuk ?? '08:00') ? 'text-red-600' : 'text-emerald-600' }}">
|
<span class="font-mono font-bold {{ $presensi->jam_masuk > ($presensi->jadwal->shift->jam_mulai ?? '08:00') ? 'text-red-600' : 'text-emerald-600' }}">
|
||||||
{{ $presensi->jam_masuk ? \Carbon\Carbon::parse($presensi->jam_masuk)->format('H:i') : '--:--' }}
|
{{ $presensi->jam_masuk ? \Carbon\Carbon::parse($presensi->jam_masuk)->format('H:i') : '--:--' }}
|
||||||
</span>
|
</span>
|
||||||
<span class="text-xs text-slate-400 font-mono mt-0.5">s/d</span>
|
<span class="text-xs text-slate-400 font-mono mt-0.5">s/d</span>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
@extends('layouts.app')
|
@extends('layouts.app')
|
||||||
|
|
||||||
|
@section('title', 'Manajemen Hak Akses')
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
<div class="space-y-6">
|
<div class="space-y-6">
|
||||||
<div>
|
<x-page-header title="Manajemen Hak Akses" subtitle="Konfigurasi izin spesifik untuk setiap peran pengguna." />
|
||||||
<h1 class="text-2xl font-bold text-slate-800">Manajemen Hak Akses</h1>
|
|
||||||
<p class="text-sm text-slate-500">Konfigurasi izin spesifik untuk setiap peran pengguna.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="grid grid-cols-1 lg:grid-cols-4 gap-6">
|
<div class="grid grid-cols-1 lg:grid-cols-4 gap-6">
|
||||||
<div class="lg:col-span-1 space-y-6">
|
<div class="lg:col-span-1 space-y-6">
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,10 @@
|
||||||
@extends('layouts.app')
|
@extends('layouts.app')
|
||||||
|
|
||||||
@section('title', 'Riwayat Tukar Shift')
|
@section('title', 'Riwayat Tukar Shift')
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
<div class="space-y-6">
|
<div class="space-y-6">
|
||||||
|
|
||||||
<div class="flex justify-between items-center sm:hidden mb-4">
|
|
||||||
<h1 class="text-2xl font-bold text-slate-800">Riwayat Tukar Shift</h1>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<x-page-header title="Riwayat Tukar Shift" subtitle="Daftar log eksekusi penukaran jadwal kerja antar pegawai.">
|
<x-page-header title="Riwayat Tukar Shift" subtitle="Daftar log eksekusi penukaran jadwal kerja antar pegawai.">
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<x-back-button href="{{ route('jadwal.index') }}" />
|
<x-back-button href="{{ route('jadwal.index') }}" />
|
||||||
|
|
@ -24,7 +20,7 @@ class="px-5 py-2.5 bg-primary text-white text-sm font-semibold justify-center ro
|
||||||
|
|
||||||
<div class="bg-white rounded-2xl shadow-sm border border-slate-200 overflow-hidden">
|
<div class="bg-white rounded-2xl shadow-sm border border-slate-200 overflow-hidden">
|
||||||
<x-table>
|
<x-table>
|
||||||
<x-slot name="header">
|
<x-slot:header>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col" class="px-6 py-4 text-left font-semibold text-slate-600">Terjadi Pada</th>
|
<th scope="col" class="px-6 py-4 text-left font-semibold text-slate-600">Terjadi Pada</th>
|
||||||
<th scope="col" class="px-6 py-4 text-left font-semibold text-slate-600">Pegawai 1</th>
|
<th scope="col" class="px-6 py-4 text-left font-semibold text-slate-600">Pegawai 1</th>
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@
|
||||||
|
|
||||||
Route::get('/presensi', [PresensiController::class, 'index']);
|
Route::get('/presensi', [PresensiController::class, 'index']);
|
||||||
Route::post('/presensi', [PresensiController::class, 'store']);
|
Route::post('/presensi', [PresensiController::class, 'store']);
|
||||||
|
Route::post('/presensi/check-radius', [PresensiController::class, 'checkRadius']);
|
||||||
Route::get('/presensi/history', [PresensiController::class, 'history']);
|
Route::get('/presensi/history', [PresensiController::class, 'history']);
|
||||||
Route::post('/presensi/{id}/resubmit', [PresensiController::class, 'resubmit']);
|
Route::post('/presensi/{id}/resubmit', [PresensiController::class, 'resubmit']);
|
||||||
|
|
||||||
|
|
@ -50,6 +51,7 @@
|
||||||
Route::get('/lembur/history', [LemburController::class, 'history']);
|
Route::get('/lembur/history', [LemburController::class, 'history']);
|
||||||
|
|
||||||
Route::get('/pengumuman', [\App\Http\Controllers\Api\PengumumanApiController::class, 'index']);
|
Route::get('/pengumuman', [\App\Http\Controllers\Api\PengumumanApiController::class, 'index']);
|
||||||
|
Route::get('/pengumuman/{id}', [\App\Http\Controllers\Api\PengumumanApiController::class, 'show']);
|
||||||
|
|
||||||
Route::get('/user', [AuthController::class, 'user']);
|
Route::get('/user', [AuthController::class, 'user']);
|
||||||
|
|
||||||
|
|
@ -70,4 +72,11 @@
|
||||||
});
|
});
|
||||||
Route::post('/device-token', [\App\Http\Controllers\Api\NotifikasiApiController::class, 'saveDeviceToken']);
|
Route::post('/device-token', [\App\Http\Controllers\Api\NotifikasiApiController::class, 'saveDeviceToken']);
|
||||||
|
|
||||||
|
Route::get('/kompensasi', function () {
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'data' => \App\Models\JenisKompensasi::all(),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,7 @@
|
||||||
Route::resource('shift', \App\Http\Controllers\ShiftController::class)->except(['create', 'edit', 'show']);
|
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('jadwal', \App\Http\Controllers\JadwalController::class)->except(['edit', 'show']);
|
||||||
Route::resource('pegawai', PegawaiController::class);
|
Route::resource('pegawai', PegawaiController::class);
|
||||||
|
Route::post('/pegawai/{id}/reset-password', [PegawaiController::class, 'resetPassword'])->name('pegawai.reset-password');
|
||||||
Route::post('/hari-libur/sync', [\App\Http\Controllers\HariLiburController::class, 'syncHolidays'])->name('hari-libur.sync');
|
Route::post('/hari-libur/sync', [\App\Http\Controllers\HariLiburController::class, 'syncHolidays'])->name('hari-libur.sync');
|
||||||
Route::resource('hari-libur', \App\Http\Controllers\HariLiburController::class)->except(['create', 'edit', 'show']);
|
Route::resource('hari-libur', \App\Http\Controllers\HariLiburController::class)->except(['create', 'edit', 'show']);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue