fix final3

This commit is contained in:
rhanarmt 2026-06-06 14:08:42 +07:00
parent c92c80a7c6
commit 1a34474c37
6 changed files with 1753 additions and 1580 deletions

View File

@ -36,6 +36,10 @@ class _DashboardScreenState extends State<DashboardScreen> with RouteAware {
_controller.loadDashboard(showLoading: false);
}
Future<void> _refreshDashboard() {
return _controller.loadDashboard(showLoading: false);
}
@override
Widget build(BuildContext context) {
final topPadding = MediaQuery.of(context).padding.top;
@ -69,6 +73,25 @@ class _DashboardScreenState extends State<DashboardScreen> with RouteAware {
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.3),
borderRadius: BorderRadius.circular(10),
),
child: IconButton(
tooltip: 'Refresh',
icon: const Icon(
Icons.refresh,
color: Colors.white,
size: 20,
),
onPressed: _refreshDashboard,
padding: EdgeInsets.zero,
),
),
const SizedBox(width: 10),
Stack(
children: [
Container(
@ -188,7 +211,11 @@ class _DashboardScreenState extends State<DashboardScreen> with RouteAware {
),
),
),
body: SingleChildScrollView(
body: RefreshIndicator(
onRefresh: _refreshDashboard,
color: AppColors.primaryBrown,
child: SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
child: Column(
children: [
Padding(
@ -208,10 +235,12 @@ class _DashboardScreenState extends State<DashboardScreen> with RouteAware {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
'Penggunaan Bahan',
@ -318,7 +347,8 @@ class _DashboardScreenState extends State<DashboardScreen> with RouteAware {
style: AppTextStyles.bodySmall
.copyWith(
color:
AppColors.textTertiary,
AppColors
.textTertiary,
),
),
],
@ -357,7 +387,9 @@ class _DashboardScreenState extends State<DashboardScreen> with RouteAware {
Expanded(
child: ElevatedButton.icon(
onPressed: () {
Navigator.of(context).pushNamed('/prediction');
Navigator.of(
context,
).pushNamed('/prediction');
},
icon: const Icon(Icons.trending_up, size: 20),
label: const Text(
@ -384,7 +416,9 @@ class _DashboardScreenState extends State<DashboardScreen> with RouteAware {
Expanded(
child: ElevatedButton.icon(
onPressed: () {
Navigator.of(context).pushNamed('/transaction');
Navigator.of(
context,
).pushNamed('/transaction');
},
icon: const Icon(Icons.check_circle, size: 20),
label: const Text(
@ -416,6 +450,7 @@ class _DashboardScreenState extends State<DashboardScreen> with RouteAware {
],
),
),
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _controller.selectedIndex,
onTap: (index) {

View File

@ -27,6 +27,10 @@ class _PredictionScreenState extends State<PredictionScreen> {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(error)));
}
Future<void> _refreshRecipes() {
return _loadRecipes();
}
Future<void> _submitProduction() async {
if (_controller.isSubmitting) return;
final result = await _controller.submitProduction();
@ -203,6 +207,24 @@ class _PredictionScreenState extends State<PredictionScreen> {
],
),
),
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.3),
borderRadius: BorderRadius.circular(10),
),
child: IconButton(
tooltip: 'Refresh',
icon: const Icon(
Icons.refresh,
color: Colors.white,
size: 20,
),
onPressed: _refreshRecipes,
padding: EdgeInsets.zero,
),
),
],
),
const SizedBox(height: 12),
@ -267,7 +289,10 @@ class _PredictionScreenState extends State<PredictionScreen> {
),
),
),
body:
body: RefreshIndicator(
onRefresh: _refreshRecipes,
color: const Color(0xFFA89080),
child:
_controller.isLoading
? Center(
child: Column(
@ -300,6 +325,7 @@ class _PredictionScreenState extends State<PredictionScreen> {
),
)
: SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
child: Column(
children: [
Padding(
@ -321,7 +347,8 @@ class _PredictionScreenState extends State<PredictionScreen> {
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
const Text(
'Rencana Produksi',
@ -351,13 +378,13 @@ class _PredictionScreenState extends State<PredictionScreen> {
color: const Color(0xFFE5E7EB),
width: 1,
),
borderRadius: BorderRadius.circular(
10,
),
borderRadius:
BorderRadius.circular(10),
),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
value: _controller.selectedRecipe,
value:
_controller.selectedRecipe,
hint: const Padding(
padding: EdgeInsets.symmetric(
horizontal: 12,
@ -406,7 +433,8 @@ class _PredictionScreenState extends State<PredictionScreen> {
)
.toList(),
onChanged:
_controller.setSelectedRecipe,
_controller
.setSelectedRecipe,
),
),
),
@ -430,7 +458,8 @@ class _PredictionScreenState extends State<PredictionScreen> {
controller: _productionController,
keyboardType: TextInputType.number,
onChanged:
_controller.setProductionQuantity,
_controller
.setProductionQuantity,
decoration: InputDecoration(
hintText: '0',
hintStyle: const TextStyle(
@ -515,7 +544,8 @@ class _PredictionScreenState extends State<PredictionScreen> {
color: Color(0xFFE5E7EB),
width: 1,
),
padding: const EdgeInsets.symmetric(
padding:
const EdgeInsets.symmetric(
horizontal: 20,
),
shape: RoundedRectangleBorder(
@ -651,7 +681,9 @@ class _PredictionScreenState extends State<PredictionScreen> {
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
color: Colors.black.withOpacity(
0.05,
),
blurRadius: 10,
offset: const Offset(0, 2),
),
@ -672,7 +704,9 @@ class _PredictionScreenState extends State<PredictionScreen> {
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
color: Colors.black.withOpacity(
0.05,
),
blurRadius: 10,
offset: const Offset(0, 2),
),
@ -686,14 +720,16 @@ class _PredictionScreenState extends State<PredictionScreen> {
final isLast =
entry.key ==
displayIngredients.length - 1;
final ingredient = entry.value.key;
final ingredient =
entry.value.key;
final details = entry.value.value;
final isSelected = _controller
.isIngredientSelected(
ingredient,
);
final unit =
(details['unit'] as String?) ??
(details['unit']
as String?) ??
_controller.getIngredientUnit(
ingredient,
);
@ -721,7 +757,8 @@ class _PredictionScreenState extends State<PredictionScreen> {
ingredient,
);
final isSufficient =
stockKg >= requiredInStockUnit;
stockKg >=
requiredInStockUnit;
final statusColor =
isSelected
? (isSufficient
@ -742,7 +779,8 @@ class _PredictionScreenState extends State<PredictionScreen> {
return Column(
children: [
Padding(
padding: const EdgeInsets.all(
padding:
const EdgeInsets.all(
14,
),
child: Column(
@ -754,7 +792,9 @@ class _PredictionScreenState extends State<PredictionScreen> {
children: [
Checkbox(
value: isSelected,
onChanged: (value) {
onChanged: (
value,
) {
_controller
.setIngredientSelection(
ingredient,
@ -939,7 +979,9 @@ class _PredictionScreenState extends State<PredictionScreen> {
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
color: Colors.black.withOpacity(
0.05,
),
blurRadius: 10,
offset: const Offset(0, 2),
),
@ -984,7 +1026,9 @@ class _PredictionScreenState extends State<PredictionScreen> {
fontSize: 12,
fontWeight:
FontWeight.w700,
color: Color(0xFFDC2626),
color: Color(
0xFFDC2626,
),
),
),
],
@ -1014,7 +1058,9 @@ class _PredictionScreenState extends State<PredictionScreen> {
>(Colors.white),
),
)
: const Icon(Icons.check_circle),
: const Icon(
Icons.check_circle,
),
label: Text(
_controller.isSubmitting
? 'Memproses...'
@ -1184,6 +1230,7 @@ class _PredictionScreenState extends State<PredictionScreen> {
],
),
),
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: 3,
type: BottomNavigationBarType.fixed,

View File

@ -45,6 +45,10 @@ class _ProductListScreenState extends State<ProductListScreen> with RouteAware {
_controller.loadProducts();
}
Future<void> _refreshProducts() {
return _controller.loadProducts();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
@ -111,6 +115,25 @@ class _ProductListScreenState extends State<ProductListScreen> with RouteAware {
),
],
),
const Spacer(),
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.3),
borderRadius: BorderRadius.circular(10),
),
child: IconButton(
tooltip: 'Refresh',
icon: const Icon(
Icons.refresh,
color: Colors.white,
size: 20,
),
onPressed: _refreshProducts,
padding: EdgeInsets.zero,
),
),
],
),
),
@ -155,7 +178,7 @@ class _ProductListScreenState extends State<ProductListScreen> with RouteAware {
),
),
body: RefreshIndicator(
onRefresh: _controller.loadProducts,
onRefresh: _refreshProducts,
color: AppColors.primaryBrown,
child:
_controller.isLoading

View File

@ -61,6 +61,10 @@ class _ReportScreenState extends State<ReportScreen> {
_controller.startAutoRefresh();
}
Future<void> _refreshReports() {
return _controller.loadReports();
}
double _sectionMaxHeight() {
final height = MediaQuery.of(context).size.height * 0.35;
if (height < 220) return 220;
@ -82,6 +86,13 @@ class _ReportScreenState extends State<ReportScreen> {
'Laporan & Analitik',
style: AppTextStyles.headlineLarge.copyWith(color: Colors.white),
),
actions: [
IconButton(
tooltip: 'Refresh',
icon: const Icon(Icons.refresh, color: Colors.white),
onPressed: _refreshReports,
),
],
),
body: AnimatedBuilder(
animation: _controller,
@ -98,7 +109,7 @@ class _ReportScreenState extends State<ReportScreen> {
final List<double> demandTrend = _controller.demandTrend;
return RefreshIndicator(
onRefresh: () => _controller.loadReports(),
onRefresh: _refreshReports,
child: SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
padding: const EdgeInsets.all(16),

View File

@ -125,8 +125,19 @@ class _SettingsScreenState extends State<SettingsScreen> {
'Pengaturan',
style: AppTextStyles.headlineLarge.copyWith(color: Colors.white),
),
actions: [
IconButton(
tooltip: 'Refresh',
icon: const Icon(Icons.refresh, color: Colors.white),
onPressed: _loadAccount,
),
body: SingleChildScrollView(
],
),
body: RefreshIndicator(
onRefresh: _loadAccount,
color: AppColors.primaryBrown,
child: SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
@ -167,6 +178,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
),
),
),
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _controller.selectedIndex,
onTap: (index) {

View File

@ -48,6 +48,20 @@ class _TransactionScreenState extends State<TransactionScreen> {
);
}
Future<void> _refreshProducts() async {
try {
await _controller.loadProducts();
} catch (_) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: const Text('Gagal memuat data produk'),
backgroundColor: AppColors.statusError,
),
);
}
}
Future<void> _submitAllTransactions() async {
final result = await _controller.submitAllTransactions();
@ -504,8 +518,19 @@ class _TransactionScreenState extends State<TransactionScreen> {
icon: const Icon(Icons.arrow_back, color: Colors.white),
onPressed: () => Navigator.pop(context),
),
actions: [
IconButton(
tooltip: 'Refresh',
icon: const Icon(Icons.refresh, color: Colors.white),
onPressed: _refreshProducts,
),
body: SingleChildScrollView(
],
),
body: RefreshIndicator(
onRefresh: _refreshProducts,
color: AppColors.primaryBrown,
child: SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
@ -585,7 +610,9 @@ class _TransactionScreenState extends State<TransactionScreen> {
label: const Text('Tambah ke Keranjang'),
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.primaryBrown,
padding: const EdgeInsets.symmetric(vertical: 12),
padding: const EdgeInsets.symmetric(
vertical: 12,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
@ -600,8 +627,12 @@ class _TransactionScreenState extends State<TransactionScreen> {
icon: const Icon(Icons.add_circle_outline),
label: const Text('Tambah Produk Baru'),
style: OutlinedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 12),
side: BorderSide(color: AppColors.secondaryBlue),
padding: const EdgeInsets.symmetric(
vertical: 12,
),
side: BorderSide(
color: AppColors.secondaryBlue,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
@ -657,7 +688,9 @@ class _TransactionScreenState extends State<TransactionScreen> {
decoration: BoxDecoration(
color: AppColors.bgLight,
borderRadius: BorderRadius.circular(10),
border: Border.all(color: AppColors.grey300),
border: Border.all(
color: AppColors.grey300,
),
),
child: Column(
children: [
@ -672,16 +705,19 @@ class _TransactionScreenState extends State<TransactionScreen> {
children: [
Text(
item.productName,
style: AppTextStyles.labelLarge
style: AppTextStyles
.labelLarge
.copyWith(
color:
AppColors.textPrimary,
AppColors
.textPrimary,
),
),
const SizedBox(height: 4),
Text(
'Rp ${item.unitPrice} / unit',
style: AppTextStyles.labelSmall
style: AppTextStyles
.labelSmall
.copyWith(
color:
AppColors
@ -695,7 +731,8 @@ class _TransactionScreenState extends State<TransactionScreen> {
'Rp ${item.totalPrice}',
style: AppTextStyles.labelLarge
.copyWith(
color: AppColors.statusSuccess,
color:
AppColors.statusSuccess,
fontWeight: FontWeight.w700,
),
),
@ -719,9 +756,12 @@ class _TransactionScreenState extends State<TransactionScreen> {
width: 32,
height: 32,
decoration: BoxDecoration(
color: AppColors.primaryBrown,
color:
AppColors.primaryBrown,
borderRadius:
BorderRadius.circular(6),
BorderRadius.circular(
6,
),
),
child: const Icon(
Icons.remove,
@ -737,7 +777,8 @@ class _TransactionScreenState extends State<TransactionScreen> {
.copyWith(
color:
AppColors.textPrimary,
fontWeight: FontWeight.w600,
fontWeight:
FontWeight.w600,
),
),
const SizedBox(width: 12),
@ -752,9 +793,12 @@ class _TransactionScreenState extends State<TransactionScreen> {
width: 32,
height: 32,
decoration: BoxDecoration(
color: AppColors.primaryBrown,
color:
AppColors.primaryBrown,
borderRadius:
BorderRadius.circular(6),
BorderRadius.circular(
6,
),
),
child: const Icon(
Icons.add,
@ -767,9 +811,8 @@ class _TransactionScreenState extends State<TransactionScreen> {
),
GestureDetector(
onTap:
() => _controller.removeFromCart(
index,
),
() => _controller
.removeFromCart(index),
child: Container(
width: 32,
height: 32,
@ -827,7 +870,8 @@ class _TransactionScreenState extends State<TransactionScreen> {
final picked = await showDatePicker(
context: context,
initialDate:
_controller.selectedDate ?? DateTime.now(),
_controller.selectedDate ??
DateTime.now(),
firstDate: DateTime(2020),
lastDate: DateTime.now(),
);
@ -1001,6 +1045,7 @@ class _TransactionScreenState extends State<TransactionScreen> {
),
),
),
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _controller.selectedIndex,
onTap: (index) {