first commit

This commit is contained in:
Aditya Siregar
2025-07-30 22:38:44 +07:00
commit 73320561b0
444 changed files with 64633 additions and 0 deletions
@@ -0,0 +1,51 @@
import 'package:flutter/material.dart';
import '../../../core/components/spaces.dart';
import '../../../core/constants/colors.dart';
class AddData extends StatelessWidget {
final String title;
final VoidCallback onPressed;
const AddData({
super.key,
required this.title,
required this.onPressed,
});
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onPressed,
child: Container(
padding: const EdgeInsets.all(16.0),
decoration: ShapeDecoration(
shape: RoundedRectangleBorder(
side: const BorderSide(width: 1, color: AppColors.card),
borderRadius: BorderRadius.circular(19),
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
Icons.add,
color: AppColors.primary,
),
const SpaceHeight(8.0),
Text(
title,
style: const TextStyle(
color: AppColors.primary,
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
],
),
),
);
}
}
@@ -0,0 +1,308 @@
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:enaklo_pos/core/components/components.dart';
import 'package:enaklo_pos/core/constants/colors.dart';
import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
import 'package:enaklo_pos/core/utils/printer_service.dart';
import 'package:enaklo_pos/data/dataoutputs/print_dataoutputs.dart';
import 'package:enaklo_pos/data/models/response/print_model.dart';
import 'package:enaklo_pos/presentation/setting/bloc/create_printer/create_printer_bloc.dart';
import 'package:enaklo_pos/presentation/setting/bloc/get_printer_bar/get_printer_bar_bloc.dart';
import 'package:enaklo_pos/presentation/setting/bloc/update_printer/update_printer_bloc.dart';
import 'package:enaklo_pos/presentation/setting/widgets/dialog_search_printer.dart';
class BarPrinterPage extends StatefulWidget {
const BarPrinterPage({super.key});
@override
State<BarPrinterPage> createState() => _BarPrinterPageState();
}
class _BarPrinterPageState extends State<BarPrinterPage> {
String selectedPrinter = 'Bluetooth';
TextEditingController? addressController;
TextEditingController? printNameController;
String paper = '58';
bool isInitialized = false;
@override
void initState() {
// TODO: implement initState
super.initState();
addressController = TextEditingController();
printNameController = TextEditingController();
context.read<GetPrinterBarBloc>().add(GetPrinterBarEvent.get());
}
@override
void dispose() {
addressController!.dispose();
printNameController!.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: BlocBuilder<GetPrinterBarBloc, GetPrinterBarState>(
builder: (context, state) {
return state.maybeWhen(
orElse: () {
return SizedBox.shrink();
},
loading: () {
return Center(child: CircularProgressIndicator());
},
success: (data) {
if (data != null && !isInitialized) {
addressController!.text = data.address;
printNameController!.text = data.name;
selectedPrinter = data.type;
paper = data.paper;
isInitialized = true;
}
return Container(
// width: 300,
width: context.deviceWidth,
padding: const EdgeInsets.all(16.0),
color: Colors.white,
child: ListView(
children: [
const Text(
'Bar Printer',
style: TextStyle(
color: Colors.black,
fontSize: 20,
fontWeight: FontWeight.w600,
),
),
SpaceHeight(16),
DropdownButtonFormField<String>(
decoration: InputDecoration(
labelText: 'Printer Type',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
),
),
value: selectedPrinter, // nilai default
items: const [
DropdownMenuItem(
value: 'Bluetooth',
child: Text('Bluetooth'),
),
DropdownMenuItem(
value: 'Network',
child: Text('Network'),
),
],
onChanged: (value) {
selectedPrinter = value ?? 'Bluetooth';
setState(() {});
},
),
SpaceHeight(8),
//button search
selectedPrinter == 'Bluetooth'
? Button.outlined(
onPressed: () {
showDialog(
context: context,
builder: (context) => DialogSearchPrinter(
onSelected: (value) {
addressController!.text = value;
setState(() {});
},
),
);
},
label: 'Search')
: CustomTextField(
controller: addressController!,
label: 'Address',
showLabel: false,
),
SpaceHeight(16),
// Textfield for name
CustomTextField(
controller: printNameController!,
label: 'Print Name',
showLabel: false,
),
SpaceHeight(16),
// Textfield for width paper
DropdownButtonFormField<String>(
decoration: InputDecoration(
labelText: 'Width Paper',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
),
),
value: paper, // nilai default
items: const [
DropdownMenuItem(
value: '58',
child: Text('58 mm'),
),
DropdownMenuItem(
value: '80',
child: Text('80 mm'),
),
],
onChanged: (value) {
paper = value ?? '58';
log('Paper : $paper');
},
),
SpaceHeight(16),
// button test print
Button.outlined(
onPressed: () async {
if (addressController!.text.isNotEmpty && printNameController!.text.isNotEmpty) {
try {
// Create a test print model
final testPrinter = PrintModel(
code: 'bar',
name: printNameController!.text,
address: addressController!.text,
paper: paper,
type: selectedPrinter,
);
// Generate test print data
final testPrintData = await PrintDataoutputs.instance.printBar(
[], // Empty product list for test
'Test Table',
'Test Order',
'Test Cashier',
int.parse(paper),
'DINE IN',
);
// Print test
await PrinterService().printWithPrinter(
testPrinter,
testPrintData,
context,
);
} catch (e) {
log("Error test printing: $e");
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error test printing: $e')),
);
}
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Please fill in printer details first')),
);
}
},
label: 'Test Print'
),
SpaceHeight(8),
// button save
data == null
? BlocConsumer<CreatePrinterBloc, CreatePrinterState>(
listener: (context, state) {
state.maybeWhen(
orElse: () {},
success: (message) {
final snackBar = SnackBar(
content: Text(message),
backgroundColor: AppColors.primary,
);
ScaffoldMessenger.of(context)
.showSnackBar(snackBar);
context
.read<GetPrinterBarBloc>()
.add(GetPrinterBarEvent.get());
},
);
},
builder: (context, state) {
return state.maybeWhen(
orElse: () {
return Button.filled(
onPressed: () {
final printData = PrintModel(
code: 'bar',
name: printNameController!.text,
address: addressController!.text,
paper: paper,
type: selectedPrinter,
);
context.read<CreatePrinterBloc>().add(
CreatePrinterEvent.createPrinter(
printData),
);
},
label: 'Save',
);
},
loading: () {
return CircularProgressIndicator();
},
);
},
)
: BlocConsumer<UpdatePrinterBloc, UpdatePrinterState>(
listener: (context, state) {
state.maybeWhen(
orElse: () {},
success: (message) {
final snackBar = SnackBar(
content: Text(message),
backgroundColor: AppColors.primary,
);
ScaffoldMessenger.of(context)
.showSnackBar(snackBar);
context
.read<GetPrinterBarBloc>()
.add(GetPrinterBarEvent.get());
},
);
},
builder: (context, state) {
return state.maybeWhen(
orElse: () {
return Button.filled(
onPressed: () {
final printData = PrintModel(
id: data.id,
code: 'bar',
name: printNameController!.text,
address: addressController!.text,
paper: paper,
type: selectedPrinter,
);
log("Print Data : ${printData.toMap()}");
context.read<UpdatePrinterBloc>().add(
UpdatePrinterEvent.updatePrinter(
printData,
),
);
},
label: 'Save',
);
},
loading: () {
return CircularProgressIndicator();
},
);
},
),
SpaceHeight(16),
],
),
);
},
);
},
));
}
}
@@ -0,0 +1,310 @@
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:enaklo_pos/core/components/components.dart';
import 'package:enaklo_pos/core/constants/colors.dart';
import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
import 'package:enaklo_pos/core/utils/printer_service.dart';
import 'package:enaklo_pos/data/dataoutputs/print_dataoutputs.dart';
import 'package:enaklo_pos/data/models/response/print_model.dart';
import 'package:enaklo_pos/presentation/setting/bloc/create_printer/create_printer_bloc.dart';
import 'package:enaklo_pos/presentation/setting/bloc/get_printer_checker/get_printer_checker_bloc.dart';
import 'package:enaklo_pos/presentation/setting/bloc/update_printer/update_printer_bloc.dart';
import 'package:enaklo_pos/presentation/setting/widgets/dialog_search_printer.dart';
class CheckerPrinterPage extends StatefulWidget {
const CheckerPrinterPage({super.key});
@override
State<CheckerPrinterPage> createState() => _CheckerPrinterPageState();
}
class _CheckerPrinterPageState extends State<CheckerPrinterPage> {
String selectedPrinter = 'Bluetooth';
TextEditingController? addressController;
TextEditingController? printNameController;
String paper = '58';
bool isInitialized = false;
@override
void initState() {
// TODO: implement initState
super.initState();
addressController = TextEditingController();
printNameController = TextEditingController();
context.read<GetPrinterCheckerBloc>().add(GetPrinterCheckerEvent.get());
}
@override
void dispose() {
addressController!.dispose();
printNameController!.dispose();
isInitialized = false;
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: BlocBuilder<GetPrinterCheckerBloc, GetPrinterCheckerState>(
builder: (context, state) {
return state.maybeWhen(
orElse: () {
return SizedBox.shrink();
},
loading: () {
return Center(child: CircularProgressIndicator());
},
success: (data) {
log("Checker Printer: ${data?.toMap()}");
if (data != null && !isInitialized) {
addressController!.text = data.address;
printNameController!.text = data.name;
selectedPrinter = data.type;
paper = data.paper;
isInitialized = true;
}
return Container(
// width: 300,
width: context.deviceWidth,
padding: const EdgeInsets.all(16.0),
color: Colors.white,
child: ListView(
children: [
const Text(
'Checker Printer',
style: TextStyle(
color: Colors.black,
fontSize: 20,
fontWeight: FontWeight.w600,
),
),
SpaceHeight(16),
DropdownButtonFormField<String>(
decoration: InputDecoration(
labelText: 'Printer Type',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
),
),
value: selectedPrinter, // nilai default
items: const [
DropdownMenuItem(
value: 'Bluetooth',
child: Text('Bluetooth'),
),
DropdownMenuItem(
value: 'Network',
child: Text('Network'),
),
],
onChanged: (value) {
selectedPrinter = value ?? 'Bluetooth';
setState(() {});
},
),
SpaceHeight(8),
//button search
selectedPrinter == 'Bluetooth'
? Button.outlined(
onPressed: () {
showDialog(
context: context,
builder: (context) => DialogSearchPrinter(
onSelected: (value) {
addressController!.text = value;
setState(() {});
},
),
);
},
label: 'Search')
: CustomTextField(
controller: addressController!,
label: 'Address',
showLabel: false,
),
SpaceHeight(16),
// Textfield for name
CustomTextField(
controller: printNameController!,
label: 'Print Name',
showLabel: false,
),
SpaceHeight(16),
// Textfield for width paper
DropdownButtonFormField<String>(
decoration: InputDecoration(
labelText: 'Width Paper',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
),
),
value: paper, // nilai default
items: const [
DropdownMenuItem(
value: '58',
child: Text('58 mm'),
),
DropdownMenuItem(
value: '80',
child: Text('80 mm'),
),
],
onChanged: (value) {
paper = value ?? '58';
log('Paper : $paper');
},
),
SpaceHeight(16),
// button test print
Button.outlined(
onPressed: () async {
if (addressController!.text.isNotEmpty && printNameController!.text.isNotEmpty) {
try {
// Create a test print model
final testPrinter = PrintModel(
code: 'checker',
name: printNameController!.text,
address: addressController!.text,
paper: paper,
type: selectedPrinter,
);
// Generate test print data
final testPrintData = await PrintDataoutputs.instance.printChecker(
[], // Empty product list for test
'Test Table',
'Test Order',
'Test Cashier',
int.parse(paper),
'DINE IN',
);
// Print test
await PrinterService().printWithPrinter(
testPrinter,
testPrintData,
context,
);
} catch (e) {
log("Error test printing: $e");
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error test printing: $e')),
);
}
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Please fill in printer details first')),
);
}
},
label: 'Test Print'
),
SpaceHeight(8),
// button save
data == null
? BlocConsumer<CreatePrinterBloc, CreatePrinterState>(
listener: (context, state) {
state.maybeWhen(
orElse: () {},
success: (message) {
final snackBar = SnackBar(
content: Text(message),
backgroundColor: AppColors.primary,
);
ScaffoldMessenger.of(context)
.showSnackBar(snackBar);
context
.read<GetPrinterCheckerBloc>()
.add(GetPrinterCheckerEvent.get());
},
);
},
builder: (context, state) {
return state.maybeWhen(
orElse: () {
return Button.filled(
onPressed: () {
final printData = PrintModel(
code: 'checker',
name: printNameController!.text,
address: addressController!.text,
paper: paper,
type: selectedPrinter,
);
context.read<CreatePrinterBloc>().add(
CreatePrinterEvent.createPrinter(
printData),
);
},
label: 'Save',
);
},
loading: () {
return CircularProgressIndicator();
},
);
},
)
: BlocConsumer<UpdatePrinterBloc, UpdatePrinterState>(
listener: (context, state) {
state.maybeWhen(
orElse: () {},
success: (message) {
final snackBar = SnackBar(
content: Text(message),
backgroundColor: AppColors.primary,
);
ScaffoldMessenger.of(context)
.showSnackBar(snackBar);
context
.read<GetPrinterCheckerBloc>()
.add(GetPrinterCheckerEvent.get());
},
);
},
builder: (context, state) {
return state.maybeWhen(
orElse: () {
return Button.filled(
onPressed: () {
final printData = PrintModel(
id: data.id,
code: 'checker',
name: printNameController!.text,
address: addressController!.text,
paper: paper,
type: selectedPrinter,
);
log("Print Data : ${printData.toMap()}");
context.read<UpdatePrinterBloc>().add(
UpdatePrinterEvent.updatePrinter(
printData,
),
);
},
label: 'Save',
);
},
loading: () {
return CircularProgressIndicator();
},
);
},
),
SpaceHeight(16),
],
),
);
},
);
},
));
}
}
@@ -0,0 +1,108 @@
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:print_bluetooth_thermal/print_bluetooth_thermal.dart';
import 'package:enaklo_pos/core/utils/printer_service.dart';
class DialogSearchPrinter extends StatefulWidget {
final Function(String) onSelected;
const DialogSearchPrinter({
super.key,
required this.onSelected,
});
@override
State<DialogSearchPrinter> createState() => _DialogSearchPrinterState();
}
class _DialogSearchPrinterState extends State<DialogSearchPrinter> {
String macName = '';
String? macConnected;
bool connected = false;
List<BluetoothInfo> items = [];
Future<void> getBluetoots() async {
setState(() {
items = [];
});
if (!mounted) return;
final bool result = await PrinterService().isBluetoothEnabled();
if (result) {
final List<BluetoothInfo> listResult = await PrinterService().getPairedBluetoothDevices();
log("listResult: $listResult");
setState(() {
items = listResult;
});
}
}
Future<void> connect(String mac) async {
final bool result = await PrinterService().connectBluetoothPrinter(mac);
if (result) {
widget.onSelected(mac);
}
}
@override
void initState() {
loadPermession();
getBluetoots();
super.initState();
}
Future<void> loadPermession() async {
var status2 = await Permission.bluetoothScan.status;
log("Permession: $status2");
if (status2.isDenied) {
await Permission.bluetoothScan.request();
}
var status = await Permission.bluetoothConnect.status;
if (status.isDenied) {
await Permission.bluetoothConnect.request();
}
}
@override
Widget build(BuildContext context) {
return AlertDialog(
title: const Text('Search Printer'),
content: SizedBox(
width: context.deviceWidth / 2,
child: items.isEmpty
? const Padding(
padding: EdgeInsets.all(20),
child: Center(child: CircularProgressIndicator()),
)
: SizedBox(
height: 200,
child: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(items[index].name),
subtitle: Text(items[index].macAdress),
onTap: () {
connect(items[index].macAdress);
widget.onSelected(items[index].macAdress);
Navigator.of(context).pop();
},
);
},
),
),
),
actions: <Widget>[
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('Cancel'),
),
],
);
}
}
@@ -0,0 +1,308 @@
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:enaklo_pos/core/components/components.dart';
import 'package:enaklo_pos/core/constants/colors.dart';
import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
import 'package:enaklo_pos/core/utils/printer_service.dart';
import 'package:enaklo_pos/data/dataoutputs/print_dataoutputs.dart';
import 'package:enaklo_pos/data/models/response/print_model.dart';
import 'package:enaklo_pos/presentation/setting/bloc/create_printer/create_printer_bloc.dart';
import 'package:enaklo_pos/presentation/setting/bloc/get_printer_kitchen/get_printer_kitchen_bloc.dart';
import 'package:enaklo_pos/presentation/setting/bloc/update_printer/update_printer_bloc.dart';
import 'package:enaklo_pos/presentation/setting/widgets/dialog_search_printer.dart';
class KitchenPrinterPage extends StatefulWidget {
const KitchenPrinterPage({super.key});
@override
State<KitchenPrinterPage> createState() => _KitchenPrinterPageState();
}
class _KitchenPrinterPageState extends State<KitchenPrinterPage> {
String selectedPrinter = 'Bluetooth';
TextEditingController? addressController;
TextEditingController? printNameController;
String paper = '58';
bool isInitialized = false;
@override
void initState() {
// TODO: implement initState
super.initState();
addressController = TextEditingController();
printNameController = TextEditingController();
context.read<GetPrinterKitchenBloc>().add(GetPrinterKitchenEvent.get());
}
@override
void dispose() {
addressController!.dispose();
printNameController!.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: BlocBuilder<GetPrinterKitchenBloc, GetPrinterKitchenState>(
builder: (context, state) {
return state.maybeWhen(
orElse: () {
return SizedBox.shrink();
},
loading: () {
return Center(child: CircularProgressIndicator());
},
success: (data) {
if (data != null && !isInitialized) {
addressController!.text = data.address;
printNameController!.text = data.name;
selectedPrinter = data.type;
paper = data.paper;
isInitialized = true;
}
return Container(
// width: 300,
width: context.deviceWidth,
padding: const EdgeInsets.all(16.0),
color: Colors.white,
child: ListView(
children: [
const Text(
'Kitchen Printer',
style: TextStyle(
color: Colors.black,
fontSize: 20,
fontWeight: FontWeight.w600,
),
),
SpaceHeight(16),
DropdownButtonFormField<String>(
decoration: InputDecoration(
labelText: 'Printer Type',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
),
),
value: selectedPrinter, // nilai default
items: const [
DropdownMenuItem(
value: 'Bluetooth',
child: Text('Bluetooth'),
),
DropdownMenuItem(
value: 'Network',
child: Text('Network'),
),
],
onChanged: (value) {
selectedPrinter = value ?? 'Bluetooth';
setState(() {});
},
),
SpaceHeight(8),
//button search
selectedPrinter == 'Bluetooth'
? Button.outlined(
onPressed: () {
showDialog(
context: context,
builder: (context) => DialogSearchPrinter(
onSelected: (value) {
addressController!.text = value;
setState(() {});
},
),
);
},
label: 'Search')
: CustomTextField(
controller: addressController!,
label: 'Address',
showLabel: false,
),
SpaceHeight(16),
// Textfield for name
CustomTextField(
controller: printNameController!,
label: 'Print Name',
showLabel: false,
),
SpaceHeight(16),
// Textfield for width paper
DropdownButtonFormField<String>(
decoration: InputDecoration(
labelText: 'Width Paper',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
),
),
value: paper, // nilai default
items: const [
DropdownMenuItem(
value: '58',
child: Text('58 mm'),
),
DropdownMenuItem(
value: '80',
child: Text('80 mm'),
),
],
onChanged: (value) {
paper = value ?? '58';
log('Paper : $paper');
},
),
SpaceHeight(16),
// button test print
Button.outlined(
onPressed: () async {
if (addressController!.text.isNotEmpty && printNameController!.text.isNotEmpty) {
try {
// Create a test print model
final testPrinter = PrintModel(
code: 'kitchen',
name: printNameController!.text,
address: addressController!.text,
paper: paper,
type: selectedPrinter,
);
// Generate test print data
final testPrintData = await PrintDataoutputs.instance.printKitchen(
[], // Empty product list for test
'Test Table',
'Test Order',
'Test Cashier',
int.parse(paper),
'DINE IN',
);
// Print test
await PrinterService().printWithPrinter(
testPrinter,
testPrintData,
context,
);
} catch (e) {
log("Error test printing: $e");
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error test printing: $e')),
);
}
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Please fill in printer details first')),
);
}
},
label: 'Test Print'
),
SpaceHeight(8),
// button save
data == null
? BlocConsumer<CreatePrinterBloc, CreatePrinterState>(
listener: (context, state) {
state.maybeWhen(
orElse: () {},
success: (message) {
final snackBar = SnackBar(
content: Text(message),
backgroundColor: AppColors.primary,
);
ScaffoldMessenger.of(context)
.showSnackBar(snackBar);
context
.read<GetPrinterKitchenBloc>()
.add(GetPrinterKitchenEvent.get());
},
);
},
builder: (context, state) {
return state.maybeWhen(
orElse: () {
return Button.filled(
onPressed: () {
final printData = PrintModel(
code: 'kitchen',
name: printNameController!.text,
address: addressController!.text,
paper: paper,
type: selectedPrinter,
);
context.read<CreatePrinterBloc>().add(
CreatePrinterEvent.createPrinter(
printData),
);
},
label: 'Save',
);
},
loading: () {
return CircularProgressIndicator();
},
);
},
)
: BlocConsumer<UpdatePrinterBloc, UpdatePrinterState>(
listener: (context, state) {
state.maybeWhen(
orElse: () {},
success: (message) {
final snackBar = SnackBar(
content: Text(message),
backgroundColor: AppColors.primary,
);
ScaffoldMessenger.of(context)
.showSnackBar(snackBar);
context
.read<GetPrinterKitchenBloc>()
.add(GetPrinterKitchenEvent.get());
},
);
},
builder: (context, state) {
return state.maybeWhen(
orElse: () {
return Button.filled(
onPressed: () {
final printData = PrintModel(
id: data.id,
code: 'kitchen',
name: printNameController!.text,
address: addressController!.text,
paper: paper,
type: selectedPrinter,
);
log("Print Data : ${printData.toMap()}");
context.read<UpdatePrinterBloc>().add(
UpdatePrinterEvent.updatePrinter(
printData,
),
);
},
label: 'Save',
);
},
loading: () {
return CircularProgressIndicator();
},
);
},
),
SpaceHeight(16),
],
),
);
},
);
},
));
}
}
@@ -0,0 +1,92 @@
import 'package:flutter/material.dart';
import 'package:enaklo_pos/data/models/response/discount_response_model.dart';
import '../../../core/assets/assets.gen.dart';
import '../../../core/constants/colors.dart';
class ManageDiscountCard extends StatelessWidget {
final Discount data;
final VoidCallback onEditTap;
const ManageDiscountCard({
super.key,
required this.data,
required this.onEditTap,
});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(16.0),
decoration: ShapeDecoration(
shape: RoundedRectangleBorder(
side: const BorderSide(width: 1, color: AppColors.card),
borderRadius: BorderRadius.circular(19),
),
),
child: Stack(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Spacer(),
Container(
alignment: Alignment.center,
padding: const EdgeInsets.all(20.0),
margin: const EdgeInsets.only(top: 30.0),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: AppColors.disabled.withOpacity(0.4),
),
child: Text(
'${data.value!.replaceAll('.00', '')}%',
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.w900,
),
),
),
const Spacer(),
Center(
child: RichText(
text: TextSpan(
text: 'Nama Promo : ',
children: [
TextSpan(
text: data.name,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
],
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w400,
color: AppColors.black,
),
),
),
),
const Spacer(),
],
),
Align(
alignment: Alignment.topRight,
child: GestureDetector(
onTap: onEditTap,
child: Container(
padding: const EdgeInsets.all(8.0),
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(50.0)),
color: AppColors.primary,
),
child: Assets.icons.edit.svg(),
),
),
),
],
),
);
}
}
@@ -0,0 +1,92 @@
import 'package:flutter/material.dart';
import '../../../core/assets/assets.gen.dart';
import '../../../core/constants/colors.dart';
import '../models/tax_model.dart';
class ManageTaxCard extends StatelessWidget {
final TaxModel data;
final VoidCallback onEditTap;
const ManageTaxCard({
super.key,
required this.data,
required this.onEditTap,
});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(16.0),
decoration: ShapeDecoration(
shape: RoundedRectangleBorder(
side: const BorderSide(width: 1, color: AppColors.card),
borderRadius: BorderRadius.circular(19),
),
),
child: Stack(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Spacer(),
Container(
alignment: Alignment.center,
padding: const EdgeInsets.all(12.0),
margin: const EdgeInsets.only(top: 30.0),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: AppColors.disabled.withOpacity(0.4),
),
child: Text(
'${data.value}%',
style: const TextStyle(
fontSize: 32,
fontWeight: FontWeight.w900,
),
),
),
const Spacer(),
Center(
child: RichText(
text: TextSpan(
text: 'Nama Promo : ',
children: [
TextSpan(
text: data.type.name,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
],
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w400,
color: AppColors.black,
),
),
),
),
const Spacer(),
],
),
Align(
alignment: Alignment.topRight,
child: GestureDetector(
onTap: onEditTap,
child: Container(
padding: const EdgeInsets.all(8.0),
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(50.0)),
color: AppColors.primary,
),
child: Assets.icons.edit.svg(),
),
),
),
],
),
);
}
}
@@ -0,0 +1,52 @@
import 'package:flutter/material.dart';
import 'package:enaklo_pos/core/constants/colors.dart';
import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
class MenuPrinterButton extends StatelessWidget {
final String label;
final VoidCallback onPressed;
final bool isActive;
const MenuPrinterButton({
super.key,
required this.label,
required this.onPressed,
required this.isActive,
});
@override
Widget build(BuildContext context) {
return Flexible(
child: InkWell(
onTap: onPressed,
child: Container(
width: context.deviceWidth,
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: isActive ? Colors.white : AppColors.card,
borderRadius: BorderRadius.circular(6),
boxShadow: isActive
? const [
BoxShadow(
color: AppColors.white,
blurRadius: 2,
offset: Offset(0, 1),
spreadRadius: 0,
),
]
: null,
),
child: Text(
label,
textAlign: TextAlign.center,
style: TextStyle(
color: isActive ? AppColors.primary : AppColors.black,
),
),
),
),
);
}
}
@@ -0,0 +1,44 @@
import 'package:flutter/material.dart';
import 'package:print_bluetooth_thermal/print_bluetooth_thermal.dart';
import '../../../../core/constants/colors.dart';
class MenuPrinterContent extends StatelessWidget {
final BluetoothInfo data;
final bool isSelected;
const MenuPrinterContent({
super.key,
required this.data,
required this.isSelected,
});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 10.0),
decoration: BoxDecoration(
border: Border.all(
width: isSelected ? 4.0 : 1.0,
color: isSelected ? AppColors.primary : AppColors.card),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Name : ${data.name}',
style: const TextStyle(
fontWeight: FontWeight.w700,
),
),
Text(
'macAddress: ${data.macAdress}',
style: const TextStyle(
fontSize: 12,
),
),
],
),
);
}
}
@@ -0,0 +1,185 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:enaklo_pos/data/models/response/product_response_model.dart';
import '../../../core/components/buttons.dart';
import '../../../core/components/spaces.dart';
import '../../../core/constants/colors.dart';
import '../../../core/constants/variables.dart';
class MenuProductItem extends StatelessWidget {
final Product data;
final Function() onTapEdit;
const MenuProductItem({
super.key,
required this.data,
required this.onTapEdit,
});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(16.0),
decoration: ShapeDecoration(
color: Colors.white,
shape: RoundedRectangleBorder(
side: const BorderSide(width: 3, color: AppColors.blueLight),
borderRadius: BorderRadius.circular(16),
),
),
child: Column(
children: buildList(context),
));
}
List<Widget> buildList(BuildContext context) {
return [
Stack(
children: [
Center(
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(10.0)),
child: CachedNetworkImage(
imageUrl: '${Variables.baseUrl}/${data.image}',
placeholder: (context, url) =>
const Center(child: CircularProgressIndicator()),
errorWidget: (context, url, error) => const Icon(
Icons.food_bank_outlined,
size: 80,
),
width: 54,
height: 54,
fit: BoxFit.cover,
),
),
),
],
),
Flexible(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Spacer(),
Text(
data.name!,
style: const TextStyle(
fontSize: 16,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
Text(
data.category?.name ?? '-',
style: const TextStyle(
fontSize: 12,
color: Colors.grey,
),
),
Spacer(),
Row(
children: [
Flexible(
child: Button.outlined(
onPressed: () {
showDialog(
context: context,
// backgroundColor: AppColors.white,
builder: (context) {
//container for product detail
return AlertDialog(
contentPadding: const EdgeInsets.all(16.0),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
data.name!,
style: const TextStyle(
fontSize: 20,
),
),
IconButton(
onPressed: () {
Navigator.pop(context);
},
icon: const Icon(Icons.close),
),
],
),
const SpaceHeight(10.0),
ClipRRect(
borderRadius: const BorderRadius.all(
Radius.circular(10.0)),
child: CachedNetworkImage(
imageUrl:
'${Variables.baseUrl}${data.image}',
placeholder: (context, url) =>
const Center(
child:
CircularProgressIndicator()),
errorWidget: (context, url, error) =>
const Icon(
Icons.food_bank_outlined,
size: 80,
),
width: 80,
),
),
const SpaceHeight(10.0),
Text(
data.category?.name ?? '-',
style: const TextStyle(
fontSize: 12,
color: Colors.grey,
),
),
const SpaceHeight(10.0),
Text(
data.price.toString(),
style: const TextStyle(
fontSize: 12,
color: Colors.grey,
),
),
const SpaceHeight(10.0),
Text(
data.stock.toString(),
style: const TextStyle(
fontSize: 12,
color: Colors.grey,
),
),
const SpaceHeight(10.0),
],
),
);
});
},
label: 'View',
fontSize: 12.0,
height: 30,
borderRadius: 10,
),
),
const SpaceWidth(6.0),
Flexible(
child: Button.outlined(
onPressed: onTapEdit,
label: 'Edit',
fontSize: 12.0,
height: 30,
borderRadius: 10,
),
),
],
),
],
),
),
];
}
}
@@ -0,0 +1,323 @@
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:enaklo_pos/core/components/components.dart';
import 'package:enaklo_pos/core/constants/colors.dart';
import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
import 'package:enaklo_pos/data/models/response/print_model.dart';
import 'package:enaklo_pos/presentation/setting/bloc/create_printer/create_printer_bloc.dart';
import 'package:enaklo_pos/presentation/setting/bloc/get_printer_receipt/get_printer_receipt_bloc.dart';
import 'package:enaklo_pos/presentation/setting/bloc/update_printer/update_printer_bloc.dart';
import 'package:enaklo_pos/presentation/setting/widgets/dialog_search_printer.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/settings_local_datasource.dart';
class ReceiptPrinterPage extends StatefulWidget {
const ReceiptPrinterPage({super.key});
@override
State<ReceiptPrinterPage> createState() => _ReceiptPrinterPageState();
}
class _ReceiptPrinterPageState extends State<ReceiptPrinterPage> {
String selectedPrinter = 'Bluetooth';
TextEditingController? addressController;
TextEditingController? printNameController;
String paper = '58';
bool isInitialized = false;
@override
void initState() {
// TODO: implement initState
super.initState();
addressController = TextEditingController();
printNameController = TextEditingController();
context.read<GetPrinterReceiptBloc>().add(GetPrinterReceiptEvent.get());
}
@override
void dispose() {
addressController!.dispose();
printNameController!.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: BlocBuilder<GetPrinterReceiptBloc, GetPrinterReceiptState>(
builder: (context, state) {
return state.maybeWhen(
orElse: () {
return SizedBox.shrink();
},
loading: () {
return Center(child: CircularProgressIndicator());
},
success: (data) {
if (data != null && !isInitialized) {
addressController!.text = data.address;
printNameController!.text = data.name;
selectedPrinter = data.type;
paper = data.paper;
isInitialized = true;
}
return Container(
// width: 300,
width: context.deviceWidth,
padding: const EdgeInsets.all(16.0),
color: Colors.white,
child: ListView(
children: [
const Text(
'Receipt Printer',
style: TextStyle(
color: Colors.black,
fontSize: 20,
fontWeight: FontWeight.w600,
),
),
SpaceHeight(16),
DropdownButtonFormField<String>(
decoration: InputDecoration(
labelText: 'Printer Type',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
),
),
value: selectedPrinter, // nilai default
items: const [
DropdownMenuItem(
value: 'Bluetooth',
child: Text('Bluetooth'),
),
DropdownMenuItem(
value: 'Network',
child: Text('Network'),
),
],
onChanged: (value) {
selectedPrinter = value ?? 'Bluetooth';
setState(() {});
},
),
SpaceHeight(8),
//button search
selectedPrinter == 'Bluetooth'
? Button.outlined(
onPressed: () {
showDialog(
context: context,
builder: (context) => DialogSearchPrinter(
onSelected: (value) {
addressController!.text = value;
setState(() {});
},
),
);
},
label: 'Search')
: CustomTextField(
controller: addressController!,
label: 'Address',
showLabel: false,
),
SpaceHeight(16),
// Textfield for name
CustomTextField(
controller: printNameController!,
label: 'Print Name',
showLabel: false,
),
SpaceHeight(16),
// Textfield for width paper
DropdownButtonFormField<String>(
decoration: InputDecoration(
labelText: 'Width Paper',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
),
),
value: paper, // nilai default
items: const [
DropdownMenuItem(
value: '58',
child: Text('58 mm'),
),
DropdownMenuItem(
value: '80',
child: Text('80 mm'),
),
],
onChanged: (value) {
paper = value ?? '58';
log('Paper : $paper');
},
),
SpaceHeight(16),
// button test print
Button.outlined(
onPressed: () async {
if (addressController!.text.isNotEmpty && printNameController!.text.isNotEmpty) {
try {
// Get actual tax and service charge settings
final settingsLocalDatasource = SettingsLocalDatasource();
final taxModel = await settingsLocalDatasource.getTax();
final serviceChargeValue = await settingsLocalDatasource.getServiceCharge();
// Create a test print model
final testPrinter = PrintModel(
code: 'receipt',
name: printNameController!.text,
address: addressController!.text,
paper: paper,
type: selectedPrinter,
);
// Generate test print data
final testPrintData = await PrintDataoutputs.instance.printOrderV3(
[], // Empty product list for test
0,
0,
'Test',
0,
0,
0,
0,
0,
1,
'Test Cashier',
'Test Customer',
int.parse(paper),
taxPercentage: taxModel.value,
serviceChargePercentage: serviceChargeValue,
);
// Print test
await PrinterService().printWithPrinter(
testPrinter,
testPrintData,
context,
);
} catch (e) {
log("Error test printing: $e");
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error test printing: $e')),
);
}
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Please fill in printer details first')),
);
}
},
label: 'Test Print'
),
SpaceHeight(8),
// button save
data == null
? BlocConsumer<CreatePrinterBloc, CreatePrinterState>(
listener: (context, state) {
state.maybeWhen(
orElse: () {},
success: (message) {
final snackBar = SnackBar(
content: Text(message),
backgroundColor: AppColors.primary,
);
ScaffoldMessenger.of(context)
.showSnackBar(snackBar);
context
.read<GetPrinterReceiptBloc>()
.add(GetPrinterReceiptEvent.get());
},
);
},
builder: (context, state) {
return state.maybeWhen(
orElse: () {
return Button.filled(
onPressed: () {
final printData = PrintModel(
code: 'receipt',
name: printNameController!.text,
address: addressController!.text,
paper: paper,
type: selectedPrinter,
);
context.read<CreatePrinterBloc>().add(
CreatePrinterEvent.createPrinter(
printData),
);
},
label: 'Save',
);
},
loading: () {
return CircularProgressIndicator();
},
);
},
)
: BlocConsumer<UpdatePrinterBloc, UpdatePrinterState>(
listener: (context, state) {
state.maybeWhen(
orElse: () {},
success: (message) {
final snackBar = SnackBar(
content: Text(message),
backgroundColor: AppColors.primary,
);
ScaffoldMessenger.of(context)
.showSnackBar(snackBar);
context
.read<GetPrinterReceiptBloc>()
.add(GetPrinterReceiptEvent.get());
},
);
},
builder: (context, state) {
return state.maybeWhen(
orElse: () {
return Button.filled(
onPressed: () {
final printData = PrintModel(
id: data.id,
code: 'receipt',
name: printNameController!.text,
address: addressController!.text,
paper: paper,
type: selectedPrinter,
);
log("Print Data : ${printData.toMap()}");
context.read<UpdatePrinterBloc>().add(
UpdatePrinterEvent.updatePrinter(
printData,
),
);
},
label: 'Save',
);
},
loading: () {
return CircularProgressIndicator();
},
);
},
),
SpaceHeight(16),
],
),
);
},
);
},
));
}
}
@@ -0,0 +1,45 @@
import 'package:flutter/material.dart';
import '../../../core/components/search_input.dart';
import '../../../core/constants/colors.dart';
class SettingsTitle extends StatelessWidget {
final String title;
final TextEditingController? controller;
final Function(String value)? onChanged;
const SettingsTitle(
this.title, {
super.key,
this.controller,
this.onChanged,
});
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
title,
style: const TextStyle(
color: AppColors.primary,
fontSize: 20,
fontWeight: FontWeight.w600,
),
),
if (controller != null)
SizedBox(
width: 300.0,
child: SearchInput(
controller: controller!,
onChanged: onChanged,
hintText: 'Search for food, coffe, etc..',
),
),
],
);
}
}