Compare commits

...
70 Commits
Author SHA1 Message Date
Efril 5d73f82017 fix payment struck 2026-01-13 23:41:56 +07:00
aefril 1d52f22f5f Merge pull request 'dev' (#1) from dev into main
Reviewed-on: #1
2025-08-13 17:19:46 +00:00
efrilm f87309af3f feat: update 2025-08-14 00:16:42 +07:00
efrilm 96d0b91984 feat: succes payment 2025-08-14 00:12:49 +07:00
efrilm e20dc53fb8 feat: profit loss widget 2025-08-13 23:27:52 +07:00
efrilm c09822b470 feat: success save order 2025-08-13 23:08:12 +07:00
efrilm 6a9a26f71c fix: success save order 2025-08-13 23:00:11 +07:00
efrilm b0006ee6bc fix: order and add to order 2025-08-13 22:52:37 +07:00
efrilm b34428965e fix: bug 2025-08-13 22:11:24 +07:00
efrilm 670a0b6414 feat: update split bill 2025-08-08 23:04:35 +07:00
efrilm b7ab9c6939 feat: update logo 2025-08-08 19:44:00 +07:00
efrilm 6b5af82307 feat: table 2025-08-08 14:59:49 +07:00
efrilm 2848678cdc fix: amount 2025-08-08 14:39:56 +07:00
efrilm f05347c90d feat: transfer table 2025-08-08 14:22:12 +07:00
efrilm 94725964ea fix: change position table 2025-08-08 13:45:03 +07:00
efrilm 2eceb0bc0c feat: update 2025-08-08 10:53:46 +07:00
efrilm e19d788f47 feat: update order detail 2025-08-08 10:29:17 +07:00
efrilm 3a07d2e7cc feat: fix payment 2025-08-08 00:12:10 +07:00
efrilm 2c8b1333d8 feat: order response model 2025-08-07 23:59:01 +07:00
efrilm b1a1303a40 feat: split add customer optional 2025-08-07 22:45:06 +07:00
efrilm de22251440 feat: update print 2025-08-07 22:03:05 +07:00
efrilm 203eda75e4 feat: save outlet to local 2025-08-07 21:43:13 +07:00
efrilm 49fef5afb2 fix: ui refund and sales page 2025-08-07 20:11:57 +07:00
efrilm e3da008a9a fix: customer ui 2025-08-07 19:59:54 +07:00
efrilm b5ad20eb8b feat: handler ui and update void ui 2025-08-07 19:57:59 +07:00
efrilm fbd59964c3 feat: update dashboard 2025-08-07 15:47:38 +07:00
efrilm 5a260ca3a1 feat: ticket printer 2025-08-07 15:30:05 +07:00
efrilm ffe076e120 feat: payment split 2025-08-07 14:48:59 +07:00
efrilm 9d78c9dcce fix: print 2025-08-07 14:28:52 +07:00
efrilm d0e1f9a5b6 feat: update success payment page 2025-08-07 12:49:42 +07:00
efrilm ab2a59930c feat: order search 2025-08-07 12:34:57 +07:00
efrilm fe92082ceb feat: search product and fix product 2025-08-07 12:19:25 +07:00
efrilm 5575596bfc fix: tax 2025-08-07 11:48:05 +07:00
efrilm 8022fa6b32 feat: print on receipt 2025-08-07 11:39:33 +07:00
efrilm 811ae06e58 fix: change outlet 2025-08-07 11:00:59 +07:00
efrilm 2cf5ce7ae8 feat: product is active 2025-08-07 10:56:08 +07:00
efrilm 1cd2b1778b fix: color variant 2025-08-07 10:49:38 +07:00
efrilm 7d1c3d1bd6 feat: package name 2025-08-06 23:45:15 +07:00
efrilm 7586df207e feat: update success save order 2025-08-06 21:11:09 +07:00
efrilm ecc817f66c feat: redesign success order page 2025-08-06 20:19:55 +07:00
efrilm f73aa619e9 feat: print 2025-08-06 19:52:40 +07:00
efrilm c667584b11 feat: update dropdown 2025-08-06 19:28:51 +07:00
efrilm 5e9d5040e7 feat: confirm save order 2025-08-06 19:08:59 +07:00
efrilm 6fdaac2c0f feat: delivery type 2025-08-06 18:47:20 +07:00
efrilm a46a2cfa7c feat: refactor 2025-08-06 18:12:27 +07:00
efrilm 561aec371d feat: Tab Category Home 2025-08-06 17:41:04 +07:00
efrilm 7ee7db225c feat: order infinty scroll 2025-08-06 15:46:22 +07:00
efrilm be803bbe4f feat: split bill 2025-08-06 15:27:05 +07:00
efrilm 20f8d5788c fix: payment page 2025-08-06 13:51:49 +07:00
efrilm f067417ab2 feat: profit loss 2025-08-06 13:38:49 +07:00
efrilm db06f0dd13 refactor 2025-08-06 13:10:40 +07:00
efrilm bdb2c0ba52 feat: Dashboard Analytic 2025-08-06 13:05:58 +07:00
efrilm 91335ad8db feat: product analytic 2025-08-06 12:32:53 +07:00
efrilm 648a4f5eb4 fix: sales analytic 2025-08-06 12:10:56 +07:00
efrilm 2a457ea5f6 feat: item sales report 2025-08-06 12:06:34 +07:00
efrilm fd254c22fd feat: payment method report 2025-08-06 11:23:03 +07:00
efrilm ae4f7b06ce fix: refund 2025-08-06 00:53:02 +07:00
efrilm 37ed6686f8 feat: update type and customer is required 2025-08-06 00:36:13 +07:00
efrilm 092e68615c feat: order item 2025-08-06 00:16:32 +07:00
efrilm b82bb84152 fix: order bayar 2025-08-06 00:03:42 +07:00
efrilm afeccac4aa fix: order 2025-08-05 23:58:16 +07:00
efrilm d7cd6ce4f2 fix: order 2025-08-05 22:39:43 +07:00
efrilm f0ff078e0e feat: setting product infinity scroll 2025-08-05 20:00:58 +07:00
efrilm 7ca96d19db fix 2025-08-05 19:52:25 +07:00
efrilm 3e9b20f237 feat: product scroll infinity 2025-08-05 19:48:32 +07:00
efrilm bb9aef55cf feat: create product 2025-08-05 19:20:45 +07:00
efrilm 935b6b9a5b feat: update outlet 2025-08-05 15:25:10 +07:00
efrilm 5af0259f0a feat: table page 2025-08-05 12:34:41 +07:00
efrilm a436769ec1 feat: payment page 2025-08-05 10:26:25 +07:00
efrilm 1b1d01c1e8 feat: refund page 2025-08-04 23:13:52 +07:00
226 changed files with 28014 additions and 5316 deletions
+3 -3
View File
@@ -23,7 +23,7 @@ if (flutterVersionName == null) {
} }
android { android {
namespace "com.example.enaklo_pos" namespace "com.appscale.pos"
compileSdkVersion 35 compileSdkVersion 35
ndkVersion flutter.ndkVersion ndkVersion flutter.ndkVersion
@@ -42,10 +42,10 @@ android {
defaultConfig { defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.enaklo_pos" applicationId "com.appscale.pos"
// You can update the following values to match your application needs. // 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. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdkVersion 21 minSdkVersion flutter.minSdkVersion
targetSdkVersion 35 targetSdkVersion 35
versionCode flutterVersionCode.toInteger() versionCode flutterVersionCode.toInteger()
versionName flutterVersionName versionName flutterVersionName
@@ -1,4 +1,4 @@
package com.example.enaklo_pos package com.appscale.pos
import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.android.FlutterActivity
Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 983 B

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 19 KiB

+10
View File
@@ -0,0 +1,10 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_17_4760)">
<path d="M9 13.75C6.66 13.75 2 14.92 2 17.25V19H16V17.25C16 14.92 11.34 13.75 9 13.75ZM4.34 17C5.18 16.42 7.21 15.75 9 15.75C10.79 15.75 12.82 16.42 13.66 17H4.34ZM9 12C10.93 12 12.5 10.43 12.5 8.5C12.5 6.57 10.93 5 9 5C7.07 5 5.5 6.57 5.5 8.5C5.5 10.43 7.07 12 9 12ZM9 7C9.83 7 10.5 7.67 10.5 8.5C10.5 9.33 9.83 10 9 10C8.17 10 7.5 9.33 7.5 8.5C7.5 7.67 8.17 7 9 7ZM16.04 13.81C17.2 14.65 18 15.77 18 17.25V19H22V17.25C22 15.23 18.5 14.08 16.04 13.81ZM15 12C16.93 12 18.5 10.43 18.5 8.5C18.5 6.57 16.93 5 15 5C14.46 5 13.96 5.13 13.5 5.35C14.13 6.24 14.5 7.33 14.5 8.5C14.5 9.67 14.13 10.76 13.5 11.65C13.96 11.87 14.46 12 15 12Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0_17_4760">
<rect width="24" height="24" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 892 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 320 B

After

Width:  |  Height:  |  Size: 666 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 804 B

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 492 B

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 804 B

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 15 KiB

+4 -4
View File
@@ -3,15 +3,15 @@
flutter_launcher_icons: flutter_launcher_icons:
android: "launcher_icon" android: "launcher_icon"
ios: true ios: true
image_path: "assets/logo/ic_launcher.png" image_path: "assets/logo/logo.png"
remove_alpha_ios: true remove_alpha_ios: true
min_sdk_android: 21 # android min sdk min:16, default 21 min_sdk_android: 21 # android min sdk min:16, default 21
adaptive_icon_background: "#ffffff" adaptive_icon_background: "#ffffff"
adaptive_icon_foreground: "assets/logo/ic_launcher.png" adaptive_icon_foreground: "assets/logo/logo.png"
web: web:
generate: true generate: true
image_path: "assets/logo/ic_launcher.png" image_path: "assets/logo/logo.png"
windows: windows:
generate: true generate: true
image_path: "assets/logo/ic_launcher.png" image_path: "assets/logo/logo.png"
icon_size: 48 icon_size: 48
+16 -17
View File
@@ -101,6 +101,9 @@ class $AssetsIconsGen {
/// File path: assets/icons/payments.svg /// File path: assets/icons/payments.svg
SvgGenImage get payments => const SvgGenImage('assets/icons/payments.svg'); SvgGenImage get payments => const SvgGenImage('assets/icons/payments.svg');
/// File path: assets/icons/people.svg
SvgGenImage get people => const SvgGenImage('assets/icons/people.svg');
/// File path: assets/icons/print.svg /// File path: assets/icons/print.svg
SvgGenImage get print => const SvgGenImage('assets/icons/print.svg'); SvgGenImage get print => const SvgGenImage('assets/icons/print.svg');
@@ -152,6 +155,7 @@ class $AssetsIconsGen {
orders, orders,
pajak, pajak,
payments, payments,
people,
print, print,
qrCode, qrCode,
report, report,
@@ -186,6 +190,12 @@ class $AssetsImagesGen {
/// File path: assets/images/drink7.png /// File path: assets/images/drink7.png
AssetGenImage get drink7 => const AssetGenImage('assets/images/drink7.png'); AssetGenImage get drink7 => const AssetGenImage('assets/images/drink7.png');
/// File path: assets/images/gojek.png
AssetGenImage get gojek => const AssetGenImage('assets/images/gojek.png');
/// File path: assets/images/grab.png
AssetGenImage get grab => const AssetGenImage('assets/images/grab.png');
/// File path: assets/images/logo.png /// File path: assets/images/logo.png
AssetGenImage get logo => const AssetGenImage('assets/images/logo.png'); AssetGenImage get logo => const AssetGenImage('assets/images/logo.png');
@@ -265,6 +275,8 @@ class $AssetsImagesGen {
drink5, drink5,
drink6, drink6,
drink7, drink7,
gojek,
grab,
logo, logo,
managePrinter, managePrinter,
manageProduct, manageProduct,
@@ -292,12 +304,8 @@ class $AssetsImagesGen {
class $AssetsLogoGen { class $AssetsLogoGen {
const $AssetsLogoGen(); const $AssetsLogoGen();
/// File path: assets/logo/ic_launcher.png /// File path: assets/logo/logo.png
AssetGenImage get icLauncher => AssetGenImage get logo => const AssetGenImage('assets/logo/logo.png');
const AssetGenImage('assets/logo/ic_launcher.png');
/// File path: assets/logo/logo.jpeg
AssetGenImage get logo => const AssetGenImage('assets/logo/logo.jpeg');
/// File path: assets/logo/logo2.png /// File path: assets/logo/logo2.png
AssetGenImage get logo2 => const AssetGenImage('assets/logo/logo2.png'); AssetGenImage get logo2 => const AssetGenImage('assets/logo/logo2.png');
@@ -322,17 +330,8 @@ class $AssetsLogoGen {
AssetGenImage get mylogo => const AssetGenImage('assets/logo/mylogo.png'); AssetGenImage get mylogo => const AssetGenImage('assets/logo/mylogo.png');
/// List of all assets /// List of all assets
List<AssetGenImage> get values => [ List<AssetGenImage> get values =>
icLauncher, [logo, logo2, logo3, logo4, logo5, logo6, logoAppIcon, mylogo];
logo,
logo2,
logo3,
logo4,
logo5,
logo6,
logoAppIcon,
mylogo
];
} }
class Assets { class Assets {
+615 -62
View File
@@ -1,91 +1,189 @@
import 'dart:io'; import 'dart:io';
import 'package:enaklo_pos/presentation/setting/bloc/upload_file/upload_file_bloc.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
import '../assets/assets.gen.dart';
import '../constants/colors.dart'; import '../constants/colors.dart';
import '../constants/variables.dart'; import '../constants/variables.dart';
import 'buttons.dart';
import 'spaces.dart'; import 'spaces.dart';
class ImagePickerWidget extends StatefulWidget { class ImagePickerWidget extends StatefulWidget {
final String label; final String label;
final void Function(XFile? file) onChanged; final void Function(XFile? file) onChanged;
final void Function(String? uploadedUrl)? onUploaded;
final bool showLabel; final bool showLabel;
final String? initialImageUrl; final String? initialImageUrl;
final bool autoUpload;
const ImagePickerWidget({ const ImagePickerWidget({
super.key, super.key,
required this.label, required this.label,
required this.onChanged, required this.onChanged,
this.onUploaded,
this.showLabel = true, this.showLabel = true,
this.initialImageUrl, this.initialImageUrl,
this.autoUpload = false,
}); });
@override @override
State<ImagePickerWidget> createState() => _ImagePickerWidgetState(); State<ImagePickerWidget> createState() => _ImagePickerWidgetState();
} }
class _ImagePickerWidgetState extends State<ImagePickerWidget> { class _ImagePickerWidgetState extends State<ImagePickerWidget>
with TickerProviderStateMixin {
String? imagePath; String? imagePath;
String? uploadedImageUrl;
bool hasInitialImage = false; bool hasInitialImage = false;
bool isHovering = false;
bool isUploading = false;
late AnimationController _scaleController;
late AnimationController _fadeController;
late AnimationController _uploadController;
late Animation<double> _scaleAnimation;
late Animation<double> _fadeAnimation;
late Animation<double> _uploadAnimation;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
hasInitialImage = widget.initialImageUrl != null; hasInitialImage = widget.initialImageUrl != null;
_scaleController = AnimationController(
duration: const Duration(milliseconds: 200),
vsync: this,
);
_fadeController = AnimationController(
duration: const Duration(milliseconds: 300),
vsync: this,
);
_uploadController = AnimationController(
duration: const Duration(milliseconds: 1000),
vsync: this,
);
_scaleAnimation = Tween<double>(
begin: 1.0,
end: 0.95,
).animate(CurvedAnimation(
parent: _scaleController,
curve: Curves.easeInOut,
));
_fadeAnimation = Tween<double>(
begin: 0.0,
end: 1.0,
).animate(CurvedAnimation(
parent: _fadeController,
curve: Curves.easeInOut,
));
_uploadAnimation = Tween<double>(
begin: 0.0,
end: 1.0,
).animate(CurvedAnimation(
parent: _uploadController,
curve: Curves.easeInOut,
));
_fadeController.forward();
}
@override
void dispose() {
_scaleController.dispose();
_fadeController.dispose();
_uploadController.dispose();
super.dispose();
} }
Future<void> _pickImage() async { Future<void> _pickImage() async {
_scaleController.forward().then((_) {
_scaleController.reverse();
});
final pickedFile = await ImagePicker().pickImage( final pickedFile = await ImagePicker().pickImage(
source: ImageSource.gallery, source: ImageSource.gallery,
); );
setState(() { if (pickedFile != null) {
if (pickedFile != null) { setState(() {
imagePath = pickedFile.path; imagePath = pickedFile.path;
hasInitialImage = false; // Clear initial image when new image is picked hasInitialImage = false;
widget.onChanged(pickedFile); uploadedImageUrl = null;
} else { });
debugPrint('No image selected.');
widget.onChanged(null); widget.onChanged(pickedFile);
// Auto upload if enabled
if (widget.autoUpload) {
_uploadImage(pickedFile.path);
} }
}); } else {
debugPrint('No image selected.');
widget.onChanged(null);
}
} }
@override void _uploadImage(String filePath) {
Widget build(BuildContext context) { setState(() {
return Column( isUploading = true;
crossAxisAlignment: CrossAxisAlignment.start, });
children: [ _uploadController.forward();
if (widget.showLabel) ...[
Text( context.read<UploadFileBloc>().add(
widget.label, UploadFileEvent.upload(filePath),
style: const TextStyle( );
fontSize: 14, }
fontWeight: FontWeight.w700,
), Widget _buildImageContainer() {
return Container(
width: 100.0,
height: 100.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20.0),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 10,
offset: const Offset(0, 4),
), ),
const SpaceHeight(12.0),
], ],
Container( ),
padding: const EdgeInsets.all(6.0), child: ClipRRect(
decoration: BoxDecoration( borderRadius: BorderRadius.circular(20.0),
borderRadius: BorderRadius.circular(16.0), child: Stack(
border: Border.all(color: AppColors.primary), children: [
), Positioned.fill(
child: Row( child: imagePath != null
children: [ ? Image.file(
SizedBox( File(imagePath!),
width: 80.0, fit: BoxFit.cover,
height: 80.0, )
child: ClipRRect( : uploadedImageUrl != null
borderRadius: BorderRadius.circular(10.0), ? CachedNetworkImage(
child: imagePath != null imageUrl: uploadedImageUrl!.contains('http')
? Image.file( ? uploadedImageUrl!
File(imagePath!), : '${Variables.baseUrl}/$uploadedImageUrl',
placeholder: (context, url) => Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
AppColors.primary.withOpacity(0.1),
AppColors.primary.withOpacity(0.05),
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
child: const Center(
child: CircularProgressIndicator(strokeWidth: 2),
),
),
errorWidget: (context, url, error) =>
_buildPlaceholder(),
fit: BoxFit.cover, fit: BoxFit.cover,
) )
: hasInitialImage && widget.initialImageUrl != null : hasInitialImage && widget.initialImageUrl != null
@@ -93,38 +191,493 @@ class _ImagePickerWidgetState extends State<ImagePickerWidget> {
imageUrl: widget.initialImageUrl!.contains('http') imageUrl: widget.initialImageUrl!.contains('http')
? widget.initialImageUrl! ? widget.initialImageUrl!
: '${Variables.baseUrl}/${widget.initialImageUrl}', : '${Variables.baseUrl}/${widget.initialImageUrl}',
placeholder: (context, url) => placeholder: (context, url) => Container(
const Center(child: CircularProgressIndicator()), decoration: BoxDecoration(
errorWidget: (context, url, error) => Container( gradient: LinearGradient(
padding: const EdgeInsets.all(16.0), colors: [
color: AppColors.black.withOpacity(0.05), AppColors.primary.withOpacity(0.1),
child: Assets.icons.image.svg(), AppColors.primary.withOpacity(0.05),
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
child: const Center(
child:
CircularProgressIndicator(strokeWidth: 2),
),
), ),
errorWidget: (context, url, error) =>
_buildPlaceholder(),
fit: BoxFit.cover, fit: BoxFit.cover,
) )
: Container( : _buildPlaceholder(),
padding: const EdgeInsets.all(16.0), ),
color: AppColors.black.withOpacity(0.05), // Upload progress overlay
child: Assets.icons.image.svg(), if (isUploading)
), Positioned.fill(
child: Container(
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.6),
),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: 24,
height: 24,
child: CircularProgressIndicator(
color: Colors.white,
strokeWidth: 2,
value: _uploadAnimation.value == 1.0
? null
: _uploadAnimation.value,
),
),
const SizedBox(height: 8),
const Text(
'Uploading...',
style: TextStyle(
color: Colors.white,
fontSize: 10,
fontWeight: FontWeight.w500,
),
),
],
),
),
), ),
), ),
const Spacer(), // Overlay gradient for better button visibility
Padding( if ((imagePath != null ||
padding: const EdgeInsets.only(right: 10.0), uploadedImageUrl != null ||
child: Button.filled( (hasInitialImage && widget.initialImageUrl != null)) &&
height: 30.0, !isUploading)
width: 140.0, Positioned.fill(
onPressed: _pickImage, child: Container(
label: 'Choose Photo', decoration: BoxDecoration(
fontSize: 12.0, gradient: LinearGradient(
borderRadius: 5.0, colors: [
Colors.transparent,
Colors.black.withOpacity(0.3),
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
),
), ),
), ),
],
),
),
);
}
Widget _buildPlaceholder() {
return Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
AppColors.primary.withOpacity(0.1),
AppColors.primary.withOpacity(0.05),
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.add_photo_alternate_outlined,
size: 32,
color: AppColors.primary.withOpacity(0.6),
),
const SizedBox(height: 4),
Text(
'Photo',
style: TextStyle(
fontSize: 10,
color: AppColors.primary.withOpacity(0.6),
fontWeight: FontWeight.w500,
),
),
],
),
),
);
}
Widget _buildActionButton() {
bool hasImage = imagePath != null ||
uploadedImageUrl != null ||
(hasInitialImage && widget.initialImageUrl != null);
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
gradient: LinearGradient(
colors: [
AppColors.primary,
AppColors.primary.withOpacity(0.8),
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
boxShadow: [
BoxShadow(
color: AppColors.primary.withOpacity(0.3),
blurRadius: 8,
offset: const Offset(0, 4),
),
],
),
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: isUploading ? null : _pickImage,
onHover: (hover) {
setState(() {
isHovering = hover;
});
},
borderRadius: BorderRadius.circular(12),
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: isHovering
? Colors.white.withOpacity(0.1)
: Colors.transparent,
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
isUploading
? Icons.cloud_upload_outlined
: hasImage
? Icons.edit_outlined
: Icons.add_photo_alternate_outlined,
color: Colors.white,
size: 18,
),
const SizedBox(width: 8),
Text(
isUploading
? 'Uploading...'
: hasImage
? 'Change Photo'
: 'Choose Photo',
style: const TextStyle(
color: Colors.white,
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
],
),
),
),
),
);
}
Widget _buildUploadButton() {
if (!widget.autoUpload &&
imagePath != null &&
uploadedImageUrl == null &&
!isUploading) {
return Padding(
padding: const EdgeInsets.only(top: 12),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
gradient: LinearGradient(
colors: [
Colors.green.shade600,
Colors.green.shade500,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
boxShadow: [
BoxShadow(
color: Colors.green.withOpacity(0.3),
blurRadius: 8,
offset: const Offset(0, 4),
),
], ],
), ),
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: () => _uploadImage(imagePath!),
borderRadius: BorderRadius.circular(12),
child: Container(
padding:
const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.cloud_upload_outlined,
color: Colors.white,
size: 16,
),
const SizedBox(width: 8),
Text(
'Upload to Server',
style: const TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.w600,
),
),
],
),
),
),
),
), ),
], );
}
return const SizedBox.shrink();
}
@override
Widget build(BuildContext context) {
return BlocListener<UploadFileBloc, UploadFileState>(
listener: (context, state) {
state.when(
initial: () {},
loading: () {
if (!isUploading) {
setState(() {
isUploading = true;
});
_uploadController.repeat();
}
},
success: (fileData) {
setState(() {
isUploading = false;
uploadedImageUrl = fileData.fileUrl;
});
_uploadController.reset();
if (widget.onUploaded != null) {
widget.onUploaded!(fileData.fileUrl);
}
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Row(
children: [
Icon(Icons.check_circle, color: Colors.white),
SizedBox(width: 8),
Text('Image uploaded successfully!'),
],
),
backgroundColor: Colors.green,
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
duration: Duration(seconds: 2),
),
);
},
error: (message) {
setState(() {
isUploading = false;
});
_uploadController.reset();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Row(
children: [
Icon(Icons.error_outline, color: Colors.white),
SizedBox(width: 8),
Expanded(child: Text('Upload failed: $message')),
],
),
backgroundColor: Colors.red,
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
duration: Duration(seconds: 3),
),
);
},
);
},
child: FadeTransition(
opacity: _fadeAnimation,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (widget.showLabel) ...[
Text(
widget.label,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700,
color: Colors.black87,
),
),
const SpaceHeight(16.0),
],
ScaleTransition(
scale: _scaleAnimation,
child: Container(
padding: const EdgeInsets.all(20.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(24.0),
color: Colors.white,
border: Border.all(
color: isUploading
? Colors.orange.withOpacity(0.5)
: uploadedImageUrl != null
? Colors.green.withOpacity(0.5)
: AppColors.primary.withOpacity(0.2),
width: 1.5,
),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 20,
offset: const Offset(0, 10),
),
],
),
child: Column(
children: [
_buildImageContainer(),
const SizedBox(height: 20),
_buildActionButton(),
_buildUploadButton(),
if ((imagePath != null ||
uploadedImageUrl != null ||
(hasInitialImage &&
widget.initialImageUrl != null)) &&
!isUploading) ...[
const SizedBox(height: 12),
TextButton.icon(
onPressed: () {
setState(() {
imagePath = null;
hasInitialImage = false;
uploadedImageUrl = null;
});
widget.onChanged(null);
if (widget.onUploaded != null) {
widget.onUploaded!(null);
}
},
icon: Icon(
Icons.delete_outline,
size: 16,
color: Colors.red.shade400,
),
label: Text(
'Remove Photo',
style: TextStyle(
color: Colors.red.shade400,
fontSize: 12,
fontWeight: FontWeight.w500,
),
),
),
],
// Upload status indicator
if (uploadedImageUrl != null && !isUploading) ...[
const SizedBox(height: 8),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: Colors.green.withOpacity(0.1),
borderRadius: BorderRadius.circular(20),
border:
Border.all(color: Colors.green.withOpacity(0.3)),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.cloud_done_outlined,
size: 14,
color: Colors.green.shade600,
),
const SizedBox(width: 6),
Text(
'Uploaded',
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.w600,
color: Colors.green.shade600,
),
),
],
),
),
],
],
),
),
),
],
),
),
); );
} }
} }
/// Cara menggunakan widget ini:
///
/// ```dart
/// // 1. Basic usage tanpa auto upload
/// ImagePickerWidget(
/// label: 'Product Image',
/// onChanged: (file) {
/// // Handle selected file
/// print('Selected file: ${file?.path}');
/// },
/// onUploaded: (url) {
/// // Handle uploaded URL
/// print('Uploaded URL: $url');
/// },
/// )
///
/// // 2. Auto upload setelah memilih gambar
/// ImagePickerWidget(
/// label: 'Profile Picture',
/// autoUpload: true,
/// onChanged: (file) => setState(() => selectedFile = file),
/// onUploaded: (url) => setState(() => profileImageUrl = url),
/// )
///
/// // 3. Dengan initial image
/// ImagePickerWidget(
/// label: 'Banner Image',
/// initialImageUrl: existingImageUrl,
/// onChanged: (file) => handleFileChange(file),
/// onUploaded: (url) => handleUploadSuccess(url),
/// )
/// ```
///
/// Pastikan untuk wrap widget ini dengan BlocProvider:
/// ```dart
/// BlocProvider(
/// create: (context) => UploadFileBloc(
/// context.read<FileRemoteDataSource>(),
/// ),
/// child: ImagePickerWidget(...),
/// )
/// ```
+4
View File
@@ -39,4 +39,8 @@ class AppColors {
static const Color stroke = Color(0xffEFF0F6); static const Color stroke = Color(0xffEFF0F6);
static const Color background = Color.fromARGB(255, 241, 241, 241); static const Color background = Color.fromARGB(255, 241, 241, 241);
static const Color primaryLight = Color(0xFF5A3E8A);
static const Color greyLight = Color(0xFFE0E0E0);
static const Color greyDark = Color(0xFF707070);
} }
+183
View File
@@ -0,0 +1,183 @@
import 'dart:developer';
import 'dart:typed_data';
import 'package:barcode_image/barcode_image.dart';
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/settings_local_datasource.dart';
import 'package:enaklo_pos/data/models/response/order_response_model.dart';
import 'package:enaklo_pos/data/type/bussines_type.dart';
import 'package:enaklo_pos/presentation/home/models/product_quantity.dart';
import 'package:flutter/material.dart';
import 'package:barcode/barcode.dart';
import 'package:image/image.dart' as img;
Future<void> onPrint(
BuildContext context, {
required List<ProductQuantity> productQuantity,
required Order order,
}) async {
final outlet = await OutletLocalDatasource().get();
if (outlet.businessType == BusinessType.restaurant) {
final checkerPrinter =
await ProductLocalDatasource.instance.getPrinterByCode('checker');
final kitchenPrinter =
await ProductLocalDatasource.instance.getPrinterByCode('kitchen');
final barPrinter =
await ProductLocalDatasource.instance.getPrinterByCode('bar');
final authData = await AuthLocalDataSource().getAuthData();
// Checker printer
if (checkerPrinter != null) {
try {
final printValue = await PrintDataoutputs.instance.printChecker(
productQuantity,
order.tableNumber ?? "",
order.orderNumber ?? "",
authData.user?.name ?? "",
checkerPrinter.paper.toIntegerFromText,
order.orderType ?? "",
);
await PrinterService()
// ignore: use_build_context_synchronously
.printWithPrinter(checkerPrinter, printValue, context);
} catch (e) {
log("Error printing checker: $e");
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error printing checker: $e')),
);
}
}
// Kitchen printer
if (kitchenPrinter != null) {
try {
final printValue = await PrintDataoutputs.instance.printKitchen(
productQuantity,
order.tableNumber!,
order.orderNumber ?? "",
authData.user?.name ?? "",
kitchenPrinter.paper.toIntegerFromText,
order.orderType ?? "",
);
await PrinterService()
.printWithPrinter(kitchenPrinter, printValue, context);
} catch (e) {
log("Error printing kitchen order: $e");
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error printing kitchen order: $e')),
);
}
}
// Bar printer
if (barPrinter != null) {
try {
final printValue = await PrintDataoutputs.instance.printBar(
productQuantity,
order.tableNumber ?? "",
order.orderNumber ?? "",
authData.user?.name ?? "",
barPrinter.paper.toIntegerFromText,
order.orderType ?? "",
);
await PrinterService()
.printWithPrinter(barPrinter, printValue, context);
} catch (e) {
log("Error printing bar order: $e");
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error printing bar order: $e')),
);
}
}
}
if (outlet.businessType == BusinessType.ticketing) {
final ticketPrinter =
await ProductLocalDatasource.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) {
log("Error printing ticket: $e");
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error printing ticket: $e')),
);
}
}
}
}
Future<void> onPrintRecipt(
context, {
required Order order,
required String paymentMethod,
required int nominalBayar,
required int kembalian,
}) async {
final receiptPrinter =
await ProductLocalDatasource.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.printOrderV4(
order,
authData.user?.name ?? "",
paymentMethod,
nominalBayar,
kembalian,
settings.value,
receiptPrinter.paper.toIntegerFromText,
order.orderType ?? "",
outlet,
);
await PrinterService()
.printWithPrinter(receiptPrinter, printValue, context);
} catch (e) {
log("Error printing receipt order: $e");
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error printing receipt order: $e')),
);
}
}
}
Future<Uint8List> generateBarcodeAsUint8List(String data) async {
// 1. Buat barcode instance (code128, qrCode, dll)
final barcode = Barcode.code128();
// 2. Siapkan canvas image dari package `image`
final image = img.Image(width: 600, height: 200);
// 3. Gambar barcode ke canvas menggunakan barcode_image
drawBarcode(
image,
barcode,
data,
);
// 4. Encode image ke Uint8List PNG
return Uint8List.fromList(img.encodePng(image));
}
+160 -1
View File
@@ -1,6 +1,9 @@
// lib/core/network/dio_client.dart
import 'package:awesome_dio_interceptor/awesome_dio_interceptor.dart'; import 'package:awesome_dio_interceptor/awesome_dio_interceptor.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
import 'package:enaklo_pos/presentation/auth/login_page.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class DioClient { class DioClient {
static final Dio _dio = Dio(BaseOptions( static final Dio _dio = Dio(BaseOptions(
@@ -10,6 +13,7 @@ class DioClient {
'Accept': 'application/json', 'Accept': 'application/json',
}, },
)) ))
..interceptors.add(AuthInterceptor())
..interceptors.add( ..interceptors.add(
AwesomeDioInterceptor( AwesomeDioInterceptor(
logRequestTimeout: true, logRequestTimeout: true,
@@ -20,3 +24,158 @@ class DioClient {
static Dio get instance => _dio; static Dio get instance => _dio;
} }
class AuthInterceptor extends Interceptor {
static final GlobalKey<NavigatorState> navigatorKey =
GlobalKey<NavigatorState>();
@override
void onRequest(
RequestOptions options, RequestInterceptorHandler handler) async {
// Add token to request headers
final prefs = await SharedPreferences.getInstance();
final token = prefs.getString('auth_token');
if (token != null) {
options.headers['Authorization'] = 'Bearer $token';
}
handler.next(options);
}
@override
void onResponse(Response response, ResponseInterceptorHandler handler) {
handler.next(response);
}
@override
void onError(DioException err, ErrorInterceptorHandler handler) async {
// Check if error is 401 (Unauthorized) - token expired
if (err.response?.statusCode == 401) {
await _handleTokenExpired();
}
handler.next(err);
}
Future<void> _handleTokenExpired() async {
// Clear stored token
final prefs = await SharedPreferences.getInstance();
await prefs.remove('auth_token');
await prefs.remove('refresh_token');
await prefs.clear(); // Optional: clear all user data
// Navigate to login page
final context = navigatorKey.currentContext;
if (context != null) {
// Option 1: Navigate and remove all previous routes
context.pushReplacement(LoginPage());
// Option 2: If using GoRouter, uncomment below:
// GoRouter.of(context).go('/login');
// Option 3: If using custom routing, uncomment below:
// Navigator.of(context).pushAndRemoveUntil(
// MaterialPageRoute(builder: (context) => LoginPage()),
// (route) => false,
// );
}
}
}
// Alternative: Dengan Refresh Token Logic
class AuthInterceptorWithRefresh extends Interceptor {
static final GlobalKey<NavigatorState> navigatorKey =
GlobalKey<NavigatorState>();
@override
void onRequest(
RequestOptions options, RequestInterceptorHandler handler) async {
final prefs = await SharedPreferences.getInstance();
final token = prefs.getString('auth_token');
if (token != null) {
options.headers['Authorization'] = 'Bearer $token';
}
handler.next(options);
}
@override
void onError(DioException err, ErrorInterceptorHandler handler) async {
if (err.response?.statusCode == 401) {
// Try refresh token first
final success = await _tryRefreshToken();
if (success) {
// Retry the original request
final response = await _retryRequest(err.requestOptions);
handler.resolve(response);
return;
} else {
// Refresh failed, redirect to login
await _handleTokenExpired();
}
}
handler.next(err);
}
Future<bool> _tryRefreshToken() async {
try {
final prefs = await SharedPreferences.getInstance();
final refreshToken = prefs.getString('refresh_token');
if (refreshToken == null) return false;
final response = await Dio().post(
'YOUR_REFRESH_TOKEN_ENDPOINT',
data: {'refresh_token': refreshToken},
);
if (response.statusCode == 200) {
final newToken = response.data['access_token'];
final newRefreshToken = response.data['refresh_token'];
await prefs.setString('auth_token', newToken);
await prefs.setString('refresh_token', newRefreshToken);
return true;
}
} catch (e) {
print('Refresh token failed: $e');
}
return false;
}
Future<Response> _retryRequest(RequestOptions options) async {
final prefs = await SharedPreferences.getInstance();
final token = prefs.getString('auth_token');
options.headers['Authorization'] = 'Bearer $token';
return await DioClient.instance.request(
options.path,
options: Options(
method: options.method,
headers: options.headers,
),
data: options.data,
queryParameters: options.queryParameters,
);
}
Future<void> _handleTokenExpired() async {
final prefs = await SharedPreferences.getInstance();
await prefs.clear();
final context = navigatorKey.currentContext;
if (context != null) {
Navigator.of(context).pushNamedAndRemoveUntil(
'/login',
(route) => false,
);
}
}
}
+298
View File
@@ -1,5 +1,7 @@
import 'dart:math'; import 'dart:math';
import 'package:enaklo_pos/data/models/response/order_response_model.dart';
import 'package:enaklo_pos/presentation/home/models/outlet_model.dart';
import 'package:esc_pos_utils_plus/esc_pos_utils_plus.dart'; import 'package:esc_pos_utils_plus/esc_pos_utils_plus.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:enaklo_pos/core/extensions/int_ext.dart'; import 'package:enaklo_pos/core/extensions/int_ext.dart';
@@ -739,6 +741,267 @@ class PrintDataoutputs {
return bytes; return bytes;
} }
Future<List<int>> printOrderV4(
Order order,
String chashierName,
String paymentMethod,
int nominalBayar,
int kembalian,
int taxPercentage,
int paper,
String orderType,
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: 'JF-${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),
),
]);
bytes += generator.row([
PosColumn(
text: 'Pembayaran',
width: 8,
styles: const PosStyles(align: PosAlign.left),
),
PosColumn(
text: paymentMethod,
width: 4,
styles: const PosStyles(align: PosAlign.right),
),
]);
bytes += generator.text(
paper == 80
? '------------------------------------------------'
: '--------------------------------',
styles: const PosStyles(bold: false, align: PosAlign.center));
bytes += generator.text(orderType,
styles: const PosStyles(bold: true, align: PosAlign.center));
bytes += generator.text(
paper == 80
? '------------------------------------------------'
: '--------------------------------',
styles: const PosStyles(bold: false, align: PosAlign.center));
for (final product
in (order.orderItems?.where((item) => item.status != 'cancelled') ??
<OrderItem>[])) {
bytes += generator.row([
PosColumn(
text: '${product.quantity} x ${product.productName}',
width: 8,
styles: const PosStyles(bold: true, align: PosAlign.left),
),
PosColumn(
text: (product.totalPrice ?? 0).currencyFormatRpV2,
width: 4,
styles: const PosStyles(bold: true, align: PosAlign.right),
),
]);
}
bytes += generator.text(
paper == 80
? '------------------------------------------------'
: '--------------------------------',
styles: const PosStyles(bold: false, align: PosAlign.center));
bytes += generator.row([
PosColumn(
text: 'Subtotal ${order.orderItems?.length ?? "0"} Product',
width: 6,
styles: const PosStyles(align: PosAlign.left),
),
PosColumn(
text: (order.subtotal ?? 0).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 ($taxPercentage%)',
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.totalAmount ?? ""}'.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: nominalBayar.currencyFormatRpV2,
width: 6,
styles: const PosStyles(align: PosAlign.right),
),
]);
bytes += generator.row([
PosColumn(
text: 'Kembali',
width: 6,
styles: const PosStyles(align: PosAlign.left),
),
PosColumn(
text: kembalian.currencyFormatRpV2,
width: 6,
styles: const PosStyles(align: PosAlign.right),
),
]);
bytes += generator.text(
paper == 80
? '------------------------------------------------'
: '--------------------------------',
styles: const PosStyles(bold: false, align: PosAlign.center));
// bytes += generator.text('Notes',
// styles: const PosStyles(bold: false, align: PosAlign.center));
// bytes += generator.text('Pass Wifi: fic14jilid2',
// styles: const PosStyles(bold: false, align: PosAlign.center));
// //terima kasih
// bytes += generator.text('Terima Kasih',
// styles: const PosStyles(bold: true, align: PosAlign.center));
paper == 80 ? bytes += generator.feed(3) : bytes += generator.feed(1);
bytes += generator.cut();
return bytes;
}
Future<List<int>> printQRIS( Future<List<int>> printQRIS(
int totalPrice, Uint8List imageQris, int paper) async { int totalPrice, Uint8List imageQris, int paper) async {
List<int> bytes = []; List<int> bytes = [];
@@ -1140,4 +1403,39 @@ class PrintDataoutputs {
return bytes; return bytes;
} }
Future<List<int>> printTicket(
int totalPrice, Uint8List imageQris, int paper) async {
List<int> bytes = [];
final profile = await CapabilityProfile.load();
final generator =
Generator(paper == 58 ? PaperSize.mm58 : PaperSize.mm80, profile);
final img.Image? orginalImage = img.decodeImage(imageQris);
bytes += generator.reset();
// final Uint8List bytesData = data.buffer.asUint8List();
// final img.Image? orginalImage = img.decodeImage(bytesData);
// bytes += generator.reset();
bytes += generator.text('Scan Ticket',
styles: const PosStyles(bold: false, align: PosAlign.center));
bytes += generator.feed(2);
if (orginalImage != null) {
final img.Image grayscalledImage = img.grayscale(orginalImage);
final img.Image resizedImage =
img.copyResize(grayscalledImage, width: 240);
bytes += generator.imageRaster(resizedImage, align: PosAlign.center);
bytes += generator.feed(1);
}
bytes += generator.text('Price : ${totalPrice.currencyFormatRp}',
styles: const PosStyles(bold: false, align: PosAlign.center));
bytes += generator.feed(4);
bytes += generator.cut();
return bytes;
}
} }
@@ -0,0 +1,187 @@
import 'dart:developer';
import 'package:dartz/dartz.dart';
import 'package:dio/dio.dart';
import 'package:enaklo_pos/core/constants/variables.dart';
import 'package:enaklo_pos/core/network/dio_client.dart';
import 'package:enaklo_pos/data/datasources/auth_local_datasource.dart';
import 'package:enaklo_pos/data/models/response/dashboard_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';
import 'package:enaklo_pos/data/models/response/profit_loss_response_model.dart';
import 'package:enaklo_pos/data/models/response/sales_analytic_response_model.dart';
import 'package:intl/intl.dart';
class AnalyticRemoteDatasource {
final Dio dio = DioClient.instance;
Future<Either<String, PaymentMethodAnalyticResponseModel>> getPaymentMethod({
required DateTime dateFrom,
required DateTime dateTo,
}) async {
final authData = await AuthLocalDataSource().getAuthData();
final headers = {
'Authorization': 'Bearer ${authData.token}',
'Accept': 'application/json',
};
try {
final response = await dio.get(
'${Variables.baseUrl}/api/v1/analytics/payment-methods',
queryParameters: {
'date_from': DateFormat('dd-MM-yyyy').format(dateFrom),
'date_to': DateFormat('dd-MM-yyyy').format(dateTo),
},
options: Options(headers: headers),
);
if (response.statusCode == 200) {
return right(PaymentMethodAnalyticResponseModel.fromMap(response.data));
} else {
return left('Terjadi Kesalahan, Coba lagi nanti.');
}
} on DioException catch (e) {
log('Dio error: ${e.message}');
return left(e.response?.data.toString() ?? e.message ?? 'Unknown error');
} catch (e) {
log('Unexpected error: $e');
return left('Unexpected error occurred');
}
}
Future<Either<String, SalesAnalyticResponseModel>> getSales({
required DateTime dateFrom,
required DateTime dateTo,
}) async {
final authData = await AuthLocalDataSource().getAuthData();
final headers = {
'Authorization': 'Bearer ${authData.token}',
'Accept': 'application/json',
};
try {
final response = await dio.get(
'${Variables.baseUrl}/api/v1/analytics/sales',
queryParameters: {
'date_from': DateFormat('dd-MM-yyyy').format(dateFrom),
'date_to': DateFormat('dd-MM-yyyy').format(dateTo),
},
options: Options(headers: headers),
);
if (response.statusCode == 200) {
return right(SalesAnalyticResponseModel.fromMap(response.data));
} else {
return left('Terjadi Kesalahan, Coba lagi nanti.');
}
} on DioException catch (e) {
log('Dio error: ${e.message}');
return left(e.response?.data.toString() ?? e.message ?? 'Unknown error');
} catch (e) {
log('Unexpected error: $e');
return left('Unexpected error occurred');
}
}
Future<Either<String, ProductAnalyticResponseModel>> getProduct({
required DateTime dateFrom,
required DateTime dateTo,
}) async {
final authData = await AuthLocalDataSource().getAuthData();
final headers = {
'Authorization': 'Bearer ${authData.token}',
'Accept': 'application/json',
};
try {
final response = await dio.get(
'${Variables.baseUrl}/api/v1/analytics/products',
queryParameters: {
'date_from': DateFormat('dd-MM-yyyy').format(dateFrom),
'date_to': DateFormat('dd-MM-yyyy').format(dateTo),
},
options: Options(headers: headers),
);
if (response.statusCode == 200) {
return right(ProductAnalyticResponseModel.fromMap(response.data));
} else {
return left('Terjadi Kesalahan, Coba lagi nanti.');
}
} on DioException catch (e) {
log('Dio error: ${e.message}');
return left(e.response?.data.toString() ?? e.message ?? 'Unknown error');
} catch (e) {
log('Unexpected error: $e');
return left('Unexpected error occurred');
}
}
Future<Either<String, DashboardAnalyticResponseModel>> getDashboard({
required DateTime dateFrom,
required DateTime dateTo,
}) async {
final authData = await AuthLocalDataSource().getAuthData();
final headers = {
'Authorization': 'Bearer ${authData.token}',
'Accept': 'application/json',
};
try {
final response = await dio.get(
'${Variables.baseUrl}/api/v1/analytics/dashboard',
queryParameters: {
'date_from': DateFormat('dd-MM-yyyy').format(dateFrom),
'date_to': DateFormat('dd-MM-yyyy').format(dateTo),
},
options: Options(headers: headers),
);
if (response.statusCode == 200) {
return right(DashboardAnalyticResponseModel.fromMap(response.data));
} else {
return left('Terjadi Kesalahan, Coba lagi nanti.');
}
} on DioException catch (e) {
log('Dio error: ${e.message}');
return left(e.response?.data.toString() ?? e.message ?? 'Unknown error');
} catch (e) {
log('Unexpected error: $e');
return left('Unexpected error occurred');
}
}
Future<Either<String, ProfitLossResponseModel>> getProfitLoss({
required DateTime dateFrom,
required DateTime dateTo,
}) async {
final authData = await AuthLocalDataSource().getAuthData();
final headers = {
'Authorization': 'Bearer ${authData.token}',
'Accept': 'application/json',
};
try {
final response = await dio.get(
'${Variables.baseUrl}/api/v1/analytics/profit-loss',
queryParameters: {
'date_from': DateFormat('dd-MM-yyyy').format(dateFrom),
'date_to': DateFormat('dd-MM-yyyy').format(dateTo),
},
options: Options(headers: headers),
);
if (response.statusCode == 200) {
return right(ProfitLossResponseModel.fromMap(response.data));
} else {
return left('Terjadi Kesalahan, Coba lagi nanti.');
}
} on DioException catch (e) {
log('Dio error: ${e.message}');
return left(e.response?.data.toString() ?? e.message ?? 'Unknown error');
} catch (e) {
log('Unexpected error: $e');
return left('Unexpected error occurred');
}
}
}
@@ -1,27 +1,48 @@
import 'dart:developer'; import 'dart:developer';
import 'package:dartz/dartz.dart'; import 'package:dartz/dartz.dart';
import 'package:dio/dio.dart';
import 'package:enaklo_pos/core/constants/variables.dart'; import 'package:enaklo_pos/core/constants/variables.dart';
import 'package:enaklo_pos/core/network/dio_client.dart';
import 'package:enaklo_pos/data/datasources/auth_local_datasource.dart'; import 'package:enaklo_pos/data/datasources/auth_local_datasource.dart';
import 'package:enaklo_pos/data/models/response/category_response_model.dart'; import 'package:enaklo_pos/data/models/response/category_response_model.dart';
import 'package:http/http.dart' as http;
class CategoryRemoteDatasource { class CategoryRemoteDatasource {
Future<Either<String, CategroyResponseModel>> getCategories() async { final Dio dio = DioClient.instance;
Future<Either<String, CategoryResponseModel>> getCategories({
int page = 1,
int limit = 10,
bool isActive = true,
}) async {
final authData = await AuthLocalDataSource().getAuthData(); final authData = await AuthLocalDataSource().getAuthData();
final Map<String, String> headers = { final headers = {
'Authorization': 'Bearer ${authData.token}', 'Authorization': 'Bearer ${authData.token}',
'Accept': 'application/json', 'Accept': 'application/json',
}; };
final response = await http.get(
Uri.parse('${Variables.baseUrl}/api/api-categories'), try {
headers: headers); final response = await dio.get(
log(response.statusCode.toString()); '${Variables.baseUrl}/api/v1/categories',
log(response.body); queryParameters: {
if (response.statusCode == 200) { 'page': page,
return right(CategroyResponseModel.fromJson(response.body)); 'limit': limit,
} else { 'is_active': isActive,
return left(response.body); },
options: Options(headers: headers),
);
if (response.statusCode == 200) {
return right(CategoryResponseModel.fromMap(response.data));
} else {
return left(response.data.toString());
}
} on DioException catch (e) {
log('Dio error: ${e.message}');
return left(e.response?.data.toString() ?? e.message ?? 'Unknown error');
} catch (e) {
log('Unexpected error: $e');
return left('Unexpected error occurred');
} }
} }
} }
@@ -0,0 +1,15 @@
import 'package:enaklo_pos/core/assets/assets.gen.dart';
import 'package:enaklo_pos/data/models/response/delivery_response_model.dart';
List<DeliveryModel> deliveries = [
DeliveryModel(
id: 'gojek',
name: 'Gojek',
imageUrl: Assets.images.gojek.path,
),
DeliveryModel(
id: 'grab',
name: 'Grab',
imageUrl: Assets.images.grab.path,
),
];
@@ -0,0 +1,53 @@
import 'package:dartz/dartz.dart';
import 'package:dio/dio.dart';
import 'package:enaklo_pos/core/constants/variables.dart';
import 'package:enaklo_pos/core/network/dio_client.dart';
import 'package:enaklo_pos/data/datasources/auth_local_datasource.dart';
import 'package:enaklo_pos/data/models/response/file_response_model.dart';
class FileRemoteDataSource {
final Dio dio = DioClient.instance;
Future<Either<String, FileResponseModel>> uploadFile({
required String filePath,
required String fileType,
required String description,
}) async {
final url = '${Variables.baseUrl}/api/v1/files/upload';
try {
final authData = await AuthLocalDataSource().getAuthData();
// Membuat FormData
final formData = FormData.fromMap({
'file': await MultipartFile.fromFile(filePath,
filename: filePath.split('/').last),
'file_type': fileType,
'description': description,
});
final response = await dio.post(
url,
data: formData,
options: Options(
headers: {
'Authorization': 'Bearer ${authData.token}',
'Accept': 'application/json',
// Content-Type otomatis diatur oleh Dio untuk FormData
},
),
);
if (response.statusCode == 201 || response.statusCode == 200) {
// Misal response.data['url'] adalah URL file yang diupload
return Right(FileResponseModel.fromJson(response.data));
} else {
return Left('Upload gagal: ${response.statusMessage}');
}
} on DioException catch (e) {
return Left(e.response?.data['message'] ?? 'Upload gagal');
} catch (e) {
return Left('Unexpected error: $e');
}
}
}
+115 -21
View File
@@ -15,6 +15,7 @@ import 'package:enaklo_pos/data/models/response/summary_response_model.dart';
import 'package:enaklo_pos/presentation/home/models/order_model.dart'; import 'package:enaklo_pos/presentation/home/models/order_model.dart';
import 'package:enaklo_pos/presentation/home/models/order_request.dart'; import 'package:enaklo_pos/presentation/home/models/order_request.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:intl/intl.dart';
class OrderRemoteDatasource { class OrderRemoteDatasource {
final Dio dio = DioClient.instance; final Dio dio = DioClient.instance;
@@ -195,7 +196,8 @@ class OrderRemoteDatasource {
// New Api // New Api
Future<Either<String, OrderDetailResponseModel>> createOrder( Future<Either<String, OrderDetailResponseModel>> createOrder(
OrderRequestModel orderModel) async { OrderRequestModel orderModel,
) async {
final authData = await AuthLocalDataSource().getAuthData(); final authData = await AuthLocalDataSource().getAuthData();
final url = '${Variables.baseUrl}/api/v1/orders'; final url = '${Variables.baseUrl}/api/v1/orders';
@@ -218,7 +220,8 @@ class OrderRemoteDatasource {
return const Left('Gagal membuat pesanan'); return const Left('Gagal membuat pesanan');
} }
} on DioException catch (e) { } on DioException catch (e) {
final errorMessage = e.response?.data['message'] ?? 'Kesalahan jaringan'; final errorMessage =
e.response?.data['message'] ?? 'Terjadi kesalahan, coba lagi nanti.';
log("đź’Ą Dio error: ${e.message}"); log("đź’Ą Dio error: ${e.message}");
log("đź’Ą Dio response: ${e.response?.data}"); log("đź’Ą Dio response: ${e.response?.data}");
return Left(errorMessage); return Left(errorMessage);
@@ -252,7 +255,8 @@ class OrderRemoteDatasource {
return const Left('Gagal membuat pembayaran'); return const Left('Gagal membuat pembayaran');
} }
} on DioException catch (e) { } on DioException catch (e) {
final errorMessage = e.response?.data['message'] ?? 'Kesalahan jaringan'; final errorMessage =
e.response?.data['message'] ?? 'Terjadi kesalahan, coba lagi nanti.';
log("đź’Ą Dio error: ${e.message}"); log("đź’Ą Dio error: ${e.message}");
log("đź’Ą Dio response: ${e.response?.data}"); log("đź’Ą Dio response: ${e.response?.data}");
return Left(errorMessage); return Left(errorMessage);
@@ -262,20 +266,32 @@ class OrderRemoteDatasource {
} }
} }
Future<Either<String, OrderResponseModel>> getOrder( Future<Either<String, OrderResponseModel>> getOrder({
{int page = 1, int page = 1,
int limit = Variables.defaultLimit, int limit = Variables.defaultLimit,
String status = 'completed'}) async { String status = 'completed',
required DateTime dateFrom,
required DateTime dateTo,
String? search,
}) async {
try { try {
final authData = await AuthLocalDataSource().getAuthData(); final authData = await AuthLocalDataSource().getAuthData();
Map<String, dynamic> params = {
'page': page,
'limit': limit,
'status': status,
'date_from': DateFormat('dd-MM-yyyy').format(dateFrom),
'date_to': DateFormat('dd-MM-yyyy').format(dateTo),
};
if (search != null && search.isNotEmpty) {
params['search'] = search;
}
final response = await dio.get( final response = await dio.get(
'${Variables.baseUrl}/api/v1/orders', '${Variables.baseUrl}/api/v1/orders',
queryParameters: { queryParameters: params,
'page': page,
'limit': limit,
'status': status,
'outlet_id': authData.user?.outletId,
},
options: Options( options: Options(
headers: { headers: {
'Authorization': 'Bearer ${authData.token}', 'Authorization': 'Bearer ${authData.token}',
@@ -378,7 +394,8 @@ class OrderRemoteDatasource {
return const Left('Gagal membuat pesanan'); return const Left('Gagal membuat pesanan');
} }
} on DioException catch (e) { } on DioException catch (e) {
final errorMessage = e.response?.data['message'] ?? 'Kesalahan jaringan'; final errorMessage =
e.response?.data['message'] ?? 'Terjadi kesalahan, coba lagi nanti.';
log("đź’Ą Dio error: ${e.message}"); log("đź’Ą Dio error: ${e.message}");
log("đź’Ą Dio response: ${e.response?.data}"); log("đź’Ą Dio response: ${e.response?.data}");
return Left(errorMessage); return Left(errorMessage);
@@ -388,7 +405,7 @@ class OrderRemoteDatasource {
} }
} }
Future<Either<String, OrderDetailResponseModel>> addToOrder({ Future<Either<String, bool>> addToOrder({
required String orderId, required String orderId,
required List<OrderItemRequest> orderItems, required List<OrderItemRequest> orderItems,
}) async { }) async {
@@ -412,19 +429,19 @@ class OrderRemoteDatasource {
); );
if (response.statusCode == 200) { if (response.statusCode == 200) {
final data = OrderDetailResponseModel.fromMap(response.data); return Right(true);
return Right(data);
} else { } else {
return const Left('Gagal menambahkan pesanan pesanan'); return const Left('Gagal menambahkan pesanan pesanan');
} }
} on DioException catch (e) { } on DioException catch (e) {
final errorMessage = e.response?.data['message'] ?? 'Kesalahan jaringan'; final errorMessage =
e.response?.data['message'] ?? 'Terjadi kesalahan, coba lagi nanti.';
log("đź’Ą Dio error: ${e.message}"); log("đź’Ą Dio error: ${e.message}");
log("đź’Ą Dio response: ${e.response?.data}"); log("đź’Ą Dio response: ${e.response?.data}");
return Left(errorMessage); return Left(errorMessage);
} catch (e) { } catch (e) {
log("đź’Ą Unexpected error: $e"); log("đź’Ą Unexpected error: $e");
return const Left('Terjadi kesalahan tak terduga'); return const Left('Terjadi kesalahan, coba lagi nanti.');
} }
} }
@@ -470,7 +487,8 @@ class OrderRemoteDatasource {
return const Left('Gagal refund'); return const Left('Gagal refund');
} }
} on DioException catch (e) { } on DioException catch (e) {
final errorMessage = e.response?.data['message'] ?? 'Kesalahan jaringan'; final errorMessage =
e.response?.data['message'] ?? 'Terjadi kesalahan, coba lagi nanti.';
log("đź’Ą Dio error: ${e.message}"); log("đź’Ą Dio error: ${e.message}");
log("đź’Ą Dio response: ${e.response?.data}"); log("đź’Ą Dio response: ${e.response?.data}");
return Left(errorMessage); return Left(errorMessage);
@@ -518,7 +536,83 @@ class OrderRemoteDatasource {
return const Left('Gagal refund'); return const Left('Gagal refund');
} }
} on DioException catch (e) { } on DioException catch (e) {
final errorMessage = e.response?.data['message'] ?? 'Kesalahan jaringan'; final errorMessage =
e.response?.data['message'] ?? 'Terjadi kesalahan, coba lagi nanti.';
log("đź’Ą Dio error: ${e.message}");
log("đź’Ą Dio response: ${e.response?.data}");
return Left(errorMessage);
} catch (e) {
log("đź’Ą Unexpected error: $e");
return const Left('Terjadi kesalahan tak terduga');
}
}
Future<Either<String, bool>> refundPayment({
required String orderId,
required String reason,
required int refundAmount,
}) async {
final authData = await AuthLocalDataSource().getAuthData();
final url = '${Variables.baseUrl}/api/v1/orders/$orderId/refund';
try {
final response = await dio.post(
url,
data: {
'refund_amount': refundAmount,
'reason': reason,
},
options: Options(
headers: {
'Authorization': 'Bearer ${authData.token}',
'Accept': 'application/json',
'Content-Type': 'application/json',
},
),
);
if (response.statusCode == 200) {
return Right(true);
} else {
return const Left('Gagal refund');
}
} on DioException catch (e) {
final errorMessage = 'Terjadi kesalahan coba lagi nanti';
log("đź’Ą Dio error: ${e.message}");
log("đź’Ą Dio response: ${e.response?.data}");
return Left(errorMessage);
} catch (e) {
log("đź’Ą Unexpected error: $e");
return const Left('Terjadi kesalahan tak terduga');
}
}
Future<Either<String, PaymentSuccessResponseModel>> createPaymentSplitBill(
PaymentSplitBillRequest request) async {
final authData = await AuthLocalDataSource().getAuthData();
final url = '${Variables.baseUrl}/api/v1/orders/split-bill';
try {
final response = await dio.post(
url,
data: request.toMap(),
options: Options(
headers: {
'Authorization': 'Bearer ${authData.token}',
'Accept': 'application/json',
'Content-Type': 'application/json',
},
),
);
if (response.statusCode == 200) {
return Right(PaymentSuccessResponseModel.fromMap(response.data));
} else {
return const Left('Gagal membuat pembayaran');
}
} on DioException catch (e) {
final errorMessage =
e.response?.data['message'] ?? 'Terjadi kesalahan, coba lagi nanti.';
log("đź’Ą Dio error: ${e.message}"); log("đź’Ą Dio error: ${e.message}");
log("đź’Ą Dio response: ${e.response?.data}"); log("đź’Ą Dio response: ${e.response?.data}");
return Left(errorMessage); return Left(errorMessage);
@@ -0,0 +1,30 @@
import 'dart:developer';
import 'package:enaklo_pos/presentation/home/models/outlet_model.dart';
import 'package:shared_preferences/shared_preferences.dart';
class OutletLocalDatasource {
Future<void> save(Outlet outlet) async {
try {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('outlet', outlet.toJson());
log('Outlet Local Data: ${outlet.toJson()}');
} catch (e) {
log('Error saving outlet: $e');
}
}
Future<void> remove() async {
final prefs = await SharedPreferences.getInstance();
await prefs.remove('outlet');
}
Future<Outlet> get() async {
final prefs = await SharedPreferences.getInstance();
final outlet = prefs.getString('outlet');
log('Outlet Local Data: $outlet');
return Outlet.fromJson(outlet!);
}
}
@@ -2,7 +2,10 @@ import 'dart:developer';
import 'package:dartz/dartz.dart'; import 'package:dartz/dartz.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:enaklo_pos/core/network/dio_client.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/settings_local_datasource.dart';
import 'package:enaklo_pos/presentation/home/models/outlet_model.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 '../../core/constants/variables.dart';
import 'auth_local_datasource.dart'; import 'auth_local_datasource.dart';
@@ -12,6 +15,7 @@ class OutletRemoteDataSource {
Future<Either<String, OutletResponse>> getOutlets() async { Future<Either<String, OutletResponse>> getOutlets() async {
try { try {
final authData = await AuthLocalDataSource().getAuthData(); final authData = await AuthLocalDataSource().getAuthData();
final url = '${Variables.baseUrl}/api/v1/outlets/list'; final url = '${Variables.baseUrl}/api/v1/outlets/list';
final response = await dio.get( final response = await dio.get(
@@ -28,7 +32,8 @@ class OutletRemoteDataSource {
); );
if (response.statusCode == 200) { if (response.statusCode == 200) {
return Right(OutletResponse.fromMap(response.data)); final data = OutletResponse.fromMap(response.data);
return Right(data);
} else { } else {
return const Left('Failed to get outlets'); return const Left('Failed to get outlets');
} }
@@ -66,7 +71,17 @@ class OutletRemoteDataSource {
); );
if (response.statusCode == 200) { if (response.statusCode == 200) {
return Right(OutletDetailResponse.fromMap(response.data)); final data = OutletDetailResponse.fromMap(response.data);
await SettingsLocalDatasource().saveTax(
TaxModel(
name: 'PB1',
type: TaxType.pajak,
value: data.data?.taxRate ?? 0,
),
);
await OutletLocalDatasource().save(data.data!);
return Right(data);
} else { } else {
return const Left('Failed to get outlets'); return const Left('Failed to get outlets');
} }
@@ -6,7 +6,6 @@ import 'package:enaklo_pos/core/network/dio_client.dart';
import 'package:enaklo_pos/data/models/request/product_request_model.dart'; import 'package:enaklo_pos/data/models/request/product_request_model.dart';
import 'package:enaklo_pos/data/models/response/add_product_response_model.dart'; import 'package:enaklo_pos/data/models/response/add_product_response_model.dart';
import 'package:enaklo_pos/data/models/response/product_response_model.dart'; import 'package:enaklo_pos/data/models/response/product_response_model.dart';
import 'package:http/http.dart' as http;
import '../../core/constants/variables.dart'; import '../../core/constants/variables.dart';
import 'auth_local_datasource.dart'; import 'auth_local_datasource.dart';
@@ -14,13 +13,32 @@ import 'auth_local_datasource.dart';
class ProductRemoteDatasource { class ProductRemoteDatasource {
final Dio dio = DioClient.instance; final Dio dio = DioClient.instance;
Future<Either<String, ProductResponseModel>> getProducts() async { Future<Either<String, ProductResponseModel>> getProducts({
int page = 1,
int limit = Variables.defaultLimit,
String? categoryId,
String? search,
}) async {
try { try {
final authData = await AuthLocalDataSource().getAuthData(); final authData = await AuthLocalDataSource().getAuthData();
final url = '${Variables.baseUrl}/api/v1/products'; final url = '${Variables.baseUrl}/api/v1/products';
Map<String, dynamic> queryParameters = {
'page': page,
'limit': limit,
};
if (categoryId != null) {
queryParameters['category_id'] = categoryId;
}
if (search != null && search.isNotEmpty) {
queryParameters['search'] = search;
}
final response = await dio.get( final response = await dio.get(
url, url,
queryParameters: queryParameters,
options: Options( options: Options(
headers: { headers: {
'Authorization': 'Bearer ${authData.token}', 'Authorization': 'Bearer ${authData.token}',
@@ -45,67 +63,64 @@ class ProductRemoteDatasource {
Future<Either<String, AddProductResponseModel>> addProduct( Future<Either<String, AddProductResponseModel>> addProduct(
ProductRequestModel productRequestModel) async { ProductRequestModel productRequestModel) async {
final authData = await AuthLocalDataSource().getAuthData(); try {
final Map<String, String> headers = { final authData = await AuthLocalDataSource().getAuthData();
'Authorization': 'Bearer ${authData.token}', final url = '${Variables.baseUrl}/api/v1/products';
};
var request = http.MultipartRequest(
'POST', Uri.parse('${Variables.baseUrl}/api/products'));
request.fields.addAll(productRequestModel.toMap());
request.files.add(await http.MultipartFile.fromPath(
'image', productRequestModel.image!.path));
request.headers.addAll(headers);
http.StreamedResponse response = await request.send(); final response = await dio.post(
url,
data: productRequestModel.toMap(),
options: Options(
headers: {
'Authorization': 'Bearer ${authData.token}',
'Accept': 'application/json',
},
),
);
final String body = await response.stream.bytesToString(); if (response.statusCode == 200) {
log(response.stream.toString()); return Right(AddProductResponseModel.fromMap(response.data));
log(response.statusCode.toString()); } else {
if (response.statusCode == 201) { return const Left('Failed to create products');
return right(AddProductResponseModel.fromJson(body)); }
} else { } on DioException catch (e) {
return left(body); log("Dio error: ${e.message}");
return Left(e.response?.data['message'] ?? 'Gagal menambah produk');
} catch (e) {
log("Unexpected error: $e");
return const Left('Unexpected error occurred');
} }
} }
Future<Either<String, AddProductResponseModel>> updateProduct( Future<Either<String, AddProductResponseModel>> updateProduct(
ProductRequestModel productRequestModel) async { ProductRequestModel productRequestModel) async {
final authData = await AuthLocalDataSource().getAuthData(); try {
final Map<String, String> headers = { final authData = await AuthLocalDataSource().getAuthData();
'Authorization': 'Bearer ${authData.token}', final url =
}; '${Variables.baseUrl}/api/v1/products/${productRequestModel.id}';
log("Update Product Request Data: ${productRequestModel.toMap()}"); final response = await dio.put(
log("Update Product ID: ${productRequestModel.id}"); url,
log("Update Product Name: ${productRequestModel.name}"); data: productRequestModel.toMap(),
log("Update Product Price: ${productRequestModel.price}"); options: Options(
log("Update Product Stock: ${productRequestModel.stock}"); headers: {
log("Update Product Category ID: ${productRequestModel.categoryId}"); 'Authorization': 'Bearer ${authData.token}',
log("Update Product Is Best Seller: ${productRequestModel.isBestSeller}"); 'Accept': 'application/json',
log("Update Product Printer Type: ${productRequestModel.printerType}"); },
log("Update Product Has Image: ${productRequestModel.image != null}"); ),
);
var request = http.MultipartRequest( if (response.statusCode == 200) {
'POST', Uri.parse('${Variables.baseUrl}/api/products/edit')); return Right(AddProductResponseModel.fromMap(response.data));
request.fields.addAll(productRequestModel.toMap()); } else {
if (productRequestModel.image != null) { return const Left('Failed to update products');
request.files.add(await http.MultipartFile.fromPath( }
'image', productRequestModel.image!.path)); } on DioException catch (e) {
} log("Dio error: ${e.message}");
request.headers.addAll(headers); return Left(e.response?.data['message'] ?? 'Gagal update produk');
} catch (e) {
log("Update Product Request Fields: ${request.fields}"); log("Unexpected error: $e");
log("Update Product Request Files: ${request.files.length}"); return const Left('Unexpected error occurred');
http.StreamedResponse response = await request.send();
final String body = await response.stream.bytesToString();
log("Update Product Status Code: ${response.statusCode}");
log("Update Product Body: $body");
if (response.statusCode == 200) {
return right(AddProductResponseModel.fromJson(body));
} else {
return left(body);
} }
} }
} }
@@ -55,7 +55,7 @@ class TableRemoteDataSource {
Future<Either<String, TableResponseModel>> getTable({ Future<Either<String, TableResponseModel>> getTable({
int page = 1, int page = 1,
int limit = 10, int limit = 50,
String? status, String? status,
}) async { }) async {
try { try {
@@ -131,4 +131,40 @@ class TableRemoteDataSource {
return const Left('Unexpected error occurred'); return const Left('Unexpected error occurred');
} }
} }
Future<Either<String, bool>> transferTable({
required String fromTableId,
required String toTableId,
}) async {
try {
final authData = await AuthLocalDataSource().getAuthData();
final url = '${Variables.baseUrl}/api/v1/tables/transfer';
final response = await dio.put(
url,
data: {
"from_table": fromTableId,
"to_table": toTableId,
},
options: Options(
headers: {
'Authorization': 'Bearer ${authData.token}',
'Accept': 'application/json',
},
),
);
if (response.statusCode == 200 || response.statusCode == 201) {
return Right(true);
} else {
return const Left('Failed to create table');
}
} on DioException catch (e) {
log("Dio error: ${e.message}");
return Left(e.response?.data['message'] ?? 'Gagal membuat table');
} catch (e) {
log("Unexpected error: $e");
return const Left('Unexpected error occurred');
}
}
} }
@@ -0,0 +1,47 @@
import 'dart:developer';
import 'package:dartz/dartz.dart';
import 'package:dio/dio.dart';
import 'package:enaklo_pos/core/constants/variables.dart';
import 'package:enaklo_pos/core/network/dio_client.dart';
import 'package:enaklo_pos/data/datasources/auth_local_datasource.dart';
import 'package:enaklo_pos/data/models/response/auth_response_model.dart';
class UserRemoteDatasource {
final Dio dio = DioClient.instance;
Future<Either<String, bool>> updateOutlet(String outletId) async {
AuthResponseModel authData = await AuthLocalDataSource().getAuthData();
final url = '${Variables.baseUrl}/api/v1/users/${authData.user?.id}';
try {
final response = await dio.put(
url,
data: {
'outlet_id': outletId,
},
options: Options(
headers: {
'Authorization': 'Bearer ${authData.token}',
'Accept': 'application/json',
'Content-Type': 'application/json',
},
),
);
if (response.statusCode == 200) {
authData.user?.outletId = response.data['outlet_id'];
await AuthLocalDataSource().saveAuthData(authData);
return Right(true);
} else {
return const Left('Failed to login');
}
} on DioException catch (e) {
log("Dio error: ${e.message}");
return Left(e.response?.data['message'] ?? 'Login gagal');
} catch (e) {
log("Unexpected error: $e");
return const Left('Unexpected error occurred');
}
}
}
@@ -78,3 +78,60 @@ class PaymentOrderItemModel {
"amount": amount, "amount": amount,
}; };
} }
class PaymentSplitBillRequest {
final String orderId;
final String paymentMethodId;
final String customerId;
final String type; // e.g., "AMOUNT" or "ITEM"
final int amount;
final List<SplitItem> items;
PaymentSplitBillRequest({
required this.orderId,
required this.paymentMethodId,
required this.customerId,
required this.type,
required this.amount,
required this.items,
});
Map<String, dynamic> toMap() {
Map<String, dynamic> data = {
'order_id': orderId,
'payment_method_id': paymentMethodId,
'type': type,
};
if (customerId.isNotEmpty) {
data['customer_id'] = customerId;
}
if (type == "AMOUNT") {
data['amount'] = amount;
}
if (type == "ITEM") {
data['items'] = items.map((e) => e.toJson()).toList();
}
return data;
}
}
class SplitItem {
final String orderItemId;
final int quantity;
SplitItem({
required this.orderItemId,
required this.quantity,
});
Map<String, dynamic> toJson() {
return {
'order_item_id': orderItemId,
'quantity': quantity,
};
}
}
@@ -1,40 +1,49 @@
import 'dart:developer';
import 'package:image_picker/image_picker.dart';
class ProductRequestModel { class ProductRequestModel {
final String? id; final String? id;
final String name; final String name;
final String? description;
final String categoryId;
final String? sku;
final String? barcode;
final int price; final int price;
final int stock; final int cost;
final int categoryId; final bool isActive;
final int isBestSeller; final bool hasVariants;
final XFile? image; final String imageUrl;
final String? printerType; final String? printerType;
ProductRequestModel({ ProductRequestModel({
this.id, this.id,
required this.name, required this.name,
required this.price, this.description,
required this.stock,
required this.categoryId, required this.categoryId,
required this.isBestSeller, this.sku,
this.image, this.barcode,
required this.price,
required this.cost,
this.isActive = true,
this.hasVariants = false,
required this.imageUrl,
this.printerType, this.printerType,
}); });
Map<String, String> toMap() { Map<String, dynamic> toMap() {
log("toMap: $isBestSeller"); final map = <String, dynamic>{
final map = {
'name': name, 'name': name,
'price': price.toString(), 'description': description ?? '',
'stock': stock.toString(), 'category_id': categoryId,
'category_id': categoryId.toString(), 'sku': sku ?? '',
'is_best_seller': isBestSeller.toString(), 'barcode': barcode ?? '',
'price': price,
'cost': cost,
'is_active': isActive,
'has_variants': hasVariants,
'image_url': imageUrl,
'printer_type': printerType ?? '', 'printer_type': printerType ?? '',
}; };
if (id != null) { if (id != null) {
map['id'] = id.toString(); map['id'] = id;
} }
return map; return map;
@@ -4,12 +4,10 @@ import 'package:enaklo_pos/data/models/response/product_response_model.dart';
class AddProductResponseModel { class AddProductResponseModel {
final bool success; final bool success;
final String message;
final Product data; final Product data;
AddProductResponseModel({ AddProductResponseModel({
required this.success, required this.success,
required this.message,
required this.data, required this.data,
}); });
@@ -21,13 +19,11 @@ class AddProductResponseModel {
factory AddProductResponseModel.fromMap(Map<String, dynamic> json) => factory AddProductResponseModel.fromMap(Map<String, dynamic> json) =>
AddProductResponseModel( AddProductResponseModel(
success: json["success"], success: json["success"],
message: json["message"],
data: Product.fromMap(json["data"]), data: Product.fromMap(json["data"]),
); );
Map<String, dynamic> toMap() => { Map<String, dynamic> toMap() => {
"success": success, "success": success,
"message": message,
"data": data.toMap(), "data": data.toMap(),
}; };
} }
@@ -29,7 +29,7 @@ class AuthResponseModel {
class User { class User {
final String? id; final String? id;
final String? organizationId; final String? organizationId;
final String? outletId; String? outletId;
final String? name; final String? name;
final String? email; final String? email;
final String? role; final String? role;
@@ -1,91 +1,120 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first
import 'dart:convert'; import 'dart:convert';
class CategroyResponseModel { class CategoryResponseModel {
final String status; final bool success;
final List<CategoryModel> data; final CategoryData data;
final dynamic errors;
CategroyResponseModel({ CategoryResponseModel({
required this.status, required this.success,
required this.data, required this.data,
this.errors,
}); });
Map<String, dynamic> toMap() { factory CategoryResponseModel.fromMap(Map<String, dynamic> map) {
return <String, dynamic>{ return CategoryResponseModel(
'status': status, success: map['success'] as bool,
'data': data.map((x) => x.toMap()).toList(), data: CategoryData.fromMap(map['data'] as Map<String, dynamic>),
}; errors: map['errors'],
}
factory CategroyResponseModel.fromMap(Map<String, dynamic> map) {
return CategroyResponseModel(
status: map['status'] as String,
data: List<CategoryModel>.from(
(map['data']).map<CategoryModel>(
(x) => CategoryModel.fromMap(x as Map<String, dynamic>),
),
),
); );
} }
factory CategroyResponseModel.fromJson(String str) => factory CategoryResponseModel.fromJson(String str) =>
CategroyResponseModel.fromMap(json.decode(str)); CategoryResponseModel.fromMap(json.decode(str));
String toJson() => json.encode(toMap());
}
class CategoryModel {
int? id;
String? name;
int? categoryId;
int? isSync;
String? image;
// DateTime createdAt;
// DateTime updatedAt;
CategoryModel({this.id, this.name, this.categoryId, this.isSync, this.image});
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() {
return <String, dynamic>{ return {
// 'id': id, 'success': success,
'name': name, 'data': data.toMap(),
'is_sync': isSync ?? 1, 'errors': errors,
'category_id': id,
'image': image
}; };
} }
factory CategoryModel.fromMap(Map<String, dynamic> map) {
return CategoryModel(
id: map['id'] as int?,
name: map['name'] as String?,
isSync: map['is_sync'] as int?,
categoryId: map['id'],
image: map['image']);
}
factory CategoryModel.fromJson(String str) =>
CategoryModel.fromMap(json.decode(str));
String toJson() => json.encode(toMap()); String toJson() => json.encode(toMap());
}
@override class CategoryData {
bool operator ==(covariant CategoryModel other) { final List<CategoryModel> categories;
if (identical(this, other)) return true; final int totalCount;
final int page;
final int limit;
final int totalPages;
return other.id == id && CategoryData({
other.name == name && required this.categories,
other.categoryId == categoryId && required this.totalCount,
other.isSync == isSync && required this.page,
other.image == image; required this.limit,
required this.totalPages,
});
factory CategoryData.fromMap(Map<String, dynamic> map) {
return CategoryData(
categories: List<CategoryModel>.from(
(map['categories'] as List).map((x) => CategoryModel.fromMap(x)),
),
totalCount: map['total_count'] as int,
page: map['page'] as int,
limit: map['limit'] as int,
totalPages: map['total_pages'] as int,
);
} }
@override Map<String, dynamic> toMap() {
int get hashCode { return {
return id.hashCode ^ 'categories': categories.map((x) => x.toMap()).toList(),
name.hashCode ^ 'total_count': totalCount,
categoryId.hashCode ^ 'page': page,
isSync.hashCode ^ 'limit': limit,
image.hashCode; 'total_pages': totalPages,
};
}
}
class CategoryModel {
String id;
final String organizationId;
final String name;
final String? description;
final String businessType;
final Map<String, dynamic> metadata;
final DateTime createdAt;
final DateTime updatedAt;
CategoryModel({
required this.id,
required this.organizationId,
required this.name,
this.description,
required this.businessType,
required this.metadata,
required this.createdAt,
required this.updatedAt,
});
factory CategoryModel.fromMap(Map<String, dynamic> map) {
return CategoryModel(
id: map['id'] as String,
organizationId: map['organization_id'] as String,
name: map['name'] as String,
description: map['description'] as String?,
businessType: map['business_type'] as String,
metadata: Map<String, dynamic>.from(map['metadata'] ?? {}),
createdAt: DateTime.parse(map['created_at'] as String),
updatedAt: DateTime.parse(map['updated_at'] as String),
);
}
Map<String, dynamic> toMap() {
return {
'id': id,
'organization_id': organizationId,
'name': name,
'description': description,
'business_type': businessType,
'metadata': metadata,
'created_at': createdAt.toIso8601String(),
'updated_at': updatedAt.toIso8601String(),
};
} }
} }
@@ -0,0 +1,248 @@
class DashboardAnalyticResponseModel {
final bool success;
final DashboardAnalyticData data;
final dynamic errors;
DashboardAnalyticResponseModel({
required this.success,
required this.data,
this.errors,
});
/// Khusus untuk JSON string
factory DashboardAnalyticResponseModel.fromJson(Map<String, dynamic> json) =>
DashboardAnalyticResponseModel.fromMap(json);
/// Untuk menerima Map biasa (bukan dari JSON string)
factory DashboardAnalyticResponseModel.fromMap(Map<String, dynamic> map) {
return DashboardAnalyticResponseModel(
success: map['success'] ?? false,
data: DashboardAnalyticData.fromMap(map['data'] ?? {}),
errors: map['errors'],
);
}
Map<String, dynamic> toJson() => toMap();
Map<String, dynamic> toMap() => {
'success': success,
'data': data.toMap(),
'errors': errors,
};
}
class DashboardAnalyticData {
final String organizationId;
final String outletId;
final String dateFrom;
final String dateTo;
final DashboardOverview overview;
final List<TopProduct> topProducts;
final List<PaymentMethodAnalytic> paymentMethods;
final List<RecentSale> recentSales;
DashboardAnalyticData({
required this.organizationId,
required this.outletId,
required this.dateFrom,
required this.dateTo,
required this.overview,
required this.topProducts,
required this.paymentMethods,
required this.recentSales,
});
factory DashboardAnalyticData.fromMap(Map<String, dynamic> map) =>
DashboardAnalyticData(
organizationId: map['organization_id'],
outletId: map['outlet_id'],
dateFrom: map['date_from'],
dateTo: map['date_to'],
overview: DashboardOverview.fromMap(map['overview']),
topProducts: map['top_products'] == null
? []
: List<TopProduct>.from(
map['top_products']?.map((x) => TopProduct.fromMap(x))),
paymentMethods: map['payment_methods'] == null
? []
: List<PaymentMethodAnalytic>.from(map['payment_methods']
?.map((x) => PaymentMethodAnalytic.fromMap(x))),
recentSales: map['recent_sales'] == null
? []
: List<RecentSale>.from(
map['recent_sales']?.map((x) => RecentSale.fromMap(x))),
);
Map<String, dynamic> toMap() => {
'organization_id': organizationId,
'outlet_id': outletId,
'date_from': dateFrom,
'date_to': dateTo,
'overview': overview.toMap(),
'top_products': topProducts.map((x) => x.toMap()).toList(),
'payment_methods': paymentMethods.map((x) => x.toMap()).toList(),
'recent_sales': recentSales.map((x) => x.toMap()).toList(),
};
}
class DashboardOverview {
final int totalSales;
final int totalOrders;
final double averageOrderValue;
final int totalCustomers;
final int voidedOrders;
final int refundedOrders;
DashboardOverview({
required this.totalSales,
required this.totalOrders,
required this.averageOrderValue,
required this.totalCustomers,
required this.voidedOrders,
required this.refundedOrders,
});
factory DashboardOverview.fromMap(Map<String, dynamic> map) =>
DashboardOverview(
totalSales: map['total_sales'],
totalOrders: map['total_orders'],
averageOrderValue: map['average_order_value']?.toDouble() ?? 0.0,
totalCustomers: map['total_customers'],
voidedOrders: map['voided_orders'],
refundedOrders: map['refunded_orders'],
);
Map<String, dynamic> toMap() => {
'total_sales': totalSales,
'total_orders': totalOrders,
'average_order_value': averageOrderValue,
'total_customers': totalCustomers,
'voided_orders': voidedOrders,
'refunded_orders': refundedOrders,
};
}
class TopProduct {
final String productId;
final String productName;
final String categoryId;
final String categoryName;
final int quantitySold;
final int revenue;
final double averagePrice;
final int orderCount;
TopProduct({
required this.productId,
required this.productName,
required this.categoryId,
required this.categoryName,
required this.quantitySold,
required this.revenue,
required this.averagePrice,
required this.orderCount,
});
factory TopProduct.fromMap(Map<String, dynamic> map) => TopProduct(
productId: map['product_id'],
productName: map['product_name'],
categoryId: map['category_id'],
categoryName: map['category_name'],
quantitySold: map['quantity_sold'],
revenue: map['revenue'],
averagePrice: map['average_price']?.toDouble() ?? 0.0,
orderCount: map['order_count'],
);
Map<String, dynamic> toMap() => {
'product_id': productId,
'product_name': productName,
'category_id': categoryId,
'category_name': categoryName,
'quantity_sold': quantitySold,
'revenue': revenue,
'average_price': averagePrice,
'order_count': orderCount,
};
}
class PaymentMethodAnalytic {
final String paymentMethodId;
final String paymentMethodName;
final String paymentMethodType;
final int totalAmount;
final int orderCount;
final int paymentCount;
final double percentage;
PaymentMethodAnalytic({
required this.paymentMethodId,
required this.paymentMethodName,
required this.paymentMethodType,
required this.totalAmount,
required this.orderCount,
required this.paymentCount,
required this.percentage,
});
factory PaymentMethodAnalytic.fromMap(Map<String, dynamic> map) =>
PaymentMethodAnalytic(
paymentMethodId: map['payment_method_id'],
paymentMethodName: map['payment_method_name'],
paymentMethodType: map['payment_method_type'],
totalAmount: map['total_amount'],
orderCount: map['order_count'],
paymentCount: map['payment_count'],
percentage: map['percentage']?.toDouble() ?? 0.0,
);
Map<String, dynamic> toMap() => {
'payment_method_id': paymentMethodId,
'payment_method_name': paymentMethodName,
'payment_method_type': paymentMethodType,
'total_amount': totalAmount,
'order_count': orderCount,
'payment_count': paymentCount,
'percentage': percentage,
};
}
class RecentSale {
final String date;
final int sales;
final int orders;
final int items;
final int tax;
final int discount;
final int netSales;
RecentSale({
required this.date,
required this.sales,
required this.orders,
required this.items,
required this.tax,
required this.discount,
required this.netSales,
});
factory RecentSale.fromMap(Map<String, dynamic> map) => RecentSale(
date: map['date'],
sales: map['sales'],
orders: map['orders'],
items: map['items'],
tax: map['tax'],
discount: map['discount'],
netSales: map['net_sales'],
);
Map<String, dynamic> toMap() => {
'date': date,
'sales': sales,
'orders': orders,
'items': items,
'tax': tax,
'discount': discount,
'net_sales': netSales,
};
}
@@ -0,0 +1,11 @@
class DeliveryModel {
String id;
String name;
String imageUrl;
DeliveryModel({
required this.id,
required this.name,
required this.imageUrl,
});
}
@@ -0,0 +1,154 @@
class FileResponseModel {
final FileModel data;
final String message;
final bool success;
FileResponseModel({
required this.data,
required this.message,
required this.success,
});
factory FileResponseModel.fromJson(Map<String, dynamic> json) {
return FileResponseModel(
data: FileModel.fromJson(json['data']),
message: json['message'] as String,
success: json['success'] as bool,
);
}
Map<String, dynamic> toJson() => {
'data': data.toJson(),
'message': message,
'success': success,
};
factory FileResponseModel.fromMap(Map<String, dynamic> map) =>
FileResponseModel.fromJson(map);
Map<String, dynamic> toMap() => toJson();
FileResponseModel copyWith({
FileModel? data,
String? message,
bool? success,
}) {
return FileResponseModel(
data: data ?? this.data,
message: message ?? this.message,
success: success ?? this.success,
);
}
@override
String toString() =>
'FileResponseModel(data: $data, message: $message, success: $success)';
}
class FileModel {
final String id;
final String organizationId;
final String userId;
final String fileName;
final String originalName;
final String fileUrl;
final int fileSize;
final String mimeType;
final String fileType;
final String uploadPath;
final bool isPublic;
final DateTime createdAt;
final DateTime updatedAt;
FileModel({
required this.id,
required this.organizationId,
required this.userId,
required this.fileName,
required this.originalName,
required this.fileUrl,
required this.fileSize,
required this.mimeType,
required this.fileType,
required this.uploadPath,
required this.isPublic,
required this.createdAt,
required this.updatedAt,
});
factory FileModel.fromJson(Map<String, dynamic> json) {
return FileModel(
id: json['id'] as String,
organizationId: json['organization_id'] as String,
userId: json['user_id'] as String,
fileName: json['file_name'] as String,
originalName: json['original_name'] as String,
fileUrl: json['file_url'] as String,
fileSize: json['file_size'] as int,
mimeType: json['mime_type'] as String,
fileType: json['file_type'] as String,
uploadPath: json['upload_path'] as String,
isPublic: json['is_public'] as bool,
createdAt: DateTime.parse(json['created_at'] as String),
updatedAt: DateTime.parse(json['updated_at'] as String),
);
}
Map<String, dynamic> toJson() => {
'id': id,
'organization_id': organizationId,
'user_id': userId,
'file_name': fileName,
'original_name': originalName,
'file_url': fileUrl,
'file_size': fileSize,
'mime_type': mimeType,
'file_type': fileType,
'upload_path': uploadPath,
'is_public': isPublic,
'created_at': createdAt.toIso8601String(),
'updated_at': updatedAt.toIso8601String(),
};
factory FileModel.fromMap(Map<String, dynamic> map) =>
FileModel.fromJson(map);
Map<String, dynamic> toMap() => toJson();
FileModel copyWith({
String? id,
String? organizationId,
String? userId,
String? fileName,
String? originalName,
String? fileUrl,
int? fileSize,
String? mimeType,
String? fileType,
String? uploadPath,
bool? isPublic,
DateTime? createdAt,
DateTime? updatedAt,
}) {
return FileModel(
id: id ?? this.id,
organizationId: organizationId ?? this.organizationId,
userId: userId ?? this.userId,
fileName: fileName ?? this.fileName,
originalName: originalName ?? this.originalName,
fileUrl: fileUrl ?? this.fileUrl,
fileSize: fileSize ?? this.fileSize,
mimeType: mimeType ?? this.mimeType,
fileType: fileType ?? this.fileType,
uploadPath: uploadPath ?? this.uploadPath,
isPublic: isPublic ?? this.isPublic,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
);
}
@override
String toString() {
return 'FileModel(id: $id, organizationId: $organizationId, userId: $userId, fileName: $fileName, originalName: $originalName, fileUrl: $fileUrl, fileSize: $fileSize, mimeType: $mimeType, fileType: $fileType, uploadPath: $uploadPath, isPublic: $isPublic, createdAt: $createdAt, updatedAt: $updatedAt)';
}
}
+276 -148
View File
@@ -1,35 +1,5 @@
import 'dart:convert'; import 'dart:convert';
class OrderResponseModel {
final bool? success;
final OrderData? data;
final dynamic errors;
OrderResponseModel({
this.success,
this.data,
this.errors,
});
factory OrderResponseModel.fromJson(String str) =>
OrderResponseModel.fromMap(json.decode(str));
String toJson() => json.encode(toMap());
factory OrderResponseModel.fromMap(Map<String, dynamic> json) =>
OrderResponseModel(
success: json["success"],
data: json["data"] == null ? null : OrderData.fromMap(json["data"]),
errors: json["errors"],
);
Map<String, dynamic> toMap() => {
"success": success,
"data": data?.toMap(),
"errors": errors,
};
}
class OrderDetailResponseModel { class OrderDetailResponseModel {
final bool? success; final bool? success;
final Order? data; final Order? data;
@@ -60,40 +30,77 @@ class OrderDetailResponseModel {
}; };
} }
class OrderResponseModel {
final bool? success;
final OrderData? data;
OrderResponseModel({
this.success,
this.data,
});
factory OrderResponseModel.fromJson(String str) =>
OrderResponseModel.fromMap(json.decode(str));
String toJson() => json.encode(toMap());
factory OrderResponseModel.fromMap(Map<String, dynamic> map) {
return OrderResponseModel(
success: map['success'] ?? false,
data: OrderData.fromMap(map['data']),
);
}
Map<String, dynamic> toMap() {
return {
'success': success,
'data': data?.toMap(),
};
}
}
class OrderData { class OrderData {
final List<Order>? orders; final List<Order>? orders;
final List<Payment>? payments;
final int? totalCount; final int? totalCount;
final int? page; final int? page;
final int? limit; final int? limit;
final int? totalPages; final int? totalPages;
OrderData({ OrderData({
this.orders, required this.orders,
this.totalCount, required this.payments,
this.page, required this.totalCount,
this.limit, required this.page,
this.totalPages, required this.limit,
required this.totalPages,
}); });
factory OrderData.fromMap(Map<String, dynamic> json) => OrderData( factory OrderData.fromMap(Map<String, dynamic> map) {
orders: json["orders"] == null return OrderData(
? [] orders: map["orders"] == null
: List<Order>.from(json["orders"].map((x) => Order.fromMap(x))), ? []
totalCount: json["total_count"], : List<Order>.from(map['orders']?.map((x) => Order.fromMap(x))),
page: json["page"], payments: map["payments"] == null
limit: json["limit"], ? []
totalPages: json["total_pages"], : List<Payment>.from(map['payments']?.map((x) => Payment.fromMap(x))),
); totalCount: map['total_count'],
page: map['page'],
limit: map['limit'],
totalPages: map['total_pages'],
);
}
Map<String, dynamic> toMap() => { Map<String, dynamic> toMap() {
"orders": orders == null return {
? [] 'orders': orders?.map((x) => x.toMap()).toList(),
: List<dynamic>.from(orders!.map((x) => x.toMap())), 'payments': payments?.map((x) => x.toMap()).toList(),
"total_count": totalCount, 'total_count': totalCount,
"page": page, 'page': page,
"limit": limit, 'limit': limit,
"total_pages": totalPages, 'total_pages': totalPages,
}; };
}
} }
class Order { class Order {
@@ -108,11 +115,21 @@ class Order {
final int? taxAmount; final int? taxAmount;
final int? discountAmount; final int? discountAmount;
final int? totalAmount; final int? totalAmount;
final int? totalCost;
final int? remainingAmount;
final String? paymentStatus;
final int? refundAmount;
final bool? isVoid;
final bool? isRefund;
final String? notes; final String? notes;
final Map<String, dynamic>? metadata; final Map<String, dynamic>? metadata;
final DateTime? createdAt; final DateTime? createdAt;
final DateTime? updatedAt; final DateTime? updatedAt;
final List<OrderItem>? orderItems; final List<OrderItem>? orderItems;
final List<Payment>? payments;
final int? totalPaid;
final int? paymentCount;
final String? splitType;
Order({ Order({
this.id, this.id,
@@ -126,76 +143,108 @@ class Order {
this.taxAmount, this.taxAmount,
this.discountAmount, this.discountAmount,
this.totalAmount, this.totalAmount,
this.totalCost,
this.remainingAmount,
this.paymentStatus,
this.refundAmount,
this.isVoid,
this.isRefund,
this.notes, this.notes,
this.metadata, this.metadata,
this.createdAt, this.createdAt,
this.updatedAt, this.updatedAt,
this.orderItems, this.orderItems,
this.payments,
this.totalPaid,
this.paymentCount,
this.splitType,
}); });
factory Order.fromMap(Map<String, dynamic> json) => Order( factory Order.fromMap(Map<String, dynamic> map) {
id: json["id"], return Order(
orderNumber: json["order_number"], id: map['id'],
outletId: json["outlet_id"], orderNumber: map['order_number'],
userId: json["user_id"], outletId: map['outlet_id'],
tableNumber: json["table_number"], userId: map['user_id'],
orderType: json["order_type"], tableNumber: map['table_number'],
status: json["status"], orderType: map['order_type'],
subtotal: json["subtotal"], status: map['status'],
taxAmount: json["tax_amount"], subtotal: map['subtotal'],
discountAmount: json["discount_amount"], taxAmount: map['tax_amount'],
totalAmount: json["total_amount"], discountAmount: map['discount_amount'],
notes: json["notes"], totalAmount: map['total_amount'],
metadata: json["metadata"] ?? {}, totalCost: map['total_cost'],
createdAt: json["created_at"] == null remainingAmount: map['remaining_amount'],
? null paymentStatus: map['payment_status'],
: DateTime.parse(json["created_at"]), refundAmount: map['refund_amount'],
updatedAt: json["updated_at"] == null isVoid: map['is_void'],
? null isRefund: map['is_refund'],
: DateTime.parse(json["updated_at"]), notes: map['notes'],
orderItems: json["order_items"] == null metadata: map['metadata'] ?? {},
? [] createdAt: DateTime.parse(map['created_at']),
: List<OrderItem>.from( updatedAt: DateTime.parse(map['updated_at']),
json["order_items"].map((x) => OrderItem.fromMap(x))), orderItems: map["order_items"] == null
); ? []
: List<OrderItem>.from(
map['order_items'].map((x) => OrderItem.fromMap(x))),
payments: map["payments"] == null
? []
: List<Payment>.from(map['payments'].map((x) => Payment.fromMap(x))),
totalPaid: map['total_paid'],
paymentCount: map['payment_count'],
splitType: map['split_type'] ?? "",
);
}
Map<String, dynamic> toMap() => { Map<String, dynamic> toMap() {
"id": id, return {
"order_number": orderNumber, 'id': id,
"outlet_id": outletId, 'order_number': orderNumber,
"user_id": userId, 'outlet_id': outletId,
"table_number": tableNumber, 'user_id': userId,
"order_type": orderType, 'table_number': tableNumber,
"status": status, 'order_type': orderType,
"subtotal": subtotal, 'status': status,
"tax_amount": taxAmount, 'subtotal': subtotal,
"discount_amount": discountAmount, 'tax_amount': taxAmount,
"total_amount": totalAmount, 'discount_amount': discountAmount,
"notes": notes, 'total_amount': totalAmount,
"metadata": metadata, 'total_cost': totalCost,
"created_at": createdAt?.toIso8601String(), 'remaining_amount': remainingAmount,
"updated_at": updatedAt?.toIso8601String(), 'payment_status': paymentStatus,
"order_items": orderItems == null 'refund_amount': refundAmount,
? [] 'is_void': isVoid,
: List<dynamic>.from(orderItems!.map((x) => x.toMap())), 'is_refund': isRefund,
}; 'notes': notes,
'metadata': metadata,
'created_at': createdAt?.toIso8601String(),
'updated_at': updatedAt?.toIso8601String(),
'order_items': orderItems?.map((x) => x.toMap()).toList(),
'payments': payments?.map((x) => x.toMap()).toList(),
'total_paid': totalPaid,
'payment_count': paymentCount,
'split_type': splitType,
};
}
} }
class OrderItem { class OrderItem {
final String? id; String? id;
final String? orderId; String? orderId;
final String? productId; String? productId;
final String? productName; String? productName;
final String? productVariantId; String? productVariantId;
final String? productVariantName; String? productVariantName;
final int? quantity; int? quantity;
final int? unitPrice; int? unitPrice;
final int? totalPrice; int? totalPrice;
final List<dynamic>? modifiers; List<dynamic>? modifiers;
final String? notes; String? notes;
final String? status; String? status;
final DateTime? createdAt; DateTime? createdAt;
final DateTime? updatedAt; DateTime? updatedAt;
String? printerType;
int? paidQuantity;
OrderItem({ OrderItem({
this.id, this.id,
@@ -212,43 +261,122 @@ class OrderItem {
this.status, this.status,
this.createdAt, this.createdAt,
this.updatedAt, this.updatedAt,
this.printerType,
this.paidQuantity,
}); });
factory OrderItem.fromMap(Map<String, dynamic> json) => OrderItem( factory OrderItem.fromMap(Map<String, dynamic> map) {
id: json["id"], return OrderItem(
orderId: json["order_id"], id: map['id'],
productId: json["product_id"], orderId: map['order_id'],
productName: json["product_name"], productId: map['product_id'],
productVariantId: json["product_variant_id"], productName: map['product_name'],
productVariantName: json["product_variant_name"], productVariantId: map['product_variant_id'],
quantity: json["quantity"], productVariantName: map['product_variant_name'],
unitPrice: json["unit_price"], quantity: map['quantity'],
totalPrice: json["total_price"], unitPrice: map['unit_price'],
modifiers: json["modifiers"] ?? [], totalPrice: map['total_price'],
notes: json["notes"], modifiers:
status: json["status"], map['modifiers'] == null ? [] : List<dynamic>.from(map['modifiers']),
createdAt: json["created_at"] == null notes: map['notes'],
? null status: map['status'],
: DateTime.parse(json["created_at"]), createdAt: DateTime.parse(map['created_at']),
updatedAt: json["updated_at"] == null updatedAt: DateTime.parse(map['updated_at']),
? null printerType: map['printer_type'],
: DateTime.parse(json["updated_at"]), paidQuantity: map['paid_quantity'],
); );
}
Map<String, dynamic> toMap() => { Map<String, dynamic> toMap() {
"id": id, return {
"order_id": orderId, 'id': id,
"product_id": productId, 'order_id': orderId,
"product_name": productName, 'product_id': productId,
"product_variant_id": productVariantId, 'product_name': productName,
"product_variant_name": productVariantName, 'product_variant_id': productVariantId,
"quantity": quantity, 'product_variant_name': productVariantName,
"unit_price": unitPrice, 'quantity': quantity,
"total_price": totalPrice, 'unit_price': unitPrice,
"modifiers": modifiers, 'total_price': totalPrice,
"notes": notes, 'modifiers': modifiers,
"status": status, 'notes': notes,
"created_at": createdAt?.toIso8601String(), 'status': status,
"updated_at": updatedAt?.toIso8601String(), 'created_at': createdAt?.toIso8601String(),
}; 'updated_at': updatedAt?.toIso8601String(),
'printer_type': printerType,
'paid_quantity': paidQuantity,
};
}
}
class Payment {
final String? id;
final String? orderId;
final String? paymentMethodId;
final String? paymentMethodName;
final String? paymentMethodType;
final int? amount;
final String? status;
final int? splitNumber;
final int? splitTotal;
final String? splitDescription;
final int? refundAmount;
final Map<String, dynamic>? metadata;
final DateTime? createdAt;
final DateTime? updatedAt;
Payment({
this.id,
this.orderId,
this.paymentMethodId,
this.paymentMethodName,
this.paymentMethodType,
this.amount,
this.status,
this.splitNumber,
this.splitTotal,
this.splitDescription,
this.refundAmount,
this.metadata,
this.createdAt,
this.updatedAt,
});
factory Payment.fromMap(Map<String, dynamic> map) {
return Payment(
id: map['id'],
orderId: map['order_id'],
paymentMethodId: map['payment_method_id'],
paymentMethodName: map['payment_method_name'],
paymentMethodType: map['payment_method_type'],
amount: map['amount'],
status: map['status'],
splitNumber: map['split_number'],
splitTotal: map['split_total'],
splitDescription: map['split_description'],
refundAmount: map['refund_amount'],
metadata: map['metadata'] ?? {},
createdAt: DateTime.parse(map['created_at']),
updatedAt: DateTime.parse(map['updated_at']),
);
}
Map<String, dynamic> toMap() {
return {
'id': id,
'order_id': orderId,
'payment_method_id': paymentMethodId,
'payment_method_name': paymentMethodName,
'payment_method_type': paymentMethodType,
'amount': amount,
'status': status,
'split_number': splitNumber,
'split_total': splitTotal,
'split_description': splitDescription,
'refund_amount': refundAmount,
'metadata': metadata,
'created_at': createdAt?.toIso8601String(),
'updated_at': updatedAt?.toIso8601String(),
};
}
} }
@@ -0,0 +1,173 @@
class PaymentMethodAnalyticResponseModel {
final bool success;
final PaymentMethodAnalyticData data;
final dynamic errors;
PaymentMethodAnalyticResponseModel({
required this.success,
required this.data,
this.errors,
});
factory PaymentMethodAnalyticResponseModel.fromJson(
Map<String, dynamic> json) =>
PaymentMethodAnalyticResponseModel.fromMap(json);
Map<String, dynamic> toJson() => toMap();
factory PaymentMethodAnalyticResponseModel.fromMap(Map<String, dynamic> map) {
return PaymentMethodAnalyticResponseModel(
success: map['success'],
data: PaymentMethodAnalyticData.fromMap(map['data']),
errors: map['errors'],
);
}
Map<String, dynamic> toMap() {
return {
'success': success,
'data': data.toMap(),
'errors': errors,
};
}
}
class PaymentMethodAnalyticData {
final String organizationId;
final String outletId;
final DateTime dateFrom;
final DateTime dateTo;
final String groupBy;
final PaymentSummary summary;
final List<PaymentMethodAnalyticItem> data;
PaymentMethodAnalyticData({
required this.organizationId,
required this.outletId,
required this.dateFrom,
required this.dateTo,
required this.groupBy,
required this.summary,
required this.data,
});
factory PaymentMethodAnalyticData.fromJson(Map<String, dynamic> json) =>
PaymentMethodAnalyticData.fromMap(json);
Map<String, dynamic> toJson() => toMap();
factory PaymentMethodAnalyticData.fromMap(Map<String, dynamic> map) {
return PaymentMethodAnalyticData(
organizationId: map['organization_id'],
outletId: map['outlet_id'],
dateFrom: DateTime.parse(map['date_from']),
dateTo: DateTime.parse(map['date_to']),
groupBy: map['group_by'],
summary: PaymentSummary.fromMap(map['summary']),
data: map['data'] == null
? []
: List<PaymentMethodAnalyticItem>.from(
map['data']?.map((x) => PaymentMethodAnalyticItem.fromMap(x)) ??
[],
),
);
}
Map<String, dynamic> toMap() {
return {
'organization_id': organizationId,
'outlet_id': outletId,
'date_from': dateFrom.toIso8601String(),
'date_to': dateTo.toIso8601String(),
'group_by': groupBy,
'summary': summary.toMap(),
'data': data.map((x) => x.toMap()).toList(),
};
}
}
class PaymentSummary {
final int totalAmount;
final int totalOrders;
final int totalPayments;
final double averageOrderValue;
PaymentSummary({
required this.totalAmount,
required this.totalOrders,
required this.totalPayments,
required this.averageOrderValue,
});
factory PaymentSummary.fromJson(Map<String, dynamic> json) =>
PaymentSummary.fromMap(json);
Map<String, dynamic> toJson() => toMap();
factory PaymentSummary.fromMap(Map<String, dynamic> map) {
return PaymentSummary(
totalAmount: map['total_amount'],
totalOrders: map['total_orders'],
totalPayments: map['total_payments'],
averageOrderValue: (map['average_order_value'] as num).toDouble(),
);
}
Map<String, dynamic> toMap() {
return {
'total_amount': totalAmount,
'total_orders': totalOrders,
'total_payments': totalPayments,
'average_order_value': averageOrderValue,
};
}
}
class PaymentMethodAnalyticItem {
final String paymentMethodId;
final String paymentMethodName;
final String paymentMethodType;
final int totalAmount;
final int orderCount;
final int paymentCount;
final int percentage;
PaymentMethodAnalyticItem({
required this.paymentMethodId,
required this.paymentMethodName,
required this.paymentMethodType,
required this.totalAmount,
required this.orderCount,
required this.paymentCount,
required this.percentage,
});
factory PaymentMethodAnalyticItem.fromJson(Map<String, dynamic> json) =>
PaymentMethodAnalyticItem.fromMap(json);
Map<String, dynamic> toJson() => toMap();
factory PaymentMethodAnalyticItem.fromMap(Map<String, dynamic> map) {
return PaymentMethodAnalyticItem(
paymentMethodId: map['payment_method_id'],
paymentMethodName: map['payment_method_name'],
paymentMethodType: map['payment_method_type'],
totalAmount: map['total_amount'],
orderCount: map['order_count'],
paymentCount: map['payment_count'],
percentage: map['percentage'],
);
}
Map<String, dynamic> toMap() {
return {
'payment_method_id': paymentMethodId,
'payment_method_name': paymentMethodName,
'payment_method_type': paymentMethodType,
'total_amount': totalAmount,
'order_count': orderCount,
'payment_count': paymentCount,
'percentage': percentage,
};
}
}
@@ -0,0 +1,145 @@
class ProductAnalyticResponseModel {
final bool success;
final ProductAnalyticData data;
final dynamic errors;
ProductAnalyticResponseModel({
required this.success,
required this.data,
this.errors,
});
factory ProductAnalyticResponseModel.fromJson(Map<String, dynamic> json) =>
ProductAnalyticResponseModel.fromMap(json);
Map<String, dynamic> toJson() => toMap();
factory ProductAnalyticResponseModel.fromMap(Map<String, dynamic> map) {
return ProductAnalyticResponseModel(
success: map['success'] ?? false,
data: ProductAnalyticData.fromMap(map['data']),
errors: map['errors'],
);
}
Map<String, dynamic> toMap() {
return {
'success': success,
'data': data.toMap(),
'errors': errors,
};
}
}
class ProductAnalyticData {
final String organizationId;
final String outletId;
final DateTime dateFrom;
final DateTime dateTo;
final List<ProductAnalyticItem> data;
ProductAnalyticData({
required this.organizationId,
required this.outletId,
required this.dateFrom,
required this.dateTo,
required this.data,
});
factory ProductAnalyticData.fromMap(Map<String, dynamic> map) =>
ProductAnalyticData(
organizationId: map['organization_id'],
outletId: map['outlet_id'],
dateFrom: DateTime.parse(map['date_from']),
dateTo: DateTime.parse(map['date_to']),
data: map['data'] == null
? []
: List<ProductAnalyticItem>.from(
map['data'].map((x) => ProductAnalyticItem.fromMap(x)),
),
);
Map<String, dynamic> toMap() => {
'organization_id': organizationId,
'outlet_id': outletId,
'date_from': dateFrom.toIso8601String(),
'date_to': dateTo.toIso8601String(),
'data': data.map((x) => x.toMap()).toList(),
};
}
class ProductAnalyticItem {
final String productId;
final String productName;
final String categoryId;
final String categoryName;
final int quantitySold;
final int revenue;
final double averagePrice;
final int orderCount;
ProductAnalyticItem({
required this.productId,
required this.productName,
required this.categoryId,
required this.categoryName,
required this.quantitySold,
required this.revenue,
required this.averagePrice,
required this.orderCount,
});
factory ProductAnalyticItem.fromMap(Map<String, dynamic> map) =>
ProductAnalyticItem(
productId: map['product_id'],
productName: map['product_name'],
categoryId: map['category_id'],
categoryName: map['category_name'],
quantitySold: map['quantity_sold'],
revenue: map['revenue'],
averagePrice: (map['average_price'] as num).toDouble(),
orderCount: map['order_count'],
);
Map<String, dynamic> toMap() => {
'product_id': productId,
'product_name': productName,
'category_id': categoryId,
'category_name': categoryName,
'quantity_sold': quantitySold,
'revenue': revenue,
'average_price': averagePrice,
'order_count': orderCount,
};
}
class ProductInsights {
final List<ProductAnalyticItem> topProducts;
final ProductAnalyticItem? bestProduct;
final List<CategorySummary> categorySummary;
final int totalProducts;
final int totalRevenue;
final int totalQuantitySold;
ProductInsights({
required this.topProducts,
required this.bestProduct,
required this.categorySummary,
required this.totalProducts,
required this.totalRevenue,
required this.totalQuantitySold,
});
}
// Category summary class
class CategorySummary {
final String categoryName;
int productCount;
int totalRevenue;
CategorySummary({
required this.categoryName,
required this.productCount,
required this.totalRevenue,
});
}
@@ -0,0 +1,277 @@
class ProfitLossResponseModel {
final bool success;
final ProfitLossData data;
final dynamic errors;
ProfitLossResponseModel({
required this.success,
required this.data,
this.errors,
});
/// Dari JSON ke model (misal response dari API)
factory ProfitLossResponseModel.fromJson(Map<String, dynamic> json) {
return ProfitLossResponseModel(
success: json['success'],
data: ProfitLossData.fromMap(json['data']),
errors: json['errors'],
);
}
/// Dari model ke JSON (misal saat kirim ke API)
Map<String, dynamic> toJson() {
return {
'success': success,
'data': data.toMap(),
'errors': errors,
};
}
/// Alias dari fromJson, kadang dibutuhkan untuk naming yang konsisten
factory ProfitLossResponseModel.fromMap(Map<String, dynamic> map) {
return ProfitLossResponseModel(
success: map['success'],
data: ProfitLossData.fromMap(map['data']),
errors: map['errors'],
);
}
/// Alias dari toJson
Map<String, dynamic> toMap() {
return {
'success': success,
'data': data.toMap(),
'errors': errors,
};
}
}
class ProfitLossData {
final String organizationId;
final String dateFrom;
final String dateTo;
final String groupBy;
final ProfitLossSummary summary;
final List<ProfitLossItem> data;
final List<ProfitLossProduct> productData;
ProfitLossData({
required this.organizationId,
required this.dateFrom,
required this.dateTo,
required this.groupBy,
required this.summary,
required this.data,
required this.productData,
});
factory ProfitLossData.fromMap(Map<String, dynamic> map) {
return ProfitLossData(
organizationId: map['organization_id'],
dateFrom: map['date_from'],
dateTo: map['date_to'],
groupBy: map['group_by'],
summary: ProfitLossSummary.fromMap(map['summary']),
data: map['data'] == null
? []
: List<ProfitLossItem>.from(
map['data'].map((x) => ProfitLossItem.fromMap(x))),
productData: map['product_data'] == null
? []
: List<ProfitLossProduct>.from(
map['product_data'].map((x) => ProfitLossProduct.fromMap(x))),
);
}
Map<String, dynamic> toMap() {
return {
'organization_id': organizationId,
'date_from': dateFrom,
'date_to': dateTo,
'group_by': groupBy,
'summary': summary.toMap(),
'data': data.map((x) => x.toMap()).toList(),
'product_data': productData.map((x) => x.toMap()).toList(),
};
}
}
class ProfitLossSummary {
final int totalRevenue;
final int totalCost;
final int grossProfit;
final double grossProfitMargin;
final int totalTax;
final int totalDiscount;
final int netProfit;
final double netProfitMargin;
final int totalOrders;
final int averageProfit;
final double profitabilityRatio;
ProfitLossSummary({
required this.totalRevenue,
required this.totalCost,
required this.grossProfit,
required this.grossProfitMargin,
required this.totalTax,
required this.totalDiscount,
required this.netProfit,
required this.netProfitMargin,
required this.totalOrders,
required this.averageProfit,
required this.profitabilityRatio,
});
factory ProfitLossSummary.fromMap(Map<String, dynamic> map) {
return ProfitLossSummary(
totalRevenue: map['total_revenue'],
totalCost: map['total_cost'],
grossProfit: map['gross_profit'],
grossProfitMargin: (map['gross_profit_margin'] as num).toDouble(),
totalTax: map['total_tax'],
totalDiscount: map['total_discount'],
netProfit: map['net_profit'],
netProfitMargin: (map['net_profit_margin'] as num).toDouble(),
totalOrders: map['total_orders'],
averageProfit: map['average_profit'],
profitabilityRatio: (map['profitability_ratio'] as num).toDouble(),
);
}
Map<String, dynamic> toMap() {
return {
'total_revenue': totalRevenue,
'total_cost': totalCost,
'gross_profit': grossProfit,
'gross_profit_margin': grossProfitMargin,
'total_tax': totalTax,
'total_discount': totalDiscount,
'net_profit': netProfit,
'net_profit_margin': netProfitMargin,
'total_orders': totalOrders,
'average_profit': averageProfit,
'profitability_ratio': profitabilityRatio,
};
}
}
class ProfitLossItem {
final String date;
final int revenue;
final int cost;
final int grossProfit;
final double grossProfitMargin;
final int tax;
final int discount;
final int netProfit;
final double netProfitMargin;
final int orders;
ProfitLossItem({
required this.date,
required this.revenue,
required this.cost,
required this.grossProfit,
required this.grossProfitMargin,
required this.tax,
required this.discount,
required this.netProfit,
required this.netProfitMargin,
required this.orders,
});
factory ProfitLossItem.fromMap(Map<String, dynamic> map) {
return ProfitLossItem(
date: map['date'],
revenue: map['revenue'],
cost: map['cost'],
grossProfit: map['gross_profit'],
grossProfitMargin: (map['gross_profit_margin'] as num).toDouble(),
tax: map['tax'],
discount: map['discount'],
netProfit: map['net_profit'],
netProfitMargin: (map['net_profit_margin'] as num).toDouble(),
orders: map['orders'],
);
}
Map<String, dynamic> toMap() {
return {
'date': date,
'revenue': revenue,
'cost': cost,
'gross_profit': grossProfit,
'gross_profit_margin': grossProfitMargin,
'tax': tax,
'discount': discount,
'net_profit': netProfit,
'net_profit_margin': netProfitMargin,
'orders': orders,
};
}
}
class ProfitLossProduct {
final String productId;
final String productName;
final String categoryId;
final String categoryName;
final int quantitySold;
final int revenue;
final int cost;
final int grossProfit;
final double grossProfitMargin;
final int averagePrice;
final int averageCost;
final int profitPerUnit;
ProfitLossProduct({
required this.productId,
required this.productName,
required this.categoryId,
required this.categoryName,
required this.quantitySold,
required this.revenue,
required this.cost,
required this.grossProfit,
required this.grossProfitMargin,
required this.averagePrice,
required this.averageCost,
required this.profitPerUnit,
});
factory ProfitLossProduct.fromMap(Map<String, dynamic> map) {
return ProfitLossProduct(
productId: map['product_id'],
productName: map['product_name'],
categoryId: map['category_id'],
categoryName: map['category_name'],
quantitySold: map['quantity_sold'],
revenue: map['revenue'],
cost: map['cost'],
grossProfit: map['gross_profit'],
grossProfitMargin: (map['gross_profit_margin'] as num).toDouble(),
averagePrice: map['average_price'],
averageCost: map['average_cost'],
profitPerUnit: map['profit_per_unit'],
);
}
Map<String, dynamic> toMap() {
return {
'product_id': productId,
'product_name': productName,
'category_id': categoryId,
'category_name': categoryName,
'quantity_sold': quantitySold,
'revenue': revenue,
'cost': cost,
'gross_profit': grossProfit,
'gross_profit_margin': grossProfitMargin,
'average_price': averagePrice,
'average_cost': averageCost,
'profit_per_unit': profitPerUnit,
};
}
}
@@ -0,0 +1,195 @@
class SalesAnalyticResponseModel {
final bool success;
final SalesAnalyticData data;
final dynamic errors;
SalesAnalyticResponseModel({
required this.success,
required this.data,
this.errors,
});
factory SalesAnalyticResponseModel.fromJson(Map<String, dynamic> json) =>
SalesAnalyticResponseModel.fromMap(json);
Map<String, dynamic> toJson() => toMap();
factory SalesAnalyticResponseModel.fromMap(Map<String, dynamic> map) {
return SalesAnalyticResponseModel(
success: map['success'],
data: SalesAnalyticData.fromMap(map['data']),
errors: map['errors'],
);
}
Map<String, dynamic> toMap() {
return {
'success': success,
'data': data.toMap(),
'errors': errors,
};
}
}
class SalesAnalyticData {
final String organizationId;
final String outletId;
final DateTime dateFrom;
final DateTime dateTo;
final String groupBy;
final SalesSummary summary;
final List<SalesAnalyticItem> data;
SalesAnalyticData({
required this.organizationId,
required this.outletId,
required this.dateFrom,
required this.dateTo,
required this.groupBy,
required this.summary,
required this.data,
});
factory SalesAnalyticData.fromJson(Map<String, dynamic> json) =>
SalesAnalyticData.fromMap(json);
Map<String, dynamic> toJson() => toMap();
factory SalesAnalyticData.fromMap(Map<String, dynamic> map) {
return SalesAnalyticData(
organizationId: map['organization_id'],
outletId: map['outlet_id'],
dateFrom: DateTime.parse(map['date_from']),
dateTo: DateTime.parse(map['date_to']),
groupBy: map['group_by'],
summary: SalesSummary.fromMap(map['summary']),
data: List<SalesAnalyticItem>.from(
map['data']?.map((x) => SalesAnalyticItem.fromMap(x)) ?? [],
),
);
}
Map<String, dynamic> toMap() {
return {
'organization_id': organizationId,
'outlet_id': outletId,
'date_from': dateFrom.toIso8601String(),
'date_to': dateTo.toIso8601String(),
'group_by': groupBy,
'summary': summary.toMap(),
'data': data.map((x) => x.toMap()).toList(),
};
}
}
class SalesSummary {
final int totalSales;
final int totalOrders;
final int totalItems;
final double averageOrderValue;
final int totalTax;
final int totalDiscount;
final int netSales;
SalesSummary({
required this.totalSales,
required this.totalOrders,
required this.totalItems,
required this.averageOrderValue,
required this.totalTax,
required this.totalDiscount,
required this.netSales,
});
factory SalesSummary.fromJson(Map<String, dynamic> json) =>
SalesSummary.fromMap(json);
Map<String, dynamic> toJson() => toMap();
factory SalesSummary.fromMap(Map<String, dynamic> map) {
return SalesSummary(
totalSales: map['total_sales'],
totalOrders: map['total_orders'],
totalItems: map['total_items'],
averageOrderValue: (map['average_order_value'] as num).toDouble(),
totalTax: map['total_tax'],
totalDiscount: map['total_discount'],
netSales: map['net_sales'],
);
}
Map<String, dynamic> toMap() {
return {
'total_sales': totalSales,
'total_orders': totalOrders,
'total_items': totalItems,
'average_order_value': averageOrderValue,
'total_tax': totalTax,
'total_discount': totalDiscount,
'net_sales': netSales,
};
}
}
class SalesAnalyticItem {
final DateTime date;
final int sales;
final int orders;
final int items;
final int tax;
final int discount;
final int netSales;
SalesAnalyticItem({
required this.date,
required this.sales,
required this.orders,
required this.items,
required this.tax,
required this.discount,
required this.netSales,
});
factory SalesAnalyticItem.fromJson(Map<String, dynamic> json) =>
SalesAnalyticItem.fromMap(json);
Map<String, dynamic> toJson() => toMap();
factory SalesAnalyticItem.fromMap(Map<String, dynamic> map) {
return SalesAnalyticItem(
date: DateTime.parse(map['date']),
sales: map['sales'],
orders: map['orders'],
items: map['items'],
tax: map['tax'],
discount: map['discount'],
netSales: map['net_sales'],
);
}
Map<String, dynamic> toMap() {
return {
'date': date.toIso8601String(),
'sales': sales,
'orders': orders,
'items': items,
'tax': tax,
'discount': discount,
'net_sales': netSales,
};
}
}
class SalesInsights {
final List<SalesAnalyticItem> originalData;
final List<SalesAnalyticItem> sortedDailyData;
final SalesAnalyticItem? highestRevenueDay;
final SalesSummary summary;
SalesInsights({
required this.originalData,
required this.sortedDailyData,
required this.highestRevenueDay,
required this.summary,
});
}
+12 -12
View File
@@ -68,18 +68,18 @@ class TableData {
} }
class TableModel { class TableModel {
final String? id; String? id;
final String? organizationId; String? organizationId;
final String? outletId; String? outletId;
final String? tableName; String? tableName;
final String? status; String? status;
final int? paymentAmount; int? paymentAmount;
final double? positionX; double? positionX;
final double? positionY; double? positionY;
final int? capacity; int? capacity;
final bool? isActive; bool? isActive;
final DateTime? createdAt; DateTime? createdAt;
final DateTime? updatedAt; DateTime? updatedAt;
TableModel({ TableModel({
this.id, this.id,
+17
View File
@@ -0,0 +1,17 @@
enum BusinessType {
restaurant('restaurant'),
retail('retail'),
service('service'),
ticketing('ticketing'),
entertainment('entertainment');
final String value;
const BusinessType(this.value);
static BusinessType fromString(String value) {
return BusinessType.values.firstWhere(
(type) => type.value == value,
orElse: () => BusinessType.restaurant,
);
}
}
+38 -5
View File
@@ -1,16 +1,27 @@
import 'dart:developer'; import 'dart:developer';
import 'package:enaklo_pos/core/constants/theme.dart'; import 'package:enaklo_pos/core/constants/theme.dart';
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/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_remote_data_source.dart';
import 'package:enaklo_pos/data/datasources/table_remote_datasource.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'; import 'package:enaklo_pos/presentation/customer/bloc/customer_form/customer_form_bloc.dart';
import 'package:enaklo_pos/presentation/customer/bloc/customer_loader/customer_loader_bloc.dart'; import 'package:enaklo_pos/presentation/customer/bloc/customer_loader/customer_loader_bloc.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/current_outlet/current_outlet_bloc.dart';
import 'package:enaklo_pos/presentation/home/bloc/order_form/order_form_bloc.dart'; import 'package:enaklo_pos/presentation/home/bloc/order_form/order_form_bloc.dart';
import 'package:enaklo_pos/presentation/home/bloc/outlet_loader/outlet_loader_bloc.dart'; import 'package:enaklo_pos/presentation/home/bloc/outlet_loader/outlet_loader_bloc.dart';
import 'package:enaklo_pos/presentation/home/bloc/product_loader/product_loader_bloc.dart'; import 'package:enaklo_pos/presentation/home/bloc/product_loader/product_loader_bloc.dart';
import 'package:enaklo_pos/presentation/home/bloc/user_update_outlet/user_update_outlet_bloc.dart';
import 'package:enaklo_pos/presentation/refund/bloc/refund_bloc.dart';
import 'package:enaklo_pos/presentation/report/blocs/profit_loss/profit_loss_bloc.dart';
import 'package:enaklo_pos/presentation/sales/blocs/order_loader/order_loader_bloc.dart'; import 'package:enaklo_pos/presentation/sales/blocs/order_loader/order_loader_bloc.dart';
import 'package:enaklo_pos/presentation/sales/blocs/payment_form/payment_form_bloc.dart'; import 'package:enaklo_pos/presentation/sales/blocs/payment_form/payment_form_bloc.dart';
import 'package:enaklo_pos/presentation/setting/bloc/get_printer_ticket/get_printer_ticket_bloc.dart';
import 'package:enaklo_pos/presentation/setting/bloc/upload_file/upload_file_bloc.dart';
import 'package:enaklo_pos/presentation/table/blocs/transfer_table/transfer_table_bloc.dart';
import 'package:enaklo_pos/presentation/void/bloc/void_order_bloc.dart'; import 'package:enaklo_pos/presentation/void/bloc/void_order_bloc.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:enaklo_pos/data/datasources/auth_local_datasource.dart'; import 'package:enaklo_pos/data/datasources/auth_local_datasource.dart';
@@ -21,7 +32,6 @@ 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/order_remote_datasource.dart';
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart'; import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
import 'package:enaklo_pos/data/datasources/product_remote_datasource.dart'; import 'package:enaklo_pos/data/datasources/product_remote_datasource.dart';
import 'package:enaklo_pos/data/datasources/order_item_remote_datasource.dart';
import 'package:enaklo_pos/data/datasources/payment_methods_remote_datasource.dart'; import 'package:enaklo_pos/data/datasources/payment_methods_remote_datasource.dart';
import 'package:enaklo_pos/data/datasources/settings_local_datasource.dart'; import 'package:enaklo_pos/data/datasources/settings_local_datasource.dart';
import 'package:enaklo_pos/presentation/auth/bloc/logout/logout_bloc.dart'; import 'package:enaklo_pos/presentation/auth/bloc/logout/logout_bloc.dart';
@@ -184,16 +194,20 @@ class _MyAppState extends State<MyApp> {
create: (context) => GetCategoriesBloc(CategoryRemoteDatasource()), create: (context) => GetCategoriesBloc(CategoryRemoteDatasource()),
), ),
BlocProvider( BlocProvider(
create: (context) => SummaryBloc(OrderRemoteDatasource()), create: (context) => SummaryBloc(AnalyticRemoteDatasource()),
), ),
BlocProvider( BlocProvider(
create: (context) => ProductSalesBloc(OrderItemRemoteDatasource()), create: (context) => ProfitLossBloc(AnalyticRemoteDatasource()),
), ),
BlocProvider( BlocProvider(
create: (context) => ItemSalesReportBloc(OrderItemRemoteDatasource()), create: (context) => ProductSalesBloc(AnalyticRemoteDatasource()),
), ),
BlocProvider( BlocProvider(
create: (context) => PaymentMethodReportBloc(OrderRemoteDatasource()), create: (context) => ItemSalesReportBloc(AnalyticRemoteDatasource()),
),
BlocProvider(
create: (context) =>
PaymentMethodReportBloc(AnalyticRemoteDatasource()),
), ),
BlocProvider( BlocProvider(
create: (context) => DaySalesBloc(ProductLocalDatasource.instance), create: (context) => DaySalesBloc(ProductLocalDatasource.instance),
@@ -256,8 +270,27 @@ class _MyAppState extends State<MyApp> {
BlocProvider( BlocProvider(
create: (context) => VoidOrderBloc(OrderRemoteDatasource()), create: (context) => VoidOrderBloc(OrderRemoteDatasource()),
), ),
BlocProvider(
create: (context) => RefundBloc(OrderRemoteDatasource()),
),
BlocProvider(
create: (context) => UserUpdateOutletBloc(UserRemoteDatasource()),
),
BlocProvider(
create: (context) => UploadFileBloc(FileRemoteDataSource()),
),
BlocProvider(
create: (context) => CategoryLoaderBloc(CategoryRemoteDatasource()),
),
BlocProvider(
create: (context) => GetPrinterTicketBloc(),
),
BlocProvider(
create: (context) => TransferTableBloc(TableRemoteDataSource()),
),
], ],
child: MaterialApp( child: MaterialApp(
navigatorKey: AuthInterceptor.navigatorKey,
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
title: 'POS Resto App', title: 'POS Resto App',
theme: getApplicationTheme, theme: getApplicationTheme,
@@ -30,11 +30,6 @@ class CustomerTitle extends StatelessWidget {
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
IconButton(
onPressed: () => context.pop(),
icon: Icon(Icons.arrow_back, color: AppColors.black),
),
SpaceWidth(16),
Expanded( Expanded(
child: Text( child: Text(
title, title,
@@ -0,0 +1,43 @@
import 'package:bloc/bloc.dart';
import 'package:enaklo_pos/data/datasources/category_remote_datasource.dart';
import 'package:enaklo_pos/data/models/response/category_response_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'category_loader_event.dart';
part 'category_loader_state.dart';
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));
},
);
});
on<_SetCategoryId>((event, emit) async {
var currentState = state as _Loaded;
emit(_Loaded(currentState.categories, event.categoryId));
});
}
}
@@ -0,0 +1,976 @@
// 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 'category_loader_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 _$CategoryLoaderEvent {
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() get,
required TResult Function(String categoryId) setCategoryId,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? get,
TResult? Function(String categoryId)? setCategoryId,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? get,
TResult Function(String categoryId)? setCategoryId,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Get value) get,
required TResult Function(_SetCategoryId value) setCategoryId,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Get value)? get,
TResult? Function(_SetCategoryId value)? setCategoryId,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Get value)? get,
TResult Function(_SetCategoryId value)? setCategoryId,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $CategoryLoaderEventCopyWith<$Res> {
factory $CategoryLoaderEventCopyWith(
CategoryLoaderEvent value, $Res Function(CategoryLoaderEvent) then) =
_$CategoryLoaderEventCopyWithImpl<$Res, CategoryLoaderEvent>;
}
/// @nodoc
class _$CategoryLoaderEventCopyWithImpl<$Res, $Val extends CategoryLoaderEvent>
implements $CategoryLoaderEventCopyWith<$Res> {
_$CategoryLoaderEventCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of CategoryLoaderEvent
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
abstract class _$$GetImplCopyWith<$Res> {
factory _$$GetImplCopyWith(_$GetImpl value, $Res Function(_$GetImpl) then) =
__$$GetImplCopyWithImpl<$Res>;
}
/// @nodoc
class __$$GetImplCopyWithImpl<$Res>
extends _$CategoryLoaderEventCopyWithImpl<$Res, _$GetImpl>
implements _$$GetImplCopyWith<$Res> {
__$$GetImplCopyWithImpl(_$GetImpl _value, $Res Function(_$GetImpl) _then)
: super(_value, _then);
/// Create a copy of CategoryLoaderEvent
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
class _$GetImpl implements _Get {
const _$GetImpl();
@override
String toString() {
return 'CategoryLoaderEvent.get()';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _$GetImpl);
}
@override
int get hashCode => runtimeType.hashCode;
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() get,
required TResult Function(String categoryId) setCategoryId,
}) {
return get();
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? get,
TResult? Function(String categoryId)? setCategoryId,
}) {
return get?.call();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? get,
TResult Function(String categoryId)? setCategoryId,
required TResult orElse(),
}) {
if (get != null) {
return get();
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Get value) get,
required TResult Function(_SetCategoryId value) setCategoryId,
}) {
return get(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Get value)? get,
TResult? Function(_SetCategoryId value)? setCategoryId,
}) {
return get?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Get value)? get,
TResult Function(_SetCategoryId value)? setCategoryId,
required TResult orElse(),
}) {
if (get != null) {
return get(this);
}
return orElse();
}
}
abstract class _Get implements CategoryLoaderEvent {
const factory _Get() = _$GetImpl;
}
/// @nodoc
abstract class _$$SetCategoryIdImplCopyWith<$Res> {
factory _$$SetCategoryIdImplCopyWith(
_$SetCategoryIdImpl value, $Res Function(_$SetCategoryIdImpl) then) =
__$$SetCategoryIdImplCopyWithImpl<$Res>;
@useResult
$Res call({String categoryId});
}
/// @nodoc
class __$$SetCategoryIdImplCopyWithImpl<$Res>
extends _$CategoryLoaderEventCopyWithImpl<$Res, _$SetCategoryIdImpl>
implements _$$SetCategoryIdImplCopyWith<$Res> {
__$$SetCategoryIdImplCopyWithImpl(
_$SetCategoryIdImpl _value, $Res Function(_$SetCategoryIdImpl) _then)
: super(_value, _then);
/// Create a copy of CategoryLoaderEvent
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? categoryId = null,
}) {
return _then(_$SetCategoryIdImpl(
null == categoryId
? _value.categoryId
: categoryId // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
class _$SetCategoryIdImpl implements _SetCategoryId {
const _$SetCategoryIdImpl(this.categoryId);
@override
final String categoryId;
@override
String toString() {
return 'CategoryLoaderEvent.setCategoryId(categoryId: $categoryId)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$SetCategoryIdImpl &&
(identical(other.categoryId, categoryId) ||
other.categoryId == categoryId));
}
@override
int get hashCode => Object.hash(runtimeType, categoryId);
/// Create a copy of CategoryLoaderEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$SetCategoryIdImplCopyWith<_$SetCategoryIdImpl> get copyWith =>
__$$SetCategoryIdImplCopyWithImpl<_$SetCategoryIdImpl>(this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() get,
required TResult Function(String categoryId) setCategoryId,
}) {
return setCategoryId(categoryId);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? get,
TResult? Function(String categoryId)? setCategoryId,
}) {
return setCategoryId?.call(categoryId);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? get,
TResult Function(String categoryId)? setCategoryId,
required TResult orElse(),
}) {
if (setCategoryId != null) {
return setCategoryId(categoryId);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Get value) get,
required TResult Function(_SetCategoryId value) setCategoryId,
}) {
return setCategoryId(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Get value)? get,
TResult? Function(_SetCategoryId value)? setCategoryId,
}) {
return setCategoryId?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Get value)? get,
TResult Function(_SetCategoryId value)? setCategoryId,
required TResult orElse(),
}) {
if (setCategoryId != null) {
return setCategoryId(this);
}
return orElse();
}
}
abstract class _SetCategoryId implements CategoryLoaderEvent {
const factory _SetCategoryId(final String categoryId) = _$SetCategoryIdImpl;
String get categoryId;
/// Create a copy of CategoryLoaderEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
_$$SetCategoryIdImplCopyWith<_$SetCategoryIdImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
mixin _$CategoryLoaderState {
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() loading,
required TResult Function(
List<CategoryModel> categories, String? categoryId)
loaded,
required TResult Function(String message) error,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function(List<CategoryModel> categories, String? categoryId)?
loaded,
TResult? Function(String message)? error,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function(List<CategoryModel> categories, String? categoryId)?
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 $CategoryLoaderStateCopyWith<$Res> {
factory $CategoryLoaderStateCopyWith(
CategoryLoaderState value, $Res Function(CategoryLoaderState) then) =
_$CategoryLoaderStateCopyWithImpl<$Res, CategoryLoaderState>;
}
/// @nodoc
class _$CategoryLoaderStateCopyWithImpl<$Res, $Val extends CategoryLoaderState>
implements $CategoryLoaderStateCopyWith<$Res> {
_$CategoryLoaderStateCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of CategoryLoaderState
/// 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 _$CategoryLoaderStateCopyWithImpl<$Res, _$InitialImpl>
implements _$$InitialImplCopyWith<$Res> {
__$$InitialImplCopyWithImpl(
_$InitialImpl _value, $Res Function(_$InitialImpl) _then)
: super(_value, _then);
/// Create a copy of CategoryLoaderState
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
class _$InitialImpl implements _Initial {
const _$InitialImpl();
@override
String toString() {
return 'CategoryLoaderState.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<CategoryModel> categories, String? categoryId)
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<CategoryModel> categories, String? categoryId)?
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<CategoryModel> categories, String? categoryId)?
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 CategoryLoaderState {
const factory _Initial() = _$InitialImpl;
}
/// @nodoc
abstract class _$$LoadingImplCopyWith<$Res> {
factory _$$LoadingImplCopyWith(
_$LoadingImpl value, $Res Function(_$LoadingImpl) then) =
__$$LoadingImplCopyWithImpl<$Res>;
}
/// @nodoc
class __$$LoadingImplCopyWithImpl<$Res>
extends _$CategoryLoaderStateCopyWithImpl<$Res, _$LoadingImpl>
implements _$$LoadingImplCopyWith<$Res> {
__$$LoadingImplCopyWithImpl(
_$LoadingImpl _value, $Res Function(_$LoadingImpl) _then)
: super(_value, _then);
/// Create a copy of CategoryLoaderState
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
class _$LoadingImpl implements _Loading {
const _$LoadingImpl();
@override
String toString() {
return 'CategoryLoaderState.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<CategoryModel> categories, String? categoryId)
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<CategoryModel> categories, String? categoryId)?
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<CategoryModel> categories, String? categoryId)?
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 CategoryLoaderState {
const factory _Loading() = _$LoadingImpl;
}
/// @nodoc
abstract class _$$LoadedImplCopyWith<$Res> {
factory _$$LoadedImplCopyWith(
_$LoadedImpl value, $Res Function(_$LoadedImpl) then) =
__$$LoadedImplCopyWithImpl<$Res>;
@useResult
$Res call({List<CategoryModel> categories, String? categoryId});
}
/// @nodoc
class __$$LoadedImplCopyWithImpl<$Res>
extends _$CategoryLoaderStateCopyWithImpl<$Res, _$LoadedImpl>
implements _$$LoadedImplCopyWith<$Res> {
__$$LoadedImplCopyWithImpl(
_$LoadedImpl _value, $Res Function(_$LoadedImpl) _then)
: super(_value, _then);
/// Create a copy of CategoryLoaderState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? categories = null,
Object? categoryId = freezed,
}) {
return _then(_$LoadedImpl(
null == categories
? _value._categories
: categories // ignore: cast_nullable_to_non_nullable
as List<CategoryModel>,
freezed == categoryId
? _value.categoryId
: categoryId // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
/// @nodoc
class _$LoadedImpl implements _Loaded {
const _$LoadedImpl(final List<CategoryModel> categories, this.categoryId)
: _categories = categories;
final List<CategoryModel> _categories;
@override
List<CategoryModel> get categories {
if (_categories is EqualUnmodifiableListView) return _categories;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_categories);
}
@override
final String? categoryId;
@override
String toString() {
return 'CategoryLoaderState.loaded(categories: $categories, categoryId: $categoryId)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$LoadedImpl &&
const DeepCollectionEquality()
.equals(other._categories, _categories) &&
(identical(other.categoryId, categoryId) ||
other.categoryId == categoryId));
}
@override
int get hashCode => Object.hash(runtimeType,
const DeepCollectionEquality().hash(_categories), categoryId);
/// Create a copy of CategoryLoaderState
/// 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<CategoryModel> categories, String? categoryId)
loaded,
required TResult Function(String message) error,
}) {
return loaded(categories, categoryId);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function(List<CategoryModel> categories, String? categoryId)?
loaded,
TResult? Function(String message)? error,
}) {
return loaded?.call(categories, categoryId);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function(List<CategoryModel> categories, String? categoryId)?
loaded,
TResult Function(String message)? error,
required TResult orElse(),
}) {
if (loaded != null) {
return loaded(categories, categoryId);
}
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 CategoryLoaderState {
const factory _Loaded(
final List<CategoryModel> categories, final String? categoryId) =
_$LoadedImpl;
List<CategoryModel> get categories;
String? get categoryId;
/// Create a copy of CategoryLoaderState
/// 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 _$CategoryLoaderStateCopyWithImpl<$Res, _$ErrorImpl>
implements _$$ErrorImplCopyWith<$Res> {
__$$ErrorImplCopyWithImpl(
_$ErrorImpl _value, $Res Function(_$ErrorImpl) _then)
: super(_value, _then);
/// Create a copy of CategoryLoaderState
/// 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 'CategoryLoaderState.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 CategoryLoaderState
/// 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<CategoryModel> categories, String? categoryId)
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<CategoryModel> categories, String? categoryId)?
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<CategoryModel> categories, String? categoryId)?
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 CategoryLoaderState {
const factory _Error(final String message) = _$ErrorImpl;
String get message;
/// Create a copy of CategoryLoaderState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
_$$ErrorImplCopyWith<_$ErrorImpl> get copyWith =>
throw _privateConstructorUsedError;
}
@@ -0,0 +1,8 @@
part of 'category_loader_bloc.dart';
@freezed
class CategoryLoaderEvent with _$CategoryLoaderEvent {
const factory CategoryLoaderEvent.get() = _Get;
const factory CategoryLoaderEvent.setCategoryId(String categoryId) =
_SetCategoryId;
}
@@ -0,0 +1,10 @@
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.error(String message) = _Error;
}
@@ -2,6 +2,7 @@ import 'dart:developer';
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart'; import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
import 'package:enaklo_pos/data/models/response/delivery_response_model.dart';
import 'package:enaklo_pos/data/models/response/discount_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'; import 'package:enaklo_pos/presentation/table/models/draft_order_item.dart';
import 'package:enaklo_pos/presentation/table/models/draft_order_model.dart'; import 'package:enaklo_pos/presentation/table/models/draft_order_model.dart';
@@ -58,7 +59,8 @@ class CheckoutBloc extends Bloc<CheckoutEvent, CheckoutState> {
currentState.totalQuantity, currentState.totalQuantity,
currentState.totalPrice, currentState.totalPrice,
currentState.draftName, currentState.draftName,
currentState.orderType)); currentState.orderType,
currentState.deliveryType));
}); });
on<_RemoveItem>((event, emit) { on<_RemoveItem>((event, emit) {
@@ -91,7 +93,8 @@ class CheckoutBloc extends Bloc<CheckoutEvent, CheckoutState> {
currentState.totalQuantity, currentState.totalQuantity,
currentState.totalPrice, currentState.totalPrice,
currentState.draftName, currentState.draftName,
currentState.orderType)); currentState.orderType,
currentState.deliveryType));
}); });
on<_DeleteItem>((event, emit) { on<_DeleteItem>((event, emit) {
@@ -116,7 +119,8 @@ class CheckoutBloc extends Bloc<CheckoutEvent, CheckoutState> {
currentState.totalQuantity, currentState.totalQuantity,
currentState.totalPrice, currentState.totalPrice,
currentState.draftName, currentState.draftName,
currentState.orderType)); currentState.orderType,
currentState.deliveryType));
}); });
on<_Started>((event, emit) async { on<_Started>((event, emit) async {
@@ -126,29 +130,41 @@ class CheckoutBloc extends Bloc<CheckoutEvent, CheckoutState> {
final tax = await settingsLocalDatasource.getTax(); final tax = await settingsLocalDatasource.getTax();
final serviceCharge = await settingsLocalDatasource.getServiceCharge(); final serviceCharge = await settingsLocalDatasource.getServiceCharge();
emit(_Loaded([], null, 0, 0, tax.value, serviceCharge, 0, 0, '', emit(_Loaded(
OrderType.dineIn)); [],
null,
0,
0,
tax.value,
serviceCharge,
0,
0,
'',
OrderType.dineIn,
null,
));
} catch (e) { } catch (e) {
// If loading fails, use default values // If loading fails, use default values
log('Failed to load settings: $e'); log('Failed to load settings: $e');
emit(const _Loaded([], null, 0, 0, 10, 5, 0, 0, '', OrderType.dineIn)); emit(const _Loaded(
[], null, 0, 0, 10, 5, 0, 0, '', OrderType.dineIn, null));
} }
}); });
on<_AddDiscount>((event, emit) { on<_AddDiscount>((event, emit) {
var currentState = state as _Loaded; var currentState = state as _Loaded;
emit(_Loaded( emit(_Loaded(
currentState.items, currentState.items,
event.discount, event.discount,
currentState.discount, currentState.discount,
currentState.discountAmount, currentState.discountAmount,
currentState.tax, currentState.tax,
currentState.serviceCharge, currentState.serviceCharge,
currentState.totalQuantity, currentState.totalQuantity,
currentState.totalPrice, currentState.totalPrice,
currentState.draftName, currentState.draftName,
currentState.orderType, currentState.orderType,
)); currentState.deliveryType));
}); });
on<_RemoveDiscount>((event, emit) { on<_RemoveDiscount>((event, emit) {
@@ -163,7 +179,8 @@ class CheckoutBloc extends Bloc<CheckoutEvent, CheckoutState> {
currentState.totalQuantity, currentState.totalQuantity,
currentState.totalPrice, currentState.totalPrice,
currentState.draftName, currentState.draftName,
currentState.orderType)); currentState.orderType,
currentState.deliveryType));
}); });
on<_AddTax>((event, emit) { on<_AddTax>((event, emit) {
@@ -178,7 +195,8 @@ class CheckoutBloc extends Bloc<CheckoutEvent, CheckoutState> {
currentState.totalQuantity, currentState.totalQuantity,
currentState.totalPrice, currentState.totalPrice,
currentState.draftName, currentState.draftName,
currentState.orderType)); currentState.orderType,
currentState.deliveryType));
}); });
on<_AddServiceCharge>((event, emit) { on<_AddServiceCharge>((event, emit) {
@@ -194,6 +212,7 @@ class CheckoutBloc extends Bloc<CheckoutEvent, CheckoutState> {
currentState.totalPrice, currentState.totalPrice,
currentState.draftName, currentState.draftName,
currentState.orderType, currentState.orderType,
currentState.deliveryType,
)); ));
}); });
@@ -209,7 +228,8 @@ class CheckoutBloc extends Bloc<CheckoutEvent, CheckoutState> {
currentState.totalQuantity, currentState.totalQuantity,
currentState.totalPrice, currentState.totalPrice,
currentState.draftName, currentState.draftName,
currentState.orderType)); currentState.orderType,
currentState.deliveryType));
}); });
on<_RemoveServiceCharge>((event, emit) { on<_RemoveServiceCharge>((event, emit) {
@@ -224,7 +244,8 @@ class CheckoutBloc extends Bloc<CheckoutEvent, CheckoutState> {
currentState.totalQuantity, currentState.totalQuantity,
currentState.totalPrice, currentState.totalPrice,
currentState.draftName, currentState.draftName,
currentState.orderType)); currentState.orderType,
currentState.deliveryType));
}); });
on<_UpdateOrderType>((event, emit) { on<_UpdateOrderType>((event, emit) {
@@ -239,7 +260,8 @@ class CheckoutBloc extends Bloc<CheckoutEvent, CheckoutState> {
currentState.totalQuantity, currentState.totalQuantity,
currentState.totalPrice, currentState.totalPrice,
currentState.draftName, currentState.draftName,
event.orderType)); event.orderType,
currentState.deliveryType));
}); });
on<_UpdateItemNotes>((event, emit) { on<_UpdateItemNotes>((event, emit) {
@@ -260,7 +282,8 @@ class CheckoutBloc extends Bloc<CheckoutEvent, CheckoutState> {
currentState.totalQuantity, currentState.totalQuantity,
currentState.totalPrice, currentState.totalPrice,
currentState.draftName, currentState.draftName,
currentState.orderType)); currentState.orderType,
currentState.deliveryType));
}); });
on<_SaveDraftOrder>((event, emit) async { on<_SaveDraftOrder>((event, emit) async {
@@ -298,19 +321,38 @@ class CheckoutBloc extends Bloc<CheckoutEvent, CheckoutState> {
final draftOrder = event.data; final draftOrder = event.data;
log("draftOrder: ${draftOrder.toMap()}"); log("draftOrder: ${draftOrder.toMap()}");
emit(_Loaded( emit(_Loaded(
draftOrder.orders draftOrder.orders
.map((e) => .map((e) =>
ProductQuantity(product: e.product, quantity: e.quantity)) ProductQuantity(product: e.product, quantity: e.quantity))
.toList(), .toList(),
null, null,
draftOrder.discount, draftOrder.discount,
draftOrder.discountAmount, draftOrder.discountAmount,
draftOrder.tax, draftOrder.tax,
draftOrder.serviceCharge, draftOrder.serviceCharge,
draftOrder.totalQuantity, draftOrder.totalQuantity,
draftOrder.totalPrice, draftOrder.totalPrice,
draftOrder.draftName, draftOrder.draftName,
OrderType.dineIn)); OrderType.dineIn,
null,
));
});
on<_UpdateDeliveryType>((event, emit) {
var currentState = state as _Loaded;
emit(_Loaded(
currentState.items,
currentState.discountModel,
currentState.discount,
currentState.discountAmount,
currentState.tax,
currentState.serviceCharge,
currentState.totalQuantity,
currentState.totalPrice,
currentState.draftName,
currentState.orderType,
event.delivery,
));
}); });
} }
} }
File diff suppressed because it is too large Load Diff
@@ -42,4 +42,8 @@ class CheckoutEvent with _$CheckoutEvent {
//load draft order //load draft order
const factory CheckoutEvent.loadDraftOrder(DraftOrderModel data) = const factory CheckoutEvent.loadDraftOrder(DraftOrderModel data) =
_LoadDraftOrder; _LoadDraftOrder;
// Update delivery type
const factory CheckoutEvent.updateDeliveryType(DeliveryModel delivery) =
_UpdateDeliveryType;
} }
@@ -5,16 +5,18 @@ class CheckoutState with _$CheckoutState {
const factory CheckoutState.initial() = _Initial; const factory CheckoutState.initial() = _Initial;
const factory CheckoutState.loading() = _Loading; const factory CheckoutState.loading() = _Loading;
const factory CheckoutState.loaded( const factory CheckoutState.loaded(
List<ProductQuantity> items, List<ProductQuantity> items,
Discount? discountModel, Discount? discountModel,
int discount, int discount,
int discountAmount, int discountAmount,
int tax, int tax,
int serviceCharge, int serviceCharge,
int totalQuantity, int totalQuantity,
int totalPrice, int totalPrice,
String draftName, String draftName,
OrderType orderType) = _Loaded; OrderType orderType,
DeliveryModel? deliveryType,
) = _Loaded;
const factory CheckoutState.error(String message) = _Error; const factory CheckoutState.error(String message) = _Error;
//save draft order //save draft order
@@ -35,8 +35,8 @@ class OrderFormBloc extends Bloc<OrderFormEvent, OrderFormState> {
customerName: event.customerName, customerName: event.customerName,
notes: '', notes: '',
orderType: event.orderType.name, orderType: event.orderType.name,
tableId: event.table.id, tableId: event.table?.id,
tableNumber: event.table.tableName, tableNumber: event.table?.tableName,
outletId: userData.user?.outletId, outletId: userData.user?.outletId,
customerId: event.customer?.id ?? '', customerId: event.customer?.id ?? '',
orderItems: event.items orderItems: event.items
@@ -77,8 +77,8 @@ class OrderFormBloc extends Bloc<OrderFormEvent, OrderFormState> {
customerName: event.customerName, customerName: event.customerName,
notes: '', notes: '',
orderType: event.orderType.name, orderType: event.orderType.name,
tableId: event.table.id, tableId: event.table?.id ?? "",
tableNumber: event.table.tableName, tableNumber: event.table?.tableName ?? "",
outletId: userData.user?.outletId, outletId: userData.user?.outletId,
customerId: event.customer?.id ?? '', customerId: event.customer?.id ?? '',
orderItems: event.items orderItems: event.items
@@ -131,11 +131,11 @@ class OrderFormBloc extends Bloc<OrderFormEvent, OrderFormState> {
result.fold( result.fold(
(error) => emit(_Error(error)), (error) => emit(_Error(error)),
(success) => emit(_Success(success.data!)), (success) => emit(_SuccessMsg()),
); );
} catch (e) { } catch (e) {
log("Error in AddOrderItemsBloc: $e"); log("Error in AddOrderItemsBloc: $e");
emit(_Error("Failed to add order items: $e")); emit(_Error("Ada kesalahan. Coba lagi nanti"));
} }
}, },
); );
@@ -20,14 +20,14 @@ mixin _$OrderFormEvent {
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function(Order order) started, required TResult Function(Order order) started,
required TResult Function(List<ProductQuantity> items, String customerName, required TResult Function(List<ProductQuantity> items, String customerName,
Customer? customer, OrderType orderType, TableModel table) Customer? customer, OrderType orderType, TableModel? table)
create, create,
required TResult Function( required TResult Function(
List<ProductQuantity> items, List<ProductQuantity> items,
String customerName, String customerName,
Customer? customer, Customer? customer,
OrderType orderType, OrderType orderType,
TableModel table, TableModel? table,
PaymentMethod paymentMethod) PaymentMethod paymentMethod)
createWithPayment, createWithPayment,
required TResult Function(List<ProductQuantity> items, String orderId) required TResult Function(List<ProductQuantity> items, String orderId)
@@ -46,14 +46,14 @@ mixin _$OrderFormEvent {
TResult? whenOrNull<TResult extends Object?>({ TResult? whenOrNull<TResult extends Object?>({
TResult? Function(Order order)? started, TResult? Function(Order order)? started,
TResult? Function(List<ProductQuantity> items, String customerName, TResult? Function(List<ProductQuantity> items, String customerName,
Customer? customer, OrderType orderType, TableModel table)? Customer? customer, OrderType orderType, TableModel? table)?
create, create,
TResult? Function( TResult? Function(
List<ProductQuantity> items, List<ProductQuantity> items,
String customerName, String customerName,
Customer? customer, Customer? customer,
OrderType orderType, OrderType orderType,
TableModel table, TableModel? table,
PaymentMethod paymentMethod)? PaymentMethod paymentMethod)?
createWithPayment, createWithPayment,
TResult? Function(List<ProductQuantity> items, String orderId)? addToOrder, TResult? Function(List<ProductQuantity> items, String orderId)? addToOrder,
@@ -69,14 +69,14 @@ mixin _$OrderFormEvent {
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function(Order order)? started, TResult Function(Order order)? started,
TResult Function(List<ProductQuantity> items, String customerName, TResult Function(List<ProductQuantity> items, String customerName,
Customer? customer, OrderType orderType, TableModel table)? Customer? customer, OrderType orderType, TableModel? table)?
create, create,
TResult Function( TResult Function(
List<ProductQuantity> items, List<ProductQuantity> items,
String customerName, String customerName,
Customer? customer, Customer? customer,
OrderType orderType, OrderType orderType,
TableModel table, TableModel? table,
PaymentMethod paymentMethod)? PaymentMethod paymentMethod)?
createWithPayment, createWithPayment,
TResult Function(List<ProductQuantity> items, String orderId)? addToOrder, TResult Function(List<ProductQuantity> items, String orderId)? addToOrder,
@@ -219,14 +219,14 @@ class _$StartedImpl implements _Started {
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function(Order order) started, required TResult Function(Order order) started,
required TResult Function(List<ProductQuantity> items, String customerName, required TResult Function(List<ProductQuantity> items, String customerName,
Customer? customer, OrderType orderType, TableModel table) Customer? customer, OrderType orderType, TableModel? table)
create, create,
required TResult Function( required TResult Function(
List<ProductQuantity> items, List<ProductQuantity> items,
String customerName, String customerName,
Customer? customer, Customer? customer,
OrderType orderType, OrderType orderType,
TableModel table, TableModel? table,
PaymentMethod paymentMethod) PaymentMethod paymentMethod)
createWithPayment, createWithPayment,
required TResult Function(List<ProductQuantity> items, String orderId) required TResult Function(List<ProductQuantity> items, String orderId)
@@ -248,14 +248,14 @@ class _$StartedImpl implements _Started {
TResult? whenOrNull<TResult extends Object?>({ TResult? whenOrNull<TResult extends Object?>({
TResult? Function(Order order)? started, TResult? Function(Order order)? started,
TResult? Function(List<ProductQuantity> items, String customerName, TResult? Function(List<ProductQuantity> items, String customerName,
Customer? customer, OrderType orderType, TableModel table)? Customer? customer, OrderType orderType, TableModel? table)?
create, create,
TResult? Function( TResult? Function(
List<ProductQuantity> items, List<ProductQuantity> items,
String customerName, String customerName,
Customer? customer, Customer? customer,
OrderType orderType, OrderType orderType,
TableModel table, TableModel? table,
PaymentMethod paymentMethod)? PaymentMethod paymentMethod)?
createWithPayment, createWithPayment,
TResult? Function(List<ProductQuantity> items, String orderId)? addToOrder, TResult? Function(List<ProductQuantity> items, String orderId)? addToOrder,
@@ -274,14 +274,14 @@ class _$StartedImpl implements _Started {
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function(Order order)? started, TResult Function(Order order)? started,
TResult Function(List<ProductQuantity> items, String customerName, TResult Function(List<ProductQuantity> items, String customerName,
Customer? customer, OrderType orderType, TableModel table)? Customer? customer, OrderType orderType, TableModel? table)?
create, create,
TResult Function( TResult Function(
List<ProductQuantity> items, List<ProductQuantity> items,
String customerName, String customerName,
Customer? customer, Customer? customer,
OrderType orderType, OrderType orderType,
TableModel table, TableModel? table,
PaymentMethod paymentMethod)? PaymentMethod paymentMethod)?
createWithPayment, createWithPayment,
TResult Function(List<ProductQuantity> items, String orderId)? addToOrder, TResult Function(List<ProductQuantity> items, String orderId)? addToOrder,
@@ -372,7 +372,7 @@ abstract class _$$CreateImplCopyWith<$Res> {
String customerName, String customerName,
Customer? customer, Customer? customer,
OrderType orderType, OrderType orderType,
TableModel table}); TableModel? table});
} }
/// @nodoc /// @nodoc
@@ -392,7 +392,7 @@ class __$$CreateImplCopyWithImpl<$Res>
Object? customerName = null, Object? customerName = null,
Object? customer = freezed, Object? customer = freezed,
Object? orderType = null, Object? orderType = null,
Object? table = null, Object? table = freezed,
}) { }) {
return _then(_$CreateImpl( return _then(_$CreateImpl(
items: null == items items: null == items
@@ -411,10 +411,10 @@ class __$$CreateImplCopyWithImpl<$Res>
? _value.orderType ? _value.orderType
: orderType // ignore: cast_nullable_to_non_nullable : orderType // ignore: cast_nullable_to_non_nullable
as OrderType, as OrderType,
table: null == table table: freezed == table
? _value.table ? _value.table
: table // ignore: cast_nullable_to_non_nullable : table // ignore: cast_nullable_to_non_nullable
as TableModel, as TableModel?,
)); ));
} }
} }
@@ -445,7 +445,7 @@ class _$CreateImpl implements _Create {
@override @override
final OrderType orderType; final OrderType orderType;
@override @override
final TableModel table; final TableModel? table;
@override @override
String toString() { String toString() {
@@ -489,14 +489,14 @@ class _$CreateImpl implements _Create {
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function(Order order) started, required TResult Function(Order order) started,
required TResult Function(List<ProductQuantity> items, String customerName, required TResult Function(List<ProductQuantity> items, String customerName,
Customer? customer, OrderType orderType, TableModel table) Customer? customer, OrderType orderType, TableModel? table)
create, create,
required TResult Function( required TResult Function(
List<ProductQuantity> items, List<ProductQuantity> items,
String customerName, String customerName,
Customer? customer, Customer? customer,
OrderType orderType, OrderType orderType,
TableModel table, TableModel? table,
PaymentMethod paymentMethod) PaymentMethod paymentMethod)
createWithPayment, createWithPayment,
required TResult Function(List<ProductQuantity> items, String orderId) required TResult Function(List<ProductQuantity> items, String orderId)
@@ -518,14 +518,14 @@ class _$CreateImpl implements _Create {
TResult? whenOrNull<TResult extends Object?>({ TResult? whenOrNull<TResult extends Object?>({
TResult? Function(Order order)? started, TResult? Function(Order order)? started,
TResult? Function(List<ProductQuantity> items, String customerName, TResult? Function(List<ProductQuantity> items, String customerName,
Customer? customer, OrderType orderType, TableModel table)? Customer? customer, OrderType orderType, TableModel? table)?
create, create,
TResult? Function( TResult? Function(
List<ProductQuantity> items, List<ProductQuantity> items,
String customerName, String customerName,
Customer? customer, Customer? customer,
OrderType orderType, OrderType orderType,
TableModel table, TableModel? table,
PaymentMethod paymentMethod)? PaymentMethod paymentMethod)?
createWithPayment, createWithPayment,
TResult? Function(List<ProductQuantity> items, String orderId)? addToOrder, TResult? Function(List<ProductQuantity> items, String orderId)? addToOrder,
@@ -544,14 +544,14 @@ class _$CreateImpl implements _Create {
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function(Order order)? started, TResult Function(Order order)? started,
TResult Function(List<ProductQuantity> items, String customerName, TResult Function(List<ProductQuantity> items, String customerName,
Customer? customer, OrderType orderType, TableModel table)? Customer? customer, OrderType orderType, TableModel? table)?
create, create,
TResult Function( TResult Function(
List<ProductQuantity> items, List<ProductQuantity> items,
String customerName, String customerName,
Customer? customer, Customer? customer,
OrderType orderType, OrderType orderType,
TableModel table, TableModel? table,
PaymentMethod paymentMethod)? PaymentMethod paymentMethod)?
createWithPayment, createWithPayment,
TResult Function(List<ProductQuantity> items, String orderId)? addToOrder, TResult Function(List<ProductQuantity> items, String orderId)? addToOrder,
@@ -625,13 +625,13 @@ abstract class _Create implements OrderFormEvent {
required final String customerName, required final String customerName,
required final Customer? customer, required final Customer? customer,
required final OrderType orderType, required final OrderType orderType,
required final TableModel table}) = _$CreateImpl; required final TableModel? table}) = _$CreateImpl;
List<ProductQuantity> get items; List<ProductQuantity> get items;
String get customerName; String get customerName;
Customer? get customer; Customer? get customer;
OrderType get orderType; OrderType get orderType;
TableModel get table; TableModel? get table;
/// Create a copy of OrderFormEvent /// Create a copy of OrderFormEvent
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@@ -652,7 +652,7 @@ abstract class _$$CreateWithPaymentMethodImplCopyWith<$Res> {
String customerName, String customerName,
Customer? customer, Customer? customer,
OrderType orderType, OrderType orderType,
TableModel table, TableModel? table,
PaymentMethod paymentMethod}); PaymentMethod paymentMethod});
} }
@@ -674,7 +674,7 @@ class __$$CreateWithPaymentMethodImplCopyWithImpl<$Res>
Object? customerName = null, Object? customerName = null,
Object? customer = freezed, Object? customer = freezed,
Object? orderType = null, Object? orderType = null,
Object? table = null, Object? table = freezed,
Object? paymentMethod = null, Object? paymentMethod = null,
}) { }) {
return _then(_$CreateWithPaymentMethodImpl( return _then(_$CreateWithPaymentMethodImpl(
@@ -694,10 +694,10 @@ class __$$CreateWithPaymentMethodImplCopyWithImpl<$Res>
? _value.orderType ? _value.orderType
: orderType // ignore: cast_nullable_to_non_nullable : orderType // ignore: cast_nullable_to_non_nullable
as OrderType, as OrderType,
table: null == table table: freezed == table
? _value.table ? _value.table
: table // ignore: cast_nullable_to_non_nullable : table // ignore: cast_nullable_to_non_nullable
as TableModel, as TableModel?,
paymentMethod: null == paymentMethod paymentMethod: null == paymentMethod
? _value.paymentMethod ? _value.paymentMethod
: paymentMethod // ignore: cast_nullable_to_non_nullable : paymentMethod // ignore: cast_nullable_to_non_nullable
@@ -733,7 +733,7 @@ class _$CreateWithPaymentMethodImpl implements _CreateWithPaymentMethod {
@override @override
final OrderType orderType; final OrderType orderType;
@override @override
final TableModel table; final TableModel? table;
@override @override
final PaymentMethod paymentMethod; final PaymentMethod paymentMethod;
@@ -783,14 +783,14 @@ class _$CreateWithPaymentMethodImpl implements _CreateWithPaymentMethod {
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function(Order order) started, required TResult Function(Order order) started,
required TResult Function(List<ProductQuantity> items, String customerName, required TResult Function(List<ProductQuantity> items, String customerName,
Customer? customer, OrderType orderType, TableModel table) Customer? customer, OrderType orderType, TableModel? table)
create, create,
required TResult Function( required TResult Function(
List<ProductQuantity> items, List<ProductQuantity> items,
String customerName, String customerName,
Customer? customer, Customer? customer,
OrderType orderType, OrderType orderType,
TableModel table, TableModel? table,
PaymentMethod paymentMethod) PaymentMethod paymentMethod)
createWithPayment, createWithPayment,
required TResult Function(List<ProductQuantity> items, String orderId) required TResult Function(List<ProductQuantity> items, String orderId)
@@ -813,14 +813,14 @@ class _$CreateWithPaymentMethodImpl implements _CreateWithPaymentMethod {
TResult? whenOrNull<TResult extends Object?>({ TResult? whenOrNull<TResult extends Object?>({
TResult? Function(Order order)? started, TResult? Function(Order order)? started,
TResult? Function(List<ProductQuantity> items, String customerName, TResult? Function(List<ProductQuantity> items, String customerName,
Customer? customer, OrderType orderType, TableModel table)? Customer? customer, OrderType orderType, TableModel? table)?
create, create,
TResult? Function( TResult? Function(
List<ProductQuantity> items, List<ProductQuantity> items,
String customerName, String customerName,
Customer? customer, Customer? customer,
OrderType orderType, OrderType orderType,
TableModel table, TableModel? table,
PaymentMethod paymentMethod)? PaymentMethod paymentMethod)?
createWithPayment, createWithPayment,
TResult? Function(List<ProductQuantity> items, String orderId)? addToOrder, TResult? Function(List<ProductQuantity> items, String orderId)? addToOrder,
@@ -840,14 +840,14 @@ class _$CreateWithPaymentMethodImpl implements _CreateWithPaymentMethod {
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function(Order order)? started, TResult Function(Order order)? started,
TResult Function(List<ProductQuantity> items, String customerName, TResult Function(List<ProductQuantity> items, String customerName,
Customer? customer, OrderType orderType, TableModel table)? Customer? customer, OrderType orderType, TableModel? table)?
create, create,
TResult Function( TResult Function(
List<ProductQuantity> items, List<ProductQuantity> items,
String customerName, String customerName,
Customer? customer, Customer? customer,
OrderType orderType, OrderType orderType,
TableModel table, TableModel? table,
PaymentMethod paymentMethod)? PaymentMethod paymentMethod)?
createWithPayment, createWithPayment,
TResult Function(List<ProductQuantity> items, String orderId)? addToOrder, TResult Function(List<ProductQuantity> items, String orderId)? addToOrder,
@@ -922,7 +922,7 @@ abstract class _CreateWithPaymentMethod implements OrderFormEvent {
required final String customerName, required final String customerName,
required final Customer? customer, required final Customer? customer,
required final OrderType orderType, required final OrderType orderType,
required final TableModel table, required final TableModel? table,
required final PaymentMethod paymentMethod}) = required final PaymentMethod paymentMethod}) =
_$CreateWithPaymentMethodImpl; _$CreateWithPaymentMethodImpl;
@@ -930,7 +930,7 @@ abstract class _CreateWithPaymentMethod implements OrderFormEvent {
String get customerName; String get customerName;
Customer? get customer; Customer? get customer;
OrderType get orderType; OrderType get orderType;
TableModel get table; TableModel? get table;
PaymentMethod get paymentMethod; PaymentMethod get paymentMethod;
/// Create a copy of OrderFormEvent /// Create a copy of OrderFormEvent
@@ -1027,14 +1027,14 @@ class _$AddToOrderImpl implements _AddToOrder {
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function(Order order) started, required TResult Function(Order order) started,
required TResult Function(List<ProductQuantity> items, String customerName, required TResult Function(List<ProductQuantity> items, String customerName,
Customer? customer, OrderType orderType, TableModel table) Customer? customer, OrderType orderType, TableModel? table)
create, create,
required TResult Function( required TResult Function(
List<ProductQuantity> items, List<ProductQuantity> items,
String customerName, String customerName,
Customer? customer, Customer? customer,
OrderType orderType, OrderType orderType,
TableModel table, TableModel? table,
PaymentMethod paymentMethod) PaymentMethod paymentMethod)
createWithPayment, createWithPayment,
required TResult Function(List<ProductQuantity> items, String orderId) required TResult Function(List<ProductQuantity> items, String orderId)
@@ -1056,14 +1056,14 @@ class _$AddToOrderImpl implements _AddToOrder {
TResult? whenOrNull<TResult extends Object?>({ TResult? whenOrNull<TResult extends Object?>({
TResult? Function(Order order)? started, TResult? Function(Order order)? started,
TResult? Function(List<ProductQuantity> items, String customerName, TResult? Function(List<ProductQuantity> items, String customerName,
Customer? customer, OrderType orderType, TableModel table)? Customer? customer, OrderType orderType, TableModel? table)?
create, create,
TResult? Function( TResult? Function(
List<ProductQuantity> items, List<ProductQuantity> items,
String customerName, String customerName,
Customer? customer, Customer? customer,
OrderType orderType, OrderType orderType,
TableModel table, TableModel? table,
PaymentMethod paymentMethod)? PaymentMethod paymentMethod)?
createWithPayment, createWithPayment,
TResult? Function(List<ProductQuantity> items, String orderId)? addToOrder, TResult? Function(List<ProductQuantity> items, String orderId)? addToOrder,
@@ -1082,14 +1082,14 @@ class _$AddToOrderImpl implements _AddToOrder {
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function(Order order)? started, TResult Function(Order order)? started,
TResult Function(List<ProductQuantity> items, String customerName, TResult Function(List<ProductQuantity> items, String customerName,
Customer? customer, OrderType orderType, TableModel table)? Customer? customer, OrderType orderType, TableModel? table)?
create, create,
TResult Function( TResult Function(
List<ProductQuantity> items, List<ProductQuantity> items,
String customerName, String customerName,
Customer? customer, Customer? customer,
OrderType orderType, OrderType orderType,
TableModel table, TableModel? table,
PaymentMethod paymentMethod)? PaymentMethod paymentMethod)?
createWithPayment, createWithPayment,
TResult Function(List<ProductQuantity> items, String orderId)? addToOrder, TResult Function(List<ProductQuantity> items, String orderId)? addToOrder,
@@ -1269,14 +1269,14 @@ class _$RefundImpl implements _Refund {
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function(Order order) started, required TResult Function(Order order) started,
required TResult Function(List<ProductQuantity> items, String customerName, required TResult Function(List<ProductQuantity> items, String customerName,
Customer? customer, OrderType orderType, TableModel table) Customer? customer, OrderType orderType, TableModel? table)
create, create,
required TResult Function( required TResult Function(
List<ProductQuantity> items, List<ProductQuantity> items,
String customerName, String customerName,
Customer? customer, Customer? customer,
OrderType orderType, OrderType orderType,
TableModel table, TableModel? table,
PaymentMethod paymentMethod) PaymentMethod paymentMethod)
createWithPayment, createWithPayment,
required TResult Function(List<ProductQuantity> items, String orderId) required TResult Function(List<ProductQuantity> items, String orderId)
@@ -1298,14 +1298,14 @@ class _$RefundImpl implements _Refund {
TResult? whenOrNull<TResult extends Object?>({ TResult? whenOrNull<TResult extends Object?>({
TResult? Function(Order order)? started, TResult? Function(Order order)? started,
TResult? Function(List<ProductQuantity> items, String customerName, TResult? Function(List<ProductQuantity> items, String customerName,
Customer? customer, OrderType orderType, TableModel table)? Customer? customer, OrderType orderType, TableModel? table)?
create, create,
TResult? Function( TResult? Function(
List<ProductQuantity> items, List<ProductQuantity> items,
String customerName, String customerName,
Customer? customer, Customer? customer,
OrderType orderType, OrderType orderType,
TableModel table, TableModel? table,
PaymentMethod paymentMethod)? PaymentMethod paymentMethod)?
createWithPayment, createWithPayment,
TResult? Function(List<ProductQuantity> items, String orderId)? addToOrder, TResult? Function(List<ProductQuantity> items, String orderId)? addToOrder,
@@ -1324,14 +1324,14 @@ class _$RefundImpl implements _Refund {
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function(Order order)? started, TResult Function(Order order)? started,
TResult Function(List<ProductQuantity> items, String customerName, TResult Function(List<ProductQuantity> items, String customerName,
Customer? customer, OrderType orderType, TableModel table)? Customer? customer, OrderType orderType, TableModel? table)?
create, create,
TResult Function( TResult Function(
List<ProductQuantity> items, List<ProductQuantity> items,
String customerName, String customerName,
Customer? customer, Customer? customer,
OrderType orderType, OrderType orderType,
TableModel table, TableModel? table,
PaymentMethod paymentMethod)? PaymentMethod paymentMethod)?
createWithPayment, createWithPayment,
TResult Function(List<ProductQuantity> items, String orderId)? addToOrder, TResult Function(List<ProductQuantity> items, String orderId)? addToOrder,
@@ -1513,14 +1513,14 @@ class _$VoidOrderImpl implements _VoidOrder {
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function(Order order) started, required TResult Function(Order order) started,
required TResult Function(List<ProductQuantity> items, String customerName, required TResult Function(List<ProductQuantity> items, String customerName,
Customer? customer, OrderType orderType, TableModel table) Customer? customer, OrderType orderType, TableModel? table)
create, create,
required TResult Function( required TResult Function(
List<ProductQuantity> items, List<ProductQuantity> items,
String customerName, String customerName,
Customer? customer, Customer? customer,
OrderType orderType, OrderType orderType,
TableModel table, TableModel? table,
PaymentMethod paymentMethod) PaymentMethod paymentMethod)
createWithPayment, createWithPayment,
required TResult Function(List<ProductQuantity> items, String orderId) required TResult Function(List<ProductQuantity> items, String orderId)
@@ -1542,14 +1542,14 @@ class _$VoidOrderImpl implements _VoidOrder {
TResult? whenOrNull<TResult extends Object?>({ TResult? whenOrNull<TResult extends Object?>({
TResult? Function(Order order)? started, TResult? Function(Order order)? started,
TResult? Function(List<ProductQuantity> items, String customerName, TResult? Function(List<ProductQuantity> items, String customerName,
Customer? customer, OrderType orderType, TableModel table)? Customer? customer, OrderType orderType, TableModel? table)?
create, create,
TResult? Function( TResult? Function(
List<ProductQuantity> items, List<ProductQuantity> items,
String customerName, String customerName,
Customer? customer, Customer? customer,
OrderType orderType, OrderType orderType,
TableModel table, TableModel? table,
PaymentMethod paymentMethod)? PaymentMethod paymentMethod)?
createWithPayment, createWithPayment,
TResult? Function(List<ProductQuantity> items, String orderId)? addToOrder, TResult? Function(List<ProductQuantity> items, String orderId)? addToOrder,
@@ -1568,14 +1568,14 @@ class _$VoidOrderImpl implements _VoidOrder {
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function(Order order)? started, TResult Function(Order order)? started,
TResult Function(List<ProductQuantity> items, String customerName, TResult Function(List<ProductQuantity> items, String customerName,
Customer? customer, OrderType orderType, TableModel table)? Customer? customer, OrderType orderType, TableModel? table)?
create, create,
TResult Function( TResult Function(
List<ProductQuantity> items, List<ProductQuantity> items,
String customerName, String customerName,
Customer? customer, Customer? customer,
OrderType orderType, OrderType orderType,
TableModel table, TableModel? table,
PaymentMethod paymentMethod)? PaymentMethod paymentMethod)?
createWithPayment, createWithPayment,
TResult Function(List<ProductQuantity> items, String orderId)? addToOrder, TResult Function(List<ProductQuantity> items, String orderId)? addToOrder,
@@ -1730,14 +1730,14 @@ class _$ToggleItemImpl implements _ToggleItem {
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function(Order order) started, required TResult Function(Order order) started,
required TResult Function(List<ProductQuantity> items, String customerName, required TResult Function(List<ProductQuantity> items, String customerName,
Customer? customer, OrderType orderType, TableModel table) Customer? customer, OrderType orderType, TableModel? table)
create, create,
required TResult Function( required TResult Function(
List<ProductQuantity> items, List<ProductQuantity> items,
String customerName, String customerName,
Customer? customer, Customer? customer,
OrderType orderType, OrderType orderType,
TableModel table, TableModel? table,
PaymentMethod paymentMethod) PaymentMethod paymentMethod)
createWithPayment, createWithPayment,
required TResult Function(List<ProductQuantity> items, String orderId) required TResult Function(List<ProductQuantity> items, String orderId)
@@ -1759,14 +1759,14 @@ class _$ToggleItemImpl implements _ToggleItem {
TResult? whenOrNull<TResult extends Object?>({ TResult? whenOrNull<TResult extends Object?>({
TResult? Function(Order order)? started, TResult? Function(Order order)? started,
TResult? Function(List<ProductQuantity> items, String customerName, TResult? Function(List<ProductQuantity> items, String customerName,
Customer? customer, OrderType orderType, TableModel table)? Customer? customer, OrderType orderType, TableModel? table)?
create, create,
TResult? Function( TResult? Function(
List<ProductQuantity> items, List<ProductQuantity> items,
String customerName, String customerName,
Customer? customer, Customer? customer,
OrderType orderType, OrderType orderType,
TableModel table, TableModel? table,
PaymentMethod paymentMethod)? PaymentMethod paymentMethod)?
createWithPayment, createWithPayment,
TResult? Function(List<ProductQuantity> items, String orderId)? addToOrder, TResult? Function(List<ProductQuantity> items, String orderId)? addToOrder,
@@ -1785,14 +1785,14 @@ class _$ToggleItemImpl implements _ToggleItem {
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function(Order order)? started, TResult Function(Order order)? started,
TResult Function(List<ProductQuantity> items, String customerName, TResult Function(List<ProductQuantity> items, String customerName,
Customer? customer, OrderType orderType, TableModel table)? Customer? customer, OrderType orderType, TableModel? table)?
create, create,
TResult Function( TResult Function(
List<ProductQuantity> items, List<ProductQuantity> items,
String customerName, String customerName,
Customer? customer, Customer? customer,
OrderType orderType, OrderType orderType,
TableModel table, TableModel? table,
PaymentMethod paymentMethod)? PaymentMethod paymentMethod)?
createWithPayment, createWithPayment,
TResult Function(List<ProductQuantity> items, String orderId)? addToOrder, TResult Function(List<ProductQuantity> items, String orderId)? addToOrder,
@@ -1944,14 +1944,14 @@ class _$ToggleSelectAllImpl implements _ToggleSelectAll {
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function(Order order) started, required TResult Function(Order order) started,
required TResult Function(List<ProductQuantity> items, String customerName, required TResult Function(List<ProductQuantity> items, String customerName,
Customer? customer, OrderType orderType, TableModel table) Customer? customer, OrderType orderType, TableModel? table)
create, create,
required TResult Function( required TResult Function(
List<ProductQuantity> items, List<ProductQuantity> items,
String customerName, String customerName,
Customer? customer, Customer? customer,
OrderType orderType, OrderType orderType,
TableModel table, TableModel? table,
PaymentMethod paymentMethod) PaymentMethod paymentMethod)
createWithPayment, createWithPayment,
required TResult Function(List<ProductQuantity> items, String orderId) required TResult Function(List<ProductQuantity> items, String orderId)
@@ -1973,14 +1973,14 @@ class _$ToggleSelectAllImpl implements _ToggleSelectAll {
TResult? whenOrNull<TResult extends Object?>({ TResult? whenOrNull<TResult extends Object?>({
TResult? Function(Order order)? started, TResult? Function(Order order)? started,
TResult? Function(List<ProductQuantity> items, String customerName, TResult? Function(List<ProductQuantity> items, String customerName,
Customer? customer, OrderType orderType, TableModel table)? Customer? customer, OrderType orderType, TableModel? table)?
create, create,
TResult? Function( TResult? Function(
List<ProductQuantity> items, List<ProductQuantity> items,
String customerName, String customerName,
Customer? customer, Customer? customer,
OrderType orderType, OrderType orderType,
TableModel table, TableModel? table,
PaymentMethod paymentMethod)? PaymentMethod paymentMethod)?
createWithPayment, createWithPayment,
TResult? Function(List<ProductQuantity> items, String orderId)? addToOrder, TResult? Function(List<ProductQuantity> items, String orderId)? addToOrder,
@@ -1999,14 +1999,14 @@ class _$ToggleSelectAllImpl implements _ToggleSelectAll {
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function(Order order)? started, TResult Function(Order order)? started,
TResult Function(List<ProductQuantity> items, String customerName, TResult Function(List<ProductQuantity> items, String customerName,
Customer? customer, OrderType orderType, TableModel table)? Customer? customer, OrderType orderType, TableModel? table)?
create, create,
TResult Function( TResult Function(
List<ProductQuantity> items, List<ProductQuantity> items,
String customerName, String customerName,
Customer? customer, Customer? customer,
OrderType orderType, OrderType orderType,
TableModel table, TableModel? table,
PaymentMethod paymentMethod)? PaymentMethod paymentMethod)?
createWithPayment, createWithPayment,
TResult Function(List<ProductQuantity> items, String orderId)? addToOrder, TResult Function(List<ProductQuantity> items, String orderId)? addToOrder,
@@ -8,14 +8,14 @@ class OrderFormEvent with _$OrderFormEvent {
required String customerName, required String customerName,
required Customer? customer, required Customer? customer,
required OrderType orderType, required OrderType orderType,
required TableModel table, required TableModel? table,
}) = _Create; }) = _Create;
const factory OrderFormEvent.createWithPayment({ const factory OrderFormEvent.createWithPayment({
required List<ProductQuantity> items, required List<ProductQuantity> items,
required String customerName, required String customerName,
required Customer? customer, required Customer? customer,
required OrderType orderType, required OrderType orderType,
required TableModel table, required TableModel? table,
required PaymentMethod paymentMethod, required PaymentMethod paymentMethod,
}) = _CreateWithPaymentMethod; }) = _CreateWithPaymentMethod;
const factory OrderFormEvent.addToOrder({ const factory OrderFormEvent.addToOrder({
@@ -1,3 +1,5 @@
import 'dart:async';
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:enaklo_pos/data/datasources/product_remote_datasource.dart'; import 'package:enaklo_pos/data/datasources/product_remote_datasource.dart';
import 'package:enaklo_pos/data/models/response/product_response_model.dart'; import 'package:enaklo_pos/data/models/response/product_response_model.dart';
@@ -9,15 +11,138 @@ part 'product_loader_bloc.freezed.dart';
class ProductLoaderBloc extends Bloc<ProductLoaderEvent, ProductLoaderState> { class ProductLoaderBloc extends Bloc<ProductLoaderEvent, ProductLoaderState> {
final ProductRemoteDatasource _productRemoteDatasource; final ProductRemoteDatasource _productRemoteDatasource;
// Debouncing untuk mencegah multiple load more calls
Timer? _loadMoreDebounce;
bool _isLoadingMore = false;
ProductLoaderBloc(this._productRemoteDatasource) ProductLoaderBloc(this._productRemoteDatasource)
: super(ProductLoaderState.initial()) { : super(ProductLoaderState.initial()) {
on<_GetProduct>((event, emit) async { on<_GetProduct>(_onGetProduct);
emit(const _Loading()); on<_LoadMore>(_onLoadMore);
final result = await _productRemoteDatasource.getProducts(); on<_Refresh>(_onRefresh);
result.fold( }
(l) => emit(_Error(l)),
(r) => emit(_Loaded(r.data?.products ?? [])), @override
Future<void> close() {
_loadMoreDebounce?.cancel();
return super.close();
}
// Debounce transformer untuk load more
// EventTransformer<T> _debounceTransformer<T>() {
// return (events, mapper) {
// return events
// .debounceTime(const Duration(milliseconds: 300))
// .asyncExpand(mapper);
// };
// }
// Initial load
Future<void> _onGetProduct(
_GetProduct event,
Emitter<ProductLoaderState> emit,
) async {
emit(const _Loading());
_isLoadingMore = false; // Reset loading state
final result = await _productRemoteDatasource.getProducts(
page: 1,
limit: 10,
categoryId: event.categoryId,
);
await result.fold(
(failure) async => emit(_Error(failure)),
(response) async {
final products = response.data?.products ?? [];
final hasReachedMax = products.length < 10;
emit(_Loaded(
products: products,
hasReachedMax: hasReachedMax,
currentPage: 1,
isLoadingMore: false,
));
},
);
}
// Load more with enhanced debouncing
Future<void> _onLoadMore(
_LoadMore event,
Emitter<ProductLoaderState> emit,
) async {
final currentState = state;
// Enhanced validation
if (currentState is! _Loaded ||
currentState.hasReachedMax ||
_isLoadingMore ||
currentState.isLoadingMore) {
return;
}
_isLoadingMore = true;
// Emit loading more state
emit(currentState.copyWith(isLoadingMore: true));
final nextPage = currentState.currentPage + 1;
try {
final result = await _productRemoteDatasource.getProducts(
page: nextPage,
limit: 10,
categoryId: event.categoryId,
); );
});
await result.fold(
(failure) async {
// On error, revert loading state but don't show error
// Just silently fail and allow retry
emit(currentState.copyWith(isLoadingMore: false));
_isLoadingMore = false;
},
(response) async {
final newProducts = response.data?.products ?? [];
// Prevent duplicate products
final currentProductIds =
currentState.products.map((p) => p.id).toSet();
final filteredNewProducts = newProducts
.where((product) => !currentProductIds.contains(product.id))
.toList();
final allProducts = List<Product>.from(currentState.products)
..addAll(filteredNewProducts);
final hasReachedMax = newProducts.length < 10;
emit(_Loaded(
products: allProducts,
hasReachedMax: hasReachedMax,
currentPage: nextPage,
isLoadingMore: false,
));
_isLoadingMore = false;
},
);
} catch (e) {
// Handle unexpected errors
emit(currentState.copyWith(isLoadingMore: false));
_isLoadingMore = false;
}
}
// Refresh data
Future<void> _onRefresh(
_Refresh event,
Emitter<ProductLoaderState> emit,
) async {
_isLoadingMore = false;
_loadMoreDebounce?.cancel();
add(const _GetProduct());
} }
} }
@@ -18,33 +18,45 @@ final _privateConstructorUsedError = UnsupportedError(
mixin _$ProductLoaderEvent { mixin _$ProductLoaderEvent {
@optionalTypeArgs @optionalTypeArgs
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function() getProduct, required TResult Function(String? categoryId, String? search) getProduct,
required TResult Function(String? categoryId, String? search) loadMore,
required TResult Function() refresh,
}) => }) =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
@optionalTypeArgs @optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({ TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? getProduct, TResult? Function(String? categoryId, String? search)? getProduct,
TResult? Function(String? categoryId, String? search)? loadMore,
TResult? Function()? refresh,
}) => }) =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
@optionalTypeArgs @optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function()? getProduct, TResult Function(String? categoryId, String? search)? getProduct,
TResult Function(String? categoryId, String? search)? loadMore,
TResult Function()? refresh,
required TResult orElse(), required TResult orElse(),
}) => }) =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
@optionalTypeArgs @optionalTypeArgs
TResult map<TResult extends Object?>({ TResult map<TResult extends Object?>({
required TResult Function(_GetProduct value) getProduct, required TResult Function(_GetProduct value) getProduct,
required TResult Function(_LoadMore value) loadMore,
required TResult Function(_Refresh value) refresh,
}) => }) =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
@optionalTypeArgs @optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({ TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_GetProduct value)? getProduct, TResult? Function(_GetProduct value)? getProduct,
TResult? Function(_LoadMore value)? loadMore,
TResult? Function(_Refresh value)? refresh,
}) => }) =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
@optionalTypeArgs @optionalTypeArgs
TResult maybeMap<TResult extends Object?>({ TResult maybeMap<TResult extends Object?>({
TResult Function(_GetProduct value)? getProduct, TResult Function(_GetProduct value)? getProduct,
TResult Function(_LoadMore value)? loadMore,
TResult Function(_Refresh value)? refresh,
required TResult orElse(), required TResult orElse(),
}) => }) =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
@@ -76,6 +88,8 @@ abstract class _$$GetProductImplCopyWith<$Res> {
factory _$$GetProductImplCopyWith( factory _$$GetProductImplCopyWith(
_$GetProductImpl value, $Res Function(_$GetProductImpl) then) = _$GetProductImpl value, $Res Function(_$GetProductImpl) then) =
__$$GetProductImplCopyWithImpl<$Res>; __$$GetProductImplCopyWithImpl<$Res>;
@useResult
$Res call({String? categoryId, String? search});
} }
/// @nodoc /// @nodoc
@@ -88,51 +102,91 @@ class __$$GetProductImplCopyWithImpl<$Res>
/// Create a copy of ProductLoaderEvent /// Create a copy of ProductLoaderEvent
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? categoryId = freezed,
Object? search = freezed,
}) {
return _then(_$GetProductImpl(
categoryId: freezed == categoryId
? _value.categoryId
: categoryId // ignore: cast_nullable_to_non_nullable
as String?,
search: freezed == search
? _value.search
: search // ignore: cast_nullable_to_non_nullable
as String?,
));
}
} }
/// @nodoc /// @nodoc
class _$GetProductImpl implements _GetProduct { class _$GetProductImpl implements _GetProduct {
const _$GetProductImpl(); const _$GetProductImpl({this.categoryId, this.search});
@override
final String? categoryId;
@override
final String? search;
@override @override
String toString() { String toString() {
return 'ProductLoaderEvent.getProduct()'; return 'ProductLoaderEvent.getProduct(categoryId: $categoryId, search: $search)';
} }
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || return identical(this, other) ||
(other.runtimeType == runtimeType && other is _$GetProductImpl); (other.runtimeType == runtimeType &&
other is _$GetProductImpl &&
(identical(other.categoryId, categoryId) ||
other.categoryId == categoryId) &&
(identical(other.search, search) || other.search == search));
} }
@override @override
int get hashCode => runtimeType.hashCode; int get hashCode => Object.hash(runtimeType, categoryId, search);
/// Create a copy of ProductLoaderEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$GetProductImplCopyWith<_$GetProductImpl> get copyWith =>
__$$GetProductImplCopyWithImpl<_$GetProductImpl>(this, _$identity);
@override @override
@optionalTypeArgs @optionalTypeArgs
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function() getProduct, required TResult Function(String? categoryId, String? search) getProduct,
required TResult Function(String? categoryId, String? search) loadMore,
required TResult Function() refresh,
}) { }) {
return getProduct(); return getProduct(categoryId, search);
} }
@override @override
@optionalTypeArgs @optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({ TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? getProduct, TResult? Function(String? categoryId, String? search)? getProduct,
TResult? Function(String? categoryId, String? search)? loadMore,
TResult? Function()? refresh,
}) { }) {
return getProduct?.call(); return getProduct?.call(categoryId, search);
} }
@override @override
@optionalTypeArgs @optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function()? getProduct, TResult Function(String? categoryId, String? search)? getProduct,
TResult Function(String? categoryId, String? search)? loadMore,
TResult Function()? refresh,
required TResult orElse(), required TResult orElse(),
}) { }) {
if (getProduct != null) { if (getProduct != null) {
return getProduct(); return getProduct(categoryId, search);
} }
return orElse(); return orElse();
} }
@@ -141,6 +195,8 @@ class _$GetProductImpl implements _GetProduct {
@optionalTypeArgs @optionalTypeArgs
TResult map<TResult extends Object?>({ TResult map<TResult extends Object?>({
required TResult Function(_GetProduct value) getProduct, required TResult Function(_GetProduct value) getProduct,
required TResult Function(_LoadMore value) loadMore,
required TResult Function(_Refresh value) refresh,
}) { }) {
return getProduct(this); return getProduct(this);
} }
@@ -149,6 +205,8 @@ class _$GetProductImpl implements _GetProduct {
@optionalTypeArgs @optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({ TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_GetProduct value)? getProduct, TResult? Function(_GetProduct value)? getProduct,
TResult? Function(_LoadMore value)? loadMore,
TResult? Function(_Refresh value)? refresh,
}) { }) {
return getProduct?.call(this); return getProduct?.call(this);
} }
@@ -157,6 +215,8 @@ class _$GetProductImpl implements _GetProduct {
@optionalTypeArgs @optionalTypeArgs
TResult maybeMap<TResult extends Object?>({ TResult maybeMap<TResult extends Object?>({
TResult Function(_GetProduct value)? getProduct, TResult Function(_GetProduct value)? getProduct,
TResult Function(_LoadMore value)? loadMore,
TResult Function(_Refresh value)? refresh,
required TResult orElse(), required TResult orElse(),
}) { }) {
if (getProduct != null) { if (getProduct != null) {
@@ -167,7 +227,285 @@ class _$GetProductImpl implements _GetProduct {
} }
abstract class _GetProduct implements ProductLoaderEvent { abstract class _GetProduct implements ProductLoaderEvent {
const factory _GetProduct() = _$GetProductImpl; const factory _GetProduct({final String? categoryId, final String? search}) =
_$GetProductImpl;
String? get categoryId;
String? get search;
/// Create a copy of ProductLoaderEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
_$$GetProductImplCopyWith<_$GetProductImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class _$$LoadMoreImplCopyWith<$Res> {
factory _$$LoadMoreImplCopyWith(
_$LoadMoreImpl value, $Res Function(_$LoadMoreImpl) then) =
__$$LoadMoreImplCopyWithImpl<$Res>;
@useResult
$Res call({String? categoryId, String? search});
}
/// @nodoc
class __$$LoadMoreImplCopyWithImpl<$Res>
extends _$ProductLoaderEventCopyWithImpl<$Res, _$LoadMoreImpl>
implements _$$LoadMoreImplCopyWith<$Res> {
__$$LoadMoreImplCopyWithImpl(
_$LoadMoreImpl _value, $Res Function(_$LoadMoreImpl) _then)
: super(_value, _then);
/// Create a copy of ProductLoaderEvent
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? categoryId = freezed,
Object? search = freezed,
}) {
return _then(_$LoadMoreImpl(
categoryId: freezed == categoryId
? _value.categoryId
: categoryId // ignore: cast_nullable_to_non_nullable
as String?,
search: freezed == search
? _value.search
: search // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
/// @nodoc
class _$LoadMoreImpl implements _LoadMore {
const _$LoadMoreImpl({this.categoryId, this.search});
@override
final String? categoryId;
@override
final String? search;
@override
String toString() {
return 'ProductLoaderEvent.loadMore(categoryId: $categoryId, search: $search)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$LoadMoreImpl &&
(identical(other.categoryId, categoryId) ||
other.categoryId == categoryId) &&
(identical(other.search, search) || other.search == search));
}
@override
int get hashCode => Object.hash(runtimeType, categoryId, search);
/// Create a copy of ProductLoaderEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$LoadMoreImplCopyWith<_$LoadMoreImpl> get copyWith =>
__$$LoadMoreImplCopyWithImpl<_$LoadMoreImpl>(this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(String? categoryId, String? search) getProduct,
required TResult Function(String? categoryId, String? search) loadMore,
required TResult Function() refresh,
}) {
return loadMore(categoryId, search);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(String? categoryId, String? search)? getProduct,
TResult? Function(String? categoryId, String? search)? loadMore,
TResult? Function()? refresh,
}) {
return loadMore?.call(categoryId, search);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(String? categoryId, String? search)? getProduct,
TResult Function(String? categoryId, String? search)? loadMore,
TResult Function()? refresh,
required TResult orElse(),
}) {
if (loadMore != null) {
return loadMore(categoryId, search);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_GetProduct value) getProduct,
required TResult Function(_LoadMore value) loadMore,
required TResult Function(_Refresh value) refresh,
}) {
return loadMore(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_GetProduct value)? getProduct,
TResult? Function(_LoadMore value)? loadMore,
TResult? Function(_Refresh value)? refresh,
}) {
return loadMore?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_GetProduct value)? getProduct,
TResult Function(_LoadMore value)? loadMore,
TResult Function(_Refresh value)? refresh,
required TResult orElse(),
}) {
if (loadMore != null) {
return loadMore(this);
}
return orElse();
}
}
abstract class _LoadMore implements ProductLoaderEvent {
const factory _LoadMore({final String? categoryId, final String? search}) =
_$LoadMoreImpl;
String? get categoryId;
String? get search;
/// Create a copy of ProductLoaderEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
_$$LoadMoreImplCopyWith<_$LoadMoreImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class _$$RefreshImplCopyWith<$Res> {
factory _$$RefreshImplCopyWith(
_$RefreshImpl value, $Res Function(_$RefreshImpl) then) =
__$$RefreshImplCopyWithImpl<$Res>;
}
/// @nodoc
class __$$RefreshImplCopyWithImpl<$Res>
extends _$ProductLoaderEventCopyWithImpl<$Res, _$RefreshImpl>
implements _$$RefreshImplCopyWith<$Res> {
__$$RefreshImplCopyWithImpl(
_$RefreshImpl _value, $Res Function(_$RefreshImpl) _then)
: super(_value, _then);
/// Create a copy of ProductLoaderEvent
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
class _$RefreshImpl implements _Refresh {
const _$RefreshImpl();
@override
String toString() {
return 'ProductLoaderEvent.refresh()';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _$RefreshImpl);
}
@override
int get hashCode => runtimeType.hashCode;
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(String? categoryId, String? search) getProduct,
required TResult Function(String? categoryId, String? search) loadMore,
required TResult Function() refresh,
}) {
return refresh();
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(String? categoryId, String? search)? getProduct,
TResult? Function(String? categoryId, String? search)? loadMore,
TResult? Function()? refresh,
}) {
return refresh?.call();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(String? categoryId, String? search)? getProduct,
TResult Function(String? categoryId, String? search)? loadMore,
TResult Function()? refresh,
required TResult orElse(),
}) {
if (refresh != null) {
return refresh();
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_GetProduct value) getProduct,
required TResult Function(_LoadMore value) loadMore,
required TResult Function(_Refresh value) refresh,
}) {
return refresh(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_GetProduct value)? getProduct,
TResult? Function(_LoadMore value)? loadMore,
TResult? Function(_Refresh value)? refresh,
}) {
return refresh?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_GetProduct value)? getProduct,
TResult Function(_LoadMore value)? loadMore,
TResult Function(_Refresh value)? refresh,
required TResult orElse(),
}) {
if (refresh != null) {
return refresh(this);
}
return orElse();
}
}
abstract class _Refresh implements ProductLoaderEvent {
const factory _Refresh() = _$RefreshImpl;
} }
/// @nodoc /// @nodoc
@@ -176,7 +514,9 @@ mixin _$ProductLoaderState {
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function() initial, required TResult Function() initial,
required TResult Function() loading, required TResult Function() loading,
required TResult Function(List<Product> products) loaded, required TResult Function(List<Product> products, bool hasReachedMax,
int currentPage, bool isLoadingMore)
loaded,
required TResult Function(String message) error, required TResult Function(String message) error,
}) => }) =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
@@ -184,7 +524,9 @@ mixin _$ProductLoaderState {
TResult? whenOrNull<TResult extends Object?>({ TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial, TResult? Function()? initial,
TResult? Function()? loading, TResult? Function()? loading,
TResult? Function(List<Product> products)? loaded, TResult? Function(List<Product> products, bool hasReachedMax,
int currentPage, bool isLoadingMore)?
loaded,
TResult? Function(String message)? error, TResult? Function(String message)? error,
}) => }) =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
@@ -192,7 +534,9 @@ mixin _$ProductLoaderState {
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial, TResult Function()? initial,
TResult Function()? loading, TResult Function()? loading,
TResult Function(List<Product> products)? loaded, TResult Function(List<Product> products, bool hasReachedMax,
int currentPage, bool isLoadingMore)?
loaded,
TResult Function(String message)? error, TResult Function(String message)? error,
required TResult orElse(), required TResult orElse(),
}) => }) =>
@@ -288,7 +632,9 @@ class _$InitialImpl implements _Initial {
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function() initial, required TResult Function() initial,
required TResult Function() loading, required TResult Function() loading,
required TResult Function(List<Product> products) loaded, required TResult Function(List<Product> products, bool hasReachedMax,
int currentPage, bool isLoadingMore)
loaded,
required TResult Function(String message) error, required TResult Function(String message) error,
}) { }) {
return initial(); return initial();
@@ -299,7 +645,9 @@ class _$InitialImpl implements _Initial {
TResult? whenOrNull<TResult extends Object?>({ TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial, TResult? Function()? initial,
TResult? Function()? loading, TResult? Function()? loading,
TResult? Function(List<Product> products)? loaded, TResult? Function(List<Product> products, bool hasReachedMax,
int currentPage, bool isLoadingMore)?
loaded,
TResult? Function(String message)? error, TResult? Function(String message)? error,
}) { }) {
return initial?.call(); return initial?.call();
@@ -310,7 +658,9 @@ class _$InitialImpl implements _Initial {
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial, TResult Function()? initial,
TResult Function()? loading, TResult Function()? loading,
TResult Function(List<Product> products)? loaded, TResult Function(List<Product> products, bool hasReachedMax,
int currentPage, bool isLoadingMore)?
loaded,
TResult Function(String message)? error, TResult Function(String message)? error,
required TResult orElse(), required TResult orElse(),
}) { }) {
@@ -405,7 +755,9 @@ class _$LoadingImpl implements _Loading {
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function() initial, required TResult Function() initial,
required TResult Function() loading, required TResult Function() loading,
required TResult Function(List<Product> products) loaded, required TResult Function(List<Product> products, bool hasReachedMax,
int currentPage, bool isLoadingMore)
loaded,
required TResult Function(String message) error, required TResult Function(String message) error,
}) { }) {
return loading(); return loading();
@@ -416,7 +768,9 @@ class _$LoadingImpl implements _Loading {
TResult? whenOrNull<TResult extends Object?>({ TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial, TResult? Function()? initial,
TResult? Function()? loading, TResult? Function()? loading,
TResult? Function(List<Product> products)? loaded, TResult? Function(List<Product> products, bool hasReachedMax,
int currentPage, bool isLoadingMore)?
loaded,
TResult? Function(String message)? error, TResult? Function(String message)? error,
}) { }) {
return loading?.call(); return loading?.call();
@@ -427,7 +781,9 @@ class _$LoadingImpl implements _Loading {
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial, TResult Function()? initial,
TResult Function()? loading, TResult Function()? loading,
TResult Function(List<Product> products)? loaded, TResult Function(List<Product> products, bool hasReachedMax,
int currentPage, bool isLoadingMore)?
loaded,
TResult Function(String message)? error, TResult Function(String message)? error,
required TResult orElse(), required TResult orElse(),
}) { }) {
@@ -485,7 +841,11 @@ abstract class _$$LoadedImplCopyWith<$Res> {
_$LoadedImpl value, $Res Function(_$LoadedImpl) then) = _$LoadedImpl value, $Res Function(_$LoadedImpl) then) =
__$$LoadedImplCopyWithImpl<$Res>; __$$LoadedImplCopyWithImpl<$Res>;
@useResult @useResult
$Res call({List<Product> products}); $Res call(
{List<Product> products,
bool hasReachedMax,
int currentPage,
bool isLoadingMore});
} }
/// @nodoc /// @nodoc
@@ -502,12 +862,27 @@ class __$$LoadedImplCopyWithImpl<$Res>
@override @override
$Res call({ $Res call({
Object? products = null, Object? products = null,
Object? hasReachedMax = null,
Object? currentPage = null,
Object? isLoadingMore = null,
}) { }) {
return _then(_$LoadedImpl( return _then(_$LoadedImpl(
null == products products: null == products
? _value._products ? _value._products
: products // ignore: cast_nullable_to_non_nullable : products // ignore: cast_nullable_to_non_nullable
as List<Product>, as List<Product>,
hasReachedMax: null == hasReachedMax
? _value.hasReachedMax
: hasReachedMax // ignore: cast_nullable_to_non_nullable
as bool,
currentPage: null == currentPage
? _value.currentPage
: currentPage // ignore: cast_nullable_to_non_nullable
as int,
isLoadingMore: null == isLoadingMore
? _value.isLoadingMore
: isLoadingMore // ignore: cast_nullable_to_non_nullable
as bool,
)); ));
} }
} }
@@ -515,7 +890,12 @@ class __$$LoadedImplCopyWithImpl<$Res>
/// @nodoc /// @nodoc
class _$LoadedImpl implements _Loaded { class _$LoadedImpl implements _Loaded {
const _$LoadedImpl(final List<Product> products) : _products = products; const _$LoadedImpl(
{required final List<Product> products,
required this.hasReachedMax,
required this.currentPage,
required this.isLoadingMore})
: _products = products;
final List<Product> _products; final List<Product> _products;
@override @override
@@ -525,9 +905,16 @@ class _$LoadedImpl implements _Loaded {
return EqualUnmodifiableListView(_products); return EqualUnmodifiableListView(_products);
} }
@override
final bool hasReachedMax;
@override
final int currentPage;
@override
final bool isLoadingMore;
@override @override
String toString() { String toString() {
return 'ProductLoaderState.loaded(products: $products)'; return 'ProductLoaderState.loaded(products: $products, hasReachedMax: $hasReachedMax, currentPage: $currentPage, isLoadingMore: $isLoadingMore)';
} }
@override @override
@@ -535,12 +922,22 @@ class _$LoadedImpl implements _Loaded {
return identical(this, other) || return identical(this, other) ||
(other.runtimeType == runtimeType && (other.runtimeType == runtimeType &&
other is _$LoadedImpl && other is _$LoadedImpl &&
const DeepCollectionEquality().equals(other._products, _products)); const DeepCollectionEquality().equals(other._products, _products) &&
(identical(other.hasReachedMax, hasReachedMax) ||
other.hasReachedMax == hasReachedMax) &&
(identical(other.currentPage, currentPage) ||
other.currentPage == currentPage) &&
(identical(other.isLoadingMore, isLoadingMore) ||
other.isLoadingMore == isLoadingMore));
} }
@override @override
int get hashCode => int get hashCode => Object.hash(
Object.hash(runtimeType, const DeepCollectionEquality().hash(_products)); runtimeType,
const DeepCollectionEquality().hash(_products),
hasReachedMax,
currentPage,
isLoadingMore);
/// Create a copy of ProductLoaderState /// Create a copy of ProductLoaderState
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@@ -555,10 +952,12 @@ class _$LoadedImpl implements _Loaded {
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function() initial, required TResult Function() initial,
required TResult Function() loading, required TResult Function() loading,
required TResult Function(List<Product> products) loaded, required TResult Function(List<Product> products, bool hasReachedMax,
int currentPage, bool isLoadingMore)
loaded,
required TResult Function(String message) error, required TResult Function(String message) error,
}) { }) {
return loaded(products); return loaded(products, hasReachedMax, currentPage, isLoadingMore);
} }
@override @override
@@ -566,10 +965,12 @@ class _$LoadedImpl implements _Loaded {
TResult? whenOrNull<TResult extends Object?>({ TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial, TResult? Function()? initial,
TResult? Function()? loading, TResult? Function()? loading,
TResult? Function(List<Product> products)? loaded, TResult? Function(List<Product> products, bool hasReachedMax,
int currentPage, bool isLoadingMore)?
loaded,
TResult? Function(String message)? error, TResult? Function(String message)? error,
}) { }) {
return loaded?.call(products); return loaded?.call(products, hasReachedMax, currentPage, isLoadingMore);
} }
@override @override
@@ -577,12 +978,14 @@ class _$LoadedImpl implements _Loaded {
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial, TResult Function()? initial,
TResult Function()? loading, TResult Function()? loading,
TResult Function(List<Product> products)? loaded, TResult Function(List<Product> products, bool hasReachedMax,
int currentPage, bool isLoadingMore)?
loaded,
TResult Function(String message)? error, TResult Function(String message)? error,
required TResult orElse(), required TResult orElse(),
}) { }) {
if (loaded != null) { if (loaded != null) {
return loaded(products); return loaded(products, hasReachedMax, currentPage, isLoadingMore);
} }
return orElse(); return orElse();
} }
@@ -626,9 +1029,16 @@ class _$LoadedImpl implements _Loaded {
} }
abstract class _Loaded implements ProductLoaderState { abstract class _Loaded implements ProductLoaderState {
const factory _Loaded(final List<Product> products) = _$LoadedImpl; const factory _Loaded(
{required final List<Product> products,
required final bool hasReachedMax,
required final int currentPage,
required final bool isLoadingMore}) = _$LoadedImpl;
List<Product> get products; List<Product> get products;
bool get hasReachedMax;
int get currentPage;
bool get isLoadingMore;
/// Create a copy of ProductLoaderState /// Create a copy of ProductLoaderState
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@@ -707,7 +1117,9 @@ class _$ErrorImpl implements _Error {
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function() initial, required TResult Function() initial,
required TResult Function() loading, required TResult Function() loading,
required TResult Function(List<Product> products) loaded, required TResult Function(List<Product> products, bool hasReachedMax,
int currentPage, bool isLoadingMore)
loaded,
required TResult Function(String message) error, required TResult Function(String message) error,
}) { }) {
return error(message); return error(message);
@@ -718,7 +1130,9 @@ class _$ErrorImpl implements _Error {
TResult? whenOrNull<TResult extends Object?>({ TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial, TResult? Function()? initial,
TResult? Function()? loading, TResult? Function()? loading,
TResult? Function(List<Product> products)? loaded, TResult? Function(List<Product> products, bool hasReachedMax,
int currentPage, bool isLoadingMore)?
loaded,
TResult? Function(String message)? error, TResult? Function(String message)? error,
}) { }) {
return error?.call(message); return error?.call(message);
@@ -729,7 +1143,9 @@ class _$ErrorImpl implements _Error {
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial, TResult Function()? initial,
TResult Function()? loading, TResult Function()? loading,
TResult Function(List<Product> products)? loaded, TResult Function(List<Product> products, bool hasReachedMax,
int currentPage, bool isLoadingMore)?
loaded,
TResult Function(String message)? error, TResult Function(String message)? error,
required TResult orElse(), required TResult orElse(),
}) { }) {
@@ -2,5 +2,9 @@ part of 'product_loader_bloc.dart';
@freezed @freezed
class ProductLoaderEvent with _$ProductLoaderEvent { class ProductLoaderEvent with _$ProductLoaderEvent {
const factory ProductLoaderEvent.getProduct() = _GetProduct; const factory ProductLoaderEvent.getProduct(
{String? categoryId, String? search}) = _GetProduct;
const factory ProductLoaderEvent.loadMore(
{String? categoryId, String? search}) = _LoadMore;
const factory ProductLoaderEvent.refresh() = _Refresh;
} }
@@ -4,6 +4,11 @@ part of 'product_loader_bloc.dart';
class ProductLoaderState with _$ProductLoaderState { class ProductLoaderState with _$ProductLoaderState {
const factory ProductLoaderState.initial() = _Initial; const factory ProductLoaderState.initial() = _Initial;
const factory ProductLoaderState.loading() = _Loading; const factory ProductLoaderState.loading() = _Loading;
const factory ProductLoaderState.loaded(List<Product> products) = _Loaded; const factory ProductLoaderState.loaded({
required List<Product> products,
required bool hasReachedMax,
required int currentPage,
required bool isLoadingMore,
}) = _Loaded;
const factory ProductLoaderState.error(String message) = _Error; const factory ProductLoaderState.error(String message) = _Error;
} }
@@ -1,4 +1,3 @@
import 'dart:async';
import 'dart:developer'; import 'dart:developer';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
@@ -0,0 +1,30 @@
import 'package:bloc/bloc.dart';
import 'package:enaklo_pos/data/datasources/user_remote_datasource.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'user_update_outlet_event.dart';
part 'user_update_outlet_state.dart';
part 'user_update_outlet_bloc.freezed.dart';
class UserUpdateOutletBloc
extends Bloc<UserUpdateOutletEvent, UserUpdateOutletState> {
final UserRemoteDatasource _userRemoteDatasource;
UserUpdateOutletBloc(this._userRemoteDatasource)
: super(UserUpdateOutletState.initial()) {
on<_Update>(
(event, emit) async {
emit(const _Loading());
final result = await _userRemoteDatasource.updateOutlet(
event.outlet,
);
result.fold(
(error) => emit(_Error(error)),
(success) => emit(_Success()),
);
},
);
}
}
@@ -0,0 +1,811 @@
// 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 'user_update_outlet_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 _$UserUpdateOutletEvent {
String get outlet => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(String outlet) update,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(String outlet)? update,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(String outlet)? update,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Update value) update,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Update value)? update,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Update value)? update,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
/// Create a copy of UserUpdateOutletEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$UserUpdateOutletEventCopyWith<UserUpdateOutletEvent> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $UserUpdateOutletEventCopyWith<$Res> {
factory $UserUpdateOutletEventCopyWith(UserUpdateOutletEvent value,
$Res Function(UserUpdateOutletEvent) then) =
_$UserUpdateOutletEventCopyWithImpl<$Res, UserUpdateOutletEvent>;
@useResult
$Res call({String outlet});
}
/// @nodoc
class _$UserUpdateOutletEventCopyWithImpl<$Res,
$Val extends UserUpdateOutletEvent>
implements $UserUpdateOutletEventCopyWith<$Res> {
_$UserUpdateOutletEventCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of UserUpdateOutletEvent
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? outlet = null,
}) {
return _then(_value.copyWith(
outlet: null == outlet
? _value.outlet
: outlet // ignore: cast_nullable_to_non_nullable
as String,
) as $Val);
}
}
/// @nodoc
abstract class _$$UpdateImplCopyWith<$Res>
implements $UserUpdateOutletEventCopyWith<$Res> {
factory _$$UpdateImplCopyWith(
_$UpdateImpl value, $Res Function(_$UpdateImpl) then) =
__$$UpdateImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({String outlet});
}
/// @nodoc
class __$$UpdateImplCopyWithImpl<$Res>
extends _$UserUpdateOutletEventCopyWithImpl<$Res, _$UpdateImpl>
implements _$$UpdateImplCopyWith<$Res> {
__$$UpdateImplCopyWithImpl(
_$UpdateImpl _value, $Res Function(_$UpdateImpl) _then)
: super(_value, _then);
/// Create a copy of UserUpdateOutletEvent
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? outlet = null,
}) {
return _then(_$UpdateImpl(
null == outlet
? _value.outlet
: outlet // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
class _$UpdateImpl implements _Update {
const _$UpdateImpl(this.outlet);
@override
final String outlet;
@override
String toString() {
return 'UserUpdateOutletEvent.update(outlet: $outlet)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$UpdateImpl &&
(identical(other.outlet, outlet) || other.outlet == outlet));
}
@override
int get hashCode => Object.hash(runtimeType, outlet);
/// Create a copy of UserUpdateOutletEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$UpdateImplCopyWith<_$UpdateImpl> get copyWith =>
__$$UpdateImplCopyWithImpl<_$UpdateImpl>(this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(String outlet) update,
}) {
return update(outlet);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(String outlet)? update,
}) {
return update?.call(outlet);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(String outlet)? update,
required TResult orElse(),
}) {
if (update != null) {
return update(outlet);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Update value) update,
}) {
return update(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Update value)? update,
}) {
return update?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Update value)? update,
required TResult orElse(),
}) {
if (update != null) {
return update(this);
}
return orElse();
}
}
abstract class _Update implements UserUpdateOutletEvent {
const factory _Update(final String outlet) = _$UpdateImpl;
@override
String get outlet;
/// Create a copy of UserUpdateOutletEvent
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$UpdateImplCopyWith<_$UpdateImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
mixin _$UserUpdateOutletState {
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() loading,
required TResult Function() success,
required TResult Function(String message) error,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function()? success,
TResult? Function(String message)? error,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function()? success,
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(_Success value) success,
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(_Success value)? success,
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(_Success value)? success,
TResult Function(_Error value)? error,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $UserUpdateOutletStateCopyWith<$Res> {
factory $UserUpdateOutletStateCopyWith(UserUpdateOutletState value,
$Res Function(UserUpdateOutletState) then) =
_$UserUpdateOutletStateCopyWithImpl<$Res, UserUpdateOutletState>;
}
/// @nodoc
class _$UserUpdateOutletStateCopyWithImpl<$Res,
$Val extends UserUpdateOutletState>
implements $UserUpdateOutletStateCopyWith<$Res> {
_$UserUpdateOutletStateCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of UserUpdateOutletState
/// 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 _$UserUpdateOutletStateCopyWithImpl<$Res, _$InitialImpl>
implements _$$InitialImplCopyWith<$Res> {
__$$InitialImplCopyWithImpl(
_$InitialImpl _value, $Res Function(_$InitialImpl) _then)
: super(_value, _then);
/// Create a copy of UserUpdateOutletState
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
class _$InitialImpl implements _Initial {
const _$InitialImpl();
@override
String toString() {
return 'UserUpdateOutletState.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,
required TResult Function(String message) error,
}) {
return initial();
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function()? success,
TResult? Function(String message)? error,
}) {
return initial?.call();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function()? success,
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(_Success value) success,
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(_Success value)? success,
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(_Success value)? success,
TResult Function(_Error value)? error,
required TResult orElse(),
}) {
if (initial != null) {
return initial(this);
}
return orElse();
}
}
abstract class _Initial implements UserUpdateOutletState {
const factory _Initial() = _$InitialImpl;
}
/// @nodoc
abstract class _$$LoadingImplCopyWith<$Res> {
factory _$$LoadingImplCopyWith(
_$LoadingImpl value, $Res Function(_$LoadingImpl) then) =
__$$LoadingImplCopyWithImpl<$Res>;
}
/// @nodoc
class __$$LoadingImplCopyWithImpl<$Res>
extends _$UserUpdateOutletStateCopyWithImpl<$Res, _$LoadingImpl>
implements _$$LoadingImplCopyWith<$Res> {
__$$LoadingImplCopyWithImpl(
_$LoadingImpl _value, $Res Function(_$LoadingImpl) _then)
: super(_value, _then);
/// Create a copy of UserUpdateOutletState
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
class _$LoadingImpl implements _Loading {
const _$LoadingImpl();
@override
String toString() {
return 'UserUpdateOutletState.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,
required TResult Function(String message) error,
}) {
return loading();
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function()? success,
TResult? Function(String message)? error,
}) {
return loading?.call();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function()? success,
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(_Success value) success,
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(_Success value)? success,
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(_Success value)? success,
TResult Function(_Error value)? error,
required TResult orElse(),
}) {
if (loading != null) {
return loading(this);
}
return orElse();
}
}
abstract class _Loading implements UserUpdateOutletState {
const factory _Loading() = _$LoadingImpl;
}
/// @nodoc
abstract class _$$SuccessImplCopyWith<$Res> {
factory _$$SuccessImplCopyWith(
_$SuccessImpl value, $Res Function(_$SuccessImpl) then) =
__$$SuccessImplCopyWithImpl<$Res>;
}
/// @nodoc
class __$$SuccessImplCopyWithImpl<$Res>
extends _$UserUpdateOutletStateCopyWithImpl<$Res, _$SuccessImpl>
implements _$$SuccessImplCopyWith<$Res> {
__$$SuccessImplCopyWithImpl(
_$SuccessImpl _value, $Res Function(_$SuccessImpl) _then)
: super(_value, _then);
/// Create a copy of UserUpdateOutletState
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
class _$SuccessImpl implements _Success {
const _$SuccessImpl();
@override
String toString() {
return 'UserUpdateOutletState.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,
required TResult Function(String message) error,
}) {
return success();
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function()? success,
TResult? Function(String message)? error,
}) {
return success?.call();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function()? success,
TResult Function(String message)? error,
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,
required TResult Function(_Error value) error,
}) {
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,
TResult? Function(_Error value)? error,
}) {
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,
TResult Function(_Error value)? error,
required TResult orElse(),
}) {
if (success != null) {
return success(this);
}
return orElse();
}
}
abstract class _Success implements UserUpdateOutletState {
const factory _Success() = _$SuccessImpl;
}
/// @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 _$UserUpdateOutletStateCopyWithImpl<$Res, _$ErrorImpl>
implements _$$ErrorImplCopyWith<$Res> {
__$$ErrorImplCopyWithImpl(
_$ErrorImpl _value, $Res Function(_$ErrorImpl) _then)
: super(_value, _then);
/// Create a copy of UserUpdateOutletState
/// 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 'UserUpdateOutletState.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 UserUpdateOutletState
/// 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() success,
required TResult Function(String message) error,
}) {
return error(message);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function()? success,
TResult? Function(String message)? error,
}) {
return error?.call(message);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function()? success,
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(_Success value) success,
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(_Success value)? success,
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(_Success value)? success,
TResult Function(_Error value)? error,
required TResult orElse(),
}) {
if (error != null) {
return error(this);
}
return orElse();
}
}
abstract class _Error implements UserUpdateOutletState {
const factory _Error(final String message) = _$ErrorImpl;
String get message;
/// Create a copy of UserUpdateOutletState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
_$$ErrorImplCopyWith<_$ErrorImpl> get copyWith =>
throw _privateConstructorUsedError;
}
@@ -0,0 +1,6 @@
part of 'user_update_outlet_bloc.dart';
@freezed
class UserUpdateOutletEvent with _$UserUpdateOutletEvent {
const factory UserUpdateOutletEvent.update(String outlet) = _Update;
}
@@ -0,0 +1,9 @@
part of 'user_update_outlet_bloc.dart';
@freezed
class UserUpdateOutletState with _$UserUpdateOutletState {
const factory UserUpdateOutletState.initial() = _Initial;
const factory UserUpdateOutletState.loading() = _Loading;
const factory UserUpdateOutletState.success() = _Success;
const factory UserUpdateOutletState.error(String message) = _Error;
}
@@ -0,0 +1,410 @@
import 'package:enaklo_pos/core/components/flushbar.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/int_ext.dart';
import 'package:enaklo_pos/data/models/response/customer_response_model.dart';
import 'package:enaklo_pos/data/models/response/delivery_response_model.dart';
import 'package:enaklo_pos/presentation/home/bloc/order_form/order_form_bloc.dart';
import 'package:enaklo_pos/presentation/home/models/order_type.dart';
import 'package:enaklo_pos/presentation/home/models/product_quantity.dart';
import 'package:enaklo_pos/presentation/success/pages/success_save_order_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class ConfirmSaveOrderDialog extends StatelessWidget {
final String customerName;
final OrderType orderType;
final List<ProductQuantity> items;
final Customer? customer;
final DeliveryModel? delivery;
const ConfirmSaveOrderDialog({
super.key,
required this.customerName,
required this.orderType,
required this.items,
this.customer,
this.delivery,
});
@override
Widget build(BuildContext context) {
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
elevation: 8,
child: Container(
width: MediaQuery.of(context).size.width * 0.9,
constraints: BoxConstraints(
maxWidth: 600,
maxHeight: MediaQuery.of(context).size.height * 0.8,
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// Header
Container(
width: double.infinity,
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
AppColors.primary,
AppColors.primary.withOpacity(0.8)
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(16),
topRight: Radius.circular(16),
),
),
child: Column(
children: [
Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
shape: BoxShape.circle,
),
child: Icon(
Icons.warning_rounded,
color: Colors.white,
size: 28,
),
),
SizedBox(height: 12),
Text(
'Konfirmasi Pesanan',
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
Text(
'Tindakan ini tidak dapat dibatalkan',
style: TextStyle(
color: Colors.white.withOpacity(0.9),
fontSize: 12,
),
),
],
),
),
// Scrollable Content
Flexible(
child: SingleChildScrollView(
padding: EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Main message
Container(
width: double.infinity,
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.grey[50],
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.grey[200]!),
),
child: Text(
message,
style: TextStyle(
fontSize: 14,
height: 1.4,
color: Colors.grey[800],
),
),
),
if (items.isNotEmpty) ...[
SizedBox(height: 16),
// Items section
Container(
width: double.infinity,
decoration: BoxDecoration(
color: Colors.orange[50],
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.orange[200]!),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.all(16),
child: Row(
children: [
Container(
padding: EdgeInsets.all(6),
decoration: BoxDecoration(
color: Colors.orange[100],
borderRadius: BorderRadius.circular(6),
),
child: Icon(
Icons.list_alt_rounded,
color: Colors.orange[700],
size: 16,
),
),
SizedBox(width: 8),
Text(
'Item yang akan dipesan:',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
color: Colors.orange[800],
),
),
],
),
),
Container(
constraints: BoxConstraints(maxHeight: 120),
child: Scrollbar(
child: SingleChildScrollView(
padding: EdgeInsets.only(
left: 16, right: 16, bottom: 16),
child: Column(
children: items.map((item) {
return Container(
margin: EdgeInsets.only(bottom: 6),
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.white,
borderRadius:
BorderRadius.circular(6),
boxShadow: [
BoxShadow(
color: Colors.black
.withOpacity(0.03),
blurRadius: 2,
offset: Offset(0, 1),
),
],
),
child: Row(
children: [
Container(
width: 6,
height: 6,
decoration: BoxDecoration(
color: Colors.red[400],
shape: BoxShape.circle,
),
),
SizedBox(width: 8),
Expanded(
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
item.product.name ??
'Unknown Product',
style: TextStyle(
fontWeight:
FontWeight.w600,
fontSize: 12,
),
maxLines: 1,
overflow:
TextOverflow.ellipsis,
),
Text(
'${item.quantity} qty',
style: TextStyle(
fontSize: 10,
color: Colors.grey[600],
),
),
],
),
),
Container(
padding: EdgeInsets.symmetric(
horizontal: 6, vertical: 2),
decoration: BoxDecoration(
color: Colors.red[100],
borderRadius:
BorderRadius.circular(4),
),
child: Text(
((item.product.price ?? 0) *
item.quantity)
.currencyFormatRpV2,
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.bold,
color: Colors.red[700],
),
),
),
],
),
);
}).toList(),
),
),
),
),
],
),
),
],
SizedBox(height: 16),
],
),
),
),
// Action buttons
Container(
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.grey[50],
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(16),
bottomRight: Radius.circular(16),
),
),
child: Row(
children: [
Expanded(
child: SizedBox(
height: 44,
child: ElevatedButton(
onPressed: () => Navigator.pop(context),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey[300],
foregroundColor: Colors.grey[700],
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.close_rounded, size: 18),
SizedBox(width: 6),
Text(
'Batal',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
],
),
),
),
),
SizedBox(width: 12),
Expanded(
child: BlocListener<OrderFormBloc, OrderFormState>(
listener: (context, state) {
state.maybeWhen(
orElse: () {},
success: (data) {
context.pushReplacement(SuccessSaveOrderPage(
productQuantity: items,
orderId: data.id ?? "",
));
},
error: (message) =>
AppFlushbar.showError(context, message),
);
},
child: BlocBuilder<OrderFormBloc, OrderFormState>(
builder: (context, state) {
return state.maybeWhen(
orElse: () => SizedBox(
height: 44,
child: ElevatedButton(
onPressed: () {
context.read<OrderFormBloc>().add(
OrderFormEvent.create(
items: items,
customerName: customerName,
orderType: orderType,
table: null,
customer: customer,
),
);
},
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.primary,
foregroundColor: Colors.white,
elevation: 2,
shadowColor:
AppColors.primary.withOpacity(0.3),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.delete_forever_rounded,
size: 18),
SizedBox(width: 6),
Text(
'Konfirmasi',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
),
),
],
),
),
),
loading: () =>
Center(child: CircularProgressIndicator()),
);
},
),
),
),
],
),
),
],
),
),
);
}
String get message {
switch (orderType) {
case OrderType.dineIn:
return 'Konfirmasi untuk menyimpan pesanan Dine-In\n\n'
'Pesanan untuk ${customerName.isNotEmpty ? customerName : "Pelanggan"} akan disimpan ke dalam sistem. '
'Pelanggan dapat langsung menikmati pesanan di meja yang telah disediakan. '
'Pastikan semua item pesanan sudah sesuai sebelum menyimpan.';
case OrderType.delivery:
return 'Konfirmasi untuk menyimpan pesanan Delivery\n\n'
'Pesanan delivery untuk ${customerName.isNotEmpty ? customerName : "Pelanggan"} akan disimpan. '
'${delivery != null ? "Pengiriman: ${delivery!.name}. " : ""}'
'Tim delivery akan segera mempersiapkan pesanan.';
case OrderType.takeAway:
return 'Konfirmasi untuk menyimpan pesanan Take Away\n\n'
'Pesanan take away untuk ${customerName.isNotEmpty ? customerName : "Pelanggan"} akan disimpan. '
'Dapur akan mulai mempersiapkan pesanan dan pelanggan dapat mengambil pesanan sesuai estimasi waktu yang diberikan.';
case OrderType.freeTable:
return 'Konfirmasi untuk menyimpan pesanan Free Table\n\n'
'Pesanan free table untuk ${customerName.isNotEmpty ? customerName : "Pelanggan"} akan disimpan. '
'Meja akan direservasi dan pesanan akan dipersiapkan sesuai dengan waktu kedatangan pelanggan.';
}
}
}
@@ -0,0 +1,114 @@
import 'package:enaklo_pos/core/components/custom_modal_dialog.dart';
import 'package:enaklo_pos/core/components/spaces.dart';
import 'package:enaklo_pos/core/constants/colors.dart';
import 'package:enaklo_pos/data/datasources/delivery_local_datasource.dart';
import 'package:enaklo_pos/data/models/response/delivery_response_model.dart';
import 'package:enaklo_pos/presentation/home/bloc/checkout/checkout_bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class DeliveryDialog extends StatefulWidget {
const DeliveryDialog({super.key});
@override
State<DeliveryDialog> createState() => _DeliveryDialogState();
}
class _DeliveryDialogState extends State<DeliveryDialog> {
@override
Widget build(BuildContext context) {
return CustomModalDialog(
title: 'Pilih Pengiriman',
subtitle: 'Silahkan pilih pengiriman yang sesuai',
contentPadding:
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 24.0),
child: BlocBuilder<CheckoutBloc, CheckoutState>(
builder: (context, state) {
return state.maybeWhen(
orElse: () => const SizedBox.shrink(),
loaded: (items,
discountModel,
discount,
discountAmount,
tax,
serviceCharge,
totalQuantity,
totalPrice,
draftName,
orderType,
deliveryType) {
return Column(
children: List.generate(deliveries.length, (index) {
return _buildItem(
context,
deliveries[index],
selectedType: deliveryType,
);
}),
);
});
},
),
);
}
Widget _buildItem(BuildContext context, DeliveryModel delivery,
{DeliveryModel? selectedType}) {
return GestureDetector(
onTap: () {
context.read<CheckoutBloc>().add(
CheckoutEvent.updateDeliveryType(delivery),
);
Navigator.pop(context);
},
child: Container(
padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 16.0),
margin: const EdgeInsets.only(bottom: 8.0),
decoration: BoxDecoration(
color: selectedType?.id == delivery.id
? AppColors.primary
: AppColors.white,
borderRadius: BorderRadius.circular(8.0),
border: Border.all(
color: selectedType?.id == delivery.id
? AppColors.primary
: AppColors.grey,
width: 1.0,
),
),
child: Row(
children: [
Image.asset(
delivery.imageUrl,
width: 40.0,
height: 40.0,
fit: BoxFit.contain,
),
SpaceWidth(12.0),
Expanded(
child: Text(
delivery.name,
style: TextStyle(
fontSize: 16,
color: selectedType?.id == delivery.id
? AppColors.white
: AppColors.black,
fontWeight: FontWeight.bold,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
SpaceWidth(12.0),
Icon(
Icons.check_circle,
color: selectedType?.id == delivery.id
? AppColors.green
: Colors.transparent,
),
],
),
),
);
}
}
@@ -1,6 +1,10 @@
import 'package:enaklo_pos/core/components/buttons.dart';
import 'package:enaklo_pos/core/components/custom_modal_dialog.dart'; import 'package:enaklo_pos/core/components/custom_modal_dialog.dart';
import 'package:enaklo_pos/core/components/spaces.dart';
import 'package:enaklo_pos/core/extensions/build_context_ext.dart'; import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
import 'package:enaklo_pos/presentation/home/bloc/outlet_loader/outlet_loader_bloc.dart'; import 'package:enaklo_pos/presentation/home/bloc/outlet_loader/outlet_loader_bloc.dart';
import 'package:enaklo_pos/presentation/home/bloc/user_update_outlet/user_update_outlet_bloc.dart';
import 'package:enaklo_pos/presentation/home/models/outlet_model.dart';
import 'package:enaklo_pos/presentation/home/widgets/outlet_card.dart'; import 'package:enaklo_pos/presentation/home/widgets/outlet_card.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
@@ -13,6 +17,19 @@ class OutletDialog extends StatefulWidget {
} }
class _OutletDialogState extends State<OutletDialog> { class _OutletDialogState extends State<OutletDialog> {
Outlet? selectedOutlet;
void selectOutlet(Outlet outlet) {
setState(() {
if (selectedOutlet == outlet) {
selectedOutlet = null; // Deselect jika outlet yang sama diklik
} else {
selectedOutlet =
outlet; // Select outlet baru (akan mengganti selection sebelumnya)
}
});
}
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@@ -38,12 +55,51 @@ class _OutletDialogState extends State<OutletDialog> {
child: Text(message), child: Text(message),
), ),
loaded: (outlets) => Column( loaded: (outlets) => Column(
children: List.generate( children: [
outlets.length, ...List.generate(
(index) => OutletCard( outlets.length,
outlet: outlets[index], (index) => GestureDetector(
onTap: () {
selectOutlet(outlets[index]);
},
child: OutletCard(
outlet: outlets[index],
isSelected: selectedOutlet == outlets[index],
),
),
), ),
), SpaceHeight(24),
BlocListener<UserUpdateOutletBloc, UserUpdateOutletState>(
listener: (context, state) {
state.maybeWhen(
orElse: () {},
success: () {
context.pop();
},
);
},
child:
BlocBuilder<UserUpdateOutletBloc, UserUpdateOutletState>(
builder: (context, state) {
return state.maybeWhen(
orElse: () => Button.filled(
onPressed: selectedOutlet == null
? null
: () {
context.read<UserUpdateOutletBloc>().add(
UserUpdateOutletEvent.update(
selectedOutlet!.id ?? ""));
},
label: 'Terapkan',
),
loading: () => Center(
child: CircularProgressIndicator(),
),
);
},
),
),
],
), ),
); );
}, },
@@ -1,3 +1,6 @@
import 'dart:developer';
import 'package:dropdown_search/dropdown_search.dart';
import 'package:enaklo_pos/core/components/buttons.dart'; import 'package:enaklo_pos/core/components/buttons.dart';
import 'package:enaklo_pos/core/components/custom_modal_dialog.dart'; import 'package:enaklo_pos/core/components/custom_modal_dialog.dart';
import 'package:enaklo_pos/core/components/flushbar.dart'; import 'package:enaklo_pos/core/components/flushbar.dart';
@@ -26,9 +29,14 @@ class _PaymentAddOrderDialogState extends State<PaymentAddOrderDialog> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
context context.read<OrderLoaderBloc>().add(
.read<OrderLoaderBloc>() OrderLoaderEvent.getByStatus(
.add(OrderLoaderEvent.getByStatus('pending')); 'pending',
dateFrom: DateTime.now(),
dateTo: DateTime.now(),
limit: 20,
),
);
} }
@override @override
@@ -62,7 +70,13 @@ class _PaymentAddOrderDialogState extends State<PaymentAddOrderDialog> {
Center(child: const CircularProgressIndicator()), Center(child: const CircularProgressIndicator()),
loading: () => loading: () =>
Center(child: const CircularProgressIndicator()), Center(child: const CircularProgressIndicator()),
loaded: (orders, totalOrder) { loaded: (
orders,
totalOrder,
_,
__,
___,
) {
final availableOrders = orders; final availableOrders = orders;
if (selectOrder == null && availableOrders.isNotEmpty) { if (selectOrder == null && availableOrders.isNotEmpty) {
@@ -87,40 +101,183 @@ class _PaymentAddOrderDialogState extends State<PaymentAddOrderDialog> {
); );
} }
return Container( return DropdownSearch<Order>(
padding: const EdgeInsets.symmetric(horizontal: 16), items: orders,
decoration: BoxDecoration( selectedItem: selectOrder,
color: Colors.white,
borderRadius: BorderRadius.circular(16), // Dropdown properties
border: Border.all( dropdownDecoratorProps: DropDownDecoratorProps(
color: Theme.of(context).primaryColor, dropdownSearchDecoration: InputDecoration(
width: 2, hintText: "Pilih Meja",
hintStyle: TextStyle(
color: Colors.grey.shade600,
fontSize: 14,
),
prefixIcon: Icon(
Icons.category_outlined,
color: Colors.grey.shade500,
size: 20,
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(
color: Colors.grey.shade300,
width: 1.5,
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(
color: Colors.grey.shade300,
width: 1.5,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(
color: Colors.blue.shade400,
width: 2,
),
),
filled: true,
fillColor: Colors.white,
contentPadding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 16,
),
), ),
), ),
child: DropdownButtonHideUnderline(
child: DropdownButton<Order>( // Popup properties
isExpanded: true, popupProps: PopupProps.menu(
value: availableOrders.firstWhere( showSearchBox: true,
(t) => t.id == selectOrder?.id, searchFieldProps: TextFieldProps(
orElse: () => availableOrders.first, decoration: InputDecoration(
hintText: "Cari meja...",
prefixIcon: const Icon(Icons.search),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
contentPadding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 12,
),
), ),
onChanged: (Order? newValue) { ),
setState(() { menuProps: MenuProps(
selectOrder = newValue; backgroundColor: Colors.white,
}); elevation: 8,
}, shape: RoundedRectangleBorder(
items: availableOrders borderRadius: BorderRadius.circular(12),
.map<DropdownMenuItem<Order>>( ),
(Order value) => DropdownMenuItem<Order>( ),
value: value, itemBuilder: (context, order, isSelected) {
child: Text( return Container(
"${value.tableNumber ?? ""} - ${value.metadata?['customer_name'] ?? ""}", padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 12,
),
decoration: BoxDecoration(
color: isSelected
? Colors.blue.shade50
: Colors.transparent,
border: Border(
bottom: BorderSide(
color: Colors.grey.shade100,
width: 0.5,
),
),
),
child: Row(
children: [
Container(
width: 8,
height: 8,
decoration: BoxDecoration(
color: isSelected
? Colors.blue.shade600
: Colors.grey.shade400,
shape: BoxShape.circle,
), ),
), ),
) const SizedBox(width: 12),
.toList(), Expanded(
), child: Text(
"${order.tableNumber ?? ""} - ${order.metadata?['customer_name']}",
style: TextStyle(
fontSize: 14,
fontWeight: isSelected
? FontWeight.w600
: FontWeight.w500,
color: isSelected
? Colors.blue.shade700
: Colors.black87,
),
),
),
if (isSelected)
Icon(
Icons.check,
color: Colors.blue.shade600,
size: 18,
),
],
),
);
},
emptyBuilder: (context, searchEntry) {
return Container(
padding: const EdgeInsets.all(20),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.search_off,
color: Colors.grey.shade400,
size: 48,
),
const SizedBox(height: 12),
Text(
searchEntry.isEmpty
? "Tidak ada meja tersedia"
: "Tidak ditemukan meja dengan '${searchEntry}'",
style: TextStyle(
color: Colors.grey.shade600,
fontSize: 14,
),
textAlign: TextAlign.center,
),
],
),
);
},
), ),
// Item as string (for search functionality)
itemAsString: (Order order) => order.tableNumber ?? "",
// Comparison function
compareFn: (Order? item1, Order? item2) {
return item1?.id == item2?.id;
},
// On changed callback
onChanged: (Order? selectedOrder) {
if (selectedOrder != null) {
setState(() {
selectOrder = selectedOrder;
});
log("selectOrder: ${selectOrder!.tableNumber}");
}
},
// Validator (optional)
validator: (Order? value) {
if (value == null) {
return "Meja harus dipilih";
}
return null;
},
); );
}, },
); );
@@ -132,7 +289,8 @@ class _PaymentAddOrderDialogState extends State<PaymentAddOrderDialog> {
listener: (context, state) { listener: (context, state) {
state.maybeWhen( state.maybeWhen(
orElse: () {}, orElse: () {},
success: (data) { successMsg: () {
context.pop();
context.pushReplacement( context.pushReplacement(
SuccessSaveOrderPage( SuccessSaveOrderPage(
productQuantity: widget.items, productQuantity: widget.items,
@@ -13,8 +13,6 @@ import 'package:intl/intl.dart';
import 'package:enaklo_pos/core/extensions/build_context_ext.dart'; import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
import 'package:enaklo_pos/presentation/home/bloc/qris/qris_bloc.dart'; import 'package:enaklo_pos/presentation/home/bloc/qris/qris_bloc.dart';
import 'package:enaklo_pos/presentation/home/widgets/success_payment_dialog.dart'; import 'package:enaklo_pos/presentation/home/widgets/success_payment_dialog.dart';
import 'package:enaklo_pos/presentation/setting/bloc/sync_order/sync_order_bloc.dart';
import 'package:print_bluetooth_thermal/print_bluetooth_thermal.dart';
import 'package:widgets_to_image/widgets_to_image.dart'; import 'package:widgets_to_image/widgets_to_image.dart';
import 'package:enaklo_pos/core/utils/printer_service.dart'; import 'package:enaklo_pos/core/utils/printer_service.dart';
@@ -230,24 +228,25 @@ class _PaymentQrisDialogState extends State<PaymentQrisDialog> {
ElevatedButton( ElevatedButton(
onPressed: () async { onPressed: () async {
try { try {
final sizeReceipt = await AuthLocalDataSource().getSizeReceipt(); final sizeReceipt =
await AuthLocalDataSource().getSizeReceipt();
final bytes = await controller.capture(); final bytes = await controller.capture();
final printValue = await PrintDataoutputs.instance final printValue = await PrintDataoutputs.instance
.printQRIS(widget.price, bytes!, int.parse(sizeReceipt)); .printQRIS(
widget.price, bytes!, int.parse(sizeReceipt));
// Get the receipt printer to print QRIS // Get the receipt printer to print QRIS
final receiptPrinter = await ProductLocalDatasource.instance final receiptPrinter = await ProductLocalDatasource
.instance
.getPrinterByCode('receipt'); .getPrinterByCode('receipt');
if (receiptPrinter != null) { if (receiptPrinter != null) {
await PrinterService().printWithPrinter( await PrinterService().printWithPrinter(
receiptPrinter, receiptPrinter, printValue, context);
printValue,
context
);
} else { } else {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('No receipt printer configured')), SnackBar(
content: Text('No receipt printer configured')),
); );
} }
} catch (e) { } catch (e) {
@@ -1,3 +1,6 @@
import 'dart:developer';
import 'package:dropdown_search/dropdown_search.dart';
import 'package:enaklo_pos/core/components/buttons.dart'; import 'package:enaklo_pos/core/components/buttons.dart';
import 'package:enaklo_pos/core/components/custom_modal_dialog.dart'; import 'package:enaklo_pos/core/components/custom_modal_dialog.dart';
import 'package:enaklo_pos/core/components/flushbar.dart'; import 'package:enaklo_pos/core/components/flushbar.dart';
@@ -103,39 +106,183 @@ class _PaymentSaveDialogState extends State<PaymentSaveDialog> {
); );
} }
return Container( return DropdownSearch<TableModel>(
padding: const EdgeInsets.symmetric(horizontal: 16), items: tables,
decoration: BoxDecoration( selectedItem: selectTable,
color: Colors.white,
borderRadius: BorderRadius.circular(16), // Dropdown properties
border: Border.all( dropdownDecoratorProps: DropDownDecoratorProps(
color: Theme.of(context).primaryColor, dropdownSearchDecoration: InputDecoration(
width: 2, hintText: "Pilih meja",
), hintStyle: TextStyle(
), color: Colors.grey.shade600,
child: DropdownButtonHideUnderline( fontSize: 14,
child: DropdownButton<TableModel>( ),
isExpanded: true, prefixIcon: Icon(
value: availableTables.firstWhere( Icons.category_outlined,
(t) => t.id == selectTable?.id, color: Colors.grey.shade500,
orElse: () => availableTables.first, size: 20,
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(
color: Colors.grey.shade300,
width: 1.5,
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(
color: Colors.grey.shade300,
width: 1.5,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(
color: Colors.blue.shade400,
width: 2,
),
),
filled: true,
fillColor: Colors.white,
contentPadding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 16,
), ),
onChanged: (TableModel? newValue) {
setState(() {
selectTable = newValue;
});
},
items: availableTables
.map<DropdownMenuItem<TableModel>>(
(TableModel value) =>
DropdownMenuItem<TableModel>(
value: value,
child: Text(value.tableName ?? ""),
),
)
.toList(),
), ),
), ),
// Popup properties
popupProps: PopupProps.menu(
showSearchBox: true,
searchFieldProps: TextFieldProps(
decoration: InputDecoration(
hintText: "Cari meja...",
prefixIcon: const Icon(Icons.search),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
contentPadding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 12,
),
),
),
menuProps: MenuProps(
backgroundColor: Colors.white,
elevation: 8,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
itemBuilder: (context, category, isSelected) {
return Container(
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 12,
),
decoration: BoxDecoration(
color: isSelected
? Colors.blue.shade50
: Colors.transparent,
border: Border(
bottom: BorderSide(
color: Colors.grey.shade100,
width: 0.5,
),
),
),
child: Row(
children: [
Container(
width: 8,
height: 8,
decoration: BoxDecoration(
color: isSelected
? Colors.blue.shade600
: Colors.grey.shade400,
shape: BoxShape.circle,
),
),
const SizedBox(width: 12),
Expanded(
child: Text(
category.tableName ?? "",
style: TextStyle(
fontSize: 14,
fontWeight: isSelected
? FontWeight.w600
: FontWeight.w500,
color: isSelected
? Colors.blue.shade700
: Colors.black87,
),
),
),
if (isSelected)
Icon(
Icons.check,
color: Colors.blue.shade600,
size: 18,
),
],
),
);
},
emptyBuilder: (context, searchEntry) {
return Container(
padding: const EdgeInsets.all(20),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.search_off,
color: Colors.grey.shade400,
size: 48,
),
const SizedBox(height: 12),
Text(
searchEntry.isEmpty
? "Tidak ada meja tersedia"
: "Tidak ditemukan meja dengan '${searchEntry}'",
style: TextStyle(
color: Colors.grey.shade600,
fontSize: 14,
),
textAlign: TextAlign.center,
),
],
),
);
},
),
// Item as string (for search functionality)
itemAsString: (TableModel table) => table.tableName ?? "",
// Comparison function
compareFn: (TableModel? item1, TableModel? item2) {
return item1?.id == item2?.id;
},
// On changed callback
onChanged: (TableModel? selectedTable) {
if (selectedTable != null) {
setState(() {
selectTable = selectedTable;
});
log("selectTable: ${selectTable!.tableName}");
}
},
// Validator (optional)
validator: (TableModel? value) {
if (value == null) {
return "Meja harus dipilih";
}
return null;
},
); );
}, },
); );
@@ -148,6 +295,7 @@ class _PaymentSaveDialogState extends State<PaymentSaveDialog> {
state.maybeWhen( state.maybeWhen(
orElse: () {}, orElse: () {},
success: (data) { success: (data) {
context.pop();
context.pushReplacement(SuccessSaveOrderPage( context.pushReplacement(SuccessSaveOrderPage(
productQuantity: widget.items, productQuantity: widget.items,
orderId: data.id ?? "", orderId: data.id ?? "",
+27 -10
View File
@@ -3,7 +3,9 @@ import 'package:enaklo_pos/core/components/spaces.dart';
import 'package:enaklo_pos/core/constants/colors.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/build_context_ext.dart';
import 'package:enaklo_pos/data/models/response/customer_response_model.dart'; import 'package:enaklo_pos/data/models/response/customer_response_model.dart';
import 'package:enaklo_pos/data/models/response/delivery_response_model.dart';
import 'package:enaklo_pos/data/models/response/table_model.dart'; import 'package:enaklo_pos/data/models/response/table_model.dart';
import 'package:enaklo_pos/presentation/home/dialog/confirm_save_order_dialog.dart';
import 'package:enaklo_pos/presentation/home/dialog/payment_add_order_dialog.dart'; import 'package:enaklo_pos/presentation/home/dialog/payment_add_order_dialog.dart';
import 'package:enaklo_pos/presentation/home/dialog/payment_save_dialog.dart'; import 'package:enaklo_pos/presentation/home/dialog/payment_save_dialog.dart';
import 'package:enaklo_pos/presentation/home/models/order_type.dart'; import 'package:enaklo_pos/presentation/home/models/order_type.dart';
@@ -16,6 +18,7 @@ class SaveDialog extends StatefulWidget {
final OrderType orderType; final OrderType orderType;
final List<ProductQuantity> items; final List<ProductQuantity> items;
final Customer? customer; final Customer? customer;
final DeliveryModel? deliveryModel;
const SaveDialog({ const SaveDialog({
super.key, super.key,
@@ -24,6 +27,7 @@ class SaveDialog extends StatefulWidget {
required this.orderType, required this.orderType,
required this.items, required this.items,
required this.customer, required this.customer,
this.deliveryModel,
}); });
@override @override
@@ -46,16 +50,29 @@ class _SaveDialogState extends State<SaveDialog> {
subtitle: 'Simpan pesanan dan bayar nanti', subtitle: 'Simpan pesanan dan bayar nanti',
onTap: () { onTap: () {
context.pop(); context.pop();
showDialog( if (OrderType.dineIn == widget.orderType) {
context: context, showDialog(
builder: (context) => PaymentSaveDialog( context: context,
selectedTable: widget.selectedTable, builder: (context) => PaymentSaveDialog(
customerName: widget.customerName, selectedTable: widget.selectedTable,
orderType: widget.orderType, customerName: widget.customerName,
items: widget.items, orderType: widget.orderType,
customer: widget.customer, items: widget.items,
), customer: widget.customer,
); ),
);
} else {
showDialog(
context: context,
builder: (context) => ConfirmSaveOrderDialog(
delivery: widget.deliveryModel,
customerName: widget.customerName,
orderType: widget.orderType,
items: widget.items,
customer: widget.customer,
),
);
}
}), }),
SpaceHeight(16.0), SpaceHeight(16.0),
_item( _item(

Some files were not shown because too many files have changed in this diff Show More