import 'dart:io'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'package:image_picker/image_picker.dart'; import '../../../services/auth_service.dart'; // Model untuk menyimpan media (foto/video) class MediaItem { final XFile file; final bool isVideo; MediaItem({required this.file, required this.isVideo}); } class EditLaporanScreen extends StatefulWidget { final Map laporan; const EditLaporanScreen({super.key, required this.laporan}); @override State createState() => _EditLaporanScreenState(); } class _EditLaporanScreenState extends State { final _formKey = GlobalKey(); late TextEditingController keteranganController; List selectedMedia = []; // ✅ Ganti dari List ke List bool isLoading = false; @override void initState() { super.initState(); keteranganController = TextEditingController( text: widget.laporan['keterangan'] ?? '', ); } @override void dispose() { keteranganController.dispose(); super.dispose(); } // ====== AMBIL FOTO DARI KAMERA ====== Future capturePhoto() async { final picker = ImagePicker(); final photo = await picker.pickImage( source: ImageSource.camera, imageQuality: 85, ); if (photo != null) { setState(() { selectedMedia.add(MediaItem(file: photo, isVideo: false)); }); } } // ====== REKAM VIDEO DARI KAMERA ====== Future captureVideo() async { final picker = ImagePicker(); final video = await picker.pickVideo( source: ImageSource.camera, maxDuration: const Duration(minutes: 5), // Batas durasi 5 menit ); if (video != null) { setState(() { selectedMedia.add(MediaItem(file: video, isVideo: true)); }); } } // ====== HAPUS MEDIA ====== void removeMedia(int index) { setState(() { selectedMedia.removeAt(index); }); } // ====== TAMPILKAN BOTTOM SHEET PILIHAN MEDIA ====== void showMediaPicker() { showModalBottomSheet( context: context, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(20)), ), builder: (context) { return SafeArea( child: Padding( padding: const EdgeInsets.symmetric(vertical: 16), child: Column( mainAxisSize: MainAxisSize.min, children: [ Container( width: 40, height: 4, margin: const EdgeInsets.only(bottom: 16), decoration: BoxDecoration( color: Colors.grey.shade300, borderRadius: BorderRadius.circular(2), ), ), const Text( "Tambah Media", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16), ), const SizedBox(height: 16), _MediaOption( icon: Icons.camera_alt, label: "Ambil Foto", subtitle: "Buka kamera untuk foto", color: const Color(0xFF2F5BEA), onTap: () { Navigator.pop(context); capturePhoto(); }, ), _MediaOption( icon: Icons.videocam, label: "Rekam Video", subtitle: "Buka kamera untuk video (maks. 5 menit)", color: Colors.deepOrange, onTap: () { Navigator.pop(context); captureVideo(); }, ), const SizedBox(height: 8), ], ), ), ); }, ); } // ====== SUBMIT ULANG ====== Future submitUlang() async { if (!_formKey.currentState!.validate()) return; setState(() => isLoading = true); try { final token = await AuthService.getToken(); final laporanId = widget.laporan['id']; final request = http.MultipartRequest( 'POST', Uri.parse('${AuthService.baseUrl}/laporan/$laporanId/resubmit'), ); request.headers['Authorization'] = 'Bearer $token'; request.headers['Accept'] = 'application/json'; request.fields['keterangan'] = keteranganController.text; // ✅ Tambahkan foto dan video sesuai field-nya for (var media in selectedMedia) { if (media.isVideo) { request.files.add( await http.MultipartFile.fromPath('video[]', media.file.path), ); } else { request.files.add( await http.MultipartFile.fromPath('foto[]', media.file.path), ); } } final response = await request.send(); if (!mounted) return; setState(() => isLoading = false); if (response.statusCode == 200) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text("✅ Laporan berhasil dikirim ulang"), backgroundColor: Colors.green, ), ); Navigator.pop(context); Navigator.pop(context); } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text("❌ Gagal mengirim ulang laporan"), backgroundColor: Colors.red, ), ); } } catch (e) { setState(() => isLoading = false); debugPrint("Resubmit error: $e"); if (!mounted) return; ScaffoldMessenger.of( context, ).showSnackBar(const SnackBar(content: Text("❌ Terjadi kesalahan"))); } } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: const Color(0xFFF8FAFF), appBar: AppBar( title: const Text( "Edit & Kirim Ulang", style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold), ), backgroundColor: const Color(0xFF2F5BEA), iconTheme: const IconThemeData(color: Colors.white), centerTitle: true, ), body: SingleChildScrollView( padding: const EdgeInsets.all(24), child: Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // ====== INFO PENOLAKAN ====== Container( width: double.infinity, padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.red[50], borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.red.shade200), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Row( children: [ Icon(Icons.info_outline, color: Colors.red, size: 18), SizedBox(width: 6), Text( "Alasan Penolakan:", style: TextStyle( fontWeight: FontWeight.bold, color: Colors.red, ), ), ], ), const SizedBox(height: 8), Text( widget.laporan['catatan_penolakan'] ?? '-', style: const TextStyle(fontSize: 14), ), ], ), ), const SizedBox(height: 24), // ====== KETERANGAN ====== const Text( "Keterangan Laporan", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 15), ), const SizedBox(height: 8), TextFormField( controller: keteranganController, maxLines: 4, decoration: InputDecoration( hintText: "Tulis keterangan laporan...", filled: true, fillColor: Colors.white, border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.grey.shade300), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.grey.shade300), ), ), validator: (v) => (v == null || v.trim().isEmpty) ? "Wajib diisi" : null, ), const SizedBox(height: 24), // ====== MEDIA (FOTO & VIDEO) ====== Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( "Ganti Media (opsional)", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 15), ), if (selectedMedia.isNotEmpty) Text( "${selectedMedia.length} file", style: const TextStyle( color: Color(0xFF2F5BEA), fontSize: 13, ), ), ], ), const SizedBox(height: 8), // Tombol pilih media GestureDetector( onTap: showMediaPicker, child: Container( width: double.infinity, padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), border: Border.all(color: const Color(0xFF2F5BEA)), ), child: Column( children: [ const Icon( Icons.perm_media_outlined, size: 40, color: Color(0xFF2F5BEA), ), const SizedBox(height: 8), Text( selectedMedia.isEmpty ? "Ketuk untuk tambah foto / video" : "Ketuk untuk tambah lebih banyak", style: const TextStyle(color: Color(0xFF2F5BEA)), ), const SizedBox(height: 4), // Label pilihan Row( mainAxisAlignment: MainAxisAlignment.center, children: [ _PillLabel( icon: Icons.camera_alt, label: "Foto Kamera", color: const Color(0xFF2F5BEA), ), const SizedBox(width: 8), _PillLabel( icon: Icons.videocam, label: "Video", color: Colors.deepOrange, ), const SizedBox(width: 8), ], ), ], ), ), ), // ====== PREVIEW MEDIA ====== if (selectedMedia.isNotEmpty) ...[ const SizedBox(height: 12), SizedBox( height: 110, child: ListView.builder( scrollDirection: Axis.horizontal, itemCount: selectedMedia.length, itemBuilder: (context, index) { final media = selectedMedia[index]; return Stack( children: [ Container( margin: const EdgeInsets.only(right: 8), width: 100, decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), color: Colors.grey.shade200, ), child: ClipRRect( borderRadius: BorderRadius.circular(8), child: media.isVideo // Tampilkan placeholder video ? Container( color: Colors.black87, child: const Center( child: Icon( Icons.play_circle_fill, color: Colors.white, size: 40, ), ), ) // Tampilkan preview foto : Image.file( File(media.file.path), fit: BoxFit.cover, width: 100, height: 110, ), ), ), // Badge tipe media Positioned( bottom: 6, left: 6, child: Container( padding: const EdgeInsets.symmetric( horizontal: 6, vertical: 2, ), decoration: BoxDecoration( color: media.isVideo ? Colors.deepOrange : const Color(0xFF2F5BEA), borderRadius: BorderRadius.circular(4), ), child: Text( media.isVideo ? "VIDEO" : "FOTO", style: const TextStyle( color: Colors.white, fontSize: 9, fontWeight: FontWeight.bold, ), ), ), ), // Tombol hapus Positioned( top: 4, right: 12, child: GestureDetector( onTap: () => removeMedia(index), child: Container( width: 22, height: 22, decoration: const BoxDecoration( color: Colors.red, shape: BoxShape.circle, ), child: const Icon( Icons.close, color: Colors.white, size: 14, ), ), ), ), ], ); }, ), ), ], const SizedBox(height: 32), // ====== TOMBOL SUBMIT ====== SizedBox( width: double.infinity, height: 50, child: ElevatedButton.icon( onPressed: isLoading ? null : submitUlang, icon: isLoading ? const SizedBox( width: 18, height: 18, child: CircularProgressIndicator( color: Colors.white, strokeWidth: 2, ), ) : const Icon(Icons.send), label: Text(isLoading ? "Mengirim..." : "Kirim Ulang"), style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF2F5BEA), foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), ), ), ], ), ), ), ); } } // ====== WIDGET HELPER: Opsi di Bottom Sheet ====== class _MediaOption extends StatelessWidget { final IconData icon; final String label; final String subtitle; final Color color; final VoidCallback onTap; const _MediaOption({ required this.icon, required this.label, required this.subtitle, required this.color, required this.onTap, }); @override Widget build(BuildContext context) { return ListTile( leading: Container( width: 44, height: 44, decoration: BoxDecoration( color: color.withOpacity(0.12), borderRadius: BorderRadius.circular(10), ), child: Icon(icon, color: color), ), title: Text(label, style: const TextStyle(fontWeight: FontWeight.w600)), subtitle: Text(subtitle, style: const TextStyle(fontSize: 12)), onTap: onTap, ); } } // ====== WIDGET HELPER: Pill Label ====== class _PillLabel extends StatelessWidget { final IconData icon; final String label; final Color color; const _PillLabel({ required this.icon, required this.label, required this.color, }); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: color.withOpacity(0.1), borderRadius: BorderRadius.circular(20), border: Border.all(color: color.withOpacity(0.3)), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(icon, size: 12, color: color), const SizedBox(width: 4), Text( label, style: TextStyle( fontSize: 11, color: color, fontWeight: FontWeight.w500, ), ), ], ), ); } }