first commit
This commit is contained in:
55
lib/app/constant.dart
Normal file
55
lib/app/constant.dart
Normal file
@@ -0,0 +1,55 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class Constant {
|
||||
static double designHeight = 982;
|
||||
static double designWidth = 1512;
|
||||
static String versi = "Versi 16";
|
||||
|
||||
static TextStyle title({required BuildContext context}) {
|
||||
return TextStyle(
|
||||
fontSize: Constant.getActualX(context: context, x: 32),
|
||||
fontWeight: FontWeight.w600,
|
||||
);
|
||||
}
|
||||
|
||||
static TextStyle subTitle({required BuildContext context}) {
|
||||
return TextStyle(
|
||||
fontSize: Constant.getActualX(context: context, x: 22),
|
||||
fontWeight: FontWeight.w600,
|
||||
);
|
||||
}
|
||||
|
||||
static TextStyle layananTitle({required BuildContext context}) {
|
||||
return TextStyle(
|
||||
fontSize: Constant.getActualX(context: context, x: 18),
|
||||
fontWeight: FontWeight.w600,
|
||||
);
|
||||
}
|
||||
|
||||
static TextStyle label({required BuildContext context}) {
|
||||
return TextStyle(
|
||||
fontSize: Constant.getActualX(context: context, x: 16),
|
||||
fontStyle: FontStyle.italic);
|
||||
}
|
||||
|
||||
static TextStyle normal({required BuildContext context}) {
|
||||
return TextStyle(
|
||||
fontSize: Constant.getActualX(context: context, x: 16),
|
||||
);
|
||||
}
|
||||
|
||||
//size convertion
|
||||
static double getActualX({
|
||||
required BuildContext context,
|
||||
required double x,
|
||||
}) {
|
||||
return x / designWidth * MediaQuery.of(context).size.width;
|
||||
}
|
||||
|
||||
static double getActualY({
|
||||
required BuildContext context,
|
||||
required double y,
|
||||
}) {
|
||||
return y / designHeight * MediaQuery.of(context).size.height;
|
||||
}
|
||||
}
|
||||
367
lib/app/print_ticket.dart
Normal file
367
lib/app/print_ticket.dart
Normal file
@@ -0,0 +1,367 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:esc_pos_utils/esc_pos_utils.dart';
|
||||
import 'package:flutter_pos_printer_platform_image_3/flutter_pos_printer_platform_image_3.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
class PrintTicket {
|
||||
// Future printTest() async {
|
||||
// List<int> bytes = [];
|
||||
// var profile = await CapabilityProfile.load();
|
||||
|
||||
// var generator = Generator(PaperSize.mm80, profile);
|
||||
// bytes += generator.setGlobalCodeTable('CP1252');
|
||||
// bytes += generator.text('TEST PRINT',
|
||||
// styles: const PosStyles(align: PosAlign.center));
|
||||
// bytes += generator.cut();
|
||||
// PrinterManager.instance.send(type: PrinterType.usb, bytes: bytes);
|
||||
// }
|
||||
|
||||
Future printTicket(
|
||||
{required String boothId,
|
||||
required String header,
|
||||
required String date,
|
||||
required String time,
|
||||
required String antrianNumb,
|
||||
required String name,
|
||||
required String location,
|
||||
required StringqrCode,
|
||||
required String isConsult,
|
||||
required String DoctorName,
|
||||
required String layananName,
|
||||
required String capabilityName,
|
||||
required String footer}) async {
|
||||
try {
|
||||
List<int> bytes = [];
|
||||
final dt = DateFormat('yyyy-MM-dd-HH-mm-ss').format(DateTime.now());
|
||||
final splited = dt.split('-');
|
||||
final date = splited[2].toString();
|
||||
final month = splited[1].toString();
|
||||
final year = splited[0].toString();
|
||||
final hour = splited[3].toString();
|
||||
final minute = splited[4].toString();
|
||||
|
||||
final displayDate = "$date/$month/$year - $hour.$minute WIB";
|
||||
|
||||
final profile = await CapabilityProfile.load(name: capabilityName);
|
||||
final avl = await CapabilityProfile.getAvailableProfiles();
|
||||
// final avl = await CapabilityProfile.getAvailableProfiles(name: "XP-N160I");
|
||||
// print(avl);
|
||||
|
||||
var generator = Generator(PaperSize.mm80, profile);
|
||||
|
||||
bytes += generator.text(
|
||||
"Booth :$boothId",
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.right,
|
||||
bold: true,
|
||||
height: PosTextSize.size1,
|
||||
width: PosTextSize.size1),
|
||||
);
|
||||
// bytes += generator.text(
|
||||
// "capability :$capabilityName",
|
||||
// styles: const PosStyles(
|
||||
// align: PosAlign.right,
|
||||
// bold: true,
|
||||
// height: PosTextSize.size1,
|
||||
// width: PosTextSize.size1),
|
||||
// );
|
||||
bytes += generator.hr(ch: " ");
|
||||
|
||||
bytes += generator.text(
|
||||
header,
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.center,
|
||||
bold: true,
|
||||
height: PosTextSize.size1,
|
||||
width: PosTextSize.size1),
|
||||
);
|
||||
|
||||
bytes += generator.hr(ch: " ");
|
||||
bytes += generator.text(
|
||||
displayDate,
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.center,
|
||||
bold: false,
|
||||
height: PosTextSize.size1,
|
||||
width: PosTextSize.size1),
|
||||
);
|
||||
bytes += generator.hr(ch: " ");
|
||||
bytes += generator.text("NOMOR ANTRIAN",
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.center,
|
||||
bold: true,
|
||||
height: PosTextSize.size1,
|
||||
width: PosTextSize.size1),
|
||||
linesAfter: 1);
|
||||
if (isConsult == 'N') {
|
||||
bytes += generator.text("${layananName}",
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.center,
|
||||
bold: true,
|
||||
height: PosTextSize.size1,
|
||||
width: PosTextSize.size1),
|
||||
linesAfter: 1);
|
||||
}
|
||||
|
||||
if (isConsult == 'Y') {
|
||||
bytes += generator.text("${layananName} ${DoctorName}",
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.center,
|
||||
bold: true,
|
||||
height: PosTextSize.size1,
|
||||
width: PosTextSize.size1),
|
||||
linesAfter: 1);
|
||||
}
|
||||
|
||||
bytes += generator.text(antrianNumb,
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.center,
|
||||
bold: true,
|
||||
height: PosTextSize.size2,
|
||||
width: PosTextSize.size2),
|
||||
linesAfter: 1);
|
||||
// final List<dynamic> barData = "{A$antrianNumb".split("");
|
||||
// bytes += generator.barcode(Barcode.code128(barData),
|
||||
// height: 60, textPos: BarcodeText.none);
|
||||
// bytes += generator.hr(ch: " ");
|
||||
bytes += generator.text(
|
||||
"SIMPAN TIKET INI HINGGA",
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.center,
|
||||
bold: false,
|
||||
height: PosTextSize.size1,
|
||||
width: PosTextSize.size1),
|
||||
);
|
||||
bytes += generator.text(
|
||||
"SELURUH LAYANAN ANDA SELESAI",
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.center,
|
||||
bold: false,
|
||||
height: PosTextSize.size1,
|
||||
width: PosTextSize.size1),
|
||||
);
|
||||
bytes += generator.hr(ch: " ");
|
||||
if (name != "") {
|
||||
bytes += generator.text(
|
||||
name,
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.center,
|
||||
bold: false,
|
||||
height: PosTextSize.size1,
|
||||
width: PosTextSize.size1),
|
||||
);
|
||||
}
|
||||
// bytes += generator.text(
|
||||
// location,
|
||||
// linesAfter: 1,
|
||||
// styles: const PosStyles(
|
||||
// align: PosAlign.center,
|
||||
// bold: false,
|
||||
// height: PosTextSize.size1,
|
||||
// width: PosTextSize.size1),
|
||||
// );
|
||||
// bytes += generator.text(
|
||||
// "LAYANAN LABORATORIUM MEDIS",
|
||||
// styles: const PosStyles(
|
||||
// align: PosAlign.center,
|
||||
// bold: false,
|
||||
// height: PosTextSize.size1,
|
||||
// width: PosTextSize.size1),
|
||||
// );
|
||||
|
||||
// bytes +=
|
||||
// generator.qrcode("$StringqrCode$antrianNumb", size: QRSize.Size8);
|
||||
// bytes += generator.hr(ch: " ");
|
||||
|
||||
bytes += generator.text(
|
||||
footer,
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.center,
|
||||
bold: false,
|
||||
height: PosTextSize.size1,
|
||||
width: PosTextSize.size1),
|
||||
);
|
||||
|
||||
bytes += generator.cut();
|
||||
// final directory = await getApplicationDocumentsDirectory();
|
||||
|
||||
// final file =
|
||||
// File('${directory.path}/$antrianNumb-$date-$month-$year.txt');
|
||||
// await file.writeAsString(bytes.toString());
|
||||
|
||||
PrinterManager.instance.send(type: PrinterType.usb, bytes: bytes);
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
|
||||
Future printTest(
|
||||
{String boothId = "Test Booth",
|
||||
String header = "Test header",
|
||||
String date = "Test Date",
|
||||
String time = "Test time",
|
||||
String antrianNumb = "Test01",
|
||||
String name = "Test name",
|
||||
String location = "Test location",
|
||||
StringqrCode = "Test01",
|
||||
String isConsult = "N",
|
||||
String DoctorName = "-",
|
||||
String layananName = "Test Layanan",
|
||||
String footer = "Test footer"}) async {
|
||||
try {
|
||||
List<int> bytes = [];
|
||||
final dt = DateFormat('yyyy-MM-dd-HH-mm-ss').format(DateTime.now());
|
||||
final splited = dt.split('-');
|
||||
final date = splited[2].toString();
|
||||
final month = splited[1].toString();
|
||||
final year = splited[0].toString();
|
||||
final hour = splited[3].toString();
|
||||
final minute = splited[4].toString();
|
||||
|
||||
final displayDate = "$date/$month/$year - $hour.$minute WIB";
|
||||
|
||||
final profile = await CapabilityProfile.load(name: "XP-N160I");
|
||||
|
||||
var generator = Generator(PaperSize.mm80, profile);
|
||||
|
||||
bytes += generator.text(
|
||||
"Booth :$boothId",
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.right,
|
||||
bold: true,
|
||||
height: PosTextSize.size1,
|
||||
width: PosTextSize.size1),
|
||||
);
|
||||
bytes += generator.hr(ch: " ");
|
||||
|
||||
bytes += generator.text(
|
||||
header,
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.center,
|
||||
bold: true,
|
||||
height: PosTextSize.size1,
|
||||
width: PosTextSize.size1),
|
||||
);
|
||||
|
||||
bytes += generator.hr(ch: " ");
|
||||
bytes += generator.text(
|
||||
displayDate,
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.center,
|
||||
bold: false,
|
||||
height: PosTextSize.size1,
|
||||
width: PosTextSize.size1),
|
||||
);
|
||||
bytes += generator.hr(ch: " ");
|
||||
bytes += generator.text("NOMOR ANTRIAN",
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.center,
|
||||
bold: true,
|
||||
height: PosTextSize.size1,
|
||||
width: PosTextSize.size1),
|
||||
linesAfter: 1);
|
||||
if (isConsult == 'N') {
|
||||
bytes += generator.text("${layananName}",
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.center,
|
||||
bold: true,
|
||||
height: PosTextSize.size1,
|
||||
width: PosTextSize.size1),
|
||||
linesAfter: 1);
|
||||
}
|
||||
|
||||
if (isConsult == 'Y') {
|
||||
bytes += generator.text("${layananName} ${DoctorName}",
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.center,
|
||||
bold: true,
|
||||
height: PosTextSize.size1,
|
||||
width: PosTextSize.size1),
|
||||
linesAfter: 1);
|
||||
}
|
||||
|
||||
bytes += generator.text(antrianNumb,
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.center,
|
||||
bold: true,
|
||||
height: PosTextSize.size8,
|
||||
width: PosTextSize.size8),
|
||||
linesAfter: 1);
|
||||
final List<dynamic> barData = "{A$antrianNumb".split("");
|
||||
bytes += generator.barcode(Barcode.code128(barData),
|
||||
height: 60, textPos: BarcodeText.none);
|
||||
bytes += generator.hr(ch: " ");
|
||||
bytes += generator.text(
|
||||
"SIMPAN TIKET INI HINGGA",
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.center,
|
||||
bold: false,
|
||||
height: PosTextSize.size1,
|
||||
width: PosTextSize.size1),
|
||||
);
|
||||
bytes += generator.text(
|
||||
"SELURUH LAYANAN ANDA SELESAI",
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.center,
|
||||
bold: false,
|
||||
height: PosTextSize.size1,
|
||||
width: PosTextSize.size1),
|
||||
);
|
||||
bytes += generator.hr(ch: " ");
|
||||
if (name != "") {
|
||||
bytes += generator.text(
|
||||
name,
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.center,
|
||||
bold: false,
|
||||
height: PosTextSize.size1,
|
||||
width: PosTextSize.size1),
|
||||
);
|
||||
}
|
||||
bytes += generator.text(
|
||||
location,
|
||||
linesAfter: 1,
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.center,
|
||||
bold: false,
|
||||
height: PosTextSize.size1,
|
||||
width: PosTextSize.size1),
|
||||
);
|
||||
// bytes += generator.text(
|
||||
// "LAYANAN LABORATORIUM MEDIS",
|
||||
// styles: const PosStyles(
|
||||
// align: PosAlign.center,
|
||||
// bold: false,
|
||||
// height: PosTextSize.size1,
|
||||
// width: PosTextSize.size1),
|
||||
// );
|
||||
|
||||
// bytes +=
|
||||
// generator.qrcode("$StringqrCode$antrianNumb", size: QRSize.Size8);
|
||||
// bytes += generator.hr(ch: " ");
|
||||
|
||||
bytes += generator.text(
|
||||
footer,
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.center,
|
||||
bold: false,
|
||||
height: PosTextSize.size1,
|
||||
width: PosTextSize.size1),
|
||||
);
|
||||
|
||||
bytes += generator.cut();
|
||||
// final directory = await getApplicationDocumentsDirectory();
|
||||
|
||||
// final file =
|
||||
// File('${directory.path}/$antrianNumb-$date-$month-$year.txt');
|
||||
// await file.writeAsString(bytes.toString());
|
||||
|
||||
await PrinterManager.instance.send(type: PrinterType.usb, bytes: bytes);
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
98
lib/app/route.dart
Normal file
98
lib/app/route.dart
Normal file
@@ -0,0 +1,98 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:ticket_booth/screen/initial/initial_screen.dart';
|
||||
import 'package:ticket_booth/screen/queue/queue_screen.dart';
|
||||
import 'package:ticket_booth/screen/settings/setting_screen.dart';
|
||||
import 'package:ticket_booth/screen/splash/splash_screen.dart';
|
||||
|
||||
const splashScreen = "/splashScreen";
|
||||
const settingRoute = "/settingRoute";
|
||||
const displayRoute = "/displayScreenRoute";
|
||||
const queueRoute = "/queueRoute";
|
||||
const tvRoute = "/tvRoute";
|
||||
|
||||
class AppRoute {
|
||||
static Route<dynamic>? generateRoute(RouteSettings settings) {
|
||||
// initial screen
|
||||
if (settings.name == displayRoute) {
|
||||
return MaterialPageRoute(builder: (context) {
|
||||
return MediaQuery(
|
||||
data: MediaQuery.of(context).copyWith(
|
||||
textScaleFactor: 1.0,
|
||||
padding: const EdgeInsets.all(0),
|
||||
),
|
||||
child: const InitialScreen());
|
||||
});
|
||||
}
|
||||
if (settings.name == settingRoute) {
|
||||
return PageRouteBuilder(
|
||||
pageBuilder: (context, animation, secondaryAnimation) =>
|
||||
const SettingScreen(),
|
||||
transitionsBuilder: (context, animation, secondaryAnimation, child) {
|
||||
const begin = Offset(0.0, 1.0);
|
||||
const end = Offset.zero;
|
||||
const curve = Curves.ease;
|
||||
|
||||
var tween =
|
||||
Tween(begin: begin, end: end).chain(CurveTween(curve: curve));
|
||||
|
||||
return SlideTransition(
|
||||
position: animation.drive(tween),
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
//Queue screen
|
||||
if (settings.name == queueRoute) {
|
||||
return PageRouteBuilder(
|
||||
pageBuilder: (context, animation, secondaryAnimation) =>
|
||||
const QueueScreen(),
|
||||
transitionsBuilder: (context, animation, secondaryAnimation, child) {
|
||||
const begin = Offset(0.0, 1.0);
|
||||
const end = Offset.zero;
|
||||
const curve = Curves.ease;
|
||||
|
||||
var tween =
|
||||
Tween(begin: begin, end: end).chain(CurveTween(curve: curve));
|
||||
|
||||
return SlideTransition(
|
||||
position: animation.drive(tween),
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
// //Queue screen
|
||||
// if (settings.name == tvRoute) {
|
||||
// return PageRouteBuilder(
|
||||
// pageBuilder: (context, animation, secondaryAnimation) =>
|
||||
// const TvTemplate(),
|
||||
// transitionsBuilder: (context, animation, secondaryAnimation, child) {
|
||||
// const begin = Offset(0.0, 1.0);
|
||||
// const end = Offset.zero;
|
||||
// const curve = Curves.ease;
|
||||
|
||||
// var tween =
|
||||
// Tween(begin: begin, end: end).chain(CurveTween(curve: curve));
|
||||
|
||||
// return SlideTransition(
|
||||
// position: animation.drive(tween),
|
||||
// child: child,
|
||||
// );
|
||||
// },
|
||||
// );
|
||||
// }
|
||||
|
||||
//splash screen
|
||||
if (settings.name == splashScreen) {
|
||||
return MaterialPageRoute(builder: (context) {
|
||||
return MediaQuery(
|
||||
data: MediaQuery.of(context).copyWith(
|
||||
textScaleFactor: 1.0,
|
||||
padding: const EdgeInsets.all(0),
|
||||
),
|
||||
child: const SplashScreen());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
40
lib/main.dart
Normal file
40
lib/main.dart
Normal file
@@ -0,0 +1,40 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
import 'app/route.dart';
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
// Must add this line.
|
||||
await windowManager.ensureInitialized();
|
||||
|
||||
WindowOptions windowOptions = const WindowOptions(
|
||||
skipTaskbar: false,
|
||||
titleBarStyle: TitleBarStyle.hidden,
|
||||
);
|
||||
windowManager.waitUntilReadyToShow(windowOptions, () async {
|
||||
await windowManager.show();
|
||||
await WindowManager.instance.setFullScreen(true);
|
||||
await windowManager.focus();
|
||||
});
|
||||
runApp(const ProviderScope(child: MyApp()));
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({super.key});
|
||||
|
||||
// This widget is the root of your application.
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: 'Flutter Demo',
|
||||
debugShowCheckedModeBanner: false,
|
||||
theme: ThemeData(
|
||||
primarySwatch: Colors.blue,
|
||||
),
|
||||
initialRoute: splashScreen,
|
||||
onGenerateRoute: AppRoute.generateRoute,
|
||||
);
|
||||
}
|
||||
}
|
||||
18
lib/model/booth.dart
Normal file
18
lib/model/booth.dart
Normal file
@@ -0,0 +1,18 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
class Booth extends Equatable {
|
||||
int id;
|
||||
String name;
|
||||
String code;
|
||||
|
||||
Booth({required this.name, required this.id, required this.code});
|
||||
Booth.fromJson(Map<String, dynamic> json)
|
||||
: id = json['id'].runtimeType == int ? json['id'] : int.parse(json['id']),
|
||||
name = json['name'],
|
||||
code = json['code'];
|
||||
|
||||
Map<String, dynamic> toJson() => {'name': name, 'id': id, 'code': code};
|
||||
|
||||
@override
|
||||
List<Object?> get props => [id];
|
||||
}
|
||||
60
lib/model/branch_model.dart
Normal file
60
lib/model/branch_model.dart
Normal file
@@ -0,0 +1,60 @@
|
||||
class BranchModel {
|
||||
String? mBranchID;
|
||||
String? mBranchCode;
|
||||
String? mBranchCodeLab;
|
||||
String? mBranchName;
|
||||
String? mBranchAddress;
|
||||
String? mBranchIsActive;
|
||||
String? mBranchCreated;
|
||||
String? mBranchCreatedUserID;
|
||||
String? mBranchLastUpdated;
|
||||
String? mBranchLastUpdatedUserID;
|
||||
String? mBranchDeleted;
|
||||
String? mBranchDeletedUserID;
|
||||
|
||||
BranchModel(
|
||||
{this.mBranchID,
|
||||
this.mBranchCode,
|
||||
this.mBranchCodeLab,
|
||||
this.mBranchName,
|
||||
this.mBranchAddress,
|
||||
this.mBranchIsActive,
|
||||
this.mBranchCreated,
|
||||
this.mBranchCreatedUserID,
|
||||
this.mBranchLastUpdated,
|
||||
this.mBranchLastUpdatedUserID,
|
||||
this.mBranchDeleted,
|
||||
this.mBranchDeletedUserID});
|
||||
|
||||
BranchModel.fromJson(Map<String, dynamic> json) {
|
||||
mBranchID = json['M_BranchID'];
|
||||
mBranchCode = json['M_BranchCode'];
|
||||
mBranchCodeLab = json['M_BranchCodeLab'];
|
||||
mBranchName = json['M_BranchName'];
|
||||
mBranchAddress = json['M_BranchAddress'];
|
||||
mBranchIsActive = json['M_BranchIsActive'];
|
||||
mBranchCreated = json['M_BranchCreated'];
|
||||
mBranchCreatedUserID = json['M_BranchCreatedUserID'];
|
||||
mBranchLastUpdated = json['M_BranchLastUpdated'];
|
||||
mBranchLastUpdatedUserID = json['M_BranchLastUpdatedUserID'];
|
||||
mBranchDeleted = json['M_BranchDeleted'];
|
||||
mBranchDeletedUserID = json['M_BranchDeletedUserID'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['M_BranchID'] = this.mBranchID;
|
||||
data['M_BranchCode'] = this.mBranchCode;
|
||||
data['M_BranchCodeLab'] = this.mBranchCodeLab;
|
||||
data['M_BranchName'] = this.mBranchName;
|
||||
data['M_BranchAddress'] = this.mBranchAddress;
|
||||
data['M_BranchIsActive'] = this.mBranchIsActive;
|
||||
data['M_BranchCreated'] = this.mBranchCreated;
|
||||
data['M_BranchCreatedUserID'] = this.mBranchCreatedUserID;
|
||||
data['M_BranchLastUpdated'] = this.mBranchLastUpdated;
|
||||
data['M_BranchLastUpdatedUserID'] = this.mBranchLastUpdatedUserID;
|
||||
data['M_BranchDeleted'] = this.mBranchDeleted;
|
||||
data['M_BranchDeletedUserID'] = this.mBranchDeletedUserID;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
24
lib/model/cabapility_model.dart
Normal file
24
lib/model/cabapility_model.dart
Normal file
@@ -0,0 +1,24 @@
|
||||
class CapabilityProfileModel {
|
||||
String? key;
|
||||
String? vendor;
|
||||
String? model;
|
||||
String? description;
|
||||
|
||||
CapabilityProfileModel({this.key, this.vendor, this.model, this.description});
|
||||
|
||||
CapabilityProfileModel.fromJson(Map<String, dynamic> json) {
|
||||
key = json['key'];
|
||||
vendor = json['vendor'];
|
||||
model = json['model'];
|
||||
description = json['description'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['key'] = this.key;
|
||||
data['vendor'] = this.vendor;
|
||||
data['model'] = this.model;
|
||||
data['description'] = this.description;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
40
lib/model/layanan.dart
Normal file
40
lib/model/layanan.dart
Normal file
@@ -0,0 +1,40 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
class Layanan extends Equatable {
|
||||
int id;
|
||||
String name;
|
||||
String code;
|
||||
bool value;
|
||||
int priority;
|
||||
String isConsultDoctor;
|
||||
String doctorName;
|
||||
String? img;
|
||||
|
||||
Layanan(
|
||||
{required this.name,
|
||||
required this.id,
|
||||
required this.priority,
|
||||
required this.isConsultDoctor,
|
||||
this.doctorName = '',
|
||||
this.value = false,
|
||||
this.img,
|
||||
required this.code});
|
||||
Layanan.fromJson(Map<String, dynamic> json)
|
||||
: name = (json['serviceName'] as String).replaceAll("</br>", "\n"),
|
||||
code = json['serviceCode'].toString(),
|
||||
priority = int.parse(json['servicePriority']),
|
||||
value = false,
|
||||
isConsultDoctor = json['serviceIsConsultDoctor'].toString(),
|
||||
doctorName = json['serviceDoctorName'].toString(),
|
||||
id = int.parse(json['serviceID']);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'serviceName': name,
|
||||
'serviceID': id,
|
||||
'serviceID': code,
|
||||
'servicePriority': priority,
|
||||
'value': value
|
||||
};
|
||||
@override
|
||||
List<Object?> get props => [id];
|
||||
}
|
||||
46
lib/model/printer_device.dart
Normal file
46
lib/model/printer_device.dart
Normal file
@@ -0,0 +1,46 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
class PrinterDev extends Equatable {
|
||||
int id;
|
||||
String deviceName;
|
||||
String? address;
|
||||
String? port;
|
||||
String? vendorId;
|
||||
String? productId;
|
||||
bool? isBle;
|
||||
|
||||
bool? state;
|
||||
|
||||
PrinterDev(
|
||||
{required this.deviceName,
|
||||
required this.id,
|
||||
this.address,
|
||||
this.port,
|
||||
this.state = true,
|
||||
this.vendorId,
|
||||
this.productId,
|
||||
this.isBle = false});
|
||||
|
||||
PrinterDev.fromJson(Map<String, dynamic> json)
|
||||
: deviceName = json['deviceName'],
|
||||
id = json['id'],
|
||||
address = json['address'],
|
||||
port = json['port'],
|
||||
state = json['state'],
|
||||
vendorId = json['vendorId'],
|
||||
productId = json['productId'],
|
||||
isBle = json['this.isBle'];
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'deviceName': deviceName,
|
||||
'address': address,
|
||||
'port': port,
|
||||
'state': state,
|
||||
'vendorId': vendorId,
|
||||
'productId': productId,
|
||||
'isBle': isBle,
|
||||
'id': id
|
||||
};
|
||||
@override
|
||||
List<Object?> get props => [deviceName];
|
||||
}
|
||||
6
lib/provider/all_service_provider.dart
Normal file
6
lib/provider/all_service_provider.dart
Normal file
@@ -0,0 +1,6 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:ticket_booth/model/layanan.dart';
|
||||
|
||||
final allServiceProvider = StateProvider<List<Layanan>>(
|
||||
(ref) => List.empty(),
|
||||
);
|
||||
4
lib/provider/dio_provider.dart
Normal file
4
lib/provider/dio_provider.dart
Normal file
@@ -0,0 +1,4 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
final dioProvider = Provider<Dio>((ref) => Dio());
|
||||
153
lib/repository/base_repository.dart
Normal file
153
lib/repository/base_repository.dart
Normal file
@@ -0,0 +1,153 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
|
||||
abstract class BaseRepository {
|
||||
final Dio dio;
|
||||
BaseRepository({required this.dio});
|
||||
|
||||
Future<Map<String, dynamic>> post({
|
||||
required Map<String, dynamic> param,
|
||||
required String service,
|
||||
String? token,
|
||||
}) async {
|
||||
try {
|
||||
final response = await dio.post(
|
||||
// Constant.baseUrl + service,
|
||||
service,
|
||||
data: jsonEncode(param),
|
||||
options: Options(
|
||||
headers: token != null
|
||||
? {
|
||||
HttpHeaders.contentTypeHeader: "application/json",
|
||||
HttpHeaders.authorizationHeader: "Bearer $token",
|
||||
}
|
||||
: {
|
||||
HttpHeaders.contentTypeHeader: "application/json",
|
||||
},
|
||||
contentType: "application/json",
|
||||
),
|
||||
);
|
||||
if (response.statusCode != 200) {
|
||||
throw BaseRepositoryException(
|
||||
message: "Invalid Http Response ${response.statusCode}",
|
||||
);
|
||||
}
|
||||
Map<String, dynamic> jsonData = jsonDecode(response.data);
|
||||
if (jsonData["status"] != "OK") {
|
||||
throw BaseRepositoryException(
|
||||
message: jsonData["message"],
|
||||
);
|
||||
} else {
|
||||
return jsonData;
|
||||
}
|
||||
} on DioError catch (e) {
|
||||
throw BaseRepositoryException(message: e.message ?? '');
|
||||
} on SocketException catch (e) {
|
||||
throw BaseRepositoryException(message: e.message);
|
||||
} on BaseRepositoryException catch (e) {
|
||||
throw BaseRepositoryException(message: e.message);
|
||||
}
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> get({
|
||||
required String service,
|
||||
}) async {
|
||||
try {
|
||||
final response = await dio.get(
|
||||
// Constant.baseUrl + service,
|
||||
service,
|
||||
);
|
||||
if (response.statusCode != 200) {
|
||||
throw BaseRepositoryException(
|
||||
message: "Invalid Http Response ${response.statusCode}",
|
||||
);
|
||||
}
|
||||
// print(jsonDecode(response.data));
|
||||
Map<String, dynamic> jsonData = response.data;
|
||||
if (jsonData["status"] != "OK") {
|
||||
throw BaseRepositoryException(
|
||||
message: jsonData["message"],
|
||||
);
|
||||
} else {
|
||||
return jsonData;
|
||||
}
|
||||
} on DioError catch (e) {
|
||||
throw BaseRepositoryException(message: e.message ?? '');
|
||||
} on SocketException catch (e) {
|
||||
throw BaseRepositoryException(message: e.message);
|
||||
} on BaseRepositoryException catch (e) {
|
||||
throw BaseRepositoryException(message: e.message);
|
||||
}
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> getBooth({
|
||||
required String service,
|
||||
}) async {
|
||||
try {
|
||||
final response = await dio.get(
|
||||
// Constant.baseUrl + service,
|
||||
service,
|
||||
);
|
||||
if (response.statusCode != 200) {
|
||||
throw BaseRepositoryException(
|
||||
message: "Invalid Http Response ${response.statusCode}",
|
||||
);
|
||||
}
|
||||
// print(jsonDecode(response.data));
|
||||
Map<String, dynamic> jsonData = jsonDecode(response.data);
|
||||
if (jsonData["status"] != "OK") {
|
||||
throw BaseRepositoryException(
|
||||
message: jsonData["message"],
|
||||
);
|
||||
} else {
|
||||
return jsonData;
|
||||
}
|
||||
} on DioError catch (e) {
|
||||
throw BaseRepositoryException(message: e.message ?? '');
|
||||
} on SocketException catch (e) {
|
||||
throw BaseRepositoryException(message: e.message);
|
||||
} on BaseRepositoryException catch (e) {
|
||||
throw BaseRepositoryException(message: e.message);
|
||||
}
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> getService({
|
||||
required String service,
|
||||
}) async {
|
||||
try {
|
||||
final response = await dio.get(
|
||||
// Constant.baseUrl + service,
|
||||
service,
|
||||
);
|
||||
if (response.statusCode != 200) {
|
||||
throw BaseRepositoryException(
|
||||
message: "Invalid Http Response ${response.statusCode}",
|
||||
);
|
||||
}
|
||||
// print(jsonDecode(response.data));
|
||||
Map<String, dynamic> jsonData = jsonDecode(response.data);
|
||||
if (jsonData["status"] != "OK") {
|
||||
throw BaseRepositoryException(
|
||||
message: jsonData["message"],
|
||||
);
|
||||
} else {
|
||||
return jsonData;
|
||||
}
|
||||
} on DioError catch (e) {
|
||||
throw BaseRepositoryException(message: e.message ?? "");
|
||||
} on SocketException catch (e) {
|
||||
throw BaseRepositoryException(message: e.message);
|
||||
} on BaseRepositoryException catch (e) {
|
||||
throw BaseRepositoryException(message: e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BaseRepositoryException implements Exception {
|
||||
final String message;
|
||||
BaseRepositoryException({
|
||||
required this.message,
|
||||
});
|
||||
}
|
||||
36
lib/repository/booth_repository.dart
Normal file
36
lib/repository/booth_repository.dart
Normal file
@@ -0,0 +1,36 @@
|
||||
import 'package:ticket_booth/app/constant.dart';
|
||||
import 'package:ticket_booth/model/booth.dart';
|
||||
import 'package:ticket_booth/model/branch_model.dart';
|
||||
import 'package:ticket_booth/repository/base_repository.dart';
|
||||
|
||||
class BoothRepository extends BaseRepository {
|
||||
BoothRepository({required super.dio});
|
||||
Future<List<Booth>> getData({required String hostIP}) async {
|
||||
final url =
|
||||
"$hostIP/one-api/mockup/masterdata/one-md-ticket-dispenser/ticketbooth/index";
|
||||
print(url);
|
||||
// final url = "$hostIP/one-api/training/ticketbooth/index";
|
||||
final resp = await getBooth(service: url);
|
||||
final List<Booth> listBooth = List.empty(growable: true);
|
||||
resp['data']['records'].forEach((e) {
|
||||
final model = Booth.fromJson(e);
|
||||
listBooth.add(model);
|
||||
});
|
||||
return listBooth;
|
||||
}
|
||||
|
||||
Future<List<BranchModel>> getBranch({required String hostIP}) async {
|
||||
// https://devcpone.aplikasi.web.id/one-api/mockup/fo/antrian/AntrianByStationAndLocation/getbranch
|
||||
final url =
|
||||
"$hostIP/one-api/mockup/fo/antrian/AntrianByStationAndLocation/getbranch";
|
||||
print(url);
|
||||
// final url = "$hostIP/one-api/training/ticketbooth/index";
|
||||
final resp = await getBooth(service: url);
|
||||
final List<BranchModel> listBooth = List.empty(growable: true);
|
||||
resp['data']['records'].forEach((e) {
|
||||
final model = BranchModel.fromJson(e);
|
||||
listBooth.add(model);
|
||||
});
|
||||
return listBooth;
|
||||
}
|
||||
}
|
||||
24
lib/repository/queue_repository.dart
Normal file
24
lib/repository/queue_repository.dart
Normal file
@@ -0,0 +1,24 @@
|
||||
import '../app/constant.dart';
|
||||
import 'base_repository.dart';
|
||||
|
||||
class QueueRepository extends BaseRepository {
|
||||
QueueRepository({required super.dio});
|
||||
Future<Map<String, dynamic>> getData(
|
||||
{required service_id,
|
||||
required booth_id,
|
||||
required hostIp,
|
||||
required branchID}) async {
|
||||
// var url = "$hostIp/ticket/$service_id/$booth_id";
|
||||
var url = "$hostIp/one-api/antrian/ticket/getAntrian";
|
||||
// final resp = await get(
|
||||
// service: url,
|
||||
// );
|
||||
final resp = await post(param: {
|
||||
"service_id": service_id,
|
||||
"booth_id": booth_id,
|
||||
"branch_id": branchID
|
||||
}, service: url);
|
||||
// print(resp);
|
||||
return resp;
|
||||
}
|
||||
}
|
||||
24
lib/repository/service_repository.dart
Normal file
24
lib/repository/service_repository.dart
Normal file
@@ -0,0 +1,24 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:ticket_booth/model/layanan.dart';
|
||||
|
||||
import '../app/constant.dart';
|
||||
import 'base_repository.dart';
|
||||
|
||||
class ServiceRepository extends BaseRepository {
|
||||
ServiceRepository({required super.dio});
|
||||
Future<List<Layanan>> getData({required String hostIp}) async {
|
||||
final url =
|
||||
"$hostIp/one-api/mockup/masterdata/one-md-fo-service/service/listService";
|
||||
print(url);
|
||||
final resp = await getService(service: url);
|
||||
|
||||
final List<Layanan> listLayanan = List.empty(growable: true);
|
||||
|
||||
resp['data']['records'].forEach((e) {
|
||||
final model = Layanan.fromJson(e);
|
||||
listLayanan.add(model);
|
||||
});
|
||||
return listLayanan;
|
||||
}
|
||||
}
|
||||
242
lib/screen/initial/initial_screen.dart
Normal file
242
lib/screen/initial/initial_screen.dart
Normal file
@@ -0,0 +1,242 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:ticket_booth/app/constant.dart';
|
||||
import 'package:ticket_booth/app/print_ticket.dart';
|
||||
import 'package:ticket_booth/app/route.dart';
|
||||
import 'package:ticket_booth/provider/all_service_provider.dart';
|
||||
import 'package:ticket_booth/screen/initial/service_list_provider.dart';
|
||||
|
||||
import '../widgets/error_dialog.dart';
|
||||
|
||||
class InitialScreen extends HookConsumerWidget {
|
||||
const InitialScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final isLoading = useState(false);
|
||||
final errorMsg = useState('');
|
||||
final hostIp = useState('N');
|
||||
final testPrintDisabled = useState(true);
|
||||
Future getData() async {
|
||||
try {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
var raw_data = prefs.getString('tb-westerindo') ?? 'a';
|
||||
|
||||
if (raw_data != 'a') {
|
||||
var data = json.decode(raw_data);
|
||||
hostIp.value = data['hostIp'];
|
||||
testPrintDisabled.value = false;
|
||||
}
|
||||
} catch (e) {
|
||||
print(e);
|
||||
ErrorDialog(context, e.toString(), 'ERROR');
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() {
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
||||
await getData();
|
||||
if (hostIp.value != 'N') {
|
||||
ref.read(ServiceListProvider.notifier).list(hostIp: hostIp.value);
|
||||
}
|
||||
});
|
||||
// Navigator.of(context).pushNamed(queueRoute);
|
||||
// final timer = Timer(const Duration(seconds: 10), () {
|
||||
// });
|
||||
return () {
|
||||
// timer.cancel();
|
||||
};
|
||||
}, []);
|
||||
ref.listen(ServiceListProvider, (previous, next) {
|
||||
if (next is ServiceListStateLoading) {
|
||||
isLoading.value = true;
|
||||
} else if (next is ServiceListStateError) {
|
||||
errorMsg.value = next.message;
|
||||
ErrorDialog(context, errorMsg.value, 'ERROR');
|
||||
} else if (next is ServiceListStateDone) {
|
||||
ref.read(allServiceProvider.notifier).state = next.model;
|
||||
// print(ref.read(allServiceProvider));
|
||||
}
|
||||
});
|
||||
return Material(
|
||||
child: Container(
|
||||
height: Constant.getActualY(context: context, y: 982),
|
||||
width: Constant.getActualX(context: context, x: 1512),
|
||||
decoration: const BoxDecoration(
|
||||
image: DecorationImage(
|
||||
fit: BoxFit.cover,
|
||||
image: AssetImage("assets/images/new-bg-icon.png"))),
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: Constant.getActualY(context: context, y: 600),
|
||||
width: Constant.getActualX(context: context, x: 1512),
|
||||
),
|
||||
Text(
|
||||
Constant.versi,
|
||||
style: Constant.subTitle(context: context)
|
||||
.copyWith(fontStyle: FontStyle.italic, color: Colors.grey),
|
||||
),
|
||||
SizedBox(
|
||||
height: Constant.getActualY(context: context, y: 302),
|
||||
width: Constant.getActualX(context: context, x: 1512),
|
||||
child: Center(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
maximumSize: Size(
|
||||
Constant.getActualX(context: context, x: 350),
|
||||
Constant.getActualY(context: context, y: 100)),
|
||||
elevation: 5,
|
||||
side: const BorderSide(
|
||||
width: 0.5,
|
||||
color: Color.fromARGB(162, 204, 92, 146)),
|
||||
backgroundColor: Colors.white,
|
||||
foregroundColor: Colors.grey,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(50))),
|
||||
onPressed: () {
|
||||
Navigator.of(context).popAndPushNamed(settingRoute);
|
||||
},
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal:
|
||||
Constant.getActualX(context: context, x: 51),
|
||||
vertical:
|
||||
Constant.getActualY(context: context, y: 21)),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Image.asset(
|
||||
'assets/images/setting.png',
|
||||
height:
|
||||
Constant.getActualY(context: context, y: 57),
|
||||
width: Constant.getActualY(context: context, y: 57),
|
||||
),
|
||||
SizedBox(
|
||||
width: Constant.getActualX(context: context, x: 15),
|
||||
),
|
||||
Text(
|
||||
'Setting',
|
||||
style: Constant.title(context: context)
|
||||
.copyWith(color: Colors.black),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: Constant.getActualX(context: context, x: 166.5),
|
||||
),
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
elevation: 5,
|
||||
maximumSize: Size(
|
||||
Constant.getActualX(context: context, x: 350),
|
||||
Constant.getActualY(context: context, y: 100)),
|
||||
foregroundColor: Colors.grey,
|
||||
side: const BorderSide(
|
||||
width: 0.5,
|
||||
color: Color.fromARGB(162, 204, 92, 146)),
|
||||
backgroundColor: testPrintDisabled.value
|
||||
? Colors.grey
|
||||
: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(50))),
|
||||
onPressed: () {
|
||||
if (testPrintDisabled.value) {
|
||||
} else {
|
||||
PrintTicket().printTest();
|
||||
}
|
||||
},
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal:
|
||||
Constant.getActualX(context: context, x: 51),
|
||||
vertical:
|
||||
Constant.getActualY(context: context, y: 21)),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Image.asset(
|
||||
'assets/images/next.png',
|
||||
height:
|
||||
Constant.getActualY(context: context, y: 57),
|
||||
width: Constant.getActualY(context: context, y: 57),
|
||||
),
|
||||
SizedBox(
|
||||
width: Constant.getActualX(context: context, x: 15),
|
||||
),
|
||||
Text(
|
||||
'Test Print',
|
||||
style: Constant.title(context: context).copyWith(
|
||||
color: testPrintDisabled.value
|
||||
? Colors.white
|
||||
: Colors.black),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: Constant.getActualX(context: context, x: 166.5),
|
||||
),
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
elevation: 5,
|
||||
maximumSize: Size(
|
||||
Constant.getActualX(context: context, x: 350),
|
||||
Constant.getActualY(context: context, y: 100)),
|
||||
foregroundColor: Colors.grey,
|
||||
side: const BorderSide(
|
||||
width: 0.5,
|
||||
color: Color.fromARGB(162, 204, 92, 146)),
|
||||
backgroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(50))),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pushNamed(queueRoute);
|
||||
},
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal:
|
||||
Constant.getActualX(context: context, x: 51),
|
||||
vertical:
|
||||
Constant.getActualY(context: context, y: 21)),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Image.asset(
|
||||
'assets/images/next.png',
|
||||
height:
|
||||
Constant.getActualY(context: context, y: 57),
|
||||
width: Constant.getActualY(context: context, y: 57),
|
||||
),
|
||||
SizedBox(
|
||||
width: Constant.getActualX(context: context, x: 15),
|
||||
),
|
||||
Text(
|
||||
'Next',
|
||||
style: Constant.title(context: context)
|
||||
.copyWith(color: Colors.black),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
63
lib/screen/initial/service_list_provider.dart
Normal file
63
lib/screen/initial/service_list_provider.dart
Normal file
@@ -0,0 +1,63 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:ticket_booth/model/layanan.dart';
|
||||
import 'package:ticket_booth/provider/dio_provider.dart';
|
||||
import 'package:ticket_booth/repository/base_repository.dart';
|
||||
import 'package:ticket_booth/repository/service_repository.dart';
|
||||
|
||||
abstract class ServiceListState extends Equatable {
|
||||
final DateTime date;
|
||||
const ServiceListState(this.date);
|
||||
@override
|
||||
List<Object?> get props => [date];
|
||||
}
|
||||
|
||||
class ServiceListStateInit extends ServiceListState {
|
||||
ServiceListStateInit() : super(DateTime.now());
|
||||
}
|
||||
|
||||
class ServiceListStateLoading extends ServiceListState {
|
||||
ServiceListStateLoading() : super(DateTime.now());
|
||||
}
|
||||
|
||||
class ServiceListStateError extends ServiceListState {
|
||||
final String message;
|
||||
ServiceListStateError({
|
||||
required this.message,
|
||||
}) : super(DateTime.now());
|
||||
}
|
||||
|
||||
class ServiceListStateDone extends ServiceListState {
|
||||
final List<Layanan> model;
|
||||
ServiceListStateDone({
|
||||
required this.model,
|
||||
}) : super(DateTime.now());
|
||||
}
|
||||
|
||||
//notifier
|
||||
class ServiceListNotifier extends StateNotifier<ServiceListState> {
|
||||
final Ref ref;
|
||||
ServiceListNotifier({
|
||||
required this.ref,
|
||||
}) : super(ServiceListStateInit());
|
||||
|
||||
void list({required hostIp}) async {
|
||||
try {
|
||||
state = ServiceListStateLoading();
|
||||
final dio = ref.read(dioProvider);
|
||||
final resp = await ServiceRepository(dio: dio).getData(hostIp: hostIp);
|
||||
state = ServiceListStateDone(model: resp);
|
||||
} catch (e) {
|
||||
if (e is BaseRepositoryException) {
|
||||
state = ServiceListStateError(message: e.message);
|
||||
} else {
|
||||
state = ServiceListStateError(message: e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//provider
|
||||
final ServiceListProvider =
|
||||
StateNotifierProvider<ServiceListNotifier, ServiceListState>(
|
||||
(ref) => ServiceListNotifier(ref: ref));
|
||||
71
lib/screen/queue/queue_provider.dart
Normal file
71
lib/screen/queue/queue_provider.dart
Normal file
@@ -0,0 +1,71 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:ticket_booth/provider/dio_provider.dart';
|
||||
import 'package:ticket_booth/repository/base_repository.dart';
|
||||
|
||||
import 'package:ticket_booth/repository/queue_repository.dart';
|
||||
|
||||
abstract class QueueState extends Equatable {
|
||||
final DateTime date;
|
||||
const QueueState(this.date);
|
||||
@override
|
||||
List<Object?> get props => [date];
|
||||
}
|
||||
|
||||
class QueueStateInit extends QueueState {
|
||||
QueueStateInit() : super(DateTime.now());
|
||||
}
|
||||
|
||||
class QueueStateLoading extends QueueState {
|
||||
QueueStateLoading() : super(DateTime.now());
|
||||
}
|
||||
|
||||
class QueueStateError extends QueueState {
|
||||
final String message;
|
||||
QueueStateError({
|
||||
required this.message,
|
||||
}) : super(DateTime.now());
|
||||
}
|
||||
|
||||
class QueueStateDone extends QueueState {
|
||||
final Map<String, dynamic> response;
|
||||
QueueStateDone({
|
||||
required this.response,
|
||||
}) : super(DateTime.now());
|
||||
}
|
||||
|
||||
//notifier
|
||||
class QueueNotifier extends StateNotifier<QueueState> {
|
||||
final Ref ref;
|
||||
QueueNotifier({
|
||||
required this.ref,
|
||||
}) : super(QueueStateInit());
|
||||
|
||||
void get(
|
||||
{required service_id,
|
||||
required booth_id,
|
||||
required hostIp,
|
||||
required branchID}) async {
|
||||
try {
|
||||
state = QueueStateLoading();
|
||||
final dio = ref.read(dioProvider);
|
||||
final resp = await QueueRepository(dio: dio).getData(
|
||||
service_id: service_id,
|
||||
booth_id: booth_id,
|
||||
hostIp: hostIp,
|
||||
branchID: branchID);
|
||||
state = QueueStateDone(response: resp);
|
||||
} catch (e) {
|
||||
if (e is BaseRepositoryException) {
|
||||
state = QueueStateError(message: e.message);
|
||||
} else {
|
||||
state = QueueStateError(message: e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//provider
|
||||
final QueueProvider = StateNotifierProvider<QueueNotifier, QueueState>(
|
||||
(ref) => QueueNotifier(ref: ref));
|
||||
543
lib/screen/queue/queue_screen.dart
Normal file
543
lib/screen/queue/queue_screen.dart
Normal file
@@ -0,0 +1,543 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:flutter_pos_printer_platform_image_3/flutter_pos_printer_platform_image_3.dart';
|
||||
import 'package:flutter_spinkit/flutter_spinkit.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:ticket_booth/app/print_ticket.dart';
|
||||
import 'package:ticket_booth/app/route.dart';
|
||||
import 'package:ticket_booth/model/booth.dart';
|
||||
import 'package:ticket_booth/model/branch_model.dart';
|
||||
import 'package:ticket_booth/model/printer_device.dart';
|
||||
import 'package:ticket_booth/provider/all_service_provider.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:ticket_booth/screen/queue/queue_provider.dart';
|
||||
import 'package:ticket_booth/screen/widgets/dialog_print.dart';
|
||||
import 'package:ticket_booth/screen/widgets/pasien_online_dialog.dart';
|
||||
|
||||
import '../../app/constant.dart';
|
||||
import '../../model/cabapility_model.dart';
|
||||
import '../../model/layanan.dart';
|
||||
import '../widgets/error_dialog.dart';
|
||||
|
||||
class QueueScreen extends HookConsumerWidget {
|
||||
const QueueScreen({super.key});
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final isLoading = useState(true);
|
||||
final lyn = useState<List<Layanan>>(List.empty());
|
||||
final activeLayanan = useState<List<dynamic>>(List.empty());
|
||||
final selectedPrinter =
|
||||
useState<PrinterDev>(PrinterDev(deviceName: "a", id: 0));
|
||||
final selectedBooth =
|
||||
useState<Booth>(Booth(name: 'B001', id: 0, code: 'a'));
|
||||
final hostIp = useState('N');
|
||||
final hostqR = useState('N');
|
||||
final headerDisplay = useState('N');
|
||||
final headerTicket = useState('N');
|
||||
final footerTicket = useState('N');
|
||||
final footerDisplay = useState('N');
|
||||
final isLoadingPrint = useState(false);
|
||||
final errorMsg = useState('');
|
||||
final splitedHost = useState('');
|
||||
final tapAnimation = useState(0);
|
||||
final selectedCapabilityProfile = useState(CapabilityProfileModel());
|
||||
final selectedBranch = useState<BranchModel?>(null);
|
||||
|
||||
final selectedLayanan = useState<Layanan>(Layanan(
|
||||
name: 'x', id: 0, priority: 0, isConsultDoctor: 'n', code: '0'));
|
||||
final getNumberLoading = useState(false);
|
||||
final btnSize = useState<Map<String, dynamic>>({'name': 'n'});
|
||||
Future getData() async {
|
||||
try {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
var raw_data = prefs.getString('tb-westerindo') ?? 'a';
|
||||
|
||||
if (raw_data != 'a') {
|
||||
List<Layanan> allLayanan = ref.read(allServiceProvider);
|
||||
var data = json.decode(raw_data);
|
||||
hostIp.value = data['hostIp'];
|
||||
hostqR.value = data['hostQrIp'];
|
||||
headerDisplay.value = data['headerDisplay'];
|
||||
headerTicket.value = data['headerTicket'];
|
||||
footerTicket.value = data['footerTicket'];
|
||||
footerDisplay.value = data['footerDisplay'];
|
||||
selectedPrinter.value = PrinterDev.fromJson(data['selectedPrinter']);
|
||||
selectedBooth.value = Booth.fromJson(data['selectedBooth']);
|
||||
selectedBranch.value = BranchModel.fromJson(data['selectedBranch']);
|
||||
selectedCapabilityProfile.value =
|
||||
CapabilityProfileModel.fromJson(data['selectedCapability']);
|
||||
activeLayanan.value = data['activeLayanan'];
|
||||
btnSize.value = data['buttonSize'];
|
||||
print(btnSize.value);
|
||||
var sp = hostIp.value.split(':');
|
||||
splitedHost.value = sp[0] + ":" + sp[1];
|
||||
List<Layanan> actvLayanan = [];
|
||||
activeLayanan.value.forEach((e) {
|
||||
allLayanan.forEach((f) {
|
||||
if (e == f.id) {
|
||||
actvLayanan.add(f);
|
||||
}
|
||||
});
|
||||
});
|
||||
actvLayanan.sort(((a, b) => a.priority.compareTo(b.priority)));
|
||||
lyn.value = actvLayanan;
|
||||
} else {
|
||||
Navigator.of(context).popAndPushNamed(settingRoute);
|
||||
}
|
||||
isLoading.value = false;
|
||||
} catch (e) {
|
||||
print(e);
|
||||
ErrorDialog(context, e.toString(), 'ERROR');
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
getDataApi(e) {
|
||||
ref.read(QueueProvider.notifier).get(
|
||||
branchID: selectedBranch.value?.mBranchID,
|
||||
booth_id: selectedBooth.value.id,
|
||||
service_id: e.id,
|
||||
hostIp: hostIp.value);
|
||||
}
|
||||
|
||||
printTicketDialog(String number) {
|
||||
try {
|
||||
// DialogPrint(context, number);
|
||||
PrintTicket().printTicket(
|
||||
boothId: selectedBooth.value.name.toString(),
|
||||
header: headerTicket.value,
|
||||
date: 'a',
|
||||
time: 'a',
|
||||
antrianNumb: number,
|
||||
name: '',
|
||||
StringqrCode: hostqR.value,
|
||||
location: '',
|
||||
footer: footerTicket.value,
|
||||
DoctorName: selectedLayanan.value.doctorName,
|
||||
isConsult: selectedLayanan.value.isConsultDoctor,
|
||||
capabilityName: selectedCapabilityProfile.value.key ?? "default",
|
||||
layananName: selectedLayanan.value.name);
|
||||
} catch (e) {
|
||||
errorMsg.value = e.toString();
|
||||
ErrorDialog(context, errorMsg.value, 'ERROR');
|
||||
}
|
||||
}
|
||||
|
||||
ref.listen(QueueProvider, (previous, next) {
|
||||
if (next is QueueStateLoading) {
|
||||
isLoadingPrint.value = true;
|
||||
} else if (next is QueueStateError) {
|
||||
errorMsg.value = next.message;
|
||||
ErrorDialog(context, errorMsg.value, 'ERROR');
|
||||
getNumberLoading.value = false;
|
||||
} else if (next is QueueStateDone) {
|
||||
String number = next.response['data']['number'].toString();
|
||||
String location = next.response['data']['location'].toString();
|
||||
// print(location);
|
||||
DialogPrint(
|
||||
context, number, printTicketDialog, selectedLayanan.value.name);
|
||||
try {
|
||||
// PrintTicket().printTicket(
|
||||
// boothId: selectedBooth.value.name.toString(),
|
||||
// header: headerTicket.value,
|
||||
// date: 'a',
|
||||
// time: 'a',
|
||||
// antrianNumb: number,
|
||||
// name: '',
|
||||
// StringqrCode: hostqR.value,
|
||||
// location: location,
|
||||
// footer: footerTicket.value,
|
||||
// DoctorName: selectedLayanan.value.doctorName,
|
||||
// isConsult: selectedLayanan.value.isConsultDoctor,
|
||||
// capabilityName: selectedCapabilityProfile.value.key ?? "default",
|
||||
// layananName: selectedLayanan.value.name);
|
||||
} catch (e) {
|
||||
errorMsg.value = e.toString();
|
||||
ErrorDialog(context, errorMsg.value, 'ERROR');
|
||||
}
|
||||
Timer(Duration(seconds: 2), () {
|
||||
getNumberLoading.value = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() {
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
||||
getData();
|
||||
});
|
||||
|
||||
return () {};
|
||||
}, []);
|
||||
|
||||
return Material(
|
||||
child: Container(
|
||||
height: Constant.getActualY(context: context, y: 982),
|
||||
width: Constant.getActualX(context: context, x: 1512),
|
||||
decoration: const BoxDecoration(
|
||||
image: DecorationImage(
|
||||
fit: BoxFit.cover,
|
||||
image: AssetImage("assets/images/new-bg.png"))),
|
||||
child: isLoading.value
|
||||
? const SpinKitCubeGrid(
|
||||
color: Colors.blue,
|
||||
)
|
||||
: Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: Constant.getActualY(context: context, y: 150),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
// Navigator.of(context).popAndPushNamed(displayRoute);
|
||||
},
|
||||
child: Container(
|
||||
width:
|
||||
Constant.getActualX(context: context, x: 150),
|
||||
height:
|
||||
Constant.getActualY(context: context, y: 150),
|
||||
margin: EdgeInsets.only(
|
||||
left: Constant.getActualX(
|
||||
context: context, x: 30)),
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Image.network(
|
||||
'${splitedHost.value.toString()}/one-media/one-queue/logo-westerindo.png',
|
||||
loadingBuilder:
|
||||
(context, child, loadingProgress) {
|
||||
if (loadingProgress == null) return child;
|
||||
return const CircularProgressIndicator();
|
||||
},
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
return Image.asset(
|
||||
'assets/images/logo-westerindo.png',
|
||||
);
|
||||
},
|
||||
)),
|
||||
),
|
||||
Text(
|
||||
headerDisplay.value,
|
||||
style: Constant.title(context: context),
|
||||
),
|
||||
Container(
|
||||
alignment: Alignment.center,
|
||||
margin: EdgeInsets.only(
|
||||
right:
|
||||
Constant.getActualX(context: context, x: 30)),
|
||||
padding: const EdgeInsets.all(20),
|
||||
width: Constant.getActualX(context: context, x: 150),
|
||||
height: Constant.getActualY(context: context, y: 150),
|
||||
child: Text(selectedBranch.value?.mBranchName ?? "",
|
||||
textAlign: TextAlign.center,
|
||||
style: Constant.label(context: context).copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: Constant.getActualY(context: context, y: 682),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: Constant.getActualX(
|
||||
context: context,
|
||||
x: double.parse(btnSize.value['rightLeftWidth'])),
|
||||
// color: Colors.green,
|
||||
),
|
||||
Container(
|
||||
// color: Colors.red,
|
||||
width: Constant.getActualX(
|
||||
context: context,
|
||||
x: double.parse(btnSize.value['centerWidth'])),
|
||||
child: GridView.count(
|
||||
crossAxisCount: int.parse(btnSize.value['column']),
|
||||
childAspectRatio:
|
||||
double.parse(btnSize.value['childAspectRatio']),
|
||||
mainAxisSpacing: Constant.getActualY(
|
||||
context: context,
|
||||
y: double.parse(
|
||||
btnSize.value['mainAxisSpacing'])),
|
||||
crossAxisSpacing: Constant.getActualY(
|
||||
context: context,
|
||||
y: double.parse(
|
||||
btnSize.value['crossAxisSpacing'])),
|
||||
children: lyn.value
|
||||
.map((e) => ButtonLayanan(
|
||||
getNumberLoading,
|
||||
tapAnimation,
|
||||
e,
|
||||
selectedLayanan,
|
||||
context,
|
||||
getDataApi,
|
||||
btnSize,
|
||||
splitedHost))
|
||||
.toList()),
|
||||
),
|
||||
Container(
|
||||
width: Constant.getActualX(
|
||||
context: context,
|
||||
x: double.parse(btnSize.value['rightLeftWidth'])),
|
||||
// color: Colors.green,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: Constant.getActualY(context: context, y: 150),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
width: Constant.getActualX(context: context, x: 200),
|
||||
margin: EdgeInsets.only(
|
||||
left:
|
||||
Constant.getActualX(context: context, x: 30)),
|
||||
padding: EdgeInsets.all(20),
|
||||
child: Center(
|
||||
child: Text(
|
||||
selectedBooth.value.name,
|
||||
style: Constant.subTitle(context: context),
|
||||
),
|
||||
)),
|
||||
Text(
|
||||
footerDisplay.value,
|
||||
style: Constant.title(context: context).copyWith(
|
||||
fontStyle: FontStyle.italic,
|
||||
fontWeight: FontWeight.normal),
|
||||
),
|
||||
const DisplayClock(),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
Container ButtonLayanan(
|
||||
ValueNotifier<bool> getNumberLoading,
|
||||
ValueNotifier<int> tapAnimation,
|
||||
Layanan e,
|
||||
ValueNotifier<Layanan> selectedLayanan,
|
||||
BuildContext context,
|
||||
Null getDataApi(dynamic e),
|
||||
ValueNotifier<Map<String, dynamic>> btnSize,
|
||||
ValueNotifier<String> splitedHost) {
|
||||
return Container(
|
||||
// color: Colors.amber,
|
||||
child: Center(
|
||||
child: InkWell(
|
||||
onTap: getNumberLoading.value
|
||||
? null
|
||||
: () {
|
||||
// DialogPrint(context, 'A0001');
|
||||
// PasienOnlineDialog(context);
|
||||
// return;
|
||||
tapAnimation.value = e.id;
|
||||
selectedLayanan.value = e;
|
||||
getNumberLoading.value = true;
|
||||
if (e.name == "Pasien Online" && e.id == 5) {
|
||||
} else {
|
||||
getDataApi(e);
|
||||
}
|
||||
|
||||
Timer(Duration(milliseconds: 250), () {
|
||||
tapAnimation.value = 0;
|
||||
});
|
||||
Timer(Duration(seconds: 2), () {
|
||||
getNumberLoading.value = false;
|
||||
print("tidak loading");
|
||||
});
|
||||
},
|
||||
child: Ink(
|
||||
// color: Colors.green,
|
||||
height: Constant.getActualY(
|
||||
context: context, y: double.parse(btnSize.value['btnHeight'])),
|
||||
width: Constant.getActualX(
|
||||
context: context, x: double.parse(btnSize.value['btnWidth'])),
|
||||
child: Stack(
|
||||
alignment: AlignmentDirectional.center,
|
||||
children: [
|
||||
Image.network(
|
||||
'${splitedHost.value.toString()}/one-media/one-queue/${e.code}.png',
|
||||
fit: BoxFit.cover,
|
||||
color:
|
||||
e.id == tapAnimation.value ? Colors.grey : Colors.white,
|
||||
// color: Colors.white
|
||||
// .withOpacity(e.id ==
|
||||
// tapAnimation.value
|
||||
// ? 0.7
|
||||
// : 1),
|
||||
colorBlendMode: BlendMode.modulate,
|
||||
height: Constant.getActualY(
|
||||
context: context,
|
||||
y: e.id == tapAnimation.value ? 310 : 400),
|
||||
width: Constant.getActualX(
|
||||
context: context,
|
||||
x: e.id == tapAnimation.value ? 280 : 300),
|
||||
loadingBuilder: (context, child, loadingProgress) {
|
||||
if (loadingProgress == null) return child;
|
||||
return const CircularProgressIndicator();
|
||||
},
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
return Image.asset(
|
||||
'assets/images/cpone-default.png',
|
||||
fit: BoxFit.cover,
|
||||
color: e.id == tapAnimation.value
|
||||
? Colors.grey
|
||||
: Colors.white,
|
||||
colorBlendMode: BlendMode.modulate,
|
||||
height: Constant.getActualY(
|
||||
context: context,
|
||||
y: e.id == tapAnimation.value ? 310 : 400),
|
||||
width: Constant.getActualX(
|
||||
context: context,
|
||||
x: e.id == tapAnimation.value ? 280 : 300),
|
||||
);
|
||||
},
|
||||
),
|
||||
Positioned(
|
||||
top: 1,
|
||||
child: Container(
|
||||
margin: EdgeInsets.only(
|
||||
top: Constant.getActualY(context: context, y: 10)),
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal:
|
||||
Constant.getActualX(context: context, x: 10)),
|
||||
// color: Colors.red,
|
||||
height: Constant.getActualY(context: context, y: 110),
|
||||
width: Constant.getActualX(context: context, x: 280),
|
||||
child: Center(
|
||||
child: e.isConsultDoctor == 'N'
|
||||
? Text(
|
||||
e.name,
|
||||
maxLines: 2,
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Constant.layananTitle(context: context)
|
||||
.copyWith(color: Colors.white),
|
||||
)
|
||||
: Column(
|
||||
children: [
|
||||
Text(
|
||||
e.name,
|
||||
maxLines: 2,
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Constant.layananTitle(context: context)
|
||||
.copyWith(color: Colors.white),
|
||||
),
|
||||
Text(
|
||||
e.doctorName,
|
||||
maxLines: 1,
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Constant.layananTitle(context: context)
|
||||
.copyWith(color: Colors.white),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DisplayClock extends HookConsumerWidget {
|
||||
const DisplayClock({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final strDate = useState("");
|
||||
final strMonth = useState("");
|
||||
final strYear = useState("");
|
||||
final strHour = useState("");
|
||||
final strMinute = useState("");
|
||||
final strSeconds = useState("");
|
||||
|
||||
useEffect(() {
|
||||
final tmr = Timer.periodic(const Duration(seconds: 1), (timer) {
|
||||
final dt = DateFormat('yyyy-MMM-dd-HH-mm-ss').format(DateTime.now());
|
||||
final splited = dt.split('-');
|
||||
final date = splited[2].toString();
|
||||
final month = splited[1].toString();
|
||||
final year = splited[0].toString();
|
||||
final hour = splited[3].toString();
|
||||
final minute = splited[4].toString();
|
||||
final second = splited[5].toString();
|
||||
if (date != strDate.value) strDate.value = date;
|
||||
if (month != strMonth.value) strMonth.value = month;
|
||||
if (year != strYear.value) strYear.value = year;
|
||||
if (hour != strHour.value) strHour.value = hour;
|
||||
if (minute != strMinute.value) strMinute.value = minute;
|
||||
if (second != strSeconds.value) strSeconds.value = second;
|
||||
});
|
||||
return () {
|
||||
tmr.cancel();
|
||||
};
|
||||
}, []);
|
||||
return Container(
|
||||
width: Constant.getActualX(context: context, x: 200),
|
||||
margin: EdgeInsets.only(
|
||||
right: Constant.getActualX(context: context, x: 30)),
|
||||
padding: EdgeInsets.all(0),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
strDate.value,
|
||||
style: Constant.title(context: context).copyWith(
|
||||
color: Colors.blue[900],
|
||||
fontSize: Constant.getActualX(context: context, x: 64)),
|
||||
),
|
||||
SizedBox(
|
||||
width: Constant.getActualX(context: context, x: 10),
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
strMonth.value,
|
||||
style: Constant.subTitle(context: context).copyWith(
|
||||
color: Colors.blue[900],
|
||||
fontSize:
|
||||
Constant.getActualX(context: context, x: 25)),
|
||||
),
|
||||
Text(
|
||||
strYear.value,
|
||||
style: Constant.subTitle(context: context).copyWith(
|
||||
color: Colors.blue[900],
|
||||
fontSize:
|
||||
Constant.getActualX(context: context, x: 25)),
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
Text(
|
||||
'${strHour.value} . ${strMinute.value} . ${strSeconds.value}',
|
||||
style: Constant.subTitle(context: context).copyWith(
|
||||
color: Colors.blue[900],
|
||||
fontSize: Constant.getActualX(context: context, x: 25)),
|
||||
)
|
||||
],
|
||||
));
|
||||
}
|
||||
}
|
||||
63
lib/screen/settings/booth_list_provider.dart
Normal file
63
lib/screen/settings/booth_list_provider.dart
Normal file
@@ -0,0 +1,63 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:ticket_booth/model/booth.dart';
|
||||
import 'package:ticket_booth/provider/dio_provider.dart';
|
||||
import 'package:ticket_booth/repository/base_repository.dart';
|
||||
import 'package:ticket_booth/repository/booth_repository.dart';
|
||||
|
||||
abstract class BoothListState extends Equatable {
|
||||
final DateTime date;
|
||||
const BoothListState(this.date);
|
||||
@override
|
||||
List<Object?> get props => [date];
|
||||
}
|
||||
|
||||
class BoothListStateInit extends BoothListState {
|
||||
BoothListStateInit() : super(DateTime.now());
|
||||
}
|
||||
|
||||
class BoothListStateLoading extends BoothListState {
|
||||
BoothListStateLoading() : super(DateTime.now());
|
||||
}
|
||||
|
||||
class BoothListStateError extends BoothListState {
|
||||
final String message;
|
||||
BoothListStateError({
|
||||
required this.message,
|
||||
}) : super(DateTime.now());
|
||||
}
|
||||
|
||||
class BoothListStateDone extends BoothListState {
|
||||
final List<Booth> model;
|
||||
BoothListStateDone({
|
||||
required this.model,
|
||||
}) : super(DateTime.now());
|
||||
}
|
||||
|
||||
//notifier
|
||||
class BoothListNotifier extends StateNotifier<BoothListState> {
|
||||
final Ref ref;
|
||||
BoothListNotifier({
|
||||
required this.ref,
|
||||
}) : super(BoothListStateInit());
|
||||
|
||||
void list({required hostIp}) async {
|
||||
try {
|
||||
state = BoothListStateLoading();
|
||||
final dio = ref.read(dioProvider);
|
||||
final resp = await BoothRepository(dio: dio).getData(hostIP: hostIp);
|
||||
state = BoothListStateDone(model: resp);
|
||||
} catch (e) {
|
||||
if (e is BaseRepositoryException) {
|
||||
state = BoothListStateError(message: e.message);
|
||||
} else {
|
||||
state = BoothListStateError(message: e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//provider
|
||||
final BoothListProvider =
|
||||
StateNotifierProvider<BoothListNotifier, BoothListState>(
|
||||
(ref) => BoothListNotifier(ref: ref));
|
||||
63
lib/screen/settings/branch_list_provider.dart
Normal file
63
lib/screen/settings/branch_list_provider.dart
Normal file
@@ -0,0 +1,63 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:ticket_booth/model/branch_model.dart';
|
||||
import 'package:ticket_booth/provider/dio_provider.dart';
|
||||
import 'package:ticket_booth/repository/base_repository.dart';
|
||||
import 'package:ticket_booth/repository/booth_repository.dart';
|
||||
|
||||
abstract class BranchListState extends Equatable {
|
||||
final DateTime date;
|
||||
const BranchListState(this.date);
|
||||
@override
|
||||
List<Object?> get props => [date];
|
||||
}
|
||||
|
||||
class BranchListStateInit extends BranchListState {
|
||||
BranchListStateInit() : super(DateTime.now());
|
||||
}
|
||||
|
||||
class BranchListStateLoading extends BranchListState {
|
||||
BranchListStateLoading() : super(DateTime.now());
|
||||
}
|
||||
|
||||
class BranchListStateError extends BranchListState {
|
||||
final String message;
|
||||
BranchListStateError({
|
||||
required this.message,
|
||||
}) : super(DateTime.now());
|
||||
}
|
||||
|
||||
class BranchListStateDone extends BranchListState {
|
||||
final List<BranchModel> model;
|
||||
BranchListStateDone({
|
||||
required this.model,
|
||||
}) : super(DateTime.now());
|
||||
}
|
||||
|
||||
//notifier
|
||||
class BranchListNotifier extends StateNotifier<BranchListState> {
|
||||
final Ref ref;
|
||||
BranchListNotifier({
|
||||
required this.ref,
|
||||
}) : super(BranchListStateInit());
|
||||
|
||||
void list({required hostIp}) async {
|
||||
try {
|
||||
state = BranchListStateLoading();
|
||||
final dio = ref.read(dioProvider);
|
||||
final resp = await BoothRepository(dio: dio).getBranch(hostIP: hostIp);
|
||||
state = BranchListStateDone(model: resp);
|
||||
} catch (e) {
|
||||
if (e is BaseRepositoryException) {
|
||||
state = BranchListStateError(message: e.message);
|
||||
} else {
|
||||
state = BranchListStateError(message: e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//provider
|
||||
final BranchListProvider =
|
||||
StateNotifierProvider<BranchListNotifier, BranchListState>(
|
||||
(ref) => BranchListNotifier(ref: ref));
|
||||
496
lib/screen/settings/form_input.dart
Normal file
496
lib/screen/settings/form_input.dart
Normal file
@@ -0,0 +1,496 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:clippy_flutter/ticket.dart';
|
||||
import 'package:esc_pos_utils/esc_pos_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:flutter_pos_printer_platform_image_3/flutter_pos_printer_platform_image_3.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:ticket_booth/app/print_ticket.dart';
|
||||
import 'package:ticket_booth/model/branch_model.dart';
|
||||
import 'package:ticket_booth/model/cabapility_model.dart';
|
||||
import 'package:ticket_booth/screen/widgets/error_dialog.dart';
|
||||
|
||||
import '../../app/constant.dart';
|
||||
import '../../model/booth.dart';
|
||||
import '../../model/printer_device.dart';
|
||||
|
||||
class FormInput extends HookConsumerWidget {
|
||||
FormInput(
|
||||
{Key? key,
|
||||
required this.selected_booth,
|
||||
required this.pm,
|
||||
required this.booth,
|
||||
required this.selected_printer,
|
||||
required this.prnt,
|
||||
required this.hostIpController,
|
||||
required this.hostQrController,
|
||||
required this.headerDisplayController,
|
||||
required this.footerDisplayController,
|
||||
required this.headerTicketController,
|
||||
required this.footerTicketController,
|
||||
required this.isLoading,
|
||||
required this.capabilityList,
|
||||
required this.selectedCapability,
|
||||
required this.getServiceBooth,
|
||||
required this.branch,
|
||||
required this.selected_branch})
|
||||
: super(key: key);
|
||||
|
||||
final ValueNotifier<Booth> selected_booth;
|
||||
final ValueNotifier<List<Booth>> booth;
|
||||
final ValueNotifier<List<BranchModel>> branch;
|
||||
final ValueNotifier<BranchModel> selected_branch;
|
||||
final ValueNotifier<PrinterDev?> selected_printer;
|
||||
final ValueNotifier<List<PrinterDev>> prnt;
|
||||
final ValueNotifier<List<CapabilityProfileModel>> capabilityList;
|
||||
final ValueNotifier<CapabilityProfileModel?> selectedCapability;
|
||||
final TextEditingController hostIpController;
|
||||
final TextEditingController hostQrController;
|
||||
final TextEditingController headerDisplayController;
|
||||
final TextEditingController footerDisplayController;
|
||||
final TextEditingController headerTicketController;
|
||||
final TextEditingController footerTicketController;
|
||||
final PrinterManager pm;
|
||||
final Function getServiceBooth;
|
||||
final bool isLoading;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final hostIpError = useState(false);
|
||||
final hostQrError = useState(false);
|
||||
;
|
||||
|
||||
final usbStream =
|
||||
useStream<USBStatus>(pm.stateUSB, initialData: USBStatus.none);
|
||||
|
||||
_connectDevice(PrinterDev selectedPrinter, PrinterType type,
|
||||
{bool reconnect = true, String? ipAddress = null}) async {
|
||||
try {
|
||||
await pm.connect(
|
||||
type: type,
|
||||
model: UsbPrinterInput(
|
||||
name: selectedPrinter.deviceName,
|
||||
productId: selectedPrinter.productId,
|
||||
vendorId: selectedPrinter.vendorId));
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
|
||||
return Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: Constant.getActualX(context: context, x: 625),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
child: Tooltip(
|
||||
message: "Pilih salah satu cabang ",
|
||||
child: Text(
|
||||
'Cabang*',
|
||||
style: Constant.label(context: context),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: Constant.getActualY(context: context, y: 60),
|
||||
child: DropdownButtonFormField<BranchModel>(
|
||||
style: Constant.normal(context: context)
|
||||
.copyWith(color: Colors.black),
|
||||
decoration: const InputDecoration(
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(8),
|
||||
),
|
||||
),
|
||||
),
|
||||
menuMaxHeight: Constant.getActualY(context: context, y: 500),
|
||||
hint: Text(selected_branch.value.mBranchName ?? ''),
|
||||
items: branch.value
|
||||
.map((e) => DropdownMenuItem<BranchModel>(
|
||||
value: e, child: Text(e.mBranchName ?? '')))
|
||||
.toList(),
|
||||
onChanged: (BranchModel? e) {
|
||||
// print(e);
|
||||
selected_branch.value = e!;
|
||||
},
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: Constant.getActualY(context: context, y: 20),
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: Constant.getActualX(context: context, x: 170),
|
||||
child: Tooltip(
|
||||
message:
|
||||
"Pilih salah satu capability profile yang akan digunakan",
|
||||
child: Text(
|
||||
'Printer*',
|
||||
style: Constant.label(context: context),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: Constant.getActualX(context: context, x: 170),
|
||||
child: Tooltip(
|
||||
message:
|
||||
"Pilih salah satu printer yang akan digunakan",
|
||||
child: Text(
|
||||
'Capability Profile*',
|
||||
style: Constant.label(context: context),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: Constant.getActualX(context: context, x: 100),
|
||||
),
|
||||
SizedBox(
|
||||
width: Constant.getActualX(context: context, x: 100),
|
||||
)
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: Constant.getActualX(context: context, x: 170),
|
||||
height: Constant.getActualY(context: context, y: 60),
|
||||
child: DropdownButtonFormField<PrinterDev>(
|
||||
value: selected_printer.value,
|
||||
isExpanded: true,
|
||||
style: Constant.normal(context: context)
|
||||
.copyWith(color: Colors.black),
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(8),
|
||||
),
|
||||
),
|
||||
),
|
||||
menuMaxHeight:
|
||||
Constant.getActualY(context: context, y: 500),
|
||||
hint: Text(
|
||||
selected_printer.value?.deviceName
|
||||
.replaceAll(' ', '\u00A0') ??
|
||||
'',
|
||||
// softWrap: true,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
items: prnt.value
|
||||
.map((e) => DropdownMenuItem<PrinterDev>(
|
||||
value: e,
|
||||
child: Text(
|
||||
e.deviceName.replaceAll(' ', '\u00A0'),
|
||||
// softWrap: true,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
)))
|
||||
.toList(),
|
||||
onChanged: (PrinterDev? e) {
|
||||
selected_printer.value = e!;
|
||||
},
|
||||
)),
|
||||
SizedBox(
|
||||
width: Constant.getActualX(context: context, x: 170),
|
||||
height: Constant.getActualY(context: context, y: 60),
|
||||
child:
|
||||
DropdownButtonFormField<CapabilityProfileModel>(
|
||||
value: selectedCapability.value,
|
||||
isExpanded: true,
|
||||
style: Constant.normal(context: context)
|
||||
.copyWith(color: Colors.black),
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(8),
|
||||
),
|
||||
),
|
||||
),
|
||||
menuMaxHeight:
|
||||
Constant.getActualY(context: context, y: 500),
|
||||
hint: Text(
|
||||
selectedCapability.value?.key
|
||||
?.replaceAll(' ', '\u00A0') ??
|
||||
'',
|
||||
// softWrap: true,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
items: capabilityList.value
|
||||
.map((e) =>
|
||||
DropdownMenuItem<CapabilityProfileModel>(
|
||||
value: e,
|
||||
child: Text(
|
||||
e.key!.replaceAll(' ', '\u00A0'),
|
||||
// softWrap: true,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
)))
|
||||
.toList(),
|
||||
onChanged: (CapabilityProfileModel? e) {
|
||||
selectedCapability.value = e!;
|
||||
},
|
||||
)),
|
||||
SizedBox(
|
||||
// width: Constant.getActualX(context: context, x: 100),
|
||||
height: Constant.getActualY(context: context, y: 60),
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.red),
|
||||
onPressed: () {
|
||||
try {
|
||||
if (selected_printer.value != null) {
|
||||
_connectDevice(
|
||||
selected_printer.value!, PrinterType.usb);
|
||||
} else {
|
||||
throw Exception("Printer tidak dipilih");
|
||||
}
|
||||
} catch (e) {
|
||||
ErrorDialog(context, e.toString(), 'ERROR');
|
||||
print(e);
|
||||
}
|
||||
},
|
||||
child: Tooltip(
|
||||
message:
|
||||
"Tombol untuk menyambungkan printer yang elah dipilih",
|
||||
child: Text(
|
||||
"Connect",
|
||||
style: Constant.label(context: context)
|
||||
.copyWith(color: Colors.white),
|
||||
),
|
||||
)),
|
||||
),
|
||||
SizedBox(
|
||||
// width: Constant.getActualX(context: context, x: 100),
|
||||
height: Constant.getActualY(context: context, y: 60),
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.green),
|
||||
onPressed: () {
|
||||
try {
|
||||
PrintTicket().printTest();
|
||||
} catch (e) {
|
||||
ErrorDialog(context, e.toString(), 'ERROR');
|
||||
|
||||
print(e);
|
||||
}
|
||||
},
|
||||
child: Tooltip(
|
||||
message:
|
||||
"Tombol untuk tes print, setelah terhubung akan mencetak teks tes print",
|
||||
child: Text(
|
||||
"Test Print",
|
||||
style: Constant.label(context: context)
|
||||
.copyWith(color: Colors.white),
|
||||
),
|
||||
)),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
height: Constant.getActualY(context: context, y: 20),
|
||||
),
|
||||
Tooltip(
|
||||
message:
|
||||
"Domain yang akan digunakan untuk mendapatkan daftar layanan dan nomor antrian",
|
||||
child: Text(
|
||||
'Host IP*',
|
||||
style: Constant.label(context: context),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: Constant.getActualY(context: context, y: 60),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: Constant.getActualX(context: context, x: 500),
|
||||
child: TextField(
|
||||
controller: hostIpController,
|
||||
onChanged: (value) {
|
||||
if (value.trim() == "") {
|
||||
hostIpError.value = true;
|
||||
}
|
||||
if (value.trim() != "") {
|
||||
hostIpError.value = false;
|
||||
}
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
errorText:
|
||||
hostIpError.value ? "Tidak boleh kosong" : null,
|
||||
border: const OutlineInputBorder(),
|
||||
focusedBorder: const OutlineInputBorder(
|
||||
borderSide:
|
||||
BorderSide(color: Colors.blue, width: 2)),
|
||||
hintText: "http://devone.aplikasi.web.id",
|
||||
hintStyle: Constant.label(context: context)),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
// width: Constant.getActualX(context: context, x: 200),
|
||||
height: Constant.getActualY(context: context, y: 60),
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.green),
|
||||
onPressed: hostIpController.text == ""
|
||||
? null
|
||||
: () {
|
||||
getServiceBooth();
|
||||
},
|
||||
child: Tooltip(
|
||||
message:
|
||||
"Tombol untuk menampilkan daftar layanan apabila tidak muncul",
|
||||
child: isLoading
|
||||
? CircularProgressIndicator(
|
||||
color: Colors.white,
|
||||
)
|
||||
: Text(
|
||||
"Layanan ",
|
||||
style: Constant.label(context: context)
|
||||
.copyWith(color: Colors.white),
|
||||
),
|
||||
)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: Constant.getActualY(context: context, y: 20),
|
||||
),
|
||||
Tooltip(
|
||||
message:
|
||||
"Teks yang akan di cetak dibagian atas tiket. Maksimal 2 baris",
|
||||
child: Text(
|
||||
'Header Ticket',
|
||||
style: Constant.label(context: context),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: Constant.getActualY(context: context, y: 80),
|
||||
child: TextField(
|
||||
controller: headerTicketController,
|
||||
maxLines: 2,
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
hintText: "Masukkan header ticket",
|
||||
hintStyle: Constant.label(context: context)),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: Constant.getActualX(context: context, x: 50),
|
||||
),
|
||||
SizedBox(
|
||||
width: Constant.getActualX(context: context, x: 625),
|
||||
child:
|
||||
Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||
Tooltip(
|
||||
message: "Teks yang akan di tampilkan di display bagian atas",
|
||||
child: Text(
|
||||
'Header Display',
|
||||
style: Constant.label(context: context),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: Constant.getActualY(context: context, y: 60),
|
||||
child: TextField(
|
||||
controller: headerDisplayController,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
hintText: "Masukkan header display",
|
||||
hintStyle: Constant.label(context: context)),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: Constant.getActualY(context: context, y: 20),
|
||||
),
|
||||
Tooltip(
|
||||
message: "Teks yang akan di tampilkan di bagian bawah display",
|
||||
child: Text(
|
||||
'Footer Display',
|
||||
style: Constant.label(context: context),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: Constant.getActualY(context: context, y: 60),
|
||||
child: TextField(
|
||||
controller: footerDisplayController,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
hintText: "Masukkan footer display",
|
||||
hintStyle: Constant.label(context: context)),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: Constant.getActualY(context: context, y: 20),
|
||||
),
|
||||
SizedBox(
|
||||
child: Tooltip(
|
||||
message: "Pilih salah satu booth ",
|
||||
child: Text(
|
||||
'Booth*',
|
||||
style: Constant.label(context: context),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: Constant.getActualY(context: context, y: 60),
|
||||
child: DropdownButtonFormField<Booth>(
|
||||
style: Constant.normal(context: context)
|
||||
.copyWith(color: Colors.black),
|
||||
decoration: const InputDecoration(
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(8),
|
||||
),
|
||||
),
|
||||
),
|
||||
menuMaxHeight: Constant.getActualY(context: context, y: 500),
|
||||
hint: Text(selected_booth.value.name),
|
||||
items: booth.value
|
||||
.map((e) => DropdownMenuItem<Booth>(
|
||||
value: e, child: Text(e.name)))
|
||||
.toList(),
|
||||
onChanged: (Booth? e) {
|
||||
// print(e);
|
||||
selected_booth.value = e!;
|
||||
},
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: Constant.getActualY(context: context, y: 20),
|
||||
),
|
||||
Tooltip(
|
||||
message:
|
||||
"Teks yang akan di cetak di bagian bawah tiket. Maksimal 2 baris",
|
||||
child: Text(
|
||||
'Footer Ticket',
|
||||
style: Constant.label(context: context),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: Constant.getActualY(context: context, y: 80),
|
||||
child: TextField(
|
||||
maxLines: 2,
|
||||
controller: footerTicketController,
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
hintText: "Masukkan footer ticket",
|
||||
hintStyle: Constant.label(context: context)),
|
||||
),
|
||||
),
|
||||
]))
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
63
lib/screen/settings/service_list_provider.dart
Normal file
63
lib/screen/settings/service_list_provider.dart
Normal file
@@ -0,0 +1,63 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:ticket_booth/model/layanan.dart';
|
||||
import 'package:ticket_booth/provider/dio_provider.dart';
|
||||
import 'package:ticket_booth/repository/base_repository.dart';
|
||||
import 'package:ticket_booth/repository/service_repository.dart';
|
||||
|
||||
abstract class ServiceListState extends Equatable {
|
||||
final DateTime date;
|
||||
const ServiceListState(this.date);
|
||||
@override
|
||||
List<Object?> get props => [date];
|
||||
}
|
||||
|
||||
class ServiceListStateInit extends ServiceListState {
|
||||
ServiceListStateInit() : super(DateTime.now());
|
||||
}
|
||||
|
||||
class ServiceListStateLoading extends ServiceListState {
|
||||
ServiceListStateLoading() : super(DateTime.now());
|
||||
}
|
||||
|
||||
class ServiceListStateError extends ServiceListState {
|
||||
final String message;
|
||||
ServiceListStateError({
|
||||
required this.message,
|
||||
}) : super(DateTime.now());
|
||||
}
|
||||
|
||||
class ServiceListStateDone extends ServiceListState {
|
||||
final List<Layanan> model;
|
||||
ServiceListStateDone({
|
||||
required this.model,
|
||||
}) : super(DateTime.now());
|
||||
}
|
||||
|
||||
//notifier
|
||||
class ServiceListNotifier extends StateNotifier<ServiceListState> {
|
||||
final Ref ref;
|
||||
ServiceListNotifier({
|
||||
required this.ref,
|
||||
}) : super(ServiceListStateInit());
|
||||
|
||||
void list({required hostIp}) async {
|
||||
try {
|
||||
state = ServiceListStateLoading();
|
||||
final dio = ref.read(dioProvider);
|
||||
final resp = await ServiceRepository(dio: dio).getData(hostIp: hostIp);
|
||||
state = ServiceListStateDone(model: resp);
|
||||
} catch (e) {
|
||||
if (e is BaseRepositoryException) {
|
||||
state = ServiceListStateError(message: e.message);
|
||||
} else {
|
||||
state = ServiceListStateError(message: e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//provider
|
||||
final ServiceListProvider =
|
||||
StateNotifierProvider<ServiceListNotifier, ServiceListState>(
|
||||
(ref) => ServiceListNotifier(ref: ref));
|
||||
559
lib/screen/settings/setting_screen.dart
Normal file
559
lib/screen/settings/setting_screen.dart
Normal file
@@ -0,0 +1,559 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:esc_pos_utils/esc_pos_utils.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:flutter_pos_printer_platform_image_3/flutter_pos_printer_platform_image_3.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:ticket_booth/app/print_ticket.dart';
|
||||
import 'package:ticket_booth/app/route.dart';
|
||||
import 'package:ticket_booth/model/booth.dart';
|
||||
import 'package:ticket_booth/model/branch_model.dart';
|
||||
import 'package:ticket_booth/model/cabapility_model.dart';
|
||||
import 'package:ticket_booth/model/layanan.dart';
|
||||
import 'package:ticket_booth/model/printer_device.dart';
|
||||
import 'package:ticket_booth/provider/all_service_provider.dart';
|
||||
import 'package:ticket_booth/screen/settings/booth_list_provider.dart';
|
||||
import 'package:ticket_booth/screen/settings/branch_list_provider.dart';
|
||||
import 'package:ticket_booth/screen/settings/service_list_provider.dart';
|
||||
|
||||
import '../../app/constant.dart';
|
||||
import '../widgets/error_dialog.dart';
|
||||
import 'form_input.dart';
|
||||
|
||||
class SettingScreen extends HookConsumerWidget {
|
||||
const SettingScreen({super.key});
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final hostIpController = useTextEditingController(text: '');
|
||||
final hostQrController = useTextEditingController(text: '');
|
||||
final headerDisplayController = useTextEditingController(text: '');
|
||||
final footerDisplayController = useTextEditingController(text: '');
|
||||
final footerTicketController = useTextEditingController(text: '');
|
||||
final headerTicketController = useTextEditingController(text: '');
|
||||
final activeLayanan = useState<List>(List.empty());
|
||||
final capabilityProfileList = useState<List<CapabilityProfileModel>>([]);
|
||||
final selectedCapabilityProfile = useState<CapabilityProfileModel?>(null);
|
||||
final isLoading = useState(false);
|
||||
final data = useState('N');
|
||||
final PrinterManager pm = PrinterManager.instance;
|
||||
final dvcs = useState<List<PrinterDev>>([]);
|
||||
var devices3 = <PrinterDev>[];
|
||||
final selectedBtnSize = useState<Map<String, dynamic>>({
|
||||
'name': '4',
|
||||
'message': 'Tombol yang akan ditampilkan berjumlah 4 ',
|
||||
'rightLeftWidth': "300",
|
||||
'centerWidth': "912",
|
||||
'childAspectRatio': '1.559',
|
||||
'mainAxisSpacing': "10",
|
||||
'crossAxisSpacing': '10',
|
||||
'btnHeight': "330",
|
||||
'btnWidth': '300',
|
||||
'column': '2'
|
||||
});
|
||||
final List<Map<String, dynamic>> listBtnSize = [
|
||||
{
|
||||
'name': '10',
|
||||
'message': 'Tombol yang akan ditampilkan berjumlah 10',
|
||||
'rightLeftWidth': "40",
|
||||
'centerWidth': "1432",
|
||||
'childAspectRatio': '1',
|
||||
'mainAxisSpacing': "20",
|
||||
'crossAxisSpacing': '10',
|
||||
'btnHeight': "290",
|
||||
'btnWidth': '260',
|
||||
'column': '5'
|
||||
},
|
||||
{
|
||||
'name': '8',
|
||||
'message': 'Tombol yang akan ditampilkan berjumlah 8 ',
|
||||
'rightLeftWidth': "100",
|
||||
'centerWidth': "1312",
|
||||
'childAspectRatio': '1.1',
|
||||
'mainAxisSpacing': "40",
|
||||
'crossAxisSpacing': '10',
|
||||
'btnHeight': "310",
|
||||
'btnWidth': '280',
|
||||
'column': '4'
|
||||
},
|
||||
{
|
||||
'name': '6',
|
||||
'message': 'Tombol yang akan ditampilkan berjumlah 6 ',
|
||||
'rightLeftWidth': "200",
|
||||
'centerWidth': "1112",
|
||||
'childAspectRatio': '1.33',
|
||||
'mainAxisSpacing': "40",
|
||||
'crossAxisSpacing': '10',
|
||||
'btnHeight': "310",
|
||||
'btnWidth': '280',
|
||||
'column': '3'
|
||||
},
|
||||
{
|
||||
'name': '4',
|
||||
'message': 'Tombol yang akan ditampilkan berjumlah 4 ',
|
||||
'rightLeftWidth': "300",
|
||||
'centerWidth': "912",
|
||||
'childAspectRatio': '1.559',
|
||||
'mainAxisSpacing': "10",
|
||||
'crossAxisSpacing': '10',
|
||||
'btnHeight': "330",
|
||||
'btnWidth': '300',
|
||||
'column': '2'
|
||||
}
|
||||
];
|
||||
getServiceBooth() async {
|
||||
activeLayanan.value = List.empty();
|
||||
ref.read(BoothListProvider.notifier).list(hostIp: hostIpController.text);
|
||||
ref
|
||||
.read(ServiceListProvider.notifier)
|
||||
.list(hostIp: hostIpController.text);
|
||||
ref.read(BranchListProvider.notifier).list(hostIp: hostIpController.text);
|
||||
}
|
||||
|
||||
_scan(PrinterType type) async {
|
||||
// Find printers
|
||||
pm.discovery(type: PrinterType.usb, isBle: false).listen((device) {
|
||||
devices3.add(PrinterDev(
|
||||
id: int.parse(device.productId ?? '0'),
|
||||
deviceName: device.name,
|
||||
address: device.address,
|
||||
isBle: false,
|
||||
vendorId: device.vendorId,
|
||||
productId: device.productId,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
full() async {
|
||||
// await FullScreen.enterFullScreen(FullScreenMode.EMERSIVE_STICKY);
|
||||
}
|
||||
final lyn = useState<List<Layanan>>(List.empty());
|
||||
|
||||
final booth = useState<List<Booth>>(List.empty());
|
||||
final branch = useState<List<BranchModel>>(List.empty());
|
||||
final selected_printer = useState<PrinterDev?>(null);
|
||||
final selected_booth =
|
||||
useState<Booth>(Booth(name: 'Pilih salah satu', id: 0, code: 'b1'));
|
||||
final selected_branch = useState<BranchModel>(
|
||||
BranchModel(mBranchID: '0', mBranchName: 'Pilih salah satu cabang'));
|
||||
Future delData() async {
|
||||
// Obtain shared preferences.
|
||||
try {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.remove('tb-westerindo');
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
|
||||
Future getData() async {
|
||||
try {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
var raw_data = prefs.getString('tb-westerindo') ?? 'a';
|
||||
data.value = raw_data;
|
||||
print(raw_data);
|
||||
if (raw_data != 'a') {
|
||||
lyn.value = ref.read(allServiceProvider);
|
||||
var data = json.decode(raw_data);
|
||||
selected_booth.value = Booth.fromJson(data['selectedBooth']);
|
||||
selected_branch.value = BranchModel.fromJson(data['selectedBranch']);
|
||||
selected_printer.value = PrinterDev.fromJson(data['selectedPrinter']);
|
||||
hostIpController.text = data['hostIp'];
|
||||
hostQrController.text = data['hostQrIp'];
|
||||
headerDisplayController.text = data['headerDisplay'];
|
||||
footerDisplayController.text = data['footerDisplay'];
|
||||
footerTicketController.text = data['footerTicket'];
|
||||
headerTicketController.text = data['headerTicket'];
|
||||
activeLayanan.value = data['activeLayanan'] ?? List.empty();
|
||||
selectedBtnSize.value = data['buttonSize'];
|
||||
selectedCapabilityProfile.value =
|
||||
CapabilityProfileModel.fromJson(data['selectedCapability']);
|
||||
|
||||
if (activeLayanan.value.isNotEmpty) {
|
||||
activeLayanan.value.forEach((e) {
|
||||
lyn.value.forEach((f) {
|
||||
if (e == f.id) {
|
||||
f.value = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
print(e);
|
||||
ErrorDialog(context, e.toString(), 'ERROR');
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() {
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
||||
await getData();
|
||||
var capabilityList = await CapabilityProfile.getAvailableProfiles();
|
||||
List<CapabilityProfileModel> cl = List.empty(growable: true);
|
||||
for (var i = 0; i < capabilityList.length; i++) {
|
||||
var data = capabilityList[i];
|
||||
final model = CapabilityProfileModel.fromJson(data);
|
||||
cl.add(model);
|
||||
}
|
||||
capabilityProfileList.value = cl;
|
||||
|
||||
var stb = capabilityProfileList.value.firstWhere(
|
||||
(element) => element.key == selectedCapabilityProfile.value?.key,
|
||||
orElse: () => CapabilityProfileModel());
|
||||
if (stb.key != null) {
|
||||
selectedCapabilityProfile.value = stb;
|
||||
}
|
||||
|
||||
_scan(PrinterType.usb);
|
||||
dvcs.value = devices3;
|
||||
if (data.value != 'a') {
|
||||
ref
|
||||
.read(BoothListProvider.notifier)
|
||||
.list(hostIp: hostIpController.text);
|
||||
ref
|
||||
.read(BranchListProvider.notifier)
|
||||
.list(hostIp: hostIpController.text);
|
||||
}
|
||||
});
|
||||
full();
|
||||
return () {};
|
||||
}, []);
|
||||
ref.listen(BoothListProvider, (previous, next) {
|
||||
if (next is BoothListStateLoading) {
|
||||
isLoading.value = true;
|
||||
} else if (next is BoothListStateError) {
|
||||
ErrorDialog(context, next.message, 'ERROR');
|
||||
isLoading.value = false;
|
||||
} else if (next is BoothListStateDone) {
|
||||
booth.value = next.model;
|
||||
isLoading.value = false;
|
||||
}
|
||||
});
|
||||
ref.listen(ServiceListProvider, (previous, next) {
|
||||
if (next is ServiceListStateLoading) {
|
||||
isLoading.value = true;
|
||||
} else if (next is ServiceListStateError) {
|
||||
ErrorDialog(context, next.message, 'ERROR');
|
||||
isLoading.value = false;
|
||||
} else if (next is ServiceListStateDone) {
|
||||
// ref.read(allServiceProvider.notifier).state = next.model;
|
||||
// print(ref.read(allServiceProvider));
|
||||
lyn.value = next.model;
|
||||
isLoading.value = false;
|
||||
}
|
||||
});
|
||||
ref.listen(BranchListProvider, (previous, next) {
|
||||
if (next is BranchListStateLoading) {
|
||||
isLoading.value = true;
|
||||
} else if (next is BranchListStateError) {
|
||||
ErrorDialog(context, next.message, 'ERROR');
|
||||
isLoading.value = false;
|
||||
} else if (next is BranchListStateDone) {
|
||||
// ref.read(allServiceProvider.notifier).state = next.model;
|
||||
// print(ref.read(allServiceProvider));
|
||||
branch.value = next.model;
|
||||
isLoading.value = false;
|
||||
}
|
||||
});
|
||||
Future<bool> saveData() async {
|
||||
try {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final Map<String, dynamic> data = {
|
||||
"headerDisplay": headerDisplayController.text != ''
|
||||
? headerDisplayController.text
|
||||
: 'Selamat Datang Di Pramita',
|
||||
"footerDisplay": footerDisplayController.text != ''
|
||||
? footerDisplayController.text
|
||||
: 'Sentuh Salah Satu Layanan',
|
||||
"footerTicket": footerTicketController.text != ''
|
||||
? footerTicketController.text
|
||||
: 'SCAN QR CODE UNTUK MELIHAT STATUS ANTRIAN',
|
||||
"headerTicket": headerTicketController.text != ''
|
||||
? headerTicketController.text
|
||||
: 'Labratorium Medis & Klinik PRAMITA',
|
||||
"hostQrIp": hostQrController.text,
|
||||
"hostIp": hostIpController.text,
|
||||
"selectedPrinter": selected_printer.value,
|
||||
"selectedBooth": selected_booth.value,
|
||||
"selectedBranch": selected_branch.value,
|
||||
"activeLayanan": activeLayanan.value,
|
||||
'buttonSize': selectedBtnSize.value,
|
||||
"selectedCapability": selectedCapabilityProfile.value
|
||||
};
|
||||
json.encode(data);
|
||||
await prefs.setString('tb-westerindo', json.encode(data));
|
||||
return true;
|
||||
} catch (e) {
|
||||
ErrorDialog(context, e.toString(), 'ERROR');
|
||||
print(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Color getColor(Set<MaterialState> states) {
|
||||
const Set<MaterialState> interactiveStates = <MaterialState>{
|
||||
MaterialState.pressed,
|
||||
MaterialState.hovered,
|
||||
MaterialState.focused,
|
||||
};
|
||||
|
||||
if (states.any(interactiveStates.contains)) {
|
||||
return Colors.blue;
|
||||
}
|
||||
if (states.contains(MaterialState.selected)) {
|
||||
return Colors.green;
|
||||
}
|
||||
return Colors.white;
|
||||
}
|
||||
|
||||
return Material(
|
||||
child: Container(
|
||||
height: Constant.getActualY(context: context, y: 982),
|
||||
width: Constant.getActualX(context: context, x: 1512),
|
||||
decoration: const BoxDecoration(
|
||||
image: DecorationImage(
|
||||
fit: BoxFit.cover,
|
||||
image: AssetImage("assets/images/new-bg-icon.png"))),
|
||||
child: Center(
|
||||
child: Card(
|
||||
color: Colors.white,
|
||||
shadowColor: Colors.white,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(10))),
|
||||
elevation: 5,
|
||||
child: SizedBox(
|
||||
height: Constant.getActualY(context: context, y: 880),
|
||||
width: Constant.getActualX(context: context, x: 1360),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: Constant.getActualX(context: context, x: 30),
|
||||
vertical: Constant.getActualY(context: context, y: 30)),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
// TextButton(
|
||||
// onPressed: () {
|
||||
// Navigator.of(context)
|
||||
// .popAndPushNamed(displayRoute);
|
||||
// // print(pm.currentStatusUSB);
|
||||
// // print(pm.currentStatusUSB);
|
||||
// },
|
||||
// child: const Text('back')),
|
||||
Text(
|
||||
'Setting',
|
||||
style: Constant.subTitle(context: context),
|
||||
),
|
||||
// TextButton(
|
||||
// onPressed: () {
|
||||
// delData();
|
||||
// },
|
||||
// child: const Text('Delete All')),
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
height: Constant.getActualY(context: context, y: 30),
|
||||
),
|
||||
FormInput(
|
||||
pm: pm,
|
||||
selected_booth: selected_booth,
|
||||
booth: booth,
|
||||
selected_printer: selected_printer,
|
||||
prnt: dvcs,
|
||||
branch: branch,
|
||||
selected_branch: selected_branch,
|
||||
hostIpController: hostIpController,
|
||||
hostQrController: hostQrController,
|
||||
headerDisplayController: headerDisplayController,
|
||||
footerDisplayController: footerDisplayController,
|
||||
headerTicketController: headerTicketController,
|
||||
getServiceBooth: getServiceBooth,
|
||||
isLoading: isLoading.value,
|
||||
selectedCapability: selectedCapabilityProfile,
|
||||
capabilityList: capabilityProfileList,
|
||||
footerTicketController: footerTicketController),
|
||||
SizedBox(
|
||||
height: Constant.getActualY(context: context, y: 20),
|
||||
),
|
||||
Container(
|
||||
// color: Colors.red,
|
||||
height: Constant.getActualY(context: context, y: 40),
|
||||
child: Row(
|
||||
children: [
|
||||
Tooltip(
|
||||
message: "Jumlah tombol pada display",
|
||||
child: Text('Button display',
|
||||
style: Constant.label(context: context)),
|
||||
),
|
||||
SizedBox(
|
||||
width: Constant.getActualX(context: context, x: 10),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: Constant.getActualX(
|
||||
context: context, x: 10),
|
||||
),
|
||||
Row(
|
||||
children: listBtnSize
|
||||
.map(
|
||||
(e) => Row(
|
||||
children: [
|
||||
Checkbox(
|
||||
fillColor: MaterialStateProperty
|
||||
.resolveWith(getColor),
|
||||
value: e['name'].toString() ==
|
||||
selectedBtnSize
|
||||
.value['name']
|
||||
? true
|
||||
: false,
|
||||
onChanged: ((value) {
|
||||
selectedBtnSize.value = e;
|
||||
})),
|
||||
SizedBox(
|
||||
width: Constant.getActualX(
|
||||
context: context, x: 3),
|
||||
),
|
||||
Tooltip(
|
||||
message: e['message'].toString(),
|
||||
child: Text(
|
||||
e['name'].toString(),
|
||||
style: Constant.normal(
|
||||
context: context),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: Constant.getActualX(
|
||||
context: context, x: 15),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: Constant.getActualY(context: context, y: 190),
|
||||
child: GridView.count(
|
||||
childAspectRatio:
|
||||
Constant.getActualY(context: context, y: 150) /
|
||||
Constant.getActualX(context: context, x: 15),
|
||||
crossAxisCount: 3,
|
||||
children: lyn.value
|
||||
.map((e) => Row(
|
||||
children: [
|
||||
Transform.scale(
|
||||
scale: 0.8,
|
||||
child: CupertinoSwitch(
|
||||
value: e.value,
|
||||
onChanged: (val) {
|
||||
if (val) {
|
||||
var rw_data = json.encode(
|
||||
activeLayanan.value);
|
||||
List data =
|
||||
json.decode(rw_data);
|
||||
List actlyn = data;
|
||||
actlyn.add(e.id);
|
||||
activeLayanan.value = actlyn;
|
||||
}
|
||||
if (!val) {
|
||||
var rw_data = json.encode(
|
||||
activeLayanan.value);
|
||||
List data =
|
||||
json.decode(rw_data);
|
||||
List actlyn = data;
|
||||
actlyn.remove(e.id);
|
||||
activeLayanan.value = actlyn;
|
||||
}
|
||||
|
||||
e.value = val;
|
||||
}),
|
||||
),
|
||||
Expanded(
|
||||
child: e.isConsultDoctor == 'N'
|
||||
? Text(
|
||||
e.name,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Constant.normal(
|
||||
context: context),
|
||||
)
|
||||
: Text(
|
||||
"${e.name} ${e.doctorName}",
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Constant.normal(
|
||||
context: context),
|
||||
),
|
||||
)
|
||||
],
|
||||
))
|
||||
.toList()),
|
||||
),
|
||||
SizedBox(
|
||||
height: Constant.getActualY(context: context, y: 20),
|
||||
),
|
||||
Center(
|
||||
child: SizedBox(
|
||||
height: Constant.getActualY(context: context, y: 60),
|
||||
width: Constant.getActualX(context: context, x: 200),
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
if (selected_booth.value != null &&
|
||||
selected_booth.value.id > 0 &&
|
||||
selected_printer.value != null &&
|
||||
selectedCapabilityProfile.value?.key !=
|
||||
null &&
|
||||
selected_branch.value != null &&
|
||||
selected_branch.value.mBranchID != '0' &&
|
||||
hostIpController.text.isNotEmpty &&
|
||||
activeLayanan.value.isNotEmpty) {
|
||||
saveData();
|
||||
ref.read(allServiceProvider.notifier).state =
|
||||
lyn.value;
|
||||
Navigator.of(context).pushNamed(queueRoute);
|
||||
} else {
|
||||
if (selected_booth.value == null ||
|
||||
selected_printer.value == null ||
|
||||
selectedCapabilityProfile.value == null ||
|
||||
hostIpController.text.isEmpty) {
|
||||
ErrorDialog(
|
||||
context,
|
||||
'Input bertanda * tidak boleh kosong',
|
||||
'PERINGATAN');
|
||||
} else if (activeLayanan.value.isEmpty) {
|
||||
ErrorDialog(
|
||||
context,
|
||||
'Layanan belum ada yang aktif',
|
||||
'PERINGATAN');
|
||||
}
|
||||
}
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
elevation: 5, backgroundColor: Colors.blue),
|
||||
child: Text(
|
||||
'Save',
|
||||
style: Constant.subTitle(context: context)
|
||||
.copyWith(color: Colors.white),
|
||||
),
|
||||
)),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
103
lib/screen/splash/splash_screen.dart
Normal file
103
lib/screen/splash/splash_screen.dart
Normal file
@@ -0,0 +1,103 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:animated_text_kit/animated_text_kit.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:flutter_pos_printer_platform_image_3/flutter_pos_printer_platform_image_3.dart';
|
||||
import 'package:flutter_spinkit/flutter_spinkit.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:ticket_booth/app/constant.dart';
|
||||
import 'package:ticket_booth/app/route.dart';
|
||||
import 'package:ticket_booth/model/printer_device.dart';
|
||||
|
||||
import '../widgets/error_dialog.dart';
|
||||
|
||||
class SplashScreen extends HookConsumerWidget {
|
||||
const SplashScreen({super.key});
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final PrinterManager pm = PrinterManager.instance;
|
||||
|
||||
_connectDevice(PrinterDev selectedPrinter, PrinterType type,
|
||||
{bool reconnect = true, String? ipAddress = null}) async {
|
||||
try {
|
||||
await pm.connect(
|
||||
type: type,
|
||||
model: UsbPrinterInput(
|
||||
name: selectedPrinter.deviceName,
|
||||
productId: selectedPrinter.productId,
|
||||
vendorId: selectedPrinter.vendorId));
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
|
||||
Future getData() async {
|
||||
try {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
var raw_data = prefs.getString('tb-westerindo') ?? 'a';
|
||||
|
||||
if (raw_data != 'a') {
|
||||
var data = json.decode(raw_data);
|
||||
PrinterDev selectedPrinter =
|
||||
PrinterDev.fromJson(data['selectedPrinter']);
|
||||
_connectDevice(selectedPrinter, PrinterType.usb);
|
||||
}
|
||||
} catch (e) {
|
||||
print(e);
|
||||
ErrorDialog(context, e.toString(), 'ERROR');
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() {
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
||||
await getData();
|
||||
Timer(const Duration(seconds: 3), () {
|
||||
Navigator.of(context).pushNamed(displayRoute);
|
||||
});
|
||||
});
|
||||
return () {};
|
||||
}, []);
|
||||
return Material(
|
||||
child: Container(
|
||||
height: Constant.getActualY(context: context, y: 982),
|
||||
width: Constant.getActualX(context: context, x: 1512),
|
||||
decoration: const BoxDecoration(
|
||||
image: DecorationImage(
|
||||
fit: BoxFit.cover,
|
||||
image: AssetImage("assets/images/new-bg-icon.png"))),
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: Constant.getActualY(context: context, y: 600),
|
||||
width: Constant.getActualX(context: context, x: 1512),
|
||||
),
|
||||
const SpinKitFadingCube(
|
||||
color: Colors.redAccent,
|
||||
size: 50.0,
|
||||
),
|
||||
SizedBox(
|
||||
height: Constant.getActualY(context: context, y: 50),
|
||||
),
|
||||
DefaultTextStyle(
|
||||
style: Constant.title(context: context)
|
||||
.copyWith(fontStyle: FontStyle.italic, color: Colors.black),
|
||||
child: AnimatedTextKit(animatedTexts: [
|
||||
TyperAnimatedText('Loading ...',
|
||||
speed: Duration(milliseconds: 300))
|
||||
])),
|
||||
SizedBox(
|
||||
height: Constant.getActualY(context: context, y: 50),
|
||||
),
|
||||
Text(
|
||||
Constant.versi,
|
||||
style: Constant.subTitle(context: context)
|
||||
.copyWith(fontStyle: FontStyle.italic, color: Colors.grey),
|
||||
)
|
||||
],
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
135
lib/screen/widgets/dialog_print.dart
Normal file
135
lib/screen/widgets/dialog_print.dart
Normal file
@@ -0,0 +1,135 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:qr_flutter/qr_flutter.dart';
|
||||
|
||||
import '../../app/constant.dart';
|
||||
|
||||
// ignore: non_constant_identifier_names
|
||||
Future<String?> DialogPrint(BuildContext context, String noAntrian,
|
||||
Function funcPrintTicket, String serviceName) {
|
||||
return showDialog<String>(
|
||||
context: context,
|
||||
builder: (BuildContext context) => Dialog(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
width: Constant.getActualX(context: context, x: 700),
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
textAlign: TextAlign.center,
|
||||
'CETAK TIKET ANTRIAN',
|
||||
style: Constant.subTitle(context: context),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
SizedBox(
|
||||
width: Constant.getActualX(context: context, x: 700),
|
||||
child: const Divider()),
|
||||
Container(
|
||||
width: Constant.getActualX(context: context, x: 700),
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
textAlign: TextAlign.center,
|
||||
serviceName,
|
||||
style: Constant.subTitle(context: context),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
Container(
|
||||
width: Constant.getActualX(context: context, x: 700),
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
textAlign: TextAlign.center,
|
||||
noAntrian,
|
||||
style: Constant.subTitle(context: context),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: Constant.getActualX(context: context, x: 700),
|
||||
alignment: Alignment.center,
|
||||
child: QrImageView(
|
||||
data: noAntrian,
|
||||
version: QrVersions.auto,
|
||||
size: 290,
|
||||
// embeddedImage: AssetImage('assets/images/logo-westerindo.png'),
|
||||
// embeddedImageStyle: ,
|
||||
gapless: false,
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
Container(
|
||||
width: Constant.getActualX(context: context, x: 700),
|
||||
alignment: Alignment.center,
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
funcPrintTicket(noAntrian);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
elevation: 5, backgroundColor: Colors.blue),
|
||||
child: Text(
|
||||
'Cetak Tiket',
|
||||
style: Constant.label(context: context)
|
||||
.copyWith(color: Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
Container(
|
||||
width: Constant.getActualX(context: context, x: 700),
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
textAlign: TextAlign.center,
|
||||
"Scan qr code atau tekan tombol 'cetak tiket' untuk mendapatkan tiket",
|
||||
style: Constant.label(context: context),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
Container(
|
||||
width: Constant.getActualX(context: context, x: 700),
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
textAlign: TextAlign.center,
|
||||
"Tutup dialog setelah mendapatkan tiket",
|
||||
style: Constant.label(context: context),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: Constant.getActualX(context: context, x: 700),
|
||||
child: const Divider()),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
SizedBox(
|
||||
width: Constant.getActualX(context: context, x: 700),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: const Text('Tutup'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
59
lib/screen/widgets/error_dialog.dart
Normal file
59
lib/screen/widgets/error_dialog.dart
Normal file
@@ -0,0 +1,59 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../app/constant.dart';
|
||||
|
||||
Future<String?> ErrorDialog(
|
||||
BuildContext context, String errorMsg, String header) {
|
||||
return showDialog<String>(
|
||||
context: context,
|
||||
builder: (BuildContext context) => Dialog(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
header,
|
||||
style: Constant.subTitle(context: context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
SizedBox(
|
||||
width: Constant.getActualX(context: context, x: 700),
|
||||
child: const Divider()),
|
||||
SizedBox(
|
||||
height: Constant.getActualY(context: context, y: 500),
|
||||
width: Constant.getActualX(context: context, x: 700),
|
||||
child: ListView(
|
||||
children: [Text(errorMsg)],
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: Constant.getActualX(context: context, x: 700),
|
||||
child: const Divider()),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
SizedBox(
|
||||
width: Constant.getActualX(context: context, x: 700),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: const Text('Tutup'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
59
lib/screen/widgets/pasien_online_dialog.dart
Normal file
59
lib/screen/widgets/pasien_online_dialog.dart
Normal file
@@ -0,0 +1,59 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../app/constant.dart';
|
||||
|
||||
// ignore: non_constant_identifier_names
|
||||
Future<String?> PasienOnlineDialog(BuildContext context) {
|
||||
return showDialog<String>(
|
||||
context: context,
|
||||
builder: (BuildContext context) => Dialog(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
'Pasien Online',
|
||||
style: Constant.subTitle(context: context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
SizedBox(
|
||||
width: Constant.getActualX(context: context, x: 700),
|
||||
child: const Divider()),
|
||||
SizedBox(
|
||||
height: Constant.getActualY(context: context, y: 500),
|
||||
width: Constant.getActualX(context: context, x: 700),
|
||||
child: ListView(
|
||||
children: [TextField()],
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: Constant.getActualX(context: context, x: 700),
|
||||
child: const Divider()),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
SizedBox(
|
||||
width: Constant.getActualX(context: context, x: 700),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: const Text('Tutup'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user