part of '../app/app.dart'; class DetailRow extends StatelessWidget { const DetailRow({super.key, required this.label, required this.value}); final String label; final Object? value; @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.only(bottom: 10), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( label, style: const TextStyle(color: Colors.black54, fontSize: 12), ), const SizedBox(height: 2), Text( value?.toString().isNotEmpty == true ? value.toString() : '-', style: const TextStyle(fontWeight: FontWeight.w600), ), ], ), ); } } class StatusPill extends StatelessWidget { const StatusPill({super.key, required this.status}); final String status; @override Widget build(BuildContext context) { final lower = status.toLowerCase(); final color = lower.contains('selesai') ? const Color(0xFF15803D) : lower.contains('dibatalkan') ? const Color(0xFF6B7280) : lower.contains('warning') || lower.contains('target') ? const Color(0xFFDC2626) : const Color(0xFF0F766E); return BadgeLabel(text: status, color: color); } } class BadgeLabel extends StatelessWidget { const BadgeLabel({super.key, required this.text, required this.color}); final String text; final Color color; @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: color.withValues(alpha: 0.12), borderRadius: BorderRadius.circular(6), ), child: Text( text, style: TextStyle( color: color, fontWeight: FontWeight.w700, fontSize: 12, ), ), ); } } class EmptyPanel extends StatelessWidget { const EmptyPanel({super.key, required this.text}); final String text; @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.symmetric(vertical: 24), child: Center( child: Text(text, style: const TextStyle(color: Colors.black54)), ), ); } } class ErrorView extends StatelessWidget { const ErrorView({super.key, required this.message, required this.onRetry}); final String message; final VoidCallback onRetry; @override Widget build(BuildContext context) { return Center( child: Padding( padding: const EdgeInsets.all(24), child: Column( mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.error_outline, size: 42, color: Color(0xFFDC2626)), const SizedBox(height: 10), Text(message, textAlign: TextAlign.center), const SizedBox(height: 12), OutlinedButton.icon( onPressed: onRetry, icon: const Icon(Icons.refresh), label: const Text('Coba Lagi'), ), ], ), ), ); } } class LoadingScreen extends StatelessWidget { const LoadingScreen({super.key}); @override Widget build(BuildContext context) { return const Scaffold(body: Center(child: CircularProgressIndicator())); } } Map asMap(Object? value) { if (value is Map) { return value; } if (value is Map) { return value.map((key, item) => MapEntry(key.toString(), item)); } return {}; } List asList(Object? value) => value is List ? value : []; String _message(Object? error) => error is ApiException ? error.message : 'Terjadi kesalahan saat memuat data.';