Compare commits

..
21 Commits
Author SHA1 Message Date
Efril 457ed38827 fix print cut 2026-03-01 12:28:00 +07:00
Efril d34487d883 update version 2026-02-28 13:20:13 +07:00
Efril f4cdfcb96f fix print 2026-02-27 19:09:39 +07:00
Efril 0bccec8a1e fix printer 2026-02-24 00:10:02 +07:00
efrilm 86d2196a04 version 2026-01-14 10:33:53 +07:00
efrilm aa25de8da7 void product 2025-11-21 20:03:06 +07:00
efrilm 96387c08f4 fix split bill 2025-11-21 19:53:02 +07:00
efrilm e585cf4292 update 2025-10-23 20:11:16 +07:00
efrilm 290360674f update 2025-10-08 17:06:23 +07:00
efrilm 1fbacae1f4 fix category tabbar 2025-10-08 13:01:26 +07:00
efrilm 613b216c04 fix force close 2025-10-07 21:49:15 +07:00
efrilm 455a6afd70 print kitchen update 2025-10-02 17:57:48 +07:00
efrilm 2813011fac update print 2025-09-27 18:10:47 +07:00
efrilm 83af323a2f version and build number 2025-09-22 15:11:04 +07:00
efrilm cef1f79032 fix overflow in confirm payment 2025-09-22 14:47:41 +07:00
efrilm 59a8d7f661 refresh token 2025-09-20 18:05:15 +07:00
efrilm a58d1040af sync data 2025-09-20 05:02:01 +07:00
efrilm 72a464b4c0 data sync page 2025-09-20 04:45:08 +07:00
efrilm 5b980d237f category local 2025-09-20 04:36:22 +07:00
efrilm c12d6525fa Update 2025-09-20 03:57:12 +07:00
efrilm 44402140fb Printer Local data and remove unused 2025-09-20 03:56:07 +07:00
82 changed files with 6983 additions and 8119 deletions
+1 -1
View File
@@ -47,7 +47,7 @@ android {
applicationId "com.appscale.pos"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdkVersion 23
minSdkVersion flutter.minSdkVersion
targetSdkVersion 35
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
+77 -3
View File
@@ -23,7 +23,7 @@ class DatabaseHelper {
return await openDatabase(
path,
version: 1,
version: 3, // Updated version for categories table
onCreate: _onCreate,
onUpgrade: _onUpgrade,
);
@@ -66,17 +66,91 @@ class DatabaseHelper {
)
''');
// Categories table - NEW
await db.execute('''
CREATE TABLE categories (
id TEXT PRIMARY KEY,
organization_id TEXT,
name TEXT NOT NULL,
description TEXT,
business_type TEXT,
metadata TEXT,
is_active INTEGER DEFAULT 1,
created_at TEXT,
updated_at TEXT
)
''');
// Printer table
await db.execute('''
CREATE TABLE printers (
id INTEGER PRIMARY KEY AUTOINCREMENT,
code TEXT UNIQUE NOT NULL,
name TEXT NOT NULL,
address TEXT,
paper TEXT,
type TEXT,
created_at TEXT,
updated_at TEXT
)
''');
// Create indexes for better performance
await db.execute(
'CREATE INDEX idx_products_category_id ON products(category_id)');
await db.execute('CREATE INDEX idx_products_name ON products(name)');
await db.execute('CREATE INDEX idx_products_sku ON products(sku)');
await db.execute('CREATE INDEX idx_categories_name ON categories(name)');
await db.execute(
'CREATE INDEX idx_products_description ON products(description)');
'CREATE INDEX idx_categories_organization_id ON categories(organization_id)');
await db.execute(
'CREATE INDEX idx_categories_is_active ON categories(is_active)');
await db.execute('CREATE INDEX idx_printers_code ON printers(code)');
await db.execute('CREATE INDEX idx_printers_type ON printers(type)');
}
Future<void> _onUpgrade(Database db, int oldVersion, int newVersion) async {
// Handle database upgrades here
if (oldVersion < 2) {
// Add printer table in version 2
await db.execute('''
CREATE TABLE printers (
id INTEGER PRIMARY KEY AUTOINCREMENT,
code TEXT UNIQUE NOT NULL,
name TEXT NOT NULL,
address TEXT,
paper TEXT,
type TEXT,
created_at TEXT,
updated_at TEXT
)
''');
await db.execute('CREATE INDEX idx_printers_code ON printers(code)');
await db.execute('CREATE INDEX idx_printers_type ON printers(type)');
}
if (oldVersion < 3) {
// Add categories table in version 3
await db.execute('''
CREATE TABLE categories (
id TEXT PRIMARY KEY,
organization_id TEXT,
name TEXT NOT NULL,
description TEXT,
business_type TEXT,
metadata TEXT,
is_active INTEGER DEFAULT 1,
created_at TEXT,
updated_at TEXT
)
''');
await db.execute('CREATE INDEX idx_categories_name ON categories(name)');
await db.execute(
'CREATE INDEX idx_categories_organization_id ON categories(organization_id)');
await db.execute(
'CREATE INDEX idx_categories_is_active ON categories(is_active)');
}
}
Future<void> close() async {
+170 -11
View File
@@ -6,8 +6,8 @@ import 'package:enaklo_pos/core/extensions/string_ext.dart';
import 'package:enaklo_pos/core/utils/printer_service.dart';
import 'package:enaklo_pos/data/dataoutputs/print_dataoutputs.dart';
import 'package:enaklo_pos/data/datasources/auth_local_datasource.dart';
import 'package:enaklo_pos/data/datasources/outlet_local_datasource.dart';
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
import 'package:enaklo_pos/data/datasources/outlet/outlet_local_datasource.dart';
import 'package:enaklo_pos/data/datasources/printer/printer_local_datasource.dart';
import 'package:enaklo_pos/data/datasources/settings_local_datasource.dart';
import 'package:enaklo_pos/data/models/response/order_response_model.dart';
import 'package:enaklo_pos/data/type/bussines_type.dart';
@@ -28,13 +28,13 @@ Future<void> onPrint(
if (outlet.businessType == BusinessType.restaurant) {
final checkerPrinter =
await ProductLocalDatasource.instance.getPrinterByCode('checker');
await PrinterLocalDatasource.instance.getPrinterByCode('checker');
final kitchenPrinter =
await ProductLocalDatasource.instance.getPrinterByCode('kitchen');
final barPrinter =
await ProductLocalDatasource.instance.getPrinterByCode('bar');
await PrinterLocalDatasource.instance.getPrinterByCode('kitchen');
final receiptPrinter =
await ProductLocalDatasource.instance.getPrinterByCode('receipt');
await PrinterLocalDatasource.instance.getPrinterByCode('receipt');
final barPrinter =
await PrinterLocalDatasource.instance.getPrinterByCode('bar');
if (receiptPrinter != null) {
try {
@@ -135,7 +135,8 @@ Future<void> onPrint(
final productByPrinter = productQuantity
.where((item) => item.product.printerType == 'kitchen')
.toList();
final printValue = await PrintDataoutputs.instance.printKitchen(
final printValue =
await PrintDataoutputs.instance.printKitchenPerProduct(
productByPrinter,
order.tableNumber ?? "",
order.orderNumber ?? "",
@@ -232,7 +233,115 @@ Future<void> onPrint(
if (outlet.businessType == BusinessType.ticketing) {
final ticketPrinter =
await ProductLocalDatasource.instance.getPrinterByCode('ticket');
await PrinterLocalDatasource.instance.getPrinterByCode('ticket');
final barcode = await generateBarcodeAsUint8List(order.orderNumber ?? "");
if (ticketPrinter != null) {
try {
final printValue = await PrintDataoutputs.instance.printTicket(
order.totalAmount ?? 0,
barcode,
ticketPrinter.paper.toIntegerFromText,
);
await PrinterService()
// ignore: use_build_context_synchronously
.printWithPrinter(ticketPrinter, printValue, context);
} catch (e, stackTrace) {
FirebaseCrashlytics.instance.recordError(
e,
stackTrace,
reason: 'Error printing ticket ${ticketPrinter.name}',
information: [
'Printer: ticket',
'data: ${ticketPrinter.toMap()}',
],
);
log("Error printing ticket: $e");
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error printing ticket: $e')),
);
}
} else {
FirebaseCrashlytics.instance.recordError(
'Ticket printer not found',
null,
reason:
'Ticket printer not found / Printer not setting in printer page',
information: [
'Order ID: ${order.id}',
],
);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Anda belum menghubungkan printer ticket')),
);
}
}
}
Future<void> onPrintBill(
BuildContext context, {
required List<ProductQuantity> productQuantity,
required Order order,
}) async {
final outlet = await OutletLocalDatasource().get();
final settings = await SettingsLocalDatasource().getTax();
final authData = await AuthLocalDataSource().getAuthData();
if (outlet.businessType == BusinessType.restaurant) {
final receiptPrinter =
await PrinterLocalDatasource.instance.getPrinterByCode('receipt');
if (receiptPrinter != null) {
try {
final printValue = await PrintDataoutputs.instance.printOrderV4(
order,
authData.user?.name ?? "",
'',
0,
0,
settings.value,
receiptPrinter.paper.toIntegerFromText,
order.orderType ?? "",
outlet,
productQuantity);
await PrinterService()
.printWithPrinter(receiptPrinter, printValue, context);
} catch (e, stackTrace) {
FirebaseCrashlytics.instance.recordError(
e,
stackTrace,
reason: 'Print receipt failed',
information: [
'Order ID: ${order.id}',
'Printer: ${receiptPrinter.name}',
],
);
log("Error printing receipt order: $e");
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error printing receipt order: $e')),
);
}
} else {
FirebaseCrashlytics.instance.recordError(
'Receipt printer not found',
null,
reason:
'Receipt printer not found / Printer not setting in printer page',
information: [
'Order ID: ${order.id}',
],
);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Anda belum menghubungkan printer receipt')),
);
}
}
if (outlet.businessType == BusinessType.ticketing) {
final ticketPrinter =
await PrinterLocalDatasource.instance.getPrinterByCode('ticket');
final barcode = await generateBarcodeAsUint8List(order.orderNumber ?? "");
@@ -288,7 +397,7 @@ Future<void> onPrintRecipt(
required List<ProductQuantity> productQuantity,
}) async {
final receiptPrinter =
await ProductLocalDatasource.instance.getPrinterByCode('receipt');
await PrinterLocalDatasource.instance.getPrinterByCode('receipt');
final authData = await AuthLocalDataSource().getAuthData();
final settings = await SettingsLocalDatasource().getTax();
final outlet = await OutletLocalDatasource().get();
@@ -346,7 +455,7 @@ Future<void> onPrinVoidRecipt(
required int totalVoid,
}) async {
final receiptPrinter =
await ProductLocalDatasource.instance.getPrinterByCode('receipt');
await PrinterLocalDatasource.instance.getPrinterByCode('receipt');
final authData = await AuthLocalDataSource().getAuthData();
final settings = await SettingsLocalDatasource().getTax();
final outlet = await OutletLocalDatasource().get();
@@ -372,6 +481,56 @@ Future<void> onPrinVoidRecipt(
}
}
Future<void> onPrintSplit(
context, {
required Order order,
}) async {
final receiptPrinter =
await PrinterLocalDatasource.instance.getPrinterByCode('receipt');
final authData = await AuthLocalDataSource().getAuthData();
final settings = await SettingsLocalDatasource().getTax();
final outlet = await OutletLocalDatasource().get();
if (receiptPrinter != null) {
try {
final printValue = await PrintDataoutputs.instance.printSplitBill(
order,
authData.user?.name ?? "",
receiptPrinter.paper.toIntegerFromText,
outlet,
);
await PrinterService()
.printWithPrinter(receiptPrinter, printValue, context);
} catch (e, stackTrace) {
FirebaseCrashlytics.instance.recordError(
e,
stackTrace,
reason: 'Print receipt failed',
information: [
'Order ID: ${order.id}',
'Printer: ${receiptPrinter.name}',
],
);
log("Error printing receipt order: $e");
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error printing receipt order: $e')),
);
}
} else {
FirebaseCrashlytics.instance.recordError(
'Kitchen printer not found',
null,
reason: 'Kitchen printer not found / Printer not setting in printer page',
information: [
'Order ID: ${order.id}',
],
);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Anda belum menghubungkan printer kitchen')),
);
}
}
Future<Uint8List> generateBarcodeAsUint8List(String data) async {
// 1. Buat barcode instance (code128, qrCode, dll)
final barcode = Barcode.code128();
+16 -4
View File
@@ -1,6 +1,9 @@
import 'package:awesome_dio_interceptor/awesome_dio_interceptor.dart';
import 'package:dio/dio.dart';
import 'package:enaklo_pos/core/constants/variables.dart';
import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
import 'package:enaklo_pos/data/datasources/auth_local_datasource.dart';
import 'package:enaklo_pos/data/models/response/auth_response_model.dart';
import 'package:enaklo_pos/presentation/auth/login_page.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
@@ -124,13 +127,17 @@ class AuthInterceptorWithRefresh extends Interceptor {
Future<bool> _tryRefreshToken() async {
try {
final prefs = await SharedPreferences.getInstance();
final refreshToken = prefs.getString('refresh_token');
final authData = await AuthLocalDataSource().getAuthData();
if (refreshToken == null) return false;
final url = '${Variables.baseUrl}/api/v1/auth/refresh';
final response = await Dio().post(
'YOUR_REFRESH_TOKEN_ENDPOINT',
data: {'refresh_token': refreshToken},
url,
options: Options(
headers: {
'Authorization': 'Bearer ${authData.refreshToken}',
},
),
);
if (response.statusCode == 200) {
@@ -140,6 +147,11 @@ class AuthInterceptorWithRefresh extends Interceptor {
await prefs.setString('auth_token', newToken);
await prefs.setString('refresh_token', newRefreshToken);
AuthResponseModel authResponseModel =
AuthResponseModel.fromMap(response.data['data']);
AuthLocalDataSource().saveAuthData(authResponseModel);
return true;
}
} catch (e) {
+1 -1
View File
@@ -1,7 +1,7 @@
import 'dart:io';
import 'package:enaklo_pos/core/utils/helper_pdf_service.dart';
import 'package:enaklo_pos/data/datasources/outlet_local_datasource.dart';
import 'package:enaklo_pos/data/datasources/outlet/outlet_local_datasource.dart';
import 'package:enaklo_pos/data/models/response/inventory_analytic_response_model.dart';
import 'package:flutter/services.dart';
import 'package:pdf/pdf.dart';
+93 -94
View File
@@ -4,12 +4,16 @@ import 'package:flutter/material.dart';
import 'package:flutter_esc_pos_network/flutter_esc_pos_network.dart';
import 'package:print_bluetooth_thermal/print_bluetooth_thermal.dart';
import 'package:enaklo_pos/data/models/response/print_model.dart';
import 'package:synchronized/synchronized.dart';
class PrinterService {
static final PrinterService _instance = PrinterService._internal();
factory PrinterService() => _instance;
PrinterService._internal();
final Map<String, Lock> _locks = {};
String? _connectedMac;
/// Connect to Bluetooth printer
Future<bool> connectBluetoothPrinter(String macAddress) async {
try {
@@ -56,6 +60,71 @@ class PrinterService {
}
}
Lock _getLock(String address) {
return _locks.putIfAbsent(address, () => Lock());
}
Future<bool> printWithPrinter(
PrintModel printer, List<int> printData, BuildContext context) async {
if (printer.type == 'Bluetooth') {
return await _getLock(printer.address).synchronized(() async {
try {
bool isConnected = await PrintBluetoothThermal.connectionStatus;
if (!isConnected || _connectedMac != printer.address) {
if (isConnected) {
await PrintBluetoothThermal.disconnect;
await Future.delayed(const Duration(milliseconds: 1500));
}
bool connected = await PrintBluetoothThermal.connect(
macPrinterAddress: printer.address);
if (!connected) {
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Gagal connect ke ${printer.name}')),
);
}
return false;
}
_connectedMac = printer.address;
await Future.delayed(
const Duration(milliseconds: 1000)); // naikkan dari 500ms
}
bool result = await _writeBytesChunked(printData);
// Beri waktu printer flush sebelum job berikutnya
await Future.delayed(const Duration(milliseconds: 1000));
log("Print result ${printer.name}: $result");
return result;
} catch (e, stackTrace) {
_connectedMac = null;
FirebaseCrashlytics.instance.recordError(e, stackTrace,
reason: 'Error printing ${printer.name}',
information: [
'Printer: ${printer.name}',
'MAC: ${printer.address}'
]);
return false;
}
});
} else {
return await printNetwork(printer, printData, context);
}
}
// Kirim data per chunk untuk hindari buffer overflow Bluetooth
Future<bool> _writeBytesChunked(List<int> data, {int chunkSize = 512}) async {
for (int i = 0; i < data.length; i += chunkSize) {
final end = (i + chunkSize > data.length) ? data.length : i + chunkSize;
final chunk = data.sublist(i, end);
bool result = await PrintBluetoothThermal.writeBytes(chunk);
if (!result) return false;
await Future.delayed(const Duration(milliseconds: 30));
}
return true;
}
/// Print using Bluetooth printer
Future<bool> printBluetooth(List<int> printData) async {
try {
@@ -99,17 +168,18 @@ class PrinterService {
}
/// Print using Network printer
Future<bool> printNetwork(String ipAddress, List<int> printData) async {
Future<bool> printNetwork(
PrintModel printer, List<int> printData, BuildContext context) async {
try {
final printer = PrinterNetworkManager(ipAddress);
PosPrintResult connect = await printer.connect();
final networkPrinter = PrinterNetworkManager(printer.address);
PosPrintResult connect = await networkPrinter.connect();
if (connect == PosPrintResult.success) {
PosPrintResult printing = await printer.printTicket(printData);
printer.disconnect();
PosPrintResult printing = await networkPrinter.printTicket(printData);
networkPrinter.disconnect();
if (printing == PosPrintResult.success) {
log("Successfully printed via Network printer: $ipAddress");
log("Successfully printed via Network printer: ${printer.address}");
return true;
} else {
FirebaseCrashlytics.instance.recordError(
@@ -117,28 +187,34 @@ class PrinterService {
null,
reason: 'Failed to print via Network printer',
information: [
'function: printNetwork(String ipAddress, List<int> printData)',
'Printer: Network printer',
'ipAddress: $ipAddress',
'printData: $printData',
'Printer: ${printer.name}',
'ipAddress: ${printer.address}',
],
);
log("Failed to print via Network printer: ${printing.msg}");
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Gagal print ke ${printer.name}')),
);
}
return false;
}
} else {
FirebaseCrashlytics.instance.recordError(
'Failed to connect to Network printer: ${connect.msg}',
null,
reason: 'Failed to connectNetwork printer',
reason: 'Failed to connect Network printer',
information: [
'function: printNetwork(String ipAddress, List<int> printData)',
'Printer: Network printer',
'ipAddress: $ipAddress',
'printData: $printData',
'Printer: ${printer.name}',
'ipAddress: ${printer.address}',
],
);
log("Failed to connect to Network printer: ${connect.msg}");
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Gagal connect ke ${printer.name}')),
);
}
return false;
}
} catch (e, stackTrace) {
@@ -147,10 +223,8 @@ class PrinterService {
stackTrace,
reason: 'Error printing via Network',
information: [
'function: printNetwork(String ipAddress, List<int> printData)',
'Printer: Network printer',
'ipAddress: $ipAddress',
'printData: $printData',
'Printer: ${printer.name}',
'ipAddress: ${printer.address}',
],
);
log("Error printing via Network: $e");
@@ -158,81 +232,6 @@ class PrinterService {
}
}
/// Print with automatic printer type detection
Future<bool> printWithPrinter(
PrintModel printer, List<int> printData, BuildContext context) async {
try {
if (printer.type == 'Bluetooth') {
bool connected = await connectBluetoothPrinter(printer.address);
if (!connected) {
FirebaseCrashlytics.instance.recordError(
'Failed to connect to Bluetooth printer',
null,
reason: 'Failed to connect to Bluetooth printe',
information: [
'function: connectBluetoothPrinter(String macAddress)',
'Printer: ${printer.name}',
'macAddress: ${printer.address}',
],
);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Failed to connect to ${printer.name}')),
);
return false;
}
bool printResult = await printBluetooth(printData);
if (!printResult) {
FirebaseCrashlytics.instance.recordError(
'Failed to print to ${printer.name}',
null,
information: [
'function: await printBluetooth(printData);',
'print: $printData',
],
);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Failed to print to ${printer.name}')),
);
}
return printResult;
} else {
bool printResult = await printNetwork(printer.address, printData);
if (!printResult) {
FirebaseCrashlytics.instance.recordError(
'Failed to connect to Network Printer',
null,
reason: 'Failed to connect to Network Printer',
information: [
'function: await printNetwork(printer.address, printData);',
'Printer: ${printer.name}',
'ipAddress: ${printer.address}',
'print: $printData',
],
);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Failed to print to ${printer.name}')),
);
}
return printResult;
}
} catch (e, stackTrace) {
FirebaseCrashlytics.instance.recordError(
e,
stackTrace,
reason: 'Error printing with printer ${printer.name}',
information: [
'Printer: ${printer.name}',
],
);
log("Error printing with printer ${printer.name}: $e");
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error printing to ${printer.name}: $e')),
);
return false;
}
}
/// Disconnect from Bluetooth printer
Future<bool> disconnectBluetooth() async {
try {
+532 -138
View File
@@ -882,6 +882,20 @@ class PrintDataoutputs {
styles: const PosStyles(bold: true, align: PosAlign.right),
),
]);
if (product.notes != '') {
bytes += generator.row([
PosColumn(
text: 'Note',
width: 4,
styles: const PosStyles(bold: false, align: PosAlign.left),
),
PosColumn(
text: product.notes,
width: 8,
styles: const PosStyles(bold: false, align: PosAlign.right),
),
]);
}
}
bytes += generator.text(
paper == 80
@@ -1398,141 +1412,7 @@ class PrintDataoutputs {
styles: const PosStyles(bold: false, align: PosAlign.center));
paper == 80 ? bytes += generator.feed(3) : bytes += generator.feed(1);
//cut
if (paper == 80) {
bytes += generator.cut();
}
return bytes;
}
Future<List<int>> printKitchen(
List<ProductQuantity> products,
String tableNumber,
String draftName,
String cashierName,
String customerName,
int paper,
String orderType) async {
List<int> bytes = [];
final profile = await CapabilityProfile.load();
final generator =
Generator(paper == 58 ? PaperSize.mm58 : PaperSize.mm80, profile);
bytes += generator.reset();
bytes += generator.text('Table Kitchen',
styles: const PosStyles(
bold: true,
align: PosAlign.center,
height: PosTextSize.size2,
width: PosTextSize.size2,
));
bytes += generator.feed(1);
if (tableNumber.isNotEmpty) {
bytes += generator.text(tableNumber,
styles: const PosStyles(
bold: true,
align: PosAlign.center,
height: PosTextSize.size2,
width: PosTextSize.size2,
));
bytes += generator.feed(1);
}
bytes += generator.row([
PosColumn(
text: 'Date',
width: 6,
styles: const PosStyles(align: PosAlign.left),
),
PosColumn(
text: DateFormat('dd-MM-yyyy HH:mm').format(DateTime.now()),
width: 6,
styles: const PosStyles(align: PosAlign.right),
),
]);
// bytes += generator.text(
// 'Date: ${DateFormat('dd-MM-yyyy HH:mm').format(DateTime.now())}',
// styles: const PosStyles(bold: false, align: PosAlign.left));
//reciept number
bytes += generator.row([
PosColumn(
text: 'Receipt',
width: 6,
styles: const PosStyles(align: PosAlign.left),
),
PosColumn(
text: DateFormat('yyyyMMddhhmm').format(DateTime.now()),
width: 6,
styles: const PosStyles(align: PosAlign.right),
),
]);
bytes += generator.row([
PosColumn(
text: 'Cashier',
width: 6,
styles: const PosStyles(align: PosAlign.left),
),
PosColumn(
text: cashierName,
width: 6,
styles: const PosStyles(align: PosAlign.right),
),
]);
bytes += generator.row([
PosColumn(
text: '$customerName - $draftName',
width: 6,
styles: const PosStyles(align: PosAlign.left),
),
PosColumn(
text: orderType,
width: 6,
styles: const PosStyles(align: PosAlign.right, bold: true),
),
]);
//----
bytes += generator.text(
paper == 80
? '------------------------------------------------'
: '--------------------------------',
styles: const PosStyles(bold: false, align: PosAlign.center));
bytes += generator.feed(1);
final kitchenProducts =
products.where((p) => p.product.printerType == 'kitchen');
for (final product in kitchenProducts) {
bytes += generator.text('${product.quantity} x ${product.product.name}',
styles: const PosStyles(
align: PosAlign.left,
bold: false,
height: PosTextSize.size2,
width: PosTextSize.size1,
));
if (product.notes.isNotEmpty) {
bytes += generator.text(' Notes: ${product.notes}',
styles: const PosStyles(
align: PosAlign.left,
bold: false,
height: PosTextSize.size1,
width: PosTextSize.size1,
fontType: PosFontType.fontA,
));
}
}
bytes += generator.feed(1);
bytes += generator.text(
paper == 80
? '------------------------------------------------'
: '--------------------------------',
styles: const PosStyles(bold: false, align: PosAlign.center));
paper == 80 ? bytes += generator.feed(3) : bytes += generator.feed(1);
//cut
if (paper == 80) {
bytes += generator.cut();
}
bytes += generator.cut();
return bytes;
}
@@ -1662,9 +1542,7 @@ class PrintDataoutputs {
styles: const PosStyles(bold: false, align: PosAlign.center));
paper == 80 ? bytes += generator.feed(3) : bytes += generator.feed(1);
//cut
if (paper == 80) {
bytes += generator.cut();
}
bytes += generator.cut();
return bytes;
}
@@ -1703,4 +1581,520 @@ class PrintDataoutputs {
return bytes;
}
Future<List<int>> printKitchenAllItem(
List<ProductQuantity> products,
String tableNumber,
String draftName,
String cashierName,
String customerName,
int paper,
String orderType) async {
List<int> bytes = [];
final profile = await CapabilityProfile.load();
final generator =
Generator(paper == 58 ? PaperSize.mm58 : PaperSize.mm80, profile);
bytes += generator.reset();
bytes += generator.text('Table Kitchen',
styles: const PosStyles(
bold: true,
align: PosAlign.center,
height: PosTextSize.size2,
width: PosTextSize.size2,
));
bytes += generator.feed(1);
if (tableNumber.isNotEmpty) {
bytes += generator.text(tableNumber,
styles: const PosStyles(
bold: true,
align: PosAlign.center,
height: PosTextSize.size2,
width: PosTextSize.size2,
));
bytes += generator.feed(1);
}
bytes += generator.row([
PosColumn(
text: 'Date',
width: 6,
styles: const PosStyles(align: PosAlign.left),
),
PosColumn(
text: DateFormat('dd-MM-yyyy HH:mm').format(DateTime.now()),
width: 6,
styles: const PosStyles(align: PosAlign.right),
),
]);
// bytes += generator.text(
// 'Date: ${DateFormat('dd-MM-yyyy HH:mm').format(DateTime.now())}',
// styles: const PosStyles(bold: false, align: PosAlign.left));
//reciept number
bytes += generator.row([
PosColumn(
text: 'Receipt',
width: 6,
styles: const PosStyles(align: PosAlign.left),
),
PosColumn(
text: DateFormat('yyyyMMddhhmm').format(DateTime.now()),
width: 6,
styles: const PosStyles(align: PosAlign.right),
),
]);
bytes += generator.row([
PosColumn(
text: 'Cashier',
width: 6,
styles: const PosStyles(align: PosAlign.left),
),
PosColumn(
text: cashierName,
width: 6,
styles: const PosStyles(align: PosAlign.right),
),
]);
bytes += generator.row([
PosColumn(
text: '$customerName - $draftName',
width: 6,
styles: const PosStyles(align: PosAlign.left),
),
PosColumn(
text: orderType,
width: 6,
styles: const PosStyles(align: PosAlign.right, bold: true),
),
]);
//----
bytes += generator.text(
paper == 80
? '------------------------------------------------'
: '--------------------------------',
styles: const PosStyles(bold: false, align: PosAlign.center));
bytes += generator.feed(1);
final kitchenProducts =
products.where((p) => p.product.printerType == 'kitchen');
for (final product in kitchenProducts) {
bytes += generator.text('${product.quantity} x ${product.product.name}',
styles: const PosStyles(
align: PosAlign.left,
bold: false,
height: PosTextSize.size2,
width: PosTextSize.size1,
));
if (product.notes.isNotEmpty) {
bytes += generator.text(' Notes: ${product.notes}',
styles: const PosStyles(
align: PosAlign.left,
bold: false,
height: PosTextSize.size1,
width: PosTextSize.size1,
fontType: PosFontType.fontA,
));
}
}
bytes += generator.feed(1);
bytes += generator.text(
paper == 80
? '------------------------------------------------'
: '--------------------------------',
styles: const PosStyles(bold: false, align: PosAlign.center));
paper == 80 ? bytes += generator.feed(3) : bytes += generator.feed(1);
bytes += generator.cut();
return bytes;
}
Future<List<int>> printKitchenPerProduct(
List<ProductQuantity> products,
String tableNumber,
String draftName,
String cashierName,
String customerName,
int paper,
String orderType) async {
List<int> allBytes = [];
final profile = await CapabilityProfile.load();
final generator =
Generator(paper == 58 ? PaperSize.mm58 : PaperSize.mm80, profile);
// Loop untuk setiap produk - print terpisah
for (final product in products) {
List<int> bytes = [];
bytes += generator.reset();
bytes += generator.text('Table Kitchen',
styles: const PosStyles(
bold: true,
align: PosAlign.center,
height: PosTextSize.size2,
width: PosTextSize.size2,
));
bytes += generator.feed(1);
if (tableNumber.isNotEmpty) {
bytes += generator.text(tableNumber,
styles: const PosStyles(
bold: true,
align: PosAlign.center,
height: PosTextSize.size2,
width: PosTextSize.size2,
));
bytes += generator.feed(1);
}
bytes += generator.row([
PosColumn(
text: DateFormat('dd MMM yyyy').format(DateTime.now()),
width: 6,
styles: const PosStyles(align: PosAlign.left),
),
PosColumn(
text: DateFormat('HH:mm').format(DateTime.now()),
width: 6,
styles: const PosStyles(align: PosAlign.right),
),
]);
bytes += generator.row([
PosColumn(
text: draftName,
width: 8,
styles: const PosStyles(align: PosAlign.left),
),
PosColumn(
text: '',
width: 4,
styles: const PosStyles(align: PosAlign.right),
),
]);
bytes += generator.row([
PosColumn(
text: customerName,
width: 6,
styles: const PosStyles(align: PosAlign.left),
),
PosColumn(
text: orderType,
width: 6,
styles: const PosStyles(align: PosAlign.right, bold: true),
),
]);
bytes += generator.text(
paper == 80
? '------------------------------------------------'
: '--------------------------------',
styles: const PosStyles(bold: false, align: PosAlign.center));
bytes += generator.feed(1);
// Print hanya 1 produk per struk
bytes += generator.text('${product.quantity} x ${product.product.name}',
styles: const PosStyles(
align: PosAlign.left,
bold: false,
height: PosTextSize.size2,
width: PosTextSize.size1,
));
if (product.notes.isNotEmpty) {
bytes += generator.text(' Notes: ${product.notes}',
styles: const PosStyles(
align: PosAlign.left,
bold: false,
height: PosTextSize.size1,
width: PosTextSize.size1,
fontType: PosFontType.fontA,
));
}
bytes += generator.feed(1);
bytes += generator.text(
paper == 80
? '------------------------------------------------'
: '--------------------------------',
styles: const PosStyles(bold: false, align: PosAlign.center));
paper == 80 ? bytes += generator.feed(3) : bytes += generator.feed(2);
bytes += generator.cut();
// Tambahkan ke allBytes
allBytes += bytes;
}
return allBytes;
}
Future<List<int>> printSplitBill(
Order order,
String chashierName,
int paper,
Outlet outlet,
) async {
List<int> bytes = [];
final profile = await CapabilityProfile.load();
final generator =
Generator(paper == 58 ? PaperSize.mm58 : PaperSize.mm80, profile);
bytes += generator.reset();
bytes += generator.text(outlet.name ?? "",
styles: const PosStyles(
bold: true,
align: PosAlign.center,
height: PosTextSize.size1,
width: PosTextSize.size1,
));
bytes += generator.text(outlet.address ?? "",
styles: const PosStyles(bold: false, align: PosAlign.center));
bytes += generator.text(outlet.phoneNumber ?? "",
styles: const PosStyles(bold: false, align: PosAlign.center));
bytes += generator.text(
paper == 80
? '------------------------------------------------'
: '--------------------------------',
styles: const PosStyles(bold: false, align: PosAlign.center));
bytes += generator.row([
PosColumn(
text: DateFormat('dd MMM yyyy').format(DateTime.now()),
width: 6,
styles: const PosStyles(align: PosAlign.left),
),
PosColumn(
text: DateFormat('HH:mm').format(DateTime.now()),
width: 6,
styles: const PosStyles(align: PosAlign.right),
),
]);
bytes += generator.row([
PosColumn(
text: 'Receipt Number',
width: 6,
styles: const PosStyles(align: PosAlign.left),
),
PosColumn(
text: DateFormat('yyyyMMddhhmm').format(DateTime.now()),
width: 6,
styles: const PosStyles(align: PosAlign.right),
),
]);
bytes += generator.row([
PosColumn(
text: 'Order ID',
width: 6,
styles: const PosStyles(align: PosAlign.left),
),
PosColumn(
text: Random().nextInt(100000).toString(),
width: 6,
styles: const PosStyles(align: PosAlign.right),
),
]);
bytes += generator.row([
PosColumn(
text: 'Bill Name',
width: 6,
styles: const PosStyles(align: PosAlign.left),
),
PosColumn(
text: order.metadata?['customer_name'] ?? '',
width: 6,
styles: const PosStyles(align: PosAlign.right),
),
]);
bytes += generator.row([
PosColumn(
text: 'Collected By',
width: 6,
styles: const PosStyles(align: PosAlign.left),
),
PosColumn(
text: chashierName,
width: 6,
styles: const PosStyles(align: PosAlign.right),
),
]);
if (order.payments != null) {
bytes += generator.row([
PosColumn(
text: 'Payment',
width: 8,
styles: const PosStyles(align: PosAlign.left),
),
PosColumn(
text: order.payments?.last.paymentMethodName ?? '-',
width: 4,
styles: const PosStyles(align: PosAlign.right),
),
]);
}
bytes += generator.text(
paper == 80
? '------------------------------------------------'
: '--------------------------------',
styles: const PosStyles(bold: false, align: PosAlign.center));
bytes += generator.text(order.orderType ?? '-',
styles: const PosStyles(bold: true, align: PosAlign.center));
bytes += generator.text(
paper == 80
? '------------------------------------------------'
: '--------------------------------',
styles: const PosStyles(bold: false, align: PosAlign.center));
bytes += generator.text("SPLIT",
styles: const PosStyles(bold: true, align: PosAlign.center));
bytes += generator.text(
paper == 80
? '------------------------------------------------'
: '--------------------------------',
styles: const PosStyles(bold: false, align: PosAlign.center));
for (final item in (order.orderItems ?? <OrderItem>[])) {
bytes += generator.row([
PosColumn(
text: '${item.quantity} x ${item.productName}',
width: 8,
styles: const PosStyles(bold: true, align: PosAlign.left),
),
PosColumn(
text: (((item.unitPrice ?? 0) * (item.quantity ?? 0)))
.currencyFormatRpV2,
width: 4,
styles: const PosStyles(bold: true, align: PosAlign.right),
),
]);
if (item.notes != '') {
bytes += generator.row([
PosColumn(
text: 'Note',
width: 4,
styles: const PosStyles(bold: false, align: PosAlign.left),
),
PosColumn(
text: item.notes ?? "-",
width: 8,
styles: const PosStyles(bold: false, align: PosAlign.right),
),
]);
}
}
if (order.orderItems?.isNotEmpty ?? false) {
bytes += generator.text(
paper == 80
? '------------------------------------------------'
: '--------------------------------',
styles: const PosStyles(bold: false, align: PosAlign.center));
}
bytes += generator.row([
PosColumn(
text: 'Subtotal',
width: 6,
styles: const PosStyles(align: PosAlign.left),
),
PosColumn(
text: order.payments?.last.amount?.currencyFormatRpV2 ?? '-',
width: 6,
styles: const PosStyles(align: PosAlign.right),
),
]);
bytes += generator.row([
PosColumn(
text: 'Discount',
width: 6,
styles: const PosStyles(align: PosAlign.left),
),
PosColumn(
text: (order.discountAmount ?? 0).currencyFormatRpV2,
width: 6,
styles: const PosStyles(align: PosAlign.right),
),
]);
// Only show tax if it's greater than 0
if ((order.taxAmount ?? 0) > 0) {
bytes += generator.row([
PosColumn(
text: 'Tax PB1 (${order.taxAmount}%)',
width: 6,
styles: const PosStyles(align: PosAlign.left),
),
PosColumn(
text: (order.taxAmount ?? 0).currencyFormatRpV2,
width: 6,
styles: const PosStyles(align: PosAlign.right),
),
]);
}
// Only show service charge if it's greater than 0
// if (serviceCharge > 0) {
// bytes += generator.row([
// PosColumn(
// text: 'Service Charge($serviceChargePercentage%)',
// width: 6,
// styles: const PosStyles(align: PosAlign.left),
// ),
// PosColumn(
// text: serviceCharge.currencyFormatRpV2,
// width: 6,
// styles: const PosStyles(align: PosAlign.right),
// ),
// ]);
// }
bytes += generator.text(
paper == 80
? '------------------------------------------------'
: '--------------------------------',
styles: const PosStyles(bold: false, align: PosAlign.center));
bytes += generator.row([
PosColumn(
text: 'Total',
width: 6,
styles: const PosStyles(bold: true, align: PosAlign.left),
),
PosColumn(
text: order.payments?.last.amount?.currencyFormatRpV2 ?? '-',
width: 6,
styles: const PosStyles(bold: true, align: PosAlign.right),
),
]);
bytes += generator.row([
PosColumn(
text: 'Dibayar',
width: 6,
styles: const PosStyles(align: PosAlign.left),
),
PosColumn(
text: order.payments?.last.amount?.currencyFormatRpV2 ?? '-',
width: 6,
styles: const PosStyles(align: PosAlign.right),
),
]);
bytes += generator.text(
paper == 80
? '------------------------------------------------'
: '--------------------------------',
styles: const PosStyles(bold: false, align: PosAlign.center));
paper == 80 ? bytes += generator.feed(3) : bytes += generator.feed(1);
bytes += generator.cut();
return bytes;
}
}
@@ -0,0 +1,373 @@
import 'dart:convert';
import 'dart:developer';
import 'package:enaklo_pos/core/database/database_handler.dart';
import 'package:enaklo_pos/data/models/response/category_response_model.dart';
import 'package:sqflite/sqflite.dart';
class CategoryLocalDatasource {
static CategoryLocalDatasource? _instance;
CategoryLocalDatasource._internal();
static CategoryLocalDatasource get instance {
_instance ??= CategoryLocalDatasource._internal();
return _instance!;
}
Future<Database> get _db async => await DatabaseHelper.instance.database;
// ========================================
// CACHING SYSTEM
// ========================================
final Map<String, List<CategoryModel>> _queryCache = {};
final Duration _cacheExpiry =
Duration(minutes: 10); // Lebih lama untuk categories
final Map<String, DateTime> _cacheTimestamps = {};
// ========================================
// BATCH SAVE CATEGORIES
// ========================================
Future<void> saveCategoriesBatch(List<CategoryModel> categories,
{bool clearFirst = false}) async {
final db = await _db;
try {
await db.transaction((txn) async {
if (clearFirst) {
log('🗑️ Clearing existing categories...');
await txn.delete('categories');
}
log('💾 Batch saving ${categories.length} categories...');
// Batch insert categories
final batch = txn.batch();
for (final category in categories) {
batch.insert(
'categories',
_categoryToMap(category),
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
await batch.commit(noResult: true);
});
// Clear cache after update
clearCache();
log('✅ Successfully batch saved ${categories.length} categories');
} catch (e) {
log('❌ Error batch saving categories: $e');
rethrow;
}
}
// ========================================
// CACHED QUERY
// ========================================
Future<List<CategoryModel>> getCachedCategories({
int page = 1,
int limit = 10,
bool isActive = true,
String? search,
}) async {
final cacheKey = _generateCacheKey(page, limit, isActive, search);
final now = DateTime.now();
// Check cache first
if (_queryCache.containsKey(cacheKey) &&
_cacheTimestamps.containsKey(cacheKey)) {
final cacheTime = _cacheTimestamps[cacheKey]!;
if (now.difference(cacheTime) < _cacheExpiry) {
log('🚀 Cache HIT: $cacheKey (${_queryCache[cacheKey]!.length} categories)');
return _queryCache[cacheKey]!;
}
}
log('📀 Cache MISS: $cacheKey, querying database...');
// Cache miss, query database
final categories = await getCategories(
page: page,
limit: limit,
isActive: isActive,
search: search,
);
// Store in cache
_queryCache[cacheKey] = categories;
_cacheTimestamps[cacheKey] = now;
log('💾 Cached ${categories.length} categories for key: $cacheKey');
return categories;
}
// ========================================
// REGULAR GET CATEGORIES
// ========================================
Future<List<CategoryModel>> getCategories({
int page = 1,
int limit = 10,
bool isActive = true,
String? search,
}) async {
final db = await _db;
try {
String query = 'SELECT * FROM categories WHERE 1=1';
List<dynamic> whereArgs = [];
// Note: Assuming is_active will be added to database schema
if (isActive) {
query += ' AND is_active = ?';
whereArgs.add(1);
}
if (search != null && search.isNotEmpty) {
query += ' AND (name LIKE ? OR description LIKE ?)';
whereArgs.add('%$search%');
whereArgs.add('%$search%');
}
// query += ' ORDER BY name ASC';
if (limit > 0) {
query += ' LIMIT ?';
whereArgs.add(limit);
if (page > 1) {
query += ' OFFSET ?';
whereArgs.add((page - 1) * limit);
}
}
final List<Map<String, dynamic>> maps =
await db.rawQuery(query, whereArgs);
List<CategoryModel> categories = [];
for (final map in maps) {
categories.add(_mapToCategory(map));
}
log('📊 Retrieved ${categories.length} categories from database');
return categories;
} catch (e) {
log('❌ Error getting categories: $e');
return [];
}
}
// ========================================
// GET ALL CATEGORIES (For dropdowns)
// ========================================
Future<List<CategoryModel>> getAllCategories() async {
const cacheKey = 'all_categories';
final now = DateTime.now();
// Check cache
if (_queryCache.containsKey(cacheKey) &&
_cacheTimestamps.containsKey(cacheKey)) {
final cacheTime = _cacheTimestamps[cacheKey]!;
if (now.difference(cacheTime) < _cacheExpiry) {
return _queryCache[cacheKey]!;
}
}
final db = await _db;
try {
final List<Map<String, dynamic>> maps = await db.query(
'categories',
orderBy: 'name ASC',
);
final categories = maps.map((map) => _mapToCategory(map)).toList();
// Cache all categories
_queryCache[cacheKey] = categories;
_cacheTimestamps[cacheKey] = now;
log('📊 Retrieved ${categories.length} total categories');
return categories;
} catch (e) {
log('❌ Error getting all categories: $e');
return [];
}
}
// ========================================
// GET CATEGORY BY ID
// ========================================
Future<CategoryModel?> getCategoryById(String id) async {
final db = await _db;
try {
final List<Map<String, dynamic>> maps = await db.query(
'categories',
where: 'id = ?',
whereArgs: [id],
);
if (maps.isEmpty) {
log('❌ Category not found: $id');
return null;
}
final category = _mapToCategory(maps.first);
log('✅ Category found: ${category.name}');
return category;
} catch (e) {
log('❌ Error getting category by ID: $e');
return null;
}
}
// ========================================
// GET TOTAL COUNT
// ========================================
Future<int> getTotalCount({bool isActive = true, String? search}) async {
final db = await _db;
try {
String query = 'SELECT COUNT(*) FROM categories WHERE 1=1';
List<dynamic> whereArgs = [];
if (isActive) {
query += ' AND is_active = ?';
whereArgs.add(1);
}
if (search != null && search.isNotEmpty) {
query += ' AND (name LIKE ? OR description LIKE ?)';
whereArgs.add('%$search%');
whereArgs.add('%$search%');
}
final result = await db.rawQuery(query, whereArgs);
final count = Sqflite.firstIntValue(result) ?? 0;
log('📊 Category total count: $count (isActive: $isActive, search: $search)');
return count;
} catch (e) {
log('❌ Error getting category total count: $e');
return 0;
}
}
// ========================================
// HAS CATEGORIES
// ========================================
Future<bool> hasCategories() async {
final count = await getTotalCount();
final hasData = count > 0;
log('🔍 Has categories: $hasData ($count categories)');
return hasData;
}
// ========================================
// CLEAR ALL CATEGORIES
// ========================================
Future<void> clearAllCategories() async {
final db = await _db;
try {
await db.delete('categories');
clearCache();
log('🗑️ All categories cleared from local DB');
} catch (e) {
log('❌ Error clearing categories: $e');
rethrow;
}
}
// ========================================
// CACHE MANAGEMENT
// ========================================
String _generateCacheKey(int page, int limit, bool isActive, String? search) {
return 'categories_${page}_${limit}_${isActive}_${search ?? 'null'}';
}
void clearCache() {
final count = _queryCache.length;
_queryCache.clear();
_cacheTimestamps.clear();
log('🧹 Category cache cleared: $count entries removed');
}
void clearExpiredCache() {
final now = DateTime.now();
final expiredKeys = <String>[];
_cacheTimestamps.forEach((key, timestamp) {
if (now.difference(timestamp) > _cacheExpiry) {
expiredKeys.add(key);
}
});
for (final key in expiredKeys) {
_queryCache.remove(key);
_cacheTimestamps.remove(key);
}
if (expiredKeys.isNotEmpty) {
log('⏰ Expired category cache cleared: ${expiredKeys.length} entries');
}
}
// ========================================
// DATABASE STATS
// ========================================
Future<Map<String, dynamic>> getDatabaseStats() async {
final db = await _db;
try {
final categoryCount = Sqflite.firstIntValue(
await db.rawQuery('SELECT COUNT(*) FROM categories')) ??
0;
final activeCount = Sqflite.firstIntValue(await db.rawQuery(
'SELECT COUNT(*) FROM categories WHERE is_active = 1')) ??
0;
final stats = {
'total_categories': categoryCount,
'active_categories': activeCount,
'cache_entries': _queryCache.length,
};
log('📊 Category Database Stats: $stats');
return stats;
} catch (e) {
log('❌ Error getting category database stats: $e');
return {};
}
}
// ========================================
// HELPER METHODS
// ========================================
Map<String, dynamic> _categoryToMap(CategoryModel category) {
return {
'id': category.id,
'organization_id': category.organizationId,
'name': category.name,
'description': category.description,
'business_type': category.businessType,
'metadata': json.encode(category.metadata),
'is_active': 1, // Assuming all synced categories are active
'created_at': category.createdAt.toIso8601String(),
'updated_at': category.updatedAt.toIso8601String(),
};
}
CategoryModel _mapToCategory(Map<String, dynamic> map) {
return CategoryModel(
id: map['id'],
organizationId: map['organization_id'],
name: map['name'],
description: map['description'],
businessType: map['business_type'],
metadata: map['metadata'] != null ? json.decode(map['metadata']) : {},
createdAt: DateTime.parse(map['created_at']),
updatedAt: DateTime.parse(map['updated_at']),
);
}
}
@@ -2,12 +2,12 @@ import 'dart:developer';
import 'package:dartz/dartz.dart';
import 'package:dio/dio.dart';
import 'package:enaklo_pos/core/network/dio_client.dart';
import 'package:enaklo_pos/data/datasources/outlet_local_datasource.dart';
import 'package:enaklo_pos/data/datasources/outlet/outlet_local_datasource.dart';
import 'package:enaklo_pos/data/datasources/settings_local_datasource.dart';
import 'package:enaklo_pos/presentation/home/models/outlet_model.dart';
import 'package:enaklo_pos/presentation/setting/models/tax_model.dart';
import '../../core/constants/variables.dart';
import 'auth_local_datasource.dart';
import '../../../core/constants/variables.dart';
import '../auth_local_datasource.dart';
class OutletRemoteDataSource {
final Dio dio = DioClient.instance;
@@ -0,0 +1,317 @@
import 'dart:developer';
import 'package:enaklo_pos/core/database/database_handler.dart';
import 'package:enaklo_pos/data/models/response/print_model.dart';
import 'package:sqflite/sqflite.dart';
class PrinterLocalDatasource {
static PrinterLocalDatasource? _instance;
PrinterLocalDatasource._internal();
static PrinterLocalDatasource get instance {
_instance ??= PrinterLocalDatasource._internal();
return _instance!;
}
Future<Database> get _db async => await DatabaseHelper.instance.database;
// Create new printer
Future<int> createPrinter(PrintModel printer) async {
final db = await _db;
try {
log('Creating printer: ${printer.toString()}');
final id = await db.insert(
'printers',
printer.toMapForInsert(),
conflictAlgorithm:
ConflictAlgorithm.abort, // Fail if code already exists
);
log('Successfully created printer with ID: $id');
return id;
} catch (e) {
log('Error creating printer: $e');
rethrow;
}
}
// Update existing printer
Future<void> updatePrinter(PrintModel printer, int id) async {
final db = await _db;
try {
log('Updating printer ID $id: ${printer.toString()}');
final updatedRows = await db.update(
'printers',
printer.toMapForUpdate(),
where: 'id = ?',
whereArgs: [id],
);
if (updatedRows == 0) {
throw Exception('Printer with ID $id not found');
}
log('Successfully updated printer ID: $id');
} catch (e) {
log('Error updating printer: $e');
rethrow;
}
}
// Delete printer by ID
Future<void> deletePrinter(int id) async {
final db = await _db;
try {
log('Deleting printer ID: $id');
final deletedRows = await db.delete(
'printers',
where: 'id = ?',
whereArgs: [id],
);
if (deletedRows == 0) {
throw Exception('Printer with ID $id not found');
}
log('Successfully deleted printer ID: $id');
} catch (e) {
log('Error deleting printer: $e');
rethrow;
}
}
// Get printer by code
Future<PrintModel?> getPrinterByCode(String code) async {
final db = await _db;
try {
log('Getting printer by code: $code');
final result = await db.query(
'printers',
where: 'code = ?',
whereArgs: [code],
);
if (result.isEmpty) {
log('Printer with code $code not found');
return null;
}
final printer = PrintModel.fromMap(result.first);
log('Found printer: ${printer.toString()}');
return printer;
} catch (e) {
log('Error getting printer by code: $e');
return null;
}
}
// Get printer by ID
Future<PrintModel?> getPrinterById(int id) async {
final db = await _db;
try {
log('Getting printer by ID: $id');
final result = await db.query(
'printers',
where: 'id = ?',
whereArgs: [id],
);
if (result.isEmpty) {
log('Printer with ID $id not found');
return null;
}
final printer = PrintModel.fromMap(result.first);
log('Found printer: ${printer.toString()}');
return printer;
} catch (e) {
log('Error getting printer by ID: $e');
return null;
}
}
// Get all printers
Future<List<PrintModel>> getAllPrinters() async {
final db = await _db;
try {
log('Getting all printers');
final result = await db.query(
'printers',
orderBy: 'name ASC',
);
final printers = result.map((map) => PrintModel.fromMap(map)).toList();
log('Found ${printers.length} printers');
return printers;
} catch (e) {
log('Error getting all printers: $e');
return [];
}
}
// Get printers by type
Future<List<PrintModel>> getPrintersByType(String type) async {
final db = await _db;
try {
log('Getting printers by type: $type');
final result = await db.query(
'printers',
where: 'type = ?',
whereArgs: [type],
orderBy: 'name ASC',
);
final printers = result.map((map) => PrintModel.fromMap(map)).toList();
log('Found ${printers.length} printers with type $type');
return printers;
} catch (e) {
log('Error getting printers by type: $e');
return [];
}
}
// Search printers by name
Future<List<PrintModel>> searchPrintersByName(String query) async {
final db = await _db;
try {
log('Searching printers by name: $query');
final result = await db.query(
'printers',
where: 'name LIKE ?',
whereArgs: ['%$query%'],
orderBy: 'name ASC',
);
final printers = result.map((map) => PrintModel.fromMap(map)).toList();
log('Found ${printers.length} printers matching "$query"');
return printers;
} catch (e) {
log('Error searching printers: $e');
return [];
}
}
// Check if printer code exists
Future<bool> isPrinterCodeExists(String code, {int? excludeId}) async {
final db = await _db;
try {
String whereClause = 'code = ?';
List<dynamic> whereArgs = [code];
if (excludeId != null) {
whereClause += ' AND id != ?';
whereArgs.add(excludeId);
}
final result = await db.query(
'printers',
where: whereClause,
whereArgs: whereArgs,
);
final exists = result.isNotEmpty;
log('Printer code "$code" exists: $exists');
return exists;
} catch (e) {
log('Error checking printer code existence: $e');
return false;
}
}
// Get printer statistics
Future<Map<String, dynamic>> getPrinterStats() async {
final db = await _db;
try {
// Total count
final totalResult =
await db.rawQuery('SELECT COUNT(*) as total FROM printers');
final totalCount = totalResult.first['total'] as int;
// Count by type
final typeResult = await db.rawQuery('''
SELECT type, COUNT(*) as count
FROM printers
WHERE type IS NOT NULL
GROUP BY type
''');
final typeStats = <String, int>{};
for (final row in typeResult) {
typeStats[row['type'] as String] = row['count'] as int;
}
final stats = {
'total_printers': totalCount,
'by_type': typeStats,
};
log('Printer stats: $stats');
return stats;
} catch (e) {
log('Error getting printer stats: $e');
return {
'total_printers': 0,
'by_type': <String, int>{},
};
}
}
// Clear all printers (for testing/reset purposes)
Future<void> clearAllPrinters() async {
final db = await _db;
try {
log('Clearing all printers');
await db.delete('printers');
log('All printers cleared');
} catch (e) {
log('Error clearing printers: $e');
rethrow;
}
}
// Batch insert printers
Future<void> insertPrinters(List<PrintModel> printers) async {
final db = await _db;
try {
log('Batch inserting ${printers.length} printers');
await db.transaction((txn) async {
for (final printer in printers) {
await txn.insert(
'printers',
printer.toMapForInsert(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
});
log('Successfully batch inserted ${printers.length} printers');
} catch (e) {
log('Error batch inserting printers: $e');
rethrow;
}
}
}
@@ -1,639 +0,0 @@
import 'dart:developer';
import 'dart:ui';
import 'package:enaklo_pos/data/models/response/print_model.dart';
import 'package:enaklo_pos/data/models/response/product_response_model.dart';
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/table/models/draft_order_item.dart';
import 'package:enaklo_pos/presentation/table/models/draft_order_model.dart';
import 'package:sqflite/sqflite.dart';
import '../../presentation/home/models/product_quantity.dart';
class ProductLocalDatasource {
ProductLocalDatasource._init();
static final ProductLocalDatasource instance = ProductLocalDatasource._init();
final String tableProduct = 'products';
final String tableOrder = 'orders';
final String tableOrderItem = 'order_items';
final String tableManagement = 'table_management';
final String tablePrint = 'prints';
static Database? _database;
// "id": 1,
// "category_id": 1,
// "name": "Mie Ayam",
// "description": "Ipsa dolorem impedit dolor. Libero nisi quidem expedita quod mollitia ad. Voluptas ut quia nemo nisi odit fuga. Fugit autem qui ratione laborum eum.",
// "image": "https://via.placeholder.com/640x480.png/002200?text=nihil",
// "price": "2000.44",
// "stock": 94,
// "status": 1,
// "is_favorite": 1,
// "created_at": "2024-02-08T14:30:22.000000Z",
// "updated_at": "2024-02-08T15:14:22.000000Z"
Future<void> _createDb(Database db, int version) async {
await db.execute('''
CREATE TABLE $tableProduct (
id INTEGER PRIMARY KEY,
product_id INTEGER,
name TEXT,
printer_type TEXT,
categoryId INTEGER,
categoryName TEXT,
description TEXT,
image TEXT,
price TEXT,
stock INTEGER,
status INTEGER,
isFavorite INTEGER,
createdAt TEXT,
updatedAt TEXT
)
''');
await db.execute('''
CREATE TABLE $tableOrder (
id INTEGER PRIMARY KEY AUTOINCREMENT,
payment_amount INTEGER,
sub_total INTEGER,
tax INTEGER,
discount INTEGER,
discount_amount INTEGER,
service_charge INTEGER,
total INTEGER,
payment_method TEXT,
total_item INTEGER,
id_kasir INTEGER,
nama_kasir TEXT,
transaction_time TEXT,
table_number INTEGER,
customer_name TEXT,
status TEXT,
payment_status TEXT,
order_type TEXT DEFAULT 'DINE IN',
is_sync INTEGER DEFAULT 0
)
''');
await db.execute('''
CREATE TABLE $tableOrderItem (
id INTEGER PRIMARY KEY AUTOINCREMENT,
id_order INTEGER,
id_product INTEGER,
quantity INTEGER,
price INTEGER,
notes TEXT DEFAULT ''
)
''');
await db.execute('''
CREATE TABLE $tableManagement (
id INTEGER PRIMARY KEY AUTOINCREMENT,
table_name Text,
start_time Text,
order_id INTEGER,
payment_amount INTEGER,
x_position REAL NOT NULL,
y_position REAL NOT NULL,
status TEXT
)
''');
await db.execute('''
CREATE TABLE draft_orders (
id INTEGER PRIMARY KEY AUTOINCREMENT,
total_item INTEGER,
subtotal INTEGER,
tax INTEGER,
discount INTEGER,
discount_amount INTEGER,
service_charge INTEGER,
total INTEGER,
transaction_time TEXT,
table_number INTEGER,
draft_name TEXT
)
''');
await db.execute('''
CREATE TABLE draft_order_items (
id INTEGER PRIMARY KEY AUTOINCREMENT,
id_draft_order INTEGER,
id_product INTEGER,
quantity INTEGER,
price INTEGER
)
''');
await db.execute('''
CREATE TABLE $tablePrint (
id INTEGER PRIMARY KEY AUTOINCREMENT,
code TEXT,
name TEXT,
address TEXT,
paper TEXT,
type TEXT
)
''');
}
Future<Database> _initDB(String filePath) async {
final dbPath = await getDatabasesPath();
final path = dbPath + filePath;
// Force delete existing database to ensure new schema
try {
final dbExists = await databaseExists(path);
if (dbExists) {
log("Deleting existing database to ensure new schema with order_type column");
// await deleteDatabase(path);
}
} catch (e) {
log("Error deleting database: $e");
}
return await openDatabase(
path,
version: 2,
onCreate: _createDb,
onUpgrade: _onUpgrade,
);
}
Future<void> _onUpgrade(Database db, int oldVersion, int newVersion) async {
if (oldVersion < 2) {
// Add order_type column to orders table if it doesn't exist
try {
await db.execute(
'ALTER TABLE $tableOrder ADD COLUMN order_type TEXT DEFAULT "DINE IN"');
log("Added order_type column to orders table");
} catch (e) {
log("order_type column might already exist: $e");
}
}
}
Future<Database> get database async {
if (_database != null) return _database!;
_database = await _initDB('dbresto36.db');
return _database!;
}
//save order
Future<int> saveOrder(OrderModel order) async {
final db = await instance.database;
// Since we're forcing database recreation, order_type column should exist
final orderMap = order.toMap(includeOrderType: true);
log("Final orderMap for insertion: $orderMap");
int id = await db.insert(tableOrder, orderMap,
conflictAlgorithm: ConflictAlgorithm.replace);
for (var item in order.orderItems) {
log("Item: ${item.toLocalMap(id)}");
await db.insert(tableOrderItem, item.toLocalMap(id),
conflictAlgorithm: ConflictAlgorithm.replace);
}
log("Success Order: ${order.toMap()}");
return id;
}
//get data order
Future<List<OrderModel>> getOrderByIsNotSync() async {
final db = await instance.database;
final List<Map<String, dynamic>> maps =
await db.query(tableOrder, where: 'is_sync = ?', whereArgs: [0]);
return List.generate(maps.length, (i) {
return OrderModel.fromMap(maps[i]);
});
}
Future<List<OrderModel>> getAllOrder(
DateTime date,
) async {
final db = await instance.database;
//date to iso8601
final dateIso = date.toIso8601String();
//get yyyy-MM-dd
final dateYYYYMMDD = dateIso.substring(0, 10);
// final formattedDate = DateFormat('yyyy-MM-dd').format(date);
final List<Map<String, dynamic>> maps = await db.query(
tableOrder,
where: 'transaction_time like ?',
whereArgs: ['$dateYYYYMMDD%'],
// where: 'transaction_time BETWEEN ? AND ?',
// whereArgs: [
// DateFormat.yMd().format(start),
// DateFormat.yMd().format(end)
// ],
);
return List.generate(maps.length, (i) {
log("Save save OrderModel: ${OrderModel.fromMap(maps[i])}");
return OrderModel.fromMap(maps[i]);
});
}
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
Future<List<ProductQuantity>> getOrderItemByOrderId(int orderId) async {
final db = await instance.database;
final List<Map<String, dynamic>> maps = await db
.query(tableOrderItem, where: 'id_order = ?', whereArgs: [orderId]);
return List.generate(maps.length, (i) {
log("ProductQuantity: ${ProductQuantity.fromLocalMap(maps[i])}");
return ProductQuantity.fromLocalMap(maps[i]);
});
}
//update payment status by order id
Future<void> updatePaymentStatus(
int orderId, String paymentStatus, String status) async {
final db = await instance.database;
await db.update(
tableOrder, {'payment_status': paymentStatus, 'status': status},
where: 'id = ?', whereArgs: [orderId]);
log('update payment status success | order id: $orderId | payment status: $paymentStatus | status: $status');
}
//update order is sync
Future<void> updateOrderIsSync(int orderId) async {
final db = await instance.database;
await db.update(tableOrder, {'is_sync': 1},
where: 'id = ?', whereArgs: [orderId]);
}
//insert data product
Future<void> insertProduct(Product product) async {
log("Product: ${product.toMap()}");
final db = await instance.database;
await db.insert(tableProduct, product.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace);
}
//update product
Future<void> updateProduct(Product product) async {
log("Update Product: ${product.toMap()}");
final db = await instance.database;
await db.update(
tableProduct,
product.toLocalMap(),
where: 'product_id = ?',
whereArgs: [product.id],
);
}
//insert list of product
Future<void> insertProducts(List<Product> products) async {
final db = await instance.database;
log("Save Products to Local");
for (var product in products) {
await db.insert(tableProduct, product.toLocalMap(),
conflictAlgorithm: ConflictAlgorithm.replace);
log('inserted success id: ${product.id} | name: ${product.name} | price: ${product.price} ');
}
}
//get all products
Future<List<Product>> getProducts() async {
final db = await instance.database;
final List<Map<String, dynamic>> maps = await db.query(tableProduct);
return List.generate(maps.length, (i) {
return Product.fromLocalMap(maps[i]);
});
}
Future<Product?> getProductById(int id) async {
final db = await instance.database;
final result =
await db.query(tableProduct, where: 'product_id = ?', whereArgs: [id]);
if (result.isEmpty) {
return null;
}
return Product.fromMap(result.first);
}
// get Last Table Management
Future<TableModel?> getLastTableManagement() async {
final db = await instance.database;
final List<Map<String, dynamic>> maps =
await db.query(tableManagement, orderBy: 'id DESC', limit: 1);
if (maps.isEmpty) {
return null;
}
return TableModel.fromMap(maps[0]);
}
// generate table managent with count
Future<void> createTableManagement(String tableName, Offset position) async {
// final db = await instance.database;
// TableModel newTable = TableModel(
// tableName: tableName,
// status: 'available',
// orderId: 0,
// paymentAmount: 0,
// startTime: DateTime.now().toIso8601String(),
// position: position,
// );
// await db.insert(
// tableManagement,
// newTable.toMap(),
// );
}
// change position table
Future<void> changePositionTable(int id, Offset position) async {
final db = await instance.database;
await db.update(
tableManagement,
{'x_position': position.dx, 'y_position': position.dy},
where: 'id = ?',
whereArgs: [id],
);
}
// update table
Future<void> updateTable(TableModel table) async {
final db = await instance.database;
await db.update(
tableManagement,
table.toMap(),
where: 'id = ?',
whereArgs: [table.id],
);
}
// get all table
Future<List<TableModel>> getAllTable() async {
final db = await instance.database;
final List<Map<String, dynamic>> maps = await db.query(tableManagement);
log("Table Management: $maps");
return List.generate(maps.length, (i) {
return TableModel.fromMap(maps[i]);
});
}
// get last order where table number
Future<OrderModel?> getLastOrderTable(int tableNumber) async {
final db = await instance.database;
final List<Map<String, dynamic>> maps = await db.query(
tableOrder,
where: 'table_number = ?',
whereArgs: [tableNumber],
orderBy: 'id DESC', // Urutkan berdasarkan id dari yang terbesar (terbaru)
limit: 1, // Ambil hanya satu data terakhir
);
if (maps.isEmpty) {
return null;
}
return OrderModel.fromMap(maps[0]);
}
// get table by status
Future<List<TableModel>> getTableByStatus(String status) async {
final db = await instance.database;
List<Map<String, dynamic>> maps;
if (status == 'all') {
// Get all tables
maps = await db.query(tableManagement);
log("Getting all tables, found: ${maps.length}");
// If no tables exist, create some default tables
if (maps.isEmpty) {
log("No tables found, creating default tables...");
await _createDefaultTables();
maps = await db.query(tableManagement);
log("After creating default tables, found: ${maps.length}");
}
} else {
// Get tables by specific status
maps = await db.query(
tableManagement,
where: 'status = ?',
whereArgs: [status],
);
log("Getting tables with status '$status', found: ${maps.length}");
}
final tables = List.generate(maps.length, (i) {
return TableModel.fromMap(maps[i]);
});
log("Returning ${tables.length} tables");
tables.forEach((table) {
log("Table: ${table.tableName} (ID: ${table.id}, Status: ${table.status})");
});
return tables;
}
// Create default tables if none exist
Future<void> _createDefaultTables() async {
final db = await instance.database;
// Create 5 default tables
for (int i = 1; i <= 5; i++) {
await db.insert(tableManagement, {
'table_name': 'Table $i',
'start_time': DateTime.now().toIso8601String(),
'order_id': 0,
'payment_amount': 0,
'x_position': 100.0 + (i * 50.0),
'y_position': 100.0 + (i * 50.0),
'status': 'available',
});
log("Created default table: Table $i");
}
}
// update status tabel
Future<void> updateStatusTable(TableModel table) async {
log("Updating table status: ${table.toMap()}");
final db = await instance.database;
await db.update(tableManagement, table.toMap(),
where: 'id = ?', whereArgs: [table.id]);
log("Success Update Status Table: ${table.toMap()}");
// Verify the update
final updatedTable = await db.query(
tableManagement,
where: 'id = ?',
whereArgs: [table.id],
);
if (updatedTable.isNotEmpty) {
log("Verified table update: ${updatedTable.first}");
}
}
// Debug method to reset all tables to available status
Future<void> resetAllTablesToAvailable() async {
log("Resetting all tables to available status...");
final db = await instance.database;
await db.update(
tableManagement,
{
'status': 'available',
'order_id': 0,
'payment_amount': 0,
'start_time': DateTime.now().toIso8601String(),
},
);
log("All tables reset to available status");
}
//delete all products
Future<void> deleteAllProducts() async {
final db = await instance.database;
await db.delete(tableProduct);
}
Future<int> saveDraftOrder(DraftOrderModel order) async {
log("save draft order: ${order.toMapForLocal()}");
final db = await instance.database;
int id = await db.insert('draft_orders', order.toMapForLocal());
log("draft order id: $id | ${order.discountAmount}");
for (var orderItem in order.orders) {
await db.insert('draft_order_items', orderItem.toMapForLocal(id));
log("draft order item ${orderItem.toMapForLocal(id)}");
}
return id;
}
//get all draft order
Future<List<DraftOrderModel>> getAllDraftOrder() async {
final db = await instance.database;
final result = await db.query('draft_orders', orderBy: 'id ASC');
List<DraftOrderModel> results = await Future.wait(result.map((item) async {
// Your asynchronous operation here
final draftOrderItem =
await getDraftOrderItemByOrderId(item['id'] as int);
return DraftOrderModel.newFromLocalMap(item, draftOrderItem);
}));
return results;
}
// get Darft Order by id
Future<DraftOrderModel?> getDraftOrderById(int id) async {
final db = await instance.database;
final result =
await db.query('draft_orders', where: 'id = ?', whereArgs: [id]);
if (result.isEmpty) {
return null;
}
final draftOrderItem =
await getDraftOrderItemByOrderId(result.first['id'] as int);
log("draft order item: $draftOrderItem | ${result.first.toString()}");
return DraftOrderModel.newFromLocalMap(result.first, draftOrderItem);
}
//get draft order item by id order
Future<List<DraftOrderItem>> getDraftOrderItemByOrderId(int idOrder) async {
final db = await instance.database;
final result =
await db.query('draft_order_items', where: 'id_draft_order = $idOrder');
List<DraftOrderItem> results = await Future.wait(result.map((item) async {
// Your asynchronous operation here
final product = await getProductById(item['id_product'] as int);
return DraftOrderItem(
product: product!, quantity: item['quantity'] as int);
}));
return results;
}
//remove draft order by id
Future<void> removeDraftOrderById(int id) async {
final db = await instance.database;
await db.delete('draft_orders', where: 'id = ?', whereArgs: [id]);
await db.delete('draft_order_items',
where: 'id_draft_order = ?', whereArgs: [id]);
}
//update draft order
Future<void> updateDraftOrder(DraftOrderModel draftOrder) async {
final db = await instance.database;
// Update the draft order
await db.update(
'draft_orders',
draftOrder.toMapForLocal(),
where: 'id = ?',
whereArgs: [draftOrder.id],
);
// Remove existing items and add new ones
await db.delete('draft_order_items',
where: 'id_draft_order = ?', whereArgs: [draftOrder.id]);
for (var orderItem in draftOrder.orders) {
await db.insert(
'draft_order_items', orderItem.toMapForLocal(draftOrder.id!));
}
}
/// create printer
Future<void> createPrinter(PrintModel print) async {
final db = await instance.database;
await db.insert(tablePrint, print.toMap());
}
Future<void> updatePrinter(PrintModel print, int id) async {
final db = await instance.database;
log("Update Printer: ${print.toMap()} | id: $id");
await db
.update(tablePrint, print.toMap(), where: 'id = ?', whereArgs: [id]);
}
Future<void> deletePrinter(int id) async {
final db = await instance.database;
await db.delete(tablePrint, where: 'id = ?', whereArgs: [id]);
}
// get printer by code
Future<PrintModel?> getPrinterByCode(String code) async {
final db = await instance.database;
final result =
await db.query(tablePrint, where: 'code = ?', whereArgs: [code]);
if (result.isEmpty) {
return null;
}
return PrintModel.fromMap(result.first);
}
}
@@ -2,9 +2,11 @@ import 'dart:convert';
class AuthResponseModel {
final String? token;
final String? refreshToken;
final User? user;
AuthResponseModel({
this.refreshToken,
this.token,
this.user,
});
@@ -17,11 +19,13 @@ class AuthResponseModel {
factory AuthResponseModel.fromMap(Map<String, dynamic> json) =>
AuthResponseModel(
token: json["token"],
refreshToken: json["refresh_token"],
user: json["user"] == null ? null : User.fromMap(json["user"]),
);
Map<String, dynamic> toMap() => {
"token": token,
"refresh_token": refreshToken,
"user": user?.toMap(),
};
}
@@ -117,4 +117,14 @@ class CategoryModel {
'updated_at': updatedAt.toIso8601String(),
};
}
factory CategoryModel.all() => CategoryModel(
id: 'all',
organizationId: '',
name: 'Semua',
businessType: 'restaurant',
metadata: {},
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
);
}
@@ -401,3 +401,103 @@ extension OrderItemListExtension on List<OrderItem> {
quantity: e.quantity ?? 0,
)).toList();
}
extension OrderCopyWith on Order {
Order copyWith({
String? id,
String? orderNumber,
String? outletId,
String? userId,
String? tableNumber,
String? orderType,
String? status,
int? subtotal,
int? taxAmount,
int? discountAmount,
int? totalAmount,
num? totalCost,
int? remainingAmount,
String? paymentStatus,
int? refundAmount,
bool? isVoid,
bool? isRefund,
String? notes,
Map<String, dynamic>? metadata,
DateTime? createdAt,
DateTime? updatedAt,
List<OrderItem>? orderItems,
List<Payment>? payments,
int? totalPaid,
int? paymentCount,
String? splitType,
}) {
return Order(
id: id ?? this.id,
orderNumber: orderNumber ?? this.orderNumber,
outletId: outletId ?? this.outletId,
userId: userId ?? this.userId,
tableNumber: tableNumber ?? this.tableNumber,
orderType: orderType ?? this.orderType,
status: status ?? this.status,
subtotal: subtotal ?? this.subtotal,
taxAmount: taxAmount ?? this.taxAmount,
discountAmount: discountAmount ?? this.discountAmount,
totalAmount: totalAmount ?? this.totalAmount,
totalCost: totalCost ?? this.totalCost,
remainingAmount: remainingAmount ?? this.remainingAmount,
paymentStatus: paymentStatus ?? this.paymentStatus,
refundAmount: refundAmount ?? this.refundAmount,
isVoid: isVoid ?? this.isVoid,
isRefund: isRefund ?? this.isRefund,
notes: notes ?? this.notes,
metadata: metadata ?? this.metadata,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
orderItems: orderItems ?? this.orderItems,
payments: payments ?? this.payments,
totalPaid: totalPaid ?? this.totalPaid,
paymentCount: paymentCount ?? this.paymentCount,
splitType: splitType ?? this.splitType,
);
}
}
extension OrderItemCopyWith on OrderItem {
OrderItem copyWith({
String? id,
String? orderId,
String? productId,
String? productName,
String? productVariantId,
String? productVariantName,
int? quantity,
int? unitPrice,
int? totalPrice,
List<dynamic>? modifiers,
String? notes,
String? status,
DateTime? createdAt,
DateTime? updatedAt,
String? printerType,
int? paidQuantity,
}) {
return OrderItem(
id: id ?? this.id,
orderId: orderId ?? this.orderId,
productId: productId ?? this.productId,
productName: productName ?? this.productName,
productVariantId: productVariantId ?? this.productVariantId,
productVariantName: productVariantName ?? this.productVariantName,
quantity: quantity ?? this.quantity,
unitPrice: unitPrice ?? this.unitPrice,
totalPrice: totalPrice ?? this.totalPrice,
modifiers: modifiers ?? this.modifiers,
notes: notes ?? this.notes,
status: status ?? this.status,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
printerType: printerType ?? this.printerType,
paidQuantity: paidQuantity ?? this.paidQuantity,
);
}
}
+121 -15
View File
@@ -5,6 +5,8 @@ class PrintModel {
final String address;
final String paper;
final String type;
final DateTime? createdAt;
final DateTime? updatedAt;
PrintModel({
this.id,
@@ -13,26 +15,130 @@ class PrintModel {
required this.address,
required this.paper,
required this.type,
this.createdAt,
this.updatedAt,
});
// from map
// Factory constructor from map (updated)
factory PrintModel.fromMap(Map<String, dynamic> map) {
return PrintModel(
id: map['id'],
code: map['code'],
name: map['name'],
address: map['address'],
paper: map['paper'],
type: map['type'],
id: map['id'] as int?,
code: map['code'] as String,
name: map['name'] as String,
address: map['address'] as String,
paper: map['paper'] as String,
type: map['type'] as String,
createdAt: map['created_at'] != null
? DateTime.tryParse(map['created_at'] as String)
: null,
updatedAt: map['updated_at'] != null
? DateTime.tryParse(map['updated_at'] as String)
: null,
);
}
// to map
Map<String, dynamic> toMap() => {
"code": code,
"name": name,
"address": address,
"paper": paper,
"type": type,
};
// Convert to map for database insertion (without id, with timestamps)
Map<String, dynamic> toMapForInsert() {
final now = DateTime.now().toIso8601String();
return {
'code': code,
'name': name,
'address': address,
'paper': paper,
'type': type,
'created_at': now,
'updated_at': now,
};
}
// Convert to map for database update (without id and created_at)
Map<String, dynamic> toMapForUpdate() {
return {
'code': code,
'name': name,
'address': address,
'paper': paper,
'type': type,
'updated_at': DateTime.now().toIso8601String(),
};
}
// Convert to complete map (original method, enhanced)
Map<String, dynamic> toMap() {
return {
if (id != null) 'id': id,
'code': code,
'name': name,
'address': address,
'paper': paper,
'type': type,
if (createdAt != null) 'created_at': createdAt!.toIso8601String(),
if (updatedAt != null) 'updated_at': updatedAt!.toIso8601String(),
};
}
// Copy with method for creating modified instances
PrintModel copyWith({
int? id,
String? code,
String? name,
String? address,
String? paper,
String? type,
DateTime? createdAt,
DateTime? updatedAt,
}) {
return PrintModel(
id: id ?? this.id,
code: code ?? this.code,
name: name ?? this.name,
address: address ?? this.address,
paper: paper ?? this.paper,
type: type ?? this.type,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
);
}
// Equality and hashCode
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is PrintModel &&
other.id == id &&
other.code == code &&
other.name == name &&
other.address == address &&
other.paper == paper &&
other.type == type;
}
@override
int get hashCode {
return Object.hash(id, code, name, address, paper, type);
}
// String representation for debugging (matches datasource logging)
@override
String toString() {
return 'PrintModel(id: $id, code: $code, name: $name, address: $address, paper: $paper, type: $type, createdAt: $createdAt, updatedAt: $updatedAt)';
}
// Validation methods
bool get isValid {
return code.isNotEmpty &&
name.isNotEmpty &&
address.isNotEmpty &&
paper.isNotEmpty &&
type.isNotEmpty;
}
String? get validationError {
if (code.isEmpty) return 'Printer code cannot be empty';
if (name.isEmpty) return 'Printer name cannot be empty';
if (address.isEmpty) return 'Printer address cannot be empty';
if (paper.isEmpty) return 'Paper size cannot be empty';
if (type.isEmpty) return 'Printer type cannot be empty';
return null;
}
}
@@ -0,0 +1,297 @@
import 'dart:developer';
import 'package:dartz/dartz.dart';
import 'package:enaklo_pos/data/datasources/category/category_local_datasource.dart';
import 'package:enaklo_pos/data/datasources/category/category_remote_datasource.dart';
import 'package:enaklo_pos/data/models/response/category_response_model.dart';
class CategoryRepository {
static CategoryRepository? _instance;
final CategoryLocalDatasource _localDatasource;
final CategoryRemoteDatasource _remoteDatasource;
CategoryRepository._internal()
: _localDatasource = CategoryLocalDatasource.instance,
_remoteDatasource = CategoryRemoteDatasource();
static CategoryRepository get instance {
_instance ??= CategoryRepository._internal();
return _instance!;
}
// ========================================
// SYNC STRATEGY: REMOTE-FIRST WITH LOCAL FALLBACK
// ========================================
Future<Either<String, CategoryResponseModel>> getCategories({
int page = 1,
int limit = 10,
bool isActive = true,
String? search,
bool forceRemote = false,
}) async {
try {
log('📱 Getting categories - page: $page, isActive: $isActive, search: $search, forceRemote: $forceRemote');
// Clean expired cache
_localDatasource.clearExpiredCache();
// Check if we should try remote first
if (forceRemote || !await _localDatasource.hasCategories()) {
log('🌐 Attempting remote fetch first...');
final remoteResult = await _getRemoteCategories(
page: page,
limit: limit,
isActive: isActive,
);
return await remoteResult.fold(
(failure) async {
log('❌ Remote fetch failed: $failure');
log('📱 Falling back to local data...');
return _getLocalCategories(
page: page,
limit: limit,
isActive: isActive,
search: search,
);
},
(response) async {
log('✅ Remote fetch successful, syncing to local...');
// Sync remote data to local
if (response.data.categories.isNotEmpty) {
await _syncToLocal(response.data.categories,
clearFirst: page == 1);
}
return Right(response);
},
);
} else {
log('📱 Using local data (cache available)...');
return _getLocalCategories(
page: page,
limit: limit,
isActive: isActive,
search: search,
);
}
} catch (e) {
log('❌ Error in getCategories: $e');
return Left('Gagal memuat kategori: $e');
}
}
// ========================================
// PURE LOCAL OPERATIONS
// ========================================
Future<Either<String, CategoryResponseModel>> _getLocalCategories({
int page = 1,
int limit = 10,
bool isActive = true,
String? search,
}) async {
try {
final cachedCategories = await _localDatasource.getCachedCategories(
page: page,
limit: limit,
isActive: isActive,
search: search,
);
final totalCount = await _localDatasource.getTotalCount(
isActive: isActive,
search: search,
);
final categoryData = CategoryData(
categories: cachedCategories,
totalCount: totalCount,
page: page,
limit: limit,
totalPages: totalCount > 0 ? (totalCount / limit).ceil() : 0,
);
final response = CategoryResponseModel(
success: true,
data: categoryData,
);
log('✅ Returned ${cachedCategories.length} local categories (${totalCount} total)');
return Right(response);
} catch (e) {
log('❌ Error getting local categories: $e');
return Left('Gagal memuat kategori dari database lokal: $e');
}
}
// ========================================
// REMOTE FETCH
// ========================================
Future<Either<String, CategoryResponseModel>> _getRemoteCategories({
int page = 1,
int limit = 10,
bool isActive = true,
}) async {
try {
log('🌐 Fetching categories from remote...');
return await _remoteDatasource.getCategories(
page: page,
limit: limit,
isActive: isActive,
);
} catch (e) {
log('❌ Remote fetch error: $e');
return Left('Gagal mengambil data dari server: $e');
}
}
// ========================================
// SYNC TO LOCAL
// ========================================
Future<void> _syncToLocal(List<CategoryModel> categories,
{bool clearFirst = false}) async {
try {
log('💾 Syncing ${categories.length} categories to local database...');
await _localDatasource.saveCategoriesBatch(categories,
clearFirst: clearFirst);
log('✅ Categories synced to local successfully');
} catch (e) {
log('❌ Error syncing categories to local: $e');
rethrow;
}
}
// ========================================
// MANUAL SYNC OPERATIONS
// ========================================
Future<Either<String, String>> syncAllCategories() async {
try {
log('🔄 Starting manual sync of all categories...');
int page = 1;
const limit = 50; // Higher limit for bulk sync
bool hasMore = true;
int totalSynced = 0;
// Clear local data first for fresh sync
await _localDatasource.clearAllCategories();
while (hasMore) {
log('📄 Syncing page $page...');
final result = await _remoteDatasource.getCategories(
page: page,
limit: limit,
isActive: true,
);
await result.fold(
(failure) async {
log('❌ Sync failed at page $page: $failure');
throw Exception(failure);
},
(response) async {
final categories = response.data.categories;
if (categories.isNotEmpty) {
await _localDatasource.saveCategoriesBatch(
categories,
clearFirst: false, // Don't clear on subsequent pages
);
totalSynced += categories.length;
// Check if we have more pages
hasMore = page < response.data.totalPages;
page++;
log('📦 Page $page synced: ${categories.length} categories');
} else {
hasMore = false;
}
},
);
}
final message = 'Berhasil sinkronisasi $totalSynced kategori';
log('$message');
return Right(message);
} catch (e) {
final error = 'Gagal sinkronisasi kategori: $e';
log('$error');
return Left(error);
}
}
// ========================================
// UTILITY METHODS
// ========================================
Future<Either<String, CategoryResponseModel>> refreshCategories({
bool isActive = true,
String? search,
}) async {
log('🔄 Refreshing categories...');
clearCache();
return await getCategories(
page: 1,
limit: 10,
isActive: isActive,
search: search,
forceRemote: true, // Force remote refresh
);
}
Future<CategoryModel?> getCategoryById(String id) async {
log('🔍 Getting category by ID: $id');
return await _localDatasource.getCategoryById(id);
}
Future<List<CategoryModel>> getAllCategories() async {
log('📋 Getting all categories for dropdown...');
return await _localDatasource.getAllCategories();
}
Future<bool> hasLocalCategories() async {
final hasCategories = await _localDatasource.hasCategories();
log('📊 Has local categories: $hasCategories');
return hasCategories;
}
Future<Map<String, dynamic>> getDatabaseStats() async {
final stats = await _localDatasource.getDatabaseStats();
log('📊 Category database stats: $stats');
return stats;
}
void clearCache() {
log('🧹 Clearing category cache');
_localDatasource.clearCache();
}
Future<bool> isLocalDatabaseReady() async {
try {
final stats = await getDatabaseStats();
final categoryCount = stats['total_categories'] ?? 0;
final isReady = categoryCount > 0;
log('🔍 Category database ready: $isReady ($categoryCount categories)');
return isReady;
} catch (e) {
log('❌ Error checking category database readiness: $e');
return false;
}
}
Future<void> clearAllCategories() async {
try {
log('🗑️ Clearing all categories from repository...');
await _localDatasource.clearAllCategories();
clearCache();
log('✅ All categories cleared successfully');
} catch (e) {
log('❌ Error clearing all categories: $e');
rethrow;
}
}
}
@@ -1,15 +1,18 @@
import 'dart:developer';
import 'package:dartz/dartz.dart';
import 'package:enaklo_pos/data/datasources/product/product_local_datasource.dart';
import 'package:enaklo_pos/data/datasources/product_remote_datasource.dart';
import 'package:enaklo_pos/data/models/response/product_response_model.dart';
class ProductRepository {
static ProductRepository? _instance;
final ProductLocalDatasource _localDatasource;
final ProductRemoteDatasource _remoteDatasource;
ProductRepository._internal()
: _localDatasource = ProductLocalDatasource.instance;
: _localDatasource = ProductLocalDatasource.instance,
_remoteDatasource = ProductRemoteDatasource();
static ProductRepository get instance {
_instance ??= ProductRepository._internal();
@@ -85,6 +88,66 @@ class ProductRepository {
}
}
// ========================================
// PRODUCT SYNC OPERATIONS
// ========================================
Future<Either<String, String>> syncAllProducts() async {
try {
log('🔄 Starting manual sync of all products...');
int page = 1;
const limit = 50; // Higher limit for bulk sync
bool hasMore = true;
int totalSynced = 0;
// Clear local data first for fresh sync
await _localDatasource.clearAllProducts();
while (hasMore) {
log('📄 Syncing page $page...');
final result = await _remoteDatasource.getProducts(
page: page,
limit: limit,
);
await result.fold(
(failure) async {
log('❌ Sync failed at page $page: $failure');
throw Exception(failure);
},
(response) async {
final products = response.data?.products ?? [];
if (products.isNotEmpty) {
await _localDatasource.saveProductsBatch(
products,
clearFirst: false, // Don't clear on subsequent pages
);
totalSynced += products.length;
// Check if we have more pages
hasMore = page < (response.data?.totalPages ?? 0);
page++;
log('📦 Page $page synced: ${products.length} products');
} else {
hasMore = false;
}
},
);
}
final message = 'Berhasil sinkronisasi $totalSynced produk';
log('$message');
return Right(message);
} catch (e) {
final error = 'Gagal sinkronisasi produk: $e';
log('$error');
return Left(error);
}
}
// ========================================
// LOCAL DATABASE OPERATIONS
// ========================================
@@ -142,4 +205,16 @@ class ProductRepository {
return false;
}
}
Future<void> clearAllProducts() async {
try {
log('🗑️ Clearing all products from repository...');
await _localDatasource.clearAllProducts();
clearCache();
log('✅ All products cleared successfully');
} catch (e) {
log('❌ Error clearing all products: $e');
rethrow;
}
}
}
+3 -26
View File
@@ -6,7 +6,7 @@ import 'package:enaklo_pos/core/network/dio_client.dart';
import 'package:enaklo_pos/data/datasources/analytic_remote_datasource.dart';
import 'package:enaklo_pos/data/datasources/customer_remote_datasource.dart';
import 'package:enaklo_pos/data/datasources/file_remote_datasource.dart';
import 'package:enaklo_pos/data/datasources/outlet_remote_data_source.dart';
import 'package:enaklo_pos/data/datasources/outlet/outlet_remote_data_source.dart';
import 'package:enaklo_pos/data/datasources/table_remote_datasource.dart';
import 'package:enaklo_pos/data/datasources/user_remote_datasource.dart';
import 'package:enaklo_pos/presentation/customer/bloc/customer_form/customer_form_bloc.dart';
@@ -34,11 +34,10 @@ import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:flutter/material.dart';
import 'package:enaklo_pos/data/datasources/auth_local_datasource.dart';
import 'package:enaklo_pos/data/datasources/auth_remote_datasource.dart';
import 'package:enaklo_pos/data/datasources/category_remote_datasource.dart';
import 'package:enaklo_pos/data/datasources/category/category_remote_datasource.dart';
import 'package:enaklo_pos/data/datasources/discount_remote_datasource.dart';
import 'package:enaklo_pos/data/datasources/midtrans_remote_datasource.dart';
import 'package:enaklo_pos/data/datasources/order_remote_datasource.dart';
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
import 'package:enaklo_pos/data/datasources/product_remote_datasource.dart';
import 'package:enaklo_pos/data/datasources/payment_methods_remote_datasource.dart';
import 'package:enaklo_pos/data/datasources/settings_local_datasource.dart';
@@ -48,14 +47,11 @@ import 'package:enaklo_pos/presentation/home/bloc/get_table_status/get_table_sta
import 'package:enaklo_pos/presentation/home/bloc/online_checker/online_checker_bloc.dart';
import 'package:enaklo_pos/presentation/home/bloc/payment_methods/payment_methods_bloc.dart';
import 'package:enaklo_pos/presentation/home/bloc/qris/qris_bloc.dart';
import 'package:enaklo_pos/presentation/home/bloc/status_table/status_table_bloc.dart';
import 'package:enaklo_pos/presentation/home/bloc/checkout/checkout_bloc.dart';
import 'package:enaklo_pos/presentation/report/blocs/item_sales_report/item_sales_report_bloc.dart';
import 'package:enaklo_pos/presentation/report/blocs/payment_method_report/payment_method_report_bloc.dart';
import 'package:enaklo_pos/presentation/report/blocs/product_sales/product_sales_bloc.dart';
import 'package:enaklo_pos/presentation/report/blocs/summary/summary_bloc.dart';
import 'package:enaklo_pos/presentation/sales/blocs/bloc/last_order_table_bloc.dart';
import 'package:enaklo_pos/presentation/sales/blocs/day_sales/day_sales_bloc.dart';
import 'package:enaklo_pos/presentation/setting/bloc/add_product/add_product_bloc.dart';
import 'package:enaklo_pos/presentation/setting/bloc/create_printer/create_printer_bloc.dart';
import 'package:enaklo_pos/presentation/setting/bloc/get_categories/get_categories_bloc.dart';
@@ -69,12 +65,10 @@ import 'package:enaklo_pos/presentation/setting/bloc/update_printer/update_print
import 'package:enaklo_pos/presentation/table/blocs/change_position_table/change_position_table_bloc.dart';
import 'package:enaklo_pos/presentation/table/blocs/create_table/create_table_bloc.dart';
import 'package:enaklo_pos/presentation/table/blocs/get_table/get_table_bloc.dart';
import 'package:enaklo_pos/presentation/home/bloc/local_product/local_product_bloc.dart';
import 'package:enaklo_pos/presentation/home/bloc/order/order_bloc.dart';
import 'package:enaklo_pos/presentation/report/blocs/transaction_report/transaction_report_bloc.dart';
import 'package:enaklo_pos/presentation/setting/bloc/add_discount/add_discount_bloc.dart';
import 'package:enaklo_pos/presentation/setting/bloc/discount/discount_bloc.dart';
import 'package:enaklo_pos/presentation/setting/bloc/sync_order/sync_order_bloc.dart';
import 'package:enaklo_pos/presentation/setting/bloc/sync_product/sync_product_bloc.dart';
import 'package:enaklo_pos/presentation/setting/bloc/tax_settings/tax_settings_bloc.dart';
import 'package:enaklo_pos/presentation/table/blocs/update_table/update_table_bloc.dart';
@@ -148,10 +142,6 @@ class _MyAppState extends State<MyApp> {
BlocProvider(
create: (context) => SyncProductBloc(ProductRemoteDatasource()),
),
BlocProvider(
create: (context) =>
LocalProductBloc(ProductLocalDatasource.instance),
),
BlocProvider(
create: (context) =>
CheckoutBloc(settingsLocalDatasource: SettingsLocalDatasource()),
@@ -165,9 +155,6 @@ class _MyAppState extends State<MyApp> {
return OrderBloc(OrderRemoteDatasource());
},
),
BlocProvider(
create: (context) => SyncOrderBloc(OrderRemoteDatasource()),
),
BlocProvider(
create: (context) => DiscountBloc(DiscountRemoteDatasource()),
),
@@ -189,13 +176,6 @@ class _MyAppState extends State<MyApp> {
BlocProvider(
create: (context) => UpdateTableBloc(),
),
BlocProvider(
create: (context) => StatusTableBloc(ProductLocalDatasource.instance),
),
BlocProvider(
create: (context) =>
LastOrderTableBloc(ProductLocalDatasource.instance),
),
BlocProvider(
create: (context) => GetTableStatusBloc(TableRemoteDataSource()),
),
@@ -227,9 +207,6 @@ class _MyAppState extends State<MyApp> {
create: (context) =>
PaymentMethodReportBloc(AnalyticRemoteDatasource()),
),
BlocProvider(
create: (context) => DaySalesBloc(ProductLocalDatasource.instance),
),
BlocProvider(
create: (context) => QrisBloc(MidtransRemoteDatasource()),
),
@@ -298,7 +275,7 @@ class _MyAppState extends State<MyApp> {
create: (context) => UploadFileBloc(FileRemoteDataSource()),
),
BlocProvider(
create: (context) => CategoryLoaderBloc(CategoryRemoteDatasource()),
create: (context) => CategoryLoaderBloc(),
),
BlocProvider(
create: (context) => GetPrinterTicketBloc(),
@@ -2,6 +2,8 @@ import 'dart:async';
import 'dart:developer';
import 'package:bloc/bloc.dart';
import 'package:enaklo_pos/data/datasources/product/product_local_datasource.dart';
import 'package:enaklo_pos/data/datasources/category/category_local_datasource.dart';
import 'package:enaklo_pos/data/repositories/category/category_repository.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import '../../../data/datasources/product_remote_datasource.dart';
@@ -9,7 +11,7 @@ part 'data_sync_event.dart';
part 'data_sync_state.dart';
part 'data_sync_bloc.freezed.dart';
enum SyncStep { products, categories, variants, completed }
enum SyncStep { categories, products, variants, completed }
class SyncStats {
final int totalProducts;
@@ -26,9 +28,13 @@ class SyncStats {
}
class DataSyncBloc extends Bloc<DataSyncEvent, DataSyncState> {
final ProductRemoteDatasource _remoteDatasource = ProductRemoteDatasource();
final ProductLocalDatasource _localDatasource =
final ProductRemoteDatasource _productRemoteDatasource =
ProductRemoteDatasource();
final ProductLocalDatasource _productLocalDatasource =
ProductLocalDatasource.instance;
final CategoryLocalDatasource _categoryLocalDatasource =
CategoryLocalDatasource.instance;
final CategoryRepository _categoryRepository = CategoryRepository.instance;
Timer? _progressTimer;
bool _isCancelled = false;
@@ -48,36 +54,75 @@ class DataSyncBloc extends Bloc<DataSyncEvent, DataSyncState> {
_StartSync event,
Emitter<DataSyncState> emit,
) async {
log('🔄 Starting data sync...');
log('🔄 Starting full data sync (categories + products)...');
_isCancelled = false;
try {
// Step 1: Clear existing local data
emit(const DataSyncState.syncing(
SyncStep.products, 0.1, 'Membersihkan data lama...'));
await _localDatasource.clearAllProducts();
SyncStep.categories, 0.05, 'Membersihkan data lama...'));
await _productLocalDatasource.clearAllProducts();
await _categoryLocalDatasource.clearAllCategories();
if (_isCancelled) return;
// Step 2: Sync products
// Step 2: Sync categories first (products depend on categories)
await _syncCategories(emit);
if (_isCancelled) return;
// Step 3: Sync products
await _syncProducts(emit);
if (_isCancelled) return;
// Step 3: Generate final stats
// Step 4: Generate final stats
emit(const DataSyncState.syncing(
SyncStep.completed, 0.9, 'Menyelesaikan sinkronisasi...'));
SyncStep.completed, 0.95, 'Menyelesaikan sinkronisasi...'));
final stats = await _generateSyncStats();
emit(DataSyncState.completed(stats));
log('Sync completed successfully');
log('Full sync completed successfully');
} catch (e) {
log('❌ Sync failed: $e');
emit(DataSyncState.error('Gagal sinkronisasi: $e'));
}
}
Future<void> _syncCategories(Emitter<DataSyncState> emit) async {
log('📁 Syncing categories...');
emit(const DataSyncState.syncing(
SyncStep.categories,
0.1,
'Mengunduh kategori...',
));
try {
// Use CategoryRepository sync method
final result = await _categoryRepository.syncAllCategories();
await result.fold(
(failure) async {
throw Exception('Gagal sync kategori: $failure');
},
(successMessage) async {
log('✅ Categories sync completed: $successMessage');
emit(const DataSyncState.syncing(
SyncStep.categories,
0.2,
'Kategori berhasil diunduh',
));
},
);
} catch (e) {
log('❌ Category sync failed: $e');
throw Exception('Gagal sync kategori: $e');
}
}
Future<void> _syncProducts(Emitter<DataSyncState> emit) async {
log('📦 Syncing products...');
@@ -88,10 +133,10 @@ class DataSyncBloc extends Bloc<DataSyncEvent, DataSyncState> {
bool shouldContinue = true;
while (!_isCancelled && shouldContinue) {
// Calculate accurate progress based on total count
// Calculate accurate progress (categories = 0.2, products = 0.2-0.9)
double progress = 0.2;
if (totalCount != null && (totalCount ?? 0) > 0) {
progress = 0.2 + (totalSynced / (totalCount ?? 0)) * 0.6;
progress = 0.2 + (totalSynced / (totalCount ?? 0)) * 0.7;
}
emit(DataSyncState.syncing(
@@ -102,7 +147,7 @@ class DataSyncBloc extends Bloc<DataSyncEvent, DataSyncState> {
: 'Mengunduh produk... ($totalSynced produk)',
));
final result = await _remoteDatasource.getProducts(
final result = await _productRemoteDatasource.getProducts(
page: page,
limit: 50, // Bigger batch for sync
);
@@ -128,7 +173,7 @@ class DataSyncBloc extends Bloc<DataSyncEvent, DataSyncState> {
}
// Save to local database in batches
await _localDatasource.saveProductsBatch(products);
await _productLocalDatasource.saveProductsBatch(products);
totalSynced += products.length;
page++;
@@ -154,8 +199,8 @@ class DataSyncBloc extends Bloc<DataSyncEvent, DataSyncState> {
}
emit(DataSyncState.syncing(
SyncStep.completed,
0.8,
SyncStep.products,
0.9,
'Produk berhasil diunduh ($totalSynced dari ${totalCount ?? totalSynced})',
));
@@ -163,13 +208,15 @@ class DataSyncBloc extends Bloc<DataSyncEvent, DataSyncState> {
}
Future<SyncStats> _generateSyncStats() async {
final dbStats = await _localDatasource.getDatabaseStats();
final productStats = await _productLocalDatasource.getDatabaseStats();
final categoryStats = await _categoryLocalDatasource.getDatabaseStats();
return SyncStats(
totalProducts: dbStats['total_products'] ?? 0,
totalCategories: dbStats['total_categories'] ?? 0,
totalVariants: dbStats['total_variants'] ?? 0,
databaseSizeMB: dbStats['database_size_mb'] ?? 0.0,
totalProducts: productStats['total_products'] ?? 0,
totalCategories: categoryStats['total_categories'] ?? 0,
totalVariants: productStats['total_variants'] ?? 0,
databaseSizeMB: (productStats['database_size_mb'] ?? 0.0) +
(categoryStats['database_size_mb'] ?? 0.0),
);
}
@@ -87,20 +87,15 @@ class _DataSyncPageState extends State<DataSyncPage>
);
}
// Portrait layout (original)
// Portrait layout
Widget _buildPortraitLayout(DataSyncState state, double screenHeight) {
return Padding(
padding: EdgeInsets.all(24),
child: Column(
children: [
SizedBox(height: screenHeight * 0.08), // Responsive spacing
// Header
_buildHeader(false),
SizedBox(height: screenHeight * 0.08),
// Sync progress
_buildHeader(false),
SizedBox(height: screenHeight * 0.08),
Expanded(
child: state.when(
initial: () => _buildInitialState(false),
@@ -110,24 +105,20 @@ class _DataSyncPageState extends State<DataSyncPage>
error: (message) => _buildErrorState(message, false),
),
),
SizedBox(height: 20),
// Actions
_buildActions(state),
],
),
);
}
// Landscape layout (side by side)
// Landscape layout
Widget _buildLandscapeLayout(
DataSyncState state, double screenWidth, double screenHeight) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 32, vertical: 16),
child: Row(
children: [
// Left side - Header and info
Expanded(
flex: 2,
child: Column(
@@ -139,10 +130,7 @@ class _DataSyncPageState extends State<DataSyncPage>
],
),
),
SizedBox(width: 40),
// Right side - Sync progress
Expanded(
flex: 3,
child: Container(
@@ -188,7 +176,7 @@ class _DataSyncPageState extends State<DataSyncPage>
),
SizedBox(height: isLandscape ? 4 : 8),
Text(
'Mengunduh data terbaru ke perangkat',
'Mengunduh kategori dan produk terbaru',
style: TextStyle(
fontSize: isLandscape ? 14 : 16,
color: Colors.grey.shade600,
@@ -319,8 +307,8 @@ class _DataSyncPageState extends State<DataSyncPage>
Widget _buildStepIndicator(SyncStep currentStep, bool isLandscape) {
final steps = [
('Produk', SyncStep.products, Icons.inventory_2),
('Kategori', SyncStep.categories, Icons.category),
('Produk', SyncStep.products, Icons.inventory_2),
('Variant', SyncStep.variants, Icons.tune),
('Selesai', SyncStep.completed, Icons.check_circle),
];
@@ -551,11 +539,11 @@ class _DataSyncPageState extends State<DataSyncPage>
// Vertical layout for landscape
Column(
children: [
_buildStatItem('Produk', '${stats.totalProducts}',
Icons.inventory_2, Colors.blue, isLandscape),
SizedBox(height: 8),
_buildStatItem('Kategori', '${stats.totalCategories}',
Icons.category, Colors.green, isLandscape),
Icons.category, Colors.blue, isLandscape),
SizedBox(height: 8),
_buildStatItem('Produk', '${stats.totalProducts}',
Icons.inventory_2, Colors.green, isLandscape),
SizedBox(height: 8),
_buildStatItem('Variant', '${stats.totalVariants}',
Icons.tune, Colors.orange, isLandscape),
@@ -566,10 +554,10 @@ class _DataSyncPageState extends State<DataSyncPage>
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildStatItem('Produk', '${stats.totalProducts}',
Icons.inventory_2, Colors.blue, isLandscape),
_buildStatItem('Kategori', '${stats.totalCategories}',
Icons.category, Colors.green, isLandscape),
Icons.category, Colors.blue, isLandscape),
_buildStatItem('Produk', '${stats.totalProducts}',
Icons.inventory_2, Colors.green, isLandscape),
_buildStatItem('Variant', '${stats.totalVariants}',
Icons.tune, Colors.orange, isLandscape),
],
@@ -765,10 +753,10 @@ class _DataSyncPageState extends State<DataSyncPage>
IconData _getSyncIcon(SyncStep step) {
switch (step) {
case SyncStep.products:
return Icons.inventory_2;
case SyncStep.categories:
return Icons.category;
case SyncStep.products:
return Icons.inventory_2;
case SyncStep.variants:
return Icons.tune;
case SyncStep.completed:
@@ -778,10 +766,10 @@ class _DataSyncPageState extends State<DataSyncPage>
String _getStepLabel(SyncStep step) {
switch (step) {
case SyncStep.products:
return 'Mengunduh Produk';
case SyncStep.categories:
return 'Mengunduh Kategori';
case SyncStep.products:
return 'Mengunduh Produk';
case SyncStep.variants:
return 'Mengunduh Variant';
case SyncStep.completed:
@@ -1,6 +1,8 @@
import 'package:bloc/bloc.dart';
import 'package:enaklo_pos/data/datasources/category_remote_datasource.dart';
import 'dart:async';
import 'dart:developer';
import 'package:enaklo_pos/data/models/response/category_response_model.dart';
import 'package:enaklo_pos/data/repositories/category/category_repository.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'category_loader_event.dart';
@@ -9,35 +11,314 @@ part 'category_loader_bloc.freezed.dart';
class CategoryLoaderBloc
extends Bloc<CategoryLoaderEvent, CategoryLoaderState> {
final CategoryRemoteDatasource _datasource;
CategoryLoaderBloc(this._datasource) : super(CategoryLoaderState.initial()) {
on<_Get>((event, emit) async {
emit(const _Loading());
final result = await _datasource.getCategories(limit: 50);
result.fold(
(l) => emit(_Error(l)),
(r) async {
List<CategoryModel> categories = r.data.categories;
categories.insert(
0,
CategoryModel(
id: "",
name: 'Semua',
organizationId: '',
businessType: '',
metadata: {},
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
),
);
emit(_Loaded(categories, null));
final CategoryRepository _categoryRepository = CategoryRepository.instance;
Timer? _searchDebounce;
bool _isLoadingMore = false;
CategoryLoaderBloc() : super(const CategoryLoaderState.initial()) {
on<_GetCategories>(_onGetCategories);
on<_LoadMore>(_onLoadMore);
on<_Refresh>(_onRefresh);
on<_Search>(_onSearch);
on<_SyncAll>(_onSyncAll);
on<_GetAllCategories>(_onGetAllCategories);
on<_ClearCache>(_onClearCache);
on<_GetDatabaseStats>(_onGetDatabaseStats);
}
@override
Future<void> close() {
_searchDebounce?.cancel();
return super.close();
}
// ========================================
// GET CATEGORIES (Remote-first with local fallback)
// ========================================
Future<void> _onGetCategories(
_GetCategories event,
Emitter<CategoryLoaderState> emit,
) async {
emit(const CategoryLoaderState.loading());
_isLoadingMore = false;
log('📱 Loading categories - isActive: ${event.isActive}, forceRemote: ${event.forceRemote}');
final result = await _categoryRepository.getCategories(
page: 1,
limit: 50,
isActive: event.isActive,
search: event.search,
forceRemote: event.forceRemote,
);
await result.fold(
(failure) async {
log('❌ Error loading categories: $failure');
emit(CategoryLoaderState.error(failure));
},
(response) async {
final categories = [
CategoryModel.all(),
...response.data.categories,
];
final totalPages = response.data.totalPages;
final hasReachedMax = categories.length < 50 || 1 >= totalPages;
log('✅ Categories loaded: ${categories.length}, hasReachedMax: $hasReachedMax');
emit(CategoryLoaderState.loaded(
categories: categories,
hasReachedMax: hasReachedMax,
currentPage: 1,
isLoadingMore: false,
isActive: event.isActive,
searchQuery: event.search,
));
},
);
}
// ========================================
// LOAD MORE CATEGORIES
// ========================================
Future<void> _onLoadMore(
_LoadMore event,
Emitter<CategoryLoaderState> emit,
) async {
final currentState = state;
if (currentState is! _Loaded ||
currentState.hasReachedMax ||
_isLoadingMore ||
currentState.isLoadingMore) {
log('⏹️ Load more blocked - state: ${currentState.runtimeType}, isLoadingMore: $_isLoadingMore');
return;
}
_isLoadingMore = true;
emit(currentState.copyWith(isLoadingMore: true));
final nextPage = currentState.currentPage + 1;
log('📄 Loading more categories - page: $nextPage');
try {
final result = await _categoryRepository.getCategories(
page: nextPage,
limit: 10,
isActive: currentState.isActive,
search: currentState.searchQuery,
);
await result.fold(
(failure) async {
log('❌ Error loading more categories: $failure');
emit(currentState.copyWith(isLoadingMore: false));
},
(response) async {
final newCategories = response.data.categories;
final totalPages = response.data.totalPages;
// Prevent duplicate categories
final currentCategoryIds =
currentState.categories.map((c) => c.id).toSet();
final filteredNewCategories = newCategories
.where((category) => !currentCategoryIds.contains(category.id))
.toList();
final allCategories =
List<CategoryModel>.from(currentState.categories)
..addAll(filteredNewCategories);
final hasReachedMax =
newCategories.length < 10 || nextPage >= totalPages;
log('✅ More categories loaded: ${filteredNewCategories.length} new, total: ${allCategories.length}');
emit(CategoryLoaderState.loaded(
categories: allCategories,
hasReachedMax: hasReachedMax,
currentPage: nextPage,
isLoadingMore: false,
isActive: currentState.isActive,
searchQuery: currentState.searchQuery,
));
},
);
} catch (e) {
log('❌ Exception loading more categories: $e');
emit(currentState.copyWith(isLoadingMore: false));
} finally {
_isLoadingMore = false;
}
}
// ========================================
// REFRESH CATEGORIES
// ========================================
Future<void> _onRefresh(
_Refresh event,
Emitter<CategoryLoaderState> emit,
) async {
final currentState = state;
bool isActive = true;
String? searchQuery;
if (currentState is _Loaded) {
isActive = currentState.isActive;
searchQuery = currentState.searchQuery;
}
_isLoadingMore = false;
_searchDebounce?.cancel();
log('🔄 Refreshing categories');
// Clear local cache
_categoryRepository.clearCache();
add(CategoryLoaderEvent.getCategories(
isActive: isActive,
search: searchQuery,
forceRemote: true, // Force remote refresh
));
}
// ========================================
// SEARCH CATEGORIES
// ========================================
Future<void> _onSearch(
_Search event,
Emitter<CategoryLoaderState> emit,
) async {
// Cancel previous search
_searchDebounce?.cancel();
// Debounce search for better UX
_searchDebounce = Timer(Duration(milliseconds: 300), () async {
emit(const CategoryLoaderState.loading());
_isLoadingMore = false;
log('🔍 Searching categories: "${event.query}"');
final result = await _categoryRepository.getCategories(
page: 1,
limit: 20, // More results for search
isActive: event.isActive,
search: event.query,
);
await result.fold(
(failure) async {
log('❌ Search error: $failure');
emit(CategoryLoaderState.error(failure));
},
(response) async {
final categories = response.data.categories;
final totalPages = response.data.totalPages;
final hasReachedMax = categories.length < 20 || 1 >= totalPages;
log('✅ Search results: ${categories.length} categories found');
emit(CategoryLoaderState.loaded(
categories: categories,
hasReachedMax: hasReachedMax,
currentPage: 1,
isLoadingMore: false,
isActive: event.isActive,
searchQuery: event.query,
));
},
);
});
on<_SetCategoryId>((event, emit) async {
var currentState = state as _Loaded;
}
emit(_Loaded(currentState.categories, event.categoryId));
});
// ========================================
// SYNC ALL CATEGORIES
// ========================================
Future<void> _onSyncAll(
_SyncAll event,
Emitter<CategoryLoaderState> emit,
) async {
emit(const CategoryLoaderState.syncing());
log('🔄 Starting full category sync...');
final result = await _categoryRepository.syncAllCategories();
await result.fold(
(failure) async {
log('❌ Sync failed: $failure');
emit(CategoryLoaderState.syncError(failure));
// After sync error, try to load local data
Timer(Duration(seconds: 2), () {
add(const CategoryLoaderEvent.getCategories());
});
},
(successMessage) async {
log('✅ Sync completed: $successMessage');
emit(CategoryLoaderState.syncSuccess(successMessage));
// After successful sync, load the updated data
Timer(Duration(seconds: 1), () {
add(const CategoryLoaderEvent.getCategories());
});
},
);
}
// ========================================
// GET ALL CATEGORIES (For Dropdown)
// ========================================
Future<void> _onGetAllCategories(
_GetAllCategories event,
Emitter<CategoryLoaderState> emit,
) async {
try {
log('📋 Loading all categories for dropdown...');
final categories = await _categoryRepository.getAllCategories();
emit(CategoryLoaderState.allCategoriesLoaded(categories));
log('✅ All categories loaded: ${categories.length}');
} catch (e) {
log('❌ Error loading all categories: $e');
emit(CategoryLoaderState.error('Gagal memuat semua kategori: $e'));
}
}
// ========================================
// GET DATABASE STATS
// ========================================
Future<void> _onGetDatabaseStats(
_GetDatabaseStats event,
Emitter<CategoryLoaderState> emit,
) async {
try {
final stats = await _categoryRepository.getDatabaseStats();
log('📊 Category database stats retrieved: $stats');
// You can emit a special state here if needed for UI updates
// For now, just log the stats
} catch (e) {
log('❌ Error getting category database stats: $e');
}
}
// ========================================
// CLEAR CACHE
// ========================================
Future<void> _onClearCache(
_ClearCache event,
Emitter<CategoryLoaderState> emit,
) async {
log('🧹 Manually clearing category cache');
_categoryRepository.clearCache();
// Refresh current data after cache clear
add(const CategoryLoaderEvent.refresh());
}
}
File diff suppressed because it is too large Load Diff
@@ -2,7 +2,26 @@ part of 'category_loader_bloc.dart';
@freezed
class CategoryLoaderEvent with _$CategoryLoaderEvent {
const factory CategoryLoaderEvent.get() = _Get;
const factory CategoryLoaderEvent.setCategoryId(String categoryId) =
_SetCategoryId;
const factory CategoryLoaderEvent.getCategories({
@Default(true) bool isActive,
String? search,
@Default(false) bool forceRemote,
}) = _GetCategories;
const factory CategoryLoaderEvent.loadMore() = _LoadMore;
const factory CategoryLoaderEvent.refresh() = _Refresh;
const factory CategoryLoaderEvent.search({
required String query,
@Default(true) bool isActive,
}) = _Search;
const factory CategoryLoaderEvent.syncAll() = _SyncAll;
const factory CategoryLoaderEvent.getAllCategories() = _GetAllCategories;
const factory CategoryLoaderEvent.getDatabaseStats() = _GetDatabaseStats;
const factory CategoryLoaderEvent.clearCache() = _ClearCache;
}
@@ -3,8 +3,29 @@ part of 'category_loader_bloc.dart';
@freezed
class CategoryLoaderState with _$CategoryLoaderState {
const factory CategoryLoaderState.initial() = _Initial;
const factory CategoryLoaderState.loading() = _Loading;
const factory CategoryLoaderState.loaded(
List<CategoryModel> categories, String? categoryId) = _Loaded;
const factory CategoryLoaderState.loaded({
required List<CategoryModel> categories,
required bool hasReachedMax,
required int currentPage,
required bool isLoadingMore,
required bool isActive,
String? searchQuery,
}) = _Loaded;
const factory CategoryLoaderState.error(String message) = _Error;
// Sync-specific states
const factory CategoryLoaderState.syncing() = _Syncing;
const factory CategoryLoaderState.syncSuccess(String message) = _SyncSuccess;
const factory CategoryLoaderState.syncError(String message) = _SyncError;
// For dropdown/all categories
const factory CategoryLoaderState.allCategoriesLoaded(
List<CategoryModel> categories,
) = _AllCategoriesLoaded;
}
@@ -1,7 +1,6 @@
import 'dart:developer';
import 'package:bloc/bloc.dart';
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
import 'package:enaklo_pos/data/models/response/delivery_response_model.dart';
import 'package:enaklo_pos/data/models/response/discount_response_model.dart';
import 'package:enaklo_pos/presentation/table/models/draft_order_item.dart';
@@ -310,9 +309,9 @@ class CheckoutBloc extends Bloc<CheckoutEvent, CheckoutState> {
DateFormat('yyyy-MM-dd HH:mm:ss').format(DateTime.now()),
);
log("draftOrder12: ${draftOrder.toMapForLocal()}");
final orderDraftId =
await ProductLocalDatasource.instance.saveDraftOrder(draftOrder);
emit(_SavedDraftOrder(orderDraftId));
// final orderDraftId =
// await ProductLocalDatasource.instance.saveDraftOrder(draftOrder);
emit(_SavedDraftOrder(0));
});
//load draft order
@@ -1,5 +1,5 @@
import 'package:bloc/bloc.dart';
import 'package:enaklo_pos/data/datasources/outlet_remote_data_source.dart';
import 'package:enaklo_pos/data/datasources/outlet/outlet_remote_data_source.dart';
import 'package:enaklo_pos/presentation/home/models/outlet_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@@ -1,26 +0,0 @@
import 'dart:developer';
import 'package:bloc/bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
import '../../../../data/models/response/product_response_model.dart';
part 'local_product_bloc.freezed.dart';
part 'local_product_event.dart';
part 'local_product_state.dart';
class LocalProductBloc extends Bloc<LocalProductEvent, LocalProductState> {
final ProductLocalDatasource productLocalDatasource;
LocalProductBloc(
this.productLocalDatasource,
) : super(const _Initial()) {
on<_GetLocalProduct>((event, emit) async {
emit(const _Loading());
final result = await productLocalDatasource.getProducts();
log("Result: ${result.length}");
emit(_Loaded(result));
});
}
}
@@ -1,907 +0,0 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'local_product_bloc.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
/// @nodoc
mixin _$LocalProductEvent {
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() started,
required TResult Function() getLocalProduct,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? started,
TResult? Function()? getLocalProduct,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? started,
TResult Function()? getLocalProduct,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Started value) started,
required TResult Function(_GetLocalProduct value) getLocalProduct,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Started value)? started,
TResult? Function(_GetLocalProduct value)? getLocalProduct,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Started value)? started,
TResult Function(_GetLocalProduct value)? getLocalProduct,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $LocalProductEventCopyWith<$Res> {
factory $LocalProductEventCopyWith(
LocalProductEvent value, $Res Function(LocalProductEvent) then) =
_$LocalProductEventCopyWithImpl<$Res, LocalProductEvent>;
}
/// @nodoc
class _$LocalProductEventCopyWithImpl<$Res, $Val extends LocalProductEvent>
implements $LocalProductEventCopyWith<$Res> {
_$LocalProductEventCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of LocalProductEvent
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
abstract class _$$StartedImplCopyWith<$Res> {
factory _$$StartedImplCopyWith(
_$StartedImpl value, $Res Function(_$StartedImpl) then) =
__$$StartedImplCopyWithImpl<$Res>;
}
/// @nodoc
class __$$StartedImplCopyWithImpl<$Res>
extends _$LocalProductEventCopyWithImpl<$Res, _$StartedImpl>
implements _$$StartedImplCopyWith<$Res> {
__$$StartedImplCopyWithImpl(
_$StartedImpl _value, $Res Function(_$StartedImpl) _then)
: super(_value, _then);
/// Create a copy of LocalProductEvent
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
class _$StartedImpl implements _Started {
const _$StartedImpl();
@override
String toString() {
return 'LocalProductEvent.started()';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _$StartedImpl);
}
@override
int get hashCode => runtimeType.hashCode;
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() started,
required TResult Function() getLocalProduct,
}) {
return started();
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? started,
TResult? Function()? getLocalProduct,
}) {
return started?.call();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? started,
TResult Function()? getLocalProduct,
required TResult orElse(),
}) {
if (started != null) {
return started();
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Started value) started,
required TResult Function(_GetLocalProduct value) getLocalProduct,
}) {
return started(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Started value)? started,
TResult? Function(_GetLocalProduct value)? getLocalProduct,
}) {
return started?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Started value)? started,
TResult Function(_GetLocalProduct value)? getLocalProduct,
required TResult orElse(),
}) {
if (started != null) {
return started(this);
}
return orElse();
}
}
abstract class _Started implements LocalProductEvent {
const factory _Started() = _$StartedImpl;
}
/// @nodoc
abstract class _$$GetLocalProductImplCopyWith<$Res> {
factory _$$GetLocalProductImplCopyWith(_$GetLocalProductImpl value,
$Res Function(_$GetLocalProductImpl) then) =
__$$GetLocalProductImplCopyWithImpl<$Res>;
}
/// @nodoc
class __$$GetLocalProductImplCopyWithImpl<$Res>
extends _$LocalProductEventCopyWithImpl<$Res, _$GetLocalProductImpl>
implements _$$GetLocalProductImplCopyWith<$Res> {
__$$GetLocalProductImplCopyWithImpl(
_$GetLocalProductImpl _value, $Res Function(_$GetLocalProductImpl) _then)
: super(_value, _then);
/// Create a copy of LocalProductEvent
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
class _$GetLocalProductImpl implements _GetLocalProduct {
const _$GetLocalProductImpl();
@override
String toString() {
return 'LocalProductEvent.getLocalProduct()';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _$GetLocalProductImpl);
}
@override
int get hashCode => runtimeType.hashCode;
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() started,
required TResult Function() getLocalProduct,
}) {
return getLocalProduct();
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? started,
TResult? Function()? getLocalProduct,
}) {
return getLocalProduct?.call();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? started,
TResult Function()? getLocalProduct,
required TResult orElse(),
}) {
if (getLocalProduct != null) {
return getLocalProduct();
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Started value) started,
required TResult Function(_GetLocalProduct value) getLocalProduct,
}) {
return getLocalProduct(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Started value)? started,
TResult? Function(_GetLocalProduct value)? getLocalProduct,
}) {
return getLocalProduct?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Started value)? started,
TResult Function(_GetLocalProduct value)? getLocalProduct,
required TResult orElse(),
}) {
if (getLocalProduct != null) {
return getLocalProduct(this);
}
return orElse();
}
}
abstract class _GetLocalProduct implements LocalProductEvent {
const factory _GetLocalProduct() = _$GetLocalProductImpl;
}
/// @nodoc
mixin _$LocalProductState {
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() loading,
required TResult Function(List<Product> products) loaded,
required TResult Function(String message) error,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function(List<Product> products)? loaded,
TResult? Function(String message)? error,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function(List<Product> products)? loaded,
TResult Function(String message)? error,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Initial value) initial,
required TResult Function(_Loading value) loading,
required TResult Function(_Loaded value) loaded,
required TResult Function(_Error value) error,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Initial value)? initial,
TResult? Function(_Loading value)? loading,
TResult? Function(_Loaded value)? loaded,
TResult? Function(_Error value)? error,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Initial value)? initial,
TResult Function(_Loading value)? loading,
TResult Function(_Loaded value)? loaded,
TResult Function(_Error value)? error,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $LocalProductStateCopyWith<$Res> {
factory $LocalProductStateCopyWith(
LocalProductState value, $Res Function(LocalProductState) then) =
_$LocalProductStateCopyWithImpl<$Res, LocalProductState>;
}
/// @nodoc
class _$LocalProductStateCopyWithImpl<$Res, $Val extends LocalProductState>
implements $LocalProductStateCopyWith<$Res> {
_$LocalProductStateCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of LocalProductState
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
abstract class _$$InitialImplCopyWith<$Res> {
factory _$$InitialImplCopyWith(
_$InitialImpl value, $Res Function(_$InitialImpl) then) =
__$$InitialImplCopyWithImpl<$Res>;
}
/// @nodoc
class __$$InitialImplCopyWithImpl<$Res>
extends _$LocalProductStateCopyWithImpl<$Res, _$InitialImpl>
implements _$$InitialImplCopyWith<$Res> {
__$$InitialImplCopyWithImpl(
_$InitialImpl _value, $Res Function(_$InitialImpl) _then)
: super(_value, _then);
/// Create a copy of LocalProductState
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
class _$InitialImpl implements _Initial {
const _$InitialImpl();
@override
String toString() {
return 'LocalProductState.initial()';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _$InitialImpl);
}
@override
int get hashCode => runtimeType.hashCode;
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() loading,
required TResult Function(List<Product> products) loaded,
required TResult Function(String message) error,
}) {
return initial();
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function(List<Product> products)? loaded,
TResult? Function(String message)? error,
}) {
return initial?.call();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function(List<Product> products)? loaded,
TResult Function(String message)? error,
required TResult orElse(),
}) {
if (initial != null) {
return initial();
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Initial value) initial,
required TResult Function(_Loading value) loading,
required TResult Function(_Loaded value) loaded,
required TResult Function(_Error value) error,
}) {
return initial(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Initial value)? initial,
TResult? Function(_Loading value)? loading,
TResult? Function(_Loaded value)? loaded,
TResult? Function(_Error value)? error,
}) {
return initial?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Initial value)? initial,
TResult Function(_Loading value)? loading,
TResult Function(_Loaded value)? loaded,
TResult Function(_Error value)? error,
required TResult orElse(),
}) {
if (initial != null) {
return initial(this);
}
return orElse();
}
}
abstract class _Initial implements LocalProductState {
const factory _Initial() = _$InitialImpl;
}
/// @nodoc
abstract class _$$LoadingImplCopyWith<$Res> {
factory _$$LoadingImplCopyWith(
_$LoadingImpl value, $Res Function(_$LoadingImpl) then) =
__$$LoadingImplCopyWithImpl<$Res>;
}
/// @nodoc
class __$$LoadingImplCopyWithImpl<$Res>
extends _$LocalProductStateCopyWithImpl<$Res, _$LoadingImpl>
implements _$$LoadingImplCopyWith<$Res> {
__$$LoadingImplCopyWithImpl(
_$LoadingImpl _value, $Res Function(_$LoadingImpl) _then)
: super(_value, _then);
/// Create a copy of LocalProductState
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
class _$LoadingImpl implements _Loading {
const _$LoadingImpl();
@override
String toString() {
return 'LocalProductState.loading()';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _$LoadingImpl);
}
@override
int get hashCode => runtimeType.hashCode;
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() loading,
required TResult Function(List<Product> products) loaded,
required TResult Function(String message) error,
}) {
return loading();
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function(List<Product> products)? loaded,
TResult? Function(String message)? error,
}) {
return loading?.call();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function(List<Product> products)? loaded,
TResult Function(String message)? error,
required TResult orElse(),
}) {
if (loading != null) {
return loading();
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Initial value) initial,
required TResult Function(_Loading value) loading,
required TResult Function(_Loaded value) loaded,
required TResult Function(_Error value) error,
}) {
return loading(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Initial value)? initial,
TResult? Function(_Loading value)? loading,
TResult? Function(_Loaded value)? loaded,
TResult? Function(_Error value)? error,
}) {
return loading?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Initial value)? initial,
TResult Function(_Loading value)? loading,
TResult Function(_Loaded value)? loaded,
TResult Function(_Error value)? error,
required TResult orElse(),
}) {
if (loading != null) {
return loading(this);
}
return orElse();
}
}
abstract class _Loading implements LocalProductState {
const factory _Loading() = _$LoadingImpl;
}
/// @nodoc
abstract class _$$LoadedImplCopyWith<$Res> {
factory _$$LoadedImplCopyWith(
_$LoadedImpl value, $Res Function(_$LoadedImpl) then) =
__$$LoadedImplCopyWithImpl<$Res>;
@useResult
$Res call({List<Product> products});
}
/// @nodoc
class __$$LoadedImplCopyWithImpl<$Res>
extends _$LocalProductStateCopyWithImpl<$Res, _$LoadedImpl>
implements _$$LoadedImplCopyWith<$Res> {
__$$LoadedImplCopyWithImpl(
_$LoadedImpl _value, $Res Function(_$LoadedImpl) _then)
: super(_value, _then);
/// Create a copy of LocalProductState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? products = null,
}) {
return _then(_$LoadedImpl(
null == products
? _value._products
: products // ignore: cast_nullable_to_non_nullable
as List<Product>,
));
}
}
/// @nodoc
class _$LoadedImpl implements _Loaded {
const _$LoadedImpl(final List<Product> products) : _products = products;
final List<Product> _products;
@override
List<Product> get products {
if (_products is EqualUnmodifiableListView) return _products;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_products);
}
@override
String toString() {
return 'LocalProductState.loaded(products: $products)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$LoadedImpl &&
const DeepCollectionEquality().equals(other._products, _products));
}
@override
int get hashCode =>
Object.hash(runtimeType, const DeepCollectionEquality().hash(_products));
/// Create a copy of LocalProductState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$LoadedImplCopyWith<_$LoadedImpl> get copyWith =>
__$$LoadedImplCopyWithImpl<_$LoadedImpl>(this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() loading,
required TResult Function(List<Product> products) loaded,
required TResult Function(String message) error,
}) {
return loaded(products);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function(List<Product> products)? loaded,
TResult? Function(String message)? error,
}) {
return loaded?.call(products);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function(List<Product> products)? loaded,
TResult Function(String message)? error,
required TResult orElse(),
}) {
if (loaded != null) {
return loaded(products);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Initial value) initial,
required TResult Function(_Loading value) loading,
required TResult Function(_Loaded value) loaded,
required TResult Function(_Error value) error,
}) {
return loaded(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Initial value)? initial,
TResult? Function(_Loading value)? loading,
TResult? Function(_Loaded value)? loaded,
TResult? Function(_Error value)? error,
}) {
return loaded?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Initial value)? initial,
TResult Function(_Loading value)? loading,
TResult Function(_Loaded value)? loaded,
TResult Function(_Error value)? error,
required TResult orElse(),
}) {
if (loaded != null) {
return loaded(this);
}
return orElse();
}
}
abstract class _Loaded implements LocalProductState {
const factory _Loaded(final List<Product> products) = _$LoadedImpl;
List<Product> get products;
/// Create a copy of LocalProductState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
_$$LoadedImplCopyWith<_$LoadedImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class _$$ErrorImplCopyWith<$Res> {
factory _$$ErrorImplCopyWith(
_$ErrorImpl value, $Res Function(_$ErrorImpl) then) =
__$$ErrorImplCopyWithImpl<$Res>;
@useResult
$Res call({String message});
}
/// @nodoc
class __$$ErrorImplCopyWithImpl<$Res>
extends _$LocalProductStateCopyWithImpl<$Res, _$ErrorImpl>
implements _$$ErrorImplCopyWith<$Res> {
__$$ErrorImplCopyWithImpl(
_$ErrorImpl _value, $Res Function(_$ErrorImpl) _then)
: super(_value, _then);
/// Create a copy of LocalProductState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? message = null,
}) {
return _then(_$ErrorImpl(
null == message
? _value.message
: message // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
class _$ErrorImpl implements _Error {
const _$ErrorImpl(this.message);
@override
final String message;
@override
String toString() {
return 'LocalProductState.error(message: $message)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$ErrorImpl &&
(identical(other.message, message) || other.message == message));
}
@override
int get hashCode => Object.hash(runtimeType, message);
/// Create a copy of LocalProductState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$ErrorImplCopyWith<_$ErrorImpl> get copyWith =>
__$$ErrorImplCopyWithImpl<_$ErrorImpl>(this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() loading,
required TResult Function(List<Product> products) loaded,
required TResult Function(String message) error,
}) {
return error(message);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function(List<Product> products)? loaded,
TResult? Function(String message)? error,
}) {
return error?.call(message);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function(List<Product> products)? loaded,
TResult Function(String message)? error,
required TResult orElse(),
}) {
if (error != null) {
return error(message);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Initial value) initial,
required TResult Function(_Loading value) loading,
required TResult Function(_Loaded value) loaded,
required TResult Function(_Error value) error,
}) {
return error(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Initial value)? initial,
TResult? Function(_Loading value)? loading,
TResult? Function(_Loaded value)? loaded,
TResult? Function(_Error value)? error,
}) {
return error?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Initial value)? initial,
TResult Function(_Loading value)? loading,
TResult Function(_Loaded value)? loaded,
TResult Function(_Error value)? error,
required TResult orElse(),
}) {
if (error != null) {
return error(this);
}
return orElse();
}
}
abstract class _Error implements LocalProductState {
const factory _Error(final String message) = _$ErrorImpl;
String get message;
/// Create a copy of LocalProductState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
_$$ErrorImplCopyWith<_$ErrorImpl> get copyWith =>
throw _privateConstructorUsedError;
}
@@ -1,7 +0,0 @@
part of 'local_product_bloc.dart';
@freezed
class LocalProductEvent with _$LocalProductEvent {
const factory LocalProductEvent.started() = _Started;
const factory LocalProductEvent.getLocalProduct() = _GetLocalProduct;
}
@@ -1,9 +0,0 @@
part of 'local_product_bloc.dart';
@freezed
class LocalProductState with _$LocalProductState {
const factory LocalProductState.initial() = _Initial;
const factory LocalProductState.loading() = _Loading;
const factory LocalProductState.loaded(List<Product> products) = _Loaded;
const factory LocalProductState.error(String message) = _Error;
}
@@ -5,7 +5,6 @@ import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:enaklo_pos/data/datasources/auth_local_datasource.dart';
import 'package:enaklo_pos/data/datasources/order_remote_datasource.dart';
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
import '../../models/order_model.dart';
import '../../models/product_quantity.dart';
@@ -78,13 +77,13 @@ class OrderBloc extends Bloc<OrderEvent, OrderState> {
value = false;
}
int id = 0;
if (value) {
id = await ProductLocalDatasource.instance
.saveOrder(dataInput.copyWith(isSync: 1));
} else {
id = await ProductLocalDatasource.instance
.saveOrder(dataInput.copyWith(isSync: 1));
}
// if (value) {
// id = await ProductLocalDatasource.instance
// .saveOrder(dataInput.copyWith(isSync: 1));
// } else {
// id = await ProductLocalDatasource.instance
// .saveOrder(dataInput.copyWith(isSync: 1));
// }
emit(_Loaded(
dataInput,
@@ -1,5 +1,5 @@
import 'package:bloc/bloc.dart';
import 'package:enaklo_pos/data/datasources/outlet_remote_data_source.dart';
import 'package:enaklo_pos/data/datasources/outlet/outlet_remote_data_source.dart';
import 'package:enaklo_pos/presentation/home/models/outlet_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@@ -1,19 +0,0 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
import 'package:enaklo_pos/data/models/response/table_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'status_table_event.dart';
part 'status_table_state.dart';
part 'status_table_bloc.freezed.dart';
class StatusTableBloc extends Bloc<StatusTableEvent, StatusTableState> {
final ProductLocalDatasource datasource;
StatusTableBloc(this.datasource) : super(StatusTableState.initial()) {
on<_StatusTable>((event, emit) async {
emit(_Loading());
await datasource.updateStatusTable(event.table);
emit(_Success());
});
}
}
@@ -1,725 +0,0 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'status_table_bloc.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
/// @nodoc
mixin _$StatusTableEvent {
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() started,
required TResult Function(TableModel table) statusTabel,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? started,
TResult? Function(TableModel table)? statusTabel,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? started,
TResult Function(TableModel table)? statusTabel,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Started value) started,
required TResult Function(_StatusTable value) statusTabel,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Started value)? started,
TResult? Function(_StatusTable value)? statusTabel,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Started value)? started,
TResult Function(_StatusTable value)? statusTabel,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $StatusTableEventCopyWith<$Res> {
factory $StatusTableEventCopyWith(
StatusTableEvent value, $Res Function(StatusTableEvent) then) =
_$StatusTableEventCopyWithImpl<$Res, StatusTableEvent>;
}
/// @nodoc
class _$StatusTableEventCopyWithImpl<$Res, $Val extends StatusTableEvent>
implements $StatusTableEventCopyWith<$Res> {
_$StatusTableEventCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of StatusTableEvent
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
abstract class _$$StartedImplCopyWith<$Res> {
factory _$$StartedImplCopyWith(
_$StartedImpl value, $Res Function(_$StartedImpl) then) =
__$$StartedImplCopyWithImpl<$Res>;
}
/// @nodoc
class __$$StartedImplCopyWithImpl<$Res>
extends _$StatusTableEventCopyWithImpl<$Res, _$StartedImpl>
implements _$$StartedImplCopyWith<$Res> {
__$$StartedImplCopyWithImpl(
_$StartedImpl _value, $Res Function(_$StartedImpl) _then)
: super(_value, _then);
/// Create a copy of StatusTableEvent
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
class _$StartedImpl implements _Started {
const _$StartedImpl();
@override
String toString() {
return 'StatusTableEvent.started()';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _$StartedImpl);
}
@override
int get hashCode => runtimeType.hashCode;
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() started,
required TResult Function(TableModel table) statusTabel,
}) {
return started();
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? started,
TResult? Function(TableModel table)? statusTabel,
}) {
return started?.call();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? started,
TResult Function(TableModel table)? statusTabel,
required TResult orElse(),
}) {
if (started != null) {
return started();
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Started value) started,
required TResult Function(_StatusTable value) statusTabel,
}) {
return started(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Started value)? started,
TResult? Function(_StatusTable value)? statusTabel,
}) {
return started?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Started value)? started,
TResult Function(_StatusTable value)? statusTabel,
required TResult orElse(),
}) {
if (started != null) {
return started(this);
}
return orElse();
}
}
abstract class _Started implements StatusTableEvent {
const factory _Started() = _$StartedImpl;
}
/// @nodoc
abstract class _$$StatusTableImplCopyWith<$Res> {
factory _$$StatusTableImplCopyWith(
_$StatusTableImpl value, $Res Function(_$StatusTableImpl) then) =
__$$StatusTableImplCopyWithImpl<$Res>;
@useResult
$Res call({TableModel table});
}
/// @nodoc
class __$$StatusTableImplCopyWithImpl<$Res>
extends _$StatusTableEventCopyWithImpl<$Res, _$StatusTableImpl>
implements _$$StatusTableImplCopyWith<$Res> {
__$$StatusTableImplCopyWithImpl(
_$StatusTableImpl _value, $Res Function(_$StatusTableImpl) _then)
: super(_value, _then);
/// Create a copy of StatusTableEvent
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? table = null,
}) {
return _then(_$StatusTableImpl(
null == table
? _value.table
: table // ignore: cast_nullable_to_non_nullable
as TableModel,
));
}
}
/// @nodoc
class _$StatusTableImpl implements _StatusTable {
const _$StatusTableImpl(this.table);
@override
final TableModel table;
@override
String toString() {
return 'StatusTableEvent.statusTabel(table: $table)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$StatusTableImpl &&
(identical(other.table, table) || other.table == table));
}
@override
int get hashCode => Object.hash(runtimeType, table);
/// Create a copy of StatusTableEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$StatusTableImplCopyWith<_$StatusTableImpl> get copyWith =>
__$$StatusTableImplCopyWithImpl<_$StatusTableImpl>(this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() started,
required TResult Function(TableModel table) statusTabel,
}) {
return statusTabel(table);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? started,
TResult? Function(TableModel table)? statusTabel,
}) {
return statusTabel?.call(table);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? started,
TResult Function(TableModel table)? statusTabel,
required TResult orElse(),
}) {
if (statusTabel != null) {
return statusTabel(table);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Started value) started,
required TResult Function(_StatusTable value) statusTabel,
}) {
return statusTabel(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Started value)? started,
TResult? Function(_StatusTable value)? statusTabel,
}) {
return statusTabel?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Started value)? started,
TResult Function(_StatusTable value)? statusTabel,
required TResult orElse(),
}) {
if (statusTabel != null) {
return statusTabel(this);
}
return orElse();
}
}
abstract class _StatusTable implements StatusTableEvent {
const factory _StatusTable(final TableModel table) = _$StatusTableImpl;
TableModel get table;
/// Create a copy of StatusTableEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
_$$StatusTableImplCopyWith<_$StatusTableImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
mixin _$StatusTableState {
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() loading,
required TResult Function() success,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function()? success,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function()? success,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Initial value) initial,
required TResult Function(_Loading value) loading,
required TResult Function(_Success value) success,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Initial value)? initial,
TResult? Function(_Loading value)? loading,
TResult? Function(_Success value)? success,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Initial value)? initial,
TResult Function(_Loading value)? loading,
TResult Function(_Success value)? success,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $StatusTableStateCopyWith<$Res> {
factory $StatusTableStateCopyWith(
StatusTableState value, $Res Function(StatusTableState) then) =
_$StatusTableStateCopyWithImpl<$Res, StatusTableState>;
}
/// @nodoc
class _$StatusTableStateCopyWithImpl<$Res, $Val extends StatusTableState>
implements $StatusTableStateCopyWith<$Res> {
_$StatusTableStateCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of StatusTableState
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
abstract class _$$InitialImplCopyWith<$Res> {
factory _$$InitialImplCopyWith(
_$InitialImpl value, $Res Function(_$InitialImpl) then) =
__$$InitialImplCopyWithImpl<$Res>;
}
/// @nodoc
class __$$InitialImplCopyWithImpl<$Res>
extends _$StatusTableStateCopyWithImpl<$Res, _$InitialImpl>
implements _$$InitialImplCopyWith<$Res> {
__$$InitialImplCopyWithImpl(
_$InitialImpl _value, $Res Function(_$InitialImpl) _then)
: super(_value, _then);
/// Create a copy of StatusTableState
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
class _$InitialImpl implements _Initial {
const _$InitialImpl();
@override
String toString() {
return 'StatusTableState.initial()';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _$InitialImpl);
}
@override
int get hashCode => runtimeType.hashCode;
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() loading,
required TResult Function() success,
}) {
return initial();
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function()? success,
}) {
return initial?.call();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function()? success,
required TResult orElse(),
}) {
if (initial != null) {
return initial();
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Initial value) initial,
required TResult Function(_Loading value) loading,
required TResult Function(_Success value) success,
}) {
return initial(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Initial value)? initial,
TResult? Function(_Loading value)? loading,
TResult? Function(_Success value)? success,
}) {
return initial?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Initial value)? initial,
TResult Function(_Loading value)? loading,
TResult Function(_Success value)? success,
required TResult orElse(),
}) {
if (initial != null) {
return initial(this);
}
return orElse();
}
}
abstract class _Initial implements StatusTableState {
const factory _Initial() = _$InitialImpl;
}
/// @nodoc
abstract class _$$LoadingImplCopyWith<$Res> {
factory _$$LoadingImplCopyWith(
_$LoadingImpl value, $Res Function(_$LoadingImpl) then) =
__$$LoadingImplCopyWithImpl<$Res>;
}
/// @nodoc
class __$$LoadingImplCopyWithImpl<$Res>
extends _$StatusTableStateCopyWithImpl<$Res, _$LoadingImpl>
implements _$$LoadingImplCopyWith<$Res> {
__$$LoadingImplCopyWithImpl(
_$LoadingImpl _value, $Res Function(_$LoadingImpl) _then)
: super(_value, _then);
/// Create a copy of StatusTableState
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
class _$LoadingImpl implements _Loading {
const _$LoadingImpl();
@override
String toString() {
return 'StatusTableState.loading()';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _$LoadingImpl);
}
@override
int get hashCode => runtimeType.hashCode;
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() loading,
required TResult Function() success,
}) {
return loading();
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function()? success,
}) {
return loading?.call();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function()? success,
required TResult orElse(),
}) {
if (loading != null) {
return loading();
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Initial value) initial,
required TResult Function(_Loading value) loading,
required TResult Function(_Success value) success,
}) {
return loading(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Initial value)? initial,
TResult? Function(_Loading value)? loading,
TResult? Function(_Success value)? success,
}) {
return loading?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Initial value)? initial,
TResult Function(_Loading value)? loading,
TResult Function(_Success value)? success,
required TResult orElse(),
}) {
if (loading != null) {
return loading(this);
}
return orElse();
}
}
abstract class _Loading implements StatusTableState {
const factory _Loading() = _$LoadingImpl;
}
/// @nodoc
abstract class _$$SuccessImplCopyWith<$Res> {
factory _$$SuccessImplCopyWith(
_$SuccessImpl value, $Res Function(_$SuccessImpl) then) =
__$$SuccessImplCopyWithImpl<$Res>;
}
/// @nodoc
class __$$SuccessImplCopyWithImpl<$Res>
extends _$StatusTableStateCopyWithImpl<$Res, _$SuccessImpl>
implements _$$SuccessImplCopyWith<$Res> {
__$$SuccessImplCopyWithImpl(
_$SuccessImpl _value, $Res Function(_$SuccessImpl) _then)
: super(_value, _then);
/// Create a copy of StatusTableState
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
class _$SuccessImpl implements _Success {
const _$SuccessImpl();
@override
String toString() {
return 'StatusTableState.success()';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _$SuccessImpl);
}
@override
int get hashCode => runtimeType.hashCode;
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() loading,
required TResult Function() success,
}) {
return success();
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function()? success,
}) {
return success?.call();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function()? success,
required TResult orElse(),
}) {
if (success != null) {
return success();
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Initial value) initial,
required TResult Function(_Loading value) loading,
required TResult Function(_Success value) success,
}) {
return success(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Initial value)? initial,
TResult? Function(_Loading value)? loading,
TResult? Function(_Success value)? success,
}) {
return success?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Initial value)? initial,
TResult Function(_Loading value)? loading,
TResult Function(_Success value)? success,
required TResult orElse(),
}) {
if (success != null) {
return success(this);
}
return orElse();
}
}
abstract class _Success implements StatusTableState {
const factory _Success() = _$SuccessImpl;
}
@@ -1,9 +0,0 @@
part of 'status_table_bloc.dart';
@freezed
class StatusTableEvent with _$StatusTableEvent {
const factory StatusTableEvent.started() = _Started;
const factory StatusTableEvent.statusTabel(
TableModel table,
) = _StatusTable;
}
@@ -1,8 +0,0 @@
part of 'status_table_bloc.dart';
@freezed
class StatusTableState with _$StatusTableState {
const factory StatusTableState.initial() = _Initial;
const factory StatusTableState.loading() = _Loading;
const factory StatusTableState.success() = _Success;
}
@@ -2,12 +2,12 @@
import 'dart:async';
import 'dart:developer';
import 'package:enaklo_pos/data/datasources/printer/printer_local_datasource.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:enaklo_pos/data/dataoutputs/print_dataoutputs.dart';
import 'package:enaklo_pos/data/datasources/auth_local_datasource.dart';
import 'package:enaklo_pos/presentation/home/models/product_quantity.dart';
import 'package:enaklo_pos/presentation/home/models/order_type.dart';
import 'package:intl/intl.dart';
import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
@@ -18,8 +18,6 @@ import 'package:enaklo_pos/core/utils/printer_service.dart';
import '../../../core/components/spaces.dart';
import '../../../core/constants/colors.dart';
import '../bloc/order/order_bloc.dart';
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
class PaymentQrisDialog extends StatefulWidget {
final List<ProductQuantity> items;
@@ -236,7 +234,7 @@ class _PaymentQrisDialogState extends State<PaymentQrisDialog> {
widget.price, bytes!, int.parse(sizeReceipt));
// Get the receipt printer to print QRIS
final receiptPrinter = await ProductLocalDatasource
final receiptPrinter = await PrinterLocalDatasource
.instance
.getPrinterByCode('receipt');
@@ -1,6 +1,5 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first
import 'dart:async';
import 'dart:developer';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:enaklo_pos/presentation/customer/pages/customer_page.dart';
@@ -16,7 +15,6 @@ import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
import 'package:enaklo_pos/data/datasources/auth_local_datasource.dart';
import 'package:enaklo_pos/presentation/auth/login_page.dart';
import 'package:enaklo_pos/presentation/report/pages/report_page.dart';
import 'package:enaklo_pos/presentation/setting/bloc/sync_order/sync_order_bloc.dart';
import '../../../core/assets/assets.gen.dart';
import '../../auth/bloc/logout/logout_bloc.dart';
@@ -155,10 +153,6 @@ class _DashboardPageState extends State<DashboardPage> {
),
),
online: () {
log("🌐 Dashboard: Internet connection detected, triggering sync");
context.read<SyncOrderBloc>().add(
const SyncOrderEvent.syncOrder(),
);
return Container(
width: 40,
margin:
+145 -374
View File
@@ -1,11 +1,11 @@
// ========================================
// OFFLINE-ONLY HOMEPAGE - NO API CALLS
// HOMEPAGE - LOCAL DATA ONLY, NO SYNC
// lib/presentation/home/pages/home_page.dart
// ========================================
import 'dart:developer';
import 'package:enaklo_pos/core/components/flushbar.dart';
import 'package:enaklo_pos/data/datasources/product/product_local_datasource.dart';
import 'package:enaklo_pos/data/models/response/category_response_model.dart';
import 'package:enaklo_pos/presentation/home/bloc/category_loader/category_loader_bloc.dart';
import 'package:enaklo_pos/presentation/home/bloc/current_outlet/current_outlet_bloc.dart';
import 'package:enaklo_pos/presentation/home/bloc/product_loader/product_loader_bloc.dart';
@@ -50,17 +50,10 @@ class _HomePageState extends State<HomePage> {
final ScrollController scrollController = ScrollController();
String searchQuery = '';
// Local database only
Map<String, dynamic> _databaseStats = {};
final ProductLocalDatasource _localDatasource =
ProductLocalDatasource.instance;
bool _isLoadingStats = true;
@override
void initState() {
super.initState();
_initializeLocalData();
_loadProducts();
_loadData();
}
@override
@@ -70,49 +63,29 @@ class _HomePageState extends State<HomePage> {
super.dispose();
}
// Initialize local data only
void _initializeLocalData() {
_loadDatabaseStats();
}
void _loadData() {
log('📱 Loading data from local database...');
// Load database statistics
void _loadDatabaseStats() async {
try {
final stats = await _localDatasource.getDatabaseStats();
if (mounted) {
setState(() {
_databaseStats = stats;
_isLoadingStats = false;
});
}
log('📊 Local database stats: $stats');
} catch (e) {
log('❌ Error loading local stats: $e');
setState(() {
_isLoadingStats = false;
});
}
}
// Load categories from local database
context
.read<CategoryLoaderBloc>()
.add(const CategoryLoaderEvent.getCategories());
void _loadProducts() {
log('📱 Loading products from local database only...');
// Load products from local database only
// Load products from local database
context
.read<ProductLoaderBloc>()
.add(const ProductLoaderEvent.getProduct());
// Initialize other components
context.read<CheckoutBloc>().add(CheckoutEvent.started(widget.items));
context.read<CategoryLoaderBloc>().add(CategoryLoaderEvent.get());
context.read<CurrentOutletBloc>().add(CurrentOutletEvent.currentOutlet());
}
void _refreshLocalData() {
log('🔄 Refreshing local data...');
context.read<ProductLoaderBloc>().add(const ProductLoaderEvent.refresh());
_loadDatabaseStats();
}
// void _refreshData() {
// log('🔄 Refreshing l ocal data...');
// context.read<ProductLoaderBloc>().add(const ProductLoaderEvent.refresh());
// context.read<CategoryLoaderBloc>().add(const CategoryLoaderEvent.refresh());
// }
void onCategoryTap(int index) {
searchController.clear();
@@ -125,11 +98,9 @@ class _HomePageState extends State<HomePage> {
ScrollNotification notification, String? categoryId) {
if (notification is ScrollEndNotification &&
scrollController.position.extentAfter == 0) {
log('📄 Loading more local products for category: $categoryId');
log('📄 Loading more products...');
context.read<ProductLoaderBloc>().add(
ProductLoaderEvent.loadMore(
categoryId: categoryId,
),
ProductLoaderEvent.loadMore(),
);
return true;
}
@@ -142,7 +113,6 @@ class _HomePageState extends State<HomePage> {
listener: (context, state) {
state.maybeWhen(
orElse: () {},
loading: () {},
success: () {
Future.delayed(Duration(milliseconds: 300), () {
AppFlushbar.showSuccess(context, 'Outlet berhasil diubah');
@@ -160,81 +130,30 @@ class _HomePageState extends State<HomePage> {
backgroundColor: AppColors.white,
body: Column(
children: [
// Local database indicator
_buildLocalModeIndicator(),
// Main content
Expanded(
child: Row(
children: [
// Left panel - Products
// Left panel - Products with Categories
Expanded(
flex: 3,
child: Align(
alignment: AlignmentDirectional.topStart,
child: BlocBuilder<CategoryLoaderBloc,
CategoryLoaderState>(
builder: (context, state) {
return state.maybeWhen(
orElse: () =>
Center(child: CircularProgressIndicator()),
loaded: (categories, categoryId) => Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
// Enhanced home title with local stats
_buildLocalHomeTitle(categoryId),
// Products section
Expanded(
child: BlocBuilder<ProductLoaderBloc,
ProductLoaderState>(
builder: (context, productState) {
return CategoryTabBar(
categories: categories,
tabViews: categories.map((category) {
return SizedBox(
child: productState.maybeWhen(
orElse: () =>
_buildLoadingState(),
loading: () =>
_buildLoadingState(),
loaded: (products,
hasReachedMax,
currentPage,
isLoadingMore,
categoryId,
searchQuery) {
if (products.isEmpty) {
return _buildEmptyState(
categoryId);
}
return _buildProductGrid(
products,
hasReachedMax,
isLoadingMore,
categoryId,
currentPage,
);
},
error: (message) =>
_buildErrorState(
message, categoryId),
),
);
}).toList(),
);
},
),
),
],
),
);
},
),
child:
BlocBuilder<CategoryLoaderBloc, CategoryLoaderState>(
builder: (context, categoryState) {
return categoryState.maybeWhen(
orElse: () => _buildCategoryLoadingState(),
loading: () => _buildCategoryLoadingState(),
error: (message) =>
_buildCategoryErrorState(message),
loaded: (categories, hasReachedMax, currentPage,
isLoadingMore, isActive, searchQuery) =>
_buildCategoryContent(categories),
);
},
),
),
// Right panel - Cart (unchanged)
// Right panel - Cart
Expanded(
flex: 2,
child: _buildCartSection(),
@@ -249,219 +168,120 @@ class _HomePageState extends State<HomePage> {
);
}
// Local mode indicator
Widget _buildLocalModeIndicator() {
return Container(
width: double.infinity,
padding: EdgeInsets.symmetric(vertical: 8, horizontal: 16),
color: Colors.blue.shade600,
child: Row(
Widget _buildCategoryLoadingState() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.storage, color: Colors.white, size: 16),
SizedBox(width: 8),
Expanded(
child: Text(
_isLoadingStats
? 'Mode Lokal - Memuat data...'
: 'Mode Lokal - ${_databaseStats['total_products'] ?? 0} produk tersimpan',
style: TextStyle(
color: Colors.white,
fontSize: 13,
fontWeight: FontWeight.w500,
),
),
),
if (_databaseStats.isNotEmpty) ...[
Text(
'${(_databaseStats['database_size_mb'] ?? 0.0).toStringAsFixed(1)} MB',
style: TextStyle(
color: Colors.white.withOpacity(0.8),
fontSize: 11,
),
),
SizedBox(width: 8),
],
InkWell(
onTap: _refreshLocalData,
child: Container(
padding: EdgeInsets.all(4),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(4),
),
child: Icon(Icons.refresh, color: Colors.white, size: 14),
),
CircularProgressIndicator(color: AppColors.primary),
SizedBox(height: 16),
Text(
'Memuat kategori...',
style: TextStyle(color: Colors.grey.shade600),
),
],
),
);
}
// Enhanced home title with local stats only
Widget _buildLocalHomeTitle(String? categoryId) {
return Container(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
border: Border(bottom: BorderSide(color: Colors.grey.shade200)),
),
Widget _buildCategoryErrorState(String message) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Original HomeTitle with faster search
HomeTitle(
controller: searchController,
onChanged: (value) {
setState(() {
searchQuery = value;
});
Icon(Icons.error_outline, size: 48, color: Colors.red.shade400),
SizedBox(height: 16),
Text('Error Kategori',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600)),
SizedBox(height: 8),
Padding(
padding: EdgeInsets.symmetric(horizontal: 32),
child: Text(
message,
textAlign: TextAlign.center,
style: TextStyle(color: Colors.grey.shade600),
),
),
SizedBox(height: 16),
Button.filled(
width: 120,
onPressed: () {
context
.read<CategoryLoaderBloc>()
.add(const CategoryLoaderEvent.getCategories());
},
label: 'Coba Lagi',
),
],
),
);
}
// Fast local search - no debounce needed for local data
Future.delayed(Duration(milliseconds: 200), () {
if (value == searchController.text) {
log('🔍 Local search: "$value"');
context.read<ProductLoaderBloc>().add(
ProductLoaderEvent.searchProduct(
categoryId: categoryId,
query: value,
),
);
}
});
Widget _buildCategoryContent(List<CategoryModel> categories) {
return Column(
children: [
// Simple home title
_buildSimpleHomeTitle(),
// Products section with categories
Expanded(
child: BlocBuilder<ProductLoaderBloc, ProductLoaderState>(
builder: (context, productState) {
return CategoryTabBar(
key: ValueKey(categories.length),
categories: categories,
tabViews: categories.map((category) {
return SizedBox(
child: productState.maybeWhen(
orElse: () => _buildLoadingState(),
loading: () => _buildLoadingState(),
loaded: (products, hasReachedMax, currentPage,
isLoadingMore, categoryId, searchQuery) {
if (products.isEmpty) {
return _buildEmptyState(categoryId);
}
return _buildProductGrid(
products,
hasReachedMax,
isLoadingMore,
categoryId,
currentPage,
);
},
error: (message) =>
_buildErrorState(message, category.id),
),
);
}).toList(),
);
},
),
// Local database stats
if (_databaseStats.isNotEmpty) ...[
SizedBox(height: 8),
Row(
children: [
// Local storage indicator
Container(
padding: EdgeInsets.symmetric(horizontal: 6, vertical: 3),
decoration: BoxDecoration(
color: Colors.blue.shade50,
borderRadius: BorderRadius.circular(10),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.storage,
size: 12, color: Colors.blue.shade600),
SizedBox(width: 3),
Text(
'Lokal',
style: TextStyle(
fontSize: 10,
color: Colors.blue.shade600,
fontWeight: FontWeight.w500,
),
),
],
),
),
SizedBox(width: 8),
// Database stats chips
_buildStatChip(
'${_databaseStats['total_products'] ?? 0}',
'produk',
Icons.inventory_2,
Colors.green,
),
SizedBox(width: 6),
_buildStatChip(
'${_databaseStats['total_variants'] ?? 0}',
'varian',
Icons.tune,
Colors.orange,
),
SizedBox(width: 6),
_buildStatChip(
'${_databaseStats['cache_entries'] ?? 0}',
'cache',
Icons.memory,
Colors.purple,
),
Spacer(),
// Clear cache button
InkWell(
onTap: () {
_localDatasource.clearExpiredCache();
_loadDatabaseStats();
AppFlushbar.showSuccess(context, 'Cache dibersihkan');
},
child: Container(
padding: EdgeInsets.all(5),
decoration: BoxDecoration(
color: Colors.grey.shade100,
borderRadius: BorderRadius.circular(5),
),
child: Icon(
Icons.clear_all,
size: 14,
color: Colors.grey.shade600,
),
),
),
SizedBox(width: 4),
// Refresh button
InkWell(
onTap: _refreshLocalData,
child: Container(
padding: EdgeInsets.all(5),
decoration: BoxDecoration(
color: AppColors.primary.withOpacity(0.1),
borderRadius: BorderRadius.circular(5),
),
child: Icon(
Icons.refresh,
size: 14,
color: AppColors.primary,
),
),
),
],
),
],
],
),
),
],
);
}
Widget _buildStatChip(
String value, String label, IconData icon, Color color) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 5, vertical: 2),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(6),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(icon, size: 10, color: color),
SizedBox(width: 2),
Text(
value,
style: TextStyle(
fontSize: 9,
fontWeight: FontWeight.w600,
color: color,
),
),
SizedBox(width: 1),
Text(
label,
style: TextStyle(
fontSize: 8,
color: color.withOpacity(0.8),
),
),
],
),
// Simple home title
Widget _buildSimpleHomeTitle() {
return HomeTitle(
controller: searchController,
onChanged: (value) {
setState(() {
searchQuery = value;
});
// Fast local search
Future.delayed(Duration(milliseconds: 200), () {
if (value == searchController.text) {
log('🔍 Local search: "$value"');
context.read<ProductLoaderBloc>().add(
ProductLoaderEvent.searchProduct(
query: value,
),
);
}
});
},
);
}
@@ -473,7 +293,7 @@ class _HomePageState extends State<HomePage> {
CircularProgressIndicator(color: AppColors.primary),
SizedBox(height: 16),
Text(
'Memuat data lokal...',
'Memuat data...',
style: TextStyle(color: Colors.grey.shade600),
),
],
@@ -491,12 +311,12 @@ class _HomePageState extends State<HomePage> {
Text(
searchQuery.isNotEmpty
? 'Produk "$searchQuery" tidak ditemukan'
: 'Belum ada data produk lokal',
: 'Belum ada data produk',
textAlign: TextAlign.center,
),
SizedBox(height: 8),
Text(
'Tambahkan produk ke database lokal terlebih dahulu',
'Data akan dimuat dari database lokal',
style: TextStyle(
color: Colors.grey.shade600,
fontSize: 12,
@@ -542,18 +362,11 @@ class _HomePageState extends State<HomePage> {
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.error_outline,
size: 48,
color: Colors.red.shade400,
),
Icon(Icons.error_outline, size: 48, color: Colors.red.shade400),
SizedBox(height: 16),
Text(
'Error Database Lokal',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
),
'Error Database',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
),
SizedBox(height: 8),
Padding(
@@ -588,61 +401,19 @@ class _HomePageState extends State<HomePage> {
) {
return Column(
children: [
// Product count with local indicator
// Simple product count
if (products.isNotEmpty)
Container(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 6),
child: Row(
children: [
Container(
padding: EdgeInsets.symmetric(horizontal: 4, vertical: 1),
decoration: BoxDecoration(
color: Colors.blue.shade50,
borderRadius: BorderRadius.circular(4),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.storage,
size: 10, color: Colors.blue.shade600),
SizedBox(width: 2),
Text(
'${products.length}',
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w600,
color: Colors.blue.shade600,
),
),
],
),
),
SizedBox(width: 6),
Text(
'produk dari database lokal',
'${products.length} produk ditemukan',
style: TextStyle(
color: Colors.grey.shade600,
fontSize: 11,
fontSize: 12,
),
),
if (currentPage > 1) ...[
SizedBox(width: 6),
Container(
padding: EdgeInsets.symmetric(horizontal: 4, vertical: 1),
decoration: BoxDecoration(
color: AppColors.primary.withOpacity(0.1),
borderRadius: BorderRadius.circular(4),
),
child: Text(
'Hal $currentPage',
style: TextStyle(
color: AppColors.primary,
fontSize: 9,
fontWeight: FontWeight.w500,
),
),
),
],
Spacer(),
if (isLoadingMore)
SizedBox(
@@ -657,7 +428,7 @@ class _HomePageState extends State<HomePage> {
),
),
// Products grid - faster loading from local DB
// Products grid
Expanded(
child: NotificationListener<ScrollNotification>(
onNotification: (notification) =>
@@ -666,7 +437,7 @@ class _HomePageState extends State<HomePage> {
itemCount: products.length,
controller: scrollController,
padding: const EdgeInsets.all(16),
cacheExtent: 200.0, // Bigger cache for smooth scrolling
cacheExtent: 200.0,
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 180,
mainAxisSpacing: 30,
@@ -683,12 +454,12 @@ class _HomePageState extends State<HomePage> {
),
),
// End of data indicator
// End indicator
if (hasReachedMax && products.isNotEmpty)
Container(
padding: EdgeInsets.all(8),
child: Text(
'Semua produk lokal telah dimuat',
'Semua produk telah dimuat',
style: TextStyle(
color: Colors.grey.shade500,
fontSize: 11,
@@ -786,7 +557,7 @@ class _HomePageState extends State<HomePage> {
),
),
// Payment section (unchanged)
// Payment section
Padding(
padding: const EdgeInsets.all(16.0).copyWith(top: 0),
child: Column(
@@ -1,6 +1,5 @@
import 'package:enaklo_pos/core/constants/colors.dart';
import 'package:enaklo_pos/data/models/response/category_response_model.dart';
import 'package:enaklo_pos/presentation/home/bloc/category_loader/category_loader_bloc.dart';
import 'package:enaklo_pos/presentation/home/bloc/product_loader/product_loader_bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@@ -37,14 +36,41 @@ class _CategoryTabBarState extends State<CategoryTabBar>
context.read<ProductLoaderBloc>().add(
ProductLoaderEvent.getProduct(categoryId: selectedCategoryId),
);
context
.read<CategoryLoaderBloc>()
.add(CategoryLoaderEvent.setCategoryId(selectedCategoryId ?? ""));
}
}
});
}
@override
void didUpdateWidget(CategoryTabBar oldWidget) {
super.didUpdateWidget(oldWidget);
// ✅ Update TabController when categories length changes
if (oldWidget.categories.length != widget.categories.length) {
_tabController.dispose();
_tabController = TabController(
length: widget.categories.length,
vsync: this,
initialIndex: 0, // Reset to first tab
);
_tabController.addListener(() {
if (_tabController.indexIsChanging) {
if (_tabController.index == 0) {
context.read<ProductLoaderBloc>().add(
ProductLoaderEvent.getProduct(),
);
} else {
selectedCategoryId = widget.categories[_tabController.index].id;
context.read<ProductLoaderBloc>().add(
ProductLoaderEvent.getProduct(categoryId: selectedCategoryId),
);
}
}
});
}
}
@override
void dispose() {
_tabController.dispose();
@@ -53,48 +79,45 @@ class _CategoryTabBarState extends State<CategoryTabBar>
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: widget.categories.length,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Material(
elevation: 0,
color: Colors.white,
borderOnForeground: false,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 16),
child: TabBar(
controller: _tabController,
isScrollable: true,
tabAlignment: TabAlignment.start,
labelColor: AppColors.primary,
labelStyle: TextStyle(
fontWeight: FontWeight.bold,
),
dividerColor: AppColors.primary,
unselectedLabelColor: AppColors.primary,
indicatorSize: TabBarIndicatorSize.label,
indicatorWeight: 4,
indicatorColor: AppColors.primary,
tabs: widget.categories
.map((category) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Tab(text: category.name),
))
.toList(),
),
),
),
Expanded(
// ✅ ini bagian penting
child: TabBarView(
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Material(
elevation: 0,
color: Colors.white,
borderOnForeground: false,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 16),
child: TabBar(
controller: _tabController,
children: widget.tabViews,
isScrollable: true,
tabAlignment: TabAlignment.start,
labelColor: AppColors.primary,
labelStyle: TextStyle(
fontWeight: FontWeight.bold,
),
dividerColor: AppColors.primary,
unselectedLabelColor: AppColors.primary,
indicatorSize: TabBarIndicatorSize.label,
indicatorWeight: 4,
indicatorColor: AppColors.primary,
tabs: widget.categories
.map((category) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Tab(text: category.name),
))
.toList(),
),
),
],
),
),
Expanded(
// ✅ ini bagian penting
child: TabBarView(
controller: _tabController,
children: widget.tabViews,
),
),
],
);
}
}
@@ -19,7 +19,7 @@ class ConfirmPaymentTitle extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
height: context.deviceHeight * 0.1,
height: context.deviceHeight * 0.123,
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0),
decoration: BoxDecoration(
color: AppColors.white,
+42 -21
View File
@@ -1,6 +1,7 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:enaklo_pos/core/extensions/int_ext.dart';
import 'package:enaklo_pos/presentation/home/dialog/variant_dialog.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:enaklo_pos/core/constants/variables.dart';
@@ -53,27 +54,47 @@ class ProductCard extends StatelessWidget {
children: [
ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(8.0)),
child: CachedNetworkImage(
imageUrl: (data.imageUrl ?? "").contains('http')
? data.imageUrl!
: '${Variables.baseUrl}/${data.imageUrl}',
fit: BoxFit.cover,
width: double.infinity,
height: 120,
memCacheHeight: 120,
memCacheWidth: 120,
errorWidget: (context, url, error) => Container(
width: double.infinity,
height: 120,
decoration: BoxDecoration(
color: AppColors.disabled.withOpacity(0.4),
),
child: const Icon(
Icons.image,
color: AppColors.grey,
),
),
),
child: data.imageUrl == ""
? Container(
width: double.infinity,
height: 120,
decoration: BoxDecoration(
color: AppColors.disabled.withOpacity(0.4),
),
child: const Icon(
Icons.image,
color: AppColors.grey,
),
)
: CachedNetworkImage(
imageUrl: (data.imageUrl ?? "").contains('http')
? data.imageUrl!
: '${Variables.baseUrl}/${data.imageUrl}',
fit: BoxFit.cover,
width: double.infinity,
height: 120,
memCacheHeight: 120,
memCacheWidth: 120,
errorWidget: (context, url, error) {
FirebaseCrashlytics.instance.recordError(
error,
StackTrace.current,
reason:
'Failed to load image from: $url, productId: ${data.id}, productName: ${data.name}, dataUrl: ${data.imageUrl}',
fatal: false,
);
return Container(
width: double.infinity,
height: 120,
decoration: BoxDecoration(
color: AppColors.disabled.withOpacity(0.4),
),
child: const Icon(
Icons.image,
color: AppColors.grey,
),
);
}),
),
const Spacer(),
Text(
@@ -12,6 +12,7 @@ import 'package:enaklo_pos/presentation/home/bloc/payment_methods/payment_method
import 'package:enaklo_pos/presentation/home/models/product_quantity.dart';
import 'package:enaklo_pos/presentation/sales/blocs/payment_form/payment_form_bloc.dart';
import 'package:enaklo_pos/presentation/success/pages/success_payment_page.dart';
import 'package:enaklo_pos/presentation/success/pages/success_split_bill_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@@ -370,23 +371,43 @@ class _PaymentPageState extends State<PaymentPage> {
state.maybeWhen(
orElse: () {},
success: (data) {
context.pushReplacement(SuccessPaymentPage(
productQuantity: widget.order.orderItems
?.map(
(item) => ProductQuantity(
product: Product(
name: item.productName,
price: item.unitPrice,
),
quantity: item.quantity ?? 0,
if (widget.isSplit) {
context.pushReplacement(SuccessSplitBillPage(
productQuantity: getOrderItemPending()
.map(
(item) => ProductQuantity(
product: Product(
name: item.productName,
price: item.unitPrice,
),
)
.toList() ??
[],
payment: data,
paymentMethod: selectedPaymentMethod?.name ?? '',
nominalBayar: totalPriceController.text.toIntegerFromText,
));
quantity: item.quantity ?? 0,
),
)
.toList(),
payment: data,
paymentMethod: selectedPaymentMethod?.name ?? '',
nominalBayar:
totalPriceController.text.toIntegerFromText,
));
} else {
context.pushReplacement(SuccessPaymentPage(
productQuantity: getOrderItemPending()
.map(
(item) => ProductQuantity(
product: Product(
name: item.productName,
price: item.unitPrice,
),
quantity: item.quantity ?? 0,
),
)
.toList(),
payment: data,
paymentMethod: selectedPaymentMethod?.name ?? '',
nominalBayar:
totalPriceController.text.toIntegerFromText,
));
}
},
error: (message) {
AppFlushbar.showError(context, message);
@@ -443,6 +464,7 @@ class _PaymentPageState extends State<PaymentPage> {
final itemPending = widget.order.orderItems
?.where((item) => item.status == "pending")
.toList();
if (widget.isSplit == false) {
final request = PaymentRequestModel(
amount: widget.order.totalAmount ?? 0,
@@ -1,6 +1,6 @@
import 'package:bloc/bloc.dart';
import 'package:enaklo_pos/data/datasources/analytic_remote_datasource.dart';
import 'package:enaklo_pos/data/datasources/outlet_local_datasource.dart';
import 'package:enaklo_pos/data/datasources/outlet/outlet_local_datasource.dart';
import 'package:enaklo_pos/data/models/response/category_analytic_response_model.dart';
import 'package:enaklo_pos/data/models/response/payment_method_analytic_response_model.dart';
import 'package:enaklo_pos/data/models/response/product_analytic_response_model.dart';
@@ -1,23 +0,0 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
import 'package:enaklo_pos/presentation/home/models/order_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'last_order_table_event.dart';
part 'last_order_table_state.dart';
part 'last_order_table_bloc.freezed.dart';
class LastOrderTableBloc
extends Bloc<LastOrderTableEvent, LastOrderTableState> {
final ProductLocalDatasource datasource;
LastOrderTableBloc(this.datasource)
: super(const LastOrderTableState.initial()) {
on<_LastOrderTable>((event, emit) async {
emit(_Loading());
final order = await datasource.getLastOrderTable(event.tableNumber);
emit(_Success(order));
});
}
}
@@ -1,762 +0,0 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'last_order_table_bloc.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
/// @nodoc
mixin _$LastOrderTableEvent {
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() started,
required TResult Function(int tableNumber) lastOrderTable,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? started,
TResult? Function(int tableNumber)? lastOrderTable,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? started,
TResult Function(int tableNumber)? lastOrderTable,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Started value) started,
required TResult Function(_LastOrderTable value) lastOrderTable,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Started value)? started,
TResult? Function(_LastOrderTable value)? lastOrderTable,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Started value)? started,
TResult Function(_LastOrderTable value)? lastOrderTable,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $LastOrderTableEventCopyWith<$Res> {
factory $LastOrderTableEventCopyWith(
LastOrderTableEvent value, $Res Function(LastOrderTableEvent) then) =
_$LastOrderTableEventCopyWithImpl<$Res, LastOrderTableEvent>;
}
/// @nodoc
class _$LastOrderTableEventCopyWithImpl<$Res, $Val extends LastOrderTableEvent>
implements $LastOrderTableEventCopyWith<$Res> {
_$LastOrderTableEventCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of LastOrderTableEvent
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
abstract class _$$StartedImplCopyWith<$Res> {
factory _$$StartedImplCopyWith(
_$StartedImpl value, $Res Function(_$StartedImpl) then) =
__$$StartedImplCopyWithImpl<$Res>;
}
/// @nodoc
class __$$StartedImplCopyWithImpl<$Res>
extends _$LastOrderTableEventCopyWithImpl<$Res, _$StartedImpl>
implements _$$StartedImplCopyWith<$Res> {
__$$StartedImplCopyWithImpl(
_$StartedImpl _value, $Res Function(_$StartedImpl) _then)
: super(_value, _then);
/// Create a copy of LastOrderTableEvent
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
class _$StartedImpl implements _Started {
const _$StartedImpl();
@override
String toString() {
return 'LastOrderTableEvent.started()';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _$StartedImpl);
}
@override
int get hashCode => runtimeType.hashCode;
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() started,
required TResult Function(int tableNumber) lastOrderTable,
}) {
return started();
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? started,
TResult? Function(int tableNumber)? lastOrderTable,
}) {
return started?.call();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? started,
TResult Function(int tableNumber)? lastOrderTable,
required TResult orElse(),
}) {
if (started != null) {
return started();
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Started value) started,
required TResult Function(_LastOrderTable value) lastOrderTable,
}) {
return started(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Started value)? started,
TResult? Function(_LastOrderTable value)? lastOrderTable,
}) {
return started?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Started value)? started,
TResult Function(_LastOrderTable value)? lastOrderTable,
required TResult orElse(),
}) {
if (started != null) {
return started(this);
}
return orElse();
}
}
abstract class _Started implements LastOrderTableEvent {
const factory _Started() = _$StartedImpl;
}
/// @nodoc
abstract class _$$LastOrderTableImplCopyWith<$Res> {
factory _$$LastOrderTableImplCopyWith(_$LastOrderTableImpl value,
$Res Function(_$LastOrderTableImpl) then) =
__$$LastOrderTableImplCopyWithImpl<$Res>;
@useResult
$Res call({int tableNumber});
}
/// @nodoc
class __$$LastOrderTableImplCopyWithImpl<$Res>
extends _$LastOrderTableEventCopyWithImpl<$Res, _$LastOrderTableImpl>
implements _$$LastOrderTableImplCopyWith<$Res> {
__$$LastOrderTableImplCopyWithImpl(
_$LastOrderTableImpl _value, $Res Function(_$LastOrderTableImpl) _then)
: super(_value, _then);
/// Create a copy of LastOrderTableEvent
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? tableNumber = null,
}) {
return _then(_$LastOrderTableImpl(
null == tableNumber
? _value.tableNumber
: tableNumber // ignore: cast_nullable_to_non_nullable
as int,
));
}
}
/// @nodoc
class _$LastOrderTableImpl implements _LastOrderTable {
const _$LastOrderTableImpl(this.tableNumber);
@override
final int tableNumber;
@override
String toString() {
return 'LastOrderTableEvent.lastOrderTable(tableNumber: $tableNumber)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$LastOrderTableImpl &&
(identical(other.tableNumber, tableNumber) ||
other.tableNumber == tableNumber));
}
@override
int get hashCode => Object.hash(runtimeType, tableNumber);
/// Create a copy of LastOrderTableEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$LastOrderTableImplCopyWith<_$LastOrderTableImpl> get copyWith =>
__$$LastOrderTableImplCopyWithImpl<_$LastOrderTableImpl>(
this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() started,
required TResult Function(int tableNumber) lastOrderTable,
}) {
return lastOrderTable(tableNumber);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? started,
TResult? Function(int tableNumber)? lastOrderTable,
}) {
return lastOrderTable?.call(tableNumber);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? started,
TResult Function(int tableNumber)? lastOrderTable,
required TResult orElse(),
}) {
if (lastOrderTable != null) {
return lastOrderTable(tableNumber);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Started value) started,
required TResult Function(_LastOrderTable value) lastOrderTable,
}) {
return lastOrderTable(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Started value)? started,
TResult? Function(_LastOrderTable value)? lastOrderTable,
}) {
return lastOrderTable?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Started value)? started,
TResult Function(_LastOrderTable value)? lastOrderTable,
required TResult orElse(),
}) {
if (lastOrderTable != null) {
return lastOrderTable(this);
}
return orElse();
}
}
abstract class _LastOrderTable implements LastOrderTableEvent {
const factory _LastOrderTable(final int tableNumber) = _$LastOrderTableImpl;
int get tableNumber;
/// Create a copy of LastOrderTableEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
_$$LastOrderTableImplCopyWith<_$LastOrderTableImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
mixin _$LastOrderTableState {
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() loading,
required TResult Function(OrderModel? order) success,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function(OrderModel? order)? success,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function(OrderModel? order)? success,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Initial value) initial,
required TResult Function(_Loading value) loading,
required TResult Function(_Success value) success,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Initial value)? initial,
TResult? Function(_Loading value)? loading,
TResult? Function(_Success value)? success,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Initial value)? initial,
TResult Function(_Loading value)? loading,
TResult Function(_Success value)? success,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $LastOrderTableStateCopyWith<$Res> {
factory $LastOrderTableStateCopyWith(
LastOrderTableState value, $Res Function(LastOrderTableState) then) =
_$LastOrderTableStateCopyWithImpl<$Res, LastOrderTableState>;
}
/// @nodoc
class _$LastOrderTableStateCopyWithImpl<$Res, $Val extends LastOrderTableState>
implements $LastOrderTableStateCopyWith<$Res> {
_$LastOrderTableStateCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of LastOrderTableState
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
abstract class _$$InitialImplCopyWith<$Res> {
factory _$$InitialImplCopyWith(
_$InitialImpl value, $Res Function(_$InitialImpl) then) =
__$$InitialImplCopyWithImpl<$Res>;
}
/// @nodoc
class __$$InitialImplCopyWithImpl<$Res>
extends _$LastOrderTableStateCopyWithImpl<$Res, _$InitialImpl>
implements _$$InitialImplCopyWith<$Res> {
__$$InitialImplCopyWithImpl(
_$InitialImpl _value, $Res Function(_$InitialImpl) _then)
: super(_value, _then);
/// Create a copy of LastOrderTableState
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
class _$InitialImpl implements _Initial {
const _$InitialImpl();
@override
String toString() {
return 'LastOrderTableState.initial()';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _$InitialImpl);
}
@override
int get hashCode => runtimeType.hashCode;
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() loading,
required TResult Function(OrderModel? order) success,
}) {
return initial();
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function(OrderModel? order)? success,
}) {
return initial?.call();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function(OrderModel? order)? success,
required TResult orElse(),
}) {
if (initial != null) {
return initial();
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Initial value) initial,
required TResult Function(_Loading value) loading,
required TResult Function(_Success value) success,
}) {
return initial(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Initial value)? initial,
TResult? Function(_Loading value)? loading,
TResult? Function(_Success value)? success,
}) {
return initial?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Initial value)? initial,
TResult Function(_Loading value)? loading,
TResult Function(_Success value)? success,
required TResult orElse(),
}) {
if (initial != null) {
return initial(this);
}
return orElse();
}
}
abstract class _Initial implements LastOrderTableState {
const factory _Initial() = _$InitialImpl;
}
/// @nodoc
abstract class _$$LoadingImplCopyWith<$Res> {
factory _$$LoadingImplCopyWith(
_$LoadingImpl value, $Res Function(_$LoadingImpl) then) =
__$$LoadingImplCopyWithImpl<$Res>;
}
/// @nodoc
class __$$LoadingImplCopyWithImpl<$Res>
extends _$LastOrderTableStateCopyWithImpl<$Res, _$LoadingImpl>
implements _$$LoadingImplCopyWith<$Res> {
__$$LoadingImplCopyWithImpl(
_$LoadingImpl _value, $Res Function(_$LoadingImpl) _then)
: super(_value, _then);
/// Create a copy of LastOrderTableState
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
class _$LoadingImpl implements _Loading {
const _$LoadingImpl();
@override
String toString() {
return 'LastOrderTableState.loading()';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _$LoadingImpl);
}
@override
int get hashCode => runtimeType.hashCode;
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() loading,
required TResult Function(OrderModel? order) success,
}) {
return loading();
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function(OrderModel? order)? success,
}) {
return loading?.call();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function(OrderModel? order)? success,
required TResult orElse(),
}) {
if (loading != null) {
return loading();
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Initial value) initial,
required TResult Function(_Loading value) loading,
required TResult Function(_Success value) success,
}) {
return loading(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Initial value)? initial,
TResult? Function(_Loading value)? loading,
TResult? Function(_Success value)? success,
}) {
return loading?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Initial value)? initial,
TResult Function(_Loading value)? loading,
TResult Function(_Success value)? success,
required TResult orElse(),
}) {
if (loading != null) {
return loading(this);
}
return orElse();
}
}
abstract class _Loading implements LastOrderTableState {
const factory _Loading() = _$LoadingImpl;
}
/// @nodoc
abstract class _$$SuccessImplCopyWith<$Res> {
factory _$$SuccessImplCopyWith(
_$SuccessImpl value, $Res Function(_$SuccessImpl) then) =
__$$SuccessImplCopyWithImpl<$Res>;
@useResult
$Res call({OrderModel? order});
}
/// @nodoc
class __$$SuccessImplCopyWithImpl<$Res>
extends _$LastOrderTableStateCopyWithImpl<$Res, _$SuccessImpl>
implements _$$SuccessImplCopyWith<$Res> {
__$$SuccessImplCopyWithImpl(
_$SuccessImpl _value, $Res Function(_$SuccessImpl) _then)
: super(_value, _then);
/// Create a copy of LastOrderTableState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? order = freezed,
}) {
return _then(_$SuccessImpl(
freezed == order
? _value.order
: order // ignore: cast_nullable_to_non_nullable
as OrderModel?,
));
}
}
/// @nodoc
class _$SuccessImpl implements _Success {
const _$SuccessImpl(this.order);
@override
final OrderModel? order;
@override
String toString() {
return 'LastOrderTableState.success(order: $order)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$SuccessImpl &&
(identical(other.order, order) || other.order == order));
}
@override
int get hashCode => Object.hash(runtimeType, order);
/// Create a copy of LastOrderTableState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$SuccessImplCopyWith<_$SuccessImpl> get copyWith =>
__$$SuccessImplCopyWithImpl<_$SuccessImpl>(this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() loading,
required TResult Function(OrderModel? order) success,
}) {
return success(order);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function(OrderModel? order)? success,
}) {
return success?.call(order);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function(OrderModel? order)? success,
required TResult orElse(),
}) {
if (success != null) {
return success(order);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Initial value) initial,
required TResult Function(_Loading value) loading,
required TResult Function(_Success value) success,
}) {
return success(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Initial value)? initial,
TResult? Function(_Loading value)? loading,
TResult? Function(_Success value)? success,
}) {
return success?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Initial value)? initial,
TResult Function(_Loading value)? loading,
TResult Function(_Success value)? success,
required TResult orElse(),
}) {
if (success != null) {
return success(this);
}
return orElse();
}
}
abstract class _Success implements LastOrderTableState {
const factory _Success(final OrderModel? order) = _$SuccessImpl;
OrderModel? get order;
/// Create a copy of LastOrderTableState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
_$$SuccessImplCopyWith<_$SuccessImpl> get copyWith =>
throw _privateConstructorUsedError;
}
@@ -1,8 +0,0 @@
part of 'last_order_table_bloc.dart';
@freezed
class LastOrderTableEvent with _$LastOrderTableEvent {
const factory LastOrderTableEvent.started() = _Started;
const factory LastOrderTableEvent.lastOrderTable(int tableNumber) =
_LastOrderTable;
}
@@ -1,8 +0,0 @@
part of 'last_order_table_bloc.dart';
@freezed
class LastOrderTableState with _$LastOrderTableState {
const factory LastOrderTableState.initial() = _Initial;
const factory LastOrderTableState.loading() = _Loading;
const factory LastOrderTableState.success(OrderModel? order) = _Success;
}
@@ -1,25 +0,0 @@
import 'package:bloc/bloc.dart';
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
import 'package:enaklo_pos/presentation/home/models/order_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'day_sales_event.dart';
part 'day_sales_state.dart';
part 'day_sales_bloc.freezed.dart';
class DaySalesBloc extends Bloc<DaySalesEvent, DaySalesState> {
final ProductLocalDatasource datasource;
DaySalesBloc(this.datasource) : super(const _Initial()) {
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());
final result =
await datasource.getAllOrderByRange(event.startDate, event.endDate);
emit(_Loaded(result));
});
}
}
@@ -1,947 +0,0 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'day_sales_bloc.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
/// @nodoc
mixin _$DaySalesEvent {
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() started,
required TResult Function(DateTime date) getDaySales,
required TResult Function(DateTime startDate, DateTime endDate)
getRangeDateSales,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? started,
TResult? Function(DateTime date)? getDaySales,
TResult? Function(DateTime startDate, DateTime endDate)? getRangeDateSales,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? started,
TResult Function(DateTime date)? getDaySales,
TResult Function(DateTime startDate, DateTime endDate)? getRangeDateSales,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Started value) started,
required TResult Function(_GetDaySales value) getDaySales,
required TResult Function(_GetRangeDateSales value) getRangeDateSales,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Started value)? started,
TResult? Function(_GetDaySales value)? getDaySales,
TResult? Function(_GetRangeDateSales value)? getRangeDateSales,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Started value)? started,
TResult Function(_GetDaySales value)? getDaySales,
TResult Function(_GetRangeDateSales value)? getRangeDateSales,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $DaySalesEventCopyWith<$Res> {
factory $DaySalesEventCopyWith(
DaySalesEvent value, $Res Function(DaySalesEvent) then) =
_$DaySalesEventCopyWithImpl<$Res, DaySalesEvent>;
}
/// @nodoc
class _$DaySalesEventCopyWithImpl<$Res, $Val extends DaySalesEvent>
implements $DaySalesEventCopyWith<$Res> {
_$DaySalesEventCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of DaySalesEvent
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
abstract class _$$StartedImplCopyWith<$Res> {
factory _$$StartedImplCopyWith(
_$StartedImpl value, $Res Function(_$StartedImpl) then) =
__$$StartedImplCopyWithImpl<$Res>;
}
/// @nodoc
class __$$StartedImplCopyWithImpl<$Res>
extends _$DaySalesEventCopyWithImpl<$Res, _$StartedImpl>
implements _$$StartedImplCopyWith<$Res> {
__$$StartedImplCopyWithImpl(
_$StartedImpl _value, $Res Function(_$StartedImpl) _then)
: super(_value, _then);
/// Create a copy of DaySalesEvent
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
class _$StartedImpl implements _Started {
const _$StartedImpl();
@override
String toString() {
return 'DaySalesEvent.started()';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _$StartedImpl);
}
@override
int get hashCode => runtimeType.hashCode;
@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 started();
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? started,
TResult? Function(DateTime date)? getDaySales,
TResult? Function(DateTime startDate, DateTime endDate)? getRangeDateSales,
}) {
return started?.call();
}
@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 (started != null) {
return started();
}
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 started(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Started value)? started,
TResult? Function(_GetDaySales value)? getDaySales,
TResult? Function(_GetRangeDateSales value)? getRangeDateSales,
}) {
return started?.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 (started != null) {
return started(this);
}
return orElse();
}
}
abstract class _Started implements DaySalesEvent {
const factory _Started() = _$StartedImpl;
}
/// @nodoc
abstract class _$$GetDaySalesImplCopyWith<$Res> {
factory _$$GetDaySalesImplCopyWith(
_$GetDaySalesImpl value, $Res Function(_$GetDaySalesImpl) then) =
__$$GetDaySalesImplCopyWithImpl<$Res>;
@useResult
$Res call({DateTime date});
}
/// @nodoc
class __$$GetDaySalesImplCopyWithImpl<$Res>
extends _$DaySalesEventCopyWithImpl<$Res, _$GetDaySalesImpl>
implements _$$GetDaySalesImplCopyWith<$Res> {
__$$GetDaySalesImplCopyWithImpl(
_$GetDaySalesImpl _value, $Res Function(_$GetDaySalesImpl) _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? date = null,
}) {
return _then(_$GetDaySalesImpl(
null == date
? _value.date
: date // ignore: cast_nullable_to_non_nullable
as DateTime,
));
}
}
/// @nodoc
class _$GetDaySalesImpl implements _GetDaySales {
const _$GetDaySalesImpl(this.date);
@override
final DateTime date;
@override
String toString() {
return 'DaySalesEvent.getDaySales(date: $date)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$GetDaySalesImpl &&
(identical(other.date, date) || other.date == date));
}
@override
int get hashCode => Object.hash(runtimeType, date);
/// 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')
_$$GetDaySalesImplCopyWith<_$GetDaySalesImpl> get copyWith =>
__$$GetDaySalesImplCopyWithImpl<_$GetDaySalesImpl>(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 getDaySales(date);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? started,
TResult? Function(DateTime date)? getDaySales,
TResult? Function(DateTime startDate, DateTime endDate)? getRangeDateSales,
}) {
return getDaySales?.call(date);
}
@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 (getDaySales != null) {
return getDaySales(date);
}
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 getDaySales(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Started value)? started,
TResult? Function(_GetDaySales value)? getDaySales,
TResult? Function(_GetRangeDateSales value)? getRangeDateSales,
}) {
return getDaySales?.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 (getDaySales != null) {
return getDaySales(this);
}
return orElse();
}
}
abstract class _GetDaySales implements DaySalesEvent {
const factory _GetDaySales(final DateTime date) = _$GetDaySalesImpl;
DateTime get date;
/// Create a copy of DaySalesEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
_$$GetDaySalesImplCopyWith<_$GetDaySalesImpl> get copyWith =>
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
mixin _$DaySalesState {
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() loading,
required TResult Function(List<OrderModel> orders) loaded,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function(List<OrderModel> orders)? loaded,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function(List<OrderModel> orders)? loaded,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Initial value) initial,
required TResult Function(_Loading value) loading,
required TResult Function(_Loaded value) loaded,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Initial value)? initial,
TResult? Function(_Loading value)? loading,
TResult? Function(_Loaded value)? loaded,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Initial value)? initial,
TResult Function(_Loading value)? loading,
TResult Function(_Loaded value)? loaded,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $DaySalesStateCopyWith<$Res> {
factory $DaySalesStateCopyWith(
DaySalesState value, $Res Function(DaySalesState) then) =
_$DaySalesStateCopyWithImpl<$Res, DaySalesState>;
}
/// @nodoc
class _$DaySalesStateCopyWithImpl<$Res, $Val extends DaySalesState>
implements $DaySalesStateCopyWith<$Res> {
_$DaySalesStateCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of DaySalesState
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
abstract class _$$InitialImplCopyWith<$Res> {
factory _$$InitialImplCopyWith(
_$InitialImpl value, $Res Function(_$InitialImpl) then) =
__$$InitialImplCopyWithImpl<$Res>;
}
/// @nodoc
class __$$InitialImplCopyWithImpl<$Res>
extends _$DaySalesStateCopyWithImpl<$Res, _$InitialImpl>
implements _$$InitialImplCopyWith<$Res> {
__$$InitialImplCopyWithImpl(
_$InitialImpl _value, $Res Function(_$InitialImpl) _then)
: super(_value, _then);
/// Create a copy of DaySalesState
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
class _$InitialImpl implements _Initial {
const _$InitialImpl();
@override
String toString() {
return 'DaySalesState.initial()';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _$InitialImpl);
}
@override
int get hashCode => runtimeType.hashCode;
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() loading,
required TResult Function(List<OrderModel> orders) loaded,
}) {
return initial();
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function(List<OrderModel> orders)? loaded,
}) {
return initial?.call();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function(List<OrderModel> orders)? loaded,
required TResult orElse(),
}) {
if (initial != null) {
return initial();
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Initial value) initial,
required TResult Function(_Loading value) loading,
required TResult Function(_Loaded value) loaded,
}) {
return initial(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Initial value)? initial,
TResult? Function(_Loading value)? loading,
TResult? Function(_Loaded value)? loaded,
}) {
return initial?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Initial value)? initial,
TResult Function(_Loading value)? loading,
TResult Function(_Loaded value)? loaded,
required TResult orElse(),
}) {
if (initial != null) {
return initial(this);
}
return orElse();
}
}
abstract class _Initial implements DaySalesState {
const factory _Initial() = _$InitialImpl;
}
/// @nodoc
abstract class _$$LoadingImplCopyWith<$Res> {
factory _$$LoadingImplCopyWith(
_$LoadingImpl value, $Res Function(_$LoadingImpl) then) =
__$$LoadingImplCopyWithImpl<$Res>;
}
/// @nodoc
class __$$LoadingImplCopyWithImpl<$Res>
extends _$DaySalesStateCopyWithImpl<$Res, _$LoadingImpl>
implements _$$LoadingImplCopyWith<$Res> {
__$$LoadingImplCopyWithImpl(
_$LoadingImpl _value, $Res Function(_$LoadingImpl) _then)
: super(_value, _then);
/// Create a copy of DaySalesState
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
class _$LoadingImpl implements _Loading {
const _$LoadingImpl();
@override
String toString() {
return 'DaySalesState.loading()';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _$LoadingImpl);
}
@override
int get hashCode => runtimeType.hashCode;
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() loading,
required TResult Function(List<OrderModel> orders) loaded,
}) {
return loading();
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function(List<OrderModel> orders)? loaded,
}) {
return loading?.call();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function(List<OrderModel> orders)? loaded,
required TResult orElse(),
}) {
if (loading != null) {
return loading();
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Initial value) initial,
required TResult Function(_Loading value) loading,
required TResult Function(_Loaded value) loaded,
}) {
return loading(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Initial value)? initial,
TResult? Function(_Loading value)? loading,
TResult? Function(_Loaded value)? loaded,
}) {
return loading?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Initial value)? initial,
TResult Function(_Loading value)? loading,
TResult Function(_Loaded value)? loaded,
required TResult orElse(),
}) {
if (loading != null) {
return loading(this);
}
return orElse();
}
}
abstract class _Loading implements DaySalesState {
const factory _Loading() = _$LoadingImpl;
}
/// @nodoc
abstract class _$$LoadedImplCopyWith<$Res> {
factory _$$LoadedImplCopyWith(
_$LoadedImpl value, $Res Function(_$LoadedImpl) then) =
__$$LoadedImplCopyWithImpl<$Res>;
@useResult
$Res call({List<OrderModel> orders});
}
/// @nodoc
class __$$LoadedImplCopyWithImpl<$Res>
extends _$DaySalesStateCopyWithImpl<$Res, _$LoadedImpl>
implements _$$LoadedImplCopyWith<$Res> {
__$$LoadedImplCopyWithImpl(
_$LoadedImpl _value, $Res Function(_$LoadedImpl) _then)
: super(_value, _then);
/// Create a copy of DaySalesState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? orders = null,
}) {
return _then(_$LoadedImpl(
null == orders
? _value._orders
: orders // ignore: cast_nullable_to_non_nullable
as List<OrderModel>,
));
}
}
/// @nodoc
class _$LoadedImpl implements _Loaded {
const _$LoadedImpl(final List<OrderModel> orders) : _orders = orders;
final List<OrderModel> _orders;
@override
List<OrderModel> get orders {
if (_orders is EqualUnmodifiableListView) return _orders;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_orders);
}
@override
String toString() {
return 'DaySalesState.loaded(orders: $orders)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$LoadedImpl &&
const DeepCollectionEquality().equals(other._orders, _orders));
}
@override
int get hashCode =>
Object.hash(runtimeType, const DeepCollectionEquality().hash(_orders));
/// Create a copy of DaySalesState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$LoadedImplCopyWith<_$LoadedImpl> get copyWith =>
__$$LoadedImplCopyWithImpl<_$LoadedImpl>(this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() loading,
required TResult Function(List<OrderModel> orders) loaded,
}) {
return loaded(orders);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function(List<OrderModel> orders)? loaded,
}) {
return loaded?.call(orders);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function(List<OrderModel> orders)? loaded,
required TResult orElse(),
}) {
if (loaded != null) {
return loaded(orders);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Initial value) initial,
required TResult Function(_Loading value) loading,
required TResult Function(_Loaded value) loaded,
}) {
return loaded(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Initial value)? initial,
TResult? Function(_Loading value)? loading,
TResult? Function(_Loaded value)? loaded,
}) {
return loaded?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Initial value)? initial,
TResult Function(_Loading value)? loading,
TResult Function(_Loaded value)? loaded,
required TResult orElse(),
}) {
if (loaded != null) {
return loaded(this);
}
return orElse();
}
}
abstract class _Loaded implements DaySalesState {
const factory _Loaded(final List<OrderModel> orders) = _$LoadedImpl;
List<OrderModel> get orders;
/// Create a copy of DaySalesState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
_$$LoadedImplCopyWith<_$LoadedImpl> get copyWith =>
throw _privateConstructorUsedError;
}
@@ -1,13 +0,0 @@
part of 'day_sales_bloc.dart';
@freezed
class DaySalesEvent with _$DaySalesEvent {
const factory DaySalesEvent.started() = _Started;
const factory DaySalesEvent.getDaySales(
DateTime date,
) = _GetDaySales;
const factory DaySalesEvent.getRangeDateSales(
DateTime startDate,
DateTime endDate,
) = _GetRangeDateSales;
}
@@ -1,8 +0,0 @@
part of 'day_sales_bloc.dart';
@freezed
class DaySalesState with _$DaySalesState {
const factory DaySalesState.initial() = _Initial;
const factory DaySalesState.loading() = _Loading;
const factory DaySalesState.loaded(List<OrderModel> orders) = _Loaded;
}
+8 -2
View File
@@ -271,18 +271,24 @@ class _SalesPageState extends State<SalesPage> {
nominalBayar: orderDetail?.totalPaid ?? 0,
kembalian: 0,
productQuantity: orderDetail!.orderItems!
.where((item) =>
item.status != 'cancelled')
.toList()
.toProductQuantities(),
);
} else {
onPrint(
onPrintBill(
context,
productQuantity: orderDetail!.orderItems!
.where((item) =>
item.status != 'cancelled')
.toList()
.toProductQuantities(),
order: orderDetail!,
);
}
},
label: 'Print',
label: 'Print Bill',
icon: Icon(
Icons.print,
),
@@ -1,7 +1,5 @@
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
import 'package:enaklo_pos/data/datasources/printer/printer_local_datasource.dart';
import 'package:enaklo_pos/data/models/response/print_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@@ -13,10 +11,10 @@ class CreatePrinterBloc extends Bloc<CreatePrinterEvent, CreatePrinterState> {
CreatePrinterBloc() : super(_Initial()) {
on<_CreatePrinter>((event, emit) async {
emit(_Loading());
await ProductLocalDatasource.instance.createPrinter(
await PrinterLocalDatasource.instance.createPrinter(
event.print,
);
emit(_Success('Create Table Success'));
emit(_Success('Create Printer Success'));
});
}
}
@@ -1,5 +1,5 @@
import 'package:bloc/bloc.dart';
import 'package:enaklo_pos/data/datasources/category_remote_datasource.dart';
import 'package:enaklo_pos/data/datasources/category/category_remote_datasource.dart';
import 'package:enaklo_pos/data/models/response/category_response_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@@ -1,6 +1,5 @@
import 'package:bloc/bloc.dart';
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
import 'package:enaklo_pos/data/datasources/printer/printer_local_datasource.dart';
import 'package:enaklo_pos/data/models/response/print_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@@ -13,7 +12,7 @@ class GetPrinterBarBloc extends Bloc<GetPrinterBarEvent, GetPrinterBarState> {
on<_Get>((event, emit) async {
emit(_Loading());
final result =
await ProductLocalDatasource.instance.getPrinterByCode('bar');
await PrinterLocalDatasource.instance.getPrinterByCode('bar');
emit(_Success(result));
});
}
@@ -1,9 +1,8 @@
import 'package:bloc/bloc.dart';
import 'package:enaklo_pos/data/datasources/printer/printer_local_datasource.dart';
import 'package:enaklo_pos/data/models/response/print_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import '../../../../data/datasources/product_local_datasource.dart';
part 'get_printer_checker_event.dart';
part 'get_printer_checker_state.dart';
part 'get_printer_checker_bloc.freezed.dart';
@@ -14,7 +13,7 @@ class GetPrinterCheckerBloc
on<_Get>((event, emit) async {
emit(_Loading());
final result =
await ProductLocalDatasource.instance.getPrinterByCode('checker');
await PrinterLocalDatasource.instance.getPrinterByCode('checker');
emit(_Success(result));
});
}
@@ -1,5 +1,5 @@
import 'package:bloc/bloc.dart';
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
import 'package:enaklo_pos/data/datasources/printer/printer_local_datasource.dart';
import 'package:enaklo_pos/data/models/response/print_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@@ -13,7 +13,7 @@ class GetPrinterKitchenBloc
on<_Get>((event, emit) async {
emit(_Loading());
final result =
await ProductLocalDatasource.instance.getPrinterByCode('kitchen');
await PrinterLocalDatasource.instance.getPrinterByCode('kitchen');
emit(_Success(result));
});
}
@@ -1,5 +1,5 @@
import 'package:bloc/bloc.dart';
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
import 'package:enaklo_pos/data/datasources/printer/printer_local_datasource.dart';
import 'package:enaklo_pos/data/models/response/print_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@@ -13,7 +13,7 @@ class GetPrinterReceiptBloc
on<_Get>((event, emit) async {
emit(_Loading());
final result =
await ProductLocalDatasource.instance.getPrinterByCode('receipt');
await PrinterLocalDatasource.instance.getPrinterByCode('receipt');
emit(_Success(result));
});
}
@@ -1,5 +1,5 @@
import 'package:bloc/bloc.dart';
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
import 'package:enaklo_pos/data/datasources/printer/printer_local_datasource.dart';
import 'package:enaklo_pos/data/models/response/print_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@@ -13,7 +13,7 @@ class GetPrinterTicketBloc
on<_Get>((event, emit) async {
emit(_Loading());
final result =
await ProductLocalDatasource.instance.getPrinterByCode('ticket');
await PrinterLocalDatasource.instance.getPrinterByCode('ticket');
emit(_Success(result));
});
}
@@ -1,42 +0,0 @@
import 'dart:developer';
import 'package:bloc/bloc.dart';
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:enaklo_pos/data/datasources/order_remote_datasource.dart';
part 'sync_order_bloc.freezed.dart';
part 'sync_order_event.dart';
part 'sync_order_state.dart';
class SyncOrderBloc extends Bloc<SyncOrderEvent, SyncOrderState> {
final OrderRemoteDatasource orderRemoteDatasource;
SyncOrderBloc(
this.orderRemoteDatasource,
) : super(const _Initial()) {
on<_SyncOrder>((event, emit) async {
emit(const _Loading());
log("🔄 SyncOrderBloc: Starting sync process");
final dataOrderNotSynced =
await ProductLocalDatasource.instance.getOrderByIsNotSync();
log("🔄 SyncOrderBloc: Found ${dataOrderNotSynced.length} orders to sync");
for (var order in dataOrderNotSynced) {
final orderItem = await ProductLocalDatasource.instance
.getOrderItemByOrderId(order.id!);
final newOrder = order.copyWith(orderItems: orderItem);
log("🔄 SyncOrderBloc: Syncing order ${order.id} to API");
log("Order: ${newOrder.toMap()}");
final result = await orderRemoteDatasource.saveOrder(newOrder);
if (result) {
await ProductLocalDatasource.instance.updateOrderIsSync(order.id!);
} else {
emit(const _Error('Sync Order Failed'));
return;
}
}
emit(const _Loaded());
});
}
}
@@ -1,866 +0,0 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'sync_order_bloc.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
/// @nodoc
mixin _$SyncOrderEvent {
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() started,
required TResult Function() syncOrder,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? started,
TResult? Function()? syncOrder,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? started,
TResult Function()? syncOrder,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Started value) started,
required TResult Function(_SyncOrder value) syncOrder,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Started value)? started,
TResult? Function(_SyncOrder value)? syncOrder,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Started value)? started,
TResult Function(_SyncOrder value)? syncOrder,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $SyncOrderEventCopyWith<$Res> {
factory $SyncOrderEventCopyWith(
SyncOrderEvent value, $Res Function(SyncOrderEvent) then) =
_$SyncOrderEventCopyWithImpl<$Res, SyncOrderEvent>;
}
/// @nodoc
class _$SyncOrderEventCopyWithImpl<$Res, $Val extends SyncOrderEvent>
implements $SyncOrderEventCopyWith<$Res> {
_$SyncOrderEventCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of SyncOrderEvent
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
abstract class _$$StartedImplCopyWith<$Res> {
factory _$$StartedImplCopyWith(
_$StartedImpl value, $Res Function(_$StartedImpl) then) =
__$$StartedImplCopyWithImpl<$Res>;
}
/// @nodoc
class __$$StartedImplCopyWithImpl<$Res>
extends _$SyncOrderEventCopyWithImpl<$Res, _$StartedImpl>
implements _$$StartedImplCopyWith<$Res> {
__$$StartedImplCopyWithImpl(
_$StartedImpl _value, $Res Function(_$StartedImpl) _then)
: super(_value, _then);
/// Create a copy of SyncOrderEvent
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
class _$StartedImpl implements _Started {
const _$StartedImpl();
@override
String toString() {
return 'SyncOrderEvent.started()';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _$StartedImpl);
}
@override
int get hashCode => runtimeType.hashCode;
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() started,
required TResult Function() syncOrder,
}) {
return started();
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? started,
TResult? Function()? syncOrder,
}) {
return started?.call();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? started,
TResult Function()? syncOrder,
required TResult orElse(),
}) {
if (started != null) {
return started();
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Started value) started,
required TResult Function(_SyncOrder value) syncOrder,
}) {
return started(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Started value)? started,
TResult? Function(_SyncOrder value)? syncOrder,
}) {
return started?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Started value)? started,
TResult Function(_SyncOrder value)? syncOrder,
required TResult orElse(),
}) {
if (started != null) {
return started(this);
}
return orElse();
}
}
abstract class _Started implements SyncOrderEvent {
const factory _Started() = _$StartedImpl;
}
/// @nodoc
abstract class _$$SyncOrderImplCopyWith<$Res> {
factory _$$SyncOrderImplCopyWith(
_$SyncOrderImpl value, $Res Function(_$SyncOrderImpl) then) =
__$$SyncOrderImplCopyWithImpl<$Res>;
}
/// @nodoc
class __$$SyncOrderImplCopyWithImpl<$Res>
extends _$SyncOrderEventCopyWithImpl<$Res, _$SyncOrderImpl>
implements _$$SyncOrderImplCopyWith<$Res> {
__$$SyncOrderImplCopyWithImpl(
_$SyncOrderImpl _value, $Res Function(_$SyncOrderImpl) _then)
: super(_value, _then);
/// Create a copy of SyncOrderEvent
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
class _$SyncOrderImpl implements _SyncOrder {
const _$SyncOrderImpl();
@override
String toString() {
return 'SyncOrderEvent.syncOrder()';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _$SyncOrderImpl);
}
@override
int get hashCode => runtimeType.hashCode;
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() started,
required TResult Function() syncOrder,
}) {
return syncOrder();
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? started,
TResult? Function()? syncOrder,
}) {
return syncOrder?.call();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? started,
TResult Function()? syncOrder,
required TResult orElse(),
}) {
if (syncOrder != null) {
return syncOrder();
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Started value) started,
required TResult Function(_SyncOrder value) syncOrder,
}) {
return syncOrder(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Started value)? started,
TResult? Function(_SyncOrder value)? syncOrder,
}) {
return syncOrder?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Started value)? started,
TResult Function(_SyncOrder value)? syncOrder,
required TResult orElse(),
}) {
if (syncOrder != null) {
return syncOrder(this);
}
return orElse();
}
}
abstract class _SyncOrder implements SyncOrderEvent {
const factory _SyncOrder() = _$SyncOrderImpl;
}
/// @nodoc
mixin _$SyncOrderState {
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() loading,
required TResult Function() loaded,
required TResult Function(String message) error,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function()? loaded,
TResult? Function(String message)? error,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function()? loaded,
TResult Function(String message)? error,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Initial value) initial,
required TResult Function(_Loading value) loading,
required TResult Function(_Loaded value) loaded,
required TResult Function(_Error value) error,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Initial value)? initial,
TResult? Function(_Loading value)? loading,
TResult? Function(_Loaded value)? loaded,
TResult? Function(_Error value)? error,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Initial value)? initial,
TResult Function(_Loading value)? loading,
TResult Function(_Loaded value)? loaded,
TResult Function(_Error value)? error,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $SyncOrderStateCopyWith<$Res> {
factory $SyncOrderStateCopyWith(
SyncOrderState value, $Res Function(SyncOrderState) then) =
_$SyncOrderStateCopyWithImpl<$Res, SyncOrderState>;
}
/// @nodoc
class _$SyncOrderStateCopyWithImpl<$Res, $Val extends SyncOrderState>
implements $SyncOrderStateCopyWith<$Res> {
_$SyncOrderStateCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of SyncOrderState
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
abstract class _$$InitialImplCopyWith<$Res> {
factory _$$InitialImplCopyWith(
_$InitialImpl value, $Res Function(_$InitialImpl) then) =
__$$InitialImplCopyWithImpl<$Res>;
}
/// @nodoc
class __$$InitialImplCopyWithImpl<$Res>
extends _$SyncOrderStateCopyWithImpl<$Res, _$InitialImpl>
implements _$$InitialImplCopyWith<$Res> {
__$$InitialImplCopyWithImpl(
_$InitialImpl _value, $Res Function(_$InitialImpl) _then)
: super(_value, _then);
/// Create a copy of SyncOrderState
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
class _$InitialImpl implements _Initial {
const _$InitialImpl();
@override
String toString() {
return 'SyncOrderState.initial()';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _$InitialImpl);
}
@override
int get hashCode => runtimeType.hashCode;
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() loading,
required TResult Function() loaded,
required TResult Function(String message) error,
}) {
return initial();
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function()? loaded,
TResult? Function(String message)? error,
}) {
return initial?.call();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function()? loaded,
TResult Function(String message)? error,
required TResult orElse(),
}) {
if (initial != null) {
return initial();
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Initial value) initial,
required TResult Function(_Loading value) loading,
required TResult Function(_Loaded value) loaded,
required TResult Function(_Error value) error,
}) {
return initial(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Initial value)? initial,
TResult? Function(_Loading value)? loading,
TResult? Function(_Loaded value)? loaded,
TResult? Function(_Error value)? error,
}) {
return initial?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Initial value)? initial,
TResult Function(_Loading value)? loading,
TResult Function(_Loaded value)? loaded,
TResult Function(_Error value)? error,
required TResult orElse(),
}) {
if (initial != null) {
return initial(this);
}
return orElse();
}
}
abstract class _Initial implements SyncOrderState {
const factory _Initial() = _$InitialImpl;
}
/// @nodoc
abstract class _$$LoadingImplCopyWith<$Res> {
factory _$$LoadingImplCopyWith(
_$LoadingImpl value, $Res Function(_$LoadingImpl) then) =
__$$LoadingImplCopyWithImpl<$Res>;
}
/// @nodoc
class __$$LoadingImplCopyWithImpl<$Res>
extends _$SyncOrderStateCopyWithImpl<$Res, _$LoadingImpl>
implements _$$LoadingImplCopyWith<$Res> {
__$$LoadingImplCopyWithImpl(
_$LoadingImpl _value, $Res Function(_$LoadingImpl) _then)
: super(_value, _then);
/// Create a copy of SyncOrderState
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
class _$LoadingImpl implements _Loading {
const _$LoadingImpl();
@override
String toString() {
return 'SyncOrderState.loading()';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _$LoadingImpl);
}
@override
int get hashCode => runtimeType.hashCode;
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() loading,
required TResult Function() loaded,
required TResult Function(String message) error,
}) {
return loading();
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function()? loaded,
TResult? Function(String message)? error,
}) {
return loading?.call();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function()? loaded,
TResult Function(String message)? error,
required TResult orElse(),
}) {
if (loading != null) {
return loading();
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Initial value) initial,
required TResult Function(_Loading value) loading,
required TResult Function(_Loaded value) loaded,
required TResult Function(_Error value) error,
}) {
return loading(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Initial value)? initial,
TResult? Function(_Loading value)? loading,
TResult? Function(_Loaded value)? loaded,
TResult? Function(_Error value)? error,
}) {
return loading?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Initial value)? initial,
TResult Function(_Loading value)? loading,
TResult Function(_Loaded value)? loaded,
TResult Function(_Error value)? error,
required TResult orElse(),
}) {
if (loading != null) {
return loading(this);
}
return orElse();
}
}
abstract class _Loading implements SyncOrderState {
const factory _Loading() = _$LoadingImpl;
}
/// @nodoc
abstract class _$$LoadedImplCopyWith<$Res> {
factory _$$LoadedImplCopyWith(
_$LoadedImpl value, $Res Function(_$LoadedImpl) then) =
__$$LoadedImplCopyWithImpl<$Res>;
}
/// @nodoc
class __$$LoadedImplCopyWithImpl<$Res>
extends _$SyncOrderStateCopyWithImpl<$Res, _$LoadedImpl>
implements _$$LoadedImplCopyWith<$Res> {
__$$LoadedImplCopyWithImpl(
_$LoadedImpl _value, $Res Function(_$LoadedImpl) _then)
: super(_value, _then);
/// Create a copy of SyncOrderState
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
class _$LoadedImpl implements _Loaded {
const _$LoadedImpl();
@override
String toString() {
return 'SyncOrderState.loaded()';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _$LoadedImpl);
}
@override
int get hashCode => runtimeType.hashCode;
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() loading,
required TResult Function() loaded,
required TResult Function(String message) error,
}) {
return loaded();
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function()? loaded,
TResult? Function(String message)? error,
}) {
return loaded?.call();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function()? loaded,
TResult Function(String message)? error,
required TResult orElse(),
}) {
if (loaded != null) {
return loaded();
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Initial value) initial,
required TResult Function(_Loading value) loading,
required TResult Function(_Loaded value) loaded,
required TResult Function(_Error value) error,
}) {
return loaded(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Initial value)? initial,
TResult? Function(_Loading value)? loading,
TResult? Function(_Loaded value)? loaded,
TResult? Function(_Error value)? error,
}) {
return loaded?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Initial value)? initial,
TResult Function(_Loading value)? loading,
TResult Function(_Loaded value)? loaded,
TResult Function(_Error value)? error,
required TResult orElse(),
}) {
if (loaded != null) {
return loaded(this);
}
return orElse();
}
}
abstract class _Loaded implements SyncOrderState {
const factory _Loaded() = _$LoadedImpl;
}
/// @nodoc
abstract class _$$ErrorImplCopyWith<$Res> {
factory _$$ErrorImplCopyWith(
_$ErrorImpl value, $Res Function(_$ErrorImpl) then) =
__$$ErrorImplCopyWithImpl<$Res>;
@useResult
$Res call({String message});
}
/// @nodoc
class __$$ErrorImplCopyWithImpl<$Res>
extends _$SyncOrderStateCopyWithImpl<$Res, _$ErrorImpl>
implements _$$ErrorImplCopyWith<$Res> {
__$$ErrorImplCopyWithImpl(
_$ErrorImpl _value, $Res Function(_$ErrorImpl) _then)
: super(_value, _then);
/// Create a copy of SyncOrderState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? message = null,
}) {
return _then(_$ErrorImpl(
null == message
? _value.message
: message // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
class _$ErrorImpl implements _Error {
const _$ErrorImpl(this.message);
@override
final String message;
@override
String toString() {
return 'SyncOrderState.error(message: $message)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$ErrorImpl &&
(identical(other.message, message) || other.message == message));
}
@override
int get hashCode => Object.hash(runtimeType, message);
/// Create a copy of SyncOrderState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$ErrorImplCopyWith<_$ErrorImpl> get copyWith =>
__$$ErrorImplCopyWithImpl<_$ErrorImpl>(this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() loading,
required TResult Function() loaded,
required TResult Function(String message) error,
}) {
return error(message);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function()? loaded,
TResult? Function(String message)? error,
}) {
return error?.call(message);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function()? loaded,
TResult Function(String message)? error,
required TResult orElse(),
}) {
if (error != null) {
return error(message);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Initial value) initial,
required TResult Function(_Loading value) loading,
required TResult Function(_Loaded value) loaded,
required TResult Function(_Error value) error,
}) {
return error(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Initial value)? initial,
TResult? Function(_Loading value)? loading,
TResult? Function(_Loaded value)? loaded,
TResult? Function(_Error value)? error,
}) {
return error?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Initial value)? initial,
TResult Function(_Loading value)? loading,
TResult Function(_Loaded value)? loaded,
TResult Function(_Error value)? error,
required TResult orElse(),
}) {
if (error != null) {
return error(this);
}
return orElse();
}
}
abstract class _Error implements SyncOrderState {
const factory _Error(final String message) = _$ErrorImpl;
String get message;
/// Create a copy of SyncOrderState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
_$$ErrorImplCopyWith<_$ErrorImpl> get copyWith =>
throw _privateConstructorUsedError;
}
@@ -1,7 +0,0 @@
part of 'sync_order_bloc.dart';
@freezed
class SyncOrderEvent with _$SyncOrderEvent {
const factory SyncOrderEvent.started() = _Started;
const factory SyncOrderEvent.syncOrder() = _SyncOrder;
}
@@ -1,10 +0,0 @@
part of 'sync_order_bloc.dart';
@freezed
class SyncOrderState with _$SyncOrderState {
const factory SyncOrderState.initial() = _Initial;
const factory SyncOrderState.loading() = _Loading;
const factory SyncOrderState.loaded() =
_Loaded;
const factory SyncOrderState.error(String message) = _Error;
}
@@ -1,5 +1,5 @@
import 'package:bloc/bloc.dart';
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
import 'package:enaklo_pos/data/datasources/printer/printer_local_datasource.dart';
import 'package:enaklo_pos/data/models/response/print_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@@ -11,7 +11,7 @@ class UpdatePrinterBloc extends Bloc<UpdatePrinterEvent, UpdatePrinterState> {
UpdatePrinterBloc() : super(_Initial()) {
on<_UpdatePrinter>((event, emit) async {
emit(_Loading());
await ProductLocalDatasource.instance.updatePrinter(
await PrinterLocalDatasource.instance.updatePrinter(
event.print,
event.print.id!,
);
@@ -17,15 +17,144 @@ enum PrinterType {
}
class PrinterModel {
final int? id;
final String code;
final String name;
final String ipAddress;
final String size;
final PrinterType type;
final DateTime? createdAt;
final DateTime? updatedAt;
PrinterModel({
this.id,
required this.code,
required this.name,
required this.ipAddress,
required this.size,
required this.type,
this.createdAt,
this.updatedAt,
});
// Factory constructor to create PrinterModel from database map
factory PrinterModel.fromMap(Map<String, dynamic> map) {
return PrinterModel(
id: map['id'] as int?,
code: map['code'] as String,
name: map['name'] as String,
ipAddress: map['ip_address'] as String,
size: map['size'] as String,
type: PrinterType.fromValue(map['type'] as String),
createdAt: map['created_at'] != null
? DateTime.parse(map['created_at'] as String)
: null,
updatedAt: map['updated_at'] != null
? DateTime.parse(map['updated_at'] as String)
: null,
);
}
// Convert to map for database insertion (excluding id, including timestamps)
Map<String, dynamic> toMapForInsert() {
final now = DateTime.now().toIso8601String();
return {
'code': code,
'name': name,
'ip_address': ipAddress,
'size': size,
'type': type.value,
'created_at': now,
'updated_at': now,
};
}
// Convert to map for database update (excluding id and created_at)
Map<String, dynamic> toMapForUpdate() {
return {
'code': code,
'name': name,
'ip_address': ipAddress,
'size': size,
'type': type.value,
'updated_at': DateTime.now().toIso8601String(),
};
}
// Convert to complete map (including id)
Map<String, dynamic> toMap() {
return {
'id': id,
'code': code,
'name': name,
'ip_address': ipAddress,
'size': size,
'type': type.value,
'created_at': createdAt?.toIso8601String(),
'updated_at': updatedAt?.toIso8601String(),
};
}
// Copy with method for creating modified instances
PrinterModel copyWith({
int? id,
String? code,
String? name,
String? ipAddress,
String? size,
PrinterType? type,
DateTime? createdAt,
DateTime? updatedAt,
}) {
return PrinterModel(
id: id ?? this.id,
code: code ?? this.code,
name: name ?? this.name,
ipAddress: ipAddress ?? this.ipAddress,
size: size ?? this.size,
type: type ?? this.type,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
);
}
// Equality and hashCode for comparing instances
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is PrinterModel &&
other.id == id &&
other.code == code &&
other.name == name &&
other.ipAddress == ipAddress &&
other.size == size &&
other.type == type;
}
@override
int get hashCode {
return Object.hash(id, code, name, ipAddress, size, type);
}
// String representation for debugging
@override
String toString() {
return 'PrinterModel(id: $id, code: $code, name: $name, ipAddress: $ipAddress, size: $size, type: ${type.value}, createdAt: $createdAt, updatedAt: $updatedAt)';
}
// Validation methods
bool get isValid {
return code.isNotEmpty &&
name.isNotEmpty &&
ipAddress.isNotEmpty &&
size.isNotEmpty;
}
String? get validationError {
if (code.isEmpty) return 'Printer code cannot be empty';
if (name.isEmpty) return 'Printer name cannot be empty';
if (ipAddress.isEmpty) return 'IP address cannot be empty';
if (size.isEmpty) return 'Printer size cannot be empty';
return null;
}
}
@@ -2,6 +2,7 @@ import 'package:enaklo_pos/core/constants/colors.dart';
import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
import 'package:enaklo_pos/presentation/setting/pages/printer_page.dart';
import 'package:enaklo_pos/presentation/setting/pages/setting_tile.dart';
import 'package:enaklo_pos/presentation/setting/pages/sync_page.dart';
import 'package:flutter/material.dart';
class SettingPage extends StatefulWidget {
@@ -84,6 +85,14 @@ class _SettingPageState extends State<SettingPage> {
icon: Icons.print_outlined,
onTap: () => indexValue(0),
),
SettingTile(
index: 1,
currentIndex: currentIndex,
title: 'Sinkronisasi',
subtitle: 'Sinkronisasi data',
icon: Icons.sync_outlined,
onTap: () => indexValue(1),
),
],
),
),
@@ -101,6 +110,7 @@ class _SettingPageState extends State<SettingPage> {
index: currentIndex,
children: [
SettingPrinterPage(),
SettingSyncPage(),
],
),
),
@@ -4,8 +4,6 @@ import 'package:enaklo_pos/core/constants/colors.dart';
import 'package:enaklo_pos/presentation/setting/widgets/settings_title.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
import 'package:enaklo_pos/presentation/setting/bloc/sync_order/sync_order_bloc.dart';
import 'package:enaklo_pos/presentation/setting/bloc/sync_product/sync_product_bloc.dart';
class SyncDataPage extends StatefulWidget {
@@ -114,53 +112,53 @@ class _SyncDataPageState extends State<SyncDataPage> {
fontWeight: FontWeight.w500,
),
),
BlocConsumer<SyncOrderBloc, SyncOrderState>(
listener: (context, state) {
state.maybeWhen(
orElse: () {},
error: (message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message),
backgroundColor: Colors.red,
),
);
},
loaded: () {
// ScaffoldMessenger.of(context).showSnackBar(
// const SnackBar(
// content: Text('Sync Order Success'),
// backgroundColor: Colors.green,
// ),
// );
},
);
},
builder: (context, state) {
return state.maybeWhen(
orElse: () {
return Button.filled(
width: 100,
height: 40,
onPressed: () {
log("🔘 Sync Order button pressed");
log("🔘 SyncOrderBloc instance: ${context.read<SyncOrderBloc>()}");
context
.read<SyncOrderBloc>()
.add(const SyncOrderEvent.syncOrder());
log("🔘 SyncOrderEvent.syncOrder dispatched");
},
label: 'Sinkronasikan',
);
},
loading: () {
return const Center(
child: CircularProgressIndicator(),
);
},
);
},
)
// BlocConsumer<SyncOrderBloc, SyncOrderState>(
// listener: (context, state) {
// state.maybeWhen(
// orElse: () {},
// error: (message) {
// ScaffoldMessenger.of(context).showSnackBar(
// SnackBar(
// content: Text(message),
// backgroundColor: Colors.red,
// ),
// );
// },
// loaded: () {
// // ScaffoldMessenger.of(context).showSnackBar(
// // const SnackBar(
// // content: Text('Sync Order Success'),
// // backgroundColor: Colors.green,
// // ),
// // );
// },
// );
// },
// builder: (context, state) {
// return state.maybeWhen(
// orElse: () {
// return Button.filled(
// width: 100,
// height: 40,
// onPressed: () {
// log("🔘 Sync Order button pressed");
// log("🔘 SyncOrderBloc instance: ${context.read<SyncOrderBloc>()}");
// context
// .read<SyncOrderBloc>()
// .add(const SyncOrderEvent.syncOrder());
// log("🔘 SyncOrderEvent.syncOrder dispatched");
// },
// label: 'Sinkronasikan',
// );
// },
// loading: () {
// return const Center(
// child: CircularProgressIndicator(),
// );
// },
// );
// },
// )
],
),
),
@@ -0,0 +1,580 @@
import 'dart:developer';
import 'package:enaklo_pos/core/components/flushbar.dart';
import 'package:enaklo_pos/core/components/buttons.dart';
import 'package:enaklo_pos/data/repositories/product/product_repository.dart';
import 'package:enaklo_pos/data/repositories/category/category_repository.dart';
import 'package:enaklo_pos/data/datasources/product/product_local_datasource.dart';
import 'package:enaklo_pos/data/datasources/category/category_local_datasource.dart';
import 'package:enaklo_pos/presentation/setting/widgets/settings_title.dart';
import 'package:flutter/material.dart';
class SettingSyncPage extends StatefulWidget {
const SettingSyncPage({super.key});
@override
State<SettingSyncPage> createState() => _SettingSyncPageState();
}
class _SettingSyncPageState extends State<SettingSyncPage> {
final ProductRepository _productRepository = ProductRepository.instance;
final CategoryRepository _categoryRepository = CategoryRepository.instance;
final ProductLocalDatasource _productLocalDatasource =
ProductLocalDatasource.instance;
final CategoryLocalDatasource _categoryLocalDatasource =
CategoryLocalDatasource.instance;
bool _isLoading = false;
bool _isSyncing = false;
Map<String, dynamic> _productStats = {};
Map<String, dynamic> _categoryStats = {};
@override
void initState() {
super.initState();
_loadStats();
}
Future<void> _loadStats() async {
setState(() => _isLoading = true);
try {
final productStats = await _productRepository.getDatabaseStats();
final categoryStats = await _categoryRepository.getDatabaseStats();
setState(() {
_productStats = productStats;
_categoryStats = categoryStats;
_isLoading = false;
});
} catch (e) {
log('Error loading stats: $e');
setState(() => _isLoading = false);
}
}
Future<void> _syncAllData() async {
setState(() => _isSyncing = true);
try {
// Show loading dialog
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => AlertDialog(
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
CircularProgressIndicator(),
SizedBox(height: 16),
Text('Sinkronisasi semua data...'),
],
),
),
);
// Sync categories first
final categoryResult = await _categoryRepository.syncAllCategories();
await categoryResult.fold(
(error) async {
Navigator.of(context).pop();
AppFlushbar.showError(context, 'Gagal sync kategori: $error');
return;
},
(success) async {
log('Categories synced successfully');
},
);
// Sync products after categories
final productResult = await _productRepository.syncAllProducts();
await productResult.fold(
(error) async {
Navigator.of(context).pop();
AppFlushbar.showError(context, 'Gagal sync produk: $error');
return;
},
(success) async {
log('Products synced successfully');
},
);
Navigator.of(context).pop();
AppFlushbar.showSuccess(context, 'Sinkronisasi berhasil');
_loadStats(); // Refresh stats
} catch (e) {
Navigator.of(context).pop();
AppFlushbar.showError(context, 'Gagal sinkronisasi: $e');
} finally {
setState(() => _isSyncing = false);
}
}
Future<void> _clearAllData() async {
// Show confirmation dialog
final confirmed = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: Text('Hapus Semua Data'),
content: Text(
'Apakah Anda yakin ingin menghapus semua data lokal? Tindakan ini tidak dapat dibatalkan.'),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: Text('Batal'),
),
TextButton(
onPressed: () => Navigator.of(context).pop(true),
child: Text('Hapus', style: TextStyle(color: Colors.red)),
),
],
),
);
if (confirmed != true) return;
setState(() => _isLoading = true);
try {
// Clear products and categories using datasource
await _productLocalDatasource.clearAllProducts();
await _categoryLocalDatasource.clearAllCategories();
// Clear caches
_productRepository.clearCache();
_categoryRepository.clearCache();
AppFlushbar.showSuccess(context, 'Semua data berhasil dihapus');
_loadStats(); // Refresh stats
} catch (e) {
AppFlushbar.showError(context, 'Gagal menghapus data: $e');
} finally {
setState(() => _isLoading = false);
}
}
Future<void> _syncProducts() async {
setState(() => _isLoading = true);
try {
final result = await _productRepository.syncAllProducts();
await result.fold(
(error) async {
AppFlushbar.showError(context, 'Gagal sync produk: $error');
},
(success) async {
AppFlushbar.showSuccess(context, success);
_loadStats(); // Refresh stats
},
);
} catch (e) {
AppFlushbar.showError(context, 'Gagal sync produk: $e');
} finally {
setState(() => _isLoading = false);
}
}
Future<void> _syncCategories() async {
setState(() => _isLoading = true);
try {
final result = await _categoryRepository.syncAllCategories();
await result.fold(
(error) async {
AppFlushbar.showError(context, 'Gagal sync kategori: $error');
},
(success) async {
AppFlushbar.showSuccess(context, success);
_loadStats(); // Refresh stats
},
);
} catch (e) {
AppFlushbar.showError(context, 'Gagal sync kategori: $e');
} finally {
setState(() => _isLoading = false);
}
}
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
padding: EdgeInsets.symmetric(horizontal: 16),
child: Column(
children: [
SettingsTitle(
'Sinkronisasi',
subtitle: 'Sinkronisasi data dengan server',
),
SizedBox(height: 24),
// Quick Actions
_buildQuickActions(),
SizedBox(height: 24),
// Sync Tables
_buildSyncTables(),
SizedBox(height: 24),
// Database Stats
_buildDatabaseStats(),
],
),
);
}
Widget _buildQuickActions() {
return Container(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.grey.shade200),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Aksi Cepat',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.grey.shade800,
),
),
SizedBox(height: 16),
Row(
children: [
Expanded(
child: Button.filled(
onPressed: _isSyncing || _isLoading ? null : _syncAllData,
label: _isSyncing ? 'Menyinkronkan...' : 'Sync Semua Data',
),
),
SizedBox(width: 12),
Expanded(
child: Button.outlined(
onPressed: _isLoading || _isSyncing ? null : _clearAllData,
label: 'Hapus Semua Data',
textColor: Colors.red,
),
),
],
),
],
),
);
}
Widget _buildSyncTables() {
return Container(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.grey.shade200),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Sinkronisasi per Tabel',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.grey.shade800,
),
),
SizedBox(height: 16),
// Categories Sync
_buildSyncTableItem(
title: 'Kategori',
subtitle: 'Sinkronkan data kategori produk',
icon: Icons.category,
color: Colors.blue,
count: _categoryStats['total_categories'] ?? 0,
onSync: _syncCategories,
onClear: () async {
final confirmed = await _showClearConfirmation('kategori');
if (confirmed) {
await _categoryLocalDatasource.clearAllCategories();
_categoryRepository.clearCache();
AppFlushbar.showSuccess(
context, 'Data kategori berhasil dihapus');
_loadStats();
}
},
),
SizedBox(height: 12),
Divider(),
SizedBox(height: 12),
// Products Sync
_buildSyncTableItem(
title: 'Produk',
subtitle: 'Sinkronkan data produk dan variant',
icon: Icons.inventory_2,
color: Colors.green,
count: _productStats['total_products'] ?? 0,
onSync: _syncProducts,
onClear: () async {
final confirmed = await _showClearConfirmation('produk');
if (confirmed) {
await _productLocalDatasource.clearAllProducts();
_productRepository.clearCache();
AppFlushbar.showSuccess(
context, 'Data produk berhasil dihapus');
_loadStats();
}
},
),
],
),
);
}
Widget _buildSyncTableItem({
required String title,
required String subtitle,
required IconData icon,
required Color color,
required int count,
required VoidCallback onSync,
required VoidCallback onClear,
}) {
return Row(
children: [
// Icon and info
Container(
padding: EdgeInsets.all(12),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
),
child: Icon(icon, color: color, size: 24),
),
SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
title,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
SizedBox(width: 8),
Container(
padding: EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(4),
),
child: Text(
'$count',
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w600,
color: color,
),
),
),
],
),
Text(
subtitle,
style: TextStyle(
fontSize: 12,
color: Colors.grey.shade600,
),
),
],
),
),
// Actions
Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
onPressed: _isLoading || _isSyncing ? null : onSync,
icon: Icon(Icons.sync, size: 20),
tooltip: 'Sync $title',
style: IconButton.styleFrom(
backgroundColor: color.withOpacity(0.1),
foregroundColor: color,
),
),
SizedBox(width: 4),
IconButton(
onPressed: _isLoading || _isSyncing ? null : onClear,
icon: Icon(Icons.delete_outline, size: 20),
tooltip: 'Hapus $title',
style: IconButton.styleFrom(
backgroundColor: Colors.red.withOpacity(0.1),
foregroundColor: Colors.red,
),
),
],
),
],
);
}
Widget _buildDatabaseStats() {
return Container(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.grey.shade200),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
'Statistik Database',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.grey.shade800,
),
),
Spacer(),
if (_isLoading)
SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator(strokeWidth: 2),
)
else
IconButton(
onPressed: _loadStats,
icon: Icon(Icons.refresh, size: 20),
tooltip: 'Refresh Stats',
),
],
),
SizedBox(height: 16),
if (_isLoading)
Center(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 20),
child: CircularProgressIndicator(),
),
)
else
Column(
children: [
// Category stats
_buildStatRow(
'Kategori',
_categoryStats['total_categories']?.toString() ?? '0',
Icons.category,
Colors.blue,
),
SizedBox(height: 8),
// Product stats
_buildStatRow(
'Produk',
_productStats['total_products']?.toString() ?? '0',
Icons.inventory_2,
Colors.green,
),
SizedBox(height: 8),
// Variant stats
_buildStatRow(
'Variant',
_productStats['total_variants']?.toString() ?? '0',
Icons.tune,
Colors.orange,
),
SizedBox(height: 8),
// Cache stats
_buildStatRow(
'Cache Entries',
'${(_productStats['cache_entries'] ?? 0) + (_categoryStats['cache_entries'] ?? 0)}',
Icons.memory,
Colors.purple,
),
SizedBox(height: 8),
// Database size
_buildStatRow(
'Ukuran Database',
'${((_productStats['database_size_mb'] ?? 0.0) + (_categoryStats['database_size_mb'] ?? 0.0)).toStringAsFixed(2)} MB',
Icons.storage,
Colors.grey.shade600,
),
],
),
],
),
);
}
Widget _buildStatRow(String label, String value, IconData icon, Color color) {
return Row(
children: [
Icon(icon, size: 16, color: color),
SizedBox(width: 8),
Expanded(
child: Text(
label,
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade700,
),
),
),
Text(
value,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: color,
),
),
],
);
}
Future<bool> _showClearConfirmation(String dataType) async {
return await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: Text('Hapus Data $dataType'),
content:
Text('Apakah Anda yakin ingin menghapus semua data $dataType?'),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: Text('Batal'),
),
TextButton(
onPressed: () => Navigator.of(context).pop(true),
child: Text('Hapus', style: TextStyle(color: Colors.red)),
),
],
),
) ??
false;
}
}
@@ -175,7 +175,7 @@ class _KitchenPrinterPageState extends State<KitchenPrinterPage> {
// Generate test print data
final testPrintData = await PrintDataoutputs
.instance
.printKitchen(
.printKitchenAllItem(
[], // Empty product list for test
'Test Table',
'Test Order',
@@ -0,0 +1,993 @@
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/core/extensions/int_ext.dart';
import 'package:enaklo_pos/core/extensions/string_ext.dart';
import 'package:enaklo_pos/core/function/app_function.dart';
import 'package:enaklo_pos/data/models/response/order_response_model.dart';
import 'package:enaklo_pos/data/models/response/payment_response_model.dart';
import 'package:enaklo_pos/presentation/home/models/product_quantity.dart';
import 'package:enaklo_pos/presentation/home/pages/dashboard_page.dart';
import 'package:enaklo_pos/presentation/sales/blocs/order_loader/order_loader_bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class SuccessSplitBillPage extends StatefulWidget {
final List<ProductQuantity> productQuantity;
final PaymentData payment;
final String paymentMethod;
final int nominalBayar;
const SuccessSplitBillPage({
super.key,
required this.payment,
required this.productQuantity,
required this.paymentMethod,
required this.nominalBayar,
});
@override
State<SuccessSplitBillPage> createState() => _SuccessSplitBillPageState();
}
class _SuccessSplitBillPageState extends State<SuccessSplitBillPage> {
@override
void initState() {
super.initState();
context
.read<OrderLoaderBloc>()
.add(OrderLoaderEvent.getById(widget.payment.orderId ?? ""));
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.background,
body: SafeArea(
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
AppColors.primary.withOpacity(0.05),
AppColors.background,
AppColors.background,
],
),
),
child: BlocBuilder<OrderLoaderBloc, OrderLoaderState>(
builder: (context, state) {
return state.maybeWhen(
orElse: () => SizedBox.shrink(),
loading: () => Center(
child: CircularProgressIndicator(),
),
loadedDetail: (order) {
return Padding(
padding: const EdgeInsets.all(24.0),
child: Row(
children: [
// Left Panel - Success Message & Order Info
Expanded(
flex: 35,
child: _buildLeftPanel(order),
),
const SizedBox(width: 16),
// Right Panel - Order Details
Expanded(
flex: 65,
child: _buildRightPanel(order),
),
],
),
);
},
);
},
),
),
),
);
}
Widget _buildLeftPanel(Order order) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(24),
boxShadow: [
BoxShadow(
color: AppColors.primary.withOpacity(0.1),
blurRadius: 30,
offset: const Offset(0, 10),
),
],
),
child: Column(
children: [
// Success Header
Expanded(
child: SingleChildScrollView(
child: Column(
children: [
Container(
width: double.infinity,
padding: const EdgeInsets.all(32.0),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
AppColors.primary.withOpacity(0.1),
AppColors.primary.withOpacity(0.05),
],
),
borderRadius: const BorderRadius.vertical(
top: Radius.circular(24),
),
),
child: Column(
children: [
// Success Icon
Container(
padding: const EdgeInsets.all(20.0),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
AppColors.primary,
AppColors.primary.withOpacity(0.8),
],
),
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: AppColors.primary.withOpacity(0.3),
blurRadius: 20,
offset: const Offset(0, 10),
),
],
),
child: const Icon(
Icons.check_rounded,
size: 48,
color: Colors.white,
),
),
const SizedBox(height: 24),
// Success Title
const Text(
'Split Bill Berhasil!',
style: TextStyle(
fontSize: 26,
fontWeight: FontWeight.bold,
color: AppColors.primary,
),
),
const SizedBox(height: 12),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 8,
),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(20),
),
child: const Text(
'Pesanan telah diterima dan sedang diproses',
style: TextStyle(
fontSize: 14,
color: AppColors.primary,
fontWeight: FontWeight.w500,
),
textAlign: TextAlign.center,
),
),
],
),
),
// Order Information Section
Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildSectionTitle('Informasi Pesanan'),
const SizedBox(height: 24),
// Customer Card
_buildInfoCard(
icon: Icons.person_outline_rounded,
title: 'Nama Pelanggan',
value: order.metadata?['customer_name'] ?? "-",
gradient: [
Colors.blue.withOpacity(0.1),
Colors.purple.withOpacity(0.1),
],
),
const SizedBox(height: 16),
// Order Details
Column(
children: [
_buildInfoRow(
icon: Icons.receipt_long_outlined,
label: 'No. Pesanan',
value: order.orderNumber ?? "-",
),
const SizedBox(height: 12),
_buildInfoRow(
icon: Icons.receipt_long_outlined,
label: 'Metode Pembayaran',
value: widget.paymentMethod,
),
const SizedBox(height: 12),
_buildInfoRow(
icon: Icons.access_time_rounded,
label: 'Waktu',
value: (order.createdAt ?? DateTime.now())
.toFormattedDate3(),
),
const SizedBox(height: 12),
_buildInfoRow(
icon: Icons.check_circle_outline,
label: 'Status Pembayaran',
value: 'Lunas',
valueColor: Colors.green,
showBadge: true,
),
],
),
],
),
),
],
),
),
),
// Total and Action Buttons
_buildBottomSection(order),
],
),
);
}
Widget _buildRightPanel(Order order) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(24),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.08),
blurRadius: 30,
offset: const Offset(0, 10),
),
],
),
child: Column(
children: [
// Header
Container(
width: double.infinity,
padding: const EdgeInsets.all(24.0),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
AppColors.background,
Colors.grey.shade50,
],
),
borderRadius: const BorderRadius.vertical(
top: Radius.circular(24),
),
),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(12.0),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
AppColors.primary.withOpacity(0.2),
AppColors.primary.withOpacity(0.1),
],
),
borderRadius: BorderRadius.circular(16.0),
),
child: Icon(
Icons.receipt_long_rounded,
color: AppColors.primary,
size: 28,
),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Detail Pesanan',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
'Ringkasan item yang dipesan',
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade600,
),
),
],
),
),
Container(
padding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
AppColors.primary,
AppColors.primary.withOpacity(0.8),
],
),
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: AppColors.primary.withOpacity(0.3),
blurRadius: 8,
offset: const Offset(0, 4),
),
],
),
child: Text(
'${widget.productQuantity.length} Items',
style: const TextStyle(
fontSize: 13,
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
],
),
),
// Product List
Expanded(
child: ListView.separated(
padding: const EdgeInsets.all(24.0),
itemCount: widget.productQuantity.length,
separatorBuilder: (context, index) => const SizedBox(height: 12),
itemBuilder: (context, index) {
return _buildProductCard(index);
},
),
),
// Summary Footer
_buildSummaryFooter(order),
],
),
);
}
Widget _buildProductCard(int index) {
final item = widget.productQuantity[index];
final totalPrice = (item.product.price ?? 0) * item.quantity;
return Container(
padding: const EdgeInsets.all(16.0),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Colors.grey.shade50,
Colors.white,
],
),
borderRadius: BorderRadius.circular(16.0),
border: Border.all(
color: Colors.grey.withOpacity(0.1),
width: 1,
),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.04),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: Row(
children: [
// Product Image
Container(
width: 70,
height: 70,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
AppColors.primary.withOpacity(0.2),
AppColors.primary.withOpacity(0.1),
],
),
borderRadius: BorderRadius.circular(16.0),
boxShadow: [
BoxShadow(
color: AppColors.primary.withOpacity(0.2),
blurRadius: 8,
offset: const Offset(0, 4),
),
],
),
child: Icon(
Icons.restaurant_rounded,
color: AppColors.primary,
size: 28,
),
),
const SizedBox(width: 16),
// Product Details
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item.product.name ?? "-",
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 6),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 4,
),
decoration: BoxDecoration(
color: Colors.grey.shade100,
borderRadius: BorderRadius.circular(8),
),
child: Text(
(item.product.price ?? 0).toString().currencyFormatRpV2,
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade700,
fontWeight: FontWeight.w500,
),
),
),
],
),
),
const SizedBox(width: 16),
// Quantity and Total
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Container(
padding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
AppColors.primary,
AppColors.primary.withOpacity(0.8),
],
),
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: AppColors.primary.withOpacity(0.3),
blurRadius: 6,
offset: const Offset(0, 2),
),
],
),
child: Text(
'${item.quantity}x',
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
const SizedBox(height: 8),
Text(
totalPrice.toString().currencyFormatRpV2,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: AppColors.primary,
),
),
],
),
],
),
);
}
Widget _buildSectionTitle(String title) {
return Text(
title,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
);
}
Widget _buildInfoCard({
required IconData icon,
required String title,
required String value,
required List<Color> gradient,
}) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(20.0),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: gradient,
),
borderRadius: BorderRadius.circular(16.0),
border: Border.all(
color: Colors.white.withOpacity(0.3),
width: 1,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
icon,
size: 20,
color: AppColors.primary,
),
const SizedBox(width: 8),
Text(
title,
style: TextStyle(
fontSize: 12,
color: Colors.grey.shade600,
fontWeight: FontWeight.w500,
),
),
],
),
const SizedBox(height: 8),
Text(
value,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: AppColors.primary,
),
),
],
),
);
}
Widget _buildInfoRow({
required IconData icon,
required String label,
required String value,
Color? valueColor,
bool showBadge = false,
}) {
return Row(
children: [
Icon(
icon,
size: 18,
color: Colors.grey.shade600,
),
const SizedBox(width: 12),
Expanded(
child: Text(
label,
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade600,
),
),
),
if (showBadge && valueColor != null)
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 4,
),
decoration: BoxDecoration(
color: valueColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.check_circle,
size: 14,
color: valueColor,
),
const SizedBox(width: 4),
Text(
value,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
color: valueColor,
),
),
],
),
)
else
Text(
value,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: valueColor ?? Colors.black87,
),
),
],
);
}
Widget _buildBottomSection(Order order) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(24.0),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.grey.shade50,
Colors.white,
],
),
borderRadius: const BorderRadius.vertical(
bottom: Radius.circular(24),
),
),
child: Column(
children: [
// Total Amount
Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
AppColors.primary.withOpacity(0.1),
AppColors.primary.withOpacity(0.05),
],
),
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: AppColors.primary.withOpacity(0.2),
width: 1,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'Total Pembayaran',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
Text(
widget.nominalBayar.currencyFormatRpV2,
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: AppColors.primary,
),
),
],
),
),
const SizedBox(height: 24),
// Action Buttons
Row(
children: [
Expanded(
child: Container(
height: 50,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: AppColors.primary.withOpacity(0.3),
width: 2,
),
),
child: Material(
color: Colors.transparent,
borderRadius: BorderRadius.circular(14),
child: InkWell(
borderRadius: BorderRadius.circular(14),
onTap: () {
context.push(DashboardPage());
},
child: const Center(
child: Text(
'Kembali ke Beranda',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: AppColors.primary,
),
),
),
),
),
),
),
const SizedBox(width: 16),
Expanded(
child: Container(
height: 50,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [
AppColors.primary,
AppColors.primary.withOpacity(0.8),
],
),
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: AppColors.primary.withOpacity(0.3),
blurRadius: 8,
offset: const Offset(0, 4),
),
],
),
child: Material(
color: Colors.transparent,
borderRadius: BorderRadius.circular(16),
child: InkWell(
borderRadius: BorderRadius.circular(16),
onTap: () async {
final updatedOrderItems =
widget.productQuantity.map((pq) {
return OrderItem(
productName: pq.product.name,
printerType: pq.product.printerType,
productVariantName: pq.variant?.name,
quantity: pq.quantity,
unitPrice: pq.product.price,
totalPrice: (pq.product.price ?? 0) * (pq.quantity),
);
}).toList();
onPrintSplit(
context,
order: order.copyWith(
orderItems: updatedOrderItems,
),
);
},
child: const Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.print_rounded,
color: Colors.white,
size: 20,
),
SizedBox(width: 8),
Text(
'Cetak Struk',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
],
),
),
),
),
),
],
),
],
),
);
}
Widget _buildSummaryFooter(Order order) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(24.0),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.grey.shade50,
Colors.white,
],
),
borderRadius: const BorderRadius.vertical(
bottom: Radius.circular(24),
),
),
child: Column(
children: [
// Decorative Divider
Container(
height: 1,
margin: const EdgeInsets.only(bottom: 20),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.transparent,
AppColors.primary.withOpacity(0.3),
Colors.transparent,
],
),
),
),
// Subtotal Row
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Icon(
Icons.shopping_cart_outlined,
size: 16,
color: Colors.grey.shade600,
),
const SizedBox(width: 8),
Text(
'Subtotal (${widget.productQuantity.length} items)',
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade600,
fontWeight: FontWeight.w500,
),
),
],
),
Text(
widget.nominalBayar.currencyFormatRpV2,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
],
),
const SizedBox(height: 16),
// Total Payment Row
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
AppColors.primary.withOpacity(0.1),
AppColors.primary.withOpacity(0.05),
],
),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: AppColors.primary.withOpacity(0.2),
width: 1,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Container(
padding: const EdgeInsets.all(6),
decoration: BoxDecoration(
color: AppColors.primary.withOpacity(0.2),
borderRadius: BorderRadius.circular(8),
),
child: Icon(
Icons.payments_rounded,
size: 16,
color: AppColors.primary,
),
),
const SizedBox(width: 12),
const Text(
'Total Pembayaran',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
],
),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 6,
),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
AppColors.primary,
AppColors.primary.withOpacity(0.8),
],
),
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: AppColors.primary.withOpacity(0.3),
blurRadius: 4,
offset: const Offset(0, 2),
),
],
),
child: Text(
widget.nominalBayar.currencyFormatRpV2,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
],
),
),
],
),
);
}
}
@@ -1,6 +1,5 @@
import 'package:bloc/bloc.dart';
import 'package:flutter/material.dart';
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'generate_table_event.dart';
@@ -1,6 +1,5 @@
import 'package:enaklo_pos/data/datasources/table_remote_datasource.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
import 'package:enaklo_pos/data/models/response/table_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@@ -1,7 +1,4 @@
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
import 'package:enaklo_pos/data/models/response/table_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@@ -13,9 +10,6 @@ class UpdateTableBloc extends Bloc<UpdateTableEvent, UpdateTableState> {
UpdateTableBloc() : super(_Initial()) {
on<_UpdateTable>((event, emit) async {
emit(_Loading());
await ProductLocalDatasource.instance.updateTable(
event.table,
);
emit(_Success('Update Table Success'));
});
}
File diff suppressed because it is too large Load Diff
@@ -1,95 +0,0 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter/material.dart';
import 'package:enaklo_pos/core/components/components.dart';
import 'package:enaklo_pos/core/constants/colors.dart';
import 'package:enaklo_pos/presentation/table/blocs/get_table/get_table_bloc.dart';
import 'package:enaklo_pos/presentation/table/dialogs/form_table_dialog.dart';
import 'package:enaklo_pos/presentation/table/widgets/card_table_widget.dart';
class TablePage extends StatefulWidget {
const TablePage({super.key});
@override
State<TablePage> createState() => _TablePageState();
}
class _TablePageState extends State<TablePage> {
@override
void initState() {
context.read<GetTableBloc>().add(const GetTableEvent.getTables());
super.initState();
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(24),
child: ListView(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Table Management",
style: TextStyle(
fontSize: 24.0,
fontWeight: FontWeight.bold,
color: AppColors.primary,
),
),
Button.filled(
onPressed: () {
showDialog(
context: context,
builder: (context) => FormTableDialog(),
);
},
label: 'Generate Table',
height: 48.0,
width: 200.0,
),
],
),
SpaceHeight(24.0),
BlocBuilder<GetTableBloc, GetTableState>(
builder: (context, state) {
return state.maybeWhen(
orElse: () {
return SizedBox.shrink();
},
loading: () {
return const CircularProgressIndicator();
},
success: (tables) {
if (tables.isEmpty) {
return const Center(
child: Text('No table available'),
);
}
return GridView.builder(
padding: EdgeInsets.zero,
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
childAspectRatio: 1.0,
crossAxisCount: 4,
mainAxisSpacing: 16,
crossAxisSpacing: 16,
),
itemCount: tables.length,
shrinkWrap: true,
physics: const ScrollPhysics(),
itemBuilder: (BuildContext context, int index) {
return CardTableWidget(
table: tables[index],
);
},
);
},
);
},
),
],
),
);
}
}
@@ -1,113 +0,0 @@
import 'dart:developer';
import 'package:enaklo_pos/presentation/home/models/product_quantity.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter/material.dart';
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/core/extensions/int_ext.dart';
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
import 'package:enaklo_pos/data/models/response/table_model.dart';
import 'package:enaklo_pos/presentation/home/bloc/checkout/checkout_bloc.dart';
import 'package:enaklo_pos/presentation/home/bloc/status_table/status_table_bloc.dart';
import 'package:enaklo_pos/presentation/home/pages/home_page.dart';
import 'package:enaklo_pos/presentation/table/blocs/get_table/get_table_bloc.dart';
import 'package:enaklo_pos/presentation/table/models/draft_order_model.dart';
import 'package:enaklo_pos/presentation/table/pages/payment_table_page.dart.old';
class CardTableWidget extends StatefulWidget {
final TableModel table;
final List<ProductQuantity> items;
const CardTableWidget({
super.key,
required this.table,
required this.items,
});
@override
State<CardTableWidget> createState() => _CardTableWidgetState();
}
class _CardTableWidgetState extends State<CardTableWidget> {
DraftOrderModel? data;
@override
void initState() {
loadData();
super.initState();
}
loadData() async {
if (widget.table.status != 'available') {
// data = await ProductLocalDatasource.instance
// .getDraftOrderById(widget.table.orderId);
}
}
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(16.0),
height: 200,
width: 200,
decoration: BoxDecoration(
border: Border.all(
color: widget.table.status == 'available'
? AppColors.primary
: AppColors.red,
width: 2),
borderRadius: BorderRadius.circular(16),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Table ${widget.table.tableName}',
style: TextStyle(
color: AppColors.black,
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
Text(
// widget.table.status == 'available'
// ? widget.table.status
// : "${widget.table.status} - ${DateTime.parse(widget.table.startTime).toFormattedTime()}",
"",
style: TextStyle(
color: AppColors.black,
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
Button.filled(
color: widget.table.status == 'available'
? AppColors.primary
: AppColors.red,
onPressed: () async {
if (widget.table.status == 'available') {
context.push(HomePage(
isTable: true,
table: widget.table,
items: widget.items,
));
} else {
context.read<CheckoutBloc>().add(
CheckoutEvent.loadDraftOrder(data!),
);
log("Data Draft Order: ${data!.toMap()}");
context.push(PaymentTablePage(
table: widget.table,
draftOrder: data!,
));
}
},
label: widget.table.status == 'available' ? 'Open' : 'Close')
],
),
);
}
}
@@ -1,399 +0,0 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:enaklo_pos/core/components/buttons.dart';
import 'package:enaklo_pos/core/components/custom_text_field.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/utils/date_formatter.dart';
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
import 'package:enaklo_pos/data/models/response/table_model.dart';
import 'package:enaklo_pos/presentation/home/bloc/checkout/checkout_bloc.dart';
import 'package:enaklo_pos/presentation/home/bloc/status_table/status_table_bloc.dart';
import 'package:enaklo_pos/presentation/home/pages/dashboard_page.dart';
import 'package:enaklo_pos/presentation/table/blocs/create_table/create_table_bloc.dart';
import 'package:enaklo_pos/presentation/table/blocs/get_table/get_table_bloc.dart';
import 'package:enaklo_pos/presentation/table/blocs/update_table/update_table_bloc.dart';
import 'package:enaklo_pos/presentation/table/models/draft_order_model.dart';
import '../pages/payment_table_page.dart';
class TableWidget extends StatefulWidget {
final TableModel table;
const TableWidget({
super.key,
required this.table,
});
@override
State<TableWidget> createState() => _TableWidgetState();
}
class _TableWidgetState extends State<TableWidget> {
TextEditingController? tableNameController;
DraftOrderModel? data;
@override
void initState() {
super.initState();
loadData();
tableNameController = TextEditingController(text: widget.table.tableName);
}
@override
void dispose() {
tableNameController!.dispose();
super.dispose();
}
loadData() async {
if (widget.table.status != 'available') {
// data = await ProductLocalDatasource.instance
// .getDraftOrderById(widget.table.orderId);
}
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () async {
if (widget.table.status == 'available') {
context.push(DashboardPage(
table: widget.table,
));
} else {
// Handle occupied table click - load draft order and navigate to payment
context.read<CheckoutBloc>().add(
CheckoutEvent.loadDraftOrder(data!),
);
log("Data Draft Order: ${data!.toMap()}");
context.push(PaymentTablePage(
table: widget.table,
draftOrder: data!,
));
}
},
onLongPress: () {
// dialog info table
showDialog(
context: context,
builder: (context) {
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16)),
title: Row(
children: [
Icon(Icons.table_bar, color: AppColors.primary),
SizedBox(width: 8),
Text('Table ${widget.table.tableName}'),
Spacer(),
BlocListener<UpdateTableBloc, UpdateTableState>(
listener: (context, state) {
state.maybeWhen(
orElse: () {},
success: (message) {
context
.read<GetTableBloc>()
.add(const GetTableEvent.getTables());
context.pop();
});
},
child: IconButton(
onPressed: () {
// show dialaog adn input table name
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text('Update Table'),
content: SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
maxHeight: 180,
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
CustomTextField(
controller: tableNameController!,
label: 'Table Name',
),
SpaceHeight(16),
Row(
children: [
Expanded(
child: Button.outlined(
onPressed: () {
context.pop();
},
label: 'close',
),
),
SpaceWidth(16),
Expanded(
child: Button.filled(
onPressed: () {
// final newData =
// TableModel(
// id: widget.table.id,
// tableName:
// tableNameController!
// .text,
// status:
// widget.table.status,
// startTime: widget
// .table.startTime,
// orderId: widget
// .table.orderId,
// paymentAmount: widget
// .table
// .paymentAmount,
// position: widget
// .table.position,
// );
// context
// .read<
// UpdateTableBloc>()
// .add(
// UpdateTableEvent
// .updateTable(
// newData,
// ),
// );
context
.pop(); // close dialog after adding
},
label: 'Update',
),
)
],
)
],
),
),
),
actions: []);
});
},
icon: Icon(Icons.edit)),
),
],
),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildInfoRow(
'Status:',
widget.table.status == 'available'
? 'Available'
: 'Occupied',
color: widget.table.status == 'available'
? Colors.green
: Colors.red),
// widget.table.status == 'available'
// ? SizedBox.shrink()
// : _buildInfoRow(
// 'Start Time:',
// DateFormatter.formatDateTime2(
// widget.table.startTime)),
// widget.table.status == 'available'
// ? SizedBox.shrink()
// : _buildInfoRow(
// 'Order ID:', widget.table.orderId.toString()),
widget.table.status == 'available'
? SizedBox.shrink()
: SpaceHeight(16),
widget.table.status == 'available'
? SizedBox.shrink()
: Row(
children: [
Expanded(
child: Button.outlined(
onPressed: () {
// Show void confirmation dialog
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Row(
children: [
Icon(Icons.warning,
color: AppColors.red),
SizedBox(width: 8),
Text('Void Order?'),
],
),
content: Text(
'Apakah anda yakin ingin membatalkan pesanan untuk meja ${widget.table.tableName}?\n\nPesanan akan dihapus secara permanen.'),
actions: [
TextButton(
onPressed: () =>
Navigator.pop(context),
child: Text('Tidak',
style: TextStyle(
color: AppColors.primary)),
),
BlocListener<StatusTableBloc,
StatusTableState>(
listener: (context, state) {
state.maybeWhen(
orElse: () {},
success: () {
context
.read<GetTableBloc>()
.add(const GetTableEvent
.getTables());
Navigator.pop(
context); // Close void dialog
Navigator.pop(
context); // Close table info dialog
ScaffoldMessenger.of(context)
.showSnackBar(
const SnackBar(
content: Text(
'Pesanan berhasil dibatalkan'),
backgroundColor:
AppColors.primary,
),
);
},
);
},
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.red,
),
onPressed: () {
// // Void the order
// final newTable = TableModel(
// id: widget.table.id,
// tableName:
// widget.table.tableName,
// status: 'available',
// orderId: 0,
// paymentAmount: 0,
// startTime: DateTime.now()
// .toIso8601String(),
// position: widget.table.position,
// );
// context
// .read<StatusTableBloc>()
// .add(
// StatusTableEvent
// .statusTabel(newTable),
// );
// // Remove draft order from local storage
// ProductLocalDatasource.instance
// .removeDraftOrderById(
// widget.table.orderId);
// log("Voided order for table: ${widget.table.tableName}");
},
child: const Text(
"Ya, Batalkan",
style: TextStyle(
color: Colors.white),
),
),
),
],
),
);
},
label: 'Void Order',
color: AppColors.red,
textColor: AppColors.red,
),
),
SizedBox(width: 12),
Expanded(
child: BlocConsumer<StatusTableBloc,
StatusTableState>(
listener: (context, state) {
state.maybeWhen(
orElse: () {},
success: () {
context.read<GetTableBloc>().add(
const GetTableEvent.getTables());
context.pop();
});
},
builder: (context, state) {
return Button.filled(
onPressed: () {
context.pop();
context.read<CheckoutBloc>().add(
CheckoutEvent.loadDraftOrder(
data!),
);
context.push(PaymentTablePage(
table: widget.table,
draftOrder: data!,
));
},
label: 'Selesai');
},
),
),
],
),
],
),
actions: [
TextButton(
child:
Text('Close', style: TextStyle(color: AppColors.primary)),
onPressed: () => Navigator.of(context).pop(),
),
],
);
},
);
},
child: Container(
padding: const EdgeInsets.all(16.0),
alignment: Alignment.center,
decoration: BoxDecoration(
color: widget.table.status == 'available'
? AppColors.primary
: AppColors.red,
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(10),
),
child: Text('${widget.table.tableName}',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w600,
)),
),
);
}
Widget _buildInfoRow(String label, String value, {Color? color}) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: Row(
children: [
Text(
label,
style: TextStyle(fontWeight: FontWeight.w600),
),
SizedBox(width: 8),
Expanded(
child: Text(
value,
style: TextStyle(
color: color ?? Colors.black87,
),
),
),
],
),
);
}
}
+32 -45
View File
@@ -5,10 +5,10 @@ packages:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab"
sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7"
url: "https://pub.dev"
source: hosted
version: "76.0.0"
version: "67.0.0"
_flutterfire_internals:
dependency: transitive
description:
@@ -17,19 +17,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.61"
_macros:
dependency: transitive
description: dart
source: sdk
version: "0.3.3"
analyzer:
dependency: transitive
description:
name: analyzer
sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e"
sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d"
url: "https://pub.dev"
source: hosted
version: "6.11.0"
version: "6.4.1"
another_flushbar:
dependency: "direct main"
description:
@@ -114,10 +109,10 @@ packages:
dependency: transitive
description:
name: build
sha256: cef23f1eda9b57566c81e2133d196f8e3df48f244b317368d65c5943d91148f0
sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0"
url: "https://pub.dev"
source: hosted
version: "2.4.2"
version: "2.4.1"
build_config:
dependency: transitive
description:
@@ -138,26 +133,26 @@ packages:
dependency: transitive
description:
name: build_resolvers
sha256: "99d3980049739a985cf9b21f30881f46db3ebc62c5b8d5e60e27440876b1ba1e"
sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a"
url: "https://pub.dev"
source: hosted
version: "2.4.3"
version: "2.4.2"
build_runner:
dependency: "direct dev"
description:
name: build_runner
sha256: "74691599a5bc750dc96a6b4bfd48f7d9d66453eab04c7f4063134800d6a5c573"
sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d"
url: "https://pub.dev"
source: hosted
version: "2.4.14"
version: "2.4.13"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021"
sha256: f8126682b87a7282a339b871298cc12009cb67109cfa1614d6436fb0289193e0
url: "https://pub.dev"
source: hosted
version: "8.0.0"
version: "7.3.2"
built_collection:
dependency: transitive
description:
@@ -322,10 +317,10 @@ packages:
dependency: transitive
description:
name: dart_style
sha256: "7856d364b589d1f08986e140938578ed36ed948581fbc3bc9aef1805039ac5ab"
sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9"
url: "https://pub.dev"
source: hosted
version: "2.3.7"
version: "2.3.6"
dartx:
dependency: transitive
description:
@@ -633,10 +628,10 @@ packages:
dependency: "direct main"
description:
name: freezed
sha256: "44c19278dd9d89292cf46e97dc0c1e52ce03275f40a97c5a348e802a924bf40e"
sha256: a434911f643466d78462625df76fd9eb13e57348ff43fe1f77bbe909522c67a1
url: "https://pub.dev"
source: hosted
version: "2.5.7"
version: "2.5.2"
freezed_annotation:
dependency: "direct main"
description:
@@ -857,26 +852,26 @@ packages:
dependency: transitive
description:
name: leak_tracker
sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0"
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
url: "https://pub.dev"
source: hosted
version: "10.0.9"
version: "11.0.2"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
url: "https://pub.dev"
source: hosted
version: "3.0.9"
version: "3.0.10"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
url: "https://pub.dev"
source: hosted
version: "3.0.1"
version: "3.0.2"
lints:
dependency: transitive
description:
@@ -893,14 +888,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.0"
macros:
dependency: transitive
description:
name: macros
sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656"
url: "https://pub.dev"
source: hosted
version: "0.1.3-main.0"
matcher:
dependency: transitive
description:
@@ -921,10 +908,10 @@ packages:
dependency: transitive
description:
name: meta
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
url: "https://pub.dev"
source: hosted
version: "1.16.0"
version: "1.17.0"
mime:
dependency: transitive
description:
@@ -1451,13 +1438,13 @@ packages:
source: hosted
version: "30.2.5"
synchronized:
dependency: transitive
dependency: "direct main"
description:
name: synchronized
sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225"
sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0
url: "https://pub.dev"
source: hosted
version: "3.3.0+3"
version: "3.4.0"
term_glyph:
dependency: transitive
description:
@@ -1470,10 +1457,10 @@ packages:
dependency: transitive
description:
name: test_api
sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
url: "https://pub.dev"
source: hosted
version: "0.7.4"
version: "0.7.7"
time:
dependency: transitive
description:
@@ -1534,10 +1521,10 @@ packages:
dependency: transitive
description:
name: vector_math
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
url: "https://pub.dev"
source: hosted
version: "2.1.4"
version: "2.2.0"
vm_service:
dependency: transitive
description:
@@ -1635,5 +1622,5 @@ packages:
source: hosted
version: "3.1.3"
sdks:
dart: ">=3.7.0 <4.0.0"
dart: ">=3.8.0 <4.0.0"
flutter: ">=3.29.0"
+2 -1
View File
@@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 1.0.0+1
version: 1.0.5+16
environment:
sdk: ">=3.2.4 <4.0.0"
@@ -70,6 +70,7 @@ dependencies:
firebase_core: ^4.1.0
firebase_crashlytics: ^5.0.1
path: ^1.9.1
synchronized: ^3.4.0
# imin_printer: ^0.6.10
dev_dependencies: