import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import '../api/GajiApi.dart'; const _bg = Color(0xFFF9FAFB); const _bg1 = Color(0xFFFFFFFF); const _bg2 = Color(0xFFF3F4F6); const _green = Color(0xFF10B981); const _greenDim = Color(0x1A10B981); const _cyan = Color(0xFF06B6D4); const _cyanDim = Color(0x1A06B6D4); const _amber = Color(0xFFF59E0B); const _amberDim = Color(0x1AF59E0B); const _rose = Color(0xFFEF4444); const _roseDim = Color(0x1AEF4444); const _t1 = Color(0xFF111827); const _t2 = Color(0xFF6B7280); const _t3 = Color(0xFF9CA3AF); const _line2 = Color(0xFFE5E7EB); class GajiRiwayatScreen extends StatefulWidget { const GajiRiwayatScreen({super.key}); @override State createState() => _GajiRiwayatScreenState(); } class _GajiRiwayatScreenState extends State { final GajiApi _apiService = GajiApi(); List _riwayatGaji = []; bool _isLoading = false; @override void initState() { super.initState(); _fetchRiwayat(); } Future _fetchRiwayat() async { setState(() => _isLoading = true); final res = await _apiService.getRiwayat(); if (mounted) { setState(() { _isLoading = false; if (res['success'] == true) { // Laravel paginate structure is res['data']['data'] final rawData = res['data']; if (rawData is Map && rawData.containsKey('data')) { _riwayatGaji = rawData['data'] ?? []; } else { _riwayatGaji = rawData ?? []; } } }); } } String _formatCurrency(dynamic amount) { final formatter = NumberFormat.currency(locale: 'id_ID', symbol: 'Rp ', decimalDigits: 0); return formatter.format(amount ?? 0); } @override Widget build(BuildContext context) { return _isLoading ? const Center(child: CircularProgressIndicator(color: _amber)) : RefreshIndicator( onRefresh: _fetchRiwayat, color: _amber, backgroundColor: _bg1, child: _riwayatGaji.isEmpty ? _buildEmptyState() : ListView.builder( padding: const EdgeInsets.all(20), itemCount: _riwayatGaji.length, itemBuilder: (context, i) => _buildGajiCard(_riwayatGaji[i]), ), ); } Widget _buildEmptyState() { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: _bg1, shape: BoxShape.circle, border: Border.all(color: _line2), ), child: const Icon(Icons.receipt_long_rounded, size: 40, color: _t3), ), const SizedBox(height: 16), const Text('Belum Ada Riwayat', style: TextStyle(color: _t1, fontSize: 16, fontWeight: FontWeight.w700)), const SizedBox(height: 4), const Text('Slip gaji Anda akan muncul di sini', style: TextStyle(color: _t2, fontSize: 13)), ], ), ); } Widget _buildGajiCard(dynamic item) { return Container( margin: const EdgeInsets.only(bottom: 16), decoration: BoxDecoration( color: _bg1, borderRadius: BorderRadius.circular(16), border: Border.all(color: _line2), ), child: Material( color: Colors.transparent, child: InkWell( onTap: () => _showGajiDetail(item['id_penggajian']), borderRadius: BorderRadius.circular(16), child: Padding( padding: const EdgeInsets.all(16), child: Row( children: [ Container( width: 48, height: 48, decoration: BoxDecoration( color: _amberDim, borderRadius: BorderRadius.circular(12), border: Border.all(color: _amber.withOpacity(0.3)), ), child: const Icon(Icons.receipt_long_rounded, color: _amber, size: 22), ), const SizedBox(width: 14), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Periode: ${item['nama_bulan'] ?? ''} ${item['periode_tahun'] ?? ''}', style: const TextStyle( color: _t1, fontWeight: FontWeight.w700, fontSize: 14)), const SizedBox(height: 4), Text('Dibayar: ${item['tanggal_bayar'] ?? '-'}', style: const TextStyle(color: _t2, fontSize: 11)), ], ), ), Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text(_formatCurrency(item['gaji_bersih']), style: const TextStyle( color: _green, fontWeight: FontWeight.w800, fontSize: 15)), const SizedBox(height: 4), Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), decoration: BoxDecoration( color: (item['is_paid'] ?? false) ? _greenDim : _amberDim, borderRadius: BorderRadius.circular(6), ), child: Text((item['is_paid'] ?? false) ? 'LUNAS' : 'BELUM DIBAYAR', style: TextStyle( color: (item['is_paid'] ?? false) ? _green : _amber, fontSize: 9, fontWeight: FontWeight.w900, letterSpacing: 0.5)), ), ], ), ], ), ), ), ), ); } void _showGajiDetail(int id) { showModalBottomSheet( context: context, isScrollControlled: true, backgroundColor: Colors.transparent, builder: (context) => _GajiDetailSheet(id: id, apiService: _apiService), ); } } class _GajiDetailSheet extends StatefulWidget { final int id; final GajiApi apiService; const _GajiDetailSheet({required this.id, required this.apiService}); @override State<_GajiDetailSheet> createState() => _GajiDetailSheetState(); } class _GajiDetailSheetState extends State<_GajiDetailSheet> { bool _isLoading = true; dynamic _detail; @override void initState() { super.initState(); _fetchDetail(); } Future _fetchDetail() async { final res = await widget.apiService.getDetail(widget.id); if (mounted) { setState(() { _detail = res['data']?['header']; // We use 'header' for top summary _isLoading = false; }); } } String _formatCurrency(dynamic amount) { final formatter = NumberFormat.currency(locale: 'id_ID', symbol: 'Rp ', decimalDigits: 0); return formatter.format(amount ?? 0); } @override Widget build(BuildContext context) { return DraggableScrollableSheet( initialChildSize: 0.8, maxChildSize: 0.95, minChildSize: 0.5, builder: (_, controller) => Container( decoration: const BoxDecoration( color: _bg1, borderRadius: BorderRadius.vertical(top: Radius.circular(24)), ), child: _isLoading ? const Center(child: CircularProgressIndicator(color: _amber)) : Column( children: [ const SizedBox(height: 12), Container(width: 40, height: 4, decoration: BoxDecoration(color: _line2, borderRadius: BorderRadius.circular(2))), const SizedBox(height: 24), Expanded( child: ListView( controller: controller, padding: const EdgeInsets.symmetric(horizontal: 24), children: [ Center( child: Column( children: [ const Text('SLIP GAJI TEKNISI', style: TextStyle(color: _t2, fontSize: 11, fontWeight: FontWeight.w800, letterSpacing: 2)), const SizedBox(height: 8), Text(_detail['periode'] ?? '-', style: const TextStyle(color: _t1, fontSize: 18, fontWeight: FontWeight.w800)), const SizedBox(height: 24), ], ), ), _buildInfoSection('RINCIAN PENDAPATAN', [ _buildDetailRow('Gaji Pokok', _formatCurrency(_detail['gaji_pokok'] ?? 0)), _buildDetailRow('Uang Makan', _formatCurrency(_detail['potongan_makan'] ?? 0)), // It's usually a static or dynamic field _buildDetailRow('Insentif Pekerjaan', _formatCurrency(_detail['gaji_kotor'] ?? 0), isBold: true), ]), const SizedBox(height: 20), _buildInfoSection('POTONGAN', [ _buildDetailRow('Potongan Kasbon', '- ${_formatCurrency(_detail['potongan_kasbon'])}', color: _rose), _buildDetailRow('Potongan Absensi', '- ${_formatCurrency(_detail['potongan_absensi'])}', color: _rose), ]), const Divider(height: 48, color: _line2), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text('TOTAL DITERIMA', style: TextStyle(color: _t1, fontSize: 14, fontWeight: FontWeight.w800)), Text(_formatCurrency(_detail['gaji_bersih']), style: const TextStyle(color: _green, fontSize: 22, fontWeight: FontWeight.w900)), ], ), const SizedBox(height: 32), Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: _bg2, borderRadius: BorderRadius.circular(12), border: Border.all(color: _line2), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('CATATAN PEKERJAAN', style: TextStyle(color: _t2, fontSize: 10, fontWeight: FontWeight.w800, letterSpacing: 1)), const SizedBox(height: 8), Text(_detail['rincian_pekerjaan'] ?? 'Tidak ada rincian', style: const TextStyle(color: _t1, fontSize: 12, height: 1.5)), ], ), ), const SizedBox(height: 40), ], ), ), ], ), ), ); } Widget _buildInfoSection(String title, List children) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(title, style: const TextStyle(color: _t3, fontSize: 10, fontWeight: FontWeight.w900, letterSpacing: 1.2)), const SizedBox(height: 12), ...children, ], ); } Widget _buildDetailRow(String label, String value, {bool isBold = false, Color? color}) { return Padding( padding: const EdgeInsets.only(bottom: 8), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(label, style: TextStyle(color: _t2, fontSize: 13, fontWeight: isBold ? FontWeight.w700 : FontWeight.w400)), Text(value, style: TextStyle(color: color ?? _t1, fontSize: 13, fontWeight: isBold ? FontWeight.w800 : FontWeight.w600)), ], ), ); } }