feat: void page
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import 'package:enaklo_pos/core/components/spaces.dart';
|
||||
import 'package:enaklo_pos/core/constants/colors.dart';
|
||||
import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
|
||||
import 'package:enaklo_pos/core/extensions/string_ext.dart';
|
||||
import 'package:enaklo_pos/data/models/response/order_response_model.dart';
|
||||
import 'package:enaklo_pos/presentation/home/bloc/order_form/order_form_bloc.dart';
|
||||
@@ -17,7 +16,7 @@ class SalesListOrder extends StatelessWidget {
|
||||
margin: const EdgeInsets.only(top: 16),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.white,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: BlocBuilder<OrderFormBloc, OrderFormState>(
|
||||
builder: (context, state) {
|
||||
@@ -27,51 +26,10 @@ class SalesListOrder extends StatelessWidget {
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
width: double.infinity,
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(color: AppColors.background),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Checkbox(
|
||||
value: isAllSelected,
|
||||
activeColor: AppColors.primary,
|
||||
onChanged: (val) {
|
||||
context.read<OrderFormBloc>().add(
|
||||
OrderFormEvent.toggleSelectAll(val ?? false));
|
||||
},
|
||||
),
|
||||
Text(
|
||||
'Daftar Pembelian',
|
||||
style: TextStyle(
|
||||
color: AppColors.black,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SpaceHeight(12),
|
||||
Column(
|
||||
children: List.generate(
|
||||
order?.orderItems?.length ?? 0,
|
||||
(index) {
|
||||
final item = order!.orderItems![index];
|
||||
final isSelected =
|
||||
selectedItems.any((e) => e.id == item.id);
|
||||
return _item(
|
||||
context,
|
||||
isSelected,
|
||||
item,
|
||||
);
|
||||
},
|
||||
).toList(),
|
||||
),
|
||||
_buildHeader(context, isAllSelected),
|
||||
const SpaceHeight(8),
|
||||
_buildItemsList(context, selectedItems),
|
||||
const SpaceHeight(8),
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -80,96 +38,345 @@ class SalesListOrder extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Padding _item(BuildContext context, bool isSelected, OrderItem product) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16)
|
||||
.copyWith(top: 0),
|
||||
child: Column(
|
||||
Widget _buildHeader(BuildContext context, bool isAllSelected) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
AppColors.primary.withOpacity(0.1),
|
||||
AppColors.primary.withOpacity(0.05),
|
||||
],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
),
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(16),
|
||||
topRight: Radius.circular(16),
|
||||
),
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: AppColors.primary.withOpacity(0.1),
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: Text(
|
||||
product.status == "pending"
|
||||
? "Pending"
|
||||
: product.status == "cancelled"
|
||||
? "Batal"
|
||||
: product.status == 'refund'
|
||||
? "Refund"
|
||||
: "Selesai",
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: product.status == "pending"
|
||||
? Colors.blue
|
||||
: product.status == "cancelled"
|
||||
? Colors.red
|
||||
: product.status == 'refund'
|
||||
? Colors.red
|
||||
: Colors.green,
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.white,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(
|
||||
color: isAllSelected ? AppColors.primary : Colors.grey.shade300,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
child: Checkbox(
|
||||
value: isAllSelected,
|
||||
activeColor: AppColors.primary,
|
||||
checkColor: AppColors.white,
|
||||
onChanged: (val) {
|
||||
context
|
||||
.read<OrderFormBloc>()
|
||||
.add(OrderFormEvent.toggleSelectAll(val ?? false));
|
||||
},
|
||||
),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: context.deviceWidth * 0.2,
|
||||
child: Row(
|
||||
children: [
|
||||
Checkbox(
|
||||
value: isSelected,
|
||||
activeColor: AppColors.primary,
|
||||
onChanged: (_) {
|
||||
context
|
||||
.read<OrderFormBloc>()
|
||||
.add(OrderFormEvent.toggleItem(product));
|
||||
},
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Daftar Pembelian',
|
||||
style: TextStyle(
|
||||
color: AppColors.black,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w700,
|
||||
letterSpacing: -0.5,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'${order?.orderItems?.length ?? 0} item',
|
||||
style: TextStyle(
|
||||
color: Colors.grey.shade600,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.primary.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
border: Border.all(
|
||||
color: AppColors.primary.withOpacity(0.2),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.shopping_cart_outlined,
|
||||
size: 16,
|
||||
color: AppColors.primary,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
'Order',
|
||||
style: TextStyle(
|
||||
color: AppColors.primary,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildItemsList(BuildContext context, List selectedItems) {
|
||||
return Column(
|
||||
children: List.generate(
|
||||
order?.orderItems?.length ?? 0,
|
||||
(index) {
|
||||
final item = order!.orderItems![index];
|
||||
final isSelected = selectedItems.any((e) => e.id == item.id);
|
||||
return _buildItem(context, isSelected, item, index);
|
||||
},
|
||||
).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildItem(
|
||||
BuildContext context, bool isSelected, OrderItem product, int index) {
|
||||
return AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color:
|
||||
isSelected ? AppColors.primary.withOpacity(0.05) : AppColors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(
|
||||
color: isSelected
|
||||
? AppColors.primary.withOpacity(0.3)
|
||||
: Colors.grey.shade200,
|
||||
width: isSelected ? 2 : 1,
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.white,
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
border: Border.all(
|
||||
color:
|
||||
isSelected ? AppColors.primary : Colors.grey.shade300,
|
||||
width: 1.5,
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
product.productName ?? '',
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
if (product.productVariantName != null)
|
||||
Text(
|
||||
product.productVariantName ?? '',
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
child: Checkbox(
|
||||
value: isSelected,
|
||||
activeColor: AppColors.primary,
|
||||
checkColor: AppColors.white,
|
||||
onChanged: (_) {
|
||||
context
|
||||
.read<OrderFormBloc>()
|
||||
.add(OrderFormEvent.toggleItem(product));
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
product.productName ?? '',
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
letterSpacing: -0.2,
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
(product.unitPrice ?? 0)
|
||||
.toString()
|
||||
.currencyFormatRpV2,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
_buildStatusBadge(product.status),
|
||||
],
|
||||
),
|
||||
if (product.productVariantName != null) ...[
|
||||
const SizedBox(height: 4),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey.shade100,
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
child: Text(
|
||||
product.productVariantName ?? '',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.grey.shade700,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Harga Satuan',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.grey.shade600,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
(product.unitPrice ?? 0)
|
||||
.toString()
|
||||
.currencyFormatRpV2,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.primary.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Text(
|
||||
'x${product.quantity}',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
'Total',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.grey.shade600,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
(product.totalPrice ?? 0)
|
||||
.toString()
|
||||
.currencyFormatRpV2,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.primary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'X${product.quantity}',
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
(product.totalPrice ?? 0).toString().currencyFormatRpV2,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStatusBadge(String? status) {
|
||||
Color backgroundColor;
|
||||
Color textColor;
|
||||
String displayText;
|
||||
IconData icon;
|
||||
|
||||
switch (status) {
|
||||
case "pending":
|
||||
backgroundColor = Colors.orange.withOpacity(0.1);
|
||||
textColor = Colors.orange.shade700;
|
||||
displayText = "Pending";
|
||||
icon = Icons.access_time;
|
||||
break;
|
||||
case "cancelled":
|
||||
backgroundColor = Colors.red.withOpacity(0.1);
|
||||
textColor = Colors.red.shade700;
|
||||
displayText = "Batal";
|
||||
icon = Icons.cancel_outlined;
|
||||
break;
|
||||
case "refund":
|
||||
backgroundColor = Colors.purple.withOpacity(0.1);
|
||||
textColor = Colors.purple.shade700;
|
||||
displayText = "Refund";
|
||||
icon = Icons.undo;
|
||||
break;
|
||||
default:
|
||||
backgroundColor = Colors.green.withOpacity(0.1);
|
||||
textColor = Colors.green.shade700;
|
||||
displayText = "Selesai";
|
||||
icon = Icons.check_circle_outline;
|
||||
}
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
color: backgroundColor,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
border: Border.all(
|
||||
color: textColor.withOpacity(0.2),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
icon,
|
||||
size: 14,
|
||||
color: textColor,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
displayText,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: textColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user