first commit
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:enaklo_pos/presentation/setting/bloc/discount/discount_bloc.dart';
|
||||
|
||||
import '../../home/widgets/custom_tab_bar.dart';
|
||||
import '../dialogs/form_discount_dialog.dart';
|
||||
import '../models/discount_model.dart';
|
||||
import '../widgets/add_data.dart';
|
||||
import '../widgets/manage_discount_card.dart';
|
||||
import '../widgets/settings_title.dart';
|
||||
|
||||
class DiscountPage extends StatefulWidget {
|
||||
const DiscountPage({super.key});
|
||||
|
||||
@override
|
||||
State<DiscountPage> createState() => _DiscountPageState();
|
||||
}
|
||||
|
||||
class _DiscountPageState extends State<DiscountPage> {
|
||||
// final List<DiscountModel> discounts = [
|
||||
// DiscountModel(
|
||||
// name: '20',
|
||||
// code: 'BUKAPUASA',
|
||||
// description: null,
|
||||
// discount: 50,
|
||||
// category: ProductCategory.food,
|
||||
// ),
|
||||
// ];
|
||||
|
||||
void onEditTap(DiscountModel item) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => FormDiscountDialog(data: item),
|
||||
);
|
||||
}
|
||||
|
||||
void onAddDataTap() {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => const FormDiscountDialog(),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
context.read<DiscountBloc>().add(const DiscountEvent.getDiscounts());
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
const SettingsTitle('Kelola Diskon'),
|
||||
const SizedBox(height: 24),
|
||||
CustomTabBar(
|
||||
tabTitles: const ['Semua'],
|
||||
initialTabIndex: 0,
|
||||
tabViews: [
|
||||
// SEMUA TAB
|
||||
SizedBox(
|
||||
child: BlocBuilder<DiscountBloc, DiscountState>(
|
||||
builder: (context, state) {
|
||||
return state.maybeWhen(orElse: () {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
}, loaded: (discounts) {
|
||||
return GridView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: discounts.length + 1,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
gridDelegate:
|
||||
const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
childAspectRatio: 0.85,
|
||||
crossAxisCount: 3,
|
||||
crossAxisSpacing: 30.0,
|
||||
mainAxisSpacing: 30.0,
|
||||
),
|
||||
itemBuilder: (context, index) {
|
||||
if (index == 0) {
|
||||
return AddData(
|
||||
title: 'Tambah Diskon Baru',
|
||||
onPressed: onAddDataTap,
|
||||
);
|
||||
}
|
||||
final item = discounts[index - 1];
|
||||
return ManageDiscountCard(
|
||||
data: item,
|
||||
onEditTap: (){},
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
// return GridView.builder(
|
||||
// shrinkWrap: true,
|
||||
// itemCount: discounts.length + 1,
|
||||
// physics: const NeverScrollableScrollPhysics(),
|
||||
// gridDelegate:
|
||||
// const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
// childAspectRatio: 0.85,
|
||||
// crossAxisCount: 3,
|
||||
// crossAxisSpacing: 30.0,
|
||||
// mainAxisSpacing: 30.0,
|
||||
// ),
|
||||
// itemBuilder: (context, index) {
|
||||
// if (index == 0) {
|
||||
// return AddData(
|
||||
// title: 'Tambah Diskon Baru',
|
||||
// onPressed: onAddDataTap,
|
||||
// );
|
||||
// }
|
||||
// final item = discounts[index - 1];
|
||||
// return ManageDiscountCard(
|
||||
// data: item,
|
||||
// onEditTap: () => onEditTap(item),
|
||||
// );
|
||||
// },
|
||||
// );
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,431 @@
|
||||
import 'package:esc_pos_utils_plus/esc_pos_utils_plus.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:enaklo_pos/core/components/components.dart';
|
||||
import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
|
||||
import 'package:enaklo_pos/data/datasources/auth_local_datasource.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:print_bluetooth_thermal/print_bluetooth_thermal.dart';
|
||||
|
||||
import '../../../core/constants/colors.dart';
|
||||
import '../widgets/menu_printer_button.dart';
|
||||
import '../widgets/menu_printer_content.dart';
|
||||
|
||||
class ManagePrinterPage extends StatefulWidget {
|
||||
const ManagePrinterPage({super.key});
|
||||
|
||||
@override
|
||||
State<ManagePrinterPage> createState() => _ManagePrinterPageState();
|
||||
}
|
||||
|
||||
class _ManagePrinterPageState extends State<ManagePrinterPage> {
|
||||
int selectedIndex = 0;
|
||||
int? selectedSize;
|
||||
// final List<PrinterModel> datas = [
|
||||
// PrinterModel(
|
||||
// name: 'Galaxy A30',
|
||||
// address: 12324567412,
|
||||
// ),
|
||||
// PrinterModel(
|
||||
// name: 'Galaxy A30',
|
||||
// address: 12324567412,
|
||||
// ),
|
||||
// PrinterModel(
|
||||
// name: 'Galaxy A30',
|
||||
// address: 12324567412,
|
||||
// ),
|
||||
// ];
|
||||
|
||||
String macName = '';
|
||||
|
||||
String _info = "";
|
||||
String _msj = '';
|
||||
bool connected = false;
|
||||
List<BluetoothInfo> items = [];
|
||||
final List<String> _options = [
|
||||
"permission bluetooth granted",
|
||||
"bluetooth enabled",
|
||||
"connection status",
|
||||
"update info"
|
||||
];
|
||||
|
||||
final String _selectSize = "2";
|
||||
final _txtText = TextEditingController(text: "Hello developer");
|
||||
bool _progress = false;
|
||||
String _msjprogress = "";
|
||||
|
||||
String optionprinttype = "58 mm";
|
||||
List<String> options = ["58 mm", "80 mm"];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
initPlatformState();
|
||||
loadData();
|
||||
}
|
||||
|
||||
Future<void> initPlatformState() async {
|
||||
String platformVersion;
|
||||
int porcentbatery = 0;
|
||||
// Platform messages may fail, so we use a try/catch PlatformException.
|
||||
try {
|
||||
platformVersion = await PrintBluetoothThermal.platformVersion;
|
||||
print("patformversion: $platformVersion");
|
||||
porcentbatery = await PrintBluetoothThermal.batteryLevel;
|
||||
} on PlatformException {
|
||||
platformVersion = 'Failed to get platform version.';
|
||||
}
|
||||
|
||||
// If the widget was removed from the tree while the asynchronous platform
|
||||
// message was in flight, we want to discard the reply rather than calling
|
||||
// setState to update our non-existent appearance.
|
||||
if (!mounted) return;
|
||||
|
||||
final bool result = await PrintBluetoothThermal.bluetoothEnabled;
|
||||
print("bluetooth enabled: $result");
|
||||
if (result) {
|
||||
_msj = "Bluetooth enabled, please search and connect";
|
||||
} else {
|
||||
_msj = "Bluetooth not enabled";
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_info = "$platformVersion ($porcentbatery% battery)";
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> getBluetoots() async {
|
||||
setState(() {
|
||||
_progress = true;
|
||||
_msjprogress = "Wait";
|
||||
items = [];
|
||||
});
|
||||
var status2 = await Permission.bluetoothScan.status;
|
||||
if (status2.isDenied) {
|
||||
await Permission.bluetoothScan.request();
|
||||
}
|
||||
var status = await Permission.bluetoothConnect.status;
|
||||
if (status.isDenied) {
|
||||
await Permission.bluetoothConnect.request();
|
||||
}
|
||||
final List<BluetoothInfo> listResult =
|
||||
await PrintBluetoothThermal.pairedBluetooths;
|
||||
|
||||
setState(() {
|
||||
_progress = false;
|
||||
});
|
||||
|
||||
if (listResult.isEmpty) {
|
||||
_msj =
|
||||
"There are no bluetoohs linked, go to settings and link the printer";
|
||||
} else {
|
||||
_msj = "Touch an item in the list to connect";
|
||||
}
|
||||
|
||||
setState(() {
|
||||
items = listResult;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> connect(String mac) async {
|
||||
setState(() {
|
||||
_progress = true;
|
||||
_msjprogress = "Connecting...";
|
||||
connected = false;
|
||||
});
|
||||
final bool result =
|
||||
await PrintBluetoothThermal.connect(macPrinterAddress: mac);
|
||||
print("state conected $result");
|
||||
if (result) connected = true;
|
||||
setState(() {
|
||||
_progress = false;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> disconnect() async {
|
||||
final bool status = await PrintBluetoothThermal.disconnect;
|
||||
setState(() {
|
||||
connected = false;
|
||||
});
|
||||
print("status disconnect $status");
|
||||
}
|
||||
|
||||
Future<void> printTest() async {
|
||||
bool conexionStatus = await PrintBluetoothThermal.connectionStatus;
|
||||
//print("connection status: $conexionStatus");
|
||||
if (conexionStatus) {
|
||||
List<int> ticket = await testTicket();
|
||||
final result = await PrintBluetoothThermal.writeBytes(ticket);
|
||||
print("print test result: $result");
|
||||
} else {
|
||||
//no conectado, reconecte
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<int>> testTicket() async {
|
||||
List<int> bytes = [];
|
||||
// Using default profile
|
||||
final profile = await CapabilityProfile.load();
|
||||
final generator = Generator(
|
||||
optionprinttype == "58 mm" ? PaperSize.mm58 : PaperSize.mm80, profile);
|
||||
//bytes += generator.setGlobalFont(PosFontType.fontA);
|
||||
bytes += generator.reset();
|
||||
|
||||
bytes +=
|
||||
generator.text('Enaklo POS', styles: const PosStyles(bold: true));
|
||||
bytes +=
|
||||
generator.text('Reverse text', styles: const PosStyles(reverse: true));
|
||||
bytes += generator.text('Underlined text',
|
||||
styles: const PosStyles(underline: true), linesAfter: 1);
|
||||
bytes += generator.text('Align left',
|
||||
styles: const PosStyles(align: PosAlign.left));
|
||||
bytes += generator.text('Align center',
|
||||
styles: const PosStyles(align: PosAlign.center));
|
||||
bytes += generator.text('Align right',
|
||||
styles: const PosStyles(align: PosAlign.right), linesAfter: 1);
|
||||
|
||||
bytes += generator.text(
|
||||
'FIC Batch 11',
|
||||
styles: const PosStyles(
|
||||
height: PosTextSize.size2,
|
||||
width: PosTextSize.size2,
|
||||
),
|
||||
);
|
||||
|
||||
bytes += generator.feed(2);
|
||||
//bytes += generator.cut();
|
||||
return bytes;
|
||||
}
|
||||
|
||||
Future<void> printWithoutPackage() async {
|
||||
//impresion sin paquete solo de PrintBluetoothTermal
|
||||
bool connectionStatus = await PrintBluetoothThermal.connectionStatus;
|
||||
if (connectionStatus) {
|
||||
String text = "${_txtText.text}\n";
|
||||
bool result = await PrintBluetoothThermal.writeString(
|
||||
printText: PrintTextSize(size: int.parse(_selectSize), text: text));
|
||||
print("status print result: $result");
|
||||
setState(() {
|
||||
_msj = "printed status: $result";
|
||||
});
|
||||
} else {
|
||||
//no conectado, reconecte
|
||||
setState(() {
|
||||
_msj = "no connected device";
|
||||
});
|
||||
print("no conectado");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> printString() async {
|
||||
bool conexionStatus = await PrintBluetoothThermal.connectionStatus;
|
||||
if (conexionStatus) {
|
||||
String enter = '\n';
|
||||
await PrintBluetoothThermal.writeBytes(enter.codeUnits);
|
||||
//size of 1-5
|
||||
String text = "Hello";
|
||||
await PrintBluetoothThermal.writeString(
|
||||
printText: PrintTextSize(size: 1, text: text));
|
||||
await PrintBluetoothThermal.writeString(
|
||||
printText: PrintTextSize(size: 2, text: "$text size 2"));
|
||||
await PrintBluetoothThermal.writeString(
|
||||
printText: PrintTextSize(size: 3, text: "$text size 3"));
|
||||
} else {
|
||||
//desconectado
|
||||
print("desconectado bluetooth $conexionStatus");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> loadData() async {
|
||||
final savedSize = await AuthLocalDataSource().getSizeReceipt();
|
||||
if (savedSize.isNotEmpty) {
|
||||
setState(() {
|
||||
selectedSize = int.parse(savedSize);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Kelola Printer'),
|
||||
centerTitle: true,
|
||||
),
|
||||
bottomNavigationBar: SizedBox(
|
||||
height: 60,
|
||||
child: Button.filled(
|
||||
onPressed: () async {
|
||||
// Periksa apakah ukuran dan printer telah dipilih
|
||||
if (selectedSize != null && macName.isNotEmpty) {
|
||||
// Simpan ukuran dan alamat MAC printer
|
||||
AuthLocalDataSource().saveSizeReceipt(selectedSize.toString());
|
||||
await connect(macName);
|
||||
|
||||
// Tampilkan pesan berhasil
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Pengaturan berhasil disimpan')),
|
||||
);
|
||||
} else {
|
||||
// Tampilkan pesan error jika belum dipilih
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content:
|
||||
Text('Pilih ukuran dan printer terlebih dahulu')),
|
||||
);
|
||||
}
|
||||
},
|
||||
label: 'Simpan'),
|
||||
),
|
||||
body: ListView(
|
||||
padding: const EdgeInsets.all(24.0),
|
||||
children: [
|
||||
Text(
|
||||
"Pilih Ukuran",
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColors.primary),
|
||||
),
|
||||
SpaceHeight(16),
|
||||
Container(
|
||||
width: context.deviceWidth / 2,
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.card,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
// Radio button untuk ukuran 58
|
||||
Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
Radio<int>(
|
||||
value: 58,
|
||||
groupValue: selectedSize,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
selectedSize = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
const Text('58 mm'),
|
||||
],
|
||||
),
|
||||
),
|
||||
// Radio button untuk ukuran 80
|
||||
Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
Radio<int>(
|
||||
value: 80,
|
||||
groupValue: selectedSize,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
selectedSize = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
const Text('80 mm'),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SpaceHeight(24),
|
||||
Text(
|
||||
"Pilih Printer",
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColors.primary),
|
||||
),
|
||||
SpaceHeight(16),
|
||||
Container(
|
||||
width: context.deviceWidth / 2,
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.card,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
MenuPrinterButton(
|
||||
label: 'Search',
|
||||
onPressed: () {
|
||||
getBluetoots();
|
||||
selectedIndex = 0;
|
||||
setState(() {});
|
||||
},
|
||||
isActive: selectedIndex == 0,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SpaceHeight(34.0),
|
||||
_Body(
|
||||
// selectedIndex: selectedIndex,
|
||||
macName: macName,
|
||||
datas: items,
|
||||
clickHandler: (mac) async {
|
||||
macName = mac;
|
||||
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
SpaceHeight(24),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _Body extends StatelessWidget {
|
||||
// final int selectedIndex;
|
||||
final String macName;
|
||||
final List<BluetoothInfo> datas;
|
||||
|
||||
//clickHandler
|
||||
final Function(String) clickHandler;
|
||||
|
||||
const _Body({
|
||||
required this.macName,
|
||||
required this.datas,
|
||||
required this.clickHandler,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (datas.isEmpty) {
|
||||
return const Text('No data available');
|
||||
} else {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(24.0),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
border: Border.all(color: AppColors.card, width: 2),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
child: ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: datas.length,
|
||||
separatorBuilder: (context, index) => const SpaceHeight(16.0),
|
||||
itemBuilder: (context, index) => InkWell(
|
||||
onTap: () {
|
||||
clickHandler(datas[index].macAdress);
|
||||
},
|
||||
child: MenuPrinterContent(
|
||||
isSelected: macName == datas[index].macAdress,
|
||||
data: datas[index],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
// return const Placeholder();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:enaklo_pos/data/datasources/auth_local_datasource.dart';
|
||||
import 'package:enaklo_pos/presentation/setting/pages/discount_page.dart';
|
||||
import 'package:enaklo_pos/presentation/setting/pages/manage_printer_page.dart';
|
||||
import 'package:enaklo_pos/presentation/setting/pages/product_page.dart';
|
||||
import 'package:enaklo_pos/presentation/setting/pages/server_key_page.dart';
|
||||
import 'package:enaklo_pos/presentation/setting/pages/sync_data_page.dart';
|
||||
import 'package:enaklo_pos/presentation/setting/pages/tax_page.dart';
|
||||
import 'package:enaklo_pos/presentation/setting/widgets/bar_printer_page.dart';
|
||||
import 'package:enaklo_pos/presentation/setting/widgets/checker_printer_page.dart';
|
||||
import 'package:enaklo_pos/presentation/setting/widgets/kitchen_printer_page.dart';
|
||||
import 'package:enaklo_pos/presentation/setting/widgets/receipt_printer_page.dart';
|
||||
|
||||
import '../../../core/assets/assets.gen.dart';
|
||||
import '../../../core/components/spaces.dart';
|
||||
import '../../../core/constants/colors.dart';
|
||||
|
||||
class PrinterConfigurationPage extends StatefulWidget {
|
||||
const PrinterConfigurationPage({super.key});
|
||||
|
||||
@override
|
||||
State<PrinterConfigurationPage> createState() => _SettingsPageState();
|
||||
}
|
||||
|
||||
class _SettingsPageState extends State<PrinterConfigurationPage> {
|
||||
int currentIndex = 0;
|
||||
String? role;
|
||||
|
||||
void indexValue(int index) {
|
||||
currentIndex = index;
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
setRole();
|
||||
}
|
||||
|
||||
void setRole() {
|
||||
AuthLocalDataSource().getAuthData().then((value) {
|
||||
setState(() {
|
||||
role = value.user!.role;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Row(
|
||||
children: [
|
||||
// LEFT CONTENT
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 16,
|
||||
top: 16,
|
||||
),
|
||||
children: [
|
||||
const Text(
|
||||
'Printer Configuration',
|
||||
style: TextStyle(
|
||||
color: AppColors.primary,
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SpaceHeight(16.0),
|
||||
ListTile(
|
||||
// contentPadding: const EdgeInsets.all(12.0),
|
||||
// leading: Assets.icons.kelolaDiskon.svg(),
|
||||
title: const Text('Receipt Printer',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
)),
|
||||
subtitle: const Text('To Print bill and receipt'),
|
||||
textColor: AppColors.primary,
|
||||
tileColor:
|
||||
currentIndex == 0 ? AppColors.card : Colors.transparent,
|
||||
onTap: () => indexValue(0),
|
||||
),
|
||||
ListTile(
|
||||
// contentPadding: const EdgeInsets.all(12.0),
|
||||
// leading: Assets.icons.kelolaPrinter.svg(),
|
||||
title: const Text('Checker Printer',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
)),
|
||||
subtitle: const Text('Print checker chit'),
|
||||
textColor: AppColors.primary,
|
||||
tileColor: currentIndex == 1
|
||||
? AppColors.blueLight
|
||||
: Colors.transparent,
|
||||
onTap: () => indexValue(1),
|
||||
),
|
||||
ListTile(
|
||||
// contentPadding: const EdgeInsets.all(12.0),
|
||||
// leading: Assets.icons.kelolaPajak.svg(),
|
||||
title: const Text('Kitchen Printer',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
)),
|
||||
subtitle: const Text('To print food to kitchen'),
|
||||
textColor: AppColors.primary,
|
||||
tileColor: currentIndex == 2
|
||||
? AppColors.blueLight
|
||||
: Colors.transparent,
|
||||
onTap: () => indexValue(2),
|
||||
),
|
||||
ListTile(
|
||||
// contentPadding: const EdgeInsets.all(12.0),
|
||||
// leading: Assets.icons.kelolaPajak.svg(),
|
||||
title: const Text('Bar Printer',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
)),
|
||||
subtitle: const Text('To print drink to bar'),
|
||||
textColor: AppColors.primary,
|
||||
tileColor: currentIndex == 3
|
||||
? AppColors.blueLight
|
||||
: Colors.transparent,
|
||||
onTap: () => indexValue(3),
|
||||
),
|
||||
ListTile(
|
||||
// contentPadding: const EdgeInsets.all(12.0),
|
||||
// leading: Image.asset(Assets.images.manageQr.path,
|
||||
// fit: BoxFit.contain),
|
||||
title: const Text('Additional Printer',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
)),
|
||||
subtitle:
|
||||
const Text('To print additional kitchen/bar printer'),
|
||||
|
||||
textColor: AppColors.primary,
|
||||
tileColor: currentIndex == 4
|
||||
? AppColors.blueLight
|
||||
: Colors.transparent,
|
||||
onTap: () => indexValue(4),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// RIGHT CONTENT
|
||||
Expanded(
|
||||
flex: 4,
|
||||
child: Align(
|
||||
alignment: AlignmentDirectional.topStart,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: IndexedStack(
|
||||
index: currentIndex,
|
||||
children: [
|
||||
ReceiptPrinterPage(),
|
||||
CheckerPrinterPage(),
|
||||
KitchenPrinterPage(),
|
||||
BarPrinterPage(),
|
||||
// role != null && role! != 'admin'
|
||||
// ? SizedBox()
|
||||
// : ProductPage(),
|
||||
// DiscountPage(),
|
||||
// ManagePrinterPage(),
|
||||
// TaxPage(),
|
||||
// SyncDataPage(),
|
||||
// ProductPage(),
|
||||
// ServerKeyPage()
|
||||
// Text('tax'),
|
||||
// ManageDiscount(),
|
||||
// ManagePrinterPage(),
|
||||
// ManageTax(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:enaklo_pos/data/datasources/product_remote_datasource.dart';
|
||||
import 'package:enaklo_pos/presentation/setting/bloc/get_products/get_products_bloc.dart';
|
||||
import 'package:enaklo_pos/presentation/setting/bloc/update_product/update_product_bloc.dart';
|
||||
import 'package:enaklo_pos/presentation/setting/bloc/sync_product/sync_product_bloc.dart';
|
||||
import 'package:enaklo_pos/presentation/setting/bloc/add_product/add_product_bloc.dart';
|
||||
import 'package:enaklo_pos/presentation/setting/dialogs/form_product_dialog.dart';
|
||||
import 'package:enaklo_pos/presentation/setting/widgets/add_data.dart';
|
||||
import 'package:enaklo_pos/presentation/setting/widgets/menu_product_item.dart';
|
||||
import 'package:enaklo_pos/presentation/setting/widgets/settings_title.dart';
|
||||
|
||||
class ProductPage extends StatefulWidget {
|
||||
const ProductPage({super.key});
|
||||
|
||||
@override
|
||||
State<ProductPage> createState() => _ProductPageState();
|
||||
}
|
||||
|
||||
class _ProductPageState extends State<ProductPage> {
|
||||
@override
|
||||
void initState() {
|
||||
context.read<GetProductsBloc>().add(const GetProductsEvent.fetch());
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: ListView(
|
||||
padding: const EdgeInsets.all(24.0),
|
||||
children: [
|
||||
const SettingsTitle('Manage Products'),
|
||||
const SizedBox(height: 24),
|
||||
BlocBuilder<GetProductsBloc, GetProductsState>(
|
||||
builder: (context, state) {
|
||||
return state.maybeWhen(orElse: () {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
}, success: (products) {
|
||||
return GridView.builder(
|
||||
padding: EdgeInsets.zero,
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
childAspectRatio: 1,
|
||||
crossAxisCount: 3,
|
||||
mainAxisSpacing: 12,
|
||||
crossAxisSpacing: 12,
|
||||
),
|
||||
itemCount: products.length + 1,
|
||||
shrinkWrap: true,
|
||||
physics: const ScrollPhysics(),
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
if (index == 0) {
|
||||
return AddData(
|
||||
title: 'Add New Product',
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider(
|
||||
create: (context) => AddProductBloc(ProductRemoteDatasource()),
|
||||
),
|
||||
BlocProvider.value(
|
||||
value: context.read<SyncProductBloc>(),
|
||||
),
|
||||
BlocProvider.value(
|
||||
value: context.read<GetProductsBloc>(),
|
||||
),
|
||||
],
|
||||
child: const FormProductDialog(),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
final item = products[index - 1];
|
||||
return MenuProductItem(
|
||||
data: item,
|
||||
onTapEdit: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider(
|
||||
create: (context) => UpdateProductBloc(ProductRemoteDatasource()),
|
||||
),
|
||||
BlocProvider.value(
|
||||
value: context.read<SyncProductBloc>(),
|
||||
),
|
||||
BlocProvider.value(
|
||||
value: context.read<GetProductsBloc>(),
|
||||
),
|
||||
],
|
||||
child: FormProductDialog(product: item),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
// floatingActionButton: FloatingActionButton(
|
||||
// onPressed: () {
|
||||
|
||||
// },
|
||||
// child: const Icon(Icons.add),
|
||||
// ),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:enaklo_pos/core/constants/colors.dart';
|
||||
import 'package:enaklo_pos/data/datasources/auth_local_datasource.dart';
|
||||
|
||||
class ServerKeyPage extends StatefulWidget {
|
||||
final bool isTablet;
|
||||
const ServerKeyPage({
|
||||
super.key,
|
||||
this.isTablet = false,
|
||||
});
|
||||
|
||||
@override
|
||||
State<ServerKeyPage> createState() => _ServerKeyPageState();
|
||||
}
|
||||
|
||||
class _ServerKeyPageState extends State<ServerKeyPage> {
|
||||
TextEditingController? serverKeyController;
|
||||
|
||||
String serverKey = '';
|
||||
|
||||
Future<void> getServerKey() async {
|
||||
serverKey = await AuthLocalDataSource().getMitransServerKey();
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
serverKeyController = TextEditingController();
|
||||
getServerKey();
|
||||
//delay 2 detik
|
||||
Future.delayed(const Duration(seconds: 2), () {
|
||||
serverKeyController!.text = serverKey;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Save Server Key'),
|
||||
centerTitle: true,
|
||||
),
|
||||
//textfield untuk input server key
|
||||
body: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: TextField(
|
||||
controller: serverKeyController,
|
||||
decoration: const InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText: 'Server Key',
|
||||
),
|
||||
),
|
||||
),
|
||||
//button untuk save server key
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
AuthLocalDataSource()
|
||||
.saveMidtransServerKey(serverKeyController!.text);
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Server Key saved'),
|
||||
backgroundColor: AppColors.primary,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: const Text('Save'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:enaklo_pos/data/datasources/auth_local_datasource.dart';
|
||||
import 'package:enaklo_pos/presentation/sales/pages/sales_page.dart';
|
||||
import 'package:enaklo_pos/presentation/setting/pages/discount_page.dart';
|
||||
import 'package:enaklo_pos/presentation/setting/pages/manage_printer_page.dart';
|
||||
import 'package:enaklo_pos/presentation/setting/pages/product_page.dart';
|
||||
import 'package:enaklo_pos/presentation/setting/pages/server_key_page.dart';
|
||||
import 'package:enaklo_pos/presentation/setting/pages/sync_data_page.dart';
|
||||
import 'package:enaklo_pos/presentation/setting/pages/tax_page.dart';
|
||||
|
||||
import '../../../core/assets/assets.gen.dart';
|
||||
import '../../../core/components/spaces.dart';
|
||||
import '../../../core/constants/colors.dart';
|
||||
|
||||
class SettingsPage extends StatefulWidget {
|
||||
const SettingsPage({super.key});
|
||||
|
||||
@override
|
||||
State<SettingsPage> createState() => _SettingsPageState();
|
||||
}
|
||||
|
||||
class _SettingsPageState extends State<SettingsPage> {
|
||||
int currentIndex = 0;
|
||||
String? role;
|
||||
|
||||
void indexValue(int index) {
|
||||
currentIndex = index;
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
setRole();
|
||||
}
|
||||
|
||||
void setRole() {
|
||||
AuthLocalDataSource().getAuthData().then((value) {
|
||||
setState(() {
|
||||
role = value.user!.role;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Row(
|
||||
children: [
|
||||
// LEFT CONTENT
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
children: [
|
||||
const Text(
|
||||
'Settings',
|
||||
style: TextStyle(
|
||||
color: AppColors.primary,
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SpaceHeight(16.0),
|
||||
role != null && role! != 'admin'
|
||||
? const SizedBox()
|
||||
: ListTile(
|
||||
contentPadding: const EdgeInsets.all(12.0),
|
||||
leading: Assets.icons.kelolaProduk.svg(),
|
||||
title: const Text('Manage Products'),
|
||||
subtitle: const Text('Manage products in your store'),
|
||||
textColor: AppColors.primary,
|
||||
tileColor: currentIndex == 0
|
||||
? AppColors.blueLight
|
||||
: Colors.transparent,
|
||||
onTap: () => indexValue(0),
|
||||
),
|
||||
ListTile(
|
||||
contentPadding: const EdgeInsets.all(12.0),
|
||||
leading: Assets.icons.kelolaDiskon.svg(),
|
||||
title: const Text('Kelola Diskon'),
|
||||
subtitle: const Text('Kelola Diskon Pelanggan'),
|
||||
textColor: AppColors.primary,
|
||||
tileColor: currentIndex == 1
|
||||
? AppColors.blueLight
|
||||
: Colors.transparent,
|
||||
onTap: () => indexValue(1),
|
||||
),
|
||||
ListTile(
|
||||
contentPadding: const EdgeInsets.all(12.0),
|
||||
leading: Assets.icons.dashboard.svg(),
|
||||
title: const Text('History Transaksi'),
|
||||
subtitle: const Text('Lihat history transaksi'),
|
||||
textColor: AppColors.primary,
|
||||
tileColor: currentIndex == 2
|
||||
? AppColors.blueLight
|
||||
: Colors.transparent,
|
||||
onTap: () => indexValue(2),
|
||||
),
|
||||
ListTile(
|
||||
contentPadding: const EdgeInsets.all(12.0),
|
||||
leading: Assets.icons.kelolaPajak.svg(),
|
||||
title: const Text('Perhitungan Biaya'),
|
||||
subtitle: const Text('Kelola biaya diluar biaya modal'),
|
||||
textColor: AppColors.primary,
|
||||
tileColor: currentIndex == 3
|
||||
? AppColors.blueLight
|
||||
: Colors.transparent,
|
||||
onTap: () => indexValue(3),
|
||||
),
|
||||
ListTile(
|
||||
contentPadding: const EdgeInsets.all(12.0),
|
||||
leading: Assets.icons.kelolaPajak.svg(),
|
||||
title: const Text('Sync Data'),
|
||||
subtitle:
|
||||
const Text('Sinkronisasi data dari dan ke server'),
|
||||
textColor: AppColors.primary,
|
||||
tileColor: currentIndex == 4
|
||||
? AppColors.blueLight
|
||||
: Colors.transparent,
|
||||
onTap: () => indexValue(4),
|
||||
),
|
||||
ListTile(
|
||||
contentPadding: const EdgeInsets.all(12.0),
|
||||
leading: Image.asset(Assets.images.manageQr.path,
|
||||
fit: BoxFit.contain),
|
||||
title: const Text('QR Key Setting'),
|
||||
subtitle: const Text('QR Key Configuration'),
|
||||
textColor: AppColors.primary,
|
||||
tileColor: currentIndex == 6
|
||||
? AppColors.blueLight
|
||||
: Colors.transparent,
|
||||
onTap: () => indexValue(6),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// RIGHT CONTENT
|
||||
Expanded(
|
||||
flex: 4,
|
||||
child: Align(
|
||||
alignment: AlignmentDirectional.topStart,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: IndexedStack(
|
||||
index: currentIndex,
|
||||
children: [
|
||||
role != null && role! != 'admin'
|
||||
? SizedBox()
|
||||
: ProductPage(),
|
||||
DiscountPage(),
|
||||
SalesPage(),
|
||||
TaxPage(),
|
||||
SyncDataPage(),
|
||||
ProductPage(),
|
||||
ServerKeyPage()
|
||||
// Text('tax'),
|
||||
// ManageDiscount(),
|
||||
// ManagePrinterPage(),
|
||||
// ManageTax(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
import 'dart:developer';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
|
||||
import 'package:enaklo_pos/presentation/setting/bloc/sync_order/sync_order_bloc.dart';
|
||||
import 'package:enaklo_pos/presentation/setting/bloc/sync_product/sync_product_bloc.dart';
|
||||
|
||||
class SyncDataPage extends StatefulWidget {
|
||||
const SyncDataPage({super.key});
|
||||
|
||||
@override
|
||||
State<SyncDataPage> createState() => _SyncDataPageState();
|
||||
}
|
||||
|
||||
class _SyncDataPageState extends State<SyncDataPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Sync Data'),
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
BlocConsumer<SyncProductBloc, SyncProductState>(
|
||||
listener: (context, state) {
|
||||
state.maybeWhen(
|
||||
orElse: () {},
|
||||
error: (message) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(message),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
},
|
||||
loaded: (productResponseModel) async {
|
||||
await ProductLocalDatasource.instance.deleteAllProducts();
|
||||
await ProductLocalDatasource.instance.insertProducts(
|
||||
productResponseModel.data!,
|
||||
);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Sync Product Success2'),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
builder: (context, state) {
|
||||
return state.maybeWhen(
|
||||
orElse: () {
|
||||
return ElevatedButton(
|
||||
onPressed: () {
|
||||
context
|
||||
.read<SyncProductBloc>()
|
||||
.add(const SyncProductEvent.syncProduct());
|
||||
},
|
||||
child: const Text('Sync Product'));
|
||||
},
|
||||
loading: () {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
BlocConsumer<SyncOrderBloc, SyncOrderState>(
|
||||
listener: (context, state) {
|
||||
state.maybeWhen(
|
||||
orElse: () {},
|
||||
error: (message) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(message),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
},
|
||||
loaded: () {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Sync Order Success'),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
builder: (context, state) {
|
||||
return state.maybeWhen(
|
||||
orElse: () {
|
||||
return ElevatedButton(
|
||||
onPressed: () {
|
||||
log("🔘 Sync Order button pressed");
|
||||
log("🔘 SyncOrderBloc instance: ${context.read<SyncOrderBloc>()}");
|
||||
context
|
||||
.read<SyncOrderBloc>()
|
||||
.add(const SyncOrderEvent.syncOrder());
|
||||
log("🔘 SyncOrderEvent.syncOrder dispatched");
|
||||
},
|
||||
child: const Text('Sync Order'),
|
||||
);
|
||||
},
|
||||
loading: () {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../home/widgets/custom_tab_bar.dart';
|
||||
import '../bloc/tax_settings/tax_settings_bloc.dart';
|
||||
import '../dialogs/form_tax_dialog.dart';
|
||||
import '../models/tax_model.dart';
|
||||
import '../widgets/add_data.dart';
|
||||
import '../widgets/manage_tax_card.dart';
|
||||
import '../widgets/settings_title.dart';
|
||||
|
||||
class TaxPage extends StatefulWidget {
|
||||
const TaxPage({super.key});
|
||||
|
||||
@override
|
||||
State<TaxPage> createState() => _TaxPageState();
|
||||
}
|
||||
|
||||
class _TaxPageState extends State<TaxPage> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
context.read<TaxSettingsBloc>().add(const TaxSettingsEvent.loadSettings());
|
||||
}
|
||||
|
||||
void onEditTap(TaxModel item, int serviceChargeValue, int taxValue) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => FormTaxDialog(
|
||||
taxValue: taxValue,
|
||||
serviceChargeValue: serviceChargeValue,
|
||||
onSave: (newTaxValue, newServiceChargeValue) {
|
||||
context.read<TaxSettingsBloc>().add(
|
||||
TaxSettingsEvent.updateSettings(
|
||||
taxValue: newTaxValue,
|
||||
serviceChargeValue: newServiceChargeValue,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void onAddDataTap(int serviceChargeValue, int taxValue) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => FormTaxDialog(
|
||||
taxValue: taxValue,
|
||||
serviceChargeValue: serviceChargeValue,
|
||||
onSave: (newTaxValue, newServiceChargeValue) {
|
||||
context.read<TaxSettingsBloc>().add(
|
||||
TaxSettingsEvent.updateSettings(
|
||||
taxValue: newTaxValue,
|
||||
serviceChargeValue: newServiceChargeValue,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
const SettingsTitle('Perhitungan Biaya'),
|
||||
const SizedBox(height: 24),
|
||||
BlocBuilder<TaxSettingsBloc, TaxSettingsState>(
|
||||
builder: (context, state) {
|
||||
return state.when(
|
||||
initial: () => const Center(child: CircularProgressIndicator()),
|
||||
loading: () => const Center(child: CircularProgressIndicator()),
|
||||
error: (message) => Center(child: Text('Error: $message')),
|
||||
loaded: (taxModel, serviceChargeValue) {
|
||||
final items = [
|
||||
TaxModel(name: 'Biaya Layanan', type: TaxType.layanan, value: serviceChargeValue),
|
||||
taxModel,
|
||||
];
|
||||
|
||||
return CustomTabBar(
|
||||
tabTitles: const ['Layanan', 'Pajak'],
|
||||
initialTabIndex: 0,
|
||||
tabViews: [
|
||||
// LAYANAN TAB
|
||||
SizedBox(
|
||||
child: GridView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: 2, // Add button + 1 service charge item
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
childAspectRatio: 0.85,
|
||||
crossAxisCount: 3,
|
||||
crossAxisSpacing: 30.0,
|
||||
mainAxisSpacing: 30.0,
|
||||
),
|
||||
itemBuilder: (context, index) {
|
||||
if (index == 0) {
|
||||
return AddData(
|
||||
title: 'Edit Perhitungan',
|
||||
onPressed: () => onAddDataTap(serviceChargeValue, taxModel.value),
|
||||
);
|
||||
}
|
||||
final item = items.firstWhere((element) => element.type.isLayanan);
|
||||
return ManageTaxCard(
|
||||
data: item,
|
||||
onEditTap: () => onEditTap(item, serviceChargeValue, taxModel.value),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
// PAJAK TAB
|
||||
SizedBox(
|
||||
child: GridView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: 2, // Add button + 1 tax item
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
childAspectRatio: 0.85,
|
||||
crossAxisCount: 3,
|
||||
crossAxisSpacing: 30.0,
|
||||
mainAxisSpacing: 30.0,
|
||||
),
|
||||
itemBuilder: (context, index) {
|
||||
if (index == 0) {
|
||||
return AddData(
|
||||
title: 'Edit Perhitungan',
|
||||
onPressed: () => onAddDataTap(serviceChargeValue, taxModel.value),
|
||||
);
|
||||
}
|
||||
final item = items.firstWhere((element) => element.type.isPajak);
|
||||
return ManageTaxCard(
|
||||
data: item,
|
||||
onEditTap: () => onEditTap(item, serviceChargeValue, taxModel.value),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user