296 lines
9.2 KiB
Dart
Raw Normal View History

2025-08-12 17:36:41 +07:00
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
2025-08-12 23:27:34 +07:00
import 'package:line_icons/line_icons.dart';
2025-08-12 17:36:41 +07:00
2025-08-12 21:27:13 +07:00
import '../../../common/theme/theme.dart';
2025-08-16 00:39:09 +07:00
import '../../components/appbar/appbar.dart';
2025-08-12 23:27:34 +07:00
import '../../components/button/button.dart';
import '../../components/spacer/spacer.dart';
import 'widgets/status_tile.dart';
2025-08-18 13:33:45 +07:00
import 'widgets/order_tile.dart';
2025-08-12 21:27:13 +07:00
2025-08-12 17:36:41 +07:00
@RoutePage()
2025-08-18 13:33:45 +07:00
class OrderPage extends StatefulWidget {
const OrderPage({super.key});
2025-08-12 17:36:41 +07:00
2025-08-12 21:27:13 +07:00
@override
2025-08-18 13:33:45 +07:00
State<OrderPage> createState() => _OrderPageState();
2025-08-12 21:27:13 +07:00
}
2025-08-18 13:33:45 +07:00
class _OrderPageState extends State<OrderPage> with TickerProviderStateMixin {
2025-08-12 23:27:34 +07:00
late AnimationController _fadeController;
late AnimationController _slideController;
late Animation<double> _fadeAnimation;
late Animation<Offset> _slideAnimation;
2025-08-12 21:27:13 +07:00
2025-08-12 23:27:34 +07:00
// Filter state
String selectedFilter = 'All';
final List<String> filterOptions = [
'All',
'Completed',
'Pending',
'Refunded',
2025-08-12 21:27:13 +07:00
];
2025-08-12 17:36:41 +07:00
@override
2025-08-12 23:27:34 +07:00
void initState() {
super.initState();
2025-08-12 21:27:13 +07:00
2025-08-12 23:27:34 +07:00
_fadeController = AnimationController(
duration: const Duration(milliseconds: 800),
vsync: this,
2025-08-12 21:27:13 +07:00
);
2025-08-12 23:27:34 +07:00
_slideController = AnimationController(
duration: const Duration(milliseconds: 1000),
vsync: this,
2025-08-12 21:27:13 +07:00
);
2025-08-12 23:27:34 +07:00
_fadeAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
CurvedAnimation(parent: _fadeController, curve: Curves.easeInOut),
2025-08-12 21:27:13 +07:00
);
2025-08-12 23:27:34 +07:00
_slideAnimation =
Tween<Offset>(begin: const Offset(0, 0.3), end: Offset.zero).animate(
CurvedAnimation(parent: _slideController, curve: Curves.elasticOut),
);
2025-08-12 21:27:13 +07:00
2025-08-12 23:27:34 +07:00
_fadeController.forward();
_slideController.forward();
2025-08-12 21:27:13 +07:00
}
2025-08-12 23:27:34 +07:00
@override
void dispose() {
_fadeController.dispose();
_slideController.dispose();
super.dispose();
2025-08-12 21:27:13 +07:00
}
2025-08-12 23:27:34 +07:00
final sampleTransactions = [
Transaction(
id: 'TXN001',
customerName: 'John Doe',
date: DateTime.now().subtract(const Duration(hours: 2)),
totalAmount: 125000,
itemCount: 3,
paymentMethod: 'Cash',
status: TransactionStatus.completed,
receiptNumber: 'RCP-2024-001',
),
Transaction(
id: 'TXN002',
customerName: 'Jane Smith',
date: DateTime.now().subtract(const Duration(hours: 5)),
totalAmount: 87500,
itemCount: 2,
paymentMethod: 'QRIS',
status: TransactionStatus.pending,
receiptNumber: 'RCP-2024-002',
),
Transaction(
id: 'TXN003',
customerName: 'Bob Johnson',
date: DateTime.now().subtract(const Duration(days: 1)),
totalAmount: 250000,
itemCount: 5,
paymentMethod: 'Credit Card',
status: TransactionStatus.refunded,
receiptNumber: 'RCP-2024-003',
),
];
// Filter transactions based on selected status
List<Transaction> get filteredTransactions {
if (selectedFilter == 'All') {
return sampleTransactions;
}
TransactionStatus? filterStatus;
switch (selectedFilter) {
2025-08-12 21:27:13 +07:00
case 'Completed':
2025-08-12 23:27:34 +07:00
filterStatus = TransactionStatus.completed;
break;
2025-08-12 21:27:13 +07:00
case 'Pending':
2025-08-12 23:27:34 +07:00
filterStatus = TransactionStatus.pending;
break;
case 'Refunded':
filterStatus = TransactionStatus.refunded;
break;
2025-08-12 21:27:13 +07:00
}
2025-08-12 23:27:34 +07:00
return sampleTransactions
.where((transaction) => transaction.status == filterStatus)
.toList();
2025-08-12 21:27:13 +07:00
}
2025-08-12 23:27:34 +07:00
// Build filter chip
2025-08-12 21:27:13 +07:00
2025-08-12 23:27:34 +07:00
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColor.background,
body: CustomScrollView(
slivers: [
// Custom App Bar with Hero Effect
SliverAppBar(
expandedHeight: 120,
floating: true,
pinned: true,
backgroundColor: AppColor.primary,
centerTitle: false,
2025-08-18 13:33:45 +07:00
flexibleSpace: CustomAppBar(title: 'Order', isBack: false),
2025-08-12 23:27:34 +07:00
actions: [
ActionIconButton(onTap: () {}, icon: LineIcons.filter),
SpaceWidth(8),
],
),
2025-08-12 21:27:13 +07:00
2025-08-12 23:27:34 +07:00
// Pinned Filter Section
SliverPersistentHeader(
pinned: true,
delegate: _FilterHeaderDelegate(
child: Container(
color: AppColor.background,
padding: EdgeInsets.fromLTRB(
AppValue.padding,
10,
AppValue.padding,
10,
2025-08-12 21:27:13 +07:00
),
2025-08-12 23:27:34 +07:00
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: filterOptions.map((option) {
final index = filterOptions.indexOf(option);
return Padding(
padding: EdgeInsets.only(
right: index < filterOptions.length - 1 ? 8 : 0,
),
2025-08-18 13:33:45 +07:00
child: OrderStatusTile(
2025-08-12 23:27:34 +07:00
label: option,
isSelected: option == selectedFilter,
onSelected: (isSelected) {
if (isSelected) {
setState(() {
selectedFilter = option;
});
}
},
),
);
}).toList(),
2025-08-12 21:27:13 +07:00
),
),
2025-08-12 23:27:34 +07:00
],
),
2025-08-12 21:27:13 +07:00
),
2025-08-12 23:27:34 +07:00
),
2025-08-12 21:27:13 +07:00
),
2025-08-12 23:27:34 +07:00
// Content
SliverPadding(
padding: EdgeInsets.all(AppValue.padding),
sliver: SliverList(
delegate: SliverChildListDelegate([
FadeTransition(
opacity: _fadeAnimation,
child: SlideTransition(
position: _slideAnimation,
child: Column(
children: [
// Show filtered transaction count
if (selectedFilter != 'All')
Padding(
padding: const EdgeInsets.only(bottom: 16),
child: Row(
children: [
Text(
'${filteredTransactions.length} ${selectedFilter.toLowerCase()} transaction${filteredTransactions.length != 1 ? 's' : ''}',
style: TextStyle(
color: AppColor.textSecondary,
fontSize: 14,
),
),
],
),
2025-08-12 21:27:13 +07:00
),
2025-08-12 23:27:34 +07:00
// Transaction List
filteredTransactions.isEmpty
? Container(
padding: const EdgeInsets.symmetric(
vertical: 40,
),
2025-08-12 21:27:13 +07:00
child: Column(
children: [
2025-08-12 23:27:34 +07:00
Icon(
LineIcons.receipt,
size: 64,
color: AppColor.textSecondary.withOpacity(
0.5,
2025-08-12 21:27:13 +07:00
),
),
2025-08-12 23:27:34 +07:00
const SizedBox(height: 16),
2025-08-12 21:27:13 +07:00
Text(
2025-08-12 23:27:34 +07:00
'No ${selectedFilter.toLowerCase()} transactions found',
2025-08-12 21:27:13 +07:00
style: TextStyle(
color: AppColor.textSecondary,
2025-08-12 23:27:34 +07:00
fontSize: 16,
2025-08-12 21:27:13 +07:00
),
),
],
),
2025-08-12 23:27:34 +07:00
)
: Column(
children: filteredTransactions.map((
transaction,
) {
2025-08-18 13:33:45 +07:00
return OrderTile(
2025-08-12 23:27:34 +07:00
transaction: transaction,
onTap: () {},
);
}).toList(),
2025-08-12 21:27:13 +07:00
),
],
),
),
2025-08-12 23:27:34 +07:00
),
]),
2025-08-12 21:27:13 +07:00
),
),
],
),
);
2025-08-12 17:36:41 +07:00
}
}
2025-08-12 23:27:34 +07:00
// Custom delegate for pinned filter header
class _FilterHeaderDelegate extends SliverPersistentHeaderDelegate {
final Widget child;
_FilterHeaderDelegate({required this.child});
@override
double get minExtent => 70; // Minimum height when collapsed
@override
double get maxExtent => 70; // Maximum height when expanded
@override
Widget build(
BuildContext context,
double shrinkOffset,
bool overlapsContent,
) {
return child;
}
@override
bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
return false;
}
}