feat: update
This commit is contained in:
parent
cc8012354f
commit
79585b253d
@ -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(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user