list orders
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user