fix final3
This commit is contained in:
parent
c92c80a7c6
commit
1a34474c37
|
|
@ -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,232 +211,244 @@ class _DashboardScreenState extends State<DashboardScreen> with RouteAware {
|
|||
),
|
||||
),
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 8),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.bgWhite,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: [AppColors.shadowLight],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Penggunaan Bahan',
|
||||
style: AppTextStyles.headlineSmall
|
||||
.copyWith(
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
'Top 5 bahan paling sering digunakan',
|
||||
style: AppTextStyles.bodySmall.copyWith(
|
||||
color: AppColors.textTertiary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Icon(
|
||||
Icons.bar_chart_rounded,
|
||||
color: AppColors.secondaryBlue,
|
||||
size: 20,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
if (_controller.isLoading)
|
||||
const Center(child: CircularProgressIndicator())
|
||||
else
|
||||
Column(
|
||||
children:
|
||||
_controller.penggunaanBahan.map((item) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
bottom: 12,
|
||||
),
|
||||
child: _buildUsageRow(item),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.bgWhite,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: [AppColors.shadowLight],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.warning_amber_rounded,
|
||||
color: AppColors.statusWarning,
|
||||
size: 22,
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Text(
|
||||
'Stok Menipis',
|
||||
style: AppTextStyles.headlineSmall.copyWith(
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
if (_controller.isLoading)
|
||||
const Center(child: CircularProgressIndicator())
|
||||
else if (_controller.lowStockItems.isEmpty)
|
||||
Text(
|
||||
'Tidak ada stok kritis',
|
||||
style: AppTextStyles.bodySmall.copyWith(
|
||||
color: AppColors.textTertiary,
|
||||
),
|
||||
)
|
||||
else
|
||||
..._controller.lowStockItems.map((item) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16),
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
body: RefreshIndicator(
|
||||
onRefresh: _refreshDashboard,
|
||||
color: AppColors.primaryBrown,
|
||||
child: SingleChildScrollView(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 8),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.bgWhite,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: [AppColors.shadowLight],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
item['name'],
|
||||
style: AppTextStyles.labelLarge
|
||||
.copyWith(
|
||||
color:
|
||||
AppColors.textPrimary,
|
||||
),
|
||||
Text(
|
||||
'Penggunaan Bahan',
|
||||
style: AppTextStyles.headlineSmall
|
||||
.copyWith(
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'Stok: ${item['stock']}',
|
||||
style: AppTextStyles.bodySmall
|
||||
.copyWith(
|
||||
color:
|
||||
AppColors.textTertiary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 6,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: item['statusColor'],
|
||||
borderRadius: BorderRadius.circular(
|
||||
6,
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
item['status'],
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.white,
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
'Top 5 bahan paling sering digunakan',
|
||||
style: AppTextStyles.bodySmall.copyWith(
|
||||
color: AppColors.textTertiary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
Icon(
|
||||
Icons.bar_chart_rounded,
|
||||
color: AppColors.secondaryBlue,
|
||||
size: 20,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
if (_controller.isLoading)
|
||||
const Center(child: CircularProgressIndicator())
|
||||
else
|
||||
Column(
|
||||
children:
|
||||
_controller.penggunaanBahan.map((item) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
bottom: 12,
|
||||
),
|
||||
child: _buildUsageRow(item),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.bgWhite,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: [AppColors.shadowLight],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.warning_amber_rounded,
|
||||
color: AppColors.statusWarning,
|
||||
size: 22,
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Text(
|
||||
'Stok Menipis',
|
||||
style: AppTextStyles.headlineSmall.copyWith(
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
if (_controller.isLoading)
|
||||
const Center(child: CircularProgressIndicator())
|
||||
else if (_controller.lowStockItems.isEmpty)
|
||||
Text(
|
||||
'Tidak ada stok kritis',
|
||||
style: AppTextStyles.bodySmall.copyWith(
|
||||
color: AppColors.textTertiary,
|
||||
),
|
||||
)
|
||||
else
|
||||
..._controller.lowStockItems.map((item) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16),
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
item['name'],
|
||||
style: AppTextStyles.labelLarge
|
||||
.copyWith(
|
||||
color:
|
||||
AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'Stok: ${item['stock']}',
|
||||
style: AppTextStyles.bodySmall
|
||||
.copyWith(
|
||||
color:
|
||||
AppColors
|
||||
.textTertiary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 6,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: item['statusColor'],
|
||||
borderRadius: BorderRadius.circular(
|
||||
6,
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
item['status'],
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
Navigator.of(
|
||||
context,
|
||||
).pushNamed('/prediction');
|
||||
},
|
||||
icon: const Icon(Icons.trending_up, size: 20),
|
||||
label: const Text(
|
||||
'Lihat Prediksi',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.secondaryBlue,
|
||||
foregroundColor: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 16,
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
),
|
||||
elevation: 2,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
Navigator.of(
|
||||
context,
|
||||
).pushNamed('/transaction');
|
||||
},
|
||||
icon: const Icon(Icons.check_circle, size: 20),
|
||||
label: const Text(
|
||||
'Tambah Stok',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.statusSuccess,
|
||||
foregroundColor: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 16,
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
),
|
||||
elevation: 2,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pushNamed('/prediction');
|
||||
},
|
||||
icon: const Icon(Icons.trending_up, size: 20),
|
||||
label: const Text(
|
||||
'Lihat Prediksi',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.secondaryBlue,
|
||||
foregroundColor: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 16,
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
),
|
||||
elevation: 2,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pushNamed('/transaction');
|
||||
},
|
||||
icon: const Icon(Icons.check_circle, size: 20),
|
||||
label: const Text(
|
||||
'Tambah Stok',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.statusSuccess,
|
||||
foregroundColor: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 16,
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
),
|
||||
elevation: 2,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
],
|
||||
const SizedBox(height: 24),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
bottomNavigationBar: BottomNavigationBar(
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -125,45 +125,57 @@ 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(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildSectionTitle('Akun'),
|
||||
const SizedBox(height: 16),
|
||||
_buildAccountCard(),
|
||||
const SizedBox(height: 32),
|
||||
_buildSectionTitle('Keamanan'),
|
||||
const SizedBox(height: 16),
|
||||
_buildActionCard(
|
||||
icon: Icons.lock_outlined,
|
||||
title: 'Ubah Password',
|
||||
subtitle: 'Verifikasi OTP melalui email akun',
|
||||
onTap: _showResetPasswordDialog,
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: _showLogoutDialog,
|
||||
icon: const Icon(Icons.logout),
|
||||
label: const Text('Logout'),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.statusError,
|
||||
foregroundColor: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
body: RefreshIndicator(
|
||||
onRefresh: _loadAccount,
|
||||
color: AppColors.primaryBrown,
|
||||
child: SingleChildScrollView(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildSectionTitle('Akun'),
|
||||
const SizedBox(height: 16),
|
||||
_buildAccountCard(),
|
||||
const SizedBox(height: 32),
|
||||
_buildSectionTitle('Keamanan'),
|
||||
const SizedBox(height: 16),
|
||||
_buildActionCard(
|
||||
icon: Icons.lock_outlined,
|
||||
title: 'Ubah Password',
|
||||
subtitle: 'Verifikasi OTP melalui email akun',
|
||||
onTap: _showResetPasswordDialog,
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: _showLogoutDialog,
|
||||
icon: const Icon(Icons.logout),
|
||||
label: const Text('Logout'),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.statusError,
|
||||
foregroundColor: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
_buildAppInfo(),
|
||||
],
|
||||
const SizedBox(height: 32),
|
||||
_buildAppInfo(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -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,115 +518,24 @@ 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(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.bgWhite,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: [AppColors.shadowLight],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Pilih Produk',
|
||||
style: AppTextStyles.headlineSmall.copyWith(
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Produk',
|
||||
style: AppTextStyles.labelLarge.copyWith(
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
DropdownButtonFormField<String>(
|
||||
value: _controller.selectedProduct,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Pilih produk',
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
filled: true,
|
||||
fillColor: AppColors.bgLight,
|
||||
),
|
||||
items:
|
||||
_controller.products
|
||||
.map(
|
||||
(product) => DropdownMenuItem(
|
||||
value: product,
|
||||
child: Text(product),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
onChanged: _controller.setSelectedProduct,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Text(
|
||||
'Jumlah',
|
||||
style: AppTextStyles.labelLarge.copyWith(
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
TextField(
|
||||
controller: _controller.quantityController,
|
||||
keyboardType: TextInputType.number,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Masukkan jumlah unit',
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
filled: true,
|
||||
fillColor: AppColors.bgLight,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: _addToCart,
|
||||
icon: const Icon(Icons.add_shopping_cart),
|
||||
label: const Text('Tambah ke Keranjang'),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.primaryBrown,
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: OutlinedButton.icon(
|
||||
onPressed: _showAddProductDialog,
|
||||
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),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
if (_controller.cartItems.isNotEmpty) ...[
|
||||
body: RefreshIndicator(
|
||||
onRefresh: _refreshProducts,
|
||||
color: AppColors.primaryBrown,
|
||||
child: SingleChildScrollView(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
|
|
@ -623,282 +546,96 @@ class _TransactionScreenState extends State<TransactionScreen> {
|
|||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Keranjang (${_controller.cartItems.length})',
|
||||
style: AppTextStyles.headlineSmall.copyWith(
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: _controller.clearCart,
|
||||
child: Text(
|
||||
'Hapus Semua',
|
||||
style: AppTextStyles.labelMedium.copyWith(
|
||||
color: AppColors.statusError,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
ListView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: _controller.cartItems.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = _controller.cartItems[index];
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(bottom: 12),
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.bgLight,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(color: AppColors.grey300),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
item.productName,
|
||||
style: AppTextStyles.labelLarge
|
||||
.copyWith(
|
||||
color:
|
||||
AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'Rp ${item.unitPrice} / unit',
|
||||
style: AppTextStyles.labelSmall
|
||||
.copyWith(
|
||||
color:
|
||||
AppColors
|
||||
.textSecondary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'Rp ${item.totalPrice}',
|
||||
style: AppTextStyles.labelLarge
|
||||
.copyWith(
|
||||
color: AppColors.statusSuccess,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap:
|
||||
() => _controller
|
||||
.updateQuantity(
|
||||
index,
|
||||
item.quantity - 1,
|
||||
),
|
||||
child: Container(
|
||||
width: 32,
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.primaryBrown,
|
||||
borderRadius:
|
||||
BorderRadius.circular(6),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.remove,
|
||||
color: Colors.white,
|
||||
size: 18,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
item.quantity.toString(),
|
||||
style: AppTextStyles.labelLarge
|
||||
.copyWith(
|
||||
color:
|
||||
AppColors.textPrimary,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
GestureDetector(
|
||||
onTap:
|
||||
() => _controller
|
||||
.updateQuantity(
|
||||
index,
|
||||
item.quantity + 1,
|
||||
),
|
||||
child: Container(
|
||||
width: 32,
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.primaryBrown,
|
||||
borderRadius:
|
||||
BorderRadius.circular(6),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.add,
|
||||
color: Colors.white,
|
||||
size: 18,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
GestureDetector(
|
||||
onTap:
|
||||
() => _controller.removeFromCart(
|
||||
index,
|
||||
),
|
||||
child: Container(
|
||||
width: 32,
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.statusError
|
||||
.withValues(alpha: 0.1),
|
||||
borderRadius:
|
||||
BorderRadius.circular(6),
|
||||
),
|
||||
child: Icon(
|
||||
Icons.delete,
|
||||
color: AppColors.statusError,
|
||||
size: 18,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const Divider(),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Total Pembayaran',
|
||||
style: AppTextStyles.labelLarge.copyWith(
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'Rp ${_controller.totalPrice}',
|
||||
style: AppTextStyles.headlineSmall.copyWith(
|
||||
color: AppColors.primaryBrown,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
],
|
||||
Text(
|
||||
'Pilih Produk',
|
||||
style: AppTextStyles.headlineSmall.copyWith(
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Tanggal Stok Masuk',
|
||||
'Produk',
|
||||
style: AppTextStyles.labelLarge.copyWith(
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
GestureDetector(
|
||||
onTap: () async {
|
||||
final picked = await showDatePicker(
|
||||
context: context,
|
||||
initialDate:
|
||||
_controller.selectedDate ?? DateTime.now(),
|
||||
firstDate: DateTime(2020),
|
||||
lastDate: DateTime.now(),
|
||||
);
|
||||
if (picked != null &&
|
||||
picked != _controller.selectedDate) {
|
||||
_controller.setSelectedDate(picked);
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 12,
|
||||
DropdownButtonFormField<String>(
|
||||
value: _controller.selectedProduct,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Pilih produk',
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: AppColors.grey300),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.calendar_today,
|
||||
color: AppColors.primaryBrown,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
_controller.selectedDate != null
|
||||
? '${_controller.selectedDate!.day}/${_controller.selectedDate!.month}/${_controller.selectedDate!.year}'
|
||||
: 'Pilih tanggal',
|
||||
style: AppTextStyles.bodyMedium,
|
||||
),
|
||||
],
|
||||
filled: true,
|
||||
fillColor: AppColors.bgLight,
|
||||
),
|
||||
items:
|
||||
_controller.products
|
||||
.map(
|
||||
(product) => DropdownMenuItem(
|
||||
value: product,
|
||||
child: Text(product),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
onChanged: _controller.setSelectedProduct,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Text(
|
||||
'Jumlah',
|
||||
style: AppTextStyles.labelLarge.copyWith(
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
TextField(
|
||||
controller: _controller.quantityController,
|
||||
keyboardType: TextInputType.number,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Masukkan jumlah unit',
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
filled: true,
|
||||
fillColor: AppColors.bgLight,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton.icon(
|
||||
onPressed:
|
||||
_controller.isLoading
|
||||
? null
|
||||
: _submitAllTransactions,
|
||||
onPressed: _addToCart,
|
||||
icon: const Icon(Icons.add_shopping_cart),
|
||||
label: const Text('Tambah ke Keranjang'),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.primaryBrown,
|
||||
foregroundColor: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 14,
|
||||
vertical: 12,
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
icon:
|
||||
_controller.isLoading
|
||||
? const SizedBox(
|
||||
width: 20,
|
||||
height: 20,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
valueColor:
|
||||
AlwaysStoppedAnimation<Color>(
|
||||
Colors.white,
|
||||
),
|
||||
),
|
||||
)
|
||||
: const Icon(Icons.check_circle),
|
||||
label: Text(
|
||||
_controller.isLoading
|
||||
? 'Menyimpan...'
|
||||
: 'Simpan Stok Masuk',
|
||||
style: AppTextStyles.labelLarge,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: OutlinedButton.icon(
|
||||
onPressed: _showAddProductDialog,
|
||||
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,
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
@ -906,98 +643,406 @@ class _TransactionScreenState extends State<TransactionScreen> {
|
|||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
],
|
||||
Text(
|
||||
'Riwayat Stok Masuk (${_controller.transactions.length})',
|
||||
style: AppTextStyles.headlineSmall.copyWith(
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
if (_controller.transactions.isEmpty)
|
||||
Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 32.0),
|
||||
if (_controller.cartItems.isNotEmpty) ...[
|
||||
Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.bgWhite,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: [AppColors.shadowLight],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.inbox,
|
||||
size: 64,
|
||||
color: AppColors.grey300,
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Keranjang (${_controller.cartItems.length})',
|
||||
style: AppTextStyles.headlineSmall.copyWith(
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: _controller.clearCart,
|
||||
child: Text(
|
||||
'Hapus Semua',
|
||||
style: AppTextStyles.labelMedium.copyWith(
|
||||
color: AppColors.statusError,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
ListView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: _controller.cartItems.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = _controller.cartItems[index];
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(bottom: 12),
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.bgLight,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(
|
||||
color: AppColors.grey300,
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
item.productName,
|
||||
style: AppTextStyles
|
||||
.labelLarge
|
||||
.copyWith(
|
||||
color:
|
||||
AppColors
|
||||
.textPrimary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'Rp ${item.unitPrice} / unit',
|
||||
style: AppTextStyles
|
||||
.labelSmall
|
||||
.copyWith(
|
||||
color:
|
||||
AppColors
|
||||
.textSecondary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'Rp ${item.totalPrice}',
|
||||
style: AppTextStyles.labelLarge
|
||||
.copyWith(
|
||||
color:
|
||||
AppColors.statusSuccess,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap:
|
||||
() => _controller
|
||||
.updateQuantity(
|
||||
index,
|
||||
item.quantity - 1,
|
||||
),
|
||||
child: Container(
|
||||
width: 32,
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
color:
|
||||
AppColors.primaryBrown,
|
||||
borderRadius:
|
||||
BorderRadius.circular(
|
||||
6,
|
||||
),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.remove,
|
||||
color: Colors.white,
|
||||
size: 18,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
item.quantity.toString(),
|
||||
style: AppTextStyles.labelLarge
|
||||
.copyWith(
|
||||
color:
|
||||
AppColors.textPrimary,
|
||||
fontWeight:
|
||||
FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
GestureDetector(
|
||||
onTap:
|
||||
() => _controller
|
||||
.updateQuantity(
|
||||
index,
|
||||
item.quantity + 1,
|
||||
),
|
||||
child: Container(
|
||||
width: 32,
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
color:
|
||||
AppColors.primaryBrown,
|
||||
borderRadius:
|
||||
BorderRadius.circular(
|
||||
6,
|
||||
),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.add,
|
||||
color: Colors.white,
|
||||
size: 18,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
GestureDetector(
|
||||
onTap:
|
||||
() => _controller
|
||||
.removeFromCart(index),
|
||||
child: Container(
|
||||
width: 32,
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.statusError
|
||||
.withValues(alpha: 0.1),
|
||||
borderRadius:
|
||||
BorderRadius.circular(6),
|
||||
),
|
||||
child: Icon(
|
||||
Icons.delete,
|
||||
color: AppColors.statusError,
|
||||
size: 18,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const Divider(),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Total Pembayaran',
|
||||
style: AppTextStyles.labelLarge.copyWith(
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'Rp ${_controller.totalPrice}',
|
||||
style: AppTextStyles.headlineSmall.copyWith(
|
||||
color: AppColors.primaryBrown,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Belum ada Stok Masuk',
|
||||
style: AppTextStyles.bodyMedium.copyWith(
|
||||
color: AppColors.textSecondary,
|
||||
'Tanggal Stok Masuk',
|
||||
style: AppTextStyles.labelLarge.copyWith(
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
GestureDetector(
|
||||
onTap: () async {
|
||||
final picked = await showDatePicker(
|
||||
context: context,
|
||||
initialDate:
|
||||
_controller.selectedDate ??
|
||||
DateTime.now(),
|
||||
firstDate: DateTime(2020),
|
||||
lastDate: DateTime.now(),
|
||||
);
|
||||
if (picked != null &&
|
||||
picked != _controller.selectedDate) {
|
||||
_controller.setSelectedDate(picked);
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 12,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: AppColors.grey300),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.calendar_today,
|
||||
color: AppColors.primaryBrown,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
_controller.selectedDate != null
|
||||
? '${_controller.selectedDate!.day}/${_controller.selectedDate!.month}/${_controller.selectedDate!.year}'
|
||||
: 'Pilih tanggal',
|
||||
style: AppTextStyles.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton.icon(
|
||||
onPressed:
|
||||
_controller.isLoading
|
||||
? null
|
||||
: _submitAllTransactions,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.primaryBrown,
|
||||
foregroundColor: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 14,
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
icon:
|
||||
_controller.isLoading
|
||||
? const SizedBox(
|
||||
width: 20,
|
||||
height: 20,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
valueColor:
|
||||
AlwaysStoppedAnimation<Color>(
|
||||
Colors.white,
|
||||
),
|
||||
),
|
||||
)
|
||||
: const Icon(Icons.check_circle),
|
||||
label: Text(
|
||||
_controller.isLoading
|
||||
? 'Menyimpan...'
|
||||
: 'Simpan Stok Masuk',
|
||||
style: AppTextStyles.labelLarge,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
else
|
||||
ListView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: _controller.transactions.length,
|
||||
itemBuilder: (context, index) {
|
||||
final tx = _controller.transactions[index];
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(bottom: 12),
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.bgWhite,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: AppColors.grey200),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
],
|
||||
Text(
|
||||
'Riwayat Stok Masuk (${_controller.transactions.length})',
|
||||
style: AppTextStyles.headlineSmall.copyWith(
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
if (_controller.transactions.isEmpty)
|
||||
Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 32.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
tx.productName,
|
||||
style: AppTextStyles.labelLarge.copyWith(
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'Rp ${tx.totalPrice}',
|
||||
style: AppTextStyles.labelLarge.copyWith(
|
||||
color: AppColors.statusSuccess,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
],
|
||||
Icon(
|
||||
Icons.inbox,
|
||||
size: 64,
|
||||
color: AppColors.grey300,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'${tx.quantity} unit × Rp ${tx.unitPrice}',
|
||||
style: AppTextStyles.labelMedium.copyWith(
|
||||
color: AppColors.textSecondary,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'${tx.date.day}/${tx.date.month}/${tx.date.year}',
|
||||
style: AppTextStyles.labelSmall.copyWith(
|
||||
color: AppColors.textSecondary,
|
||||
),
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Belum ada Stok Masuk',
|
||||
style: AppTextStyles.bodyMedium.copyWith(
|
||||
color: AppColors.textSecondary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
else
|
||||
ListView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: _controller.transactions.length,
|
||||
itemBuilder: (context, index) {
|
||||
final tx = _controller.transactions[index];
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(bottom: 12),
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.bgWhite,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: AppColors.grey200),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
tx.productName,
|
||||
style: AppTextStyles.labelLarge.copyWith(
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'Rp ${tx.totalPrice}',
|
||||
style: AppTextStyles.labelLarge.copyWith(
|
||||
color: AppColors.statusSuccess,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'${tx.quantity} unit × Rp ${tx.unitPrice}',
|
||||
style: AppTextStyles.labelMedium.copyWith(
|
||||
color: AppColors.textSecondary,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'${tx.date.day}/${tx.date.month}/${tx.date.year}',
|
||||
style: AppTextStyles.labelSmall.copyWith(
|
||||
color: AppColors.textSecondary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
Loading…
Reference in New Issue