feat: sales page
This commit is contained in:
parent
e825e5daed
commit
45b348684e
41
lib/core/components/dashed_divider.dart
Normal file
41
lib/core/components/dashed_divider.dart
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class DashedDivider extends StatelessWidget {
|
||||||
|
final double height;
|
||||||
|
final double dashWidth;
|
||||||
|
final double dashSpacing;
|
||||||
|
final Color color;
|
||||||
|
|
||||||
|
const DashedDivider({
|
||||||
|
super.key,
|
||||||
|
this.height = 1,
|
||||||
|
this.dashWidth = 5,
|
||||||
|
this.dashSpacing = 3,
|
||||||
|
this.color = Colors.grey,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SizedBox(
|
||||||
|
height: height,
|
||||||
|
child: LayoutBuilder(
|
||||||
|
builder: (context, constraints) {
|
||||||
|
final boxWidth = constraints.constrainWidth();
|
||||||
|
final dashCount = (boxWidth / (dashWidth + dashSpacing)).floor();
|
||||||
|
return Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: List.generate(dashCount, (_) {
|
||||||
|
return SizedBox(
|
||||||
|
width: dashWidth,
|
||||||
|
height: height,
|
||||||
|
child: DecoratedBox(
|
||||||
|
decoration: BoxDecoration(color: color),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -7,7 +7,6 @@ import 'package:enaklo_pos/data/models/response/table_model.dart';
|
|||||||
import 'package:enaklo_pos/presentation/home/models/order_model.dart';
|
import 'package:enaklo_pos/presentation/home/models/order_model.dart';
|
||||||
import 'package:enaklo_pos/presentation/table/models/draft_order_item.dart';
|
import 'package:enaklo_pos/presentation/table/models/draft_order_item.dart';
|
||||||
import 'package:enaklo_pos/presentation/table/models/draft_order_model.dart';
|
import 'package:enaklo_pos/presentation/table/models/draft_order_model.dart';
|
||||||
import 'package:intl/intl.dart';
|
|
||||||
import 'package:sqflite/sqflite.dart';
|
import 'package:sqflite/sqflite.dart';
|
||||||
|
|
||||||
import '../../presentation/home/models/product_quantity.dart';
|
import '../../presentation/home/models/product_quantity.dart';
|
||||||
@ -169,7 +168,8 @@ class ProductLocalDatasource {
|
|||||||
if (oldVersion < 2) {
|
if (oldVersion < 2) {
|
||||||
// Add order_type column to orders table if it doesn't exist
|
// Add order_type column to orders table if it doesn't exist
|
||||||
try {
|
try {
|
||||||
await db.execute('ALTER TABLE $tableOrder ADD COLUMN order_type TEXT DEFAULT "DINE IN"');
|
await db.execute(
|
||||||
|
'ALTER TABLE $tableOrder ADD COLUMN order_type TEXT DEFAULT "DINE IN"');
|
||||||
log("Added order_type column to orders table");
|
log("Added order_type column to orders table");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log("order_type column might already exist: $e");
|
log("order_type column might already exist: $e");
|
||||||
@ -238,6 +238,31 @@ class ProductLocalDatasource {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<List<OrderModel>> getAllOrderByRange(
|
||||||
|
DateTime start, DateTime end) async {
|
||||||
|
final db = await instance.database;
|
||||||
|
|
||||||
|
// Format ke ISO 8601 untuk range, hasil: yyyy-MM-ddTHH:mm:ss
|
||||||
|
final startIso = start.toIso8601String();
|
||||||
|
final endIso = end.toIso8601String();
|
||||||
|
|
||||||
|
final startDateYYYYMMDD = startIso.substring(0, 10);
|
||||||
|
final endDateYYYYMMDD = endIso.substring(0, 10);
|
||||||
|
|
||||||
|
final List<Map<String, dynamic>> maps = await db.query(
|
||||||
|
tableOrder,
|
||||||
|
where: 'substr(transaction_time, 1, 10) BETWEEN ? AND ?',
|
||||||
|
whereArgs: [startDateYYYYMMDD, endDateYYYYMMDD],
|
||||||
|
orderBy: 'transaction_time DESC',
|
||||||
|
);
|
||||||
|
log("Get All Order By Range: $startDateYYYYMMDD $endDateYYYYMMDD");
|
||||||
|
|
||||||
|
return List.generate(maps.length, (i) {
|
||||||
|
log("Save save OrderModel: ${OrderModel.fromMap(maps[i])}");
|
||||||
|
return OrderModel.fromMap(maps[i]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
//get order item by order id
|
//get order item by order id
|
||||||
Future<List<ProductQuantity>> getOrderItemByOrderId(int orderId) async {
|
Future<List<ProductQuantity>> getOrderItemByOrderId(int orderId) async {
|
||||||
final db = await instance.database;
|
final db = await instance.database;
|
||||||
@ -578,7 +603,8 @@ class ProductLocalDatasource {
|
|||||||
where: 'id_draft_order = ?', whereArgs: [draftOrder.id]);
|
where: 'id_draft_order = ?', whereArgs: [draftOrder.id]);
|
||||||
|
|
||||||
for (var orderItem in draftOrder.orders) {
|
for (var orderItem in draftOrder.orders) {
|
||||||
await db.insert('draft_order_items', orderItem.toMapForLocal(draftOrder.id!));
|
await db.insert(
|
||||||
|
'draft_order_items', orderItem.toMapForLocal(draftOrder.id!));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -32,10 +32,10 @@ class ConfirmPaymentPage extends StatefulWidget {
|
|||||||
final TableModel? table;
|
final TableModel? table;
|
||||||
|
|
||||||
const ConfirmPaymentPage({
|
const ConfirmPaymentPage({
|
||||||
Key? key,
|
super.key,
|
||||||
required this.isTable,
|
required this.isTable,
|
||||||
this.table,
|
this.table,
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ConfirmPaymentPage> createState() => _ConfirmPaymentPageState();
|
State<ConfirmPaymentPage> createState() => _ConfirmPaymentPageState();
|
||||||
@ -110,6 +110,7 @@ class _ConfirmPaymentPageState extends State<ConfirmPaymentPage> {
|
|||||||
child: Hero(
|
child: Hero(
|
||||||
tag: 'payment_confirmation_screen',
|
tag: 'payment_confirmation_screen',
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
|
backgroundColor: AppColors.white,
|
||||||
body: Row(
|
body: Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
@ -401,7 +402,7 @@ class _ConfirmPaymentPageState extends State<ConfirmPaymentPage> {
|
|||||||
final subTotal =
|
final subTotal =
|
||||||
price - (discount / 100 * price);
|
price - (discount / 100 * price);
|
||||||
final finalTax = subTotal * (tax / 100);
|
final finalTax = subTotal * (tax / 100);
|
||||||
final finalDiscount = discount / 100 * subTotal;
|
// final finalDiscount = discount / 100 * subTotal;
|
||||||
// discountAmountValue = finalDiscount.toInt();
|
// discountAmountValue = finalDiscount.toInt();
|
||||||
// taxFinal = finalTax.toInt();
|
// taxFinal = finalTax.toInt();
|
||||||
return Text(
|
return Text(
|
||||||
@ -655,10 +656,15 @@ class _ConfirmPaymentPageState extends State<ConfirmPaymentPage> {
|
|||||||
isAddToOrder = false;
|
isAddToOrder = false;
|
||||||
|
|
||||||
// Debug: Check all tables first
|
// Debug: Check all tables first
|
||||||
final allTables = await ProductLocalDatasource.instance.getAllTable();
|
final allTables =
|
||||||
print("🔘 All tables in database: ${allTables.length}");
|
await ProductLocalDatasource
|
||||||
|
.instance
|
||||||
|
.getAllTable();
|
||||||
|
print(
|
||||||
|
"🔘 All tables in database: ${allTables.length}");
|
||||||
allTables.forEach((table) {
|
allTables.forEach((table) {
|
||||||
print("🔘 Table: ${table.tableName} - Status: ${table.status} - ID: ${table.id}");
|
print(
|
||||||
|
"🔘 Table: ${table.tableName} - Status: ${table.status} - ID: ${table.id}");
|
||||||
});
|
});
|
||||||
|
|
||||||
// Fetch available tables for Bayar Nanti
|
// Fetch available tables for Bayar Nanti
|
||||||
@ -685,10 +691,15 @@ class _ConfirmPaymentPageState extends State<ConfirmPaymentPage> {
|
|||||||
isAddToOrder = false;
|
isAddToOrder = false;
|
||||||
|
|
||||||
// Debug: Check all tables first
|
// Debug: Check all tables first
|
||||||
final allTables = await ProductLocalDatasource.instance.getAllTable();
|
final allTables =
|
||||||
print("🔘 All tables in database: ${allTables.length}");
|
await ProductLocalDatasource
|
||||||
|
.instance
|
||||||
|
.getAllTable();
|
||||||
|
print(
|
||||||
|
"🔘 All tables in database: ${allTables.length}");
|
||||||
allTables.forEach((table) {
|
allTables.forEach((table) {
|
||||||
print("🔘 Table: ${table.tableName} - Status: ${table.status} - ID: ${table.id}");
|
print(
|
||||||
|
"🔘 Table: ${table.tableName} - Status: ${table.status} - ID: ${table.id}");
|
||||||
});
|
});
|
||||||
|
|
||||||
// Fetch available tables for Bayar Nanti
|
// Fetch available tables for Bayar Nanti
|
||||||
@ -1516,8 +1527,8 @@ class _ConfirmPaymentPageState extends State<ConfirmPaymentPage> {
|
|||||||
await ProductLocalDatasource
|
await ProductLocalDatasource
|
||||||
.instance
|
.instance
|
||||||
.getDraftOrderById(
|
.getDraftOrderById(
|
||||||
selectTable!
|
selectTable
|
||||||
.orderId ??
|
?.orderId ??
|
||||||
0);
|
0);
|
||||||
|
|
||||||
if (existingDraftOrder !=
|
if (existingDraftOrder !=
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
|
|||||||
import 'package:enaklo_pos/data/models/response/table_model.dart';
|
import 'package:enaklo_pos/data/models/response/table_model.dart';
|
||||||
import 'package:enaklo_pos/presentation/home/dialog/type_dialog.dart';
|
import 'package:enaklo_pos/presentation/home/dialog/type_dialog.dart';
|
||||||
import 'package:enaklo_pos/presentation/home/pages/dashboard_page.dart';
|
import 'package:enaklo_pos/presentation/home/pages/dashboard_page.dart';
|
||||||
|
import 'package:enaklo_pos/presentation/sales/pages/sales_page.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class HomeRightTitle extends StatelessWidget {
|
class HomeRightTitle extends StatelessWidget {
|
||||||
@ -33,7 +34,7 @@ class HomeRightTitle extends StatelessWidget {
|
|||||||
width: 180.0,
|
width: 180.0,
|
||||||
height: 40,
|
height: 40,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
onPressed: () {},
|
onPressed: () => context.push(SalesPage()),
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import 'package:bloc/bloc.dart';
|
import 'package:bloc/bloc.dart';
|
||||||
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
|
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
|
||||||
import 'package:enaklo_pos/presentation/home/models/order_model.dart';
|
import 'package:enaklo_pos/presentation/home/models/order_model.dart';
|
||||||
@ -12,9 +11,14 @@ class DaySalesBloc extends Bloc<DaySalesEvent, DaySalesState> {
|
|||||||
final ProductLocalDatasource datasource;
|
final ProductLocalDatasource datasource;
|
||||||
DaySalesBloc(this.datasource) : super(const _Initial()) {
|
DaySalesBloc(this.datasource) : super(const _Initial()) {
|
||||||
on<_GetDaySales>((event, emit) async {
|
on<_GetDaySales>((event, emit) async {
|
||||||
|
emit(const _Loading());
|
||||||
|
final result = await datasource.getAllOrder(event.date);
|
||||||
|
emit(_Loaded(result));
|
||||||
|
});
|
||||||
|
on<_GetRangeDateSales>((event, emit) async {
|
||||||
emit(const _Loading());
|
emit(const _Loading());
|
||||||
final result =
|
final result =
|
||||||
await datasource.getAllOrder(event.date);
|
await datasource.getAllOrderByRange(event.startDate, event.endDate);
|
||||||
emit(_Loaded(result));
|
emit(_Loaded(result));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,18 +20,22 @@ mixin _$DaySalesEvent {
|
|||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function() started,
|
required TResult Function() started,
|
||||||
required TResult Function(DateTime date) getDaySales,
|
required TResult Function(DateTime date) getDaySales,
|
||||||
|
required TResult Function(DateTime startDate, DateTime endDate)
|
||||||
|
getRangeDateSales,
|
||||||
}) =>
|
}) =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function()? started,
|
TResult? Function()? started,
|
||||||
TResult? Function(DateTime date)? getDaySales,
|
TResult? Function(DateTime date)? getDaySales,
|
||||||
|
TResult? Function(DateTime startDate, DateTime endDate)? getRangeDateSales,
|
||||||
}) =>
|
}) =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? started,
|
TResult Function()? started,
|
||||||
TResult Function(DateTime date)? getDaySales,
|
TResult Function(DateTime date)? getDaySales,
|
||||||
|
TResult Function(DateTime startDate, DateTime endDate)? getRangeDateSales,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) =>
|
}) =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
@ -39,18 +43,21 @@ mixin _$DaySalesEvent {
|
|||||||
TResult map<TResult extends Object?>({
|
TResult map<TResult extends Object?>({
|
||||||
required TResult Function(_Started value) started,
|
required TResult Function(_Started value) started,
|
||||||
required TResult Function(_GetDaySales value) getDaySales,
|
required TResult Function(_GetDaySales value) getDaySales,
|
||||||
|
required TResult Function(_GetRangeDateSales value) getRangeDateSales,
|
||||||
}) =>
|
}) =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? mapOrNull<TResult extends Object?>({
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
TResult? Function(_Started value)? started,
|
TResult? Function(_Started value)? started,
|
||||||
TResult? Function(_GetDaySales value)? getDaySales,
|
TResult? Function(_GetDaySales value)? getDaySales,
|
||||||
|
TResult? Function(_GetRangeDateSales value)? getRangeDateSales,
|
||||||
}) =>
|
}) =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeMap<TResult extends Object?>({
|
TResult maybeMap<TResult extends Object?>({
|
||||||
TResult Function(_Started value)? started,
|
TResult Function(_Started value)? started,
|
||||||
TResult Function(_GetDaySales value)? getDaySales,
|
TResult Function(_GetDaySales value)? getDaySales,
|
||||||
|
TResult Function(_GetRangeDateSales value)? getRangeDateSales,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) =>
|
}) =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
@ -120,6 +127,8 @@ class _$StartedImpl implements _Started {
|
|||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function() started,
|
required TResult Function() started,
|
||||||
required TResult Function(DateTime date) getDaySales,
|
required TResult Function(DateTime date) getDaySales,
|
||||||
|
required TResult Function(DateTime startDate, DateTime endDate)
|
||||||
|
getRangeDateSales,
|
||||||
}) {
|
}) {
|
||||||
return started();
|
return started();
|
||||||
}
|
}
|
||||||
@ -129,6 +138,7 @@ class _$StartedImpl implements _Started {
|
|||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function()? started,
|
TResult? Function()? started,
|
||||||
TResult? Function(DateTime date)? getDaySales,
|
TResult? Function(DateTime date)? getDaySales,
|
||||||
|
TResult? Function(DateTime startDate, DateTime endDate)? getRangeDateSales,
|
||||||
}) {
|
}) {
|
||||||
return started?.call();
|
return started?.call();
|
||||||
}
|
}
|
||||||
@ -138,6 +148,7 @@ class _$StartedImpl implements _Started {
|
|||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? started,
|
TResult Function()? started,
|
||||||
TResult Function(DateTime date)? getDaySales,
|
TResult Function(DateTime date)? getDaySales,
|
||||||
|
TResult Function(DateTime startDate, DateTime endDate)? getRangeDateSales,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
if (started != null) {
|
if (started != null) {
|
||||||
@ -151,6 +162,7 @@ class _$StartedImpl implements _Started {
|
|||||||
TResult map<TResult extends Object?>({
|
TResult map<TResult extends Object?>({
|
||||||
required TResult Function(_Started value) started,
|
required TResult Function(_Started value) started,
|
||||||
required TResult Function(_GetDaySales value) getDaySales,
|
required TResult Function(_GetDaySales value) getDaySales,
|
||||||
|
required TResult Function(_GetRangeDateSales value) getRangeDateSales,
|
||||||
}) {
|
}) {
|
||||||
return started(this);
|
return started(this);
|
||||||
}
|
}
|
||||||
@ -160,6 +172,7 @@ class _$StartedImpl implements _Started {
|
|||||||
TResult? mapOrNull<TResult extends Object?>({
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
TResult? Function(_Started value)? started,
|
TResult? Function(_Started value)? started,
|
||||||
TResult? Function(_GetDaySales value)? getDaySales,
|
TResult? Function(_GetDaySales value)? getDaySales,
|
||||||
|
TResult? Function(_GetRangeDateSales value)? getRangeDateSales,
|
||||||
}) {
|
}) {
|
||||||
return started?.call(this);
|
return started?.call(this);
|
||||||
}
|
}
|
||||||
@ -169,6 +182,7 @@ class _$StartedImpl implements _Started {
|
|||||||
TResult maybeMap<TResult extends Object?>({
|
TResult maybeMap<TResult extends Object?>({
|
||||||
TResult Function(_Started value)? started,
|
TResult Function(_Started value)? started,
|
||||||
TResult Function(_GetDaySales value)? getDaySales,
|
TResult Function(_GetDaySales value)? getDaySales,
|
||||||
|
TResult Function(_GetRangeDateSales value)? getRangeDateSales,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
if (started != null) {
|
if (started != null) {
|
||||||
@ -252,6 +266,8 @@ class _$GetDaySalesImpl implements _GetDaySales {
|
|||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function() started,
|
required TResult Function() started,
|
||||||
required TResult Function(DateTime date) getDaySales,
|
required TResult Function(DateTime date) getDaySales,
|
||||||
|
required TResult Function(DateTime startDate, DateTime endDate)
|
||||||
|
getRangeDateSales,
|
||||||
}) {
|
}) {
|
||||||
return getDaySales(date);
|
return getDaySales(date);
|
||||||
}
|
}
|
||||||
@ -261,6 +277,7 @@ class _$GetDaySalesImpl implements _GetDaySales {
|
|||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function()? started,
|
TResult? Function()? started,
|
||||||
TResult? Function(DateTime date)? getDaySales,
|
TResult? Function(DateTime date)? getDaySales,
|
||||||
|
TResult? Function(DateTime startDate, DateTime endDate)? getRangeDateSales,
|
||||||
}) {
|
}) {
|
||||||
return getDaySales?.call(date);
|
return getDaySales?.call(date);
|
||||||
}
|
}
|
||||||
@ -270,6 +287,7 @@ class _$GetDaySalesImpl implements _GetDaySales {
|
|||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? started,
|
TResult Function()? started,
|
||||||
TResult Function(DateTime date)? getDaySales,
|
TResult Function(DateTime date)? getDaySales,
|
||||||
|
TResult Function(DateTime startDate, DateTime endDate)? getRangeDateSales,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
if (getDaySales != null) {
|
if (getDaySales != null) {
|
||||||
@ -283,6 +301,7 @@ class _$GetDaySalesImpl implements _GetDaySales {
|
|||||||
TResult map<TResult extends Object?>({
|
TResult map<TResult extends Object?>({
|
||||||
required TResult Function(_Started value) started,
|
required TResult Function(_Started value) started,
|
||||||
required TResult Function(_GetDaySales value) getDaySales,
|
required TResult Function(_GetDaySales value) getDaySales,
|
||||||
|
required TResult Function(_GetRangeDateSales value) getRangeDateSales,
|
||||||
}) {
|
}) {
|
||||||
return getDaySales(this);
|
return getDaySales(this);
|
||||||
}
|
}
|
||||||
@ -292,6 +311,7 @@ class _$GetDaySalesImpl implements _GetDaySales {
|
|||||||
TResult? mapOrNull<TResult extends Object?>({
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
TResult? Function(_Started value)? started,
|
TResult? Function(_Started value)? started,
|
||||||
TResult? Function(_GetDaySales value)? getDaySales,
|
TResult? Function(_GetDaySales value)? getDaySales,
|
||||||
|
TResult? Function(_GetRangeDateSales value)? getRangeDateSales,
|
||||||
}) {
|
}) {
|
||||||
return getDaySales?.call(this);
|
return getDaySales?.call(this);
|
||||||
}
|
}
|
||||||
@ -301,6 +321,7 @@ class _$GetDaySalesImpl implements _GetDaySales {
|
|||||||
TResult maybeMap<TResult extends Object?>({
|
TResult maybeMap<TResult extends Object?>({
|
||||||
TResult Function(_Started value)? started,
|
TResult Function(_Started value)? started,
|
||||||
TResult Function(_GetDaySales value)? getDaySales,
|
TResult Function(_GetDaySales value)? getDaySales,
|
||||||
|
TResult Function(_GetRangeDateSales value)? getRangeDateSales,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
if (getDaySales != null) {
|
if (getDaySales != null) {
|
||||||
@ -322,6 +343,166 @@ abstract class _GetDaySales implements DaySalesEvent {
|
|||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$GetRangeDateSalesImplCopyWith<$Res> {
|
||||||
|
factory _$$GetRangeDateSalesImplCopyWith(_$GetRangeDateSalesImpl value,
|
||||||
|
$Res Function(_$GetRangeDateSalesImpl) then) =
|
||||||
|
__$$GetRangeDateSalesImplCopyWithImpl<$Res>;
|
||||||
|
@useResult
|
||||||
|
$Res call({DateTime startDate, DateTime endDate});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$GetRangeDateSalesImplCopyWithImpl<$Res>
|
||||||
|
extends _$DaySalesEventCopyWithImpl<$Res, _$GetRangeDateSalesImpl>
|
||||||
|
implements _$$GetRangeDateSalesImplCopyWith<$Res> {
|
||||||
|
__$$GetRangeDateSalesImplCopyWithImpl(_$GetRangeDateSalesImpl _value,
|
||||||
|
$Res Function(_$GetRangeDateSalesImpl) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
/// Create a copy of DaySalesEvent
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? startDate = null,
|
||||||
|
Object? endDate = null,
|
||||||
|
}) {
|
||||||
|
return _then(_$GetRangeDateSalesImpl(
|
||||||
|
null == startDate
|
||||||
|
? _value.startDate
|
||||||
|
: startDate // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,
|
||||||
|
null == endDate
|
||||||
|
? _value.endDate
|
||||||
|
: endDate // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
class _$GetRangeDateSalesImpl implements _GetRangeDateSales {
|
||||||
|
const _$GetRangeDateSalesImpl(this.startDate, this.endDate);
|
||||||
|
|
||||||
|
@override
|
||||||
|
final DateTime startDate;
|
||||||
|
@override
|
||||||
|
final DateTime endDate;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'DaySalesEvent.getRangeDateSales(startDate: $startDate, endDate: $endDate)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$GetRangeDateSalesImpl &&
|
||||||
|
(identical(other.startDate, startDate) ||
|
||||||
|
other.startDate == startDate) &&
|
||||||
|
(identical(other.endDate, endDate) || other.endDate == endDate));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType, startDate, endDate);
|
||||||
|
|
||||||
|
/// Create a copy of DaySalesEvent
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$GetRangeDateSalesImplCopyWith<_$GetRangeDateSalesImpl> get copyWith =>
|
||||||
|
__$$GetRangeDateSalesImplCopyWithImpl<_$GetRangeDateSalesImpl>(
|
||||||
|
this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult when<TResult extends Object?>({
|
||||||
|
required TResult Function() started,
|
||||||
|
required TResult Function(DateTime date) getDaySales,
|
||||||
|
required TResult Function(DateTime startDate, DateTime endDate)
|
||||||
|
getRangeDateSales,
|
||||||
|
}) {
|
||||||
|
return getRangeDateSales(startDate, endDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function()? started,
|
||||||
|
TResult? Function(DateTime date)? getDaySales,
|
||||||
|
TResult? Function(DateTime startDate, DateTime endDate)? getRangeDateSales,
|
||||||
|
}) {
|
||||||
|
return getRangeDateSales?.call(startDate, endDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
|
TResult Function()? started,
|
||||||
|
TResult Function(DateTime date)? getDaySales,
|
||||||
|
TResult Function(DateTime startDate, DateTime endDate)? getRangeDateSales,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) {
|
||||||
|
if (getRangeDateSales != null) {
|
||||||
|
return getRangeDateSales(startDate, endDate);
|
||||||
|
}
|
||||||
|
return orElse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult map<TResult extends Object?>({
|
||||||
|
required TResult Function(_Started value) started,
|
||||||
|
required TResult Function(_GetDaySales value) getDaySales,
|
||||||
|
required TResult Function(_GetRangeDateSales value) getRangeDateSales,
|
||||||
|
}) {
|
||||||
|
return getRangeDateSales(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function(_Started value)? started,
|
||||||
|
TResult? Function(_GetDaySales value)? getDaySales,
|
||||||
|
TResult? Function(_GetRangeDateSales value)? getRangeDateSales,
|
||||||
|
}) {
|
||||||
|
return getRangeDateSales?.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeMap<TResult extends Object?>({
|
||||||
|
TResult Function(_Started value)? started,
|
||||||
|
TResult Function(_GetDaySales value)? getDaySales,
|
||||||
|
TResult Function(_GetRangeDateSales value)? getRangeDateSales,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) {
|
||||||
|
if (getRangeDateSales != null) {
|
||||||
|
return getRangeDateSales(this);
|
||||||
|
}
|
||||||
|
return orElse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _GetRangeDateSales implements DaySalesEvent {
|
||||||
|
const factory _GetRangeDateSales(
|
||||||
|
final DateTime startDate, final DateTime endDate) =
|
||||||
|
_$GetRangeDateSalesImpl;
|
||||||
|
|
||||||
|
DateTime get startDate;
|
||||||
|
DateTime get endDate;
|
||||||
|
|
||||||
|
/// Create a copy of DaySalesEvent
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
_$$GetRangeDateSalesImplCopyWith<_$GetRangeDateSalesImpl> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
mixin _$DaySalesState {
|
mixin _$DaySalesState {
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
|
|||||||
@ -6,4 +6,8 @@ class DaySalesEvent with _$DaySalesEvent {
|
|||||||
const factory DaySalesEvent.getDaySales(
|
const factory DaySalesEvent.getDaySales(
|
||||||
DateTime date,
|
DateTime date,
|
||||||
) = _GetDaySales;
|
) = _GetDaySales;
|
||||||
|
const factory DaySalesEvent.getRangeDateSales(
|
||||||
|
DateTime startDate,
|
||||||
|
DateTime endDate,
|
||||||
|
) = _GetRangeDateSales;
|
||||||
}
|
}
|
||||||
|
|||||||
123
lib/presentation/sales/dialog/filter_dialog.dart
Normal file
123
lib/presentation/sales/dialog/filter_dialog.dart
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
import 'package:enaklo_pos/core/components/buttons.dart';
|
||||||
|
import 'package:enaklo_pos/core/components/custom_modal_dialog.dart';
|
||||||
|
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/date_time_ext.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class SalesFilterDialog extends StatefulWidget {
|
||||||
|
final DateTime startDate;
|
||||||
|
final DateTime endDate;
|
||||||
|
final void Function(DateTime start, DateTime end) onDateRangeChanged;
|
||||||
|
const SalesFilterDialog(
|
||||||
|
{super.key,
|
||||||
|
required this.startDate,
|
||||||
|
required this.endDate,
|
||||||
|
required this.onDateRangeChanged});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SalesFilterDialog> createState() => _SalesFilterDialogState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SalesFilterDialogState extends State<SalesFilterDialog> {
|
||||||
|
late DateTimeRange selectedDateRange;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
selectedDateRange =
|
||||||
|
DateTimeRange(start: widget.startDate, end: widget.endDate);
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _selectDateRange(BuildContext context) async {
|
||||||
|
final DateTimeRange? picked = await showDateRangePicker(
|
||||||
|
context: context,
|
||||||
|
initialDateRange: selectedDateRange,
|
||||||
|
firstDate: DateTime(2020),
|
||||||
|
lastDate: DateTime(2100),
|
||||||
|
builder: (context, child) {
|
||||||
|
return Theme(
|
||||||
|
data: Theme.of(context).copyWith(
|
||||||
|
colorScheme: ColorScheme.light(
|
||||||
|
primary: Colors.blue, // Header color
|
||||||
|
onPrimary: Colors.white, // Header text color
|
||||||
|
onSurface: Colors.black, // Body text color
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: child!,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (picked != null) {
|
||||||
|
setState(() {
|
||||||
|
selectedDateRange = picked;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return CustomModalDialog(
|
||||||
|
title: 'Filter',
|
||||||
|
contentPadding: const EdgeInsets.all(16.0),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Periode',
|
||||||
|
style: TextStyle(
|
||||||
|
color: AppColors.black,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () async => await _selectDateRange(context),
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||||
|
margin: EdgeInsets.only(top: 8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color: AppColors.primary,
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
'${selectedDateRange.start.toFormattedDate2()} - ${selectedDateRange.end.toFormattedDate2()}',
|
||||||
|
style: TextStyle(
|
||||||
|
color: AppColors.black,
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Icon(
|
||||||
|
Icons.calendar_month_outlined,
|
||||||
|
color: AppColors.primary,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SpaceHeight(24),
|
||||||
|
Button.filled(
|
||||||
|
onPressed: () {
|
||||||
|
context.pop();
|
||||||
|
widget.onDateRangeChanged(
|
||||||
|
selectedDateRange.start, selectedDateRange.end);
|
||||||
|
},
|
||||||
|
label: 'Terapkan'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,12 +1,18 @@
|
|||||||
import 'dart:developer';
|
import 'package:enaklo_pos/core/components/buttons.dart';
|
||||||
|
import 'package:enaklo_pos/core/components/spaces.dart';
|
||||||
|
import 'package:enaklo_pos/presentation/home/models/order_model.dart';
|
||||||
|
import 'package:enaklo_pos/presentation/sales/blocs/day_sales/day_sales_bloc.dart';
|
||||||
|
import 'package:enaklo_pos/presentation/sales/widgets/sales_detail.dart';
|
||||||
|
import 'package:enaklo_pos/presentation/sales/widgets/sales_list_order.dart';
|
||||||
|
import 'package:enaklo_pos/presentation/sales/widgets/sales_order_information.dart';
|
||||||
|
import 'package:enaklo_pos/presentation/sales/widgets/sales_payment.dart';
|
||||||
|
import 'package:enaklo_pos/presentation/sales/widgets/sales_right_title.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:enaklo_pos/core/constants/colors.dart';
|
|
||||||
import 'package:enaklo_pos/core/extensions/date_time_ext.dart';
|
|
||||||
import 'package:enaklo_pos/presentation/sales/blocs/day_sales/day_sales_bloc.dart';
|
|
||||||
|
|
||||||
import '../widgets/sales_widget.dart';
|
import '../../../core/constants/colors.dart';
|
||||||
|
import '../widgets/sales_card.dart';
|
||||||
|
import '../widgets/sales_title.dart';
|
||||||
|
|
||||||
class SalesPage extends StatefulWidget {
|
class SalesPage extends StatefulWidget {
|
||||||
const SalesPage({super.key});
|
const SalesPage({super.key});
|
||||||
@ -16,110 +22,178 @@ class SalesPage extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _SalesPageState extends State<SalesPage> {
|
class _SalesPageState extends State<SalesPage> {
|
||||||
|
DateTime startDate = DateTime.now();
|
||||||
|
DateTime endDate = DateTime.now();
|
||||||
|
OrderModel? orderDetail;
|
||||||
|
|
||||||
|
int _total = 0;
|
||||||
|
String searchQuery = '';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
context.read<DaySalesBloc>().add(DaySalesEvent.getDaySales(DateTime.now()));
|
context
|
||||||
|
.read<DaySalesBloc>()
|
||||||
|
.add(DaySalesEvent.getRangeDateSales(startDate, endDate));
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<OrderModel> _filterOrders(List<OrderModel> orders) {
|
||||||
|
if (searchQuery.isEmpty) {
|
||||||
|
return orders;
|
||||||
|
}
|
||||||
|
|
||||||
|
return orders.where((order) {
|
||||||
|
final customerName = order.customerName.toLowerCase();
|
||||||
|
final queryLower = searchQuery.toLowerCase();
|
||||||
|
return customerName.contains(queryLower);
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Padding(
|
return SafeArea(
|
||||||
padding: const EdgeInsets.all(32),
|
child: Scaffold(
|
||||||
child: Column(
|
backgroundColor: AppColors.background,
|
||||||
children: [
|
body: Row(
|
||||||
Row(
|
children: [
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
Expanded(
|
||||||
children: [
|
flex: 2,
|
||||||
const Text(
|
child: Material(
|
||||||
'Apskel POS ',
|
color: AppColors.white,
|
||||||
style: TextStyle(
|
child: Column(
|
||||||
color: AppColors.primary,
|
children: [
|
||||||
fontSize: 22,
|
SalesTitle(
|
||||||
fontWeight: FontWeight.w600,
|
startDate: startDate,
|
||||||
|
endDate: endDate,
|
||||||
|
total: _total,
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() {
|
||||||
|
searchQuery = value;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onDateRangeChanged: (start, end) {
|
||||||
|
setState(() {
|
||||||
|
startDate = start;
|
||||||
|
endDate = end;
|
||||||
|
});
|
||||||
|
|
||||||
|
context.read<DaySalesBloc>().add(
|
||||||
|
DaySalesEvent.getRangeDateSales(
|
||||||
|
startDate, endDate));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: BlocBuilder<DaySalesBloc, DaySalesState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return state.maybeWhen(
|
||||||
|
orElse: () => const Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
),
|
||||||
|
loaded: (orders) {
|
||||||
|
final filtered = _filterOrders(orders);
|
||||||
|
if (filtered.isEmpty) {
|
||||||
|
return Center(
|
||||||
|
child: Text(
|
||||||
|
"Belum ada transaksi saat ini. ",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16.0,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
WidgetsBinding.instance
|
||||||
|
.addPostFrameCallback((_) {
|
||||||
|
setState(() {
|
||||||
|
_total = filtered.length;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
children: List.generate(
|
||||||
|
filtered.length,
|
||||||
|
(index) => GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
orderDetail = filtered[index];
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: SalesCard(
|
||||||
|
order: orders[index],
|
||||||
|
isActive:
|
||||||
|
orders[index] == orderDetail,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
|
||||||
"${DateTime.now().toFormattedDate()}",
|
|
||||||
style: const TextStyle(
|
|
||||||
color: AppColors.subtitle,
|
|
||||||
fontSize: 16,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 12.0,
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: BlocBuilder<DaySalesBloc, DaySalesState>(
|
|
||||||
builder: (context, state) {
|
|
||||||
return state.maybeWhen(
|
|
||||||
orElse: () => const Center(
|
|
||||||
child: CircularProgressIndicator(),
|
|
||||||
),
|
|
||||||
loaded: (orders) {
|
|
||||||
log("message: ${orders.length}");
|
|
||||||
if (orders.isEmpty) {
|
|
||||||
return Center(
|
|
||||||
child: Text(
|
|
||||||
"Belum ada transaksi saat ini. ",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 16.0,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return SalesWidget(
|
|
||||||
headerWidgets: _getTitleHeaderWidget(),
|
|
||||||
orders: orders,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
Expanded(
|
||||||
],
|
flex: 4,
|
||||||
),
|
child: orderDetail == null
|
||||||
);
|
? Center(
|
||||||
}
|
child: Text(
|
||||||
|
"Belum ada order yang dipilih.",
|
||||||
List<Widget> _getTitleHeaderWidget() {
|
style: TextStyle(
|
||||||
return [
|
fontSize: 16.0,
|
||||||
_getTitleItemWidget('ID', 40),
|
fontWeight: FontWeight.bold,
|
||||||
_getTitleItemWidget('Customer', 120),
|
),
|
||||||
_getTitleItemWidget('Status', 120),
|
),
|
||||||
_getTitleItemWidget('Sync', 60),
|
)
|
||||||
_getTitleItemWidget('Payment Status', 120),
|
: Column(
|
||||||
_getTitleItemWidget('Payment Method', 120),
|
children: [
|
||||||
_getTitleItemWidget('Payment Amount', 120),
|
SalesRightTitle(
|
||||||
_getTitleItemWidget('Sub Total', 120),
|
order: orderDetail,
|
||||||
_getTitleItemWidget('Tax', 120),
|
actionWidget: [
|
||||||
_getTitleItemWidget('Discount', 60),
|
Button.outlined(
|
||||||
_getTitleItemWidget('Service Charge', 120),
|
onPressed: () {},
|
||||||
_getTitleItemWidget('Total', 120),
|
label: 'Refund',
|
||||||
_getTitleItemWidget('Payment', 60),
|
icon: Icon(Icons.autorenew),
|
||||||
_getTitleItemWidget('Item', 60),
|
),
|
||||||
_getTitleItemWidget('Cashier', 150),
|
],
|
||||||
_getTitleItemWidget('Time', 230),
|
),
|
||||||
_getTitleItemWidget('Action', 230),
|
Padding(
|
||||||
];
|
padding: const EdgeInsets.all(16.0),
|
||||||
}
|
child: Column(
|
||||||
|
children: [
|
||||||
Widget _getTitleItemWidget(String label, double width) {
|
Row(
|
||||||
return Container(
|
children: [
|
||||||
width: width,
|
Expanded(
|
||||||
height: 56,
|
child: SalesOrderInformation(
|
||||||
color: AppColors.primary,
|
order: orderDetail,
|
||||||
alignment: Alignment.centerLeft,
|
),
|
||||||
child: Center(
|
),
|
||||||
child: Text(
|
SpaceWidth(16),
|
||||||
label,
|
Expanded(
|
||||||
style: const TextStyle(
|
child: SalesDetail(
|
||||||
color: Colors.white,
|
order: orderDetail,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SalesListOrder(
|
||||||
|
order: orderDetail,
|
||||||
|
),
|
||||||
|
SalesPayment(
|
||||||
|
order: orderDetail,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
127
lib/presentation/sales/pages/sales_page.dart.backup
Normal file
127
lib/presentation/sales/pages/sales_page.dart.backup
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:enaklo_pos/core/constants/colors.dart';
|
||||||
|
import 'package:enaklo_pos/core/extensions/date_time_ext.dart';
|
||||||
|
import 'package:enaklo_pos/presentation/sales/blocs/day_sales/day_sales_bloc.dart';
|
||||||
|
|
||||||
|
import '../widgets/sales_widget.dart';
|
||||||
|
|
||||||
|
class SalesPage extends StatefulWidget {
|
||||||
|
const SalesPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SalesPage> createState() => _SalesPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SalesPageState extends State<SalesPage> {
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
context.read<DaySalesBloc>().add(DaySalesEvent.getDaySales(DateTime.now()));
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.all(32),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
'Apskel POS ',
|
||||||
|
style: TextStyle(
|
||||||
|
color: AppColors.primary,
|
||||||
|
fontSize: 22,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"${DateTime.now().toFormattedDate()}",
|
||||||
|
style: const TextStyle(
|
||||||
|
color: AppColors.subtitle,
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 12.0,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: BlocBuilder<DaySalesBloc, DaySalesState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return state.maybeWhen(
|
||||||
|
orElse: () => const Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
),
|
||||||
|
loaded: (orders) {
|
||||||
|
log("message: ${orders.length}");
|
||||||
|
if (orders.isEmpty) {
|
||||||
|
return Center(
|
||||||
|
child: Text(
|
||||||
|
"Belum ada transaksi saat ini. ",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16.0,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return SalesWidget(
|
||||||
|
headerWidgets: _getTitleHeaderWidget(),
|
||||||
|
orders: orders,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Widget> _getTitleHeaderWidget() {
|
||||||
|
return [
|
||||||
|
_getTitleItemWidget('ID', 40),
|
||||||
|
_getTitleItemWidget('Customer', 120),
|
||||||
|
_getTitleItemWidget('Status', 120),
|
||||||
|
_getTitleItemWidget('Sync', 60),
|
||||||
|
_getTitleItemWidget('Payment Status', 120),
|
||||||
|
_getTitleItemWidget('Payment Method', 120),
|
||||||
|
_getTitleItemWidget('Payment Amount', 120),
|
||||||
|
_getTitleItemWidget('Sub Total', 120),
|
||||||
|
_getTitleItemWidget('Tax', 120),
|
||||||
|
_getTitleItemWidget('Discount', 60),
|
||||||
|
_getTitleItemWidget('Service Charge', 120),
|
||||||
|
_getTitleItemWidget('Total', 120),
|
||||||
|
_getTitleItemWidget('Payment', 60),
|
||||||
|
_getTitleItemWidget('Item', 60),
|
||||||
|
_getTitleItemWidget('Cashier', 150),
|
||||||
|
_getTitleItemWidget('Time', 230),
|
||||||
|
_getTitleItemWidget('Action', 230),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _getTitleItemWidget(String label, double width) {
|
||||||
|
return Container(
|
||||||
|
width: width,
|
||||||
|
height: 56,
|
||||||
|
color: AppColors.primary,
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
label,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
113
lib/presentation/sales/widgets/sales_card.dart
Normal file
113
lib/presentation/sales/widgets/sales_card.dart
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
import 'package:enaklo_pos/core/constants/colors.dart';
|
||||||
|
import 'package:enaklo_pos/core/extensions/date_time_ext.dart';
|
||||||
|
import 'package:enaklo_pos/core/extensions/int_ext.dart';
|
||||||
|
import 'package:enaklo_pos/presentation/home/models/order_model.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class SalesCard extends StatelessWidget {
|
||||||
|
final OrderModel order;
|
||||||
|
final bool isActive;
|
||||||
|
|
||||||
|
const SalesCard({
|
||||||
|
super.key,
|
||||||
|
required this.order,
|
||||||
|
required this.isActive,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: isActive ? AppColors.primary.withOpacity(0.1) : AppColors.white,
|
||||||
|
border:
|
||||||
|
Border.all(color: isActive ? AppColors.primary : AppColors.stroke),
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
CircleAvatar(
|
||||||
|
radius: 22,
|
||||||
|
backgroundColor: AppColors.primary,
|
||||||
|
child: Icon(Icons.person, color: Colors.white),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
order.customerName == ""
|
||||||
|
? "Anonim"
|
||||||
|
: order.customerName,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Icon(Icons.table_bar, size: 16, color: Colors.grey),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
Text(
|
||||||
|
'Meja ${order.tableNumber}',
|
||||||
|
style: TextStyle(color: Colors.grey[600]),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.green.withOpacity(0.15),
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
order.status.toUpperCase(),
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.green,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
fontSize: 12,
|
||||||
|
letterSpacing: 0.5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
order.total.currencyFormatRpV2,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: AppColors.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
DateTime.parse(order.transactionTime).toFormattedDate2(),
|
||||||
|
style: TextStyle(
|
||||||
|
color: AppColors.black,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
76
lib/presentation/sales/widgets/sales_detail.dart
Normal file
76
lib/presentation/sales/widgets/sales_detail.dart
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import 'package:enaklo_pos/core/constants/colors.dart';
|
||||||
|
import 'package:enaklo_pos/core/extensions/date_time_ext.dart';
|
||||||
|
import 'package:enaklo_pos/presentation/home/models/order_model.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class SalesDetail extends StatelessWidget {
|
||||||
|
final OrderModel? order;
|
||||||
|
const SalesDetail({super.key, this.order});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColors.white,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Detail',
|
||||||
|
style: TextStyle(
|
||||||
|
color: AppColors.black,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
_item(
|
||||||
|
title: 'Pelanggan',
|
||||||
|
value: order?.customerName ?? "-",
|
||||||
|
),
|
||||||
|
_item(
|
||||||
|
title: 'Waktu',
|
||||||
|
value:
|
||||||
|
DateTime.parse(order?.transactionTime ?? "").toFormattedDate3(),
|
||||||
|
),
|
||||||
|
_item(
|
||||||
|
title: 'Status',
|
||||||
|
value: order?.paymentStatus ?? "-",
|
||||||
|
),
|
||||||
|
_item(
|
||||||
|
title: 'Jenis Order',
|
||||||
|
value: order?.paymentMethod ?? "-",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Padding _item({
|
||||||
|
required String title,
|
||||||
|
required String value,
|
||||||
|
}) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 12),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
title,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
value,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
88
lib/presentation/sales/widgets/sales_list_order.dart
Normal file
88
lib/presentation/sales/widgets/sales_list_order.dart
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
import 'package:enaklo_pos/core/constants/colors.dart';
|
||||||
|
import 'package:enaklo_pos/presentation/home/models/order_model.dart';
|
||||||
|
import 'package:enaklo_pos/presentation/home/models/product_quantity.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class SalesListOrder extends StatelessWidget {
|
||||||
|
final OrderModel? order;
|
||||||
|
const SalesListOrder({super.key, this.order});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
margin: const EdgeInsets.only(top: 16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColors.white,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: 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: Text(
|
||||||
|
'Daftar Pembelian',
|
||||||
|
style: TextStyle(
|
||||||
|
color: AppColors.black,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
children: List.generate(
|
||||||
|
order?.orderItems.length ?? 0,
|
||||||
|
(index) => _item(order!.orderItems[index]),
|
||||||
|
).toList(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Padding _item(ProductQuantity product) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16)
|
||||||
|
.copyWith(bottom: 0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
product.product.name ?? '',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
product.product.price ?? '',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'X${product.quantity}',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
product.product.price ?? '',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
76
lib/presentation/sales/widgets/sales_order_information.dart
Normal file
76
lib/presentation/sales/widgets/sales_order_information.dart
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import 'package:enaklo_pos/core/constants/colors.dart';
|
||||||
|
import 'package:enaklo_pos/core/extensions/date_time_ext.dart';
|
||||||
|
import 'package:enaklo_pos/presentation/home/models/order_model.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class SalesOrderInformation extends StatelessWidget {
|
||||||
|
final OrderModel? order;
|
||||||
|
const SalesOrderInformation({super.key, this.order});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColors.white,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Informasi Pesanan',
|
||||||
|
style: TextStyle(
|
||||||
|
color: AppColors.black,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
_item(
|
||||||
|
title: 'No. Order',
|
||||||
|
value: "${order?.id}",
|
||||||
|
),
|
||||||
|
_item(
|
||||||
|
title: 'Tanggal',
|
||||||
|
value:
|
||||||
|
DateTime.parse(order?.transactionTime ?? "").toFormattedDate3(),
|
||||||
|
),
|
||||||
|
_item(
|
||||||
|
title: 'Kasir',
|
||||||
|
value: order?.namaKasir ?? "-",
|
||||||
|
),
|
||||||
|
_item(
|
||||||
|
title: 'Jenis Order',
|
||||||
|
value: order?.orderType.value ?? "-",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Padding _item({
|
||||||
|
required String title,
|
||||||
|
required String value,
|
||||||
|
}) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 12),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
title,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
value,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
99
lib/presentation/sales/widgets/sales_payment.dart
Normal file
99
lib/presentation/sales/widgets/sales_payment.dart
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
import 'package:enaklo_pos/core/components/dashed_divider.dart';
|
||||||
|
import 'package:enaklo_pos/core/components/spaces.dart';
|
||||||
|
import 'package:enaklo_pos/core/constants/colors.dart';
|
||||||
|
import 'package:enaklo_pos/core/extensions/int_ext.dart';
|
||||||
|
import 'package:enaklo_pos/presentation/home/models/order_model.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class SalesPayment extends StatelessWidget {
|
||||||
|
final OrderModel? order;
|
||||||
|
const SalesPayment({super.key, this.order});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
margin: const EdgeInsets.only(top: 16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColors.white,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Informasi Pesanan',
|
||||||
|
style: TextStyle(
|
||||||
|
color: AppColors.black,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SpaceHeight(12),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Subtotal ${order?.totalItem} Produk',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
(order?.subTotal)?.currencyFormatRp ?? "0",
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SpaceHeight(12),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Pajak (11%)',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
(order?.tax)?.currencyFormatRp ?? "0",
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SpaceHeight(12),
|
||||||
|
DashedDivider(
|
||||||
|
color: AppColors.stroke,
|
||||||
|
),
|
||||||
|
SpaceHeight(12),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Total',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
(order?.total)?.currencyFormatRp ?? "0",
|
||||||
|
style: const TextStyle(
|
||||||
|
color: AppColors.primary,
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
43
lib/presentation/sales/widgets/sales_right_title.dart
Normal file
43
lib/presentation/sales/widgets/sales_right_title.dart
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import 'package:enaklo_pos/core/constants/colors.dart';
|
||||||
|
import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
|
||||||
|
import 'package:enaklo_pos/presentation/home/models/order_model.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class SalesRightTitle extends StatelessWidget {
|
||||||
|
final OrderModel? order;
|
||||||
|
final List<Widget>? actionWidget;
|
||||||
|
const SalesRightTitle({super.key, this.order, this.actionWidget});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
height: context.deviceHeight * 0.1,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColors.white,
|
||||||
|
border: Border(
|
||||||
|
bottom: BorderSide(
|
||||||
|
color: AppColors.background,
|
||||||
|
width: 1.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
"Detail Pesanan #${order?.id}",
|
||||||
|
style: TextStyle(
|
||||||
|
color: AppColors.black,
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (actionWidget != null) ...actionWidget!,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
146
lib/presentation/sales/widgets/sales_title.dart
Normal file
146
lib/presentation/sales/widgets/sales_title.dart
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
import 'package:enaklo_pos/core/components/components.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/date_time_ext.dart';
|
||||||
|
import 'package:enaklo_pos/presentation/sales/dialog/filter_dialog.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class SalesTitle extends StatelessWidget {
|
||||||
|
final DateTime startDate;
|
||||||
|
final DateTime endDate;
|
||||||
|
final int total;
|
||||||
|
final Function(String) onChanged;
|
||||||
|
final void Function(DateTime start, DateTime end) onDateRangeChanged;
|
||||||
|
|
||||||
|
const SalesTitle(
|
||||||
|
{super.key,
|
||||||
|
required this.startDate,
|
||||||
|
required this.endDate,
|
||||||
|
required this.onChanged,
|
||||||
|
required this.total,
|
||||||
|
required this.onDateRangeChanged});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
height: context.deviceHeight * 0.1,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColors.white,
|
||||||
|
border: Border(
|
||||||
|
bottom: BorderSide(
|
||||||
|
color: AppColors.background,
|
||||||
|
width: 1.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () => context.pop(),
|
||||||
|
child: Icon(
|
||||||
|
Icons.arrow_back,
|
||||||
|
color: AppColors.primary,
|
||||||
|
size: 24,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SpaceWidth(16),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
"Daftar Pesanan",
|
||||||
|
style: TextStyle(
|
||||||
|
color: AppColors.black,
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 16.0,
|
||||||
|
vertical: 12.0,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColors.white,
|
||||||
|
border: Border(
|
||||||
|
bottom: BorderSide(
|
||||||
|
color: AppColors.background,
|
||||||
|
width: 1.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
startDate.toFormattedDate2() == endDate.toFormattedDate2()
|
||||||
|
? startDate.toFormattedDate2()
|
||||||
|
: '${startDate.toFormattedDate2()} - ${endDate.toFormattedDate2()}',
|
||||||
|
style: TextStyle(
|
||||||
|
color: AppColors.black,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'$total Pesanan',
|
||||||
|
style: TextStyle(
|
||||||
|
color: AppColors.black,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SpaceHeight(16),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: TextFormField(
|
||||||
|
onChanged: onChanged,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
prefixIcon: Icon(
|
||||||
|
Icons.search,
|
||||||
|
),
|
||||||
|
hintText: 'Cari Pesanan',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SpaceWidth(12),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () => showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => SalesFilterDialog(
|
||||||
|
startDate: startDate,
|
||||||
|
endDate: endDate,
|
||||||
|
onDateRangeChanged: onDateRangeChanged,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColors.primary,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: Icon(
|
||||||
|
Icons.filter_list_outlined,
|
||||||
|
color: AppColors.white,
|
||||||
|
size: 24,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user