feat: update

This commit is contained in:
efrilm 2025-08-15 16:49:24 +07:00
parent cc8012354f
commit 79585b253d

View File

@ -28,228 +28,222 @@ class _InventoryReportWidgetState extends State<InventoryReportWidget> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Expanded( return Container(
flex: 4, width: double.infinity,
child: Container( decoration: BoxDecoration(
width: double.infinity, color: AppColors.white,
decoration: BoxDecoration( border: Border.all(color: AppColors.stroke, width: 1),
color: AppColors.white, ),
border: Border.all(color: AppColors.stroke, width: 1), child: SingleChildScrollView(
), child: Column(
child: SingleChildScrollView( crossAxisAlignment: CrossAxisAlignment.start,
child: Column( children: [
crossAxisAlignment: CrossAxisAlignment.start, // Report Header
children: [ Container(
// Report Header width: double.infinity,
Container( padding: const EdgeInsets.all(20),
width: double.infinity, decoration: BoxDecoration(
padding: const EdgeInsets.all(20), color: AppColors.light,
decoration: BoxDecoration( border: Border(
color: AppColors.light, bottom: BorderSide(color: AppColors.stroke, width: 1),
border: Border(
bottom: BorderSide(color: AppColors.stroke, width: 1),
),
), ),
child: Row( ),
mainAxisAlignment: MainAxisAlignment.spaceBetween, child: Row(
children: [ mainAxisAlignment: MainAxisAlignment.spaceBetween,
Column( children: [
crossAxisAlignment: CrossAxisAlignment.start, Column(
children: [ crossAxisAlignment: CrossAxisAlignment.start,
Text( children: [
widget.title, Text(
style: TextStyle( widget.title,
fontSize: 18, style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 18,
color: AppColors.primary, fontWeight: FontWeight.bold,
), color: AppColors.primary,
), ),
const SizedBox(height: 4), ),
Text( const SizedBox(height: 4),
widget.searchDateFormatted, Text(
style: TextStyle( widget.searchDateFormatted,
fontSize: 12, style: TextStyle(
color: AppColors.greyDark, fontSize: 12,
), color: AppColors.greyDark,
), ),
], ),
), ],
Row( ),
children: [ Row(
// Download Button children: [
GestureDetector( // Download Button
onTap: () async { GestureDetector(
try { onTap: () async {
final status = try {
await PermessionHelper().checkPermission(); final status =
if (status) { await PermessionHelper().checkPermission();
final pdfFile = if (status) {
await InventoryReport.previewPdf( final pdfFile = await InventoryReport.previewPdf(
searchDateFormatted: searchDateFormatted: widget.searchDateFormatted,
widget.searchDateFormatted, inventory: widget.inventory,
inventory: widget.inventory, );
); log("pdfFile: $pdfFile");
log("pdfFile: $pdfFile"); await HelperPdfService.openFile(pdfFile);
await HelperPdfService.openFile(pdfFile); } else {
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text(
'Storage permission is required to save PDF'),
backgroundColor: Colors.red,
),
);
}
} catch (e) {
log("Error generating PDF: $e");
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( const SnackBar(
content: Text('Failed to generate PDF: $e'), content: Text(
'Storage permission is required to save PDF'),
backgroundColor: Colors.red, backgroundColor: Colors.red,
), ),
); );
} }
}, } catch (e) {
child: Container( log("Error generating PDF: $e");
padding: const EdgeInsets.all(10), ScaffoldMessenger.of(context).showSnackBar(
decoration: BoxDecoration( SnackBar(
color: AppColors.primary.withOpacity(0.1), content: Text('Failed to generate PDF: $e'),
borderRadius: BorderRadius.circular(8), backgroundColor: Colors.red,
border: Border.all( ),
color: AppColors.primary, width: 1), );
), }
child: Icon( },
Icons.download_outlined, child: Container(
size: 18, padding: const EdgeInsets.all(10),
color: AppColors.primary,
),
),
),
const SizedBox(width: 12),
// Status Badge
Container(
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 6,
),
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppColors.green.withOpacity(0.1), color: AppColors.primary.withOpacity(0.1),
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(8),
border: border:
Border.all(color: AppColors.green, width: 1), Border.all(color: AppColors.primary, width: 1),
), ),
child: Row( child: Icon(
mainAxisSize: MainAxisSize.min, Icons.download_outlined,
children: [ size: 18,
Icon( color: AppColors.primary,
Icons.check_circle, ),
size: 14, ),
),
const SizedBox(width: 12),
// Status Badge
Container(
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 6,
),
decoration: BoxDecoration(
color: AppColors.green.withOpacity(0.1),
borderRadius: BorderRadius.circular(20),
border: Border.all(color: AppColors.green, width: 1),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.check_circle,
size: 14,
color: AppColors.green,
),
const SizedBox(width: 6),
Text(
'Aktif',
style: TextStyle(
fontSize: 12,
color: AppColors.green, color: AppColors.green,
fontWeight: FontWeight.w600,
), ),
const SizedBox(width: 6), ),
Text( ],
'Aktif',
style: TextStyle(
fontSize: 12,
color: AppColors.green,
fontWeight: FontWeight.w600,
),
),
],
),
), ),
], ),
), ],
], ),
), ],
), ),
),
// Summary Section // Summary Section
Padding( Padding(
padding: const EdgeInsets.all(20), padding: const EdgeInsets.all(20),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row( Row(
children: [ children: [
Icon( Icon(
Icons.analytics_outlined, Icons.analytics_outlined,
size: 20, size: 20,
color: AppColors.primary, color: AppColors.primary,
),
const SizedBox(width: 8),
Text(
'Ringkasan Inventori',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: AppColors.black,
), ),
const SizedBox(width: 8), ),
Text( ],
'Ringkasan Inventori', ),
style: TextStyle( const SizedBox(height: 16),
fontSize: 16,
fontWeight: FontWeight.bold,
color: AppColors.black,
),
),
],
),
const SizedBox(height: 16),
// Summary Grid // Summary Grid
GridView.count( GridView.count(
shrinkWrap: true, shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
crossAxisCount: 3, crossAxisCount: 3,
childAspectRatio: 2.2, childAspectRatio: 2.2,
mainAxisSpacing: 12, mainAxisSpacing: 12,
crossAxisSpacing: 12, crossAxisSpacing: 12,
children: [ children: [
_buildSummaryCard( _buildSummaryCard(
'Total Produk', 'Total Produk',
(widget.inventory.summary.totalProducts).toString(), (widget.inventory.summary.totalProducts).toString(),
AppColors.primary, AppColors.primary,
Icons.inventory_2_outlined, Icons.inventory_2_outlined,
), ),
_buildSummaryCard( _buildSummaryCard(
'Total Bahan', 'Total Bahan',
widget.inventory.summary.totalIngredients.toString(), widget.inventory.summary.totalIngredients.toString(),
AppColors.subtitle, AppColors.subtitle,
Icons.list_alt_outlined, Icons.list_alt_outlined,
), ),
_buildSummaryCard( _buildSummaryCard(
'Total Nilai', 'Total Nilai',
widget.inventory.summary.totalValue widget.inventory.summary.totalValue
.toString() .toString()
.currencyFormatRpV2, .currencyFormatRpV2,
AppColors.green, AppColors.green,
Icons.monetization_on_outlined, Icons.monetization_on_outlined,
), ),
], ],
), ),
], ],
),
), ),
),
// Divider // Divider
Container( Container(
height: 1, height: 1,
margin: const EdgeInsets.symmetric(horizontal: 20), margin: const EdgeInsets.symmetric(horizontal: 20),
color: AppColors.stroke, color: AppColors.stroke,
),
// Tabs
Container(
padding: const EdgeInsets.all(20),
child: Row(
children: [
_buildTab('Produk', 0),
const SizedBox(width: 12),
_buildTab('Bahan Baku', 1),
],
), ),
),
// Tabs // Content based on selected tab
Container( _selectedTabIndex == 0
padding: const EdgeInsets.all(20), ? _buildProductsContent()
child: Row( : _buildIngredientsContent(),
children: [ ],
_buildTab('Produk', 0),
const SizedBox(width: 12),
_buildTab('Bahan Baku', 1),
],
),
),
// Content based on selected tab
_selectedTabIndex == 0
? _buildProductsContent()
: _buildIngredientsContent(),
],
),
), ),
), ),
); );