import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:intl/intl.dart'; import '../../config/app_config.dart'; import '../../models/document_model.dart'; import '../../main.dart'; import 'pending_documents_provider.dart'; import '../editor/pdf_service.dart'; import '../../core/network/api_client.dart'; import 'pending_document_card.dart'; import 'document_card.dart'; class DocumentsScreen extends ConsumerStatefulWidget { const DocumentsScreen({super.key}); @override ConsumerState createState() => _DocumentsScreenState(); } class _DocumentsScreenState extends ConsumerState { final _searchController = TextEditingController(); List _allDocuments = []; List _filteredDocuments = []; String _selectedTab = 'DO'; // 'DO' | 'Product' @override void initState() { super.initState(); _loadDocuments(); _searchController.addListener(_onSearchChanged); } Future _loadDocuments() async { final localStorage = ref.read(localStorageProvider); var docs = localStorage.getAllDocuments(); final prefs = await SharedPreferences.getInstance(); final currentStoreName = prefs.getString('nama_toko') ?? 'PM KELAPA DUA KARAWACI'; bool databaseUpdated = false; final hasProductScan = docs.any((doc) => doc.orderUntuk == 'PRODUCT SCAN' && doc.kepadaYth == currentStoreName); if (!hasProductScan) { final mockProdDoc = DocumentModel( id: 'MOCK-PROD-SCAN-1', tanggal: DateFormat('dd MMMM yyyy').format(DateTime.now().subtract(const Duration(days: 1))), noPo: 'PO-PROD-12010119', noSo: '1002003004', noDo: 'Batch 1', kepadaYth: currentStoreName, orderUntuk: 'PRODUCT SCAN', alamat: 'Product Scan: Expired 15/12/2026', platTruk: 'B 1234 PFM', namaDriver: 'PRODUCT SCAN', namaPenerima: 'STORE STAFF', items: [ DocumentItem( nomorSku: '12010119', namaBarang: 'FIESTA SPICY CHICKEN NUGGET 500 GR', banyak: '1', jumlah: '1', ) ], createdAt: DateTime.now().subtract(const Duration(days: 1)), ); await localStorage.saveDocument(mockProdDoc); databaseUpdated = true; } final hasDoScan = docs.any((doc) => doc.orderUntuk != 'PRODUCT SCAN' && doc.kepadaYth == currentStoreName); if (!hasDoScan) { final mockDoDoc = DocumentModel( id: 'MOCK-DO-SCAN-1', tanggal: DateFormat('dd MMMM yyyy').format(DateTime.now().subtract(const Duration(days: 2))), noPo: 'PO-9923488', noSo: '1002008892', noDo: 'DO-9988223', kepadaYth: currentStoreName, orderUntuk: 'DELIVERY ORDER', alamat: 'Jl. Boulevard Raya No. 12, Karawaci', platTruk: 'B 9876 PFM', namaDriver: 'Budi Santoso', namaPenerima: 'Staff Toko', items: [ DocumentItem( nomorSku: '12010119', namaBarang: 'FIESTA SPICY CHICKEN NUGGET 500 GR', banyak: '10', jumlah: '10', ) ], createdAt: DateTime.now().subtract(const Duration(days: 2)), ); await localStorage.saveDocument(mockDoDoc); databaseUpdated = true; } if (databaseUpdated) { docs = localStorage.getAllDocuments(); } setState(() { _allDocuments = docs; _filteredDocuments = docs; }); try { final apiClient = ref.read(apiClientProvider); final response = await apiClient.client.get(AppConfig.fetchDocumentsEndpoint); final List serverData = response.data['data'] ?? []; final List serverDocs = serverData .map((json) => DocumentModel.fromJson(Map.from(json))) .toList(); for (var doc in serverDocs) { await localStorage.saveDocument(doc); } final updatedDocs = localStorage.getAllDocuments(); if (mounted) { setState(() { _allDocuments = updatedDocs; _filteredDocuments = updatedDocs; }); } } catch (e) { debugPrint('Offline or failed fetching: $e'); } } void _onSearchChanged() { final query = _searchController.text.toLowerCase(); setState(() { _filteredDocuments = _allDocuments.where((doc) { return doc.noDo.toLowerCase().contains(query) || doc.noPo.toString().contains(query) || doc.noSo.toString().contains(query) || doc.tanggal.toLowerCase().contains(query) || doc.kepadaYth.toLowerCase().contains(query); }).toList(); }); } @override void dispose() { _searchController.dispose(); super.dispose(); } Future _printReceipt(DocumentModel doc) async { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Membuat PDF...'), duration: Duration(seconds: 1), backgroundColor: AppConfig.secondaryColor), ); await PdfService.generateAndPrintReceipt(doc); } @override Widget build(BuildContext context) { final pendingDocs = ref.watch(pendingDocumentsProvider); final tabFilteredDocs = _filteredDocuments.where((doc) { final isProduct = doc.orderUntuk == 'PRODUCT SCAN'; if (_selectedTab == 'DO') return !isProduct; if (_selectedTab == 'Product') return isProduct; return true; }).toList(); final Map> groupedDocs = {}; for (var doc in tabFilteredDocs) { final date = doc.createdAt; final monthYearStr = DateFormat('MMMM yyyy').format(date); if (!groupedDocs.containsKey(monthYearStr)) { groupedDocs[monthYearStr] = []; } groupedDocs[monthYearStr]!.add(doc); } final List listItems = []; if (pendingDocs.isNotEmpty) { listItems.add( Padding( padding: const EdgeInsets.only(bottom: AppConfig.spaceSmall), child: Text('Tertunda & Diproses', style: AppConfig.h3), ), ); listItems.addAll(pendingDocs.map((doc) => PendingDocumentCard( doc: doc, onReload: () { if (mounted) _loadDocuments(); }, ))); listItems.add(const SizedBox(height: AppConfig.spaceLarge)); listItems.add(const Divider()); listItems.add(const SizedBox(height: AppConfig.spaceLarge)); } if (groupedDocs.isNotEmpty) { groupedDocs.forEach((monthYear, docs) { listItems.add( Padding( padding: const EdgeInsets.only(top: AppConfig.spaceLarge, bottom: AppConfig.spaceSmall), child: Text( monthYear, style: AppConfig.bodyMedium.copyWith( fontWeight: FontWeight.bold, color: AppConfig.textSecondary, ), ), ), ); listItems.addAll(docs.map((doc) => DocumentCard( doc: doc, onPrint: _printReceipt, ))); }); } return Scaffold( appBar: AppBar( title: const Text('Dokumen'), leading: IconButton(icon: const Icon(Icons.arrow_back), onPressed: () => context.go('/camera')), ), body: Column( children: [ Container( padding: AppConfig.paddingLarge, color: AppConfig.surfaceColor, child: TextField( controller: _searchController, decoration: const InputDecoration( hintText: 'Cari berdasarkan No PO, No SO, DO, Tanggal...', prefixIcon: Icon(Icons.search), ), ), ), _buildTabSwitcher(), Expanded( child: (pendingDocs.isEmpty && tabFilteredDocs.isEmpty) ? Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.folder_open, size: 64, color: AppConfig.textSecondary.withOpacity(0.5)), const SizedBox(height: AppConfig.spaceLarge), Text('Tidak ada dokumen ditemukan', style: AppConfig.bodyLarge), ], ), ) : ListView( padding: const EdgeInsets.symmetric(horizontal: AppConfig.spaceLarge), children: listItems, ), ), ], ), ); } Widget _buildTabSwitcher() { return Container( margin: const EdgeInsets.symmetric(horizontal: AppConfig.spaceLarge, vertical: AppConfig.spaceSmall), padding: const EdgeInsets.all(4), decoration: BoxDecoration(color: Colors.grey.shade100, borderRadius: BorderRadius.circular(AppConfig.borderRadiusMedium)), child: Row( children: [ _buildTabItem('DO', 'DO Scan'), _buildTabItem('Product', 'Product Scan'), ], ), ); } Widget _buildTabItem(String value, String label) { final isActive = _selectedTab == value; return Expanded( child: GestureDetector( onTap: () => setState(() { _selectedTab = value; }), child: Container( padding: const EdgeInsets.symmetric(vertical: 8), decoration: BoxDecoration( color: isActive ? Colors.white : Colors.transparent, borderRadius: BorderRadius.circular(AppConfig.borderRadiusSmall), boxShadow: isActive ? [BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 4, offset: const Offset(0, 2))] : null, ), child: Center( child: Text( label, style: TextStyle( fontWeight: isActive ? FontWeight.bold : FontWeight.normal, color: isActive ? AppConfig.primaryColor : AppConfig.textSecondary, ), ), ), ), ), ); } }