filled('status')) { if ($request->status === 'garansi_aktif') { $query->garansiAktif(); } else { $query->where('status_pekerjaan', $request->status); } } if ($request->filled('jenis_pekerjaan')) { $query->where('jenis_pekerjaan', $request->jenis_pekerjaan); } if ($request->filled('search')) { $search = $request->search; $query->where(function($q) use ($search) { $q->where('catatan_admin', 'LIKE', "%{$search}%") ->orWhere('detail_pekerjaan', 'LIKE', "%{$search}%") ->orWhereHas('teknisi', function($tq) use ($search) { $tq->where('nama', 'LIKE', "%{$search}%"); }); }); } if ($request->filled('id_teknisi')) { $teknisiId = $request->id_teknisi; $query->where(function($q) use ($teknisiId) { $q->where('id_teknisi', $teknisiId) ->orWhereHas('timTeknisi', function($tq) use ($teknisiId) { $tq->where('id_teknisi', $teknisiId); }); }); } if ($request->filled('start_date') && $request->filled('end_date')) { $query->whereBetween('tanggal_diberikan', [$request->start_date, $request->end_date]); } elseif ($request->filled('start_date')) { $query->where('tanggal_diberikan', '>=', $request->start_date); } elseif ($request->filled('end_date')) { $query->where('tanggal_diberikan', '<=', $request->end_date); } $penugasan = $query->orderBy('created_at', 'desc')->paginate(10); $totalPenugasan = Penugasan::count(); $belumMulai = Penugasan::where('status_pekerjaan', 'belum_mulai')->count(); $dalamProses = Penugasan::where('status_pekerjaan', 'dalam_proses')->count(); $selesai = Penugasan::where('status_pekerjaan', 'selesai')->count(); $dibatalkan = Penugasan::where('status_pekerjaan', 'dibatalkan')->count(); $garansiAktif = Penugasan::garansiAktif()->count(); $today = Carbon::today()->toDateString(); $absentTechIds = \App\Models\Absensi::where('tanggal', $today) ->whereIn('status', ['izin', 'sakit']) ->pluck('id_teknisi') ->toArray(); $abandonedTasks = collect(); if (!empty($absentTechIds)) { $abandonedTasks = Penugasan::whereIn('status_pekerjaan', ['belum_mulai', 'dalam_proses']) ->where(function($q) use ($absentTechIds) { $q->whereIn('id_teknisi', $absentTechIds) ->orWhereHas('timTeknisi', function($tq) use ($absentTechIds) { $tq->whereIn('id_teknisi', $absentTechIds); }); }) ->with('teknisi') ->get(); } $teknisis = Teknisi::where('status', 'aktif')->orderBy('nama')->get(); $teknisiList = $teknisis; // Use same data for the new dropdown return view('Admin.KelolaPekerjaan.Penugasan', compact( 'penugasan', 'totalPenugasan', 'belumMulai', 'dalamProses', 'selesai', 'dibatalkan', 'garansiAktif', 'teknisis', 'teknisiList', 'abandonedTasks' )); } public function store(Request $request) { try { $validated = $request->validate([ 'id_teknisi' => 'required|array', 'id_teknisi.*' => 'exists:teknisis,id_teknisi', 'tanggal_diberikan' => 'required|date', 'foto_surat' => 'nullable|image|mimes:jpeg,png,jpg,webp|max:5120', 'catatan_admin' => 'nullable|string', 'jenis_pekerjaan' => 'required|string', 'alamat_lokasi' => 'required|string', 'nama_pelanggan' => 'nullable|string', 'no_sambungan' => 'nullable|string', ]); DB::beginTransaction(); $path = null; if ($request->hasFile('foto_surat')) { $path = $request->file('foto_surat')->store('penugasan/surat', 'public'); } $penugasan = Penugasan::create([ 'id_teknisi' => $validated['id_teknisi'][0], 'foto_surat' => $path, 'tanggal_diberikan' => $validated['tanggal_diberikan'], 'catatan_admin' => $validated['catatan_admin'] ?? null, 'jenis_pekerjaan' => $validated['jenis_pekerjaan'], 'alamat_lokasi' => $validated['alamat_lokasi'], 'nama_pelanggan' => $validated['nama_pelanggan'] ?? null, 'no_sambungan' => $validated['no_sambungan'] ?? null, 'status_pekerjaan' => 'belum_mulai', ]); $techIds = array_unique($validated['id_teknisi']); foreach ($techIds as $techId) { TimTeknisiPenugasan::create([ 'id_penugasan' => $penugasan->id_penugasan, 'id_teknisi' => $techId, 'status_kehadiran' => 'hadir', ]); } DB::commit(); return redirect()->route('pekerjaan.penugasan.index') ->with('success', 'Penugasan berhasil dibuat! Teknisi akan melengkapi detail via mobile.'); } catch (\Exception $e) { DB::rollBack(); return redirect()->back() ->with('error', 'Gagal menambahkan data: ' . $e->getMessage()) ->withInput(); } } /** * ✅ FIX: Tambah field garansi lengkap di response show() * - is_garansi_aktif → true/false apakah garansi masih aktif * - sisa_hari_garansi → berapa hari tersisa * - tanggal_garansi_mulai → sudah ikut dari toArray() * - tanggal_garansi_selesai → sudah ikut dari toArray() * * Sebelumnya field-field ini tidak di-append sehingga * badge garansi di modal detail tidak pernah muncul * meski datanya sudah ada di database */ public function show($id) { try { $penugasan = Penugasan::with(['teknisi', 'tarif', 'timTeknisi.teknisi', 'items.tarif']) ->findOrFail($id); $data = $penugasan->toArray(); $data['foto_surat_url'] = $penugasan->foto_surat_url; $data['foto_sebelum_url'] = $penugasan->foto_sebelum_url; $data['foto_sesudah_url'] = $penugasan->foto_sesudah_url; $data['label_jenis_pekerjaan'] = $penugasan->label_jenis_pekerjaan; // ✅ FIX: Info garansi — dibutuhkan oleh blade untuk tampilkan badge $data['is_garansi_aktif'] = $penugasan->isGaransiAktif(); $data['sisa_hari_garansi'] = $penugasan->getSisaHariGaransi(); return response()->json(['success' => true, 'data' => $data]); } catch (\Exception $e) { return response()->json(['success' => false, 'message' => 'Data tidak ditemukan'], 404); } } public function edit($id) { try { $penugasan = Penugasan::with(['teknisi', 'timTeknisi.teknisi'])->findOrFail($id); $data = $penugasan->toArray(); $data['foto_surat_url'] = $penugasan->foto_surat_url; return response()->json(['success' => true, 'data' => $data]); } catch (\Exception $e) { return response()->json(['success' => false, 'message' => 'Data tidak ditemukan'], 404); } } public function update(Request $request, $id) { try { $penugasan = Penugasan::findOrFail($id); $validated = $request->validate([ 'id_teknisi' => 'required|array', 'id_teknisi.*' => 'exists:teknisis,id_teknisi', 'tanggal_diberikan' => 'required|date', 'foto_surat' => 'nullable|image|mimes:jpeg,png,jpg,webp|max:5120', 'catatan_admin' => 'nullable|string', 'jenis_pekerjaan' => 'required|string', 'alamat_lokasi' => 'required|string', 'nama_pelanggan' => 'nullable|string', 'no_sambungan' => 'nullable|string', ]); DB::beginTransaction(); $updateData = [ 'id_teknisi' => $validated['id_teknisi'][0], 'tanggal_diberikan' => $validated['tanggal_diberikan'], 'catatan_admin' => $validated['catatan_admin'] ?? null, 'jenis_pekerjaan' => $validated['jenis_pekerjaan'], 'alamat_lokasi' => $validated['alamat_lokasi'], 'nama_pelanggan' => $validated['nama_pelanggan'] ?? null, 'no_sambungan' => $validated['no_sambungan'] ?? null, ]; if ($request->hasFile('foto_surat')) { if ($penugasan->foto_surat) { Storage::disk('public')->delete($penugasan->foto_surat); } $updateData['foto_surat'] = $request->file('foto_surat')->store('penugasan/surat', 'public'); } $penugasan->update($updateData); TimTeknisiPenugasan::where('id_penugasan', $penugasan->id_penugasan)->delete(); $techIds = array_unique($validated['id_teknisi']); foreach ($techIds as $techId) { TimTeknisiPenugasan::create([ 'id_penugasan' => $penugasan->id_penugasan, 'id_teknisi' => $techId, 'status_kehadiran' => 'hadir', ]); } DB::commit(); return response()->json(['success' => true, 'message' => 'Data berhasil diupdate!']); } catch (\Exception $e) { DB::rollBack(); return response()->json(['success' => false, 'message' => 'Gagal update: ' . $e->getMessage()], 500); } } public function destroy($id) { try { DB::beginTransaction(); $penugasan = Penugasan::findOrFail($id); if ($penugasan->foto_surat) { Storage::disk('public')->delete($penugasan->foto_surat); } $penugasan->delete(); DB::commit(); return response()->json(['success' => true, 'message' => 'Data berhasil dihapus!']); } catch (\Exception $e) { DB::rollBack(); return response()->json(['success' => false, 'message' => 'Gagal menghapus: ' . $e->getMessage()], 500); } } public function getTarifByJenis(Request $request) { try { $tarifs = TarifPekerjaan::where('jenis_pekerjaan', $request->jenis_pekerjaan) ->where('is_active', true) ->get(); return response()->json(['success' => true, 'data' => $tarifs]); } catch (\Exception $e) { return response()->json(['success' => false, 'message' => 'Gagal mengambil tarif'], 500); } } public function getTeknisiByDate(Request $request) { try { // Selalu tampilkan seluruh teknisi aktif agar fleksibel secara operasional $teknisis = Teknisi::where('status', 'aktif') ->orderBy('nama') ->get(); return response()->json(['success' => true, 'data' => $teknisis]); } catch (\Exception $e) { return response()->json(['success' => false, 'message' => 'Gagal mengambil data teknisi: ' . $e->getMessage()], 500); } } /** * ✅ FIX: Filter tanggal monitoring pakai tanggal_diselesaikan * bukan updated_at agar hasil filter akurat */ public function monitoring(Request $request) { $query = Penugasan::with(['teknisi', 'timTeknisi.teknisi']); if ($request->filled('status')) { $query->where('status_pekerjaan', $request->status); } if ($request->filled('teknisi_id')) { $teknisiId = $request->teknisi_id; $query->where(function($q) use ($teknisiId) { $q->where('id_teknisi', $teknisiId) ->orWhereHas('timTeknisi', function($tq) use ($teknisiId) { $tq->where('id_teknisi', $teknisiId); }); }); } if ($request->filled('start_date') && $request->filled('end_date')) { $start = $request->start_date . ' 00:00:00'; $end = $request->end_date . ' 23:59:59'; $query->where(function($q) use ($start, $end) { $q->where(function($q2) use ($start, $end) { $q2->whereNotNull('tanggal_diselesaikan') ->whereBetween('tanggal_diselesaikan', [$start, $end]); })->orWhere(function($q2) use ($start, $end) { $q2->whereNull('tanggal_diselesaikan') ->whereBetween('updated_at', [$start, $end]); }); }); } elseif ($request->filled('start_date')) { $start = $request->start_date . ' 00:00:00'; $query->where(function($q) use ($start) { $q->where(function($q2) use ($start) { $q2->whereNotNull('tanggal_diselesaikan') ->where('tanggal_diselesaikan', '>=', $start); })->orWhere(function($q2) use ($start) { $q2->whereNull('tanggal_diselesaikan') ->where('updated_at', '>=', $start); }); }); } elseif ($request->filled('end_date')) { $end = $request->end_date . ' 23:59:59'; $query->where(function($q) use ($end) { $q->where(function($q2) use ($end) { $q2->whereNotNull('tanggal_diselesaikan') ->where('tanggal_diselesaikan', '<=', $end); })->orWhere(function($q2) use ($end) { $q2->whereNull('tanggal_diselesaikan') ->where('updated_at', '<=', $end); }); }); } $progresKerja = $query->orderBy('updated_at', 'desc')->paginate(12); $progresKerja->getCollection()->transform(function ($item) { $item->persentase_pekerjaan = match($item->status_pekerjaan) { 'selesai' => 100, 'dalam_proses' => 50, default => 0, }; $item->is_garansi_aktif = $item->isGaransiAktif(); $item->sisa_hari_garansi = $item->getSisaHariGaransi(); return $item; }); $statistics = [ 'total' => Penugasan::count(), 'dalam_progres' => Penugasan::where('status_pekerjaan', 'dalam_proses')->count(), 'selesai' => Penugasan::where('status_pekerjaan', 'selesai')->count(), 'belum_mulai' => Penugasan::where('status_pekerjaan', 'belum_mulai')->count(), 'garansi_aktif' => Penugasan::garansiAktif()->count(), ]; $statusList = [ 'belum_mulai' => 'Belum Mulai', 'dalam_proses' => 'Dalam Proses', 'selesai' => 'Selesai', ]; $teknisiList = Teknisi::orderBy('nama')->get(); return view('Admin.KelolaPekerjaan.ProgresKerja', compact( 'progresKerja', 'statistics', 'statusList', 'teknisiList' )); } }