list orders

This commit is contained in:
Phuoc Nguyen
2025-11-24 16:25:54 +07:00
parent 354df3ad01
commit 75d6507719
24 changed files with 1004 additions and 982 deletions

View File

@@ -8,7 +8,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:go_router/go_router.dart';
import 'package:worker/core/constants/ui_constants.dart';
import 'package:worker/core/database/models/enums.dart';
import 'package:worker/core/theme/colors.dart';
import 'package:worker/features/orders/presentation/providers/orders_provider.dart';
import 'package:worker/features/orders/presentation/widgets/order_card.dart';
@@ -77,16 +76,28 @@ class _OrdersPageState extends ConsumerState<OrdersPage> {
},
child: CustomScrollView(
slivers: [
// Search Bar
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.all(16),
child: _buildSearchBar(),
// Sticky Search Bar
SliverPersistentHeader(
pinned: true,
delegate: _SearchBarDelegate(
child: Container(
color: const Color(0xFFF4F6F8),
padding: const EdgeInsets.all(16),
child: _buildSearchBar(),
),
),
),
// Filter Pills
SliverToBoxAdapter(child: _buildFilterPills(selectedStatus)),
// Sticky Filter Pills
SliverPersistentHeader(
pinned: true,
delegate: _FilterPillsDelegate(
child: Container(
color: const Color(0xFFF4F6F8),
child: _buildFilterPills(selectedStatus),
),
),
),
// Orders List
SliverPadding(
@@ -103,7 +114,7 @@ class _OrdersPageState extends ConsumerState<OrdersPage> {
return OrderCard(
order: order,
onTap: () {
context.push('/orders/${order.orderId}');
context.push('/orders/${order.name}');
},
);
}, childCount: orders.length),
@@ -168,83 +179,74 @@ class _OrdersPageState extends ConsumerState<OrdersPage> {
);
}
/// Build filter pills
Widget _buildFilterPills(OrderStatus? selectedStatus) {
return Container(
/// Build filter pills (dynamically from cached status list)
Widget _buildFilterPills(String? selectedStatus) {
final statusListAsync = ref.watch(orderStatusListProvider);
return SizedBox(
height: 48,
padding: const EdgeInsets.symmetric(horizontal: 16),
child: ListView(
scrollDirection: Axis.horizontal,
children: [
// All filter
_buildFilterChip(
label: 'Tất cả',
isSelected: selectedStatus == null,
onTap: () {
ref.read(selectedOrderStatusProvider.notifier).clearSelection();
},
),
const SizedBox(width: 8),
child: statusListAsync.when(
data: (statusList) {
return ListView(
padding: const EdgeInsets.symmetric(horizontal: 16),
scrollDirection: Axis.horizontal,
children: [
// All filter (always first)
_buildFilterChip(
label: 'Tất cả',
isSelected: selectedStatus == null,
onTap: () {
ref.read(selectedOrderStatusProvider.notifier).clearSelection();
},
),
const SizedBox(width: 8),
// Pending filter
_buildFilterChip(
label: 'Chờ xác nhận',
isSelected: selectedStatus == OrderStatus.pending,
onTap: () {
ref
.read(selectedOrderStatusProvider.notifier)
.selectStatus(OrderStatus.pending);
},
),
const SizedBox(width: 8),
// Processing filter
_buildFilterChip(
label: 'Đang xử lý',
isSelected: selectedStatus == OrderStatus.processing,
onTap: () {
ref
.read(selectedOrderStatusProvider.notifier)
.selectStatus(OrderStatus.processing);
},
),
const SizedBox(width: 8),
// Shipped filter
_buildFilterChip(
label: 'Đang giao',
isSelected: selectedStatus == OrderStatus.shipped,
onTap: () {
ref
.read(selectedOrderStatusProvider.notifier)
.selectStatus(OrderStatus.shipped);
},
),
const SizedBox(width: 8),
// Completed filter
_buildFilterChip(
label: 'Hoàn thành',
isSelected: selectedStatus == OrderStatus.completed,
onTap: () {
ref
.read(selectedOrderStatusProvider.notifier)
.selectStatus(OrderStatus.completed);
},
),
const SizedBox(width: 8),
// Cancelled filter
_buildFilterChip(
label: 'Đã hủy',
isSelected: selectedStatus == OrderStatus.cancelled,
onTap: () {
ref
.read(selectedOrderStatusProvider.notifier)
.selectStatus(OrderStatus.cancelled);
},
),
],
// Dynamic status filters from API
...statusList.map((status) {
return Padding(
padding: const EdgeInsets.only(right: 8),
child: _buildFilterChip(
label: status.label,
isSelected: selectedStatus == status.label,
onTap: () {
ref
.read(selectedOrderStatusProvider.notifier)
.selectStatus(status.label);
},
),
);
}),
],
);
},
loading: () {
// Show minimal loading state or fallback to "All" only
return ListView(
padding: const EdgeInsets.symmetric(horizontal: 16),
scrollDirection: Axis.horizontal,
children: [
_buildFilterChip(
label: 'Tất cả',
isSelected: true,
onTap: () {},
),
],
);
},
error: (error, stack) {
// Show "All" filter only on error
return ListView(
padding: const EdgeInsets.symmetric(horizontal: 16),
scrollDirection: Axis.horizontal,
children: [
_buildFilterChip(
label: 'Tất cả',
isSelected: true,
onTap: () {},
),
],
);
},
),
);
}
@@ -349,3 +351,57 @@ class _OrdersPageState extends ConsumerState<OrdersPage> {
);
}
}
/// Search Bar Delegate for SliverPersistentHeader
class _SearchBarDelegate extends SliverPersistentHeaderDelegate {
_SearchBarDelegate({required this.child});
final Widget child;
@override
double get minExtent => 80; // Height when pinned
@override
double get maxExtent => 80; // Height when expanded
@override
Widget build(
BuildContext context,
double shrinkOffset,
bool overlapsContent,
) {
return child;
}
@override
bool shouldRebuild(_SearchBarDelegate oldDelegate) {
return child != oldDelegate.child;
}
}
/// Filter Pills Delegate for SliverPersistentHeader
class _FilterPillsDelegate extends SliverPersistentHeaderDelegate {
_FilterPillsDelegate({required this.child});
final Widget child;
@override
double get minExtent => 48; // Height when pinned (matches Container height)
@override
double get maxExtent => 48; // Height when expanded (matches Container height)
@override
Widget build(
BuildContext context,
double shrinkOffset,
bool overlapsContent,
) {
return child;
}
@override
bool shouldRebuild(_FilterPillsDelegate oldDelegate) {
return child != oldDelegate.child;
}
}