perbaikan
This commit is contained in:
parent
1f881a6c69
commit
0216c572cf
|
|
@ -110,6 +110,10 @@ public function registerEvents(): array
|
|||
$lastRow = $this->data->count() + 3;
|
||||
$dataRange = "A3:I{$lastRow}";
|
||||
|
||||
$sheet->getParent()->getDefaultStyle()->getFont()
|
||||
->setName('Calibri')
|
||||
->setSize(10);
|
||||
|
||||
// Merge judul
|
||||
$sheet->mergeCells('A1:I1');
|
||||
|
||||
|
|
|
|||
|
|
@ -150,6 +150,10 @@ public function registerEvents(): array
|
|||
$lastRow = count($this->pegawai) + 6;
|
||||
$dataStart = 7;
|
||||
|
||||
$sheet->getParent()->getDefaultStyle()->getFont()
|
||||
->setName('Calibri')
|
||||
->setSize(10);
|
||||
|
||||
$sheet->mergeCells('A1:I1');
|
||||
$sheet->mergeCells('A2:I2');
|
||||
$sheet->mergeCells('A3:I3');
|
||||
|
|
|
|||
|
|
@ -154,6 +154,10 @@ public function registerEvents(): array
|
|||
$lastRow = count($this->rekap) + 6;
|
||||
$dataStart = 7;
|
||||
|
||||
$sheet->getParent()->getDefaultStyle()->getFont()
|
||||
->setName('Calibri')
|
||||
->setSize(10);
|
||||
|
||||
// Merge judul
|
||||
$sheet->mergeCells('A1:K1');
|
||||
$sheet->mergeCells('A2:K2');
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ public function index()
|
|||
'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,
|
||||
];
|
||||
});
|
||||
|
|
@ -31,4 +32,23 @@ public function index()
|
|||
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)
|
||||
{
|
||||
try {
|
||||
|
|
@ -138,11 +193,16 @@ public function history(Request $request)
|
|||
{
|
||||
try {
|
||||
$userId = Auth::id();
|
||||
$history = Presensi::where('id_user', $userId)
|
||||
->latest('tanggal')
|
||||
->paginate(10);
|
||||
$month = $request->get('month', Carbon::now('Asia/Jakarta')->month);
|
||||
$year = $request->get('year', Carbon::now('Asia/Jakarta')->year);
|
||||
|
||||
$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')
|
||||
->where('id_user', $userId)
|
||||
->where('tanggal', $item->tanggal)
|
||||
|
|
@ -220,9 +280,11 @@ public function history(Request $request)
|
|||
];
|
||||
});
|
||||
|
||||
$history->setCollection($enriched);
|
||||
|
||||
return ApiResponse::success($history);
|
||||
return ApiResponse::success([
|
||||
'data' => $enriched->values(),
|
||||
'month' => (int) $month,
|
||||
'year' => (int) $year,
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
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);
|
||||
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) {
|
||||
return ApiResponse::error('Sisa cuti Anda sudah habis (0 hari). Pengajuan cuti tidak dapat dilanjutkan.', 400);
|
||||
}
|
||||
|
||||
$tanggalMulai = Carbon::parse($request->tanggal_mulai);
|
||||
$minDate = Carbon::now()->addDays(7)->startOfDay();
|
||||
if ($jumlahHari > 3) {
|
||||
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)) {
|
||||
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)
|
||||
{
|
||||
try {
|
||||
$query = PengajuanIzin::with(['jenisIzin', 'statusPengajuan'])
|
||||
$query = PengajuanIzin::with(['jenisIzin', 'statusPengajuan', 'suratIzin'])
|
||||
->where('id_user', Auth::id());
|
||||
|
||||
if ($request->has('status')) {
|
||||
|
|
@ -275,6 +284,16 @@ public function history(Request $request)
|
|||
|
||||
$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);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
|
|
|
|||
|
|
@ -200,12 +200,22 @@ public function show($id)
|
|||
|
||||
$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)
|
||||
->whereDate('tanggal', '>=', now()->subDays(30)->toDateString())
|
||||
->orderBy('tanggal', 'desc')
|
||||
->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')
|
||||
->where('id_user', $id)
|
||||
->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.');
|
||||
}
|
||||
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);
|
||||
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) {
|
||||
return back()->withInput()->with('error', 'Pengajuan Cuti minimal H-7!');
|
||||
|
|
@ -207,7 +218,8 @@ public function approve($id)
|
|||
if ($izin->id_jenis_izin == 2) {
|
||||
$user = \App\Models\User::find($izin->id_user);
|
||||
$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();
|
||||
|
|
|
|||
|
|
@ -64,10 +64,13 @@ public function show($id)
|
|||
$canApprove = false;
|
||||
$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;
|
||||
$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;
|
||||
$tahapApproval = 2;
|
||||
}
|
||||
|
|
@ -95,6 +98,15 @@ public function approve(Request $request, $id)
|
|||
|
||||
$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)) {
|
||||
$tahap = 1;
|
||||
} 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;
|
||||
|
||||
// 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)) {
|
||||
$tahap = 1;
|
||||
} 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) {
|
||||
$user = \App\Models\User::find($izin->id_user, ['*']);
|
||||
$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');
|
||||
}
|
||||
|
||||
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,
|
||||
StatusPengajuanSeeder::class,
|
||||
JenisPenguranganSeeder::class,
|
||||
JenisKompensasiSeeder::class,
|
||||
|
||||
// 4. Data dummy (opsional, comment jika tidak diperlukan di production)
|
||||
// PresensiSeeder::class,
|
||||
|
|
|
|||
|
|
@ -9,11 +9,16 @@ class JenisIzinSeeder extends Seeder
|
|||
{
|
||||
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) {
|
||||
DB::table('jenis_izin')->updateOrInsert(
|
||||
['nama_izin' => $type],
|
||||
['updated_at' => now(), 'created_at' => now()]
|
||||
['id_jenis_izin' => $type['id_jenis_izin']],
|
||||
['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
|
||||
{
|
||||
$statuses = [
|
||||
'Tepat Waktu',
|
||||
'Terlambat',
|
||||
'Izin',
|
||||
'Sakit',
|
||||
'Alpha',
|
||||
['id_status' => 1, 'nama_status' => 'Tepat Waktu'],
|
||||
['id_status' => 2, 'nama_status' => 'Terlambat'],
|
||||
['id_status' => 3, 'nama_status' => 'Izin'],
|
||||
['id_status' => 4, 'nama_status' => 'Sakit'],
|
||||
['id_status' => 5, 'nama_status' => 'Alpha'],
|
||||
];
|
||||
|
||||
foreach ($statuses as $status) {
|
||||
DB::table('status_presensi')->updateOrInsert(
|
||||
['nama_status' => $status],
|
||||
['created_at' => now(), 'updated_at' => now()]
|
||||
['id_status' => $status['id_status']],
|
||||
['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
|
||||
{
|
||||
$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) {
|
||||
DB::table('status_pengajuan')->updateOrInsert(
|
||||
['nama_status' => $status],
|
||||
['updated_at' => now(), 'created_at' => now()]
|
||||
['id_status' => $status['id_status']],
|
||||
['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">
|
||||
<form action="{{ route('cuti.index') }}" method="GET" class="w-full md:w-1/3">
|
||||
<div class="relative">
|
||||
<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>
|
||||
<x-search-input name="search" :value="$search" placeholder="Cari NIK atau Nama..." class="!mb-0 h-10" />
|
||||
</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">
|
||||
<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>
|
||||
|
|
@ -31,11 +23,11 @@ class="block w-full pl-10 pr-3 py-2 border border-slate-300 rounded-lg focus:rin
|
|||
</div>
|
||||
|
||||
<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-center">Sisa Cuti</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)
|
||||
<tr class="hover:bg-slate-50 transition-colors group">
|
||||
|
|
@ -65,35 +57,15 @@ class="block w-full pl-10 pr-3 py-2 border border-slate-300 rounded-lg focus:rin
|
|||
@endforelse
|
||||
</x-table>
|
||||
|
||||
@if($pegawai->hasPages())
|
||||
<div class="border-t border-slate-100 p-4">
|
||||
{{ $pegawai->links() }}
|
||||
</div>
|
||||
@endif
|
||||
<x-pagination :paginator="$pegawai" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="modalEdit" 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('modalEdit')"></div>
|
||||
<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">
|
||||
|
||||
<form id="formEditCuti" method="POST" action="">
|
||||
<x-modal name="edit-cuti" title="Edit Sisa Cuti">
|
||||
<form id="formEditCuti" method="POST" action="" class="space-y-4">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
|
||||
<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">
|
||||
|
|
@ -107,49 +79,20 @@ class="block w-full pl-10 pr-3 py-2 border border-slate-300 rounded-lg focus:rin
|
|||
<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 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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-modal>
|
||||
|
||||
<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') }}">
|
||||
<x-modal name="reset-cuti" title="Reset Cuti Massal">
|
||||
<form method="POST" action="{{ route('cuti.reset') }}" class="space-y-4">
|
||||
@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>
|
||||
|
|
@ -174,38 +117,19 @@ class="block w-full pl-10 pr-3 py-2 border border-slate-300 rounded-lg focus:rin
|
|||
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">
|
||||
<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="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>
|
||||
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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-modal>
|
||||
@endsection
|
||||
|
||||
@section('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) {
|
||||
const btn = document.getElementById('btnResetSubmit');
|
||||
btn.disabled = value !== 'RESET';
|
||||
|
|
@ -218,7 +142,7 @@ function openEditModal(id, nama, sisa) {
|
|||
let form = document.getElementById('formEditCuti');
|
||||
form.action = `/cuti/${id}`;
|
||||
|
||||
toggleModal('modalEdit');
|
||||
window.dispatchEvent(new CustomEvent('open-modal', { detail: 'edit-cuti' }));
|
||||
}
|
||||
</script>
|
||||
@endsection
|
||||
|
|
|
|||
|
|
@ -3,15 +3,9 @@
|
|||
@section('title', 'Dashboard')
|
||||
|
||||
@section('content')
|
||||
<div class="mb-8">
|
||||
<h1 class="text-2xl font-bold text-slate-800">
|
||||
{{ \Carbon\Carbon::now('Asia/Jakarta')->hour < 12 ? 'Selamat Pagi' : (\Carbon\Carbon::now('Asia/Jakarta')->hour < 15 ? 'Selamat Siang' : 'Selamat Sore') }},
|
||||
{{ 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>
|
||||
<x-page-header
|
||||
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 }}! 👋"
|
||||
subtitle="{{ Carbon\Carbon::now('Asia/Jakarta')->translatedFormat('l, d F Y') }}" />
|
||||
|
||||
<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')
|
||||
|
||||
|
|
@ -13,11 +13,57 @@
|
|||
<form action="{{ route('jadwal.store') }}" method="POST">
|
||||
@csrf
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-6">
|
||||
<x-date-input label="Tanggal Mulai" name="tanggal_mulai" :value="now()->format('Y-m-d')" required />
|
||||
<x-date-input label="Tanggal Selesai" name="tanggal_selesai" :value="now()->addDays(6)->format('Y-m-d')"
|
||||
required />
|
||||
<x-select label="Shift" name="id_shift" required>
|
||||
<style>
|
||||
input[type="date"]::-webkit-calendar-picker-indicator,
|
||||
input[type="date"]::-webkit-inner-spin-button {
|
||||
display: none;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
</style>
|
||||
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.5rem;" class="mb-6">
|
||||
<div class="space-y-1">
|
||||
<label class="text-sm font-semibold text-slate-700">Tanggal Mulai</label>
|
||||
<div class="relative">
|
||||
<input type="date" name="tanggal_mulai" value="{{ old('tanggal_mulai', now()->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_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 }}
|
||||
|
|
@ -25,7 +71,17 @@
|
|||
{{ \Carbon\Carbon::parse($shift->jam_selesai)->format('H:i') }})
|
||||
</option>
|
||||
@endforeach
|
||||
</x-select>
|
||||
</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 class="mb-6">
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
@extends('layouts.app')
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', 'Lembur')
|
||||
|
||||
|
|
@ -26,22 +26,22 @@
|
|||
</x-slot:header>
|
||||
|
||||
@forelse ($lembur as $index => $item)
|
||||
<tr class="hover:bg-gray-50 transition-colors">
|
||||
<td class="px-6 py-4 text-sm text-gray-700">
|
||||
<tr class="hover:bg-slate-50 transition-colors">
|
||||
<td class="px-6 py-4 text-sm text-slate-700">
|
||||
{{ $lembur->firstItem() + $index }}
|
||||
</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') }}
|
||||
<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_selesai)->format('H:i') }}
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
<div class="text-sm font-medium text-gray-900">{{ $item->user->nama_lengkap ?? '-' }}</div>
|
||||
<div class="text-xs text-gray-500">{{ $item->user->jabatan->nama_jabatan ?? '' }}</div>
|
||||
<div class="text-sm font-medium text-slate-900">{{ $item->user->nama_lengkap ?? '-' }}</div>
|
||||
<div class="text-xs text-slate-500">{{ $item->user->jabatan->nama_jabatan ?? '' }}</div>
|
||||
</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
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
|
|
@ -51,7 +51,7 @@
|
|||
<x-badge color="green">💵 Uang</x-badge>
|
||||
@endif
|
||||
</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) }}
|
||||
</td>
|
||||
<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>
|
||||
</div>
|
||||
@else
|
||||
<span class="text-xs text-gray-400 italic">Selesai</span>
|
||||
<span class="text-xs text-slate-400 italic">Selesai</span>
|
||||
@endif
|
||||
</td>
|
||||
</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" />
|
||||
</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
|
||||
@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>
|
||||
|
|
|
|||
|
|
@ -2,11 +2,7 @@
|
|||
@section('title', 'Notifikasi')
|
||||
|
||||
@section('content')
|
||||
<div class="mb-6 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
|
||||
<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>
|
||||
<x-page-header title="Notifikasi" subtitle="Daftar semua notifikasi Anda">
|
||||
@if($unreadCount > 0)
|
||||
<form id="mark-all-read-form" onsubmit="return false;">
|
||||
<x-button type="button" variant="secondary" onclick="markAllReadPage()">
|
||||
|
|
@ -15,7 +11,7 @@
|
|||
</x-button>
|
||||
</form>
|
||||
@endif
|
||||
</div>
|
||||
</x-page-header>
|
||||
|
||||
<div class="bg-white rounded-2xl border border-slate-200/60 shadow-sm overflow-hidden">
|
||||
@forelse($notifikasi as $item)
|
||||
|
|
@ -62,11 +58,7 @@
|
|||
@endforelse
|
||||
</div>
|
||||
|
||||
@if($notifikasi->hasPages())
|
||||
<div class="mt-6">
|
||||
{{ $notifikasi->links() }}
|
||||
</div>
|
||||
@endif
|
||||
<x-pagination :paginator="$notifikasi" />
|
||||
@endsection
|
||||
|
||||
@section('script')
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
@extends('layouts.app')
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', 'Data Karyawan')
|
||||
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
<div class="max-w-7xl mx-auto space-y-8">
|
||||
|
||||
<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">
|
||||
@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>
|
||||
@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')
|
||||
|
||||
|
|
@ -104,6 +104,23 @@ class="w-24 h-24 rounded-full bg-slate-100 flex items-center justify-center text
|
|||
</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 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 class="px-6 py-4 text-sm text-slate-600 text-center whitespace-nowrap">
|
||||
{{ $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 class="px-6 py-4 text-center whitespace-nowrap">
|
||||
<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') : '--:--' }}
|
||||
</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')
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<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>
|
||||
<x-page-header title="Manajemen Hak Akses" subtitle="Konfigurasi izin spesifik untuk setiap peran pengguna." />
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-4 gap-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('content')
|
||||
<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.">
|
||||
<div class="flex gap-2">
|
||||
<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">
|
||||
<x-table>
|
||||
<x-slot name="header">
|
||||
<x-slot:header>
|
||||
<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">Pegawai 1</th>
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
|
||||
Route::get('/presensi', [PresensiController::class, 'index']);
|
||||
Route::post('/presensi', [PresensiController::class, 'store']);
|
||||
Route::post('/presensi/check-radius', [PresensiController::class, 'checkRadius']);
|
||||
Route::get('/presensi/history', [PresensiController::class, 'history']);
|
||||
Route::post('/presensi/{id}/resubmit', [PresensiController::class, 'resubmit']);
|
||||
|
||||
|
|
@ -50,6 +51,7 @@
|
|||
Route::get('/lembur/history', [LemburController::class, 'history']);
|
||||
|
||||
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']);
|
||||
|
||||
|
|
@ -70,4 +72,11 @@
|
|||
});
|
||||
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('jadwal', \App\Http\Controllers\JadwalController::class)->except(['edit', 'show']);
|
||||
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::resource('hari-libur', \App\Http\Controllers\HariLiburController::class)->except(['create', 'edit', 'show']);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue