class PenugasanModel { final int idPenugasan; final int idTeknisi; final String? fotoSurat; // ✅ Ganti lokasi → foto surat final String? fotoSuratUrl; // ✅ URL lengkap foto surat final String tanggalDiberikan; final String? catatanAdmin; final String? alamatLokasi; final String? namaPelanggan; final String? noSambungan; // Detail diisi teknisi via mobile final String? jenisPekerjaan; final String? labelJenisPekerjaan; final String? dimensiPipa; final double? jarakMeter; final int? jumlahUnit; final int? jumlahTitik; final bool? pakaiPipaBesi; final String? jenisPengangkatan; final String? detailPekerjaan; // Ongkos final int? idTarif; final double? totalNilaiPekerjaan; // Status final String statusPekerjaan; final String? tanggalMulai; final String? tanggalDiselesaikan; // Garansi final String? tanggalGaransiMulai; final String? tanggalGaransiSelesai; final String? catatanGaransi; final bool? isGaransiAktif; final int? sisaHariGaransi; // Foto bukti pekerjaan final String? fotoSebelum; final String? fotoSesudah; final String? fotoSebelumUrl; final String? fotoSesudahUrl; // Relasi final TeknisiModel? teknisi; final TarifModel? tarif; final List? timTeknisi; PenugasanModel({ required this.idPenugasan, required this.idTeknisi, this.fotoSurat, this.fotoSuratUrl, required this.tanggalDiberikan, this.catatanAdmin, this.alamatLokasi, this.namaPelanggan, this.noSambungan, this.jenisPekerjaan, this.labelJenisPekerjaan, this.dimensiPipa, this.jarakMeter, this.jumlahUnit, this.jumlahTitik, this.pakaiPipaBesi, this.jenisPengangkatan, this.detailPekerjaan, this.idTarif, this.totalNilaiPekerjaan, required this.statusPekerjaan, this.tanggalMulai, this.tanggalDiselesaikan, this.tanggalGaransiMulai, this.tanggalGaransiSelesai, this.catatanGaransi, this.isGaransiAktif, this.sisaHariGaransi, this.fotoSebelum, this.fotoSesudah, this.fotoSebelumUrl, this.fotoSesudahUrl, this.teknisi, this.tarif, this.timTeknisi, }); factory PenugasanModel.fromJson(Map json) { return PenugasanModel( idPenugasan: int.tryParse(json['id_penugasan']?.toString() ?? '') ?? 0, idTeknisi: int.tryParse(json['id_teknisi']?.toString() ?? '') ?? 0, fotoSurat: json['foto_surat'], fotoSuratUrl: json['foto_surat_url'], tanggalDiberikan: json['tanggal_diberikan'] ?? '', catatanAdmin: json['catatan_admin'], alamatLokasi: json['alamat_lokasi'], namaPelanggan: json['nama_pelanggan'], noSambungan: json['no_sambungan'], jenisPekerjaan: json['jenis_pekerjaan'], labelJenisPekerjaan: json['label_jenis_pekerjaan'], dimensiPipa: json['dimensi_pipa'], jarakMeter: json['jarak_meter'] != null ? double.tryParse(json['jarak_meter'].toString()) : null, jumlahUnit: json['jumlah_unit'] != null ? int.tryParse(json['jumlah_unit'].toString()) : null, jumlahTitik: json['jumlah_titik'] != null ? int.tryParse(json['jumlah_titik'].toString()) : null, pakaiPipaBesi: json['pakai_pipa_besi'] != null ? json['pakai_pipa_besi'] == 1 || json['pakai_pipa_besi'] == true || json['pakai_pipa_besi'].toString() == '1' || json['pakai_pipa_besi'].toString() == 'true' : null, jenisPengangkatan: json['jenis_pengangkatan'], detailPekerjaan: json['detail_pekerjaan'], idTarif: json['id_tarif'] != null ? int.tryParse(json['id_tarif'].toString()) : null, totalNilaiPekerjaan: json['total_nilai_pekerjaan'] != null ? double.tryParse(json['total_nilai_pekerjaan'].toString()) : null, statusPekerjaan: json['status_pekerjaan'] ?? 'belum_mulai', tanggalMulai: json['tanggal_mulai'], tanggalDiselesaikan: json['tanggal_diselesaikan'], tanggalGaransiMulai: json['tanggal_garansi_mulai'], tanggalGaransiSelesai:json['tanggal_garansi_selesai'], catatanGaransi: json['catatan_garansi'], isGaransiAktif: json['is_garansi_aktif'] != null ? json['is_garansi_aktif'] == 1 || json['is_garansi_aktif'] == true || json['is_garansi_aktif'].toString() == '1' || json['is_garansi_aktif'].toString() == 'true' : null, sisaHariGaransi: json['sisa_hari_garansi'] != null ? int.tryParse(json['sisa_hari_garansi'].toString()) : null, fotoSebelum: json['foto_sebelum'], fotoSesudah: json['foto_sesudah'], fotoSebelumUrl: json['foto_sebelum_url'], fotoSesudahUrl: json['foto_sesudah_url'], teknisi: json['teknisi'] != null ? TeknisiModel.fromJson(json['teknisi']) : null, tarif: json['tarif'] != null ? TarifModel.fromJson(json['tarif']) : null, timTeknisi: json['tim_teknisi'] != null ? (json['tim_teknisi'] as List) .map((e) => TimTeknisiModel.fromJson(e)) .toList() : null, ); } Map toJson() { return { 'id_penugasan': idPenugasan, 'id_teknisi': idTeknisi, 'foto_surat': fotoSurat, 'tanggal_diberikan': tanggalDiberikan, 'catatan_admin': catatanAdmin, 'alamat_lokasi': alamatLokasi, 'nama_pelanggan': namaPelanggan, 'no_sambungan': noSambungan, 'jenis_pekerjaan': jenisPekerjaan, 'dimensi_pipa': dimensiPipa, 'jarak_meter': jarakMeter, 'jumlah_unit': jumlahUnit, 'jumlah_titik': jumlahTitik, 'pakai_pipa_besi': pakaiPipaBesi, 'jenis_pengangkatan': jenisPengangkatan, 'detail_pekerjaan': detailPekerjaan, 'id_tarif': idTarif, 'total_nilai_pekerjaan': totalNilaiPekerjaan, 'status_pekerjaan': statusPekerjaan, }; } // =================================== // HELPER // =================================== bool get isDetailLengkap => jenisPekerjaan != null; String get statusLabel { switch (statusPekerjaan) { case 'belum_mulai': return 'Belum Mulai'; case 'dalam_proses': return 'Dalam Proses'; case 'selesai': return 'Selesai'; case 'dibatalkan': return 'Dibatalkan'; default: return statusPekerjaan; } } String get statusColor { switch (statusPekerjaan) { case 'belum_mulai': return '#9E9E9E'; case 'dalam_proses': return '#FFA500'; case 'selesai': return '#4CAF50'; case 'dibatalkan': return '#F44336'; default: return '#9E9E9E'; } } String get labelJenisPekerjaanFallback { if (labelJenisPekerjaan != null) return labelJenisPekerjaan!; switch (jenisPekerjaan) { case 'sr': return 'SR (Sambungan Rumah)'; case 'pengembangan_jaringan_pipa': return 'Pengembangan Jaringan Pipa'; case 'pengangkatan': return 'Pengangkatan'; case 'pemasangan_gate_valve': return 'Pemasangan Gate Valve'; case 'gali_urug': return 'Gali Urug'; case 'perbaikan_jaringan_pipa': return 'Perbaikan Jaringan Pipa'; case 'pengecatan_pipa_besi': return 'Pengecatan Pipa Besi'; case 'penyempurnaan_jaringan_pipa': return 'Penyempurnaan Jaringan Pipa'; default: return jenisPekerjaan ?? '-'; } } String get totalNilaiFormatted { if (totalNilaiPekerjaan == null) return '-'; final formatted = totalNilaiPekerjaan!.toStringAsFixed(0) .replaceAllMapped(RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'), (m) => '${m[1]}.'); return 'Rp $formatted'; } // ✅ Getter untuk mengambil semua nama tim String get namaTim { if (timTeknisi == null || timTeknisi!.isEmpty) return teknisi?.nama ?? '-'; // Gabungkan nama unik agar tidak double final names = timTeknisi! .map((e) => e.teknisi?.nama ?? '') .where((n) => n.isNotEmpty) .toSet() .toList(); return names.isNotEmpty ? names.join(', ') : (teknisi?.nama ?? '-'); } } // =================================== // TEKNISI MODEL // =================================== class TeknisiModel { final int idTeknisi; final String nama; final String? noTelepon; final String? spesialisasi; TeknisiModel({ required this.idTeknisi, required this.nama, this.noTelepon, this.spesialisasi, }); factory TeknisiModel.fromJson(Map json) { return TeknisiModel( idTeknisi: int.tryParse(json['id_teknisi']?.toString() ?? '') ?? 0, nama: json['nama'] ?? '', noTelepon: json['no_telepon'], spesialisasi: json['spesialisasi'], ); } } // =================================== // TARIF MODEL // =================================== class TarifModel { final int idTarif; final String jenisPekerjaan; final String namaItem; final String kodeItem; final String? dimensiPipa; final double? tarifPerUnit; final double? tarifPerMeter; final bool? pakaiPipaBesi; final bool adaGaransi; final int? durasiGaransiBulan; TarifModel({ required this.idTarif, required this.jenisPekerjaan, required this.namaItem, required this.kodeItem, this.dimensiPipa, this.tarifPerUnit, this.tarifPerMeter, this.pakaiPipaBesi, required this.adaGaransi, this.durasiGaransiBulan, }); factory TarifModel.fromJson(Map json) { return TarifModel( idTarif: int.tryParse(json['id_tarif']?.toString() ?? '') ?? 0, jenisPekerjaan: json['jenis_pekerjaan'] ?? '', namaItem: json['nama_item'] ?? '', kodeItem: json['kode_item'] ?? '', dimensiPipa: json['dimensi_pipa'], tarifPerUnit: json['tarif_per_unit'] != null ? double.tryParse(json['tarif_per_unit'].toString()) : null, tarifPerMeter: json['tarif_per_meter'] != null ? double.tryParse(json['tarif_per_meter'].toString()) : null, pakaiPipaBesi: json['pakai_pipa_besi'] != null ? json['pakai_pipa_besi'] == 1 || json['pakai_pipa_besi'] == true || json['pakai_pipa_besi'].toString() == '1' || json['pakai_pipa_besi'].toString() == 'true' : null, adaGaransi: json['ada_garansi'] == 1 || json['ada_garansi'] == true || json['ada_garansi'].toString() == '1' || json['ada_garansi'].toString() == 'true', durasiGaransiBulan: json['durasi_garansi_bulan'] != null ? int.tryParse(json['durasi_garansi_bulan'].toString()) : null, ); } } // =================================== // TIM TEKNISI MODEL // =================================== class TimTeknisiModel { final int idTimTeknisi; final int idPenugasan; final int idTeknisi; final String statusKehadiran; final TeknisiModel? teknisi; TimTeknisiModel({ required this.idTimTeknisi, required this.idPenugasan, required this.idTeknisi, required this.statusKehadiran, this.teknisi, }); factory TimTeknisiModel.fromJson(Map json) { return TimTeknisiModel( idTimTeknisi: int.tryParse(json['id_tim_penugasan']?.toString() ?? json['id_tim_teknisi']?.toString() ?? '') ?? 0, idPenugasan: int.tryParse(json['id_penugasan']?.toString() ?? '') ?? 0, idTeknisi: int.tryParse(json['id_teknisi']?.toString() ?? '') ?? 0, statusKehadiran: json['status_kehadiran'] ?? 'hadir', teknisi: json['teknisi'] != null ? TeknisiModel.fromJson(json['teknisi']) : null, ); } } // =================================== // STATISTIK MODEL // =================================== class StatistikModel { final int totalPenugasan; final int belumMulai; final int dalamProses; final int selesai; final int menungguDetail; final int detailLengkap; StatistikModel({ required this.totalPenugasan, required this.belumMulai, required this.dalamProses, required this.selesai, required this.menungguDetail, required this.detailLengkap, }); factory StatistikModel.fromJson(Map json) { return StatistikModel( totalPenugasan: int.tryParse(json['total_penugasan']?.toString() ?? '') ?? 0, belumMulai: int.tryParse(json['belum_mulai']?.toString() ?? '') ?? 0, dalamProses: int.tryParse(json['dalam_proses']?.toString() ?? '') ?? 0, selesai: int.tryParse(json['selesai']?.toString() ?? '') ?? 0, menungguDetail: int.tryParse(json['menunggu_detail']?.toString() ?? '') ?? 0, detailLengkap: int.tryParse(json['detail_lengkap']?.toString() ?? '') ?? 0, ); } }