first commit
This commit is contained in:
@@ -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..',
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user