h-1 lebaran

This commit is contained in:
Khafidh Fuadi
2025-03-30 14:45:16 +07:00
parent c008020705
commit 5aaeb58d2b
91 changed files with 9448 additions and 3756 deletions

View File

@ -1,16 +1,16 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:penyaluran_app/app/data/models/pengaduan_model.dart';
import 'package:penyaluran_app/app/data/models/tindakan_pengaduan_model.dart';
import 'package:penyaluran_app/app/modules/warga/controllers/warga_dashboard_controller.dart';
import 'package:penyaluran_app/app/theme/app_theme.dart';
import 'package:penyaluran_app/app/utils/format_helper.dart';
import 'package:timeline_tile/timeline_tile.dart';
import 'package:image_picker/image_picker.dart';
import 'package:penyaluran_app/app/widgets/indicators/status_pill.dart';
import 'package:penyaluran_app/app/widgets/section_header.dart';
import 'package:penyaluran_app/app/widgets/cards/info_card.dart';
import 'dart:io';
import 'package:penyaluran_app/app/widgets/widgets.dart';
class WargaDetailPengaduanView extends GetView<WargaDashboardController> {
const WargaDetailPengaduanView({super.key});
@ -670,8 +670,7 @@ class WargaDetailPengaduanView extends GetView<WargaDashboardController> {
const SizedBox(width: 12),
Text(
pengaduan.tanggalPengaduan != null
? DateFormat('dd MMMM yyyy', 'id_ID')
.format(pengaduan.tanggalPengaduan!)
? FormatHelper.formatDateTime(pengaduan.tanggalPengaduan!)
: '-',
style: TextStyle(
fontSize: 15,
@ -1309,8 +1308,8 @@ class WargaDetailPengaduanView extends GetView<WargaDashboardController> {
child: Row(
children: tindakan.buktiTindakan!.map((bukti) {
return GestureDetector(
onTap: () =>
showFullScreenImage(context, bukti),
onTap: () => ShowImageDialog.showFullScreen(
context, bukti),
child: Container(
width: 100,
height: 100,
@ -1407,8 +1406,8 @@ class WargaDetailPengaduanView extends GetView<WargaDashboardController> {
Expanded(
child: Text(
tindakan.tanggalTindakan != null
? DateFormat('dd MMM yyyy HH:mm', 'id_ID')
.format(tindakan.tanggalTindakan!)
? FormatHelper.formatDateTime(
tindakan.tanggalTindakan!)
: '-',
style: TextStyle(
fontSize: 12,
@ -1429,183 +1428,8 @@ class WargaDetailPengaduanView extends GetView<WargaDashboardController> {
);
}
void showFullScreenImage(BuildContext context, String imageUrl) {
// Buat controller untuk InteractiveViewer
final TransformationController transformationController =
TransformationController();
Get.dialog(
Dialog(
insetPadding: EdgeInsets.zero,
child: Stack(
fit: StackFit.expand,
children: [
Container(
color: Colors.black,
child: InteractiveViewer(
panEnabled: true,
minScale: 0.5,
maxScale: 4,
transformationController: transformationController,
child: Center(
child: Hero(
tag: imageUrl,
child: imageUrl.startsWith('http')
? Image.network(
imageUrl,
fit: BoxFit.contain,
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(
Colors.white),
value: loadingProgress.expectedTotalBytes !=
null
? loadingProgress.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes!
: null,
),
);
},
errorBuilder: (context, error, stackTrace) {
return Container(
padding: const EdgeInsets.all(20),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(
Icons.broken_image,
size: 60,
color: Colors.red,
),
const SizedBox(height: 16),
Text(
'Gagal memuat gambar',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
],
),
);
},
)
: Image.file(
File(imageUrl),
fit: BoxFit.contain,
errorBuilder: (context, error, stackTrace) {
return Container(
padding: const EdgeInsets.all(20),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(
Icons.broken_image,
size: 60,
color: Colors.red,
),
const SizedBox(height: 16),
Text(
'Gagal memuat gambar',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
],
),
);
},
),
),
),
),
),
Positioned(
top: 20,
right: 20,
child: GestureDetector(
onTap: () => Get.back(),
child: Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.6),
shape: BoxShape.circle,
),
child: const Icon(
Icons.close,
color: Colors.white,
size: 24,
),
),
),
),
Positioned(
bottom: 20,
left: 0,
right: 0,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildImageControlButton(
icon: Icons.zoom_out,
onTap: () {
// Zoom out
final Matrix4 matrix =
transformationController.value.clone();
matrix.scale(0.75);
transformationController.value = matrix;
},
),
const SizedBox(width: 16),
_buildImageControlButton(
icon: Icons.refresh,
onTap: () {
// Reset
transformationController.value = Matrix4.identity();
},
),
const SizedBox(width: 16),
_buildImageControlButton(
icon: Icons.zoom_in,
onTap: () {
// Zoom in
final Matrix4 matrix =
transformationController.value.clone();
matrix.scale(1.5);
transformationController.value = matrix;
},
),
],
),
),
],
),
),
);
}
Widget _buildImageControlButton({
required IconData icon,
required Function() onTap,
}) {
return GestureDetector(
onTap: onTap,
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.6),
shape: BoxShape.circle,
),
child: Icon(
icon,
color: Colors.white,
size: 24,
),
),
);
void _showFullScreenImage(BuildContext context, String imagePath) {
ShowImageDialog.showFullScreen(context, imagePath);
}
}
@ -2056,8 +1880,7 @@ class _TambahTindakanPengaduanViewState
}
void _showFullScreenImage(BuildContext context, String imagePath) {
final wargaDetailView = Get.find<WargaDetailPengaduanView>();
wargaDetailView.showFullScreenImage(context, imagePath);
ShowImageDialog.showFullScreen(context, imagePath);
}
Future<void> _simpanTindakan() async {
@ -2078,22 +1901,6 @@ class _TambahTindakanPengaduanViewState
});
try {
// Di sini kita baru melakukan upload file ke server
// Contoh implementasi:
// 1. Upload semua file bukti tindakan
// final List<String> buktiTindakanUrls = await uploadMultipleFiles(buktiTindakanPaths);
// 2. Simpan data tindakan ke database
// await saveTindakanPengaduan(
// pengaduanId: widget.pengaduanId,
// kategoriTindakan: selectedKategori!,
// prioritas: selectedPrioritas!,
// tindakan: tindakanController.text,
// catatan: catatanController.text,
// buktiTindakanUrls: buktiTindakanUrls,
// );
// Tampilkan pesan sukses
Get.back(); // Kembali ke halaman sebelumnya
Get.snackbar(

View File

@ -11,12 +11,12 @@ class FormPengaduanView extends StatefulWidget {
final List<File>? selectedImages;
const FormPengaduanView({
Key? key,
super.key,
required this.uidPenerimaan,
this.judul,
this.deskripsi,
this.selectedImages,
}) : super(key: key);
});
@override
State<FormPengaduanView> createState() => _FormPengaduanViewState();
@ -219,7 +219,7 @@ class _FormPengaduanViewState extends State<FormPengaduanView> {
),
),
const SizedBox(height: 8),
Container(
SizedBox(
height: 120,
child: ListView.builder(
scrollDirection: Axis.horizontal,

View File

@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:penyaluran_app/app/modules/warga/controllers/warga_dashboard_controller.dart';
import 'package:penyaluran_app/app/utils/format_helper.dart';
import 'package:penyaluran_app/app/widgets/section_header.dart';
class WargaDashboardView extends GetView<WargaDashboardController> {
@ -23,6 +23,54 @@ class WargaDashboardView extends GetView<WargaDashboardController> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header DisalurKita dengan logo dan slogan
Container(
padding: const EdgeInsets.all(16),
margin: const EdgeInsets.only(bottom: 16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(15),
boxShadow: [
BoxShadow(
color: Colors.blue.withOpacity(0.1),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
),
child: Row(
children: [
Image.asset(
'assets/images/logo-disalurkita.png',
width: 50,
height: 50,
),
const SizedBox(width: 15),
const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'DisalurKita',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Color(0xFF1565C0),
),
),
SizedBox(height: 5),
Text(
'Salurkan dengan Pasti, Pantau dengan Bukti',
style: TextStyle(
fontSize: 12,
color: Colors.grey,
fontWeight: FontWeight.w500,
),
),
],
),
],
),
),
_buildWelcomeSection(),
const SizedBox(height: 24),
_buildStatisticSection(),
@ -90,10 +138,17 @@ class WargaDashboardView extends GetView<WargaDashboardController> {
? NetworkImage(controller.profilePhotoUrl!)
: null,
child: controller.profilePhotoUrl == null
? Icon(
Icons.person,
color: Colors.blue.shade700,
size: 30,
? Text(
controller.nama.isNotEmpty
? controller.nama
.substring(0, 1)
.toUpperCase()
: '?',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.blue.shade700,
fontSize: 24,
),
)
: null,
),
@ -417,12 +472,6 @@ class WargaDashboardView extends GetView<WargaDashboardController> {
}
Widget _buildPenerimaanSummary() {
final currencyFormat = NumberFormat.currency(
locale: 'id',
symbol: 'Rp ',
decimalDigits: 0,
);
double totalUang = 0;
Map<String, double> totalNonUang = {};
@ -494,7 +543,7 @@ class WargaDashboardView extends GetView<WargaDashboardController> {
icon: Icons.attach_money,
color: Colors.green,
title: 'Total Bantuan Uang',
value: currencyFormat.format(totalUang),
value: FormatHelper.formatRupiah(totalUang),
),
if (totalNonUang.isNotEmpty) ...[
if (totalUang > 0)

View File

@ -1,9 +1,9 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:penyaluran_app/app/data/models/penerima_penyaluran_model.dart';
import 'package:penyaluran_app/app/data/models/pengaduan_model.dart';
import 'package:penyaluran_app/app/modules/warga/controllers/warga_dashboard_controller.dart';
import 'package:penyaluran_app/app/utils/format_helper.dart';
import 'package:penyaluran_app/app/widgets/status_badge.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:image_picker/image_picker.dart';
@ -131,17 +131,11 @@ class WargaDetailPenerimaanView extends GetView<WargaDashboardController> {
}
Widget _buildHeaderSection(PenerimaPenyaluranModel penyaluran) {
final currencyFormat = NumberFormat.currency(
locale: 'id',
symbol: 'Rp ',
decimalDigits: 0,
);
// Format jumlah bantuan berdasarkan tipe (uang atau bukan)
String formattedJumlah = '';
if (penyaluran.jumlahBantuan != null) {
if (penyaluran.isUang == true) {
formattedJumlah = currencyFormat.format(penyaluran.jumlahBantuan);
formattedJumlah = FormatHelper.formatRupiah(penyaluran.jumlahBantuan);
} else {
formattedJumlah =
'${penyaluran.jumlahBantuan} ${penyaluran.satuan ?? ''}';
@ -390,8 +384,7 @@ class WargaDetailPenerimaanView extends GetView<WargaDashboardController> {
icon: Icons.calendar_today,
title: 'Tanggal Penerimaan',
value: penyaluran.tanggalPenerimaan != null
? DateFormat('dd MMMM yyyy', 'id_ID')
.format(penyaluran.tanggalPenerimaan!)
? FormatHelper.formatDateTime(penyaluran.tanggalPenerimaan!)
: 'Belum diterima',
statusColor: null,
),
@ -400,8 +393,7 @@ class WargaDetailPenerimaanView extends GetView<WargaDashboardController> {
icon: Icons.access_time,
title: 'Waktu Penerimaan',
value: penyaluran.tanggalPenerimaan != null
? DateFormat('HH:mm', 'id_ID')
.format(penyaluran.tanggalPenerimaan!)
? FormatHelper.formatDateTime(penyaluran.tanggalPenerimaan!)
: 'Belum diterima',
statusColor: null,
),
@ -758,8 +750,7 @@ class WargaDetailPenerimaanView extends GetView<WargaDashboardController> {
icon: Icons.update,
title: 'Terakhir Diperbarui',
value: penyaluran.tanggalPenerimaan != null
? DateFormat('dd MMMM yyyy HH:mm', 'id_ID')
.format(penyaluran.tanggalPenerimaan!)
? FormatHelper.formatDateTime(penyaluran.tanggalPenerimaan!)
: 'Tidak tersedia',
statusColor: null,
),
@ -1394,7 +1385,7 @@ class WargaDetailPenerimaanView extends GetView<WargaDashboardController> {
),
const SizedBox(width: 8),
const Text(
'Pengaduan Terdaftar',
'Pengaduan',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
@ -1547,8 +1538,7 @@ class WargaDetailPenerimaanView extends GetView<WargaDashboardController> {
const SizedBox(width: 8),
Text(
pengaduan.tanggalPengaduan != null
? DateFormat('dd MMMM yyyy HH:mm', 'id_ID')
.format(pengaduan.tanggalPengaduan!)
? FormatHelper.formatDateTime(pengaduan.tanggalPengaduan!)
: 'Tanggal tidak tersedia',
style: TextStyle(
fontSize: 12,

View File

@ -1,12 +1,7 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:penyaluran_app/app/data/models/penerima_penyaluran_model.dart';
import 'package:penyaluran_app/app/modules/warga/controllers/warga_dashboard_controller.dart';
import 'package:penyaluran_app/app/modules/warga/views/form_pengaduan_view.dart';
import 'package:penyaluran_app/app/utils/format_helper.dart';
import 'dart:io';
import 'package:image_picker/image_picker.dart';
class WargaPengaduanView extends GetView<WargaDashboardController> {
const WargaPengaduanView({super.key});
@ -380,7 +375,7 @@ class WargaPengaduanView extends GetView<WargaDashboardController> {
Expanded(
child: Text(
item.tanggalPengaduan != null
? DateTimeHelper.formatDateTime(
? FormatHelper.formatDateTime(
item.tanggalPengaduan!)
: '-',
style: TextStyle(

View File

@ -20,13 +20,13 @@ class WargaView extends GetView<WargaDashboardController> {
title: Obx(() {
switch (controller.activeTabIndex.value) {
case 0:
return const Text('Dashboard Warga');
return const Text('Dashboard');
case 1:
return const Text('Penerimaan Bantuan');
case 2:
return const Text('Pengaduan');
default:
return const Text('Dashboard Warga');
return const Text('Dashboard');
}
}),
leading: IconButton(
@ -164,16 +164,19 @@ class WargaView extends GetView<WargaDashboardController> {
child: CircleAvatar(
radius: 40,
backgroundColor: Colors.white70,
backgroundImage: controller.profilePhotoUrl != null &&
controller.profilePhotoUrl!.isNotEmpty
? NetworkImage(controller.profilePhotoUrl!)
backgroundImage: controller.fotoProfil.value.isNotEmpty
? NetworkImage(controller.fotoProfil.value)
: null,
child: controller.profilePhotoUrl == null ||
controller.profilePhotoUrl!.isEmpty
? Icon(
Icons.person,
color: Colors.white,
size: 40,
child: controller.fotoProfil.isEmpty
? Text(
controller.nama.isNotEmpty
? controller.nama.substring(0, 1).toUpperCase()
: '?',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white,
fontSize: 24,
),
)
: null,
),
@ -292,6 +295,15 @@ class WargaView extends GetView<WargaDashboardController> {
controller.refreshData();
},
),
_buildMenuItem(
icon: Icons.info_outline,
activeIcon: Icons.info,
title: 'Tentang Kami',
onTap: () {
Navigator.pop(context);
Get.toNamed('/about');
},
),
_buildMenuItem(
icon: Icons.logout,
title: 'Keluar',
@ -307,7 +319,7 @@ class WargaView extends GetView<WargaDashboardController> {
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Text(
'© ${DateTime.now().year} Aplikasi Penyaluran Bantuan',
'© ${DateTime.now().year} DisalurKita',
style: TextStyle(
fontSize: 12,
color: Colors.grey,