diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index b26c00a..f55f8e7 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -8,7 +8,7 @@ plugins { android { namespace = "com.example.kdr_kurir_app_v2" compileSdk = flutter.compileSdkVersion - ndkVersion = flutter.ndkVersion + ndkVersion = "27.0.12077973" compileOptions { sourceCompatibility = JavaVersion.VERSION_11 diff --git a/android/app/src/main/res/xml/network_security_config.xml b/android/app/src/main/res/xml/network_security_config.xml new file mode 100644 index 0000000..4806a1e --- /dev/null +++ b/android/app/src/main/res/xml/network_security_config.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/lib/app/constant.dart b/lib/app/constant.dart new file mode 100644 index 0000000..298dedf --- /dev/null +++ b/lib/app/constant.dart @@ -0,0 +1,122 @@ +import 'package:flutter/material.dart'; + +class Constant { + static String tokenName = "kdr-kurir"; + // static String baseUrl = "http://devkedungdororaya.aplikasi.web.id/one-api/"; + + static String baseUrl = "http://kd-kurir.aplikasi.web.id/one-api/"; + static String baseBirtUrl = "http://kd-kurir.aplikasi.web.id/"; + + static double designHeight = 844; + static double designWidth = 390; + + static String version = "1.01"; + + //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; + } + + //color identity + static Color backgroundWhite = const Color(0xffFFFFFF); + static Color textBlack = const Color(0xFF070708); + static Color textGrey = const Color(0xFF919EAB); + static Color primaryBlue = const Color(0xFF005AA9); + static Color backgroundBlue = const Color(0xFF1890FF).withOpacity(0.25); + static Color iconBlue = const Color(0xFF3366FF); + static Color primaryGreen = const Color(0xFF229A16); + static Color backgroundGreen = const Color(0xFF54D62C).withOpacity(0.25); + static Color primaryOrange = const Color(0xFFB78103); + static Color backgroundOrange = const Color(0xFFFFC107).withOpacity(0.25); + static Color primaryRed = const Color(0xFFB72136); + static Color primaryYellow = const Color(0xFFFFC107); + static Color primaryPurple = const Color(0xFFBF07FF); + static Color backgroundPurple = const Color(0xFFBF07FF).withOpacity(0.25); + static Color textSecondary = const Color(0xFF637381); + + // sindhu + static Color textPrimary = const Color(0xff212B36); + static Color primaryMain = const Color(0xff3366FF); + static Color errorDark = const Color(0xffB72136); + // static Color dropdownTextColor = const Color(0xff005AA9); + static Color blueButton = const Color(0xff005AA9); + static Color primaryDark = const Color(0xff005AA9); + static Color buttonIsNotActive = const Color(0xffDFE3E8); + static Color historyCardColor = const Color(0xff54D62C).withOpacity(0.08); + static Color historyCardColorPending = + const Color(0xffFF0D05).withOpacity(0.08); + + // typography + static TextStyle heading2({required BuildContext context}) { + return TextStyle( + fontSize: Constant.getActualY(context: context, y: 28), + height: 1.5, + ); + } + + static TextStyle body1({required BuildContext context}) { + return TextStyle( + fontSize: Constant.getActualY(context: context, y: 16), + height: 1.5, + ); + } + + static TextStyle caption1({required BuildContext context}) { + return TextStyle( + fontSize: Constant.getActualY(context: context, y: 12), + height: 1.5, + ); + } + + static TextStyle buttonLarge({required BuildContext context}) { + return TextStyle( + fontSize: Constant.getActualY(context: context, y: 15), + height: 1.5, + ); + } + + static TextStyle heading4({required BuildContext context}) { + return TextStyle( + fontSize: Constant.getActualY(context: context, y: 18), + height: 1.5, + ); + } + + static TextStyle body3({required BuildContext context}) { + return TextStyle( + fontSize: Constant.getActualY(context: context, y: 14), + height: 1.5, + ); + } + + static TextStyle caption2({required BuildContext context}) { + return TextStyle( + fontSize: Constant.getActualY(context: context, y: 10), + height: 1.5, + ); + } + + static TextStyle heading3({required BuildContext context}) { + return TextStyle( + fontSize: Constant.getActualY(context: context, y: 20), + height: 1.5, + ); + } + + static TextStyle body2({required BuildContext context}) { + return TextStyle( + fontSize: Constant.getActualY(context: context, y: 14), + height: 1.5, + ); + } +} diff --git a/lib/app/route.dart b/lib/app/route.dart new file mode 100644 index 0000000..c9eef2c --- /dev/null +++ b/lib/app/route.dart @@ -0,0 +1,285 @@ +import 'package:flutter/material.dart'; +import '../screen/change_password_screen/change_password_screen.dart'; +import '../screen/input_pekerjaan/input_pekerjaan_lain_lain.dart'; +import '../screen/input_pekerjaan/input_pekerjaan_pengambilan_bahan.dart'; +import '../screen/personal_information_screen/personal_information_screen.dart'; +import '../screen/test/test_home_screen.dart'; +import '../screen/work_process_screen/work_process_screen.dart'; + +import '../screen/input_pekerjaan/input_lain_lain_screen.dart'; +import '../screen/input_pekerjaan/input_pekerjaan_pengantaran_hasil.dart'; +import '../screen/input_pekerjaan/input_pekerjaan_screen.dart'; +import '../screen/input_pekerjaan/input_pengambilan_bahan_screen.dart'; +import '../screen/input_pekerjaan/input_pengantaran_hasil_dokter_screen.dart'; +import '../screen/input_pekerjaan/input_pengantaran_hasil_instansi_screen.dart'; +import '../screen/input_pekerjaan/input_pengantaran_hasil_pasien_screen.dart'; +import '../screen/input_pekerjaan/input_scan_screen.dart'; +import '../screen/konfirmasi/konfirmasi_screen.dart'; +import '../screen/login/login_screen.dart'; +import '../screen/problemlogin/problem_login_screen.dart'; +import '../screen/splash_screen/splash_screen.dart'; +import '../screen/menu_screen/menu_screen.dart'; +// import '../screen/splash_screen.dart'; + +const splashRoute = "/splashRoute"; +const loginRoute = "/loginRoute"; +const problemLoginRoute = "/problemLoginRoute"; +const testHomeRoute = "/testHomeRoute"; +const inputPekerjaan = "/inputPekerjaan"; +const inputScanRoute = "/inputScanRoute"; +const konfirmasiRoute = "/konfirmasiRoute"; +const inputPengantaranHasilPasien = "/inputPengantaranHasilPasien"; +const inputPengantaranHasilInstansi = "/inputPengantaranHasilInstansi"; +const inputPengantaranHasilDokter = "/inputPengantaranHasilDokter"; +const inputPengambilanBahan = "/inputPengambilanBahan"; +const inputLainLain = "/inputLainLain"; +const personalInformationRoute = "/personalInformation"; +const changePasswordRoute = "/changPassword"; +const workProcessRoute = "/workProcessRoute"; + +const menuRoute = "/menuRoute"; +const inputPekerjaanPengantaranHasil = "/inputPekerjaanPengantaranHasil"; +const inputPekerjaanPengambilanBahan = "/inputPekerjaanPengambilanBahan"; +const inputPekerjaanLainLain = "/inputPekerjaanLainLain"; + +class inputPekerjaanProp { + final int input; + final int tipe; + + inputPekerjaanProp(this.input, this.tipe); +} + +class InputScanPekerjaanProp { + final int input; + final int tipe; + + InputScanPekerjaanProp(this.input, this.tipe); +} + +class AppRoute { + static Route generateRoute(RouteSettings settings) { + // splash screen + if (settings.name == splashRoute) { + return MaterialPageRoute(builder: (context) { + return MediaQuery( + data: MediaQuery.of(context).copyWith( + textScaleFactor: 1.0, padding: const EdgeInsets.all(0)), + child: const SplashScreen()); + }); + } + + // login + if (settings.name == loginRoute) { + return MaterialPageRoute(builder: (context) { + return MediaQuery( + data: MediaQuery.of(context).copyWith( + textScaleFactor: 1.0, padding: const EdgeInsets.all(0)), + child: const LoginScreen()); + }); + } + + // problem login + if (settings.name == problemLoginRoute) { + return MaterialPageRoute(builder: (context) { + return MediaQuery( + data: MediaQuery.of(context).copyWith( + textScaleFactor: 1.0, padding: const EdgeInsets.all(0)), + child: const ProblemLoginScreen()); + }); + } + + // test home screen + if (settings.name == testHomeRoute) { + return MaterialPageRoute(builder: (context) { + return MediaQuery( + data: MediaQuery.of(context).copyWith( + textScaleFactor: 1.0, padding: const EdgeInsets.all(0)), + child: const TestHomeScreen()); + }); + } + // test Menu screen + if (settings.name == menuRoute) { + return MaterialPageRoute(builder: (context) { + return MediaQuery( + data: MediaQuery.of(context).copyWith( + textScaleFactor: 1.0, padding: const EdgeInsets.all(0)), + child: const MenuScreen()); + }); + } + + // input pekerjaan + if (settings.name == inputPekerjaan) { + final inputPekerjaanProp args = settings.arguments as inputPekerjaanProp; + return MaterialPageRoute(builder: (context) { + return MediaQuery( + data: MediaQuery.of(context).copyWith( + textScaleFactor: 1.0, padding: const EdgeInsets.all(0)), + child: InputPekerjaan( + input: args.input, + tipe: args.tipe, + )); + }); + } + + // input pekerjaan pengantaran hasil + if (settings.name == inputPekerjaanPengantaranHasil) { + return MaterialPageRoute(builder: (context) { + return MediaQuery( + data: MediaQuery.of(context).copyWith( + textScaleFactor: 1.0, padding: const EdgeInsets.all(0)), + child: const InputPekerjaanPengantaranHasil()); + }); + } + + // input pekerjaan pengambilan bahan + if (settings.name == inputPekerjaanPengambilanBahan) { + return MaterialPageRoute(builder: (context) { + return MediaQuery( + data: MediaQuery.of(context).copyWith( + textScaleFactor: 1.0, padding: const EdgeInsets.all(0)), + child: const InputPekerjaanPengambilanBahan()); + }); + } + + // input pekerjaan lain lain + if (settings.name == inputPekerjaanLainLain) { + return MaterialPageRoute(builder: (context) { + return MediaQuery( + data: MediaQuery.of(context).copyWith( + textScaleFactor: 1.0, padding: const EdgeInsets.all(0)), + child: const InputPekerjaanPekerjaanLainLain()); + }); + } + + // input pengantaran hasil pasien + if (settings.name == inputPengantaranHasilPasien) { + final args = settings.arguments as Map; + return MaterialPageRoute(builder: (context) { + return MediaQuery( + data: MediaQuery.of(context).copyWith( + textScaleFactor: 1.0, padding: const EdgeInsets.all(0)), + child: InputPengantaranHasilPasienScreen( + data: args, + )); + }); + } + + // input pengantaran hasil instansi + if (settings.name == inputPengantaranHasilInstansi) { + final args = settings.arguments as Map; + return MaterialPageRoute(builder: (context) { + return MediaQuery( + data: MediaQuery.of(context) + .copyWith(textScaleFactor: 1.0, padding: const EdgeInsets.all(0)), + child: InputPengantaranHasilInstansiScreen( + data: args, + ), + ); + }); + } + + // input pengantaran hasil dokter + if (settings.name == inputPengantaranHasilDokter) { + final args = settings.arguments as Map; + return MaterialPageRoute(builder: (context) { + return MediaQuery( + data: MediaQuery.of(context) + .copyWith(textScaleFactor: 1.0, padding: const EdgeInsets.all(0)), + child: InputPengantaranHasilDokterScreen( + data: args, + ), + ); + }); + } + + // input pengambilan bahan + if (settings.name == inputPengambilanBahan) { + final args = settings.arguments as Map; + return MaterialPageRoute(builder: (context) { + return MediaQuery( + data: MediaQuery.of(context) + .copyWith(textScaleFactor: 1.0, padding: const EdgeInsets.all(0)), + child: InputPengambilanBahanScreen( + data: args, + ), + ); + }); + } + + // input lain lain + if (settings.name == inputLainLain) { + final args = settings.arguments as Map; + return MaterialPageRoute(builder: (context) { + return MediaQuery( + data: MediaQuery.of(context) + .copyWith(textScaleFactor: 1.0, padding: const EdgeInsets.all(0)), + child: InputLainLain( + data: args, + ), + ); + }); + } + + // input scan + if (settings.name == inputScanRoute) { + // final args = settings.arguments as Map; + final InputScanPekerjaanProp args = + settings.arguments as InputScanPekerjaanProp; + return MaterialPageRoute(builder: (context) { + return MediaQuery( + data: MediaQuery.of(context) + .copyWith(textScaleFactor: 1.0, padding: const EdgeInsets.all(0)), + child: InputScanScreen( + data: args, + ), + ); + }); + } + + // konfirmasi + if (settings.name == konfirmasiRoute) { + return MaterialPageRoute(builder: (context) { + return MediaQuery( + data: MediaQuery.of(context).copyWith( + textScaleFactor: 1.0, padding: const EdgeInsets.all(0)), + child: const KonfirmasiScreen()); + }); + } + // Informasi Pribadi + if (settings.name == personalInformationRoute) { + return MaterialPageRoute(builder: (context) { + return MediaQuery( + data: MediaQuery.of(context).copyWith( + textScaleFactor: 1.0, padding: const EdgeInsets.all(0)), + child: const PersonalInformationScreen()); + }); + } + if (settings.name == changePasswordRoute) { + return MaterialPageRoute(builder: (context) { + return MediaQuery( + data: MediaQuery.of(context).copyWith( + textScaleFactor: 1.0, padding: const EdgeInsets.all(0)), + child: const ChangePasswordScreen()); + }); + } + if (settings.name == workProcessRoute) { + final args = settings.arguments as DetailWorkProp; + + return MaterialPageRoute(builder: (context) { + return MediaQuery( + data: MediaQuery.of(context).copyWith( + textScaleFactor: 1.0, padding: const EdgeInsets.all(0)), + child: WorkProcessScreen( + data: args, + )); + }); + } + + return MaterialPageRoute(builder: (context) { + return MediaQuery( + data: MediaQuery.of(context) + .copyWith(textScaleFactor: 1.0, padding: const EdgeInsets.all(0)), + child: const SplashScreen()); + }); + } +} diff --git a/lib/images/bg_login.png b/lib/images/bg_login.png new file mode 100644 index 0000000..31fd0e2 Binary files /dev/null and b/lib/images/bg_login.png differ diff --git a/lib/images/bg_problem_login.png b/lib/images/bg_problem_login.png new file mode 100644 index 0000000..47759da Binary files /dev/null and b/lib/images/bg_problem_login.png differ diff --git a/lib/images/icon_eye_password_active.png b/lib/images/icon_eye_password_active.png new file mode 100644 index 0000000..6b58d86 Binary files /dev/null and b/lib/images/icon_eye_password_active.png differ diff --git a/lib/images/icon_eye_password_not_active.png b/lib/images/icon_eye_password_not_active.png new file mode 100644 index 0000000..8b5183b Binary files /dev/null and b/lib/images/icon_eye_password_not_active.png differ diff --git a/lib/images/icon_home.png b/lib/images/icon_home.png new file mode 100644 index 0000000..3d98a01 Binary files /dev/null and b/lib/images/icon_home.png differ diff --git a/lib/images/icon_home_bell.png b/lib/images/icon_home_bell.png new file mode 100644 index 0000000..6728517 Binary files /dev/null and b/lib/images/icon_home_bell.png differ diff --git a/lib/images/icon_home_diterima.png b/lib/images/icon_home_diterima.png new file mode 100644 index 0000000..f7a0a17 Binary files /dev/null and b/lib/images/icon_home_diterima.png differ diff --git a/lib/images/icon_home_hasil.png b/lib/images/icon_home_hasil.png new file mode 100644 index 0000000..a534e5c Binary files /dev/null and b/lib/images/icon_home_hasil.png differ diff --git a/lib/images/icon_home_lainnya.png b/lib/images/icon_home_lainnya.png new file mode 100644 index 0000000..0fb920b Binary files /dev/null and b/lib/images/icon_home_lainnya.png differ diff --git a/lib/images/icon_home_sample.png b/lib/images/icon_home_sample.png new file mode 100644 index 0000000..a57c652 Binary files /dev/null and b/lib/images/icon_home_sample.png differ diff --git a/lib/images/icon_home_selesai.png b/lib/images/icon_home_selesai.png new file mode 100644 index 0000000..10b1328 Binary files /dev/null and b/lib/images/icon_home_selesai.png differ diff --git a/lib/images/icon_home_total.png b/lib/images/icon_home_total.png new file mode 100644 index 0000000..4e2b963 Binary files /dev/null and b/lib/images/icon_home_total.png differ diff --git a/lib/images/logo_kdr.png b/lib/images/logo_kdr.png new file mode 100644 index 0000000..fb21859 Binary files /dev/null and b/lib/images/logo_kdr.png differ diff --git a/lib/images/logo_kurir_konfirmasi.png b/lib/images/logo_kurir_konfirmasi.png new file mode 100644 index 0000000..8ed8fe0 Binary files /dev/null and b/lib/images/logo_kurir_konfirmasi.png differ diff --git a/lib/main.dart b/lib/main.dart index 7b7f5b6..90bdb59 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,7 +1,18 @@ import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter/services.dart'; +import 'package:intl/date_symbol_data_file.dart'; + +import 'app/constant.dart'; +import 'app/route.dart'; void main() { - runApp(const MyApp()); + WidgetsFlutterBinding.ensureInitialized(); + SystemChrome.setPreferredOrientations([ + DeviceOrientation.portraitUp, + DeviceOrientation.portraitUp, + ]).then((value) => runApp(const ProviderScope(child: MyApp()))); + // runApp(const ProviderScope(child: MyApp())); } class MyApp extends StatelessWidget { @@ -11,112 +22,34 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( - title: 'Flutter Demo', + title: 'Aplikasi Kurir Kedungdoro', theme: ThemeData( - // This is the theme of your application. - // - // TRY THIS: Try running your application with "flutter run". You'll see - // the application has a purple toolbar. Then, without quitting the app, - // try changing the seedColor in the colorScheme below to Colors.green - // and then invoke "hot reload" (save your changes or press the "hot - // reload" button in a Flutter-supported IDE, or press "r" if you used - // the command line to start the app). - // - // Notice that the counter didn't reset back to zero; the application - // state is not lost during the reload. To reset the state, use hot - // restart instead. - // - // This works for code too, not just values: Most code changes can be - // tested with just a hot reload. - colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), - ), - home: const MyHomePage(title: 'Flutter Demo Home Page'), + primarySwatch: createMaterialColor(const Color(0xFF005AA9)), + fontFamily: 'OpenSans'), + debugShowCheckedModeBanner: false, + // initialRoute: loginRoute, + initialRoute: splashRoute, + onGenerateRoute: AppRoute.generateRoute, ); } } -class MyHomePage extends StatefulWidget { - const MyHomePage({super.key, required this.title}); +MaterialColor createMaterialColor(Color color) { + List strengths = [.05]; + Map swatch = {}; + final int r = color.red, g = color.green, b = color.blue; - // This widget is the home page of your application. It is stateful, meaning - // that it has a State object (defined below) that contains fields that affect - // how it looks. - - // This class is the configuration for the state. It holds the values (in this - // case the title) provided by the parent (in this case the App widget) and - // used by the build method of the State. Fields in a Widget subclass are - // always marked "final". - - final String title; - - @override - State createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - int _counter = 0; - - void _incrementCounter() { - setState(() { - // This call to setState tells the Flutter framework that something has - // changed in this State, which causes it to rerun the build method below - // so that the display can reflect the updated values. If we changed - // _counter without calling setState(), then the build method would not be - // called again, and so nothing would appear to happen. - _counter++; - }); + for (int i = 1; i < 10; i++) { + strengths.add(0.1 * i); } - - @override - Widget build(BuildContext context) { - // This method is rerun every time setState is called, for instance as done - // by the _incrementCounter method above. - // - // The Flutter framework has been optimized to make rerunning build methods - // fast, so that you can just rebuild anything that needs updating rather - // than having to individually change instances of widgets. - return Scaffold( - appBar: AppBar( - // TRY THIS: Try changing the color here to a specific color (to - // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar - // change color while the other colors stay the same. - backgroundColor: Theme.of(context).colorScheme.inversePrimary, - // Here we take the value from the MyHomePage object that was created by - // the App.build method, and use it to set our appbar title. - title: Text(widget.title), - ), - body: Center( - // Center is a layout widget. It takes a single child and positions it - // in the middle of the parent. - child: Column( - // Column is also a layout widget. It takes a list of children and - // arranges them vertically. By default, it sizes itself to fit its - // children horizontally, and tries to be as tall as its parent. - // - // Column has various properties to control how it sizes itself and - // how it positions its children. Here we use mainAxisAlignment to - // center the children vertically; the main axis here is the vertical - // axis because Columns are vertical (the cross axis would be - // horizontal). - // - // TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint" - // action in the IDE, or press "p" in the console), to see the - // wireframe for each widget. - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text('You have pushed the button this many times:'), - Text( - '$_counter', - style: Theme.of(context).textTheme.headlineMedium, - ), - ], - ), - ), - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment', - child: const Icon(Icons.add), - ), // This trailing comma makes auto-formatting nicer for build methods. + for (var strength in strengths) { + final double ds = 0.5 - strength; + swatch[(strength * 1000).round()] = Color.fromRGBO( + r + ((ds < 0 ? r : (255 - r)) * ds).round(), + g + ((ds < 0 ? g : (255 - g)) * ds).round(), + b + ((ds < 0 ? b : (255 - b)) * ds).round(), + 1, ); } + return MaterialColor(color.value, swatch); } diff --git a/lib/models/auth_model.dart b/lib/models/auth_model.dart new file mode 100644 index 0000000..4afdb30 --- /dev/null +++ b/lib/models/auth_model.dart @@ -0,0 +1,71 @@ +class AuthModel { + final String token; + final AuthKurirModel model; + + AuthModel({ + required this.token, + required this.model, + }); +} + +class AuthKurirModel { + User? user; + String? token; + + AuthKurirModel({this.user, this.token}); + + AuthKurirModel.fromJson(Map json) { + user = json['user'] != null ? User.fromJson(json['user']) : null; + token = json['token']; + } + + Map toJson() { + final Map data = {}; + if (user != null) { + data['user'] = user!.toJson(); + } + data['token'] = token; + return data; + } +} + +class User { + String? mUserID; + String? mUserUsername; + String? mStaffName; + String? ip; + String? agent; + String? mUserPassword = ""; + String? mCourierID; + + User( + {this.mUserID, + this.mUserUsername, + this.mStaffName, + this.ip, + this.agent, + this.mUserPassword, + this.mCourierID}); + + User.fromJson(Map json) { + mUserID = json['M_UserID']; + mUserUsername = json['M_UserUsername']; + mStaffName = json['Nat_StaffName']; + ip = json['ip']; + agent = json['agent']; + mCourierID = json['M_CourierID']; + mUserPassword = ""; + } + + Map toJson() { + final Map data = {}; + data['M_UserID'] = mUserID; + data['M_UserUsername'] = mUserUsername; + data['Nat_StaffName'] = mStaffName; + data['ip'] = ip; + data['agent'] = agent; + data['M_CourierID'] = mCourierID; + data['M_UserPassword'] = mUserPassword; + return data; + } +} diff --git a/lib/models/company_model.dart b/lib/models/company_model.dart new file mode 100644 index 0000000..c77fce7 --- /dev/null +++ b/lib/models/company_model.dart @@ -0,0 +1,32 @@ +class CompanyModel { + String? mCompanyID; + String? mCompanyName; + String? mCompanyNumber; + String? detail; + String? mCompanyAddress; + + CompanyModel( + {this.mCompanyID, + this.mCompanyName, + this.mCompanyNumber, + this.detail, + this.mCompanyAddress}); + + CompanyModel.fromJson(Map json) { + mCompanyID = json['M_CompanyID']; + mCompanyName = json['M_CompanyName']; + mCompanyNumber = json['M_CompanyNumber']; + detail = json['detail']; + mCompanyAddress = json['M_CompanyAddress']; + } + + Map toJson() { + final Map data = new Map(); + data['M_CompanyID'] = this.mCompanyID; + data['M_CompanyName'] = this.mCompanyName; + data['M_CompanyNumber'] = this.mCompanyNumber; + data['detail'] = this.detail; + data['M_CompanyAddress'] = this.mCompanyAddress; + return data; + } +} diff --git a/lib/models/detail_history_model.dart b/lib/models/detail_history_model.dart new file mode 100644 index 0000000..d9d8538 --- /dev/null +++ b/lib/models/detail_history_model.dart @@ -0,0 +1,149 @@ +import 'dart:convert'; + +import '../models/patient_model.dart'; + +// "note_penyerahan": "lengkap", +// "alamat_pengantaran": "Jl Sampangan no 231 ABA Barusari, Semarang Selatan, Semarang", +// "penerima": "Nicolas Saputra", +// "note_penerima": "oke lengkap", +class DetailHistoryModel { + String? id; + String? tipeid; + String? tipe; + String? noSuratJalan; + String? nama; + String? distance; + String? cdate; + String? alamatPengambilan; + String? petugasPenyerahan; + String? alamatPengantaran; + String? penerima; + String? lokasiPengambilanAwal; + String? latPengambilanAwal; + String? lngPengambilanAwal; + String? lokasiPengambilanAkhir; + String? latPengambilanAkhir; + String? lngPengambilanAkhir; + String? lokasiPengantaranAwal; + String? latPengantaranAwal; + String? lngPengantaranAwal; + String? lokasiPengantaranAkhir; + String? latPengantaranAkhir; + String? lngPengantaranAkhir; + String? notePenyerahan; + String? notePenerimaan; + List? patients; + String? foNote; + String? adminNote; + + DetailHistoryModel( + {this.id, + this.tipeid, + this.tipe, + this.noSuratJalan, + this.nama, + this.distance, + this.cdate, + this.alamatPengambilan, + this.petugasPenyerahan, + this.alamatPengantaran, + this.penerima, + this.lokasiPengambilanAwal, + this.latPengambilanAwal, + this.lngPengambilanAwal, + this.lokasiPengambilanAkhir, + this.latPengambilanAkhir, + this.lngPengambilanAkhir, + this.lokasiPengantaranAwal, + this.latPengantaranAwal, + this.lngPengantaranAwal, + this.lokasiPengantaranAkhir, + this.latPengantaranAkhir, + this.lngPengantaranAkhir, + this.notePenerimaan, + this.notePenyerahan, + this.foNote, + this.adminNote, + this.patients}); + + DetailHistoryModel.fromJson(Map json) { + id = json['id']; + tipeid = json['tipeid']; + tipe = json['tipe']; + noSuratJalan = json['no_surat_jalan']; + nama = json['nama']; + distance = json['distance']; + cdate = json['cdate']; + alamatPengambilan = json['alamat_pengambilan']; + petugasPenyerahan = json['petugas_penyerahan']; + alamatPengantaran = json['alamat_pengantaran']; + penerima = json['penerima']; + lokasiPengambilanAwal = json['lokasi_pengambilan_awal']; + latPengambilanAwal = json['lat_pengambilan_awal']; + lngPengambilanAwal = json['lng_pengambilan_awal']; + lokasiPengambilanAkhir = json['lokasi_pengambilan_akhir']; + latPengambilanAkhir = json['lat_pengambilan_akhir']; + lngPengambilanAkhir = json['lng_pengambilan_akhir']; + lokasiPengantaranAwal = json['lokasi_pengantaran_awal']; + latPengantaranAwal = json['lat_pengantaran_awal']; + lngPengantaranAwal = json['lng_pengantaran_awal']; + lokasiPengantaranAkhir = json['lokasi_pengantaran_akhir']; + latPengantaranAkhir = json['lat_pengantaran_akhir']; + lngPengantaranAkhir = json['lng_pengantaran_akhir']; + notePenerimaan = json['note_penerima']; + notePenyerahan = json['note_penyerahan']; + foNote = json['fo_note']; + adminNote = json['admin_note']; + // patients = json['patients']; + if (json['patients'].toString().isNotEmpty) { + var data = []; + // print("hasil json decode"); + // print(jsonDecode(json['patients'])); + // print("end"); + try { + jsonDecode(json['patients']).forEach((v) { + data.add(PatientModel.fromJson(v)); + }); + } catch (e) { + print(e); + } + patients = data; + } + } + + Map toJson() { + final Map data = new Map(); + data['id'] = this.id; + data['tipeid'] = this.tipeid; + data['tipe'] = this.tipe; + data['no_surat_jalan'] = this.noSuratJalan; + data['nama'] = this.nama; + data['distance'] = this.distance; + data['cdate'] = this.cdate; + data['alamat_pengambilan'] = this.alamatPengambilan; + data['petugas_penyerahan'] = this.petugasPenyerahan; + data['alamat_pengantaran'] = this.alamatPengantaran; + data['penerima'] = this.penerima; + data['lokasi_pengambilan_awal'] = this.lokasiPengambilanAwal; + data['lat_pengambilan_awal'] = this.latPengambilanAwal; + data['lng_pengambilan_awal'] = this.lngPengambilanAwal; + data['lokasi_pengambilan_akhir'] = this.lokasiPengambilanAkhir; + data['lat_pengambilan_akhir'] = this.latPengambilanAkhir; + data['lng_pengambilan_akhir'] = this.lngPengambilanAkhir; + data['lokasi_pengantaran_awal'] = this.lokasiPengantaranAwal; + data['lat_pengantaran_awal'] = this.latPengantaranAwal; + data['lng_pengantaran_awal'] = this.lngPengantaranAwal; + data['lokasi_pengantaran_akhir'] = this.lokasiPengantaranAkhir; + data['lat_pengantaran_akhir'] = this.latPengantaranAkhir; + data['lng_pengantaran_akhir'] = this.lngPengantaranAkhir; + data['note_penyerahan'] = this.notePenyerahan; + data['note_penerima'] = this.notePenerimaan; + data['fo_note'] = this.foNote; + data['admin_note'] = this.adminNote; + // data['patients'] = this.patients; + if (this.patients != null || this.patients != "") { + data['patients'] = this.patients!.map((v) => v.toJson()).toList(); + } + return data; + } +} diff --git a/lib/models/detail_work_model.dart b/lib/models/detail_work_model.dart new file mode 100644 index 0000000..808fe53 --- /dev/null +++ b/lib/models/detail_work_model.dart @@ -0,0 +1,195 @@ +import 'dart:convert'; + +import '../models/patient_model.dart'; + +class DetailWorkModel { + String? id; + String? tipeid; + String? tipe; + String? noSuratJalan; + String? nama; + String? distance; + String? cdate; + String? alamatPengambilan; + String? petugasPenyerahan; + String? alamatPengantaran; + String? penerima; + String? lokasiPengambilanAwal; + String? latPengambilanAwal; + String? lngPengambilanAwal; + String? lokasiPengambilanAkhir; + String? latPengambilanAkhir; + String? lngPengambilanAkhir; + String? lokasiPengantaranAwal; + String? latPengantaranAwal; + String? lngPengantaranAwal; + String? lokasiPengantaranAkhir; + String? latPengantaranAkhir; + String? lngPengantaranAkhir; + String? notePenyerahan; + String? notePenerimaan; + List? patients; + // TAMBAHAN INPUTAN INFO + String? suhuBox; + String? suhuSample; + String? totalPasien; + String? totalEdta; + String? totalCitras; + String? totalSerum; + String? totalUrine; + String? totalPleura; + String? otherSample; + String? totalOther; + String? foNote; + String? adminNote; + String? branchID; + + DetailWorkModel( + {this.id, + this.tipeid, + this.tipe, + this.noSuratJalan, + this.nama, + this.distance, + this.cdate, + this.alamatPengambilan, + this.petugasPenyerahan, + this.alamatPengantaran, + this.penerima, + this.lokasiPengambilanAwal, + this.latPengambilanAwal, + this.lngPengambilanAwal, + this.lokasiPengambilanAkhir, + this.latPengambilanAkhir, + this.lngPengambilanAkhir, + this.lokasiPengantaranAwal, + this.latPengantaranAwal, + this.lngPengantaranAwal, + this.lokasiPengantaranAkhir, + this.latPengantaranAkhir, + this.lngPengantaranAkhir, + this.notePenerimaan, + this.notePenyerahan, + this.patients, + // TAMBAHAN INPUTAN INFO + this.suhuBox, + this.suhuSample, + this.totalPasien, + this.totalEdta, + this.totalCitras, + this.totalSerum, + this.totalUrine, + this.totalPleura, + this.otherSample, + this.totalOther, + this.foNote, + this.adminNote, + this.branchID}); + + DetailWorkModel.fromJson(Map json) { + id = json['id']; + tipeid = json['tipeid']; + tipe = json['tipe']; + noSuratJalan = json['no_surat_jalan']; + nama = json['nama']; + distance = json['distance']; + cdate = json['cdate']; + alamatPengambilan = json['alamat_pengambilan']; + petugasPenyerahan = json['petugas_penyerahan']; + alamatPengantaran = json['alamat_pengantaran']; + penerima = json['penerima']; + lokasiPengambilanAwal = json['lokasi_pengambilan_awal']; + latPengambilanAwal = json['lat_pengambilan_awal']; + lngPengambilanAwal = json['lng_pengambilan_awal']; + lokasiPengambilanAkhir = json['lokasi_pengambilan_akhir']; + latPengambilanAkhir = json['lat_pengambilan_akhir']; + lngPengambilanAkhir = json['lng_pengambilan_akhir']; + lokasiPengantaranAwal = json['lokasi_pengantaran_awal']; + latPengantaranAwal = json['lat_pengantaran_awal']; + lngPengantaranAwal = json['lng_pengantaran_awal']; + lokasiPengantaranAkhir = json['lokasi_pengantaran_akhir']; + latPengantaranAkhir = json['lat_pengantaran_akhir']; + lngPengantaranAkhir = json['lng_pengantaran_akhir']; + notePenerimaan = json['note_penerima']; + notePenyerahan = json['note_penyerahan']; + // patients = json['patients']; + if (json['patients'].toString().isNotEmpty) { + var data = []; + // print("hasil json decode"); + // print(jsonDecode(json['patients'])); + // print("end"); + try { + jsonDecode(json['patients']).forEach((v) { + data.add(PatientModel.fromJson(v)); + }); + } catch (e) { + print(e); + } + patients = data; + } + + // TAMBAHAN INPUTAN INFO + suhuBox = json['suhu_box']; + suhuSample = json['suhu_sample']; + totalPasien = json['total_pasien']; + totalEdta = json['total_edta']; + totalCitras = json['total_citras']; + totalSerum = json['total_serum']; + totalUrine = json['total_urine']; + totalPleura = json['total_pleura']; + otherSample = json['other_sample']; + totalOther = json['total_other']; + foNote = json['fo_note']; + adminNote = json['admin_note']; + branchID = json['branchid']; + } + + Map toJson() { + final Map data = new Map(); + data['id'] = this.id; + data['tipeid'] = this.tipeid; + data['tipe'] = this.tipe; + data['no_surat_jalan'] = this.noSuratJalan; + data['nama'] = this.nama; + data['distance'] = this.distance; + data['cdate'] = this.cdate; + data['alamat_pengambilan'] = this.alamatPengambilan; + data['petugas_penyerahan'] = this.petugasPenyerahan; + data['alamat_pengantaran'] = this.alamatPengantaran; + data['penerima'] = this.penerima; + data['lokasi_pengambilan_awal'] = this.lokasiPengambilanAwal; + data['lat_pengambilan_awal'] = this.latPengambilanAwal; + data['lng_pengambilan_awal'] = this.lngPengambilanAwal; + data['lokasi_pengambilan_akhir'] = this.lokasiPengambilanAkhir; + data['lat_pengambilan_akhir'] = this.latPengambilanAkhir; + data['lng_pengambilan_akhir'] = this.lngPengambilanAkhir; + data['lokasi_pengantaran_awal'] = this.lokasiPengantaranAwal; + data['lat_pengantaran_awal'] = this.latPengantaranAwal; + data['lng_pengantaran_awal'] = this.lngPengantaranAwal; + data['lokasi_pengantaran_akhir'] = this.lokasiPengantaranAkhir; + data['lat_pengantaran_akhir'] = this.latPengantaranAkhir; + data['lng_pengantaran_akhir'] = this.lngPengantaranAkhir; + data['note_penyerahan'] = this.notePenyerahan; + data['note_penerima'] = this.notePenerimaan; + // data['patients'] = this.patients; + if (this.patients != null || this.patients != "") { + data['patients'] = this.patients!.map((v) => v.toJson()).toList(); + } + + // TAMBAHAN INPUTAN INFO + data['suhu_box'] = this.suhuBox; + data['suhu_sample'] = this.suhuSample; + data['total_pasien'] = this.totalPasien; + data['total_edta'] = this.totalEdta; + data['total_citras'] = this.totalCitras; + data['total_serum'] = this.totalSerum; + data['total_urine'] = this.totalUrine; + data['total_pleura'] = this.totalPleura; + data['other_sample'] = this.otherSample; + data['total_other'] = this.totalOther; + data['fo_note'] = this.foNote; + data['admin_note'] = this.adminNote; + data['branchid'] = this.branchID; + return data; + } +} diff --git a/lib/models/history_model.dart b/lib/models/history_model.dart new file mode 100644 index 0000000..e8b9509 --- /dev/null +++ b/lib/models/history_model.dart @@ -0,0 +1,44 @@ +class HistoryModel { + String? id; + int? tipeid; + String? tipe; + String? noSuratJalan; + String? nama; + String? distance; + String? cdate; + String? ispending; + + HistoryModel( + {this.id, + this.tipeid, + this.tipe, + this.noSuratJalan, + this.nama, + this.distance, + this.cdate, + this.ispending}); + + HistoryModel.fromJson(Map json) { + id = json['id']; + tipeid = int.parse(json['tipeid']); + tipe = json['tipe']; + noSuratJalan = json['no_surat_jalan']; + nama = json['nama']; + distance = json['distance']; + cdate = json['cdate']; + ispending = json['ispending']; + } + + Map toJson() { + final Map data = new Map(); + data['id'] = this.id; + data['tipeid'] = this.tipeid; + data['tipe'] = this.tipe; + data['no_surat_jalan'] = this.noSuratJalan; + data['nama'] = this.nama; + data['distance'] = this.distance; + data['cdate'] = this.cdate; + data['ispending'] = this.ispending; + return data; + } +} diff --git a/lib/models/history_type_model.dart b/lib/models/history_type_model.dart new file mode 100644 index 0000000..e9e3c50 --- /dev/null +++ b/lib/models/history_type_model.dart @@ -0,0 +1,23 @@ +import 'package:equatable/equatable.dart'; + +class HistoryTypeModel extends Equatable { + String? tipeid; + String? tipename; + + HistoryTypeModel({this.tipeid, this.tipename}); + + HistoryTypeModel.fromJson(Map json) { + tipeid = json['tipeid']; + tipename = json['tipename']; + } + + Map toJson() { + final Map data = new Map(); + data['tipeid'] = this.tipeid; + data['tipename'] = this.tipename; + return data; + } + + @override + List get props => [tipeid]; +} diff --git a/lib/models/patient_model.dart b/lib/models/patient_model.dart new file mode 100644 index 0000000..86197b2 --- /dev/null +++ b/lib/models/patient_model.dart @@ -0,0 +1,32 @@ +class PatientModel { + String? tOrderDeliveryID; + String? tOrderHeaderID; + String? tOrderHeaderLabNumber; + String? pasien; + String? courierConfirmNote; + + PatientModel( + {this.tOrderDeliveryID, + this.tOrderHeaderID, + this.tOrderHeaderLabNumber, + this.pasien, + this.courierConfirmNote}); + + PatientModel.fromJson(Map json) { + tOrderDeliveryID = json['T_OrderDeliveryID']; + tOrderHeaderID = json['T_OrderHeaderID']; + tOrderHeaderLabNumber = json['T_OrderHeaderLabNumber']; + pasien = json['pasien']; + courierConfirmNote = json['courier_confirmNote']; + } + + Map toJson() { + final Map data = new Map(); + data['T_OrderDeliveryID'] = this.tOrderDeliveryID; + data['T_OrderHeaderID'] = this.tOrderHeaderID; + data['T_OrderHeaderLabNumber'] = this.tOrderHeaderLabNumber; + data['pasien'] = this.pasien; + data['courier_confirmNote'] = this.courierConfirmNote; + return data; + } +} diff --git a/lib/models/pending_work_model.dart b/lib/models/pending_work_model.dart new file mode 100644 index 0000000..37e347e --- /dev/null +++ b/lib/models/pending_work_model.dart @@ -0,0 +1,92 @@ +import 'dart:convert'; + +import '../models/patient_model.dart'; + +class PendingWorkModel { + int? id; + int? tipeid; + String? tipe; + String? noSuratJalan; + String? nama; + String? noLab; + String? alamat; + String? statusTransaksi; + String? branchid; + String? branchcode; + String? branchname; + String? branchaddress; + String? supervisor; + List? patients; + String? note; + + PendingWorkModel( + {this.id, + this.tipeid, + this.tipe, + this.noSuratJalan, + this.nama, + this.noLab, + this.alamat, + this.statusTransaksi, + this.branchid, + this.branchcode, + this.branchname, + this.branchaddress, + this.patients, + this.note}); + + PendingWorkModel.fromJson(Map json) { + id = int.parse(json['id']); + tipeid = int.parse(json['tipeid']); + tipe = json['tipe']; + noSuratJalan = json['no_surat_jalan']; + nama = json['nama']; + noLab = json['no_lab']; + alamat = json['alamat']; + statusTransaksi = json['status_transaksi']; + branchid = json['branchid']; + branchcode = json['branchcode']; + branchname = json['branchname']; + branchaddress = json['branchaddress']; + note = json['note']; + supervisor = json['supervisor']; + // print("patient :" + json['patients']); + if (json['patients'].toString().isNotEmpty) { + var data = []; + // print("hasil json decode"); + // print(jsonDecode(json['patients'])); + // print("end"); + try { + jsonDecode(json['patients']).forEach((v) { + data.add(PatientModel.fromJson(v)); + }); + } catch (e) { + print(e); + } + patients = data; + } + } + + Map toJson() { + final Map data = new Map(); + data['id'] = this.id; + data['tipeid'] = this.tipeid; + data['tipe'] = this.tipe; + data['no_surat_jalan'] = this.noSuratJalan; + data['nama'] = this.nama; + data['no_lab'] = this.noLab; + data['alamat'] = this.alamat; + data['status_transaksi'] = this.statusTransaksi; + data['branchid'] = this.branchid; + data['branchcode'] = this.branchcode; + data['branchname'] = this.branchname; + data['branchaddress'] = this.branchaddress; + data['patients'] = this.patients; + if (this.patients != null || this.patients != "") { + data['patients'] = this.patients!.map((v) => v.toJson()).toList(); + } + data['note'] = this.note; + data['supervisor'] = this.supervisor; + return data; + } +} diff --git a/lib/models/personal_information_model.dart b/lib/models/personal_information_model.dart new file mode 100644 index 0000000..e35b9c0 --- /dev/null +++ b/lib/models/personal_information_model.dart @@ -0,0 +1,36 @@ +class PersonalInformationModel { + String? mUserID; + String? mUserUsername; + String? mUserPassword; + String? natStaffName; + String? natStaffNIK; + String? mCourierID; + + PersonalInformationModel( + {this.mUserID, + this.mUserUsername, + this.mUserPassword, + this.natStaffName, + this.natStaffNIK, + this.mCourierID}); + + PersonalInformationModel.fromJson(Map json) { + mUserID = json['M_UserID']; + mUserUsername = json['M_UserUsername']; + mUserPassword = json['M_UserPassword']; + natStaffName = json['Nat_StaffName']; + natStaffNIK = json['Nat_StaffNIK']; + mCourierID = json['M_CourierID']; + } + + Map toJson() { + final Map data = new Map(); + data['M_UserID'] = this.mUserID; + data['M_UserUsername'] = this.mUserUsername; + data['M_UserPassword'] = this.mUserPassword; + data['Nat_StaffName'] = this.natStaffName; + data['Nat_StaffNIK'] = this.natStaffNIK; + data['M_CourierID'] = this.mCourierID; + return data; + } +} \ No newline at end of file diff --git a/lib/models/response_list_branch_model.dart b/lib/models/response_list_branch_model.dart new file mode 100644 index 0000000..3bb385e --- /dev/null +++ b/lib/models/response_list_branch_model.dart @@ -0,0 +1,25 @@ +class ResponseListBranchModel { + String? branchid; + String? branchcode; + String? branchname; + String? branchaddress; + + ResponseListBranchModel( + {this.branchid, this.branchcode, this.branchname, this.branchaddress}); + + ResponseListBranchModel.fromJson(Map json) { + branchid = json['branchid']; + branchcode = json['branchcode']; + branchname = json['branchname']; + branchaddress = json['branchaddress']; + } + + Map toJson() { + final Map data = {}; + data['branchid'] = branchid; + data['branchcode'] = branchcode; + data['branchname'] = branchname; + data['branchaddress'] = branchaddress; + return data; + } +} \ No newline at end of file diff --git a/lib/models/response_save_change_password_model.dart b/lib/models/response_save_change_password_model.dart new file mode 100644 index 0000000..99d8728 --- /dev/null +++ b/lib/models/response_save_change_password_model.dart @@ -0,0 +1,21 @@ +class ResponseSaveChangePasswordModel { + String? status; + String? message; + + ResponseSaveChangePasswordModel({ + this.status, + this.message, + }); + + ResponseSaveChangePasswordModel.fromJson(Map json) { + status = json['status']; + message = json['message']; + } + + Map toJson() { + final Map data = {}; + data['status'] = status; + data['message'] = message; + return data; + } +} diff --git a/lib/models/response_save_pengantaran_hasil_model.dart b/lib/models/response_save_pengantaran_hasil_model.dart new file mode 100644 index 0000000..ac0bdd8 --- /dev/null +++ b/lib/models/response_save_pengantaran_hasil_model.dart @@ -0,0 +1,21 @@ +class ResponseSavePengantaranHasilModel { + String? status; + String? message; + + ResponseSavePengantaranHasilModel({ + this.status, + this.message, + }); + + ResponseSavePengantaranHasilModel.fromJson(Map json) { + status = json['status']; + message = json['message']; + } + + Map toJson() { + final Map data = {}; + data['status'] = status; + data['message'] = message; + return data; + } +} diff --git a/lib/models/response_save_work_suhu_model.dart b/lib/models/response_save_work_suhu_model.dart new file mode 100644 index 0000000..8fb2d40 --- /dev/null +++ b/lib/models/response_save_work_suhu_model.dart @@ -0,0 +1,21 @@ +class ResponseSaveWorkSuhuModel { + String? status; + String? message; + + ResponseSaveWorkSuhuModel({ + this.status, + this.message, + }); + + ResponseSaveWorkSuhuModel.fromJson(Map json) { + status = json['status']; + message = json['message']; + } + + Map toJson() { + final Map data = {}; + data['status'] = status; + data['message'] = message; + return data; + } +} diff --git a/lib/models/response_save_work_tunda_model.dart b/lib/models/response_save_work_tunda_model.dart new file mode 100644 index 0000000..081b338 --- /dev/null +++ b/lib/models/response_save_work_tunda_model.dart @@ -0,0 +1,21 @@ +class ResponseSaveWorkTundaModel { + String? status; + String? message; + + ResponseSaveWorkTundaModel({ + this.status, + this.message, + }); + + ResponseSaveWorkTundaModel.fromJson(Map json) { + status = json['status']; + message = json['message']; + } + + Map toJson() { + final Map data = {}; + data['status'] = status; + data['message'] = message; + return data; + } +} diff --git a/lib/models/response_search_pengantaran_hasil_model.dart b/lib/models/response_search_pengantaran_hasil_model.dart new file mode 100644 index 0000000..f48fcd4 --- /dev/null +++ b/lib/models/response_search_pengantaran_hasil_model.dart @@ -0,0 +1,86 @@ +import 'dart:convert'; + +import 'patient_model.dart'; + +class ResponseSearchPengantaranHasilModel { + String? deliveryid; + String? tipeid; + String? tipe; + String? sourceId; + String? nama; + String? nomor; + String? detail; + String? alamat; + String? branchid; + String? branchcode; + String? branchname; + String? branchaddress; + List? patients; + + ResponseSearchPengantaranHasilModel( + {this.deliveryid, + this.tipeid, + this.tipe, + this.sourceId, + this.nama, + this.nomor, + this.detail, + this.alamat, + this.branchid, + this.branchcode, + this.branchname, + this.branchaddress, + this.patients}); + + ResponseSearchPengantaranHasilModel.fromJson(Map json) { + deliveryid = json['id']; + tipeid = json['tipeid']; + tipe = json['tipe']; + sourceId = json['source_id']; + nama = json['nama']; + nomor = json['nomor']; + detail = json['detail']; + alamat = json['alamat']; + branchid = json['branchid']; + branchcode = json['branchcode']; + branchname = json['branchname']; + branchaddress = json['branchaddress']; + // patients = json['patients']; + + if (json['patients'].toString().isNotEmpty) { + var data = []; + // print("hasil json decode"); + // print(jsonDecode(json['patients'])); + // print("end"); + try { + jsonDecode(json['patients']).forEach((v) { + data.add(PatientModel.fromJson(v)); + }); + } catch (e) { + print(e); + } + patients = data; + } + } + + Map toJson() { + final Map data = {}; + data['id'] = deliveryid; + data['tipeid'] = tipeid; + data['tipe'] = tipe; + data['source_id'] = sourceId; + data['nama'] = nama; + data['nomor'] = nomor; + data['detail'] = detail; + data['alamat'] = alamat; + data['branchid'] = branchid; + data['branchcode'] = branchcode; + data['branchname'] = branchname; + data['branchaddress'] = branchaddress; + // data['patients'] = patients; + if (patients != null || patients != "") { + data['patients'] = patients!.map((v) => v.toJson()).toList(); + } + return data; + } +} diff --git a/lib/models/work_list_model.dart b/lib/models/work_list_model.dart new file mode 100644 index 0000000..99d5288 --- /dev/null +++ b/lib/models/work_list_model.dart @@ -0,0 +1,92 @@ +import 'dart:convert'; + +import '../models/patient_model.dart'; + +class WorkListModel { + int? id; + int? tipeid; + String? tipe; + String? noSuratJalan; + String? nama; + String? noLab; + String? alamat; + String? statusTransaksi; + String? branchid; + String? branchcode; + String? branchname; + String? branchaddress; + String? supervisor; + List? patients; + String? note; + + WorkListModel( + {this.id, + this.tipeid, + this.tipe, + this.noSuratJalan, + this.nama, + this.noLab, + this.alamat, + this.statusTransaksi, + this.branchid, + this.branchcode, + this.branchname, + this.branchaddress, + this.patients, + this.note}); + + WorkListModel.fromJson(Map json) { + id = int.parse(json['id']); + tipeid = int.parse(json['tipeid']); + tipe = json['tipe']; + noSuratJalan = json['no_surat_jalan']; + nama = json['nama']; + noLab = json['no_lab']; + alamat = json['alamat']; + statusTransaksi = json['status_transaksi']; + branchid = json['branchid']; + branchcode = json['branchcode']; + branchname = json['branchname']; + branchaddress = json['branchaddress']; + note = json['note']; + supervisor = json['supervisor']; + // print("patient :" + json['patients']); + if (json['patients'].toString().isNotEmpty) { + var data = []; + // print("hasil json decode"); + // print(jsonDecode(json['patients'])); + // print("end"); + try { + jsonDecode(json['patients']).forEach((v) { + data.add(PatientModel.fromJson(v)); + }); + } catch (e) { + print(e); + } + patients = data; + } + } + + Map toJson() { + final Map data = new Map(); + data['id'] = this.id; + data['tipeid'] = this.tipeid; + data['tipe'] = this.tipe; + data['no_surat_jalan'] = this.noSuratJalan; + data['nama'] = this.nama; + data['no_lab'] = this.noLab; + data['alamat'] = this.alamat; + data['status_transaksi'] = this.statusTransaksi; + data['branchid'] = this.branchid; + data['branchcode'] = this.branchcode; + data['branchname'] = this.branchname; + data['branchaddress'] = this.branchaddress; + data['patients'] = this.patients; + if (this.patients != null || this.patients != "") { + data['patients'] = this.patients!.map((v) => v.toJson()).toList(); + } + data['note'] = this.note; + data['supervisor'] = this.supervisor; + return data; + } +} diff --git a/lib/models/work_status_model.dart b/lib/models/work_status_model.dart new file mode 100644 index 0000000..89eb96e --- /dev/null +++ b/lib/models/work_status_model.dart @@ -0,0 +1,18 @@ +class WorkStatusModel { + String? statusid; + String? statusname; + + WorkStatusModel({this.statusid, this.statusname}); + + WorkStatusModel.fromJson(Map json) { + statusid = json['statusid']; + statusname = json['statusname']; + } + + Map toJson() { + final Map data = new Map(); + data['statusid'] = this.statusid; + data['statusname'] = this.statusname; + return data; + } +} diff --git a/lib/models/work_supervisor_model.dart b/lib/models/work_supervisor_model.dart new file mode 100644 index 0000000..ea16e09 --- /dev/null +++ b/lib/models/work_supervisor_model.dart @@ -0,0 +1,18 @@ +class WorkSupervisorModel { + String? supervisorid; + String? supervisorname; + + WorkSupervisorModel({this.supervisorid, this.supervisorname}); + + WorkSupervisorModel.fromJson(Map json) { + supervisorid = json['supervisorid']; + supervisorname = json['supervisorname']; + } + + Map toJson() { + final Map data = new Map(); + data['supervisorid'] = this.supervisorid; + data['supervisorname'] = this.supervisorname; + return data; + } +} diff --git a/lib/models/work_total_model.dart b/lib/models/work_total_model.dart new file mode 100644 index 0000000..0c4e8cb --- /dev/null +++ b/lib/models/work_total_model.dart @@ -0,0 +1,21 @@ +class WorkTotalModel { + String? totalAll; + String? receive; + String? done; + + WorkTotalModel({this.totalAll, this.receive, this.done}); + + WorkTotalModel.fromJson(Map json) { + totalAll = json['total_all']; + receive = json['receive']; + done = json['done']; + } + + Map toJson() { + final Map data = new Map(); + data['total_all'] = this.totalAll; + data['receive'] = this.receive; + data['done'] = this.done; + return data; + } +} diff --git a/lib/models/work_type_model.dart b/lib/models/work_type_model.dart new file mode 100644 index 0000000..229f7f8 --- /dev/null +++ b/lib/models/work_type_model.dart @@ -0,0 +1,32 @@ +class WorkTypeModel { + int? id; + String? name; + + WorkTypeModel({this.id, this.name}); + + WorkTypeModel.fromJson(Map json) { + id = int.parse(json['tipeid']); + name = json['tipename']; + } + + Map toJson() { + final Map data = new Map(); + data['tipeid'] = this.id; + data['tipename'] = this.name; + return data; + } +} + +// 1 Pengantaran Hasil Pasien +// 2 Pengantaran Hasil Instansi +// 3 Pengantaran Hasil Dokter +// 4 Lain-lain +// 5 Pengambilan Sampel + +// List WorkTypeData = [ +// WorkTypeModel(id: 1, name: "Pengantaran Hasil Pasien"), +// WorkTypeModel(id: 2, name: "Pengantaran Hasil Instansi"), +// WorkTypeModel(id: 3, name: "Pengantaran Hasil Dokter"), +// WorkTypeModel(id: 4, name: "Lain-lain"), +// WorkTypeModel(id: 5, name: "Pengambilan Sampel"), +// ]; diff --git a/lib/provider/current_scan_provider.dart b/lib/provider/current_scan_provider.dart new file mode 100644 index 0000000..96c53e5 --- /dev/null +++ b/lib/provider/current_scan_provider.dart @@ -0,0 +1,4 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +final barcodeScanResult = StateProvider((ref) => ""); +final isFromScanScreen = StateProvider((ref) => false); \ No newline at end of file diff --git a/lib/provider/current_user_provider.dart b/lib/provider/current_user_provider.dart new file mode 100644 index 0000000..8a32ce7 --- /dev/null +++ b/lib/provider/current_user_provider.dart @@ -0,0 +1,5 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../models/auth_model.dart'; + +final currentUserProvider = StateProvider((ref) => null); diff --git a/lib/provider/dio_provider.dart b/lib/provider/dio_provider.dart new file mode 100644 index 0000000..508861f --- /dev/null +++ b/lib/provider/dio_provider.dart @@ -0,0 +1,8 @@ +import 'package:dio/dio.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +final dioProvider = Provider((ref) => Dio()); + +// final pageProvider = StateProvider( +// (ref) => 0, +// ); diff --git a/lib/provider/history_filter_provider.dart b/lib/provider/history_filter_provider.dart new file mode 100644 index 0000000..5ac2733 --- /dev/null +++ b/lib/provider/history_filter_provider.dart @@ -0,0 +1,8 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:intl/intl.dart'; + +import '../models/history_type_model.dart'; + +final selectedTypeProvider = StateProvider( + (ref) => HistoryTypeModel(tipeid: "0", tipename: "Semua")); +final dateFilterProvider = StateProvider((ref) => DateTime.now()); diff --git a/lib/repository/auth_repository.dart b/lib/repository/auth_repository.dart new file mode 100644 index 0000000..5008e0b --- /dev/null +++ b/lib/repository/auth_repository.dart @@ -0,0 +1,51 @@ +import '../app/constant.dart'; +import '../models/auth_model.dart'; +import 'base_repository.dart'; + +class AuthRepository extends BaseRepository { + AuthRepository({required super.dio}); + + Future login({ + required String username, + required String password, + }) async { + final param = { + "username": username, + "password": password + + // "username": "alhadad1", + // "doctor_id": "2891", + // "password": "3" + }; + final service = + "${Constant.baseUrl}v1/courier/login/login/?username=$username&password=$password"; + final resp = await post(param: param, service: service); + final result = AuthModel( + token: resp['data']["token"], + model: AuthKurirModel.fromJson(resp["data"]), + ); + return result; + } + + Future logout({ + required String M_UserID, + required String M_UserUsername, + }) async { + final param = { + "M_UserID": M_UserID, + "M_UserUsername": M_UserUsername + // "username": "alhadad", + // "doctor_id": "3101210841", + // "password": "riau123" + }; + + final service = "${Constant.baseUrl}v1/courier/login/logout"; + final resp = await post(param: param, service: service); + + if (resp["status"] == "OK") { + return resp['status']; + } else { + return resp['message']; + } + } +} diff --git a/lib/repository/base_repository.dart b/lib/repository/base_repository.dart new file mode 100644 index 0000000..5e6a5da --- /dev/null +++ b/lib/repository/base_repository.dart @@ -0,0 +1,100 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:dio/dio.dart'; + +abstract class BaseRepository { + final Dio dio; + BaseRepository({required this.dio}); + + Future> post({ + required Map param, + required String service, + String? token, + }) async { + try { + final response = await dio.post( + 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 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> get({ + required String service, + String? token, + }) async { + try { + final response = await dio.get( + service, + 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 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, + }); +} \ No newline at end of file diff --git a/lib/repository/change_password_repository.dart b/lib/repository/change_password_repository.dart new file mode 100644 index 0000000..f6ddb9f --- /dev/null +++ b/lib/repository/change_password_repository.dart @@ -0,0 +1,29 @@ +import 'dart:convert'; + +import '../models/personal_information_model.dart'; + +import '../app/constant.dart'; +import '../models/response_save_change_password_model.dart'; +import 'base_repository.dart'; + +class ChangePasswordRepository extends BaseRepository { + ChangePasswordRepository({required super.dio}); + + Future ubahPassword( + {required String userID, + required String oldPassword, + required String newPassword}) async { + final param = { + "userid": userID, + "old": oldPassword, + "new": newPassword, + }; + + print(param); + + final url = + "${Constant.baseUrl}/v1/courier/login/change_password/?userid=$userID&old=$oldPassword&new=$newPassword"; + final resp = await post(param: param, service: url); + return ResponseSaveChangePasswordModel.fromJson(resp); + } +} diff --git a/lib/repository/company_repository.dart b/lib/repository/company_repository.dart new file mode 100644 index 0000000..f04a92b --- /dev/null +++ b/lib/repository/company_repository.dart @@ -0,0 +1,35 @@ +import '../models/company_model.dart'; +import '../models/work_status_model.dart'; +import '../models/work_supervisor_model.dart'; + +import '../app/constant.dart'; +import '../models/work_type_model.dart'; +import 'base_repository.dart'; + +class CompanyRepository extends BaseRepository { + CompanyRepository({required super.dio}); + + Future> search({required String keyword}) async { + // http: //10.9.9.3/one-api/v1/courier/mobile/inputcourier/search_company_all/?search=sas + final url = Constant.baseUrl + + "/v1/courier/mobile/inputcourier/search_company_all/?search=$keyword"; + final resp = await get(service: url); + List data; + // print(resp['data']); + if (resp['status'] == 'OK') { + data = []; + if (resp['data'] != null) { + resp['data']['records'].forEach((e) { + final model = CompanyModel.fromJson(e); + data.add(model); + }); + } else { + data = []; + } + } else { + data = []; + } + + return data; + } +} diff --git a/lib/repository/filter_repository.dart b/lib/repository/filter_repository.dart new file mode 100644 index 0000000..6ed318a --- /dev/null +++ b/lib/repository/filter_repository.dart @@ -0,0 +1,65 @@ +import '../models/work_status_model.dart'; +import '../models/work_supervisor_model.dart'; + +import '../app/constant.dart'; +import '../models/work_type_model.dart'; +import 'base_repository.dart'; + +class FilterRepository extends BaseRepository { + FilterRepository({required super.dio}); + + Future> getType() async { + final url = Constant.baseUrl + "/v1/courier/mobile/homescreen/list_tipe"; + final resp = await get(service: url); + List data; + // print(resp['data']); + if (resp['status'] == 'OK') { + data = []; + resp['data'].forEach((e) { + final model = WorkTypeModel.fromJson(e); + data.add(model); + }); + } else { + data = []; + } + + return data; + } + + Future> getStatus() async { + final url = Constant.baseUrl + "v1/courier/mobile/homescreen/list_status"; + final resp = await get(service: url); + List data; + // print(resp['data']); + if (resp['status'] == 'OK') { + data = []; + resp['data'].forEach((e) { + final model = WorkStatusModel.fromJson(e); + data.add(model); + }); + } else { + data = []; + } + + return data; + } + + Future> getSupervisor() async { + final url = + Constant.baseUrl + "v1/courier/mobile/homescreen/list_supervisor"; + final resp = await get(service: url); + List data; + // print(resp['data']); + if (resp['status'] == 'OK') { + data = []; + resp['data'].forEach((e) { + final model = WorkSupervisorModel.fromJson(e); + data.add(model); + }); + } else { + data = []; + } + + return data; + } +} diff --git a/lib/repository/get_other_repository.dart b/lib/repository/get_other_repository.dart new file mode 100644 index 0000000..8a6f629 --- /dev/null +++ b/lib/repository/get_other_repository.dart @@ -0,0 +1,59 @@ +import '../app/constant.dart'; +import 'base_repository.dart'; + +class GetOtherRepository extends BaseRepository { + GetOtherRepository({required super.dio}); + + Future setLocation( + {required String id, + required String step, + required String lat, + required String long, + required String address, + required String branchID}) async { + final url = + "${Constant.baseUrl}/v1/courier/mobile/process/add_other/?id=$id&locid=$step&lat=$lat&long=$long&address=$address&branchid=$branchID"; + final resp = await get(service: url); + bool data = false; + + if (resp['status'] == "OK") { + data = true; + } + + return data; + } + + Future setDeliveryPerson({ + required String id, + required String name, + required String note, + }) async { + final url = + "${Constant.baseUrl}/v1/courier/mobile/process/update_deliver_other/?id=$id&name=$name¬e=$note"; + final resp = await get(service: url); + bool data = false; + + if (resp['status'] == "OK") { + data = true; + } + + return data; + } + + Future setReceivePerson({ + required String id, + required String name, + required String note, + }) async { + final url = + "${Constant.baseUrl}/v1/courier/mobile/process/update_receive_other/?id=$id&name=$name¬e=$note"; + final resp = await get(service: url); + bool data = false; + + if (resp['status'] == "OK") { + data = true; + } + + return data; + } +} diff --git a/lib/repository/get_sample_repository.dart b/lib/repository/get_sample_repository.dart new file mode 100644 index 0000000..d10bcbb --- /dev/null +++ b/lib/repository/get_sample_repository.dart @@ -0,0 +1,72 @@ +import '../app/constant.dart'; +import 'base_repository.dart'; + +class GetSampleRepository extends BaseRepository { + GetSampleRepository({required super.dio}); + + Future setLocation( + {required String id, + required String step, + required String lat, + required String long, + required String address, + required String branchID}) async { + final url = + "${Constant.baseUrl}/v1/courier/mobile/process/add_sample/?id=$id&locid=$step&lat=$lat&long=$long&address=$address&branchid=$branchID"; + final resp = await get(service: url); + bool data = false; + + if (resp['status'] == "OK") { + data = true; + } + + return data; + } + + Future setDeliveryPerson({ + required String id, + required String name, + required String note, + // Inputan INFO + required String jumlahPasien, + required String edtaRutin, + required String darahCitras, + required String serum, + required String urine, + required String pleura, + required String otherTextBahan, + required String totalOther, + }) async { + // final url = + // "${Constant.baseUrl}/v1/courier/mobile/process/update_deliver_sample/?id=$id&name=$name¬e=$note"; + + final url = + "${Constant.baseUrl}/v1/courier/mobile/process/update_deliver_sample/?id=$id&name=$name¬e=$note&t_patient=$jumlahPasien&t_edta=$edtaRutin&t_citras=$darahCitras&t_serum=$serum&t_urine=$urine&t_pleura=$pleura&t_other=$totalOther&other=$otherTextBahan"; + final resp = await get(service: url); + bool data = false; + + if (resp['status'] == "OK") { + data = true; + } + + return data; + } + + Future setReceivePerson({ + required String id, + required String name, + required String note, + required String suhuSample, + }) async { + final url = + "${Constant.baseUrl}/v1/courier/mobile/process/update_receive_sample/?id=$id&name=$name¬e=$note&suhu=$suhuSample"; + final resp = await get(service: url); + bool data = false; + + if (resp['status'] == "OK") { + data = true; + } + + return data; + } +} diff --git a/lib/repository/history_screen_repository.dart b/lib/repository/history_screen_repository.dart new file mode 100644 index 0000000..f3123bd --- /dev/null +++ b/lib/repository/history_screen_repository.dart @@ -0,0 +1,78 @@ +import 'dart:convert'; + +import '../models/detail_history_model.dart'; +import '../models/history_model.dart'; +import '../models/history_type_model.dart'; +import '../models/work_status_model.dart'; +import '../models/work_supervisor_model.dart'; + +import '../app/constant.dart'; +import '../models/work_type_model.dart'; +import 'base_repository.dart'; + +class HistoryRepository extends BaseRepository { + HistoryRepository({required super.dio}); + + Future> getData( + {required String id, required String date, required String type}) async { + // http: //10.9.9.3/one-api/v1/courier/mobile/history/list_all/?id=40&startdate=11-09-2023&tipeid=3 + final url = Constant.baseUrl + + "/v1/courier/mobile/history/list_all/?id=$id&startdate=$date&tipeid=$type"; + final resp = await get(service: url); + List data = []; + print(resp['data']); + if (resp['status'] == 'OK') { + data = []; + resp['data'].forEach((e) { + final model = HistoryModel.fromJson(e); + data.add(model); + }); + } else { + data = []; + } + + return data; + } + + Future getDetail( + {required String id, + required String tipeId, + required String deliveryId}) async { + final url = + "${Constant.baseUrl}/v1/courier/mobile/history/list_by_id/?id=$id&tipeid=$tipeId&deliveryid=$deliveryId"; + final resp = await get(service: url); + // DetailHistoryModel data; + // print(resp['data']); + + DetailHistoryModel data = DetailHistoryModel.fromJson(resp['data'][0]); + + // resp['data'].forEach((e) { + // final model = HistoryModel.fromJson(e); + // data.add(model); + // }); + // } + // print(data.alamatPengambilan); + + return data; + } + + Future> getHistoryType() async { + // http: //10.9.9.3/one-api/v1/courier/mobile/homescreen/list_tipe_history + final url = + "${Constant.baseUrl}/v1/courier/mobile/homescreen/list_tipe_history"; + final resp = await get(service: url); + // DetailHistoryModel data; + // print(resp['data']); + + List data = List.empty(growable: true); + + resp['data'].forEach((e) { + final model = HistoryTypeModel.fromJson(e); + data.add(model); + }); + + // print(data.alamatPengambilan); + + return data; + } +} diff --git a/lib/repository/home_screen_repository.dart b/lib/repository/home_screen_repository.dart new file mode 100644 index 0000000..6f3b288 --- /dev/null +++ b/lib/repository/home_screen_repository.dart @@ -0,0 +1,42 @@ +import 'dart:convert'; + +import '../app/constant.dart'; +import '../models/pending_work_model.dart'; +import '../models/work_total_model.dart'; +import 'base_repository.dart'; + +class HomeScreenRepository extends BaseRepository { + HomeScreenRepository({required super.dio}); + Future getTotalWork({required String id}) async { + final url = + Constant.baseUrl + "v1/courier/mobile/homescreen/list_total/?id=$id"; + final resp = await get(service: url); + WorkTotalModel data; + // print(resp['data']); + if (resp['status'] == 'OK') { + data = WorkTotalModel.fromJson(resp['data']); + } else { + data = {} as WorkTotalModel; + } + return data; + } + + Future> getPendingWork({required String id}) async { + final url = + Constant.baseUrl + "/v1/courier/mobile/homescreen/list_order/?id=$id"; + final resp = await get(service: url); + List data; + // print(resp['data']); + if (resp['status'] == 'OK') { + data = []; + resp['data'].forEach((e) { + final model = PendingWorkModel.fromJson(e); + data.add(model); + }); + } else { + data = []; + } + + return data; + } +} diff --git a/lib/repository/input_lain_lain_repository.dart b/lib/repository/input_lain_lain_repository.dart new file mode 100644 index 0000000..701ea06 --- /dev/null +++ b/lib/repository/input_lain_lain_repository.dart @@ -0,0 +1,76 @@ +import '../app/constant.dart'; +import '../models/auth_model.dart'; +import '../models/response_list_branch_model.dart'; +import '../models/response_save_pengantaran_hasil_model.dart'; +import '../models/response_search_pengantaran_hasil_model.dart'; +import 'base_repository.dart'; + +class DummyNoLabDokter { + final String noLab; + final int idLab; + final String nama; + final String alamatPengantaran; + + DummyNoLabDokter( + this.noLab, + this.idLab, + this.nama, + this.alamatPengantaran + ); +} + +class InputLainLainRepository extends BaseRepository { + InputLainLainRepository({required super.dio}); + + List result = [ + DummyNoLabDokter("110378", 1, "Agas", "Jl wora wari"), + DummyNoLabDokter("110390", 2, "Indy Ratna", "Jl moestopo"), + DummyNoLabDokter("140390", 3, "Jarot", "Jl pahlawan no 1"), + DummyNoLabDokter("190390", 4, "Deny", "Jl Ir Soekarno"), + DummyNoLabDokter("119090", 5, "Said Hasan", "Jl mawar no 04"), + DummyNoLabDokter("110391", 1, "Jarot P", "Jl nawangsih no 07"), + DummyNoLabDokter("110399", 2, "Joko S", "Jl Pegangsaan no 04"), + ]; + + Future> loadListCabangInputLainLain() async { + final service = + "${Constant.baseUrl}v1/courier/mobile/homescreen/list_branch"; + final resp = await get(service: service); + + final result = List.empty(growable: true); + if (resp['data'] != null) { + resp['data'].forEach((e) { + final model = ResponseListBranchModel.fromJson(e); + result.add(model); + }); + } + + return result; + } + + Future inputLainLainSave({ + required String courierID, + required String tipeID, + required String branchID, + required String branchCode, + required String pickup, + required String destination, + required String note + }) async { + final param = { + "id": courierID, + "tipeid": tipeID, + "branchid": branchID, + "branchcode": branchCode, + "pickup": pickup, + "destination":destination, + "note": note + }; + + print("param : $param"); + + final service = "${Constant.baseUrl}v1/courier/mobile/inputcourier/add/?id=$courierID&tipeid=$tipeID&branchid=$branchID&branchcode=$branchCode&pickup=$pickup&destination=$destination¬e=$note"; + final resp = await post(param: param, service: service); + return ResponseSavePengantaranHasilModel.fromJson(resp); + } +} diff --git a/lib/repository/kurir_tolak_repository.dart b/lib/repository/kurir_tolak_repository.dart new file mode 100644 index 0000000..ff47ea8 --- /dev/null +++ b/lib/repository/kurir_tolak_repository.dart @@ -0,0 +1,28 @@ +import 'dart:convert'; + +import '../app/constant.dart'; + +import '../app/constant.dart'; +import 'base_repository.dart'; + +class KurirTolakRepository extends BaseRepository { + KurirTolakRepository({required super.dio}); + + Future getKurirTolak( + {required String id, + required String tipeId, + required String deliveryId, + required String note}) async { + final url = + "${Constant.baseUrl}/v1/courier/mobile/worklist/cancel/?id=$id&tipeid=$tipeId&deliveryid=$deliveryId¬e=$note"; + final resp = await get(service: url); + bool data = false; + // print(resp['data']); + + if (resp["status"] == "OK") { + data = true; + } + + return data; + } +} diff --git a/lib/repository/pengambilan_bahan_repository.dart b/lib/repository/pengambilan_bahan_repository.dart new file mode 100644 index 0000000..e64a802 --- /dev/null +++ b/lib/repository/pengambilan_bahan_repository.dart @@ -0,0 +1,74 @@ +import '../app/constant.dart'; +import '../models/auth_model.dart'; +import '../models/response_list_branch_model.dart'; +import '../models/response_save_pengantaran_hasil_model.dart'; +import '../models/response_search_pengantaran_hasil_model.dart'; +import 'base_repository.dart'; + +class DummyNoLabDokter { + final String noLab; + final int idLab; + final String nama; + final String alamatPengantaran; + + DummyNoLabDokter( + this.noLab, + this.idLab, + this.nama, + this.alamatPengantaran + ); +} + +class PengambilanBahanRepository extends BaseRepository { + PengambilanBahanRepository({required super.dio}); + + List result = [ + DummyNoLabDokter("110378", 1, "Agas", "Jl wora wari"), + DummyNoLabDokter("110390", 2, "Indy Ratna", "Jl moestopo"), + DummyNoLabDokter("140390", 3, "Jarot", "Jl pahlawan no 1"), + DummyNoLabDokter("190390", 4, "Deny", "Jl Ir Soekarno"), + DummyNoLabDokter("119090", 5, "Said Hasan", "Jl mawar no 04"), + DummyNoLabDokter("110391", 1, "Jarot P", "Jl nawangsih no 07"), + DummyNoLabDokter("110399", 2, "Joko S", "Jl Pegangsaan no 04"), + ]; + + Future> loadListCabangPengambilanBahan() async { + final service = + "${Constant.baseUrl}v1/courier/mobile/homescreen/list_branch"; + final resp = await get(service: service); + + final result = List.empty(growable: true); + if (resp['data'] != null) { + resp['data'].forEach((e) { + final model = ResponseListBranchModel.fromJson(e); + result.add(model); + }); + } + + return result; + } + + Future pengambilanBahanSave({ + required String courierID, + required String tipeID, + required String branchID, + required String branchCode, + required String name, + required String destination + }) async { + final param = { + "id": courierID, + "tipeid": tipeID, + "branchid": branchID, + "branchcode": branchCode, + "name": name, + "destination":destination + }; + + print("param : $param"); + + final service = "${Constant.baseUrl}v1/courier/mobile/inputcourier/add/?id=$courierID&tipeid=$tipeID&branchid=$branchID&branchcode=$branchCode&name=$name&destination=$destination"; + final resp = await post(param: param, service: service); + return ResponseSavePengantaranHasilModel.fromJson(resp); + } +} diff --git a/lib/repository/pengantaran_hasil_dokter_repository.dart b/lib/repository/pengantaran_hasil_dokter_repository.dart new file mode 100644 index 0000000..1ef8fce --- /dev/null +++ b/lib/repository/pengantaran_hasil_dokter_repository.dart @@ -0,0 +1,72 @@ +import '../app/constant.dart'; +import '../models/auth_model.dart'; +import '../models/response_save_pengantaran_hasil_model.dart'; +import '../models/response_search_pengantaran_hasil_model.dart'; +import 'base_repository.dart'; + +class DummyNoLabDokter { + final String noLab; + final int idLab; + final String nama; + final String alamatPengantaran; + + DummyNoLabDokter( + this.noLab, + this.idLab, + this.nama, + this.alamatPengantaran + ); +} + +class PengantaranHasilDokterRepository extends BaseRepository { + PengantaranHasilDokterRepository({required super.dio}); + + List result = [ + DummyNoLabDokter("110378", 1, "Agas", "Jl wora wari"), + DummyNoLabDokter("110390", 2, "Indy Ratna", "Jl moestopo"), + DummyNoLabDokter("140390", 3, "Jarot", "Jl pahlawan no 1"), + DummyNoLabDokter("190390", 4, "Deny", "Jl Ir Soekarno"), + DummyNoLabDokter("119090", 5, "Said Hasan", "Jl mawar no 04"), + DummyNoLabDokter("110391", 1, "Jarot P", "Jl nawangsih no 07"), + DummyNoLabDokter("110399", 2, "Joko S", "Jl Pegangsaan no 04"), + ]; + + Future> loadListNoDokterPengantaranHasilDokter({ + required String search, + }) async { + final param = { + "search": search, + }; + final service = + "${Constant.baseUrl}v1/courier/mobile/inputcourier/search_doctor/?search=$search"; + final resp = await post(param: param, service: service); + + final result = List.empty(growable: true); + if (resp['data'] != null) { + resp['data']['records'].forEach((e) { + final model = ResponseSearchPengantaranHasilModel.fromJson(e); + result.add(model); + }); + } + + return result; + } + + Future pengantaranHasilDokterSave({ + required String courierID, + required String tipeID, + required String deliveryID, + }) async { + final param = { + "id": courierID, + "tipeid": tipeID, + "deliveryid": deliveryID + }; + + print("param : $param"); + + final service = "${Constant.baseUrl}v1/courier/mobile/inputcourier/add/?id=$courierID&tipeid=$tipeID&deliveryid=$deliveryID"; + final resp = await post(param: param, service: service); + return ResponseSavePengantaranHasilModel.fromJson(resp); + } +} diff --git a/lib/repository/pengantaran_hasil_instansi_repository.dart b/lib/repository/pengantaran_hasil_instansi_repository.dart new file mode 100644 index 0000000..a8d560d --- /dev/null +++ b/lib/repository/pengantaran_hasil_instansi_repository.dart @@ -0,0 +1,71 @@ +import '../app/constant.dart'; +import '../models/auth_model.dart'; +import '../models/response_save_pengantaran_hasil_model.dart'; +import '../models/response_search_pengantaran_hasil_model.dart'; +import 'base_repository.dart'; + +class DummyNoSuratJalan { + final String noSuratJalan; + final int idSuratJalan; + final String nama; + final String alamatPengantaran; + + DummyNoSuratJalan( + this.noSuratJalan, + this.idSuratJalan, + this.nama, + this.alamatPengantaran + ); +} +class PengantaranHasilInstansiRepository extends BaseRepository { + PengantaranHasilInstansiRepository({required super.dio}); + + List result = [ + DummyNoSuratJalan("110378", 1, "Agas", "Jl wora wari"), + DummyNoSuratJalan("110390", 2, "Indy Ratna", "Jl moestopo"), + DummyNoSuratJalan("140390", 3, "Jarot", "Jl pahlawan no 1"), + DummyNoSuratJalan("190390", 4, "Deny", "Jl Ir Soekarno"), + DummyNoSuratJalan("119090", 5, "Said Hasan", "Jl mawar no 04"), + DummyNoSuratJalan("110391", 1, "Jarot P", "Jl nawangsih no 07"), + DummyNoSuratJalan("110399", 2, "Joko S", "Jl Pegangsaan no 04"), + ]; + + Future> loadListNoSuratJalanPengantaranHasilInstansi({ + required String search, + }) async { + final param = { + "search": search, + }; + final service = + "${Constant.baseUrl}v1/courier/mobile/inputcourier/search_company/?search=$search"; + final resp = await post(param: param, service: service); + + final result = List.empty(growable: true); + if (resp['data'] != null) { + resp['data']['records'].forEach((e) { + final model = ResponseSearchPengantaranHasilModel.fromJson(e); + result.add(model); + }); + } + + return result; + } + + Future pengantaranHasilInstansiSave({ + required String courierID, + required String tipeID, + required String deliveryID, + }) async { + final param = { + "id": courierID, + "tipeid": tipeID, + "deliveryid": deliveryID + }; + + print("param : $param"); + + final service = "${Constant.baseUrl}v1/courier/mobile/inputcourier/add/?id=$courierID&tipeid=$tipeID&deliveryid=$deliveryID"; + final resp = await post(param: param, service: service); + return ResponseSavePengantaranHasilModel.fromJson(resp); + } +} diff --git a/lib/repository/pengantaran_hasil_pasien_repository.dart b/lib/repository/pengantaran_hasil_pasien_repository.dart new file mode 100644 index 0000000..35578bd --- /dev/null +++ b/lib/repository/pengantaran_hasil_pasien_repository.dart @@ -0,0 +1,48 @@ +import '../app/constant.dart'; +import '../models/response_save_pengantaran_hasil_model.dart'; +import '../models/response_search_pengantaran_hasil_model.dart'; +import 'base_repository.dart'; + +class PengantaranHasilPasienRepository extends BaseRepository { + PengantaranHasilPasienRepository({required super.dio}); + + // search no lab pasien + Future> + loadListNoLabPengantaranHasilPasien({ + required String search, + }) async { + final param = { + "search": search, + }; + final service = + "${Constant.baseUrl}v1/courier/mobile/inputcourier/search_patient/?search=$search"; + final resp = await post(param: param, service: service); + + final result = List.empty(growable: true); + if (resp['data'] != null) { + resp['data']['records'].forEach((e) { + final model = ResponseSearchPengantaranHasilModel.fromJson(e); + result.add(model); + }); + } + + return result; + } + + // simpan input pengantaran hasil pasien + Future pengantaranHasilPasienSave({ + required String courierID, + required String tipeID, + required String deliveryID, + }) async { + final param = { + "id": courierID, + "tipeid": tipeID, + "deliveryid": deliveryID + }; + + final service = "${Constant.baseUrl}v1/courier/mobile/inputcourier/add/?id=$courierID&tipeid=$tipeID&deliveryid=$deliveryID"; + final resp = await post(param: param, service: service); + return ResponseSavePengantaranHasilModel.fromJson(resp); + } +} diff --git a/lib/repository/personal_information_repository.dart b/lib/repository/personal_information_repository.dart new file mode 100644 index 0000000..0f2291d --- /dev/null +++ b/lib/repository/personal_information_repository.dart @@ -0,0 +1,34 @@ +import 'dart:convert'; + +import '../models/personal_information_model.dart'; + +import '../app/constant.dart'; +import 'base_repository.dart'; + +class PersonalInformationRepository extends BaseRepository { + PersonalInformationRepository({required super.dio}); + + Future> getPersonalInformation( + {required String mCourierID}) async { + final param = { + "id": mCourierID + }; + + final url = + "${Constant.baseUrl}/v1/courier/mobile/profile/info/?id=$mCourierID"; + final resp = await get(service: url); + // print("url :" + url); + List data; + // print(resp['data']); + if (resp['status'] == 'OK' && resp['status'] != null) { + data = []; + resp['data'].forEach((e) { + final model = PersonalInformationModel.fromJson(e); + data.add(model); + }); + } else { + data = []; + } + return data; + } +} diff --git a/lib/repository/result_delivery_repository.dart b/lib/repository/result_delivery_repository.dart new file mode 100644 index 0000000..d26fb0c --- /dev/null +++ b/lib/repository/result_delivery_repository.dart @@ -0,0 +1,59 @@ +import '../app/constant.dart'; +import 'base_repository.dart'; + +class ResultDeliveryRepository extends BaseRepository { + ResultDeliveryRepository({required super.dio}); + + Future setLocation( + {required String id, + required String step, + required String lat, + required String long, + required String address, + required String branchID}) async { + final url = + "${Constant.baseUrl}/v1/courier/mobile/process/add_delivery/?id=$id&locid=$step&lat=$lat&long=$long&address=$address&branchid=$branchID"; + final resp = await get(service: url); + bool data = false; + + if (resp['status'] == "OK") { + data = true; + } + + return data; + } + + Future setDeliveryPerson({ + required String id, + required String name, + required String note, + }) async { + final url = + "${Constant.baseUrl}/v1/courier/mobile/process/update_deliver/?id=$id&name=$name¬e=$note"; + final resp = await get(service: url); + bool data = false; + + if (resp['status'] == "OK") { + data = true; + } + + return data; + } + + Future setReceivePerson({ + required String id, + required String name, + required String note, + }) async { + final url = + "${Constant.baseUrl}/v1/courier/mobile/process/update_receive/?id=$id&name=$name¬e=$note"; + final resp = await get(service: url); + bool data = false; + + if (resp['status'] == "OK") { + data = true; + } + + return data; + } +} diff --git a/lib/repository/work_input_suhu_repository.dart b/lib/repository/work_input_suhu_repository.dart new file mode 100644 index 0000000..0fac4f5 --- /dev/null +++ b/lib/repository/work_input_suhu_repository.dart @@ -0,0 +1,28 @@ +import '../app/constant.dart'; +import '../models/response_save_work_suhu_model.dart'; +import 'base_repository.dart'; + + +class WorkInputSuhuRepository extends BaseRepository { + WorkInputSuhuRepository({required super.dio}); + + Future inputWorkSuhu({ + required String deliveryId, + required String suhu + }) async { + final param = { + "id": deliveryId, + "suhu": suhu + }; + + print("param : $param"); + + // Pengantaran Hasil => tipeID 1 + // Lain-lain => tipeID 4 + // Pengambilan Bahan => tipeID 5 + + final service = "${Constant.baseUrl}v1/courier/mobile/process/update_temperatur_box/?id=$deliveryId&suhu=$suhu"; + final resp = await post(param: param, service: service); + return ResponseSaveWorkSuhuModel.fromJson(resp); + } +} diff --git a/lib/repository/work_list_repository.dart b/lib/repository/work_list_repository.dart new file mode 100644 index 0000000..7d8bbe2 --- /dev/null +++ b/lib/repository/work_list_repository.dart @@ -0,0 +1,58 @@ +import 'dart:convert'; + +import '../models/work_list_model.dart'; + +import '../app/constant.dart'; +import '../models/detail_work_model.dart'; +import '../models/pending_work_model.dart'; +import '../models/work_total_model.dart'; +import 'base_repository.dart'; + +class WorkListRepository extends BaseRepository { + WorkListRepository({required super.dio}); + + Future> getListWork( + {required String id, + required String tipeId, + required String supervisor, + required String status}) async { + final url = + "${Constant.baseUrl}/v1/courier/mobile/worklist/listbycourierv2/?id=$id&tipeid=$tipeId&status='$status'&supervisor='$supervisor'"; + final resp = await get(service: url); + // print("url :" + url); + List data; + // print(resp['data']); + if (resp['status'] == 'OK' && resp['status'] != null) { + data = []; + resp['data'].forEach((e) { + final model = WorkListModel.fromJson(e); + data.add(model); + }); + } else { + data = []; + } + return data; + } + + Future getDetail( + {required String id, + required String tipeId, + required String deliveryId}) async { + final url = + "${Constant.baseUrl}/v1/courier/mobile/worklist/list_by_id/?id=$id&tipeid=$tipeId&deliveryid=$deliveryId"; + final resp = await get(service: url); + // DetailWorkModel data; + // print(resp['data']); + + DetailWorkModel data = DetailWorkModel.fromJson(resp['data'][0]); + + // resp['data'].forEach((e) { + // final model = HistoryModel.fromJson(e); + // data.add(model); + // }); + // } + // print(data.alamatPengambilan); + + return data; + } +} diff --git a/lib/repository/work_tunda_repository.dart b/lib/repository/work_tunda_repository.dart new file mode 100644 index 0000000..ea5ef6f --- /dev/null +++ b/lib/repository/work_tunda_repository.dart @@ -0,0 +1,32 @@ +import '../app/constant.dart'; +import '../models/response_save_work_tunda_model.dart'; +import 'base_repository.dart'; + + +class WorkTundaRepository extends BaseRepository { + WorkTundaRepository({required super.dio}); + + Future inputWorkTunda({ + required String courierID, + required String tipeID, + required String deliveryID, + required String note + }) async { + final param = { + "id": courierID, + "tipeid": tipeID, + "deliveryid": deliveryID, + "note": note + }; + + print("param : $param"); + + // Pengantaran Hasil => tipeID 1 + // Lain-lain => tipeID 4 + // Pengambilan Bahan => tipeID 5 + + final service = "${Constant.baseUrl}v1/courier/mobile/pending/add/?id=$courierID&tipeid=$tipeID&deliveryid=$deliveryID¬e=$note"; + final resp = await post(param: param, service: service); + return ResponseSaveWorkTundaModel.fromJson(resp); + } +} diff --git a/lib/screen/change_password_screen/change_password_provider.dart b/lib/screen/change_password_screen/change_password_provider.dart new file mode 100644 index 0000000..cd66e6d --- /dev/null +++ b/lib/screen/change_password_screen/change_password_provider.dart @@ -0,0 +1,78 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../models/personal_information_model.dart'; +import '../../repository/change_password_repository.dart'; +import '../../repository/personal_information_repository.dart'; +import '../../repository/work_list_repository.dart'; + +import '../../models/pending_work_model.dart'; +import '../../models/response_save_change_password_model.dart'; +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; +import '../../repository/home_screen_repository.dart'; + +abstract class ChangePasswordState extends Equatable { + final DateTime date; + const ChangePasswordState(this.date); + @override + List get props => [date]; +} + +class ChangePasswordStateInit extends ChangePasswordState { + ChangePasswordStateInit() : super(DateTime.now()); +} + +class ChangePasswordStateLoading extends ChangePasswordState { + ChangePasswordStateLoading() : super(DateTime.now()); +} + +class ChangePasswordStateError extends ChangePasswordState { + final String message; + ChangePasswordStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class ChangePasswordStateDone extends ChangePasswordState { + final ResponseSaveChangePasswordModel model; + ChangePasswordStateDone({ + required this.model, + }) : super(DateTime.now()); +} + +//notifier +class ChangePasswordNotifier extends StateNotifier { + final Ref ref; + ChangePasswordNotifier({ + required this.ref, + }) : super(ChangePasswordStateInit()); + + void ubahPassword({ + required String userID, + required String oldPassword, + required String newPassword, + }) async { + try { + state = ChangePasswordStateLoading(); + final dio = ref.read(dioProvider); + final resp = await ChangePasswordRepository(dio: dio) + .ubahPassword( + userID: userID, + oldPassword: oldPassword, + newPassword: newPassword + ); + state = ChangePasswordStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = ChangePasswordStateError(message: e.message.toString()); + } else { + state = ChangePasswordStateError(message: e.toString()); + } + } + } +} + +//provider +final ChangePasswordProvider = + StateNotifierProvider( + (ref) => ChangePasswordNotifier(ref: ref)); diff --git a/lib/screen/change_password_screen/change_password_screen.dart b/lib/screen/change_password_screen/change_password_screen.dart new file mode 100644 index 0000000..0233953 --- /dev/null +++ b/lib/screen/change_password_screen/change_password_screen.dart @@ -0,0 +1,268 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import '../../app/route.dart'; +import '../change_password_screen/change_password_provider.dart'; +import '/widget/snackbar_widget.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import '../../app/constant.dart'; +import '../../provider/current_user_provider.dart'; + +class ChangePasswordScreen extends HookConsumerWidget { + const ChangePasswordScreen({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final currentPassword = useTextEditingController(text: ""); + final passwordNow = useTextEditingController(text: ""); + final confirmPassword = useTextEditingController(text: ""); + + final hideCurrentPassword = useState(true); + final hidePasswordNow = useState(true); + final hideConfirmPassword = useState(true); + + final isLoading = useState(false); + final userID = ref.watch(currentUserProvider)?.model.user?.mUserID ?? "0"; + + ref.listen(ChangePasswordProvider, (previous, next) async { + if (next is ChangePasswordStateLoading) { + isLoading.value = true; + } else if (next is ChangePasswordStateError) { + isLoading.value = false; + // print(next.message); + SanckbarWidget( + context, next.message, snackbarType.error); + } else if (next is ChangePasswordStateDone) { + isLoading.value = false; + isLoading.value = false; + if (next.model.status != "OK") { + SanckbarWidget(context, "${next.model.message}", snackbarType.error); + } else { + final shared = await SharedPreferences.getInstance(); + final passwordSharePref = shared.getString("passwordX"); + if (passwordSharePref != null) { + await shared.setString("passwordX", confirmPassword.text); + } + SanckbarWidget( + context, + "Berhasil Mengubah Password", + snackbarType.success); + Navigator.of(context).pushNamedAndRemoveUntil( + menuRoute, + (route) => false, + ); + } + } + }); + + disabledBtn() { + if (currentPassword.text.isNotEmpty && + passwordNow.text.isNotEmpty && + confirmPassword.text.isNotEmpty && + (passwordNow.text == confirmPassword.text)) { + return true; + } else { + return false; + } + } + + return Scaffold( + bottomNavigationBar: Container( + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 35), + vertical: Constant.getActualY(context: context, y: 40)), + child: ElevatedButton( + onPressed: () { + // showDialog( + // context: context, + // builder: (context) { + // return ErrorDialog( + // title: "Something Went Wrong", + // message: "404 Not Found", + // showImage: true, + // showButton: true, + // buttonText: "Close", + // buttonFunction: () { + // print("a"); + // }); + // }, + // ); + if (currentPassword.text.isNotEmpty && + passwordNow.text.isNotEmpty && + confirmPassword.text.isNotEmpty && + (passwordNow.text == confirmPassword.text)) { + // print("a"); + + ref.read(ChangePasswordProvider.notifier).ubahPassword( + userID: userID, + oldPassword: currentPassword.text, + newPassword: passwordNow.text); + } else { + // null; + if (currentPassword.text.isEmpty) { + SanckbarWidget( + context, + "Password Saat ini tidak boleh kosong", + snackbarType.warning); + } else { + if ((passwordNow.text.isEmpty || + confirmPassword.text.isEmpty)) { + SanckbarWidget( + context, + "Password Saat ini atau Konformasi Password tidak boleh kosong", + snackbarType.warning); + } else { + if ((passwordNow.text != confirmPassword.text)) { + SanckbarWidget( + context, + "Konfirmasi Password & Password Baru harus sama", + snackbarType.warning); + } + } + } + } + }, + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + side: const BorderSide(width: 0.1, color: Colors.grey), + borderRadius: BorderRadius.circular(8), + ), + ), + child: Container( + height: Constant.getActualY(context: context, y: 46), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: const [ + Text( + "Simpan", + style: TextStyle(fontWeight: FontWeight.bold), + ) + ], + ), + )), + ), + body: ListView( + children: [ + Container( + height: Constant.getActualY(context: context, y: 56), + ), + Container( + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 10)), + height: Constant.getActualY(context: context, y: 48), + child: Row( + children: [ + IconButton( + onPressed: () { + Navigator.of(context).pop(); + }, + icon: Icon(Icons.arrow_back_ios_rounded), + ), + Text( + "Ubah Password", + style: Constant.body1(context: context).copyWith( + fontWeight: FontWeight.w600, color: Constant.textPrimary), + ), + ], + ), + ), + Container( + height: Constant.getActualY(context: context, y: 31), + ), + Container( + margin: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 34)), + child: TextFormField( + controller: currentPassword, + obscureText: hideCurrentPassword.value, + enableSuggestions: false, + autocorrect: false, + decoration: InputDecoration( + suffixIcon: InkWell( + onTap: () { + hideCurrentPassword.value = !hideCurrentPassword.value; + }, + child: Icon(!hideCurrentPassword.value + ? Icons.remove_red_eye + : Icons.remove_red_eye_outlined)), + labelText: "Password Saat Ini", + hintText: "Password Saat Ini", + border: + OutlineInputBorder(borderRadius: BorderRadius.circular(8)), + ), + ), + ), + Container( + height: Constant.getActualY(context: context, y: 24), + ), + Container( + margin: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 34)), + child: TextFormField( + controller: passwordNow, + obscureText: hidePasswordNow.value, + enableSuggestions: false, + autocorrect: false, + decoration: InputDecoration( + suffixIcon: InkWell( + onTap: () { + hidePasswordNow.value = !hidePasswordNow.value; + }, + child: Icon(!hidePasswordNow.value + ? Icons.remove_red_eye + : Icons.remove_red_eye_outlined)), + labelText: "Password Baru", + hintText: "Password Baru", + border: + OutlineInputBorder(borderRadius: BorderRadius.circular(8)), + ), + ), + ), + Container( + height: Constant.getActualY(context: context, y: 24), + ), + Container( + margin: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 34)), + child: TextFormField( + controller: confirmPassword, + obscureText: hideConfirmPassword.value, + enableSuggestions: false, + autocorrect: false, + autovalidateMode: AutovalidateMode.onUserInteraction, + validator: (value) { + if (value != passwordNow.text) { + return "Konfirmasi password berbeda"; + } + }, + onEditingComplete: () { + FocusScope.of(context).unfocus(); + }, + // onChanged: (e) { + // if (e != passwordNow.text) { + // print("diff"); + // } + // }, + decoration: InputDecoration( + suffixIcon: InkWell( + onTap: () { + hideConfirmPassword.value = !hideConfirmPassword.value; + }, + child: Icon(!hideConfirmPassword.value + ? Icons.remove_red_eye + : Icons.remove_red_eye_outlined)), + labelText: "konfirmasi Password Baru", + hintText: "konfirmasi Password Baru", + border: + OutlineInputBorder(borderRadius: BorderRadius.circular(8)), + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/screen/detail_history_screen/detail_history_provider.dart b/lib/screen/detail_history_screen/detail_history_provider.dart new file mode 100644 index 0000000..0683c9a --- /dev/null +++ b/lib/screen/detail_history_screen/detail_history_provider.dart @@ -0,0 +1,72 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '/models/detail_history_model.dart'; +import '/models/history_model.dart'; +import '/../repository/history_screen_repository.dart'; +import '/screen/history_screen/history_screen.dart'; + +import '../../models/pending_work_model.dart'; +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; +import '../../repository/home_screen_repository.dart'; + +abstract class DetailHistoryState extends Equatable { + final DateTime date; + const DetailHistoryState(this.date); + @override + List get props => [date]; +} + +class DetailHistoryStateInit extends DetailHistoryState { + DetailHistoryStateInit() : super(DateTime.now()); +} + +class DetailHistoryStateLoading extends DetailHistoryState { + DetailHistoryStateLoading() : super(DateTime.now()); +} + +class DetailHistoryStateError extends DetailHistoryState { + final String message; + DetailHistoryStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class DetailHistoryStateDone extends DetailHistoryState { + final DetailHistoryModel model; + DetailHistoryStateDone({ + required this.model, + }) : super(DateTime.now()); +} + +//notifier +class DetailHistoryNotifier extends StateNotifier { + final Ref ref; + DetailHistoryNotifier({ + required this.ref, + }) : super(DetailHistoryStateInit()); + + void getData( + {required String id, + required String tipeId, + required String deliveryId}) async { + try { + state = DetailHistoryStateLoading(); + final dio = ref.read(dioProvider); + final resp = await HistoryRepository(dio: dio) + .getDetail(id: id, tipeId: tipeId, deliveryId: deliveryId); + state = DetailHistoryStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = DetailHistoryStateError(message: e.message.toString()); + } else { + state = DetailHistoryStateError(message: e.toString()); + } + } + } +} + +//provider +final DetailHistoryProvider = + StateNotifierProvider( + (ref) => DetailHistoryNotifier(ref: ref)); diff --git a/lib/screen/detail_history_screen/detail_history_screen.dart b/lib/screen/detail_history_screen/detail_history_screen.dart new file mode 100644 index 0000000..306e32a --- /dev/null +++ b/lib/screen/detail_history_screen/detail_history_screen.dart @@ -0,0 +1,792 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:geolocator/geolocator.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:intl/intl.dart'; +import '/models/detail_history_model.dart'; +import '/screen/detail_history_screen/detail_history_provider.dart'; +import '/widget/chip_type.dart'; +import '/widget/patient_card.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; +import 'package:maps_launcher/maps_launcher.dart'; + +import '../../app/constant.dart'; +import '../../widget/snackbar_widget.dart'; + +class DetailHistoryProp { + final String tipeId; + final String kurirId; + final String deliveryId; + + DetailHistoryProp(this.tipeId, this.kurirId, this.deliveryId); +} + +class DetailHistoryScreen extends HookConsumerWidget { + const DetailHistoryScreen( + {super.key, required this.data, required this.prop}); + final Map data; + final DetailHistoryProp prop; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final detailLoading = useState(false); + final detailData = useState(DetailHistoryModel()); + var formattedDate = useState(''); + + final pengambilanAwalLoc = useState>( + {"long": "110.7104622", "lat": "-7.5031631"}); + final pengambilanAkhirLoc = useState>( + {"long": "110.7104622", "lat": "-7.5031631"}); + final pengantaranAwalLoc = useState>( + {"long": "110.7104622", "lat": "-7.5031631"}); + final pengantaranAkhirLoc = useState>( + {"long": "110.7104622", "lat": "-7.5031631"}); + final petugasPengambilan = useState("Liliana Ruby"); + final petugasPengantaran = useState("Liliana Ruby"); + final catatanPengambilan = useState("Lengkap"); + final catatanPengantaran = useState("Lengkap"); + + List> listPasien = [ + {"id": "1", "name": "Sabrina Dewi", "no_lab": "112035"}, + {"id": "2", "name": "Lisa Andriani", "no_lab": "113040"}, + {"id": "3", "name": "Leo Rolly", "no_lab": "112987"}, + ]; + fetchData() { + ref.read(DetailHistoryProvider.notifier).getData( + id: prop.kurirId, tipeId: prop.tipeId, deliveryId: prop.deliveryId); + } + + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + fetchData(); + }); + return () {}; + }, []); + ref.listen( + DetailHistoryProvider, + (previous, next) { + if (next is DetailHistoryStateLoading) { + detailLoading.value = true; + } else if (next is DetailHistoryStateError) { + // print(next.message); + detailLoading.value = false; + SanckbarWidget( + context, next.message.substring(0, 30), snackbarType.error); + } else if (next is DetailHistoryStateDone) { + // print(next.model); + detailData.value = next.model; + String originalDateString = next.model.cdate.toString(); + DateTime originalDate = + DateFormat('dd-MM-yyyy HH:mm:ss').parse(originalDateString); + // String formattedDate = DateFormat.yMMMMd('id').format(originalDate); + formattedDate.value = DateFormat('d MMM HH:mm').format(originalDate); + // print("data from json"); + // print(next.model.tipeid); + pengambilanAwalLoc.value = { + "long": next.model.lngPengambilanAwal ?? "0", + "lat": next.model.latPengambilanAwal ?? "0" + }; + pengambilanAkhirLoc.value = { + "long": next.model.lngPengambilanAkhir ?? "0", + "lat": next.model.latPengambilanAkhir ?? "0" + }; + + pengantaranAwalLoc.value = { + "long": next.model.lngPengantaranAwal ?? "0", + "lat": next.model.latPengantaranAwal ?? "0" + }; + pengantaranAkhirLoc.value = { + "long": next.model.lngPengantaranAkhir ?? "0", + "lat": next.model.latPengantaranAkhir ?? "0" + }; + petugasPengambilan.value = next.model.petugasPenyerahan ?? " "; + petugasPengantaran.value = next.model.penerima ?? " "; + catatanPengantaran.value = next.model.notePenerimaan ?? " "; + catatanPengambilan.value = next.model.notePenyerahan ?? " "; + + detailLoading.value = false; + } + }, + ); + + return detailLoading.value + ? SizedBox( + height: Constant.designHeight * 0.9, + child: Center( + child: LoadingAnimationWidget.fourRotatingDots( + color: Constant.primaryBlue, size: 50), + ), + ) + : SingleChildScrollView( + child: Column(children: [ + SizedBox( + height: Constant.getActualY(context: context, y: 24), + ), + Container( + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 20)), + height: Constant.getActualY(context: context, y: 40), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Image.asset( + "assets/icon_detail_riwayat_motor.png", + width: Constant.getActualX(context: context, x: 64), + height: Constant.getActualX(context: context, x: 64), + ), + Column( + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + formattedDate.value, + style: Constant.body1(context: context) + .copyWith(fontWeight: FontWeight.w500), + ), + ChipType( + name: detailData.value.tipe.toString(), + tipe: int.parse(prop.tipeId), + isDetailHistory: true, + ) + ], + ) + ], + ), + ), + Container( + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 35)), + // color: Colors.red, + height: Constant.getActualY(context: context, y: 737), + child: SingleChildScrollView( + // reverse: MediaQuery.of(context).viewInsets.bottom != 0 + // ? true + // : false, + child: Padding( + padding: EdgeInsets.only( + bottom: MediaQuery.of(context).viewInsets.bottom), + child: Column( + // shrinkWrap: true, + + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + height: Constant.getActualY(context: context, y: 12), + ), + displayDetailText(context, "No. Surat Jalan", + detailData.value.noSuratJalan.toString()), + SizedBox( + height: Constant.getActualY(context: context, y: 12), + ), + displayDetailText( + context, "Nama", detailData.value.nama.toString()), + SizedBox( + height: Constant.getActualY(context: context, y: 12), + ), + if (int.parse(prop.tipeId) == 1) + Container( + margin: EdgeInsets.only( + bottom: + Constant.getActualY(context: context, y: 12), + ), + child: displayDetailText(context, "Catatan FO", + detailData.value.foNote ?? ""), + ), + if (int.parse(prop.tipeId) == 1 || + int.parse(prop.tipeId) == 2 || + int.parse(prop.tipeId) == 3 || + int.parse(prop.tipeId) == 4 || + int.parse(prop.tipeId) == 5) + displayDetailText(context, "Catatan Admin", + detailData.value.adminNote ?? ""), + SizedBox( + height: Constant.getActualY(context: context, y: 12), + ), + if (int.parse(prop.tipeId) == 1 || + int.parse(prop.tipeId) == 2 || + int.parse(prop.tipeId) == 3) + Text( + "Pengambilan Hasil", + // maxLines: 5, + overflow: TextOverflow.visible, + style: Constant.body1(context: context).copyWith( + fontWeight: FontWeight.w600, + color: Constant.textPrimary), + ), + if (int.parse(prop.tipeId) == 5) + Text( + "Pengambilan Bahan", + // maxLines: 5, + overflow: TextOverflow.visible, + style: Constant.body1(context: context).copyWith( + fontWeight: FontWeight.w600, + color: Constant.textPrimary), + ), + if (int.parse(prop.tipeId) == 4) + Text( + "Pengirim", + // maxLines: 5, + overflow: TextOverflow.visible, + style: Constant.body1(context: context).copyWith( + fontWeight: FontWeight.w600, + color: Constant.textPrimary), + ), + SizedBox( + height: Constant.getActualY(context: context, y: 12), + ), + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Text( + "Alamat Pengambilan", + style: Constant.body1(context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ), + Expanded( + child: Text( + ": ${detailData.value.alamatPengambilan}", + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: Constant.body1(context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ) + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 12), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + if (pengambilanAwalLoc.value["lat"] == "" || + pengambilanAwalLoc.value['long'] == "") + Icon(Icons.location_on, + color: Constant.primaryBlue), + if (pengambilanAwalLoc.value["lat"] == "" || + pengambilanAwalLoc.value['long'] == "") + SizedBox( + width: Constant.getActualX( + context: context, x: 4), + ), + Text( + "Lokasi Awal", + style: Constant.body1(context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ) + ], + ), + if (pengambilanAwalLoc.value["lat"] != "" || + pengambilanAwalLoc.value['long'] != "") + InkWell( + onTap: () { + MapsLauncher.launchCoordinates( + double.parse( + pengambilanAwalLoc.value["lat"]!), + double.parse( + pengambilanAwalLoc.value["long"]!)); + }, + child: Row( + children: [ + Text( + "Sudah ditandai", + style: Constant.body1(context: context) + .copyWith( + fontWeight: FontWeight.w600, + color: Constant.primaryBlue), + ), + SizedBox( + width: Constant.getActualX( + context: context, x: 4), + ), + Icon( + Icons.map_outlined, + color: Constant.primaryBlue, + ), + ], + ), + ), + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 12), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + if (pengambilanAkhirLoc.value["lat"] == "" || + pengambilanAkhirLoc.value['long'] == "") + Icon( + Icons.location_on, + color: Constant.primaryBlue, + ), + if (pengambilanAkhirLoc.value["lat"] == "" || + pengambilanAkhirLoc.value['long'] == "") + SizedBox( + width: Constant.getActualX( + context: context, x: 4), + ), + Text( + "Lokasi Akhir", + style: Constant.body1(context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ) + ], + ), + if (pengambilanAkhirLoc.value["lat"] != "" || + pengambilanAkhirLoc.value['long'] != "") + InkWell( + onTap: () { + MapsLauncher.launchCoordinates( + double.parse( + pengambilanAkhirLoc.value["lat"]!), + double.parse( + pengambilanAkhirLoc.value["long"]!)); + }, + child: Row( + children: [ + Text( + "Sudah ditandai", + style: Constant.body1(context: context) + .copyWith( + fontWeight: FontWeight.w600, + color: Constant.primaryBlue), + ), + SizedBox( + width: Constant.getActualX( + context: context, x: 4), + ), + Icon( + Icons.map_outlined, + color: Constant.primaryBlue, + ), + ], + ), + ), + ], + ), + if (petugasPengambilan.value.isNotEmpty || + catatanPengambilan.value.isNotEmpty) + SizedBox( + height: + Constant.getActualY(context: context, y: 12), + ), + if (petugasPengambilan.value.isNotEmpty || + catatanPengambilan.value.isNotEmpty) + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Text( + "Petugas Penyerahan", + style: Constant.body1(context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ), + Expanded( + child: Text( + " : ${petugasPengambilan.value}", + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: Constant.body1(context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ) + ], + ), + if (petugasPengambilan.value.isNotEmpty || + catatanPengambilan.value.isNotEmpty) + SizedBox( + height: + Constant.getActualY(context: context, y: 12), + ), + if (petugasPengambilan.value.isNotEmpty || + catatanPengambilan.value.isNotEmpty) + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Text( + "Catatan", + style: Constant.body1(context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ), + Expanded( + child: Text( + " : ${catatanPengambilan.value} ", + overflow: TextOverflow.visible, + style: Constant.body1(context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ) + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 12), + ), + if (int.parse(prop.tipeId) == 1 || + int.parse(prop.tipeId) == 2 || + int.parse(prop.tipeId) == 3) + Text( + "Pengantaran Hasil", + // maxLines: 5, + overflow: TextOverflow.visible, + style: Constant.body1(context: context).copyWith( + fontWeight: FontWeight.w600, + color: Constant.textPrimary), + ), + if (int.parse(prop.tipeId) == 5) + Text( + "Pengantaran Bahan", + // maxLines: 5, + overflow: TextOverflow.visible, + style: Constant.body1(context: context).copyWith( + fontWeight: FontWeight.w600, + color: Constant.textPrimary), + ), + if (int.parse(prop.tipeId) == 4) + Text( + "Penerima", + // maxLines: 5, + overflow: TextOverflow.visible, + style: Constant.body1(context: context).copyWith( + fontWeight: FontWeight.w600, + color: Constant.textPrimary), + ), + SizedBox( + height: Constant.getActualY(context: context, y: 12), + ), + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Text( + "Alamat Pengantaran", + style: Constant.body1(context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ), + Expanded( + child: Text( + ": ${detailData.value.alamatPengantaran}", + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: Constant.body1(context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ) + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 12), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + if (pengantaranAwalLoc.value["lat"] == "" || + pengantaranAwalLoc.value['long'] == "") + Icon( + Icons.location_on, + color: Constant.primaryBlue, + ), + if (pengantaranAwalLoc.value["lat"] == "" || + pengantaranAwalLoc.value['long'] == "") + SizedBox( + width: Constant.getActualX( + context: context, x: 4), + ), + Text( + "Lokasi Awal", + style: Constant.body1(context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ) + ], + ), + if (pengantaranAwalLoc.value["lat"] != "" || + pengantaranAwalLoc.value['long'] != "") + InkWell( + onTap: () { + MapsLauncher.launchCoordinates( + double.parse( + pengantaranAwalLoc.value["lat"]!), + double.parse( + pengantaranAwalLoc.value["long"]!)); + }, + child: Row( + children: [ + Text( + "Sudah ditandai", + style: Constant.body1(context: context) + .copyWith( + fontWeight: FontWeight.w600, + color: Constant.primaryBlue), + ), + SizedBox( + width: Constant.getActualX( + context: context, x: 4), + ), + Icon( + Icons.map_outlined, + color: Constant.primaryBlue, + ), + ], + ), + ), + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 12), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + if (pengantaranAkhirLoc.value["lat"] == "" || + pengantaranAkhirLoc.value['long'] == "") + Icon( + Icons.location_on, + color: Constant.primaryBlue, + ), + if (pengantaranAkhirLoc.value["lat"] == "" || + pengantaranAkhirLoc.value['long'] == "") + SizedBox( + width: Constant.getActualX( + context: context, x: 4), + ), + Text( + "Lokasi Akhir", + style: Constant.body1(context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ) + ], + ), + if (pengantaranAkhirLoc.value["lat"] != "" || + pengantaranAkhirLoc.value['long'] != "") + InkWell( + onTap: () { + MapsLauncher.launchCoordinates( + double.parse( + pengantaranAkhirLoc.value["lat"]!), + double.parse( + pengantaranAkhirLoc.value["long"]!)); + }, + child: Row( + children: [ + Text( + "Sudah ditandai", + style: Constant.body1(context: context) + .copyWith( + fontWeight: FontWeight.w600, + color: Constant.primaryBlue), + ), + SizedBox( + width: Constant.getActualX( + context: context, x: 4), + ), + Icon( + Icons.map_outlined, + color: Constant.primaryBlue, + ), + ], + ), + ), + ], + ), + if (petugasPengantaran.value.isNotEmpty || + catatanPengantaran.value.isNotEmpty) + SizedBox( + height: + Constant.getActualY(context: context, y: 12), + ), + if (petugasPengantaran.value.isNotEmpty || + catatanPengantaran.value.isNotEmpty) + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Text( + "Penerima", + style: Constant.body1(context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ), + Expanded( + child: Text( + " : ${petugasPengantaran.value}", + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: Constant.body1(context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ) + ], + ), + if (petugasPengantaran.value.isNotEmpty || + catatanPengantaran.value.isNotEmpty) + SizedBox( + height: + Constant.getActualY(context: context, y: 12), + ), + if (petugasPengantaran.value.isNotEmpty || + catatanPengantaran.value.isNotEmpty) + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Text( + "Catatan", + style: Constant.body1(context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ), + Expanded( + child: Text( + " : ${catatanPengantaran.value} ", + overflow: TextOverflow.visible, + style: Constant.body1(context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ) + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 14), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Total Jarak", + style: Constant.heading4(context: context) + .copyWith( + fontWeight: FontWeight.w600, + color: Constant.textPrimary), + ), + Text( + " ${detailData.value.distance ?? ''} KM", + overflow: TextOverflow.visible, + style: Constant.heading3(context: context) + .copyWith( + fontWeight: FontWeight.w600, + color: Constant.textPrimary), + ) + ], + ), + if (detailData.value.patients != null) + ExpansionTile( + tilePadding: EdgeInsets.symmetric(horizontal: 0), + title: Text( + "List (${detailData.value.patients?.length} pasien)", + style: Constant.body1(context: context) + .copyWith(fontWeight: FontWeight.w400), + ), + children: detailData.value.patients! + .map((e) => PatientCard( + name: e.pasien.toString(), + noLab: e.tOrderHeaderLabNumber.toString(), + note: e.courierConfirmNote ?? "", + )) + .toList(), + ), + ], + ), + ), + ), + ) + ]), + ); + } + + Widget displayDetailText(BuildContext context, String teks, String value) { + return Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Text( + teks, + style: Constant.heading4(context: context).copyWith( + fontWeight: FontWeight.w600, color: Constant.textPrimary), + ), + ), + Expanded( + child: Text( + " : ${value}", + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: Constant.heading4(context: context).copyWith( + fontWeight: FontWeight.w600, color: Constant.textPrimary), + ), + ) + ], + ); + } + + Widget headerCard( + BuildContext context, Color bgColor, String teks, Color textColor) { + return Container( + height: Constant.getActualY(context: context, y: 44), + decoration: + BoxDecoration(color: bgColor, borderRadius: BorderRadius.circular(8)), + child: Align( + child: Text( + "${teks}", + style: Constant.heading4(context: context) + .copyWith(fontWeight: FontWeight.w600, color: textColor), + ), + ), + ); + } +} diff --git a/lib/screen/history_screen/history_list_card.dart b/lib/screen/history_screen/history_list_card.dart new file mode 100644 index 0000000..02f99bf --- /dev/null +++ b/lib/screen/history_screen/history_list_card.dart @@ -0,0 +1,207 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import '/models/history_model.dart'; +import '/screen/detail_history_screen/detail_history_screen.dart'; +import '/widget/chip_type.dart'; +import 'package:intl/intl.dart'; + +import '../../app/constant.dart'; +import '../../provider/current_user_provider.dart'; + +class HistoryListCard extends HookConsumerWidget { + const HistoryListCard({ + super.key, + required this.ListRiwayat, + }); + + final List ListRiwayat; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final mCourirID = + ref.watch(currentUserProvider)?.model.user?.mCourierID ?? "0"; + return ListView.builder( + itemCount: ListRiwayat.length, + itemBuilder: (context, index) { + var data = ListRiwayat[index]; + String originalDateString = data.cdate.toString(); + DateTime originalDate = + DateFormat('dd-MM-yyyy HH:mm:ss').parse(originalDateString); + // String formattedDate = DateFormat.yMMMMd('id').format(originalDate); + String formattedDate = DateFormat('d MMM HH:mm').format(originalDate); + // Output: 30 Mei 2023 + return Container( + margin: EdgeInsets.only( + top: Constant.getActualY(context: context, y: 24)), + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 31)), + child: ElevatedButton( + onPressed: () { + showModalBottomSheet( + enableDrag: true, + isScrollControlled: true, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(16), + topRight: Radius.circular(16)), + ), + backgroundColor: Theme.of(context).scaffoldBackgroundColor, + context: context, + builder: (context) { + return DraggableScrollableSheet( + initialChildSize: 0.9, + maxChildSize: 0.95, + minChildSize: 0.5, + expand: false, + builder: (_, scrollController) => Stack( + children: [ + Container( + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX( + context: context, x: 125)), + child: Divider( + thickness: 3, + color: Constant.textPrimary, + ), + ), + SingleChildScrollView( + controller: scrollController, + child: DetailHistoryScreen( + data: { + "id": "1", + "alamat": "Cabang Kertajaya", + "jarak": "1,2 KM", + "tanggal": "02 Desember 14:50", + "tipe": "Pengantaran hasil Instansi", + "tipe_id": 4 + }, + prop: DetailHistoryProp( + data.tipeid.toString(), + mCourirID, + data.id.toString()), + )), + ], + )); + }, + ); + }, + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.all(10), + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(16))), + backgroundColor: data.ispending == 'N' + ? Constant.historyCardColor + : Constant.historyCardColorPending, + foregroundColor: Colors.grey), + child: Column( + children: [ + SizedBox( + height: Constant.getActualY(context: context, y: 14), + ), + Row( + children: [ + Image.asset( + "assets/icon_riwayat_motor.png", + width: Constant.getActualX(context: context, x: 64), + height: Constant.getActualX(context: context, x: 64), + ), + SizedBox( + width: Constant.getActualX(context: context, x: 12), + ), + Container( + height: Constant.getActualX(context: context, x: 70), + width: Constant.getActualX(context: context, x: 232), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Container( + constraints: BoxConstraints( + maxWidth: Constant.getActualX( + context: context, x: 130)), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + data.noSuratJalan.toString(), + textAlign: TextAlign.start, + style: Constant.caption1(context: context) + .copyWith( + fontWeight: FontWeight.w600, + color: Constant.primaryBlue), + ), + Text( + data.nama.toString(), + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: Constant.caption2(context: context) + .copyWith( + fontWeight: FontWeight.w600, + color: Constant.textBlack), + ), + ], + ), + ), + Container( + constraints: BoxConstraints( + maxWidth: Constant.getActualX( + context: context, x: 100)), + child: Text( + "${data.distance ?? '0'} Km", + style: Constant.heading3(context: context) + .copyWith( + fontWeight: FontWeight.w600, + color: Constant.textBlack), + ), + ) + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + formattedDate, + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: Constant.caption1(context: context) + .copyWith( + fontWeight: FontWeight.w500, + color: Constant.textBlack), + ), + ), + Container( + constraints: BoxConstraints( + maxWidth: Constant.getActualX( + context: context, x: 150)), + child: ChipType( + name: data.tipe.toString(), + tipe: data.tipeid!, + isDetailHistory: false, + ), + ) + ], + ) + ], + ), + ), + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 14), + ), + ], + ), + ), + ); + }, + ); + } +} diff --git a/lib/screen/history_screen/history_list_provider.dart b/lib/screen/history_screen/history_list_provider.dart new file mode 100644 index 0000000..cb47b76 --- /dev/null +++ b/lib/screen/history_screen/history_list_provider.dart @@ -0,0 +1,69 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '/models/history_model.dart'; +import '../../repository/history_screen_repository.dart'; +import '/screen/history_screen/history_screen.dart'; + +import '../../models/pending_work_model.dart'; +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; +import '../../repository/home_screen_repository.dart'; + +abstract class HistoryListState extends Equatable { + final DateTime date; + const HistoryListState(this.date); + @override + List get props => [date]; +} + +class HistoryListStateInit extends HistoryListState { + HistoryListStateInit() : super(DateTime.now()); +} + +class HistoryListStateLoading extends HistoryListState { + HistoryListStateLoading() : super(DateTime.now()); +} + +class HistoryListStateError extends HistoryListState { + final String message; + HistoryListStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class HistoryListStateDone extends HistoryListState { + final List model; + HistoryListStateDone({ + required this.model, + }) : super(DateTime.now()); +} + +//notifier +class HistoryListNotifier extends StateNotifier { + final Ref ref; + HistoryListNotifier({ + required this.ref, + }) : super(HistoryListStateInit()); + + void getData( + {required String id, required String date, required String type}) async { + try { + state = HistoryListStateLoading(); + final dio = ref.read(dioProvider); + final resp = await HistoryRepository(dio: dio) + .getData(date: date, id: id, type: type); + state = HistoryListStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = HistoryListStateError(message: e.message.toString()); + } else { + state = HistoryListStateError(message: e.toString()); + } + } + } +} + +//provider +final HistoryListProvider = + StateNotifierProvider( + (ref) => HistoryListNotifier(ref: ref)); diff --git a/lib/screen/history_screen/history_screen.dart b/lib/screen/history_screen/history_screen.dart new file mode 100644 index 0000000..ad21886 --- /dev/null +++ b/lib/screen/history_screen/history_screen.dart @@ -0,0 +1,466 @@ +import 'dart:async'; +import 'dart:convert'; + +import 'package:dropdown_button2/dropdown_button2.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:intl/intl.dart'; +import '/models/history_model.dart'; +import '/models/history_type_model.dart'; +import '/provider/history_filter_provider.dart'; +import '/screen/history_screen/history_list_card.dart'; +import '/screen/history_screen/history_list_provider.dart'; +import '/screen/history_screen/history_type_provider.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; +// import 'package:eva_icons_flutter/eva_icons_flutter.dart'; + +import '../../app/constant.dart'; +import '../../provider/current_user_provider.dart'; +import '../../widget/header_widget.dart'; +import '../../widget/snackbar_widget.dart'; + +class HistoryScreen extends HookConsumerWidget { + const HistoryScreen({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final historyListData = useState>([]); + final historyListLoading = useState(false); + final isMounted = useIsMounted(); + final mCourirID = + ref.watch(currentUserProvider)?.model.user?.mCourierID ?? "0"; + final dateFilter = + DateFormat('d MMM y').format(ref.watch(dateFilterProvider)); + final SelectedTypeFilter = ref.watch(selectedTypeProvider); + + fetchData() { + ref.read(HistoryListProvider.notifier).getData( + id: mCourirID, + date: DateFormat('dd-MM-yyyy').format(ref.watch(dateFilterProvider)), + type: SelectedTypeFilter.tipeid.toString()); + } + + final dateInput = useTextEditingController( + text: DateFormat('yyyy-MM-dd').format(DateTime.now())); + + // ref.watch(dateFilterProvider); + + ref.listen( + HistoryListProvider, + (previous, next) { + if (next is HistoryListStateLoading) { + historyListLoading.value = true; + } else if (next is HistoryListStateError) { + print(next.message); + historyListLoading.value = false; + SanckbarWidget(context, next.message, snackbarType.error); + } else if (next is HistoryListStateDone) { + print(next.model); + historyListData.value = next.model; + historyListLoading.value = false; + } + }, + ); + useEffect(() { + // final cancellationSource = CancellationSource(); + if (isMounted.call()) { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + fetchData(); + }); + } + return () {}; + }, []); + return RefreshIndicator( + onRefresh: () async { + fetchData(); + }, + child: Container( + // color: const Color(0XFFF4F6F8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + color: Colors.white, + height: Constant.getActualY(context: context, y: 56), + ), + HeaderWidget(teks: "Riwayat"), + SizedBox( + height: Constant.getActualY(context: context, y: 24), + ), + Container( + width: double.infinity, + margin: EdgeInsets.only( + left: Constant.getActualX(context: context, x: 31), + right: Constant.getActualX(context: context, x: 31)), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Text( + SelectedTypeFilter.tipename.toString(), + style: Constant.body1(context: context) + .copyWith(fontWeight: FontWeight.w600), + ), + SizedBox( + width: Constant.getActualX(context: context, x: 5), + ), + Icon( + Icons.fiber_manual_record_rounded, + size: 9, + ), + SizedBox( + width: Constant.getActualX(context: context, x: 5), + ), + Chip( + label: Text( + dateFilter, + style: Constant.body3(context: context).copyWith( + fontWeight: FontWeight.w700, + color: Colors.grey.shade600), + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(6))), + ) + ], + ), + InkWell( + onTap: () { + showModalBottomSheet( + isScrollControlled: true, + context: context, + builder: (BuildContext context) { + return HookConsumer( + builder: (BuildContext context, WidgetRef ref, a) { + final typeLoading = useState(false); + final typeData = + useState>(List.empty()); + final dateCtr = useTextEditingController( + // d MMMM y' + text: DateFormat('yyyy-MM-dd') + .format(ref.read(dateFilterProvider))); + final selectedType = useState( + ref.watch(selectedTypeProvider)); + getData() { + ref + .read(HistoryTypeListProvider.notifier) + .getHistoryType(); + } + + useEffect(() { + WidgetsBinding.instance + .addPostFrameCallback((timeStamp) async { + getData(); + }); + + return () {}; + }, []); + ref.listen(HistoryTypeListProvider, (previous, next) { + if (next is HistoryTypeListStateInit) { + typeLoading.value = true; + } else if (next is HistoryTypeListStateLoading) { + typeLoading.value = true; + } else if (next is HistoryTypeListStateError) { + typeLoading.value = false; + } else if (next is HistoryTypeListStateDone) { + print(jsonEncode(next.model)); + typeData.value = next.model; + typeLoading.value = false; + } + }); + return Container( + margin: EdgeInsets.symmetric( + horizontal: Constant.getActualX( + context: context, x: 31)), + height: + Constant.getActualY(context: context, y: 370), + child: Column( + // mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + height: Constant.getActualY( + context: context, y: 20), + ), + SizedBox( + width: Constant.getActualX( + context: context, x: 100), + child: Divider( + color: Colors.grey, + height: 2, + thickness: 2, + )), + SizedBox( + height: Constant.getActualY( + context: context, y: 20), + ), + Text( + "Filter", + style: Constant.heading3(context: context) + .copyWith( + fontWeight: FontWeight.w600, + color: Constant.primaryBlue), + ), + SizedBox( + height: Constant.getActualY( + context: context, y: 20), + ), + TextField( + controller: dateCtr, + decoration: InputDecoration( + suffixIcon: Icon(Icons.today_rounded), + border: OutlineInputBorder( + borderRadius: BorderRadius.all( + Radius.circular(8))), + labelText: + "Tanggal (yyyy-mm-dd)" //label text of field + ), + readOnly: true, + onTap: () async { + DateTime? pickedDate = await showDatePicker( + context: context, + initialDate: + DateTime.parse(dateCtr.text), + // initialDate: + // DateTime.parse(dateCtr.text), + firstDate: DateTime(1950), + initialEntryMode: + DatePickerEntryMode.calendarOnly, + //DateTime.now() - not to allow to choose before today. + lastDate: DateTime(2100)); + + if (pickedDate != null) { + print( + pickedDate); //pickedDate output format => 2021-03-10 00:00:00.000 + String formattedDate = + DateFormat('yyyy-MM-dd') + .format(pickedDate); + print( + formattedDate); //formatted date output using intl package => 2021-03-16 + dateCtr.text = + formattedDate; //set output date to TextField value. + // ref + // .read(dateFilterProvider.notifier) + // .state = pickedDate; + } else {} + }, + ), + SizedBox( + height: Constant.getActualY( + context: context, y: 20), + ), + Container( + // width: Constant.getActualX( + // context: context, x: 320), + // height: Constant.getActualY( + // context: context, y: 56), + child: DropdownButtonHideUnderline( + child: DropdownButton2( + isExpanded: true, + hint: Row( + children: [ + Expanded( + child: Text( + 'Select Item', + style: Constant.body1( + context: context) + .copyWith( + fontWeight: + FontWeight.w600, + color: + Constant.primaryDark), + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + items: typeData.value + .map((HistoryTypeModel option) { + return DropdownMenuItem< + HistoryTypeModel>( + value: option, + child: Text( + option.tipename.toString(), + style: Constant.body1( + context: context) + .copyWith( + color: Constant.textBlack, + fontWeight: + FontWeight.w600), + ), + ); + }).toList(), + value: selectedType.value, + onChanged: (HistoryTypeModel? newValue) { + selectedType.value = newValue!; + // if (newValue) { + // ref + // .read(selectedTypeProvider.notifier) + // .state = newValue!; + print( + "Branch ID Selected : ${selectedType.value.tipename}"); + print( + "Branch Code Selected : ${selectedType.value.tipename}"); + // } + }, + buttonStyleData: ButtonStyleData( + padding: EdgeInsets.only( + left: Constant.getActualX( + context: context, x: 10), + right: Constant.getActualX( + context: context, x: 10), + ), + + decoration: BoxDecoration( + // color: Colors.white, + border: Border.all( + color: Colors.grey.shade400), + + borderRadius: + BorderRadius.circular(8), + ), + // elevation: 2, + ), + iconStyleData: IconStyleData( + icon: typeLoading.value + ? LoadingAnimationWidget + .discreteCircle( + color: Constant.primaryBlue, + size: 20) + : Icon( + Icons + .keyboard_arrow_down_outlined, + ), + iconSize: 24, + iconEnabledColor: Constant.textBlack, + iconDisabledColor: Colors.grey, + ), + dropdownStyleData: DropdownStyleData( + maxHeight: Constant.getActualY( + context: context, y: 120), + width: Constant.getActualX( + context: context, x: 320), + padding: EdgeInsets.only( + top: Constant.getActualY( + context: context, y: 10), + left: Constant.getActualX( + context: context, x: 10), + right: Constant.getActualX( + context: context, x: 10), + bottom: Constant.getActualY( + context: context, y: 10), + ), + decoration: BoxDecoration( + // color: Constant.backgroundWhite, + borderRadius: + BorderRadius.circular(8), + ), + // elevation: 8, + scrollbarTheme: ScrollbarThemeData( + radius: const Radius.circular(40), + thickness: + MaterialStateProperty.all( + 6), + thumbVisibility: + MaterialStateProperty.all( + true), + ), + ), + menuItemStyleData: MenuItemStyleData( + height: Constant.getActualY( + context: context, y: 56), + padding: EdgeInsets.only( + top: Constant.getActualY( + context: context, y: 10), + left: Constant.getActualX( + context: context, x: 10), + right: Constant.getActualX( + context: context, x: 10), + ), + ), + ), + ), + ), + SizedBox( + height: Constant.getActualY( + context: context, y: 40), + ), + Container( + width: double.infinity, + height: Constant.getActualY( + context: context, y: 48), + child: ElevatedButton( + onPressed: () { + ref + .read(selectedTypeProvider.notifier) + .state = selectedType.value; + ref + .read(dateFilterProvider.notifier) + .state = DateTime.parse(dateCtr.text); + ref + .read(HistoryListProvider.notifier) + .getData( + id: mCourirID, + date: DateFormat('dd-MM-yyyy') + .format(ref.watch( + dateFilterProvider)), + type: ref + .watch(selectedTypeProvider) + .tipeid!); + Navigator.pop(context); + }, + child: Text( + "Cari", + style: Constant.heading3(context: context) + .copyWith( + fontWeight: FontWeight.w700), + ), + style: ButtonStyle( + backgroundColor: + MaterialStateColor.resolveWith( + (st) => Constant.primaryBlue), + shape: MaterialStateProperty.all< + RoundedRectangleBorder>( + RoundedRectangleBorder( + borderRadius: + BorderRadius.circular(8), + // side: BorderSide(color: Colors.red), + ), + ), + shadowColor: MaterialStateProperty.all( + const Color(0xffff48423d)), + elevation: MaterialStateProperty.all(4.0), + ), + ), + ) + ], + ), + ); + }); + }, + ); + }, + child: Icon( + Icons.filter_alt_outlined, + size: 25, + ), + ) + ], + ), + ), + Container( + // color: Colors.red, + height: Constant.getActualY(context: context, y: 588), + child: historyListLoading.value + ? Center( + child: LoadingAnimationWidget.fourRotatingDots( + color: Constant.primaryBlue, size: 50), + ) + : HistoryListCard(ListRiwayat: historyListData.value), + ), + ], + )), + ); + } +} diff --git a/lib/screen/history_screen/history_type_provider.dart b/lib/screen/history_screen/history_type_provider.dart new file mode 100644 index 0000000..eefe576 --- /dev/null +++ b/lib/screen/history_screen/history_type_provider.dart @@ -0,0 +1,68 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '/models/history_model.dart'; +import '../../repository/history_screen_repository.dart'; +import '/screen/history_screen/history_screen.dart'; + +import '../../models/history_type_model.dart'; +import '../../models/pending_work_model.dart'; +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; +import '../../repository/home_screen_repository.dart'; + +abstract class HistoryTypeListState extends Equatable { + final DateTime date; + const HistoryTypeListState(this.date); + @override + List get props => [date]; +} + +class HistoryTypeListStateInit extends HistoryTypeListState { + HistoryTypeListStateInit() : super(DateTime.now()); +} + +class HistoryTypeListStateLoading extends HistoryTypeListState { + HistoryTypeListStateLoading() : super(DateTime.now()); +} + +class HistoryTypeListStateError extends HistoryTypeListState { + final String message; + HistoryTypeListStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class HistoryTypeListStateDone extends HistoryTypeListState { + final List model; + HistoryTypeListStateDone({ + required this.model, + }) : super(DateTime.now()); +} + +//notifier +class HistoryTypeListNotifier extends StateNotifier { + final Ref ref; + HistoryTypeListNotifier({ + required this.ref, + }) : super(HistoryTypeListStateInit()); + + void getHistoryType() async { + try { + state = HistoryTypeListStateLoading(); + final dio = ref.read(dioProvider); + final resp = await HistoryRepository(dio: dio).getHistoryType(); + state = HistoryTypeListStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = HistoryTypeListStateError(message: e.message.toString()); + } else { + state = HistoryTypeListStateError(message: e.toString()); + } + } + } +} + +//provider +final HistoryTypeListProvider = + StateNotifierProvider( + (ref) => HistoryTypeListNotifier(ref: ref)); diff --git a/lib/screen/home_screen/home_screen.dart b/lib/screen/home_screen/home_screen.dart new file mode 100644 index 0000000..2366e8c --- /dev/null +++ b/lib/screen/home_screen/home_screen.dart @@ -0,0 +1,434 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import '/app/route.dart'; +import '/screen/home_screen/pending_work_provider.dart'; +import '/screen/home_screen/total_work_provider.dart'; +import '/widget/information_rpt_card.dart'; +import '/widget/pending_card.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; + +import '../../app/constant.dart'; +import '../../models/pending_work_model.dart'; +import '../../models/work_total_model.dart'; +import '../../provider/current_user_provider.dart'; +import '../../widget/information_card.dart'; +import '../../widget/snackbar_widget.dart'; +import '../../widget/work_card.dart'; + +class HomeScreen extends HookConsumerWidget { + const HomeScreen({ + super.key, + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final totalWork = useState(WorkTotalModel()); + final totalWorkLoading = useState(false); + final pendingWork = useState>([]); + final pendingWorkLoading = useState(false); + final mUsername = + ref.watch(currentUserProvider)?.model.user?.mStaffName ?? "0"; + + final mCourirID = + ref.watch(currentUserProvider)?.model.user?.mCourierID ?? "0"; + + fetchData() { + // ref.read(TotalWorkProvider.notifier).getData( + // id: ref.watch(currentUserProvider)?.model.user?.mCourierID ?? "0"); + ref.read(PendingWorkProvider.notifier).getData( + id: ref.watch(currentUserProvider)?.model.user?.mCourierID ?? "0"); + } + + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + fetchData(); + }); + return () {}; + }, [mCourirID]); + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + fetchData(); + }); + return () {}; + }, []); + + ref.listen( + TotalWorkProvider, + (previous, next) { + if (next is TotalWorkStateLoading) { + totalWorkLoading.value = true; + } else if (next is TotalWorkStateError) { + print(next.message); + totalWorkLoading.value = false; + SanckbarWidget(context, next.message, snackbarType.error); + } else if (next is TotalWorkStateDone) { + print(next.model); + totalWork.value = next.model; + totalWorkLoading.value = false; + } + }, + ); + ref.listen( + PendingWorkProvider, + (previous, next) { + if (next is PendingWorkStateLoading) { + pendingWorkLoading.value = true; + } else if (next is PendingWorkStateError) { + print(next.message); + pendingWorkLoading.value = false; + SanckbarWidget(context, next.message, snackbarType.error); + } else if (next is PendingWorkStateDone) { + // print(jsonEncode(next.model)); + // print(next.model.length); + pendingWork.value = next.model; + pendingWorkLoading.value = false; + } + }, + ); + return RefreshIndicator( + onRefresh: () async { + fetchData(); + }, + triggerMode: RefreshIndicatorTriggerMode.onEdge, + child: SingleChildScrollView( + physics: const AlwaysScrollableScrollPhysics(), + child: Container( + // color: Colors.red, + padding: EdgeInsets.fromLTRB( + Constant.getActualX(context: context, x: 40), + Constant.getActualY(context: context, y: 68), + Constant.getActualX(context: context, x: 40), + 0), + child: Column( + children: [ + SizedBox( + height: Constant.getActualY(context: context, y: 48), + child: Row( + children: [ + Expanded( + flex: 10, + child: SizedBox( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Hi $mUsername", + overflow: TextOverflow.ellipsis, + style: Constant.heading4(context: context) + .copyWith( + color: Constant.primaryBlue, + fontWeight: FontWeight.w600), + ), + Text( + "Silahkan lakukan pengantaran", + overflow: TextOverflow.ellipsis, + style: Constant.caption1(context: context) + .copyWith(fontWeight: FontWeight.w500), + ) + ], + ), + )), + Expanded( + flex: 2, + child: Container( + alignment: Alignment.topRight, + // color: Colors.red, + child: Image.asset( + "assets/icon_home_bell_blue.png", + width: Constant.getActualY(context: context, y: 24), + height: + Constant.getActualY(context: context, y: 24), + ), + )) + ], + ), + ), + SizedBox( + height: Constant.getActualY(context: context, y: 20), + ), + // InformationCard( + // data: totalWork.value, + // loading: totalWorkLoading.value, + // bgColor: Color(0XFFF3F7FB), + // elevation: false, + // ), + InformationRptCard(mCourirID), + SizedBox( + height: Constant.getActualY(context: context, y: 25), + ), + SizedBox( + // color: Colors.blue, + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Ambil Pekerjaan Baru", + style: Constant.heading4(context: context).copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + // InkWell( + // onTap: () {}, + // child: Row( + // children: [ + // Text( + // "Detail", + // style: Constant.caption1(context: context) + // .copyWith( + // color: Constant.textPrimary, + // fontWeight: FontWeight.w500), + // ), + // SizedBox( + // width: Constant.getActualX( + // context: context, x: 5), + // ), + // const Icon( + // Icons.arrow_forward_ios_outlined, + // size: 15, + // ) + // ], + // )) + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 20), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + // color: Colors.red, + height: Constant.getActualY(context: context, y: 80), + width: Constant.getActualX(context: context, x: 86), + decoration: BoxDecoration( + // color: Colors.red, + boxShadow: [ + BoxShadow( + color: Color(0XFF919EAB).withOpacity(0.12), + offset: Offset(0.0, 12), //(x,y) + blurRadius: 24, + spreadRadius: -4), + BoxShadow( + color: Color(0XFF919EAB).withOpacity(0.2), + offset: Offset(0.0, 0), //(x,y) + blurRadius: 2, + ), + ], + borderRadius: BorderRadius.circular(12)), + child: ElevatedButton( + style: ElevatedButton.styleFrom( + elevation: 0.2, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + surfaceTintColor: Colors.grey, + foregroundColor: Colors.grey, + backgroundColor: Colors.white), + onPressed: () { + Navigator.of(context).pushNamed(inputPekerjaan, + arguments: inputPekerjaanProp(0, 0)); + }, + child: Column( + mainAxisAlignment: + MainAxisAlignment.spaceEvenly, + children: [ + Image.asset( + width: Constant.getActualX( + context: context, x: 24), + height: Constant.getActualX( + context: context, x: 24), + "assets/icon_home_hasil_blue.png"), + Text( + "Hasil", + style: Constant.body3(context: context) + .copyWith( + fontWeight: FontWeight.w600, + color: Constant.textSecondary), + ) + ], + )), + ), + Container( + // color: Colors.red, + height: Constant.getActualY(context: context, y: 80), + width: Constant.getActualX(context: context, x: 86), + decoration: BoxDecoration( + // color: Colors.red, + boxShadow: [ + BoxShadow( + color: Color(0XFF919EAB).withOpacity(0.12), + offset: Offset(0.0, 12), //(x,y) + blurRadius: 24, + spreadRadius: -4), + BoxShadow( + color: Color(0XFF919EAB).withOpacity(0.2), + offset: Offset(0.0, 0), //(x,y) + blurRadius: 2, + ), + ], + borderRadius: BorderRadius.circular(12)), + child: ElevatedButton( + style: ElevatedButton.styleFrom( + elevation: 0.2, + shape: RoundedRectangleBorder( + side: const BorderSide( + width: 0.1, color: Colors.grey), + borderRadius: BorderRadius.circular(12), + ), + surfaceTintColor: Colors.grey, + foregroundColor: Colors.grey, + backgroundColor: Colors.white), + onPressed: () { + Navigator.of(context).pushNamed(inputPekerjaan, + arguments: inputPekerjaanProp(1, 0)); + }, + child: Column( + mainAxisAlignment: + MainAxisAlignment.spaceEvenly, + children: [ + Image.asset( + width: Constant.getActualX( + context: context, x: 24), + height: Constant.getActualX( + context: context, x: 24), + "assets/icon_home_sample_blue.png"), + Text( + "Sample", + style: Constant.body3(context: context) + .copyWith( + fontWeight: FontWeight.w600, + color: Constant.textSecondary), + ) + ], + )), + ), + Container( + // color: Colors.red, + height: Constant.getActualY(context: context, y: 80), + width: Constant.getActualX(context: context, x: 86), + decoration: BoxDecoration( + // color: Colors.red, + boxShadow: [ + BoxShadow( + color: Color(0XFF919EAB).withOpacity(0.12), + offset: Offset(0.0, 12), //(x,y) + blurRadius: 24, + spreadRadius: -4), + BoxShadow( + color: Color(0XFF919EAB).withOpacity(0.2), + offset: Offset(0.0, 0), //(x,y) + blurRadius: 2, + ), + ], + borderRadius: BorderRadius.circular(12)), + child: ElevatedButton( + style: ElevatedButton.styleFrom( + elevation: 0.2, + shape: RoundedRectangleBorder( + side: const BorderSide( + width: 0.1, color: Colors.grey), + borderRadius: BorderRadius.circular(12), + ), + surfaceTintColor: Colors.grey, + foregroundColor: Colors.grey, + backgroundColor: Colors.white), + onPressed: () { + Navigator.of(context).pushNamed(inputPekerjaan, + arguments: inputPekerjaanProp(2, 0)); + }, + child: Column( + mainAxisAlignment: + MainAxisAlignment.spaceEvenly, + children: [ + Image.asset( + width: Constant.getActualX( + context: context, x: 24), + height: Constant.getActualX( + context: context, x: 24), + "assets/icon_home_lainnya_blue.png"), + Text( + "Lainya", + style: Constant.body3(context: context) + .copyWith( + fontWeight: FontWeight.w600, + color: Constant.textSecondary), + ) + ], + )), + ), + ], + ) + ], + ), + ), + SizedBox( + height: Constant.getActualY(context: context, y: 30), + ), + SizedBox( + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Pending List Pekerjaan", + overflow: TextOverflow.ellipsis, + style: Constant.heading4(context: context).copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + // InkWell( + // onTap: () {}, + // child: Row( + // children: [ + // Text( + // "Detail", + // style: Constant.caption1(context: context) + // .copyWith( + // color: Constant.textPrimary, + // fontWeight: FontWeight.w500), + // ), + // SizedBox( + // width: Constant.getActualX( + // context: context, x: 5), + // ), + // const Icon( + // Icons.arrow_forward_ios_outlined, + // size: 15, + // ) + // ], + // )) + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 20), + ), + Container( + // color: Colors.red, + padding: + EdgeInsets.symmetric(horizontal: 0.9, vertical: 0.5), + height: Constant.getActualY(context: context, y: 235), + child: pendingWorkLoading.value + ? LoadingAnimationWidget.fourRotatingDots( + color: Constant.primaryBlue, size: 50) + : ListView.builder( + itemCount: pendingWork.value.length, + itemBuilder: (context, index) { + return PendingCardWidget( + data: pendingWork.value[index]); + }, + ), + ) + ], + ), + ) + ], + ), + ), + ), + ); + } +} diff --git a/lib/screen/home_screen/pending_work_provider.dart b/lib/screen/home_screen/pending_work_provider.dart new file mode 100644 index 0000000..b57fe68 --- /dev/null +++ b/lib/screen/home_screen/pending_work_provider.dart @@ -0,0 +1,64 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../models/pending_work_model.dart'; +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; +import '../../repository/home_screen_repository.dart'; + +abstract class PendingWorkState extends Equatable { + final DateTime date; + const PendingWorkState(this.date); + @override + List get props => [date]; +} + +class PendingWorkStateInit extends PendingWorkState { + PendingWorkStateInit() : super(DateTime.now()); +} + +class PendingWorkStateLoading extends PendingWorkState { + PendingWorkStateLoading() : super(DateTime.now()); +} + +class PendingWorkStateError extends PendingWorkState { + final String message; + PendingWorkStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class PendingWorkStateDone extends PendingWorkState { + final List model; + PendingWorkStateDone({ + required this.model, + }) : super(DateTime.now()); +} + +//notifier +class PendingWorkNotifier extends StateNotifier { + final Ref ref; + PendingWorkNotifier({ + required this.ref, + }) : super(PendingWorkStateInit()); + + void getData({required String id}) async { + try { + state = PendingWorkStateLoading(); + final dio = ref.read(dioProvider); + final resp = await HomeScreenRepository(dio: dio).getPendingWork(id: id); + state = PendingWorkStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = PendingWorkStateError(message: e.message.toString()); + } else { + state = PendingWorkStateError(message: e.toString()); + } + } + } +} + +//provider +final PendingWorkProvider = + StateNotifierProvider( + (ref) => PendingWorkNotifier(ref: ref)); diff --git a/lib/screen/home_screen/total_work_provider.dart b/lib/screen/home_screen/total_work_provider.dart new file mode 100644 index 0000000..8e5261a --- /dev/null +++ b/lib/screen/home_screen/total_work_provider.dart @@ -0,0 +1,64 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../models/work_total_model.dart'; +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; +import '../../repository/home_screen_repository.dart'; + +abstract class TotalWorkState extends Equatable { + final DateTime date; + const TotalWorkState(this.date); + @override + List get props => [date]; +} + +class TotalWorkStateInit extends TotalWorkState { + TotalWorkStateInit() : super(DateTime.now()); +} + +class TotalWorkStateLoading extends TotalWorkState { + TotalWorkStateLoading() : super(DateTime.now()); +} + +class TotalWorkStateError extends TotalWorkState { + final String message; + TotalWorkStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class TotalWorkStateDone extends TotalWorkState { + final WorkTotalModel model; + TotalWorkStateDone({ + required this.model, + }) : super(DateTime.now()); +} + +//notifier +class TotalWorkNotifier extends StateNotifier { + final Ref ref; + TotalWorkNotifier({ + required this.ref, + }) : super(TotalWorkStateInit()); + + void getData({required String id}) async { + try { + state = TotalWorkStateLoading(); + final dio = ref.read(dioProvider); + final resp = await HomeScreenRepository(dio: dio).getTotalWork(id: id); + state = TotalWorkStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = TotalWorkStateError(message: e.message.toString()); + } else { + state = TotalWorkStateError(message: e.toString()); + } + } + } +} + +//provider +final TotalWorkProvider = + StateNotifierProvider( + (ref) => TotalWorkNotifier(ref: ref)); diff --git a/lib/screen/input_pekerjaan/input_lain_lain_loadcabang_provider.dart b/lib/screen/input_pekerjaan/input_lain_lain_loadcabang_provider.dart new file mode 100644 index 0000000..fa4f826 --- /dev/null +++ b/lib/screen/input_pekerjaan/input_lain_lain_loadcabang_provider.dart @@ -0,0 +1,61 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '/models/response_list_branch_model.dart'; +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; +import '../../repository/input_lain_lain_repository.dart'; + +// 3. state provider +final inputLainLainCabang = StateNotifierProvider( + (ref) => InputLainLainNotifier(ref: ref)); + +// 2. notifier +class InputLainLainNotifier extends StateNotifier { + final Ref ref; + InputLainLainNotifier({required this.ref}) : super(InputLainLainStateInit()); + void loadListCabangInputLainLain() async { + try { + state = InputLainLainStateLoading(); + final resp = await InputLainLainRepository(dio: ref.read(dioProvider)) + .loadListCabangInputLainLain(); + + state = InputLainLainStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = InputLainLainStateError(message: e.message ?? ""); + } else { + state = InputLainLainStateError(message: e.toString()); + } + } + } +} + +// 1. state +abstract class InputLainLainState extends Equatable { + final DateTime date; + const InputLainLainState(this.date); + @override + List get props => [date]; +} + +class InputLainLainStateInit extends InputLainLainState { + InputLainLainStateInit() : super(DateTime.now()); +} + +class InputLainLainStateLoading extends InputLainLainState { + InputLainLainStateLoading() : super(DateTime.now()); +} + +class InputLainLainStateError extends InputLainLainState { + final String? message; + InputLainLainStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class InputLainLainStateDone extends InputLainLainState { + final List model; + InputLainLainStateDone({ + required this.model, + }) : super(DateTime.now()); +} diff --git a/lib/screen/input_pekerjaan/input_lain_lain_save_provider.dart b/lib/screen/input_pekerjaan/input_lain_lain_save_provider.dart new file mode 100644 index 0000000..a797f39 --- /dev/null +++ b/lib/screen/input_pekerjaan/input_lain_lain_save_provider.dart @@ -0,0 +1,81 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../repository/input_lain_lain_repository.dart'; +import '../../models/response_save_pengantaran_hasil_model.dart'; +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; +import '../../repository/pengambilan_bahan_repository.dart'; +import '../../repository/pengantaran_hasil_dokter_repository.dart'; + +// 3. state provider +final inputLainLainSave = StateNotifierProvider((ref) => InputLainLainSaveNotifier(ref: ref)); + +// 2. notifier +class InputLainLainSaveNotifier + extends StateNotifier { + final Ref ref; + InputLainLainSaveNotifier({required this.ref}) + : super(InputLainLainSaveStateInit()); + void inputLainLainSave({ + required String courierID, + required String tipeID, + required String branchID, + required String branchCode, + required String pickup, + required String destination, + required String note + }) async { + try { + state = InputLainLainSaveStateLoading(); + final resp = await InputLainLainRepository(dio: ref.read(dioProvider)) + .inputLainLainSave( + courierID: courierID, + tipeID: tipeID, + branchID: branchID, + branchCode: branchCode, + pickup: pickup, + destination: destination, + note: note + ); + + state = InputLainLainSaveStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = InputLainLainSaveStateError(message: e.message ?? ""); + } else { + state = InputLainLainSaveStateError(message: e.toString()); + } + } + } +} + +// 1. state +abstract class InputLainLainSaveState extends Equatable { + final DateTime date; + const InputLainLainSaveState(this.date); + @override + List get props => [date]; +} + +class InputLainLainSaveStateInit extends InputLainLainSaveState { + InputLainLainSaveStateInit() : super(DateTime.now()); +} + +class InputLainLainSaveStateLoading extends InputLainLainSaveState { + InputLainLainSaveStateLoading() : super(DateTime.now()); +} + +class InputLainLainSaveStateError extends InputLainLainSaveState { + final String? message; + InputLainLainSaveStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class InputLainLainSaveStateDone extends InputLainLainSaveState { + final ResponseSavePengantaranHasilModel model; + InputLainLainSaveStateDone({ + required this.model, + }) : super(DateTime.now()); +} diff --git a/lib/screen/input_pekerjaan/input_lain_lain_screen.dart b/lib/screen/input_pekerjaan/input_lain_lain_screen.dart new file mode 100644 index 0000000..77f3ef4 --- /dev/null +++ b/lib/screen/input_pekerjaan/input_lain_lain_screen.dart @@ -0,0 +1,432 @@ +import 'dart:async'; + +import 'package:dropdown_button2/dropdown_button2.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; + +import '../../app/constant.dart'; +import '../../app/route.dart'; +import '../../models/response_list_branch_model.dart'; +import '../../provider/current_user_provider.dart'; +import '../../widget/custom_textfield.dart'; +import '../../widget/sas_textfield.dart'; +import '../../widget/snackbar_widget.dart'; +import 'input_lain_lain_loadcabang_provider.dart'; +import 'input_lain_lain_save_provider.dart'; + +class InputLainLain extends HookConsumerWidget { + // const InputLainLain({super.key}); + + const InputLainLain({super.key, required this.data}); + final Map data; + + @override + Widget build(BuildContext context, WidgetRef ref) { + print(data); + final mCourirID = + ref.watch(currentUserProvider)?.model.user?.mCourierID ?? "0"; + + final tipePekerjaanIDSave = data['tipePekerjaan']; + final focusNodeCatatan = useFocusNode(); + final catatanhasFocus = useState(false); + + final focusNodeAlamatPengambilan = useFocusNode(); + final alamatPengambilanhasFocus = useState(false); + + final focusNodeAlamatPengantaran = useFocusNode(); + final alamatPengantaranhasFocus = useState(false); + + final ctrlCatatan = useTextEditingController(text: ""); + final ctrlAlamatPengambilan = useTextEditingController(text: ""); + final ctrlAlamatPengantaran = useTextEditingController(text: ""); + + // cabang + final selectedDropdownCabang = useState( + ResponseListBranchModel( + branchid: "", branchaddress: "", branchcode: "", branchname: "")); + final dropdownItemsCabang = + useState>(List.empty(growable: true)); + + final isLoading = useState(false); + final errorMessage = useState(""); + + focusNodeCatatan.addListener(() { + if (focusNodeCatatan.hasFocus) { + catatanhasFocus.value = true; + } else { + catatanhasFocus.value = false; + } + }); + + focusNodeAlamatPengambilan.addListener(() { + if (focusNodeAlamatPengambilan.hasFocus) { + alamatPengambilanhasFocus.value = true; + } else { + alamatPengambilanhasFocus.value = false; + } + }); + + focusNodeAlamatPengantaran.addListener(() { + if (focusNodeAlamatPengantaran.hasFocus) { + alamatPengantaranhasFocus.value = true; + } else { + alamatPengantaranhasFocus.value = false; + } + }); + + // populate list cabang + ref.listen(inputLainLainCabang, (prev, next) { + if (next is InputLainLainStateLoading) { + isLoading.value = true; + } else if (next is InputLainLainStateError) { + isLoading.value = false; + errorMessage.value = next.message ?? ""; + SanckbarWidget(context, "${next.message}", snackbarType.error); + Timer(const Duration(seconds: 3), () { + errorMessage.value = ""; + }); + } else if (next is InputLainLainStateDone) { + if (next.model.isNotEmpty) { + dropdownItemsCabang.value = next.model; + selectedDropdownCabang.value = next.model[0]; + } else { + dropdownItemsCabang.value = [ + ResponseListBranchModel( + branchid: "0", + branchcode: "", + branchname: "Cabang Belum Ada", + branchaddress: ""), + ]; + + selectedDropdownCabang.value = dropdownItemsCabang.value[0]; + SanckbarWidget(context, "Data Cabang Belum Ada", snackbarType.error); + return; + } + isLoading.value = false; + } + }); + + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + ref.read(inputLainLainCabang.notifier).loadListCabangInputLainLain(); + }); + return () {}; + }, []); + +// save pengambilan bahan + ref.listen(inputLainLainSave, (prev, next) { + if (next is InputLainLainSaveStateLoading) { + isLoading.value = true; + } else if (next is InputLainLainSaveStateError) { + isLoading.value = false; + errorMessage.value = next.message ?? ""; + SanckbarWidget(context, "${next.message}", snackbarType.error); + Timer(const Duration(seconds: 3), () { + errorMessage.value = ""; + }); + } else if (next is InputLainLainSaveStateDone) { + if (next.model.status != "OK") { + SanckbarWidget(context, "${next.model.message}", snackbarType.error); + } else { + Navigator.of(context).pushNamedAndRemoveUntil( + konfirmasiRoute, + (route) => false, + ); + } + isLoading.value = false; + } + }); + + return Column( + children: [ + // dropdown cabang + Container( + width: Constant.getActualX(context: context, x: 320), + height: Constant.getActualY(context: context, y: 56), + child: DropdownButtonHideUnderline( + child: DropdownButton2( + isExpanded: true, + hint: Row( + children: [ + Expanded( + child: Text( + 'Select Item', + style: Constant.body1(context: context).copyWith( + fontWeight: FontWeight.w600, + color: Constant.primaryDark), + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + items: dropdownItemsCabang.value + .map((ResponseListBranchModel option) { + return DropdownMenuItem( + value: option, + child: Text( + option.branchname.toString(), + style: Constant.body1(context: context).copyWith( + color: Constant.primaryBlue, + fontWeight: FontWeight.w600), + ), + ); + }).toList(), + value: selectedDropdownCabang.value, + onChanged: (ResponseListBranchModel? newValue) { + // if (newValue) { + selectedDropdownCabang.value = newValue!; + print( + "Branch ID Selected : ${selectedDropdownCabang.value.branchid}"); + print( + "Branch Code Selected : ${selectedDropdownCabang.value.branchcode}"); + // } + }, + buttonStyleData: ButtonStyleData( + height: Constant.getActualY(context: context, y: 56), + width: Constant.getActualX(context: context, x: 320), + padding: EdgeInsets.only( + left: Constant.getActualX(context: context, x: 10), + right: Constant.getActualX(context: context, x: 10), + ), + decoration: BoxDecoration( + color: Constant.backgroundWhite, + border: Border.all(color: Constant.textBlack, width: 1), + borderRadius: BorderRadius.circular(8), + ), + elevation: 2, + ), + iconStyleData: IconStyleData( + icon: isLoading.value + ? LoadingAnimationWidget.discreteCircle( + color: Constant.primaryBlue, size: 20) + : Icon( + Icons.keyboard_arrow_down_outlined, + ), + iconSize: 24, + iconEnabledColor: Constant.textBlack, + iconDisabledColor: Colors.grey, + ), + dropdownStyleData: DropdownStyleData( + maxHeight: Constant.getActualY(context: context, y: 200), + width: Constant.getActualX(context: context, x: 320), + padding: EdgeInsets.only( + top: Constant.getActualY(context: context, y: 10), + left: Constant.getActualX(context: context, x: 10), + right: Constant.getActualX(context: context, x: 10), + bottom: Constant.getActualY(context: context, y: 10), + ), + decoration: BoxDecoration( + color: Constant.backgroundWhite, + borderRadius: BorderRadius.circular(8), + boxShadow: [ + BoxShadow( + color: Colors.grey, + blurRadius: 20.0, + spreadRadius: 2.0, + offset: Offset(0.0, 0.0), + ), + ], + ), + elevation: 8, + offset: const Offset(0, -10), + scrollbarTheme: ScrollbarThemeData( + radius: const Radius.circular(40), + thickness: MaterialStateProperty.all(6), + thumbVisibility: MaterialStateProperty.all(true), + ), + ), + menuItemStyleData: MenuItemStyleData( + height: Constant.getActualY(context: context, y: 56), + padding: EdgeInsets.only( + top: Constant.getActualY(context: context, y: 10), + left: Constant.getActualX(context: context, x: 10), + right: Constant.getActualX(context: context, x: 10), + ), + ), + ), + ), + ), + SizedBox( + height: Constant.getActualY(context: context, y: 28), + ), + + // catatan + Container( + width: Constant.getActualX(context: context, x: 320), + height: Constant.getActualY(context: context, y: 120), + // color: Colors.green, + child: SasTextFieldArea( + controller: ctrlCatatan, + hintText: "Catatan", + labelText: "Catatan", + hasFocus: catatanhasFocus.value, + focusNode: focusNodeCatatan, + ), + ), + SizedBox( + height: Constant.getActualY(context: context, y: 28), + ), + // alamat pengambilan + Container( + width: Constant.getActualX(context: context, x: 320), + height: Constant.getActualY(context: context, y: 120), + // color: Colors.green, + child: SasTextFieldArea( + controller: ctrlAlamatPengambilan, + hintText: "Alamat Pengambilan", + labelText: "Alamat Pengambilan", + hasFocus: alamatPengambilanhasFocus.value, + focusNode: focusNodeAlamatPengambilan, + ), + ), + SizedBox( + height: Constant.getActualY(context: context, y: 28), + ), + // alamat pengantaran + Container( + width: Constant.getActualX(context: context, x: 320), + height: Constant.getActualY(context: context, y: 120), + // color: Colors.green, + child: SasTextFieldArea( + controller: ctrlAlamatPengantaran, + hintText: "Alamat Pengantaran", + labelText: "Alamat Pengantaran", + hasFocus: alamatPengantaranhasFocus.value, + focusNode: focusNodeAlamatPengantaran, + ), + ), + + SizedBox( + height: Constant.getActualY(context: context, y: 30), + ), + + // loading + // if (isLoading.value) ...[ + // // Center( + // // child: SizedBox( + // // width: Constant.getActualX(context: context, x: 20), + // // height: Constant.getActualY(context: context, y: 20), + // // child: CircularProgressIndicator(), + // // ), + // // ), + // const Center( + // child: CircularProgressIndicator(), + // ), + // ], + + // SizedBox( + // height: Constant.getActualY(context: context, y: 60), + // ), + + // button batal & simpan + Align( + alignment: Alignment.bottomCenter, + child: Container( + width: Constant.getActualX(context: context, x: 317), + height: Constant.getActualY(context: context, y: 36), + // color: Colors.pink, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + // batal + ElevatedButton( + onPressed: isLoading.value + ? null + : () { + Navigator.pop(context); + }, + style: ButtonStyle( + backgroundColor: MaterialStateColor.resolveWith( + (st) => Constant.backgroundWhite), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: BorderSide(color: Constant.blueButton), + ), + ), + shadowColor: MaterialStateProperty.all(Color(0xffff48423d)), + elevation: MaterialStateProperty.all(4.0), + ), + child: Align( + alignment: Alignment.center, + child: Text( + 'Batal', + style: Constant.body3(context: context).copyWith( + color: Constant.blueButton, + fontWeight: FontWeight.w700, + ), + ), + ), + ), + + // simpan + ElevatedButton( + onPressed: (isLoading.value) + ? null + : () { + if (ctrlCatatan.text == "" || + ctrlAlamatPengambilan.text == "" || + ctrlAlamatPengantaran.text == "") { + SanckbarWidget( + context, + "Catatan atau Alamat Pengambilan atau Alamat Pengantaran Silahkan Diisi", + snackbarType.warning); + } else { + print(selectedDropdownCabang.value.branchid); + if (selectedDropdownCabang.value.branchid == "") { + SanckbarWidget(context, "Data Cabang Belum Ada", + snackbarType.warning); + } else { + ref + .read(inputLainLainSave.notifier) + .inputLainLainSave( + courierID: mCourirID, + tipeID: tipePekerjaanIDSave, + branchID: selectedDropdownCabang + .value.branchid + .toString(), + branchCode: selectedDropdownCabang + .value.branchcode + .toString(), + pickup: ctrlAlamatPengambilan.text, + destination: ctrlAlamatPengantaran.text, + note: ctrlCatatan.text); + } + } + }, + style: ButtonStyle( + backgroundColor: MaterialStateColor.resolveWith( + (st) => Constant.blueButton), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + // side: BorderSide(color: Constant.blueButton), + ), + ), + shadowColor: MaterialStateProperty.all(Color(0xffff48423d)), + elevation: MaterialStateProperty.all(4.0), + ), + child: Align( + alignment: Alignment.center, + child: isLoading.value + ? LoadingAnimationWidget.staggeredDotsWave( + color: Colors.white, size: 30) + : Text( + 'Simpan', + style: Constant.body3(context: context).copyWith( + color: Constant.backgroundWhite, + fontWeight: FontWeight.w700, + ), + ), + ), + ), + ], + ), + ), + ), + ], + ); + } +} diff --git a/lib/screen/input_pekerjaan/input_pekerjaan_lain_lain.dart b/lib/screen/input_pekerjaan/input_pekerjaan_lain_lain.dart new file mode 100644 index 0000000..bd644ba --- /dev/null +++ b/lib/screen/input_pekerjaan/input_pekerjaan_lain_lain.dart @@ -0,0 +1,241 @@ +import 'dart:async'; + +import 'package:dropdown_button2/dropdown_button2.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import '/screen/input_pekerjaan/input_lain_lain_screen.dart'; + +import '../../app/constant.dart'; +import 'input_pengambilan_bahan_screen.dart'; +import 'input_pengantaran_hasil_dokter_screen.dart'; +import 'input_pengantaran_hasil_instansi_screen.dart'; +import 'input_pengantaran_hasil_pasien_screen.dart'; + +class DummyDropdownTipe { + final String options; + final int id; + + DummyDropdownTipe(this.options, this.id); +} + +class DummyRadioTipe { + final String text; + final int id; + + DummyRadioTipe(this.text, this.id); +} + +class InputPekerjaanPekerjaanLainLain extends HookConsumerWidget { + const InputPekerjaanPekerjaanLainLain({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final selectedDropdown = + useState(DummyDropdownTipe("", 0)); + final dropdownItems = + useState>(List.empty(growable: true)); + + final radioButtonItems = + useState>(List.empty(growable: true)); + final selectedRadio = useState(DummyRadioTipe("", 0)); + + useEffect(() { + dropdownItems.value = [ + DummyDropdownTipe("Pengantaran Hasil", 1), + DummyDropdownTipe("Pengambilan Bahan", 2), + DummyDropdownTipe("Lain-lain", 3), + ]; + + selectedDropdown.value = dropdownItems.value[2]; + // selectedDropdown.value = dropdownItems.value[1]; + }, []); + + final isLoading = useState(false); + final isMounted = useIsMounted(); + + return GestureDetector( + onTap: () => FocusManager.instance.primaryFocus?.unfocus(), + child: Scaffold( + backgroundColor: Constant.backgroundWhite, + appBar: AppBar( + toolbarHeight: Constant.getActualY(context: context, y: 110), + backgroundColor: Constant.backgroundWhite, + elevation: 0, + titleSpacing: 0, + centerTitle: false, + title: Text( + 'Kembali', + style: Constant.body1(context: context).copyWith( + color: Constant.textBlack, + fontWeight: FontWeight.w600, + ), + ), + leading: GestureDetector( + child: Icon( + Icons.arrow_back_ios, + color: Constant.textBlack, + size: Constant.getActualY(context: context, y: 16), + ), + onTap: () { + Navigator.pop(context); + }, + ), + automaticallyImplyLeading: true, + ), + body: SingleChildScrollView( + child: SizedBox( + width: Constant.getActualX(context: context, x: 390), + height: Constant.getActualY(context: context, y: 760), + child: Padding( + padding: EdgeInsets.only( + left: Constant.getActualX(context: context, x: 35), + right: Constant.getActualX(context: context, x: 35), + // bottom: Constant.getActualY(context: context, y: 40) + ), + child: Column( + children: [ + // dropdown + if (dropdownItems.value.isEmpty) ...[ + Text('Dropdown No Data') + ] else ...[ + DropdownButtonHideUnderline( + child: DropdownButton2( + isExpanded: true, + hint: Row( + children: [ + Expanded( + child: Text( + 'Select Item', + style: Constant.body1(context: context) + .copyWith( + fontWeight: FontWeight.w600, + color: Constant.primaryDark), + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + items: + dropdownItems.value.map((DummyDropdownTipe option) { + return DropdownMenuItem( + value: option, + child: Text( + option.options, + style: Constant.body1(context: context).copyWith( + color: Constant.primaryBlue, + fontWeight: FontWeight.w600), + ), + ); + }).toList(), + value: selectedDropdown.value, + // onChanged: (DummyDropdownTipe? newValue) { + // // if (newValue) { + // selectedDropdown.value = newValue!; + // print(selectedDropdown.value.id); + // // } + // }, + onChanged: null, + buttonStyleData: ButtonStyleData( + height: Constant.getActualY(context: context, y: 56), + width: Constant.getActualX(context: context, x: 320), + padding: EdgeInsets.only( + left: Constant.getActualX(context: context, x: 10), + right: Constant.getActualX(context: context, x: 10), + ), + decoration: BoxDecoration( + color: Constant.backgroundWhite, + border: + Border.all(color: Constant.textBlack, width: 1), + borderRadius: BorderRadius.circular(8), + ), + elevation: 2, + ), + iconStyleData: IconStyleData( + icon: Icon( + Icons.keyboard_arrow_down_outlined, + ), + iconSize: 24, + iconEnabledColor: Constant.textBlack, + iconDisabledColor: Colors.grey, + ), + dropdownStyleData: DropdownStyleData( + maxHeight: + Constant.getActualY(context: context, y: 200), + width: Constant.getActualX(context: context, x: 320), + padding: EdgeInsets.only( + top: Constant.getActualY(context: context, y: 10), + left: Constant.getActualX(context: context, x: 10), + right: Constant.getActualX(context: context, x: 10), + bottom: + Constant.getActualY(context: context, y: 10), + ), + decoration: BoxDecoration( + color: Constant.backgroundWhite, + borderRadius: BorderRadius.circular(8), + boxShadow: [ + BoxShadow( + color: Colors.grey, + blurRadius: 20.0, + spreadRadius: 2.0, + offset: Offset(0.0, 0.0), + ), + ], + ), + elevation: 8, + offset: const Offset(0, -10), + scrollbarTheme: ScrollbarThemeData( + radius: const Radius.circular(40), + thickness: MaterialStateProperty.all(6), + thumbVisibility: + MaterialStateProperty.all(true), + ), + ), + menuItemStyleData: MenuItemStyleData( + height: Constant.getActualY(context: context, y: 56), + padding: EdgeInsets.only( + top: Constant.getActualY(context: context, y: 10), + left: Constant.getActualX(context: context, x: 10), + right: Constant.getActualX(context: context, x: 10), + ), + ), + ), + ), + ], + + SizedBox( + height: Constant.getActualY(context: context, y: 32), + ), + + // form + Container( + width: Constant.getActualX(context: context, x: 320), + height: Constant.getActualY(context: context, y: 612), + // color: Colors.green, + child: Column( + children: [ + if (selectedDropdown.value.id == 3) ...[ + const InputLainLain( + data: { + "tipePekerjaan": "4", + "tipePengantaranHasil": "" + }, + ) + ] + ], + ), + ), + + // spacing + SizedBox( + height: Constant.getActualY(context: context, y: 32), + ), + ], + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/screen/input_pekerjaan/input_pekerjaan_pengambilan_bahan.dart b/lib/screen/input_pekerjaan/input_pekerjaan_pengambilan_bahan.dart new file mode 100644 index 0000000..3913ad6 --- /dev/null +++ b/lib/screen/input_pekerjaan/input_pekerjaan_pengambilan_bahan.dart @@ -0,0 +1,249 @@ +import 'dart:async'; + +import 'package:dropdown_button2/dropdown_button2.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import '/screen/input_pekerjaan/input_lain_lain_screen.dart'; + +import '../../app/constant.dart'; +import 'input_pengambilan_bahan_screen.dart'; +import 'input_pengantaran_hasil_dokter_screen.dart'; +import 'input_pengantaran_hasil_instansi_screen.dart'; +import 'input_pengantaran_hasil_pasien_screen.dart'; + +class DummyDropdownTipe { + final String options; + final int id; + + DummyDropdownTipe(this.options, this.id); +} + +class DummyRadioTipe { + final String text; + final int id; + + DummyRadioTipe(this.text, this.id); +} + +class InputPekerjaanPengambilanBahan extends HookConsumerWidget { + const InputPekerjaanPengambilanBahan({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final selectedDropdown = + useState(DummyDropdownTipe("", 0)); + final dropdownItems = + useState>(List.empty(growable: true)); + + final radioButtonItems = + useState>(List.empty(growable: true)); + final selectedRadio = useState(DummyRadioTipe("", 0)); + + useEffect(() { + dropdownItems.value = [ + DummyDropdownTipe("Pengantaran Hasil", 1), + DummyDropdownTipe("Pengambilan Bahan", 2), + DummyDropdownTipe("Lain-lain", 3), + ]; + + selectedDropdown.value = dropdownItems.value[1]; + // selectedDropdown.value = dropdownItems.value[1]; + }, []); + + final isLoading = useState(false); + final isMounted = useIsMounted(); + + return GestureDetector( + onTap: () => FocusManager.instance.primaryFocus?.unfocus(), + child: Scaffold( + backgroundColor: Constant.backgroundWhite, + appBar: AppBar( + toolbarHeight: Constant.getActualY(context: context, y: 110), + backgroundColor: Constant.backgroundWhite, + elevation: 0, + titleSpacing: 0, + centerTitle: false, + title: Text( + 'Kembali', + style: Constant.body1(context: context).copyWith( + color: Constant.textBlack, + fontWeight: FontWeight.w600, + ), + ), + leading: GestureDetector( + child: Icon( + Icons.arrow_back_ios, + color: Constant.textBlack, + size: Constant.getActualY(context: context, y: 16), + ), + onTap: () { + Navigator.pop(context); + }, + ), + automaticallyImplyLeading: true, + ), + body: SingleChildScrollView( + child: SizedBox( + width: Constant.getActualX(context: context, x: 390), + height: Constant.getActualY(context: context, y: 760), + child: Padding( + padding: EdgeInsets.only( + left: Constant.getActualX(context: context, x: 35), + right: Constant.getActualX(context: context, x: 35), + // bottom: Constant.getActualY(context: context, y: 40) + ), + child: Column( + children: [ + // dropdown + if (dropdownItems.value.isEmpty) ...[ + Text('Dropdown No Data') + ] else ...[ + DropdownButtonHideUnderline( + child: DropdownButton2( + isExpanded: true, + hint: Row( + children: [ + Expanded( + child: Text( + 'Select Item', + style: Constant.body1(context: context) + .copyWith( + fontWeight: FontWeight.w600, + color: Constant.primaryDark), + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + items: + dropdownItems.value.map((DummyDropdownTipe option) { + return DropdownMenuItem( + value: option, + child: Text( + option.options, + style: Constant.body1(context: context).copyWith( + color: Constant.primaryBlue, + fontWeight: FontWeight.w600), + ), + ); + }).toList(), + value: selectedDropdown.value, + // onChanged: (DummyDropdownTipe? newValue) { + // // if (newValue) { + // selectedDropdown.value = newValue!; + // print(selectedDropdown.value.id); + // // } + // }, + onChanged: null, + buttonStyleData: ButtonStyleData( + height: Constant.getActualY(context: context, y: 56), + width: Constant.getActualX(context: context, x: 320), + padding: EdgeInsets.only( + left: Constant.getActualX(context: context, x: 10), + right: Constant.getActualX(context: context, x: 10), + ), + decoration: BoxDecoration( + color: Constant.backgroundWhite, + border: + Border.all(color: Constant.textBlack, width: 1), + borderRadius: BorderRadius.circular(8), + ), + elevation: 2, + ), + iconStyleData: IconStyleData( + icon: Icon( + Icons.keyboard_arrow_down_outlined, + ), + iconSize: 24, + iconEnabledColor: Constant.textBlack, + iconDisabledColor: Colors.grey, + ), + dropdownStyleData: DropdownStyleData( + maxHeight: + Constant.getActualY(context: context, y: 200), + width: Constant.getActualX(context: context, x: 320), + padding: EdgeInsets.only( + top: Constant.getActualY(context: context, y: 10), + left: Constant.getActualX(context: context, x: 10), + right: Constant.getActualX(context: context, x: 10), + bottom: + Constant.getActualY(context: context, y: 10), + ), + decoration: BoxDecoration( + color: Constant.backgroundWhite, + borderRadius: BorderRadius.circular(8), + boxShadow: [ + BoxShadow( + color: Colors.grey, + blurRadius: 20.0, + spreadRadius: 2.0, + offset: Offset(0.0, 0.0), + ), + ], + ), + elevation: 8, + offset: const Offset(0, -10), + scrollbarTheme: ScrollbarThemeData( + radius: const Radius.circular(40), + thickness: MaterialStateProperty.all(6), + thumbVisibility: + MaterialStateProperty.all(true), + ), + ), + menuItemStyleData: MenuItemStyleData( + height: Constant.getActualY(context: context, y: 56), + padding: EdgeInsets.only( + top: Constant.getActualY(context: context, y: 10), + left: Constant.getActualX(context: context, x: 10), + right: Constant.getActualX(context: context, x: 10), + ), + ), + ), + ), + ], + + SizedBox( + height: Constant.getActualY(context: context, y: 32), + ), + + // form + Container( + width: Constant.getActualX(context: context, x: 320), + height: Constant.getActualY(context: context, y: 612), + // color: Colors.green, + child: Column( + children: [ + if (selectedDropdown.value.id == 2) ...[ + // Text('Antar Bahan') + Container( + width: + Constant.getActualX(context: context, x: 320), + height: + Constant.getActualY(context: context, y: 612), + // color: Colors.amber, + child: const InputPengambilanBahanScreen( + data: { + "tipePekerjaan": "5", + "tipePengantaranHasil": "" + }, + ), + ), + ], + ], + ), + ), + + // spacing + SizedBox( + height: Constant.getActualY(context: context, y: 32), + ), + ], + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/screen/input_pekerjaan/input_pekerjaan_pengantaran_hasil.dart b/lib/screen/input_pekerjaan/input_pekerjaan_pengantaran_hasil.dart new file mode 100644 index 0000000..502157f --- /dev/null +++ b/lib/screen/input_pekerjaan/input_pekerjaan_pengantaran_hasil.dart @@ -0,0 +1,338 @@ +import 'dart:async'; + +import 'package:dropdown_button2/dropdown_button2.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import '/screen/input_pekerjaan/input_lain_lain_screen.dart'; + +import '../../app/constant.dart'; +import 'input_pengambilan_bahan_screen.dart'; +import 'input_pengantaran_hasil_dokter_screen.dart'; +import 'input_pengantaran_hasil_instansi_screen.dart'; +import 'input_pengantaran_hasil_pasien_screen.dart'; + +class DummyDropdownTipe { + final String options; + final int id; + + DummyDropdownTipe(this.options, this.id); +} + +class DummyRadioTipe { + final String text; + final int id; + + DummyRadioTipe(this.text, this.id); +} + +class InputPekerjaanPengantaranHasil extends HookConsumerWidget { + const InputPekerjaanPengantaranHasil({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final selectedDropdown = + useState(DummyDropdownTipe("", 0)); + final dropdownItems = + useState>(List.empty(growable: true)); + + final radioButtonItems = + useState>(List.empty(growable: true)); + final selectedRadio = useState(DummyRadioTipe("", 0)); + + useEffect(() { + dropdownItems.value = [ + DummyDropdownTipe("Pengantaran Hasil", 1), + DummyDropdownTipe("Pengambilan Bahan", 2), + DummyDropdownTipe("Lain-lain", 3), + ]; + + selectedDropdown.value = dropdownItems.value[0]; + // selectedDropdown.value = dropdownItems.value[1]; + + radioButtonItems.value = [ + DummyRadioTipe("Pasien", 1), + DummyRadioTipe("Instansi", 2), + DummyRadioTipe("Dokter", 3), + ]; + + selectedRadio.value = radioButtonItems.value[0]; + }, []); + + final isLoading = useState(false); + final isMounted = useIsMounted(); + + return GestureDetector( + onTap: () => FocusManager.instance.primaryFocus?.unfocus(), + child: Scaffold( + backgroundColor: Constant.backgroundWhite, + resizeToAvoidBottomInset: false, + appBar: AppBar( + toolbarHeight: Constant.getActualY(context: context, y: 110), + backgroundColor: Constant.backgroundWhite, + elevation: 0, + titleSpacing: 0, + centerTitle: false, + title: Text( + 'Kembali', + style: Constant.body1(context: context).copyWith( + color: Constant.textBlack, + fontWeight: FontWeight.w600, + ), + ), + leading: GestureDetector( + child: Icon( + Icons.arrow_back_ios, + color: Constant.textBlack, + size: Constant.getActualY(context: context, y: 16), + ), + onTap: () { + Navigator.pop(context); + }, + ), + automaticallyImplyLeading: true, + ), + body: SizedBox( + width: Constant.getActualX(context: context, x: 390), + height: Constant.getActualY(context: context, y: 760), + child: Padding( + padding: EdgeInsets.only( + left: Constant.getActualX(context: context, x: 35), + right: Constant.getActualX(context: context, x: 35), + // bottom: Constant.getActualY(context: context, y: 40) + ), + child: Column( + children: [ + // dropdown + if (dropdownItems.value.isEmpty) ...[ + Text('Dropdown No Data') + ] else ...[ + DropdownButtonHideUnderline( + child: DropdownButton2( + isExpanded: true, + hint: Row( + children: [ + Expanded( + child: Text( + 'Select Item', + style: Constant.body1(context: context) + .copyWith( + fontWeight: FontWeight.w600, + color: Constant.primaryDark), + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + items: + dropdownItems.value.map((DummyDropdownTipe option) { + return DropdownMenuItem( + value: option, + child: Text( + option.options, + style: Constant.body1(context: context).copyWith( + color: Constant.primaryBlue, + fontWeight: FontWeight.w600), + ), + ); + }).toList(), + value: selectedDropdown.value, + // onChanged: (DummyDropdownTipe? newValue) { + // // if (newValue) { + // selectedDropdown.value = newValue!; + // print(selectedDropdown.value.id); + // // } + // }, + onChanged: null, + buttonStyleData: ButtonStyleData( + height: Constant.getActualY(context: context, y: 56), + width: Constant.getActualX(context: context, x: 320), + padding: EdgeInsets.only( + left: Constant.getActualX(context: context, x: 10), + right: Constant.getActualX(context: context, x: 10), + ), + decoration: BoxDecoration( + color: Constant.backgroundWhite, + border: + Border.all(color: Constant.textBlack, width: 1), + borderRadius: BorderRadius.circular(8), + ), + elevation: 2, + ), + iconStyleData: IconStyleData( + icon: Icon( + Icons.keyboard_arrow_down_outlined, + ), + iconSize: 24, + iconEnabledColor: Constant.textBlack, + iconDisabledColor: Colors.grey, + ), + dropdownStyleData: DropdownStyleData( + maxHeight: + Constant.getActualY(context: context, y: 200), + width: Constant.getActualX(context: context, x: 320), + padding: EdgeInsets.only( + top: Constant.getActualY(context: context, y: 10), + left: Constant.getActualX(context: context, x: 10), + right: Constant.getActualX(context: context, x: 10), + bottom: + Constant.getActualY(context: context, y: 10), + ), + decoration: BoxDecoration( + color: Constant.backgroundWhite, + borderRadius: BorderRadius.circular(8), + boxShadow: [ + BoxShadow( + color: Colors.grey, + blurRadius: 20.0, + spreadRadius: 2.0, + offset: Offset(0.0, 0.0), + ), + ], + ), + elevation: 8, + offset: const Offset(0, -10), + scrollbarTheme: ScrollbarThemeData( + radius: const Radius.circular(40), + thickness: MaterialStateProperty.all(6), + thumbVisibility: + MaterialStateProperty.all(true), + ), + ), + menuItemStyleData: MenuItemStyleData( + height: Constant.getActualY(context: context, y: 56), + padding: EdgeInsets.only( + top: Constant.getActualY(context: context, y: 10), + left: Constant.getActualX(context: context, x: 10), + right: Constant.getActualX(context: context, x: 10), + ), + ), + ), + ), + ], + + SizedBox( + height: Constant.getActualY(context: context, y: 32), + ), + + // form + Container( + width: Constant.getActualX(context: context, x: 320), + height: Constant.getActualY(context: context, y: 612), + // color: Colors.green, + child: Column( + children: [ + if (selectedDropdown.value.id == 1) ...[ + // radio button + SizedBox( + width: + Constant.getActualX(context: context, x: 320), + height: + Constant.getActualY(context: context, y: 36), + // color: Colors.green, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + if (radioButtonItems.value.isEmpty) ...[ + Text('Radio Button Empty') + ] else ...[ + for (var i = 0; + i < radioButtonItems.value.length; + i++) ...[ + Expanded( + flex: 1, + child: Row( + children: [ + Radio( + activeColor: Constant.primaryMain, + value: radioButtonItems.value[i], + groupValue: selectedRadio.value, + onChanged: (DummyRadioTipe? index) { + if (isMounted()) { + selectedRadio.value = index!; + } else { + return; + } + }, + ), + Expanded( + child: Text( + radioButtonItems.value[i].text, + style: Constant.body3( + context: context) + .copyWith( + color: + Constant.textPrimary, + fontWeight: + FontWeight.w400), + ), + ) + ], + ), + ), + ] + ] + ], + ), + ), + SizedBox( + height: + Constant.getActualY(context: context, y: 32), + ), + + // form scan dan inputan + Container( + width: + Constant.getActualX(context: context, x: 320), + height: + Constant.getActualY(context: context, y: 544), + // color: Colors.amber, + child: Column( + children: [ + if (selectedRadio.value.id == 1) ...[ + // InputPengantaranHasilPasienScreen(data: { + // "tipePekerjaan": selectedDropdown.value.id.toString(), + // "tipePengantaranHasil": selectedRadio.value.id.toString() + // }), + + const InputPengantaranHasilPasienScreen( + data: { + "tipePekerjaan": "1", + "tipePengantaranHasil": "" + }), + ], + if (selectedRadio.value.id == 2) ...[ + const InputPengantaranHasilInstansiScreen( + data: { + "tipePekerjaan": "2", + "tipePengantaranHasil": "" + }) + ], + if (selectedRadio.value.id == 3) ...[ + const InputPengantaranHasilDokterScreen( + data: { + "tipePekerjaan": "3", + "tipePengantaranHasil": "" + }, + ) + ] + ], + ), + ), + ] + ], + ), + ), + + // spacing + SizedBox( + height: Constant.getActualY(context: context, y: 32), + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/screen/input_pekerjaan/input_pekerjaan_screen.dart b/lib/screen/input_pekerjaan/input_pekerjaan_screen.dart new file mode 100644 index 0000000..9b72ac7 --- /dev/null +++ b/lib/screen/input_pekerjaan/input_pekerjaan_screen.dart @@ -0,0 +1,376 @@ +import 'dart:async'; + +import 'package:dropdown_button2/dropdown_button2.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import '/screen/input_pekerjaan/input_lain_lain_screen.dart'; + +import '../../app/constant.dart'; +import '../../provider/current_scan_provider.dart'; +import 'input_pengambilan_bahan_screen.dart'; +import 'input_pengantaran_hasil_dokter_screen.dart'; +import 'input_pengantaran_hasil_instansi_screen.dart'; +import 'input_pengantaran_hasil_pasien_screen.dart'; + +class DummyDropdownTipe { + final String options; + final int id; + + DummyDropdownTipe(this.options, this.id); +} + +class DummyRadioTipe { + final String text; + final int id; + + DummyRadioTipe(this.text, this.id); +} + +class InputPekerjaan extends HookConsumerWidget { + const InputPekerjaan({super.key, required this.input, required this.tipe}); + final int input; + final int tipe; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final selectedDropdown = + useState(DummyDropdownTipe("", 0)); + final dropdownItems = + useState>(List.empty(growable: true)); + + final radioButtonItems = + useState>(List.empty(growable: true)); + final selectedRadio = useState(DummyRadioTipe("", 0)); + + useEffect(() { + dropdownItems.value = [ + DummyDropdownTipe("Pengantaran Hasil", 1), + DummyDropdownTipe("Pengambilan Bahan", 2), + DummyDropdownTipe("Lain-lain", 3), + ]; + + selectedDropdown.value = dropdownItems.value[input]; + // selectedDropdown.value = dropdownItems.value[1]; + + radioButtonItems.value = [ + DummyRadioTipe("Pasien", 1), + DummyRadioTipe("Instansi", 2), + DummyRadioTipe("Dokter", 3), + ]; + + selectedRadio.value = radioButtonItems.value[tipe]; + }, []); + + final isLoading = useState(false); + final isMounted = useIsMounted(); + + return GestureDetector( + onTap: () => FocusManager.instance.primaryFocus?.unfocus(), + child: Scaffold( + resizeToAvoidBottomInset: true, + backgroundColor: Constant.backgroundWhite, + appBar: AppBar( + toolbarHeight: Constant.getActualY(context: context, y: 110), + backgroundColor: Constant.backgroundWhite, + elevation: 0, + titleSpacing: 0, + centerTitle: false, + title: Text( + 'Kembali', + style: Constant.body1(context: context).copyWith( + color: Constant.textBlack, + fontWeight: FontWeight.w600, + ), + ), + leading: GestureDetector( + child: Icon( + Icons.arrow_back_ios, + color: Constant.textBlack, + size: Constant.getActualY(context: context, y: 16), + ), + onTap: () { + Navigator.pop(context); + }, + ), + automaticallyImplyLeading: true, + ), + body: SingleChildScrollView( + child: SizedBox( + width: Constant.getActualX(context: context, x: 390), + height: Constant.getActualY(context: context, y: 760), + child: Padding( + padding: EdgeInsets.only( + left: Constant.getActualX(context: context, x: 35), + right: Constant.getActualX(context: context, x: 35), + // bottom: Constant.getActualY(context: context, y: 40) + ), + child: Column( + children: [ + // dropdown + if (dropdownItems.value.isEmpty) ...[ + Text('Dropdown No Data') + ] else ...[ + DropdownButtonHideUnderline( + child: DropdownButton2( + isExpanded: true, + hint: Row( + children: [ + Expanded( + child: Text( + 'Select Item', + style: Constant.body1(context: context) + .copyWith( + fontWeight: FontWeight.w600, + color: Constant.primaryDark), + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + items: + dropdownItems.value.map((DummyDropdownTipe option) { + return DropdownMenuItem( + value: option, + child: Text( + option.options, + style: Constant.body1(context: context).copyWith( + color: Constant.primaryBlue, + fontWeight: FontWeight.w600), + ), + ); + }).toList(), + value: selectedDropdown.value, + onChanged: (DummyDropdownTipe? newValue) { + // if (newValue) { + selectedDropdown.value = newValue!; + print(selectedDropdown.value.id); + // } + }, + buttonStyleData: ButtonStyleData( + height: Constant.getActualY(context: context, y: 56), + width: Constant.getActualX(context: context, x: 320), + padding: EdgeInsets.only( + left: Constant.getActualX(context: context, x: 10), + right: Constant.getActualX(context: context, x: 10), + ), + decoration: BoxDecoration( + color: Constant.backgroundWhite, + border: + Border.all(color: Constant.textBlack, width: 1), + borderRadius: BorderRadius.circular(8), + ), + elevation: 2, + ), + iconStyleData: IconStyleData( + icon: Icon( + Icons.keyboard_arrow_down_outlined, + ), + iconSize: 24, + iconEnabledColor: Constant.textBlack, + iconDisabledColor: Colors.grey, + ), + dropdownStyleData: DropdownStyleData( + maxHeight: + Constant.getActualY(context: context, y: 200), + width: Constant.getActualX(context: context, x: 320), + padding: EdgeInsets.only( + top: Constant.getActualY(context: context, y: 10), + left: Constant.getActualX(context: context, x: 10), + right: Constant.getActualX(context: context, x: 10), + bottom: + Constant.getActualY(context: context, y: 10), + ), + decoration: BoxDecoration( + color: Constant.backgroundWhite, + borderRadius: BorderRadius.circular(8), + boxShadow: [ + BoxShadow( + color: Colors.grey, + blurRadius: 20.0, + spreadRadius: 2.0, + offset: Offset(0.0, 0.0), + ), + ], + ), + elevation: 8, + offset: const Offset(0, -10), + scrollbarTheme: ScrollbarThemeData( + radius: const Radius.circular(40), + thickness: MaterialStateProperty.all(6), + thumbVisibility: + MaterialStateProperty.all(true), + ), + ), + menuItemStyleData: MenuItemStyleData( + height: Constant.getActualY(context: context, y: 56), + padding: EdgeInsets.only( + top: Constant.getActualY(context: context, y: 10), + left: Constant.getActualX(context: context, x: 10), + right: Constant.getActualX(context: context, x: 10), + ), + ), + ), + ), + ], + + SizedBox( + height: Constant.getActualY(context: context, y: 32), + ), + + // form + Container( + width: Constant.getActualX(context: context, x: 320), + height: Constant.getActualY(context: context, y: 612), + // color: Colors.green, + child: Column( + children: [ + if (selectedDropdown.value.id == 1) ...[ + // radio button + SizedBox( + width: + Constant.getActualX(context: context, x: 320), + height: + Constant.getActualY(context: context, y: 36), + // color: Colors.green, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + if (radioButtonItems.value.isEmpty) ...[ + Text('Radio Button Empty') + ] else ...[ + for (var i = 0; + i < radioButtonItems.value.length; + i++) ...[ + Expanded( + flex: 1, + child: Row( + children: [ + Radio( + activeColor: Constant.primaryMain, + value: radioButtonItems.value[i], + groupValue: selectedRadio.value, + onChanged: (DummyRadioTipe? index) { + if (isMounted()) { + selectedRadio.value = index!; + ref + .read(barcodeScanResult + .notifier) + .update((state) => ""); + ref + .read(isFromScanScreen + .notifier) + .update((state) => false); + } else { + return; + } + }, + ), + Expanded( + child: Text( + radioButtonItems.value[i].text, + style: Constant.body3( + context: context) + .copyWith( + color: + Constant.textPrimary, + fontWeight: + FontWeight.w400), + ), + ) + ], + ), + ), + ] + ] + ], + ), + ), + SizedBox( + height: + Constant.getActualY(context: context, y: 32), + ), + + // form scan dan inputan + Container( + width: + Constant.getActualX(context: context, x: 320), + height: + Constant.getActualY(context: context, y: 544), + // color: Colors.amber, + child: Wrap( + children: [ + if (selectedRadio.value.id == 1) ...[ + // InputPengantaranHasilPasienScreen(data: { + // "tipePekerjaan": selectedDropdown.value.id.toString(), + // "tipePengantaranHasil": selectedRadio.value.id.toString() + // }), + + const InputPengantaranHasilPasienScreen( + data: { + "tipePekerjaan": "1", + "tipePengantaranHasil": "" + }), + ], + if (selectedRadio.value.id == 2) ...[ + const InputPengantaranHasilInstansiScreen( + data: { + "tipePekerjaan": "2", + "tipePengantaranHasil": "" + }) + ], + if (selectedRadio.value.id == 3) ...[ + const InputPengantaranHasilDokterScreen( + data: { + "tipePekerjaan": "3", + "tipePengantaranHasil": "" + }, + ) + ] + ], + ), + ), + ] else ...[ + // height 36 + 544 + 32 + if (selectedDropdown.value.id == 2) ...[ + // Text('Antar Bahan') + Container( + width: + Constant.getActualX(context: context, x: 320), + height: + Constant.getActualY(context: context, y: 612), + // color: Colors.amber, + child: const InputPengambilanBahanScreen( + data: { + "tipePekerjaan": "5", + "tipePengantaranHasil": "" + }, + ), + ), + ], + if (selectedDropdown.value.id == 3) ...[ + const InputLainLain( + data: { + "tipePekerjaan": "4", + "tipePengantaranHasil": "" + }, + ) + ] + ] + ], + ), + ), + + // spacing + SizedBox( + height: Constant.getActualY(context: context, y: 32), + ), + ], + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/screen/input_pekerjaan/input_pengambilan_bahan_screen.dart b/lib/screen/input_pekerjaan/input_pengambilan_bahan_screen.dart new file mode 100644 index 0000000..93b57db --- /dev/null +++ b/lib/screen/input_pekerjaan/input_pengambilan_bahan_screen.dart @@ -0,0 +1,614 @@ +import 'dart:async'; +import 'dart:convert'; + +import 'package:dio/dio.dart'; +import 'package:dropdown_button2/dropdown_button2.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:flutter_typeahead/flutter_typeahead.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import '/models/company_model.dart'; +import '/models/response_list_branch_model.dart'; +import '../../repository/company_repository.dart'; +import '/screen/input_pekerjaan/pengambilan_bahan_loadcabang_provider.dart'; +import '/screen/input_pekerjaan/search_company_provider.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; + +import '../../app/constant.dart'; +import '../../app/route.dart'; +import '../../provider/current_user_provider.dart'; +import '../../provider/dio_provider.dart'; +import '../../widget/custom_textfield.dart'; +import '../../widget/sas_textfield.dart'; +import '../../widget/snackbar_widget.dart'; +import 'pengambilan_bahan_save_provider.dart'; +import 'pengantaran_hasil_dokter_save_provider.dart'; + +class DummyCabang { + final String namaCabang; + final int idCabang; + + DummyCabang(this.namaCabang, this.idCabang); +} + +class InputPengambilanBahanScreen extends HookConsumerWidget { + // const InputPengambilanBahanScreen({super.key}); + + const InputPengambilanBahanScreen({super.key, required this.data}); + final Map data; + + @override + Widget build(BuildContext context, WidgetRef ref) { + // print(data); + final mCourirID = + ref.watch(currentUserProvider)?.model.user?.mCourierID ?? "0"; + + final tipePekerjaanIDSave = data['tipePekerjaan']; + final deliveryIDSave = useState(""); + // cabang + final selectedDropdownCabang = useState( + ResponseListBranchModel( + branchid: "", branchaddress: "", branchcode: "", branchname: "")); + final dropdownItemsCabang = + useState>(List.empty(growable: true)); + + final selectedCompany = useState(CompanyModel()); + final companyInput = useTextEditingController(); + final searchCompanyLoading = useState(false); + + final searchCompanyErrorMsg = useState(""); + + final focusNodeNamaTujuan = useFocusNode(); + final namaTujuanhasFocus = useState(false); + + final focusNodeAlamat = useFocusNode(); + final alamathasFocus = useState(false); + + final ctrlAlamat = useTextEditingController(text: ""); + final ctrlNamaTujuan = useTextEditingController(text: ""); + + final isLoading = useState(false); + final errorMessage = useState(""); + + focusNodeNamaTujuan.addListener(() { + if (focusNodeNamaTujuan.hasFocus) { + namaTujuanhasFocus.value = true; + } else { + namaTujuanhasFocus.value = false; + } + }); + + focusNodeAlamat.addListener(() { + if (focusNodeAlamat.hasFocus) { + alamathasFocus.value = true; + } else { + alamathasFocus.value = false; + } + }); + + // populate list cabang + ref.listen(pengambilanBahanListCabang, (prev, next) { + if (next is PengambilanBahanStateLoading) { + isLoading.value = true; + } else if (next is PengambilanBahanStateError) { + isLoading.value = false; + errorMessage.value = next.message ?? ""; + SanckbarWidget(context, "${next.message}", snackbarType.error); + Timer(const Duration(seconds: 3), () { + errorMessage.value = ""; + }); + } else if (next is PengambilanBahanStateDone) { + if (next.model.isNotEmpty) { + dropdownItemsCabang.value = next.model; + selectedDropdownCabang.value = next.model[0]; + } else { + dropdownItemsCabang.value = [ + ResponseListBranchModel( + branchid: "0", + branchcode: "", + branchname: "Cabang Belum Ada", + branchaddress: ""), + ]; + + selectedDropdownCabang.value = dropdownItemsCabang.value[0]; + SanckbarWidget(context, "Data Cabang Belum Ada", snackbarType.error); + return; + } + isLoading.value = false; + } + }); + + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + ref + .read(pengambilanBahanListCabang.notifier) + .loadListCabangPengambilanBahan(); + }); + return () {}; + }, []); + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + ref + .read(pengambilanBahanListCabang.notifier) + .loadListCabangPengambilanBahan(); + }); + return () {}; + }, []); + + // save pengambilan bahan + ref.listen(pengambilanBahanSave, (prev, next) { + if (next is PengambilanBahanSaveStateLoading) { + isLoading.value = true; + } else if (next is PengambilanBahanSaveStateError) { + isLoading.value = false; + errorMessage.value = next.message ?? ""; + SanckbarWidget(context, "${next.message}", snackbarType.error); + Timer(const Duration(seconds: 3), () { + errorMessage.value = ""; + }); + } else if (next is PengambilanBahanSaveStateDone) { + if (next.model.status != "OK") { + SanckbarWidget(context, "${next.model.message}", snackbarType.error); + } else { + Navigator.of(context).pushNamedAndRemoveUntil( + konfirmasiRoute, + (route) => false, + ); + } + isLoading.value = false; + } + }); + + Future> searchCompany({required String keyword}) async { + // http: //10.9.9.3/one-api/v1/courier/mobile/inputcourier/search_company_all/?search=sas + try { + searchCompanyLoading.value = true; + // final url = + // "${Constant.baseUrl}/v1/courier/mobile/inputcourier/search_comsdfpany_all/?search=$keyword"; + final url = + "${Constant.baseUrl}/v1/courier/mobile/inputcourier/search_company_all/?search=$keyword"; + final coba = await Dio().get(url); + final resp = jsonDecode(coba.toString()); + // final resp = await get(service: url); + List data; + // print(resp['data']); + if (resp['status'] == 'OK') { + data = []; + if (resp['data'] != null) { + resp['data']['records'].forEach((e) { + final model = CompanyModel.fromJson(e); + data.add(model); + }); + } else { + data = []; + } + } else { + data = []; + } + print("company repo"); + print(jsonEncode(data)); + + searchCompanyLoading.value = false; + return data; + } catch (e) { + searchCompanyErrorMsg.value = e.toString(); + SanckbarWidget(context, e.toString(), snackbarType.error); + searchCompanyLoading.value = false; + return []; + } + } + + return Column( + children: [ + // nama tujuan + // Container( + // width: Constant.getActualX(context: context, x: 320), + // height: Constant.getActualY(context: context, y: 56), + // child: SasTextField( + // controller: ctrlNamaTujuan, + // hintText: "Nama Tujuan", + // labelText: "Nama Tujuan", + // hasFocus: namaTujuanhasFocus.value, + // focusNode: focusNodeNamaTujuan, + // ), + // ), + + Autocomplete( + fieldViewBuilder: + (context, textEditingController, focusNode, onFieldSubmitted) { + ctrlNamaTujuan.text = textEditingController.text; + return TextField( + controller: textEditingController, + focusNode: focusNode, + decoration: InputDecoration( + hintText: "Nama Tujuan", + labelText: "Nama Tujuan", + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide( + color: Constant.textGrey, + width: 2, + ), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide( + color: Constant.primaryMain, + width: 2, + ), + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide( + color: Constant.primaryBlue, + width: 3, + ), + ))); + }, + optionsBuilder: (tv) async { + final debouncer = Debouncer(milliseconds: 500); + + final search = tv.text.toString(); + List data = List.empty(growable: true); + // print(tv.text.length); + + if (search == "" || search.isEmpty) { + data = List.empty(); + } else { + // var data = await searchCompany(keyword: search); + if (search.length >= 3) { + final dio = ref.read(dioProvider); + data = await searchCompany(keyword: search); + + // return CompanyRepository(dio: dio).search(keyword: search); + } + } + return data; + }, + displayStringForOption: (option) { + return option.mCompanyName.toString(); + }, + optionsViewBuilder: (BuildContext context, onSelected, options) { + return Align( + alignment: Alignment.topLeft, + child: Material( + elevation: 10, + // borderRadius: BorderRadius.all(Radius.circular(50)), + color: Colors.white, + borderRadius: const BorderRadius.only( + bottomLeft: Radius.circular(10), + bottomRight: Radius.circular(10)), + child: Container( + width: Constant.getActualX(context: context, x: 320), + height: searchCompanyLoading.value + ? Constant.getActualY(context: context, y: 50) + : Constant.getActualY(context: context, y: 250), + decoration: const BoxDecoration( + // color: Colors.white, + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(10), + bottomRight: Radius.circular(10))), + child: searchCompanyLoading.value + ? Center( + child: LoadingAnimationWidget.staggeredDotsWave( + color: Constant.primaryBlue, size: 30), + ) + : Scrollbar( + trackVisibility: true, + // isAlwaysShown: true, + child: ListView.builder( + shrinkWrap: true, + padding: EdgeInsets.all(10.0), + itemCount: options.length, + itemBuilder: (BuildContext context, int index) { + final CompanyModel option = + options.elementAt(index); + + return SizedBox( + child: ListTile( + // tileColor: Colors.green, + onTap: () { + onSelected(option); + selectedCompany.value = option; + companyInput.text = + option.mCompanyName ?? ""; + ctrlAlamat.text = + option.mCompanyAddress ?? ""; + ctrlNamaTujuan.text = + option.mCompanyName ?? ""; + }, + // leading: Icon(Icons.location_on), + title: Text(option.mCompanyName.toString(), + style: + const TextStyle(color: Colors.black)), + subtitle: + Text(option.mCompanyAddress.toString()), + ), + ); + }, + ), + ), + ), + ), + ); + }, + ), + + SizedBox( + height: Constant.getActualY(context: context, y: 28), + ), + // alamat pengambilan + Container( + width: Constant.getActualX(context: context, x: 320), + height: Constant.getActualY(context: context, y: 120), + // color: Colors.green, + child: SasTextFieldArea( + controller: ctrlAlamat, + hintText: "Alamat Pengambilan", + labelText: "Alamat Pengambilan", + hasFocus: alamathasFocus.value, + focusNode: focusNodeAlamat, + ), + ), + SizedBox( + height: Constant.getActualY(context: context, y: 28), + ), + // dropdown cabang + Container( + width: Constant.getActualX(context: context, x: 320), + height: Constant.getActualY(context: context, y: 56), + child: DropdownButtonHideUnderline( + child: DropdownButton2( + isExpanded: true, + hint: Row( + children: [ + Expanded( + child: Text( + 'Select Item', + style: Constant.body1(context: context).copyWith( + fontWeight: FontWeight.w600, + color: Constant.primaryDark), + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + items: dropdownItemsCabang.value + .map((ResponseListBranchModel option) { + return DropdownMenuItem( + value: option, + child: Text( + option.branchname.toString(), + style: Constant.body1(context: context).copyWith( + color: Constant.primaryBlue, + fontWeight: FontWeight.w600), + ), + ); + }).toList(), + value: selectedDropdownCabang.value, + onChanged: (ResponseListBranchModel? newValue) { + // if (newValue) { + selectedDropdownCabang.value = newValue!; + print( + "Branch ID Selected : ${selectedDropdownCabang.value.branchid}"); + print( + "Branch Code Selected : ${selectedDropdownCabang.value.branchcode}"); + // } + }, + buttonStyleData: ButtonStyleData( + height: Constant.getActualY(context: context, y: 56), + width: Constant.getActualX(context: context, x: 320), + padding: EdgeInsets.only( + left: Constant.getActualX(context: context, x: 10), + right: Constant.getActualX(context: context, x: 10), + ), + decoration: BoxDecoration( + color: Constant.backgroundWhite, + border: Border.all(color: Constant.textBlack, width: 1), + borderRadius: BorderRadius.circular(8), + ), + elevation: 2, + ), + iconStyleData: IconStyleData( + icon: isLoading.value + ? LoadingAnimationWidget.discreteCircle( + color: Constant.primaryBlue, size: 20) + : Icon( + Icons.keyboard_arrow_down_outlined, + ), + iconSize: 24, + iconEnabledColor: Constant.textBlack, + iconDisabledColor: Colors.grey, + ), + dropdownStyleData: DropdownStyleData( + maxHeight: Constant.getActualY(context: context, y: 200), + width: Constant.getActualX(context: context, x: 320), + padding: EdgeInsets.only( + top: Constant.getActualY(context: context, y: 10), + left: Constant.getActualX(context: context, x: 10), + right: Constant.getActualX(context: context, x: 10), + bottom: Constant.getActualY(context: context, y: 10), + ), + decoration: BoxDecoration( + color: Constant.backgroundWhite, + borderRadius: BorderRadius.circular(8), + boxShadow: [ + BoxShadow( + color: Colors.grey, + blurRadius: 20.0, + spreadRadius: 2.0, + offset: Offset(0.0, 0.0), + ), + ], + ), + elevation: 8, + offset: const Offset(0, -10), + scrollbarTheme: ScrollbarThemeData( + radius: const Radius.circular(40), + thickness: MaterialStateProperty.all(6), + thumbVisibility: MaterialStateProperty.all(true), + ), + ), + menuItemStyleData: MenuItemStyleData( + height: Constant.getActualY(context: context, y: 56), + padding: EdgeInsets.only( + top: Constant.getActualY(context: context, y: 10), + left: Constant.getActualX(context: context, x: 10), + right: Constant.getActualX(context: context, x: 10), + ), + ), + ), + ), + ), + + SizedBox( + height: Constant.getActualY(context: context, y: 30), + ), + + // loading + // if (isLoading.value) ...[ + // // Center( + // // child: SizedBox( + // // width: Constant.getActualX(context: context, x: 20), + // // height: Constant.getActualY(context: context, y: 20), + // // child: CircularProgressIndicator(), + // // ), + // // ), + // const Center( + // child: CircularProgressIndicator(), + // ), + // SizedBox( + // height: Constant.getActualY(context: context, y: 20), + // ), + // ], + + // button batal & simpan + Align( + alignment: Alignment.bottomCenter, + child: Container( + width: Constant.getActualX(context: context, x: 317), + height: Constant.getActualY(context: context, y: 36), + // color: Colors.pink, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + // batal + SizedBox( + width: Constant.getActualX(context: context, x: 80), + height: Constant.getActualY(context: context, y: 36), + child: ElevatedButton( + onPressed: isLoading.value + ? null + : () { + Navigator.pop(context); + }, + style: ButtonStyle( + backgroundColor: MaterialStateColor.resolveWith( + (st) => Constant.backgroundWhite), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: BorderSide(color: Constant.blueButton), + ), + ), + shadowColor: + MaterialStateProperty.all(Color(0xffff48423d)), + elevation: MaterialStateProperty.all(4.0), + ), + child: Align( + alignment: Alignment.center, + child: Text( + 'Batal', + style: Constant.body3(context: context).copyWith( + color: Constant.blueButton, + fontWeight: FontWeight.w700, + ), + ), + ), + ), + ), + + // simpan + SizedBox( + // width: Constant.getActualX(context: context, x: 83), + height: Constant.getActualY(context: context, y: 36), + child: ElevatedButton( + onPressed: (isLoading.value) + ? null + : () { + if (ctrlNamaTujuan.text == "" || + ctrlAlamat.text == "") { + SanckbarWidget( + context, + "Nama dan Alamat Silahkan Diisi", + snackbarType.warning); + } else { + print(selectedDropdownCabang.value.branchid); + if (selectedDropdownCabang.value.branchid == "") { + SanckbarWidget(context, "Data Cabang Belum Ada", + snackbarType.warning); + } else { + ref + .read(pengambilanBahanSave.notifier) + .pengambilanBahanSave( + courierID: mCourirID, + tipeID: tipePekerjaanIDSave, + branchID: selectedDropdownCabang + .value.branchid + .toString(), + branchCode: selectedDropdownCabang + .value.branchcode + .toString(), + destination: ctrlAlamat.text, + name: ctrlNamaTujuan.text); + } + } + }, + style: ButtonStyle( + backgroundColor: MaterialStateColor.resolveWith( + (st) => Constant.blueButton), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + // side: BorderSide(color: Constant.blueButton), + ), + ), + shadowColor: + MaterialStateProperty.all(Color(0xffff48423d)), + elevation: MaterialStateProperty.all(4.0), + ), + child: Align( + alignment: Alignment.center, + child: isLoading.value + ? LoadingAnimationWidget.staggeredDotsWave( + color: Colors.white, size: 20) + : Text( + 'Simpan', + style: Constant.body3(context: context).copyWith( + color: Constant.backgroundWhite, + fontWeight: FontWeight.w700, + ), + ), + ), + ), + ), + ], + ), + ), + ), + ], + ); + } +} + +class Debouncer { + Debouncer({required this.milliseconds}); + final int milliseconds; + Timer? _timer; + run(action) { + if (_timer?.isActive ?? false) { + _timer?.cancel(); + } + _timer = Timer(Duration(milliseconds: milliseconds), action); + } +} diff --git a/lib/screen/input_pekerjaan/input_pengantaran_hasil_dokter_screen.dart b/lib/screen/input_pekerjaan/input_pengantaran_hasil_dokter_screen.dart new file mode 100644 index 0000000..886da80 --- /dev/null +++ b/lib/screen/input_pekerjaan/input_pengantaran_hasil_dokter_screen.dart @@ -0,0 +1,794 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import '/app/route.dart'; +import '/models/patient_model.dart'; +import '/widget/patient_card.dart'; + +import '../../app/constant.dart'; +import '../../models/response_search_pengantaran_hasil_model.dart'; +import '../../provider/current_scan_provider.dart'; +import '../../provider/current_user_provider.dart'; +import '../../provider/dio_provider.dart'; +import '../../repository/pengantaran_hasil_dokter_repository.dart'; +import '../../widget/custom_textfield.dart'; +import '../../widget/sas_textfield.dart'; +import '../../widget/snackbar_widget.dart'; +import 'pengantaran_hasil_dokter_loadnolab_provider.dart'; +import 'pengantaran_hasil_dokter_save_provider.dart'; + +class InputPengantaranHasilDokterScreen extends HookConsumerWidget { + const InputPengantaranHasilDokterScreen({super.key, required this.data}); + final Map data; + + @override + Widget build(BuildContext context, WidgetRef ref) { + print(data); + + final tipePekerjaanIDSave = data['tipePekerjaan']; + final deliveryIDSave = useState(""); + final mCourirID = + ref.watch(currentUserProvider)?.model.user?.mCourierID ?? "0"; + + final isForm = useState(true); + final isScan = useState(false); + + final hasilSearchData = useState>( + List.empty(growable: true)); + final focusNodeNoDokter = useFocusNode(); + final nodokterhasFocus = useState(false); + final isLoadingSearchNoDokter = useState(false); + final ctrlNoDokter = useTextEditingController(text: ""); + + final ctrlNama = useTextEditingController(text: ""); + final focusNodeNama = useFocusNode(); + final namahasFocus = useState(false); + + final ctrlAlamat = useTextEditingController(text: ""); + final focusNodeAlamat = useFocusNode(); + final alamathasFocus = useState(false); + final errorMessage = useState(""); + + final isFromScanScreenProvider = ref.read(isFromScanScreen); + + final listPatient = + useState>(List.empty(growable: true)); + + final listViewBuilderShow = useState(false); + final singleListSearchShow = useState(true); + + focusNodeNoDokter.addListener(() { + if (focusNodeNoDokter.hasFocus) { + nodokterhasFocus.value = true; + } else { + nodokterhasFocus.value = false; + } + }); + + focusNodeNama.addListener(() { + if (focusNodeNama.hasFocus) { + namahasFocus.value = true; + } else { + namahasFocus.value = false; + } + }); + + focusNodeAlamat.addListener(() { + if (focusNodeAlamat.hasFocus) { + alamathasFocus.value = true; + } else { + alamathasFocus.value = false; + } + }); + + // useEffect(() { + // isForm.value = true; + // isScan.value = false; + // }, []); + + // search + ref.listen(pengantaranHasilDokterSearch, (prev, next) { + if (next is PengantaranhasilDokterStateLoading) { + isLoadingSearchNoDokter.value = true; + } else if (next is PengantaranhasilDokterStateError) { + isLoadingSearchNoDokter.value = false; + errorMessage.value = next.message ?? ""; + Timer(const Duration(seconds: 3), () { + errorMessage.value = ""; + }); + } else if (next is PengantaranhasilDokterStateDone) { + isLoadingSearchNoDokter.value = false; + if (next.model.isNotEmpty) { + hasilSearchData.value = next.model; + + // klu lebih dari 1 length nya dibikin list view builder + // klu cm 1 seperti ini + + if (hasilSearchData.value.length > 1) { + listViewBuilderShow.value = true; + singleListSearchShow.value = false; + } else { + // deliveryid merupakan json dari id + print('Delivery ID : ${hasilSearchData.value[0].deliveryid}'); + print('Tipe ID : ${hasilSearchData.value[0].tipeid}'); + print('Selected No Lab : ${hasilSearchData.value[0].nomor}'); + print('Selected Nama : ${hasilSearchData.value[0].nama}'); + print('Selected Alamat : ${hasilSearchData.value[0].alamat}'); + + // ctrlNoDokter.text = hasilSearchData.value[0].nomor.toString(); + ctrlNoDokter.text = hasilSearchData.value[0].detail.toString(); + ctrlNama.text = hasilSearchData.value[0].nama.toString(); + ctrlAlamat.text = hasilSearchData.value[0].alamat.toString(); + deliveryIDSave.value = + hasilSearchData.value[0].deliveryid.toString(); + + if (hasilSearchData.value[0].patients!.isNotEmpty) { + // hasilSearchData.value[0].patients?.forEach((element) { + // listPatient.value.add(element); + // }); + listPatient.value = hasilSearchData.value[0].patients!; + } + } + } else { + listPatient.value = []; + hasilSearchData.value = []; + ctrlNoDokter.text = ""; + ctrlNama.text = ""; + ctrlAlamat.text = ""; + SanckbarWidget(context, "Data Tidak Ditemukan", snackbarType.warning); + } + } + }); + + // save pengantaran hasil + ref.listen(pengantaranHasilDokterSave, (prev, next) { + if (next is PengantaranhasilDokterSaveStateLoading) { + isLoadingSearchNoDokter.value = true; + } else if (next is PengantaranhasilDokterSaveStateError) { + isLoadingSearchNoDokter.value = false; + errorMessage.value = next.message ?? ""; + SanckbarWidget(context, "${next.message}", snackbarType.error); + Timer(const Duration(seconds: 3), () { + errorMessage.value = ""; + }); + } else if (next is PengantaranhasilDokterSaveStateDone) { + isLoadingSearchNoDokter.value = false; + if (next.model.status != "OK") { + SanckbarWidget(context, "${next.model.message}", snackbarType.error); + } else { + Navigator.of(context).pushNamedAndRemoveUntil( + konfirmasiRoute, + (route) => false, + ); + } + } + }); + + // check kalau pake scan + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + final barcodeScanResultValue = ref.read(barcodeScanResult); + print("barcode Scan Result: $barcodeScanResultValue"); + print("Is from scan : ${isFromScanScreen}"); + + if (isFromScanScreenProvider == true) { + if (barcodeScanResultValue != "") { + isForm.value = true; + isScan.value = false; + ref + .read(pengantaranHasilDokterSearch.notifier) + .loadListNoDokterPengantaranHasilDokter( + search: barcodeScanResultValue, + ); + } + } else { + ref.read(barcodeScanResult.notifier).update((state) => ""); + ref.read(isFromScanScreen.notifier).update((state) => false); + } + }); + }, []); + + return Container( + width: Constant.getActualX(context: context, x: 320), + height: Constant.getActualY(context: context, y: 544), + padding: EdgeInsets.only( + bottom: Constant.getActualY(context: context, y: 20), + ), + // color: Colors.pink, + child: ListView( + children: [ + // tab nolab & scanner + Container( + width: Constant.getActualX(context: context, x: 320), + height: Constant.getActualY(context: context, y: 36), + child: Row( + children: [ + // button form kiri + SizedBox( + width: Constant.getActualX(context: context, x: 156), + height: Constant.getActualY(context: context, y: 36), + child: ElevatedButton( + // onPressed: () { + // isForm.value = !isForm.value; + // isScan.value = !isScan.value; + // }, + onPressed: () { + if (isForm.value == true) { + return; + } else { + isForm.value = true; + isScan.value = false; + } + }, + style: ButtonStyle( + backgroundColor: (isForm.value == true) + ? MaterialStateColor.resolveWith( + (st) => Constant.primaryMain) + : MaterialStateColor.resolveWith( + (st) => Constant.buttonIsNotActive), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(12), + topLeft: Radius.circular(12)), + // side: BorderSide(color: Colors.red), + ), + ), + shadowColor: + MaterialStateProperty.all(Color(0xffff48423d)), + elevation: MaterialStateProperty.all(4.0), + ), + child: Align( + alignment: Alignment.center, + child: Text( + 'Form', + style: Constant.buttonLarge(context: context).copyWith( + color: (isForm.value == true) + ? Constant.backgroundWhite + : Constant.textBlack, + fontWeight: FontWeight.w700, + ), + ), + ), + ), + ), + + SizedBox( + width: Constant.getActualX(context: context, x: 8), + ), + // button scan kanan + + SizedBox( + width: Constant.getActualX(context: context, x: 156), + height: Constant.getActualY(context: context, y: 36), + child: ElevatedButton( + // onPressed: () { + // isScan.value = !isScan.value; + // isForm.value = !isForm.value; + // }, + onPressed: () { + // isScan.value = !isScan.value; + // isForm.value = !isForm.value; + + if (isScan.value == true) { + return; + } else { + isForm.value = false; + isScan.value = true; + } + }, + style: ButtonStyle( + backgroundColor: (isScan.value == true) + ? MaterialStateColor.resolveWith( + (st) => Constant.primaryMain) + : MaterialStateColor.resolveWith( + (st) => Constant.buttonIsNotActive), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.only( + bottomRight: Radius.circular(12), + topRight: Radius.circular(12), + ), + // side: BorderSide(color: Colors.red), + ), + ), + shadowColor: + MaterialStateProperty.all(Color(0xffff48423d)), + elevation: MaterialStateProperty.all(4.0), + ), + child: Align( + alignment: Alignment.center, + child: Text( + 'Scan', + style: Constant.buttonLarge(context: context).copyWith( + color: (isScan.value == true) + ? Constant.backgroundWhite + : Constant.textBlack, + fontWeight: FontWeight.w700, + ), + ), + ), + ), + ), + ], + ), + ), + + if (isForm.value == true) ...[ + // button batal & simpan jika isForm true + SizedBox( + height: Constant.getActualY(context: context, y: 20), + ), + + // loading + if (isLoadingSearchNoDokter.value) ...[ + // Center( + // child: SizedBox( + // width: Constant.getActualX(context: context, x: 20), + // height: Constant.getActualY(context: context, y: 20), + // child: CircularProgressIndicator(), + // ), + // ), + const Center( + child: CircularProgressIndicator(), + ), + ], + + SizedBox( + height: Constant.getActualY(context: context, y: 20), + ), + + // muncul jika hasil search data > 1 + if (listViewBuilderShow.value) ...[ + Container( + width: Constant.getActualX(context: context, x: 320), + // height: Constant.getActualY(context: context, y: 56), + child: SasTextFieldSearch( + controller: ctrlNoDokter, + hintText: "Nama[Kode]", + labelText: "Nama[Kode]", + focusNode: focusNodeNoDokter, + hasFocus: nodokterhasFocus.value, + isReadOnly: false, + onPressed: () { + if (ctrlNoDokter.text != "" || + ctrlNoDokter.text.isNotEmpty) { + if (ctrlNoDokter.text.length >= 3) { + ref + .read(pengantaranHasilDokterSearch.notifier) + .loadListNoDokterPengantaranHasilDokter( + search: ctrlNoDokter.text.toString(), + ); + } + } else { + SanckbarWidget( + context, + "Silahkan Masukkan Keyword Pencarian", + snackbarType.warning); + } + }, + ), + ), + SizedBox( + height: Constant.getActualY(context: context, y: 10), + ), + Container( + padding: EdgeInsets.symmetric(horizontal: 0.9, vertical: 0.5), + height: Constant.getActualY(context: context, y: 290), + child: ListView.builder( + itemCount: hasilSearchData.value.length, + itemBuilder: (context, index) { + return InkWell( + onTap: () { + // ctrlNoDokter.text = + // hasilSearchData.value[index].nomor.toString(); + ctrlNoDokter.text = + hasilSearchData.value[index].detail.toString(); + ctrlNama.text = + hasilSearchData.value[index].nama.toString(); + ctrlAlamat.text = + hasilSearchData.value[index].alamat.toString(); + deliveryIDSave.value = hasilSearchData + .value[index].deliveryid + .toString(); + + if (hasilSearchData + .value[index].patients!.isNotEmpty) { + // hasilSearchData.value[index].patients?.forEach((element) { + // listPatient.value.add(element); + // }); + listPatient.value = + hasilSearchData.value[index].patients!; + } + listViewBuilderShow.value = false; + singleListSearchShow.value = true; + }, + child: Padding( + padding: const EdgeInsets.only(bottom: 10), + child: Container( + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX( + context: context, x: 24), + vertical: Constant.getActualY( + context: context, y: 20)), + margin: EdgeInsets.symmetric( + vertical: + Constant.getActualY(context: context, y: 4), + horizontal: Constant.getActualX( + context: context, x: 1)), + // height: Constant.getActualY(context: context, y: 76), + decoration: BoxDecoration( + color: Colors.white, + border: + Border.all(color: Colors.grey, width: 0.1), + boxShadow: [ + BoxShadow( + color: Colors.grey.shade300, + offset: const Offset(0.0, 1), //(x,y) + blurRadius: 2, + ), + ], + borderRadius: BorderRadius.circular(12)), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Text( + "Nomor", + style: Constant.caption1( + context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ), + Expanded( + child: Text( + hasilSearchData.value[index].nomor + .toString(), + // 's', + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: Constant.caption1( + context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ) + ], + ), + SizedBox( + height: Constant.getActualY( + context: context, y: 4), + ), + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Text( + "Nama", + style: Constant.caption1( + context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ), + Expanded( + child: Text( + hasilSearchData.value[index].nama + .toString(), + // 's', + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: Constant.caption1( + context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ) + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Text( + "Alamat Pengantaran", + style: Constant.caption1( + context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ), + Expanded( + child: Text( + hasilSearchData.value[index].alamat + .toString(), + // 's', + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: Constant.caption1( + context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ) + ], + ), + ], + ), + ), + ), + ); + }), + ), + ], + + // single list show + if (singleListSearchShow.value) ...[ + // no lab autocomplete + Container( + width: Constant.getActualX(context: context, x: 320), + // height: Constant.getActualY(context: context, y: 56), + child: SasTextFieldSearch( + controller: ctrlNoDokter, + hintText: "Nama[Kode]", + labelText: "Nama[Kode]", + focusNode: focusNodeNoDokter, + hasFocus: nodokterhasFocus.value, + isReadOnly: false, + onPressed: () { + if (ctrlNoDokter.text != "" || + ctrlNoDokter.text.isNotEmpty) { + if (ctrlNoDokter.text.length >= 3) { + ref + .read(pengantaranHasilDokterSearch.notifier) + .loadListNoDokterPengantaranHasilDokter( + search: ctrlNoDokter.text.toString(), + ); + } + } else { + SanckbarWidget( + context, + "Silahkan Masukkan Keyword Pencarian", + snackbarType.warning); + } + }, + ), + ), + + SizedBox( + height: Constant.getActualY(context: context, y: 20), + ), + + // // nama + // Container( + // width: Constant.getActualX(context: context, x: 320), + // height: Constant.getActualY(context: context, y: 56), + // child: SasTextField( + // hintText: "Nama", + // labelText: "Nama", + // focusNode: focusNodeNama, + // hasFocus: namahasFocus.value, + // controller: ctrlNama, + // isReadOnly: true, + // ), + // ), + // SizedBox( + // height: Constant.getActualY(context: context, y: 20), + // ), + // alamat + Container( + width: Constant.getActualX(context: context, x: 320), + height: Constant.getActualY(context: context, y: 120), + // color: Colors.green, + child: SasTextFieldArea( + hintText: "Alamat Pengantaran", + labelText: "Alamat Pengantaran", + focusNode: focusNodeAlamat, + hasFocus: alamathasFocus.value, + controller: ctrlAlamat, + isReadOnly: true, + ), + ), + + SizedBox( + height: Constant.getActualY(context: context, y: 10), + ), + + if (listPatient.value.isNotEmpty) ...[ + for (var i = 0; i < listPatient.value.length; i++) ...[ + Padding( + padding: EdgeInsets.only( + bottom: Constant.getActualY(context: context, y: 10)), + child: PatientCard( + name: listPatient.value[i].pasien.toString(), + noLab: + listPatient.value[i].tOrderHeaderLabNumber.toString(), + ), + ), + ], + ] else ...[ + SizedBox.shrink(), + SizedBox( + height: Constant.getActualY(context: context, y: 40), + ) + ], + ], + + // SizedBox( + // height: Constant.getActualY(context: context, y: 100), + // ), + + Padding( + padding: EdgeInsets.only( + top: Constant.getActualY(context: context, y: 20), + bottom: Constant.getActualY(context: context, y: 20), + ), + child: Align( + alignment: Alignment.bottomCenter, + child: Container( + // width: Constant.getActualX(context: context, x: 317), + // height: Constant.getActualY(context: context, y: 36), + // padding: EdgeInsets.symmetric( + // horizontal: Constant.getActualX(context: context, x: 10)), + height: Constant.getActualY(context: context, y: 36), + // color: Colors.pink, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + // batal + ElevatedButton( + onPressed: () { + Navigator.pop(context); + }, + style: ButtonStyle( + backgroundColor: MaterialStateColor.resolveWith( + (st) => Constant.backgroundWhite), + shape: + MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: BorderSide(color: Constant.blueButton), + ), + ), + shadowColor: + MaterialStateProperty.all(Color(0xffff48423d)), + elevation: MaterialStateProperty.all(4.0), + ), + child: Align( + alignment: Alignment.center, + child: Text( + 'Batal', + style: Constant.body3(context: context).copyWith( + color: Constant.blueButton, + fontWeight: FontWeight.w700, + ), + ), + ), + ), + + // simpan + ElevatedButton( + onPressed: (isLoadingSearchNoDokter.value) + ? null + : () { + print("kurir id : $mCourirID"); + print("tipe id : $tipePekerjaanIDSave"); + print("delivery id : ${deliveryIDSave.value}"); + + if (deliveryIDSave.value == "") { + SanckbarWidget( + context, + "Silahkan Cari No Dokter", + snackbarType.warning); + } else { + ref + .read(pengantaranHasilDokterSave.notifier) + .pengantaranHasilDokterSave( + courierID: mCourirID, + tipeID: tipePekerjaanIDSave, + deliveryID: deliveryIDSave.value, + ); + } + }, + style: ButtonStyle( + backgroundColor: MaterialStateColor.resolveWith( + (st) => Constant.blueButton, + ), + shape: + MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + // side: BorderSide(color: Constant.blueButton), + ), + ), + shadowColor: + MaterialStateProperty.all(Color(0xffff48423d)), + elevation: MaterialStateProperty.all(4.0), + ), + child: Align( + alignment: Alignment.center, + child: Text( + 'Simpan', + style: Constant.body3(context: context).copyWith( + color: Constant.backgroundWhite, + fontWeight: FontWeight.w700, + ), + ), + ), + ), + ], + ), + ), + ), + ), + ] else ...[ + // button scan barcode + SizedBox( + height: Constant.getActualY(context: context, y: 445), + ), + Container( + width: Constant.getActualX(context: context, x: 317), + height: Constant.getActualY(context: context, y: 36), + // color: Colors.pink, + child: SizedBox( + width: Constant.getActualX(context: context, x: 83), + height: Constant.getActualY(context: context, y: 36), + child: ElevatedButton( + onPressed: () { + FocusManager.instance.primaryFocus?.unfocus(); + ref.read(isFromScanScreen.notifier).update((state) => true); + Navigator.of(context).pushNamedAndRemoveUntil( + inputScanRoute, (route) => false, + arguments: InputScanPekerjaanProp(0, 2)); + }, + style: ButtonStyle( + backgroundColor: MaterialStateColor.resolveWith( + (st) => Constant.blueButton), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + // side: BorderSide(color: Constant.blueButton), + ), + ), + shadowColor: MaterialStateProperty.all(Color(0xffff48423d)), + elevation: MaterialStateProperty.all(4.0), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.document_scanner), + SizedBox( + width: Constant.getActualX(context: context, x: 10), + ), + Text( + 'Scan Barcode', + style: Constant.body3(context: context).copyWith( + color: Constant.backgroundWhite, + fontWeight: FontWeight.w700, + ), + ), + ], + ), + ), + ), + ), + ], + ], + ), + ); + } +} diff --git a/lib/screen/input_pekerjaan/input_pengantaran_hasil_instansi_screen.dart b/lib/screen/input_pekerjaan/input_pengantaran_hasil_instansi_screen.dart new file mode 100644 index 0000000..2d665ec --- /dev/null +++ b/lib/screen/input_pekerjaan/input_pengantaran_hasil_instansi_screen.dart @@ -0,0 +1,811 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import '/app/route.dart'; + +import '../../app/constant.dart'; +import '../../models/patient_model.dart'; +import '../../models/response_search_pengantaran_hasil_model.dart'; +import '../../provider/current_scan_provider.dart'; +import '../../provider/current_user_provider.dart'; +import '../../provider/dio_provider.dart'; +import '../../repository/pengantaran_hasil_instansi_repository.dart'; +import '../../widget/custom_textfield.dart'; +import '../../widget/patient_card.dart'; +import '../../widget/sas_textfield.dart'; +import '../../widget/snackbar_widget.dart'; +import 'pengantaran_hasil_instansi_loadnosuratjalan_provider.dart'; +import 'pengantaran_hasil_instansi_save_provider.dart'; + +class InputPengantaranHasilInstansiScreen extends HookConsumerWidget { + // const InputPengantaranHasilInstansiScreen({ + // super.key, + // }); + + const InputPengantaranHasilInstansiScreen({super.key, required this.data}); + final Map data; + + @override + Widget build(BuildContext context, WidgetRef ref) { + print(data); + + final tipePekerjaanIDSave = data['tipePekerjaan']; + final deliveryIDSave = useState(""); + final mCourirID = + ref.watch(currentUserProvider)?.model.user?.mCourierID ?? "0"; + + final isForm = useState(true); + final isScan = useState(false); + + final dummySuratJalan = + useState>(List.empty(growable: true)); + + final focusNodeCompany = useFocusNode(); + final companyhasFocus = useState(false); + final isLoadingSearchSuratJalan = useState(false); + final ctrlNoCompany = useTextEditingController(text: ""); + + final dummyInstansiList = + useState>(List.empty(growable: true)); + + final listPatient = + useState>(List.empty(growable: true)); + + final hasilSearchData = useState>( + List.empty(growable: true)); + + final ctrlNama = useTextEditingController(text: ""); + final focusNodeNama = useFocusNode(); + final namahasFocus = useState(false); + + final ctrlAlamat = useTextEditingController(text: ""); + final focusNodeAlamat = useFocusNode(); + final alamathasFocus = useState(false); + final errorMessage = useState(""); + + final isFromScanScreenProvider = ref.read(isFromScanScreen); + + final listViewBuilderShow = useState(false); + final singleListSearchShow = useState(true); + + focusNodeCompany.addListener(() { + if (focusNodeCompany.hasFocus) { + companyhasFocus.value = true; + } else { + companyhasFocus.value = false; + } + }); + + focusNodeNama.addListener(() { + if (focusNodeNama.hasFocus) { + namahasFocus.value = true; + } else { + namahasFocus.value = false; + } + }); + + focusNodeAlamat.addListener(() { + if (focusNodeAlamat.hasFocus) { + alamathasFocus.value = true; + } else { + alamathasFocus.value = false; + } + }); + + // useEffect(() { + // isForm.value = true; + // isScan.value = false; + // }, []); + + // search + ref.listen(pengantaranHasilInstansi, (prev, next) { + if (next is PengantaranhasilInstansiStateLoading) { + isLoadingSearchSuratJalan.value = true; + } else if (next is PengantaranhasilInstansiStateError) { + isLoadingSearchSuratJalan.value = false; + errorMessage.value = next.message ?? ""; + Timer(const Duration(seconds: 3), () { + errorMessage.value = ""; + }); + } else if (next is PengantaranhasilInstansiStateDone) { + isLoadingSearchSuratJalan.value = false; + // listSearch.value = next.model; + + if (next.model.isNotEmpty) { + hasilSearchData.value = next.model; + + // klu lebih dari 1 length nya dibikin list view builder + // klu cm 1 seperti ini + + if (hasilSearchData.value.length > 1) { + listViewBuilderShow.value = true; + singleListSearchShow.value = false; + } else { + // hanya 1 + listViewBuilderShow.value = false; + singleListSearchShow.value = true; + // deliveryid merupakan json dari id + print('Delivery ID : ${hasilSearchData.value[0].deliveryid}'); + print('Tipe ID : ${hasilSearchData.value[0].tipeid}'); + print('Selected No Instansi : ${hasilSearchData.value[0].nomor}'); + print('Selected Detail : ${hasilSearchData.value[0].detail}'); + print('Selected Nama : ${hasilSearchData.value[0].nama}'); + print('Selected Alamat : ${hasilSearchData.value[0].alamat}'); + + // ctrlNoCompany.text = hasilSearchData.value[0].nomor.toString(); + ctrlNoCompany.text = hasilSearchData.value[0].detail.toString(); + ctrlNama.text = hasilSearchData.value[0].nama.toString(); + ctrlAlamat.text = hasilSearchData.value[0].alamat.toString(); + deliveryIDSave.value = + hasilSearchData.value[0].deliveryid.toString(); + + if (hasilSearchData.value[0].patients!.isNotEmpty) { + // hasilSearchData.value[0].patients?.forEach((element) { + // listPatient.value.add(element); + // }); + listPatient.value = hasilSearchData.value[0].patients!; + } + } + } else { + listPatient.value = []; + hasilSearchData.value = []; + ctrlNoCompany.text = ""; + ctrlNama.text = ""; + ctrlAlamat.text = ""; + ref.read(barcodeScanResult.notifier).update((state) => ""); + ref.read(isFromScanScreen.notifier).update((state) => false); + SanckbarWidget(context, "Data Tidak Ditemukan", snackbarType.warning); + } + } + }); + + // save pengantaran hasil + ref.listen(pengantaranHasilCompanySave, (prev, next) { + if (next is PengantaranhasilCompanySaveStateLoading) { + isLoadingSearchSuratJalan.value = true; + } else if (next is PengantaranhasilCompanySaveStateError) { + isLoadingSearchSuratJalan.value = false; + errorMessage.value = next.message ?? ""; + SanckbarWidget(context, "${next.message}", snackbarType.error); + Timer(const Duration(seconds: 3), () { + errorMessage.value = ""; + }); + } else if (next is PengantaranhasilCompanySaveStateDone) { + isLoadingSearchSuratJalan.value = false; + if (next.model.status != "OK") { + SanckbarWidget(context, "${next.model.message}", snackbarType.error); + } else { + Navigator.of(context).pushNamedAndRemoveUntil( + konfirmasiRoute, + (route) => false, + ); + } + } + }); + + // check kalau pake scan + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + final barcodeScanResultValue = ref.read(barcodeScanResult); + print("barcode Scan Result: $barcodeScanResultValue"); + print("Is from scan : ${isFromScanScreen}"); + + if (isFromScanScreenProvider == true) { + if (barcodeScanResultValue != "") { + isForm.value = true; + isScan.value = false; + ref + .read(pengantaranHasilInstansi.notifier) + .loadListNoSuratJalanPengantaranHasilInstansi( + search: barcodeScanResultValue, + ); + } + } else { + ref.read(barcodeScanResult.notifier).update((state) => ""); + ref.read(isFromScanScreen.notifier).update((state) => false); + } + }); + }, []); + + return Container( + width: Constant.getActualX(context: context, x: 320), + height: Constant.getActualY(context: context, y: 544), + // color: Colors.pink, + child: ListView( + children: [ + // tab nolab & scanner + Container( + width: Constant.getActualX(context: context, x: 320), + height: Constant.getActualY(context: context, y: 36), + child: Row( + children: [ + // button form kiri + SizedBox( + width: Constant.getActualX(context: context, x: 156), + height: Constant.getActualY(context: context, y: 36), + child: ElevatedButton( + // onPressed: () { + // isForm.value = !isForm.value; + // isScan.value = !isScan.value; + // }, + onPressed: () { + if (isForm.value == true) { + return; + } else { + isForm.value = true; + isScan.value = false; + } + }, + style: ButtonStyle( + backgroundColor: (isForm.value == true) + ? MaterialStateColor.resolveWith( + (st) => Constant.primaryMain) + : MaterialStateColor.resolveWith( + (st) => Constant.buttonIsNotActive), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(12), + topLeft: Radius.circular(12)), + // side: BorderSide(color: Colors.red), + ), + ), + shadowColor: + MaterialStateProperty.all(Color(0xffff48423d)), + elevation: MaterialStateProperty.all(4.0), + ), + child: Align( + alignment: Alignment.center, + child: Text( + 'Form', + style: Constant.buttonLarge(context: context).copyWith( + color: (isForm.value == true) + ? Constant.backgroundWhite + : Constant.textBlack, + fontWeight: FontWeight.w700, + ), + ), + ), + ), + ), + + SizedBox( + width: Constant.getActualX(context: context, x: 8), + ), + // button scan kanan + + SizedBox( + width: Constant.getActualX(context: context, x: 156), + height: Constant.getActualY(context: context, y: 36), + child: ElevatedButton( + // onPressed: () { + // isScan.value = !isScan.value; + // isForm.value = !isForm.value; + // }, + onPressed: () { + if (isScan.value == true) { + return; + } else { + isForm.value = false; + isScan.value = true; + } + }, + style: ButtonStyle( + backgroundColor: (isScan.value == true) + ? MaterialStateColor.resolveWith( + (st) => Constant.primaryMain) + : MaterialStateColor.resolveWith( + (st) => Constant.buttonIsNotActive), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.only( + bottomRight: Radius.circular(12), + topRight: Radius.circular(12), + ), + // side: BorderSide(color: Colors.red), + ), + ), + shadowColor: + MaterialStateProperty.all(Color(0xffff48423d)), + elevation: MaterialStateProperty.all(4.0), + ), + child: Align( + alignment: Alignment.center, + child: Text( + 'Scan', + style: Constant.buttonLarge(context: context).copyWith( + color: (isScan.value == true) + ? Constant.backgroundWhite + : Constant.textBlack, + fontWeight: FontWeight.w700, + ), + ), + ), + ), + ), + ], + ), + ), + + if (isForm.value == true) ...[ + // button batal & simpan jika isForm true + SizedBox( + height: Constant.getActualY(context: context, y: 20), + ), + + // loading + if (isLoadingSearchSuratJalan.value) ...[ + // Center( + // child: SizedBox( + // width: Constant.getActualX(context: context, x: 20), + // height: Constant.getActualY(context: context, y: 20), + // child: CircularProgressIndicator(), + // ), + // ), + const Center( + child: CircularProgressIndicator(), + ), + ], + + SizedBox( + height: Constant.getActualY(context: context, y: 20), + ), + + // muncul jika hasil search data > 1 + if (listViewBuilderShow.value) ...[ + Container( + width: Constant.getActualX(context: context, x: 320), + // height: Constant.getActualY(context: context, y: 56), + child: SasTextFieldSearch( + controller: ctrlNoCompany, + // hintText: "No Company", + // labelText: "No Company", + hintText: "Nama[Kode]", + labelText: "Nama[Kode]", + focusNode: focusNodeCompany, + hasFocus: companyhasFocus.value, + isReadOnly: false, + onPressed: () { + if (ctrlNoCompany.text != "" || + ctrlNoCompany.text.isNotEmpty) { + if (ctrlNoCompany.text.length >= 3) { + ref + .read(pengantaranHasilInstansi.notifier) + .loadListNoSuratJalanPengantaranHasilInstansi( + search: ctrlNoCompany.text.toString(), + ); + } + } else { + SanckbarWidget( + context, + "Silahkan Masukkan Keyword Pencarian", + snackbarType.warning); + } + }, + ), + ), + SizedBox( + height: Constant.getActualY(context: context, y: 10), + ), + Container( + padding: EdgeInsets.symmetric(horizontal: 0.9, vertical: 0.5), + height: Constant.getActualY(context: context, y: 290), + child: ListView.builder( + itemCount: hasilSearchData.value.length, + itemBuilder: (context, index) { + return InkWell( + onTap: () { + // ctrlNoCompany.text = + // hasilSearchData.value[index].nomor.toString(); + ctrlNoCompany.text = + hasilSearchData.value[index].detail.toString(); + ctrlNama.text = + hasilSearchData.value[index].nama.toString(); + ctrlAlamat.text = + hasilSearchData.value[index].alamat.toString(); + + if (hasilSearchData + .value[index].patients!.isNotEmpty) { + // hasilSearchData.value[index].patients?.forEach((element) { + // listPatient.value.add(element); + // }); + listPatient.value = + hasilSearchData.value[index].patients!; + } + deliveryIDSave.value = + hasilSearchData.value[index].deliveryid.toString(); + listViewBuilderShow.value = false; + singleListSearchShow.value = true; + }, + child: Padding( + padding: const EdgeInsets.only(bottom: 10), + child: Container( + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX( + context: context, x: 24), + vertical: Constant.getActualY( + context: context, y: 20)), + margin: EdgeInsets.symmetric( + vertical: + Constant.getActualY(context: context, y: 4), + horizontal: Constant.getActualX( + context: context, x: 1)), + // height: Constant.getActualY(context: context, y: 76), + decoration: BoxDecoration( + color: Colors.white, + border: + Border.all(color: Colors.grey, width: 0.1), + boxShadow: [ + BoxShadow( + color: Colors.grey.shade300, + offset: const Offset(0.0, 1), //(x,y) + blurRadius: 2, + ), + ], + borderRadius: BorderRadius.circular(12)), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Text( + "Nomor", + style: Constant.caption1( + context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ), + Expanded( + child: Text( + hasilSearchData.value[index].nomor + .toString(), + // 's', + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: Constant.caption1( + context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ) + ], + ), + SizedBox( + height: Constant.getActualY( + context: context, y: 4), + ), + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Text( + "Nama", + style: Constant.caption1( + context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ), + Expanded( + child: Text( + hasilSearchData.value[index].nama + .toString(), + // 's', + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: Constant.caption1( + context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ) + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Text( + "Alamat Pengantaran", + style: Constant.caption1( + context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ), + Expanded( + child: Text( + hasilSearchData.value[index].alamat + .toString(), + // 's', + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: Constant.caption1( + context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ) + ], + ), + ], + ), + ), + ), + ); + }), + ) + ], + + // single list show + if (singleListSearchShow.value) ...[ + // no lab autocomplete + Container( + width: Constant.getActualX(context: context, x: 320), + // height: Constant.getActualY(context: context, y: 56), + child: SasTextFieldSearch( + controller: ctrlNoCompany, + hintText: "Nama[Kode]", + labelText: "Nama[Kode]", + // labelText: "No Company", + focusNode: focusNodeCompany, + hasFocus: companyhasFocus.value, + isReadOnly: false, + onPressed: () { + if (ctrlNoCompany.text != "" || + ctrlNoCompany.text.isNotEmpty) { + if (ctrlNoCompany.text.length >= 3) { + ref + .read(pengantaranHasilInstansi.notifier) + .loadListNoSuratJalanPengantaranHasilInstansi( + search: ctrlNoCompany.text.toString(), + ); + } + } else { + SanckbarWidget( + context, + "Silahkan Masukkan Keyword Pencarian", + snackbarType.warning); + } + }, + ), + ), + + // SizedBox( + // height: Constant.getActualY(context: context, y: 20), + // ), + // // nama + // Container( + // width: Constant.getActualX(context: context, x: 320), + // height: Constant.getActualY(context: context, y: 56), + // child: SasTextField( + // hintText: "Nama", + // labelText: "Nama", + // focusNode: focusNodeNama, + // hasFocus: namahasFocus.value, + // controller: ctrlNama, + // isReadOnly: true, + // ), + // ), + + SizedBox( + height: Constant.getActualY(context: context, y: 20), + ), + // alamat + Container( + width: Constant.getActualX(context: context, x: 320), + height: Constant.getActualY(context: context, y: 120), + // color: Colors.green, + child: SasTextFieldArea( + hintText: "Alamat Pengantaran", + labelText: "Alamat Pengantaran", + focusNode: focusNodeAlamat, + hasFocus: alamathasFocus.value, + controller: ctrlAlamat, + isReadOnly: true, + ), + ), + + SizedBox( + height: Constant.getActualY(context: context, y: 10), + ), + + if (listPatient.value.isNotEmpty) ...[ + for (var i = 0; i < listPatient.value.length; i++) ...[ + Padding( + padding: EdgeInsets.only( + bottom: Constant.getActualY(context: context, y: 10), + ), + child: PatientCard( + name: listPatient.value[i].pasien.toString(), + noLab: + listPatient.value[i].tOrderHeaderLabNumber.toString(), + ), + ), + ], + ] else ...[ + SizedBox.shrink(), + SizedBox( + height: Constant.getActualY(context: context, y: 40), + ) + ], + ], + + // SizedBox( + // height: Constant.getActualY(context: context, y: 156), + // ), + + Padding( + padding: EdgeInsets.only( + top: Constant.getActualY(context: context, y: 20), + bottom: Constant.getActualY(context: context, y: 20), + ), + child: Align( + alignment: Alignment.bottomCenter, + child: Container( + // width: Constant.getActualX(context: context, x: 317), + // height: Constant.getActualY(context: context, y: 36), + // padding: EdgeInsets.symmetric( + // horizontal: Constant.getActualX(context: context, x: 10), + // ), + height: Constant.getActualY(context: context, y: 36), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + // batal + ElevatedButton( + onPressed: () { + Navigator.pop(context); + }, + style: ButtonStyle( + backgroundColor: MaterialStateColor.resolveWith( + (st) => Constant.backgroundWhite), + shape: + MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: BorderSide(color: Constant.blueButton), + ), + ), + shadowColor: + MaterialStateProperty.all(Color(0xffff48423d)), + elevation: MaterialStateProperty.all(4.0), + ), + child: Align( + alignment: Alignment.center, + child: Text( + 'Batal', + style: Constant.body3(context: context).copyWith( + color: Constant.blueButton, + fontWeight: FontWeight.w700, + ), + ), + ), + ), + + // simpan + ElevatedButton( + onPressed: (isLoadingSearchSuratJalan.value) + ? null + : () { + print("kurir id : $mCourirID"); + print("tipe id : $tipePekerjaanIDSave"); + print("delivery id : ${deliveryIDSave.value}"); + + if (deliveryIDSave.value == "") { + SanckbarWidget( + context, + "Silahkan Cari No Company", + snackbarType.warning); + } else { + ref + .read( + pengantaranHasilCompanySave.notifier) + .pengantaranHasilCompanySave( + courierID: mCourirID, + tipeID: tipePekerjaanIDSave, + deliveryID: deliveryIDSave.value, + ); + } + }, + style: ButtonStyle( + backgroundColor: MaterialStateColor.resolveWith( + (st) => Constant.blueButton, + ), + shape: + MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + // side: BorderSide(color: Constant.blueButton), + ), + ), + shadowColor: + MaterialStateProperty.all(Color(0xffff48423d)), + elevation: MaterialStateProperty.all(4.0), + ), + child: Align( + alignment: Alignment.center, + child: Text( + 'Simpan', + style: Constant.body3(context: context).copyWith( + color: Constant.backgroundWhite, + fontWeight: FontWeight.w700, + ), + ), + ), + ), + ], + ), + ), + ), + ), + ] else ...[ + // button scan barcode + SizedBox( + height: Constant.getActualY(context: context, y: 445), + ), + Container( + width: Constant.getActualX(context: context, x: 317), + height: Constant.getActualY(context: context, y: 36), + // color: Colors.pink, + child: SizedBox( + width: Constant.getActualX(context: context, x: 83), + height: Constant.getActualY(context: context, y: 36), + child: ElevatedButton( + onPressed: () { + FocusManager.instance.primaryFocus?.unfocus(); + ref.read(isFromScanScreen.notifier).update((state) => true); + Navigator.of(context).pushNamedAndRemoveUntil( + inputScanRoute, (route) => false, + arguments: InputScanPekerjaanProp(0, 1)); + }, + style: ButtonStyle( + backgroundColor: MaterialStateColor.resolveWith( + (st) => Constant.blueButton), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + // side: BorderSide(color: Constant.blueButton), + ), + ), + shadowColor: MaterialStateProperty.all(Color(0xffff48423d)), + elevation: MaterialStateProperty.all(4.0), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.document_scanner), + SizedBox( + width: Constant.getActualX(context: context, x: 10), + ), + Text( + 'Scan Barcode', + style: Constant.body3(context: context).copyWith( + color: Constant.backgroundWhite, + fontWeight: FontWeight.w700, + ), + ), + ], + ), + ), + ), + ), + ], + ], + ), + ); + } +} diff --git a/lib/screen/input_pekerjaan/input_pengantaran_hasil_pasien_screen.dart b/lib/screen/input_pekerjaan/input_pengantaran_hasil_pasien_screen.dart new file mode 100644 index 0000000..38aec7a --- /dev/null +++ b/lib/screen/input_pekerjaan/input_pengantaran_hasil_pasien_screen.dart @@ -0,0 +1,545 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import '/app/route.dart'; +import '/screen/input_pekerjaan/pengantaran_hasil_pasien_loadnolab_provider.dart'; +import '/widget/sas_textfield.dart'; + +import '../../app/constant.dart'; +import '../../models/response_search_pengantaran_hasil_model.dart'; +import '../../provider/current_scan_provider.dart'; +import '../../provider/current_user_provider.dart'; +import '../../provider/dio_provider.dart'; +import '../../repository/pengantaran_hasil_pasien_repository.dart'; +import '../../widget/custom_textfield.dart'; +import '../../widget/snackbar_widget.dart'; +import 'pengantaran_hasil_pasien_save_provider.dart'; + +class InputPengantaranHasilPasienScreen extends HookConsumerWidget { + // const InputPengantaranHasilPasienScreen({ + // super.key, + // }); + + const InputPengantaranHasilPasienScreen({super.key, required this.data}); + final Map data; + + @override + Widget build(BuildContext context, WidgetRef ref) { + print(data); + final isForm = useState(true); + final isScan = useState(false); + + final mCourirID = + ref.watch(currentUserProvider)?.model.user?.mCourierID ?? "0"; + + final isFromScanScreenProvider = ref.read(isFromScanScreen); + + final tipePekerjaanIDSave = data['tipePekerjaan']; + final deliveryIDSave = useState(""); + + final hasilSearchData = useState>( + List.empty(growable: true)); + + final isLoadingSearchNoLab = useState(false); + final focusNodeNoLab = useFocusNode(); + final nolabhasFocus = useState(false); + final ctrlNoLab = useTextEditingController(text: ""); + + final ctrlNama = useTextEditingController(text: ""); + final focusNodeNama = useFocusNode(); + final namahasFocus = useState(false); + + final ctrlAlamat = useTextEditingController(text: ""); + final focusNodeAlamat = useFocusNode(); + final alamathasFocus = useState(false); + final errorMessage = useState(""); + + focusNodeNoLab.addListener(() { + if (focusNodeNoLab.hasFocus) { + nolabhasFocus.value = true; + } else { + nolabhasFocus.value = false; + } + }); + + focusNodeNama.addListener(() { + if (focusNodeNama.hasFocus) { + namahasFocus.value = true; + } else { + namahasFocus.value = false; + } + }); + + focusNodeAlamat.addListener(() { + if (focusNodeAlamat.hasFocus) { + alamathasFocus.value = true; + } else { + alamathasFocus.value = false; + } + }); + + // useEffect(() { + // isForm.value = true; + // isScan.value = false; + // }, []); + + // search no lab + ref.listen(pengantaranHasilPasienSearchNoLab, (prev, next) { + if (next is PengantaranhasilPasienStateLoading) { + isLoadingSearchNoLab.value = true; + } else if (next is PengantaranhasilPasienStateError) { + isLoadingSearchNoLab.value = false; + errorMessage.value = next.message ?? ""; + SanckbarWidget(context, "${next.message}", snackbarType.error); + Timer(const Duration(seconds: 3), () { + errorMessage.value = ""; + }); + } else if (next is PengantaranhasilPasienStateDone) { + isLoadingSearchNoLab.value = false; + if (next.model.isNotEmpty) { + hasilSearchData.value = next.model; + // deliveryid merupakan json dari id + print('Delivery ID : ${hasilSearchData.value[0].deliveryid}'); + print('Tipe ID : ${hasilSearchData.value[0].tipeid}'); + print('Selected No Lab : ${hasilSearchData.value[0].nomor}'); + print('Selected Nama : ${hasilSearchData.value[0].nama}'); + print('Selected Alamat : ${hasilSearchData.value[0].alamat}'); + + ctrlNoLab.text = hasilSearchData.value[0].nomor.toString(); + ctrlNama.text = hasilSearchData.value[0].nama.toString(); + ctrlAlamat.text = hasilSearchData.value[0].alamat.toString(); + deliveryIDSave.value = hasilSearchData.value[0].deliveryid.toString(); + } else { + hasilSearchData.value = []; + ctrlNoLab.text = ""; + ctrlNama.text = ""; + ctrlAlamat.text = ""; + SanckbarWidget(context, "Data Tidak Ditemukan", snackbarType.warning); + } + } + }); + + // save pengantaran hasil + ref.listen(pengantaranHasilPasienSave, (prev, next) { + if (next is PengantaranhasilPasienSaveStateLoading) { + isLoadingSearchNoLab.value = true; + } else if (next is PengantaranhasilPasienSaveStateError) { + isLoadingSearchNoLab.value = false; + errorMessage.value = next.message ?? ""; + SanckbarWidget(context, "${next.message}", snackbarType.error); + Timer(const Duration(seconds: 3), () { + errorMessage.value = ""; + }); + } else if (next is PengantaranhasilPasienSaveStateDone) { + isLoadingSearchNoLab.value = false; + ref.read(barcodeScanResult.notifier).update((state) => ""); + if (next.model.status != "OK") { + SanckbarWidget(context, "${next.model.message}", snackbarType.error); + } else { + Navigator.of(context).pushNamedAndRemoveUntil( + konfirmasiRoute, + (route) => false, + ); + } + } + }); + + // check kalau pake scan + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + final barcodeScanResultValue = ref.read(barcodeScanResult); + print("barcode Scan Result: $barcodeScanResultValue"); + print("Is from scan : ${isFromScanScreen}"); + + if (isFromScanScreenProvider == true) { + if (barcodeScanResultValue != "") { + isForm.value = true; + isScan.value = false; + ref + .read(pengantaranHasilPasienSearchNoLab.notifier) + .loadListNoLabPengantaranHasilPasien( + search: barcodeScanResultValue, + ); + } + } else { + ref.read(barcodeScanResult.notifier).update((state) => ""); + ref.read(isFromScanScreen.notifier).update((state) => false); + } + }); + }, []); + + return Container( + width: Constant.getActualX(context: context, x: 320), + height: Constant.getActualY(context: context, y: 544), + // color: Colors.pink, + child: ListView( + children: [ + // tab nolab & scanner + Container( + width: Constant.getActualX(context: context, x: 320), + height: Constant.getActualY(context: context, y: 36), + child: Row( + children: [ + // button form kiri + SizedBox( + width: Constant.getActualX(context: context, x: 156), + height: Constant.getActualY(context: context, y: 36), + child: ElevatedButton( + onPressed: () { + if (isForm.value == true) { + return; + } else { + isForm.value = true; + isScan.value = false; + } + // isForm.value = !isForm.value; + // isScan.value = !isScan.value; + }, + // onPressed: null, + style: ButtonStyle( + backgroundColor: (isForm.value == true) + ? MaterialStateColor.resolveWith( + (st) => Constant.primaryMain) + : MaterialStateColor.resolveWith( + (st) => Constant.buttonIsNotActive), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(12), + topLeft: Radius.circular(12)), + // side: BorderSide(color: Colors.red), + ), + ), + shadowColor: + MaterialStateProperty.all(Color(0xffff48423d)), + elevation: MaterialStateProperty.all(4.0), + ), + child: Align( + alignment: Alignment.center, + child: Text( + 'Form', + style: Constant.buttonLarge(context: context).copyWith( + color: (isForm.value == true) + ? Constant.backgroundWhite + : Constant.textBlack, + fontWeight: FontWeight.w700, + ), + ), + ), + ), + ), + + SizedBox( + width: Constant.getActualX(context: context, x: 8), + ), + // button scan kanan + + SizedBox( + width: Constant.getActualX(context: context, x: 156), + height: Constant.getActualY(context: context, y: 36), + child: ElevatedButton( + onPressed: () { + // isScan.value = !isScan.value; + // isForm.value = !isForm.value; + + if (isScan.value == true) { + return; + } else { + isForm.value = false; + isScan.value = true; + } + }, + // onPressed: null, + style: ButtonStyle( + backgroundColor: (isScan.value == true) + ? MaterialStateColor.resolveWith( + (st) => Constant.primaryMain) + : MaterialStateColor.resolveWith( + (st) => Constant.buttonIsNotActive), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.only( + bottomRight: Radius.circular(12), + topRight: Radius.circular(12), + ), + // side: BorderSide(color: Colors.red), + ), + ), + shadowColor: + MaterialStateProperty.all(Color(0xffff48423d)), + elevation: MaterialStateProperty.all(4.0), + ), + child: Align( + alignment: Alignment.center, + child: Text( + 'Scan', + style: Constant.buttonLarge(context: context).copyWith( + color: (isScan.value == true) + ? Constant.backgroundWhite + : Constant.textBlack, + fontWeight: FontWeight.w700, + ), + ), + ), + ), + ), + ], + ), + ), + + if (isForm.value == true) ...[ + // button batal & simpan jika isForm true + SizedBox( + height: Constant.getActualY(context: context, y: 20), + ), + + // loading + if (isLoadingSearchNoLab.value) ...[ + // Center( + // child: SizedBox( + // width: Constant.getActualX(context: context, x: 20), + // height: Constant.getActualY(context: context, y: 20), + // child: CircularProgressIndicator(), + // ), + // ), + const Center( + child: CircularProgressIndicator(), + ), + ], + + SizedBox( + height: Constant.getActualY(context: context, y: 20), + ), + + // no lab search with icon + Container( + width: Constant.getActualX(context: context, x: 320), + height: Constant.getActualY(context: context, y: 56), + child: SasTextFieldSearch( + controller: ctrlNoLab, + hintText: "No Lab", + labelText: "No Lab", + focusNode: focusNodeNoLab, + hasFocus: nolabhasFocus.value, + isReadOnly: false, + onPressed: () { + if (ctrlNoLab.text != "" || ctrlNoLab.text.isNotEmpty) { + if (ctrlNoLab.text.length > 3) { + ref + .read(pengantaranHasilPasienSearchNoLab.notifier) + .loadListNoLabPengantaranHasilPasien( + search: ctrlNoLab.text.toString(), + ); + } + } else { + SanckbarWidget( + context, + "Silahkan Masukkan Keyword Pencarian", + snackbarType.warning); + } + }, + ), + ), + + SizedBox( + height: Constant.getActualY(context: context, y: 20), + ), + + // nama + Container( + width: Constant.getActualX(context: context, x: 320), + height: Constant.getActualY(context: context, y: 56), + child: SasTextField( + hintText: "Nama", + labelText: "Nama", + focusNode: focusNodeNama, + hasFocus: namahasFocus.value, + controller: ctrlNama, + isReadOnly: true, + ), + ), + SizedBox( + height: Constant.getActualY(context: context, y: 20), + ), + // alamat + Container( + width: Constant.getActualX(context: context, x: 320), + height: Constant.getActualY(context: context, y: 120), + // color: Colors.green, + child: SasTextFieldArea( + hintText: "Alamat Pengantaran", + labelText: "Alamat Pengantaran", + focusNode: focusNodeAlamat, + hasFocus: alamathasFocus.value, + controller: ctrlAlamat, + isReadOnly: true, + ), + ), + + SizedBox( + height: Constant.getActualY(context: context, y: 50), + ), + + Padding( + padding: EdgeInsets.only( + top: Constant.getActualY(context: context, y: 20), + bottom: Constant.getActualY(context: context, y: 20), + ), + child: Align( + alignment: Alignment.bottomCenter, + child: Container( + // width: Constant.getActualX(context: context, x: 317), + // height: Constant.getActualY(context: context, y: 36), + // padding: EdgeInsets.symmetric( + // horizontal: Constant.getActualX(context: context, x: 10)), + height: Constant.getActualY(context: context, y: 36), + // color: Colors.pink, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + // batal + ElevatedButton( + onPressed: () { + Navigator.pop(context); + }, + style: ButtonStyle( + backgroundColor: MaterialStateColor.resolveWith( + (st) => Constant.backgroundWhite), + shape: + MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: BorderSide( + color: Constant.blueButton, + ), + ), + ), + shadowColor: + MaterialStateProperty.all(Color(0xffff48423d)), + elevation: MaterialStateProperty.all(4.0), + ), + child: Align( + alignment: Alignment.center, + child: Text( + 'Batal', + style: Constant.body3(context: context).copyWith( + color: Constant.blueButton, + fontWeight: FontWeight.w700, + ), + ), + ), + ), + + // simpan + ElevatedButton( + onPressed: (isLoadingSearchNoLab.value) + ? null + : () { + print("kurir id : $mCourirID"); + print("tipe id : $tipePekerjaanIDSave"); + print("delivery id : ${deliveryIDSave.value}"); + + if (deliveryIDSave.value == "") { + SanckbarWidget( + context, + "Silahkan Cari No Lab", + snackbarType.warning); + } else { + ref + .read(pengantaranHasilPasienSave.notifier) + .pengantaranHasilPasienSave( + courierID: mCourirID, + tipeID: tipePekerjaanIDSave, + deliveryID: deliveryIDSave.value, + ); + } + }, + style: ButtonStyle( + backgroundColor: MaterialStateColor.resolveWith( + (st) => Constant.blueButton, + ), + shape: + MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + // side: BorderSide(color: Constant.blueButton), + ), + ), + shadowColor: + MaterialStateProperty.all(Color(0xffff48423d)), + elevation: MaterialStateProperty.all(4.0), + ), + child: Align( + alignment: Alignment.center, + child: Text( + 'Simpan', + style: Constant.body3(context: context).copyWith( + color: Constant.backgroundWhite, + fontWeight: FontWeight.w700, + ), + ), + ), + ), + ], + ), + ), + ), + ), + ] else ...[ + // button scan barcode + SizedBox( + height: Constant.getActualY(context: context, y: 445), + ), + Container( + width: Constant.getActualX(context: context, x: 317), + height: Constant.getActualY(context: context, y: 36), + // color: Colors.pink, + child: SizedBox( + width: Constant.getActualX(context: context, x: 83), + height: Constant.getActualY(context: context, y: 36), + child: ElevatedButton( + onPressed: () { + FocusManager.instance.primaryFocus?.unfocus(); + ref.read(isFromScanScreen.notifier).update((state) => true); + Navigator.of(context).popAndPushNamed(inputScanRoute, + arguments: InputScanPekerjaanProp(0, 0)); + }, + style: ButtonStyle( + backgroundColor: MaterialStateColor.resolveWith( + (st) => Constant.blueButton), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + // side: BorderSide(color: Constant.blueButton), + ), + ), + shadowColor: MaterialStateProperty.all(Color(0xffff48423d)), + elevation: MaterialStateProperty.all(4.0), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.document_scanner), + SizedBox( + width: Constant.getActualX(context: context, x: 10), + ), + Text( + 'Scan Barcode', + style: Constant.body3(context: context).copyWith( + color: Constant.backgroundWhite, + fontWeight: FontWeight.w700, + ), + ), + ], + ), + ), + ), + ), + ], + ], + ), + ); + } +} diff --git a/lib/screen/input_pekerjaan/input_scan_screen.dart b/lib/screen/input_pekerjaan/input_scan_screen.dart new file mode 100644 index 0000000..756be54 --- /dev/null +++ b/lib/screen/input_pekerjaan/input_scan_screen.dart @@ -0,0 +1,103 @@ +import 'dart:async'; +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:mobile_scanner/mobile_scanner.dart'; + +import '../../app/route.dart'; +import '../../provider/current_scan_provider.dart'; + +class InputScanScreen extends HookConsumerWidget { + const InputScanScreen({super.key, required this.data}); + final InputScanPekerjaanProp data; + + @override + Widget build(BuildContext context, WidgetRef ref) { + print("DATAXSCAN : ${data.input} - ${data.tipe}"); + MobileScannerController cameraController = MobileScannerController(); + final hasilScan = useState(""); + + return Scaffold( + appBar: AppBar( + title: const Text('Scan Barcode'), + actions: [ + IconButton( + color: Colors.white, + icon: ValueListenableBuilder( + valueListenable: cameraController.torchState, + builder: (context, state, child) { + switch (state as TorchState) { + case TorchState.off: + return const Icon(Icons.flash_off, color: Colors.grey); + case TorchState.on: + return const Icon(Icons.flash_on, color: Colors.yellow); + } + }, + ), + iconSize: 32.0, + onPressed: () => cameraController.toggleTorch(), + ), + IconButton( + color: Colors.white, + icon: ValueListenableBuilder( + valueListenable: cameraController.cameraFacingState, + builder: (context, state, child) { + switch (state as CameraFacing) { + case CameraFacing.front: + return const Icon(Icons.camera_front); + case CameraFacing.back: + return const Icon(Icons.camera_rear); + } + }, + ), + iconSize: 32.0, + onPressed: () => cameraController.switchCamera(), + ), + ], + ), + body: MobileScanner( + // fit: BoxFit.contain, + controller: cameraController, + onDetect: (capture) { + final List barcodes = capture.barcodes; + for (final barcode in barcodes) { + hasilScan.value = barcode.rawValue.toString(); + debugPrint('Barcode found! ${barcode.rawValue}'); + } + + if (hasilScan.value.isNotEmpty) { + // set ke provider global + ref + .read(barcodeScanResult.notifier) + .update((state) => hasilScan.value); + + if (data.tipe == 0) { + Navigator.of(context).popAndPushNamed( + inputPekerjaan, + arguments: inputPekerjaanProp(0, 0), + ); + } else { + if (data.tipe == 1) { + Navigator.of(context).popAndPushNamed( + inputPekerjaan, + arguments: inputPekerjaanProp(0, 1), + ); + } else { + if (data.tipe == 2) { + Navigator.of(context).popAndPushNamed( + inputPekerjaan, + arguments: inputPekerjaanProp(0, 2), + ); + } + } + } + + // Navigator.pop(context); + } + }, + ), + ); + } +} diff --git a/lib/screen/input_pekerjaan/pengambilan_bahan_loadcabang_provider.dart b/lib/screen/input_pekerjaan/pengambilan_bahan_loadcabang_provider.dart new file mode 100644 index 0000000..41d7c35 --- /dev/null +++ b/lib/screen/input_pekerjaan/pengambilan_bahan_loadcabang_provider.dart @@ -0,0 +1,63 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '/models/response_list_branch_model.dart'; +import '/models/response_search_pengantaran_hasil_model.dart'; +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; +import '../../repository/pengambilan_bahan_repository.dart'; +import '../../repository/pengantaran_hasil_pasien_repository.dart'; + +// 3. state provider +final pengambilanBahanListCabang = StateNotifierProvider( + (ref) => PengambilanBahanNotifier(ref: ref)); + +// 2. notifier +class PengambilanBahanNotifier extends StateNotifier { + final Ref ref; + PengambilanBahanNotifier({required this.ref}) : super(PengambilanBahanStateInit()); + void loadListCabangPengambilanBahan() async { + try { + state = PengambilanBahanStateLoading(); + final resp = await PengambilanBahanRepository(dio: ref.read(dioProvider)) + .loadListCabangPengambilanBahan(); + + state = PengambilanBahanStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = PengambilanBahanStateError(message: e.message ?? ""); + } else { + state = PengambilanBahanStateError(message: e.toString()); + } + } + } +} + +// 1. state +abstract class PengambilanBahanState extends Equatable { + final DateTime date; + const PengambilanBahanState(this.date); + @override + List get props => [date]; +} + +class PengambilanBahanStateInit extends PengambilanBahanState { + PengambilanBahanStateInit() : super(DateTime.now()); +} + +class PengambilanBahanStateLoading extends PengambilanBahanState { + PengambilanBahanStateLoading() : super(DateTime.now()); +} + +class PengambilanBahanStateError extends PengambilanBahanState { + final String? message; + PengambilanBahanStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class PengambilanBahanStateDone extends PengambilanBahanState { + final List model; + PengambilanBahanStateDone({ + required this.model, + }) : super(DateTime.now()); +} diff --git a/lib/screen/input_pekerjaan/pengambilan_bahan_save_provider.dart b/lib/screen/input_pekerjaan/pengambilan_bahan_save_provider.dart new file mode 100644 index 0000000..7cff902 --- /dev/null +++ b/lib/screen/input_pekerjaan/pengambilan_bahan_save_provider.dart @@ -0,0 +1,77 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../models/response_save_pengantaran_hasil_model.dart'; +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; +import '../../repository/pengambilan_bahan_repository.dart'; +import '../../repository/pengantaran_hasil_dokter_repository.dart'; + +// 3. state provider +final pengambilanBahanSave = StateNotifierProvider((ref) => PengambilanBahanSaveNotifier(ref: ref)); + +// 2. notifier +class PengambilanBahanSaveNotifier + extends StateNotifier { + final Ref ref; + PengambilanBahanSaveNotifier({required this.ref}) + : super(PengambilanBahanSaveStateInit()); + void pengambilanBahanSave({ + required String courierID, + required String tipeID, + required String branchID, + required String branchCode, + required String name, + required String destination, + }) async { + try { + state = PengambilanBahanSaveStateLoading(); + final resp = await PengambilanBahanRepository(dio: ref.read(dioProvider)) + .pengambilanBahanSave( + courierID: courierID, + tipeID: tipeID, + branchID: branchID, + branchCode: branchCode, + name: name, + destination: destination); + + state = PengambilanBahanSaveStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = PengambilanBahanSaveStateError(message: e.message ?? ""); + } else { + state = PengambilanBahanSaveStateError(message: e.toString()); + } + } + } +} + +// 1. state +abstract class PengambilanBahanSaveState extends Equatable { + final DateTime date; + const PengambilanBahanSaveState(this.date); + @override + List get props => [date]; +} + +class PengambilanBahanSaveStateInit extends PengambilanBahanSaveState { + PengambilanBahanSaveStateInit() : super(DateTime.now()); +} + +class PengambilanBahanSaveStateLoading extends PengambilanBahanSaveState { + PengambilanBahanSaveStateLoading() : super(DateTime.now()); +} + +class PengambilanBahanSaveStateError extends PengambilanBahanSaveState { + final String? message; + PengambilanBahanSaveStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class PengambilanBahanSaveStateDone extends PengambilanBahanSaveState { + final ResponseSavePengantaranHasilModel model; + PengambilanBahanSaveStateDone({ + required this.model, + }) : super(DateTime.now()); +} diff --git a/lib/screen/input_pekerjaan/pengantaran_hasil_dokter_loadnolab_provider.dart b/lib/screen/input_pekerjaan/pengantaran_hasil_dokter_loadnolab_provider.dart new file mode 100644 index 0000000..af5795a --- /dev/null +++ b/lib/screen/input_pekerjaan/pengantaran_hasil_dokter_loadnolab_provider.dart @@ -0,0 +1,64 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../models/response_search_pengantaran_hasil_model.dart'; +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; +import '../../repository/pengantaran_hasil_dokter_repository.dart'; + +// 3. state provider +final pengantaranHasilDokterSearch = StateNotifierProvider( + (ref) => PengantaranhasilDokterNotifier(ref: ref)); + +// 2. notifier +class PengantaranhasilDokterNotifier extends StateNotifier { + final Ref ref; + PengantaranhasilDokterNotifier({required this.ref}) : super(PengantaranhasilDokterStateInit()); + void loadListNoDokterPengantaranHasilDokter( + { + required String search + }) async { + try { + state = PengantaranhasilDokterStateLoading(); + final resp = await PengantaranHasilDokterRepository(dio: ref.read(dioProvider)) + .loadListNoDokterPengantaranHasilDokter(search: search); + + state = PengantaranhasilDokterStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = PengantaranhasilDokterStateError(message: e.message ?? ""); + } else { + state = PengantaranhasilDokterStateError(message: e.toString()); + } + } + } +} + +// 1. state +abstract class PengantaranhasilDokterState extends Equatable { + final DateTime date; + const PengantaranhasilDokterState(this.date); + @override + List get props => [date]; +} + +class PengantaranhasilDokterStateInit extends PengantaranhasilDokterState { + PengantaranhasilDokterStateInit() : super(DateTime.now()); +} + +class PengantaranhasilDokterStateLoading extends PengantaranhasilDokterState { + PengantaranhasilDokterStateLoading() : super(DateTime.now()); +} + +class PengantaranhasilDokterStateError extends PengantaranhasilDokterState { + final String? message; + PengantaranhasilDokterStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class PengantaranhasilDokterStateDone extends PengantaranhasilDokterState { + final List model; + PengantaranhasilDokterStateDone({ + required this.model, + }) : super(DateTime.now()); +} diff --git a/lib/screen/input_pekerjaan/pengantaran_hasil_dokter_save_provider.dart b/lib/screen/input_pekerjaan/pengantaran_hasil_dokter_save_provider.dart new file mode 100644 index 0000000..64a296e --- /dev/null +++ b/lib/screen/input_pekerjaan/pengantaran_hasil_dokter_save_provider.dart @@ -0,0 +1,77 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../models/response_save_pengantaran_hasil_model.dart'; +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; +import '../../repository/pengantaran_hasil_dokter_repository.dart'; + +// 3. state provider +final pengantaranHasilDokterSave = StateNotifierProvider< + PengantaranhasilDokterSaveNotifier, PengantaranhasilDokterSaveState>( + (ref) => PengantaranhasilDokterSaveNotifier(ref: ref)); + +// 2. notifier +class PengantaranhasilDokterSaveNotifier + extends StateNotifier { + final Ref ref; + PengantaranhasilDokterSaveNotifier({required this.ref}) + : super(PengantaranhasilDokterSaveStateInit()); + void pengantaranHasilDokterSave({ + required String courierID, + required String tipeID, + required String deliveryID, + }) async { + try { + state = PengantaranhasilDokterSaveStateLoading(); + final resp = + await PengantaranHasilDokterRepository(dio: ref.read(dioProvider)) + .pengantaranHasilDokterSave( + courierID: courierID, + tipeID: tipeID, + deliveryID: deliveryID + ); + + state = PengantaranhasilDokterSaveStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = PengantaranhasilDokterSaveStateError(message: e.message ?? ""); + } else { + state = PengantaranhasilDokterSaveStateError(message: e.toString()); + } + } + } +} + +// 1. state +abstract class PengantaranhasilDokterSaveState extends Equatable { + final DateTime date; + const PengantaranhasilDokterSaveState(this.date); + @override + List get props => [date]; +} + +class PengantaranhasilDokterSaveStateInit + extends PengantaranhasilDokterSaveState { + PengantaranhasilDokterSaveStateInit() : super(DateTime.now()); +} + +class PengantaranhasilDokterSaveStateLoading + extends PengantaranhasilDokterSaveState { + PengantaranhasilDokterSaveStateLoading() : super(DateTime.now()); +} + +class PengantaranhasilDokterSaveStateError + extends PengantaranhasilDokterSaveState { + final String? message; + PengantaranhasilDokterSaveStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class PengantaranhasilDokterSaveStateDone + extends PengantaranhasilDokterSaveState { + final ResponseSavePengantaranHasilModel model; + PengantaranhasilDokterSaveStateDone({ + required this.model, + }) : super(DateTime.now()); +} diff --git a/lib/screen/input_pekerjaan/pengantaran_hasil_instansi_loadnosuratjalan_provider.dart b/lib/screen/input_pekerjaan/pengantaran_hasil_instansi_loadnosuratjalan_provider.dart new file mode 100644 index 0000000..6850784 --- /dev/null +++ b/lib/screen/input_pekerjaan/pengantaran_hasil_instansi_loadnosuratjalan_provider.dart @@ -0,0 +1,65 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../models/response_search_pengantaran_hasil_model.dart'; +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; +import '../../repository/pengantaran_hasil_instansi_repository.dart'; +import '../../repository/pengantaran_hasil_pasien_repository.dart'; + +// 3. state provider +final pengantaranHasilInstansi = StateNotifierProvider( + (ref) => PengantaranhasilInstansiNotifier(ref: ref)); + +// 2. notifier +class PengantaranhasilInstansiNotifier extends StateNotifier { + final Ref ref; + PengantaranhasilInstansiNotifier({required this.ref}) : super(PengantaranhasilInstansiStateInit()); + void loadListNoSuratJalanPengantaranHasilInstansi( + { + required String search + }) async { + try { + state = PengantaranhasilInstansiStateLoading(); + final resp = await PengantaranHasilInstansiRepository(dio: ref.read(dioProvider)) + .loadListNoSuratJalanPengantaranHasilInstansi(search: search); + + state = PengantaranhasilInstansiStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = PengantaranhasilInstansiStateError(message: e.message ?? ""); + } else { + state = PengantaranhasilInstansiStateError(message: e.toString()); + } + } + } +} + +// 1. state +abstract class PengantaranhasilInstansiState extends Equatable { + final DateTime date; + const PengantaranhasilInstansiState(this.date); + @override + List get props => [date]; +} + +class PengantaranhasilInstansiStateInit extends PengantaranhasilInstansiState { + PengantaranhasilInstansiStateInit() : super(DateTime.now()); +} + +class PengantaranhasilInstansiStateLoading extends PengantaranhasilInstansiState { + PengantaranhasilInstansiStateLoading() : super(DateTime.now()); +} + +class PengantaranhasilInstansiStateError extends PengantaranhasilInstansiState { + final String? message; + PengantaranhasilInstansiStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class PengantaranhasilInstansiStateDone extends PengantaranhasilInstansiState { + final List model; + PengantaranhasilInstansiStateDone({ + required this.model, + }) : super(DateTime.now()); +} diff --git a/lib/screen/input_pekerjaan/pengantaran_hasil_instansi_save_provider.dart b/lib/screen/input_pekerjaan/pengantaran_hasil_instansi_save_provider.dart new file mode 100644 index 0000000..718db95 --- /dev/null +++ b/lib/screen/input_pekerjaan/pengantaran_hasil_instansi_save_provider.dart @@ -0,0 +1,78 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../models/response_save_pengantaran_hasil_model.dart'; +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; +import '../../repository/pengantaran_hasil_dokter_repository.dart'; +import '../../repository/pengantaran_hasil_instansi_repository.dart'; + +// 3. state provider +final pengantaranHasilCompanySave = StateNotifierProvider< + PengantaranhasilCompanySaveNotifier, PengantaranhasilCompanySaveState>( + (ref) => PengantaranhasilCompanySaveNotifier(ref: ref)); + +// 2. notifier +class PengantaranhasilCompanySaveNotifier + extends StateNotifier { + final Ref ref; + PengantaranhasilCompanySaveNotifier({required this.ref}) + : super(PengantaranhasilCompanySaveStateInit()); + void pengantaranHasilCompanySave({ + required String courierID, + required String tipeID, + required String deliveryID, + }) async { + try { + state = PengantaranhasilCompanySaveStateLoading(); + final resp = + await PengantaranHasilInstansiRepository(dio: ref.read(dioProvider)) + .pengantaranHasilInstansiSave( + courierID: courierID, + tipeID: tipeID, + deliveryID: deliveryID + ); + + state = PengantaranhasilCompanySaveStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = PengantaranhasilCompanySaveStateError(message: e.message ?? ""); + } else { + state = PengantaranhasilCompanySaveStateError(message: e.toString()); + } + } + } +} + +// 1. state +abstract class PengantaranhasilCompanySaveState extends Equatable { + final DateTime date; + const PengantaranhasilCompanySaveState(this.date); + @override + List get props => [date]; +} + +class PengantaranhasilCompanySaveStateInit + extends PengantaranhasilCompanySaveState { + PengantaranhasilCompanySaveStateInit() : super(DateTime.now()); +} + +class PengantaranhasilCompanySaveStateLoading + extends PengantaranhasilCompanySaveState { + PengantaranhasilCompanySaveStateLoading() : super(DateTime.now()); +} + +class PengantaranhasilCompanySaveStateError + extends PengantaranhasilCompanySaveState { + final String? message; + PengantaranhasilCompanySaveStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class PengantaranhasilCompanySaveStateDone + extends PengantaranhasilCompanySaveState { + final ResponseSavePengantaranHasilModel model; + PengantaranhasilCompanySaveStateDone({ + required this.model, + }) : super(DateTime.now()); +} diff --git a/lib/screen/input_pekerjaan/pengantaran_hasil_pasien_loadnolab_provider.dart b/lib/screen/input_pekerjaan/pengantaran_hasil_pasien_loadnolab_provider.dart new file mode 100644 index 0000000..ef64287 --- /dev/null +++ b/lib/screen/input_pekerjaan/pengantaran_hasil_pasien_loadnolab_provider.dart @@ -0,0 +1,64 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '/models/response_search_pengantaran_hasil_model.dart'; +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; +import '../../repository/pengantaran_hasil_pasien_repository.dart'; + +// 3. state provider +final pengantaranHasilPasienSearchNoLab = StateNotifierProvider( + (ref) => PengantaranhasilPasienNotifier(ref: ref)); + +// 2. notifier +class PengantaranhasilPasienNotifier extends StateNotifier { + final Ref ref; + PengantaranhasilPasienNotifier({required this.ref}) : super(PengantaranhasilPasienStateInit()); + void loadListNoLabPengantaranHasilPasien( + { + required String search + }) async { + try { + state = PengantaranhasilPasienStateLoading(); + final resp = await PengantaranHasilPasienRepository(dio: ref.read(dioProvider)) + .loadListNoLabPengantaranHasilPasien(search: search); + + state = PengantaranhasilPasienStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = PengantaranhasilPasienStateError(message: e.message ?? ""); + } else { + state = PengantaranhasilPasienStateError(message: e.toString()); + } + } + } +} + +// 1. state +abstract class PengantaranhasilPasienState extends Equatable { + final DateTime date; + const PengantaranhasilPasienState(this.date); + @override + List get props => [date]; +} + +class PengantaranhasilPasienStateInit extends PengantaranhasilPasienState { + PengantaranhasilPasienStateInit() : super(DateTime.now()); +} + +class PengantaranhasilPasienStateLoading extends PengantaranhasilPasienState { + PengantaranhasilPasienStateLoading() : super(DateTime.now()); +} + +class PengantaranhasilPasienStateError extends PengantaranhasilPasienState { + final String? message; + PengantaranhasilPasienStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class PengantaranhasilPasienStateDone extends PengantaranhasilPasienState { + final List model; + PengantaranhasilPasienStateDone({ + required this.model, + }) : super(DateTime.now()); +} diff --git a/lib/screen/input_pekerjaan/pengantaran_hasil_pasien_save_provider.dart b/lib/screen/input_pekerjaan/pengantaran_hasil_pasien_save_provider.dart new file mode 100644 index 0000000..5c21488 --- /dev/null +++ b/lib/screen/input_pekerjaan/pengantaran_hasil_pasien_save_provider.dart @@ -0,0 +1,77 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../models/response_save_pengantaran_hasil_model.dart'; +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; +import '../../repository/pengantaran_hasil_pasien_repository.dart'; + +// 3. state provider +final pengantaranHasilPasienSave = StateNotifierProvider< + PengantaranhasilPasienSaveNotifier, PengantaranhasilPasienSaveState>( + (ref) => PengantaranhasilPasienSaveNotifier(ref: ref)); + +// 2. notifier +class PengantaranhasilPasienSaveNotifier + extends StateNotifier { + final Ref ref; + PengantaranhasilPasienSaveNotifier({required this.ref}) + : super(PengantaranhasilPasienSaveStateInit()); + void pengantaranHasilPasienSave({ + required String courierID, + required String tipeID, + required String deliveryID, + }) async { + try { + state = PengantaranhasilPasienSaveStateLoading(); + final resp = + await PengantaranHasilPasienRepository(dio: ref.read(dioProvider)) + .pengantaranHasilPasienSave( + courierID: courierID, + tipeID: tipeID, + deliveryID: deliveryID + ); + + state = PengantaranhasilPasienSaveStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = PengantaranhasilPasienSaveStateError(message: e.message ?? ""); + } else { + state = PengantaranhasilPasienSaveStateError(message: e.toString()); + } + } + } +} + +// 1. state +abstract class PengantaranhasilPasienSaveState extends Equatable { + final DateTime date; + const PengantaranhasilPasienSaveState(this.date); + @override + List get props => [date]; +} + +class PengantaranhasilPasienSaveStateInit + extends PengantaranhasilPasienSaveState { + PengantaranhasilPasienSaveStateInit() : super(DateTime.now()); +} + +class PengantaranhasilPasienSaveStateLoading + extends PengantaranhasilPasienSaveState { + PengantaranhasilPasienSaveStateLoading() : super(DateTime.now()); +} + +class PengantaranhasilPasienSaveStateError + extends PengantaranhasilPasienSaveState { + final String? message; + PengantaranhasilPasienSaveStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class PengantaranhasilPasienSaveStateDone + extends PengantaranhasilPasienSaveState { + final ResponseSavePengantaranHasilModel model; + PengantaranhasilPasienSaveStateDone({ + required this.model, + }) : super(DateTime.now()); +} diff --git a/lib/screen/input_pekerjaan/search_company_provider.dart b/lib/screen/input_pekerjaan/search_company_provider.dart new file mode 100644 index 0000000..61029d9 --- /dev/null +++ b/lib/screen/input_pekerjaan/search_company_provider.dart @@ -0,0 +1,68 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '/models/company_model.dart'; +import '/models/work_list_model.dart'; +import '../../repository/company_repository.dart'; +import '../../repository/work_list_repository.dart'; + +import '../../models/pending_work_model.dart'; +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; +import '../../repository/home_screen_repository.dart'; + +abstract class SearchCompanyState extends Equatable { + final DateTime date; + const SearchCompanyState(this.date); + @override + List get props => [date]; +} + +class SearchCompanyStateInit extends SearchCompanyState { + SearchCompanyStateInit() : super(DateTime.now()); +} + +class SearchCompanyStateLoading extends SearchCompanyState { + SearchCompanyStateLoading() : super(DateTime.now()); +} + +class SearchCompanyStateError extends SearchCompanyState { + final String message; + SearchCompanyStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class SearchCompanyStateDone extends SearchCompanyState { + final List model; + SearchCompanyStateDone({ + required this.model, + }) : super(DateTime.now()); +} + +//notifier +class SearchCompanyNotifier extends StateNotifier { + final Ref ref; + SearchCompanyNotifier({ + required this.ref, + }) : super(SearchCompanyStateInit()); + + void getData({required String keyword}) async { + try { + state = SearchCompanyStateLoading(); + final dio = ref.read(dioProvider); + final resp = await CompanyRepository(dio: dio).search(keyword: keyword); + state = SearchCompanyStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = SearchCompanyStateError(message: e.message.toString()); + } else { + state = SearchCompanyStateError(message: e.toString()); + } + } + } +} + +//provider +final SearchCompanyProvider = + StateNotifierProvider( + (ref) => SearchCompanyNotifier(ref: ref)); diff --git a/lib/screen/konfirmasi/konfirmasi_screen.dart b/lib/screen/konfirmasi/konfirmasi_screen.dart new file mode 100644 index 0000000..f8e9caf --- /dev/null +++ b/lib/screen/konfirmasi/konfirmasi_screen.dart @@ -0,0 +1,91 @@ +import 'package:flutter/material.dart'; +import '/app/route.dart'; + +import '../../app/constant.dart'; + +class KonfirmasiScreen extends StatelessWidget { + const KonfirmasiScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + resizeToAvoidBottomInset: true, + backgroundColor: Constant.backgroundWhite, + body: SizedBox( + width: Constant.getActualX(context: context, x: 390), + height: Constant.getActualY(context: context, y: 844), + child: Column( + children: [ + // image + Padding( + padding: EdgeInsets.only( + top: Constant.getActualY(context: context, y: 196), + left: Constant.getActualX(context: context, x: 30), + right: Constant.getActualX(context: context, x: 30), + ), + child: Container( + // width: Constant.getActualX(context: context, x: 251), + // height: Constant.getActualY(context: context, y: 78), + // color: Colors.green, + child: Image.asset( + "assets/logo_kurir_konfirmasiv2.png", + // fit: BoxFit.fill, + // scale: 1, + ), + ), + ), + SizedBox( + height: Constant.getActualY(context: context, y: 40), + ), + SizedBox( + width: Constant.getActualX(context: context, x: 268), + height: Constant.getActualY(context: context, y: 40), + child: Text( + 'Input pekerjaan baru berhasil. Harap menunggu konfirmasi.', + style: Constant.body3( + context: context, + ).copyWith(fontWeight: FontWeight.w600), + ), + ), + SizedBox( + height: Constant.getActualY(context: context, y: 68), + ), + // button konfirmasi + SizedBox( + width: Constant.getActualX(context: context, x: 320), + height: Constant.getActualY(context: context, y: 46), + child: ElevatedButton( + onPressed: () { + Navigator.of(context) + .pushNamedAndRemoveUntil(menuRoute, (route) => false); + }, + style: ButtonStyle( + backgroundColor: MaterialStateColor.resolveWith( + (st) => Constant.primaryDark), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + // side: BorderSide(color: Colors.red), + ), + ), + shadowColor: MaterialStateProperty.all(Color(0xffff48423d)), + elevation: MaterialStateProperty.all(4.0), + ), + child: Align( + alignment: Alignment.center, + child: Text( + 'Kembali ke Beranda', + style: Constant.buttonLarge(context: context).copyWith( + color: Constant.backgroundWhite, + fontWeight: FontWeight.w700, + ), + ), + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/screen/kurir_konfirmasi_screen/kurir_tolak_provider.dart b/lib/screen/kurir_konfirmasi_screen/kurir_tolak_provider.dart new file mode 100644 index 0000000..d0b76f6 --- /dev/null +++ b/lib/screen/kurir_konfirmasi_screen/kurir_tolak_provider.dart @@ -0,0 +1,70 @@ +import 'dart:convert'; + +import 'package:equatable/equatable.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import '../../repository/base_repository.dart'; + +import '../../provider/dio_provider.dart'; +import '../../repository/kurir_tolak_repository.dart'; + +//provider +final kurirTolakProvider = + StateNotifierProvider( + (ref) => KurirTolakNotifier(ref: ref)); + +// notifier +class KurirTolakNotifier extends StateNotifier { + final Ref ref; + KurirTolakNotifier({ + required this.ref, + }) : super(KurirTolakStateInit()); + + void tolakKurir( + {required String id, + required String tipeId, + required String deliveryId, + required String note}) async { + try { + state = KurirTolakStateLoading(); + final dio = ref.read(dioProvider); + final resp = await KurirTolakRepository(dio: dio).getKurirTolak( + id: id, tipeId: tipeId, deliveryId: deliveryId, note: note); + state = KurirTolakStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = KurirTolakStateError(message: e.message.toString()); + } else { + state = KurirTolakStateError(message: e.toString()); + } + } + } +} + +abstract class KurirTolakState extends Equatable { + final DateTime date; + const KurirTolakState(this.date); + @override + List get props => [date]; +} + +class KurirTolakStateInit extends KurirTolakState { + KurirTolakStateInit() : super(DateTime.now()); +} + +class KurirTolakStateLoading extends KurirTolakState { + KurirTolakStateLoading() : super(DateTime.now()); +} + +class KurirTolakStateError extends KurirTolakState { + final String message; + KurirTolakStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class KurirTolakStateDone extends KurirTolakState { + final bool model; + KurirTolakStateDone({ + required this.model, + }) : super(DateTime.now()); +} diff --git a/lib/screen/login/login_provider.dart b/lib/screen/login/login_provider.dart new file mode 100644 index 0000000..cfb6b93 --- /dev/null +++ b/lib/screen/login/login_provider.dart @@ -0,0 +1,94 @@ +import 'dart:convert'; + +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import '../../app/constant.dart'; +import '../../models/auth_model.dart'; +import '../../provider/current_user_provider.dart'; +import '../../provider/dio_provider.dart'; +import '../../repository/auth_repository.dart'; +import '../../repository/base_repository.dart'; + +// 3. state provider +final loginProvider = StateNotifierProvider( + (ref) => LoginNotifier(ref: ref)); + +// 2. notifier +class LoginNotifier extends StateNotifier { + final Ref ref; + LoginNotifier({required this.ref}) : super(LoginStateInit()); + void login( + {required String username, + required String password, + bool isRememberMe = false}) async { + try { + state = LoginStateLoading(); + final resp = await AuthRepository(dio: ref.read(dioProvider)) + .login(username: username, password: password); + + state = LoginStateDone(model: resp); + //Simpan ke token jk remember me + if (isRememberMe == true) { + final shared = await SharedPreferences.getInstance(); + final token = {"date": DateTime.now().toString(), "model": resp.model}; + final tokenEncode = jsonEncode(token); + await shared.setString(Constant.tokenName, tokenEncode); + await shared.setString("usernameX", username); + await shared.setString("passwordX", password); + await shared.setBool("isRememberMeX", isRememberMe); + } + else { + // share pref di remove karena remember me tidak di centang + final shared = await SharedPreferences.getInstance(); + await shared.remove("usernameX"); + await shared.remove("passwordX"); + await shared.remove("isRememberMeX"); + } + + ref.read(currentUserProvider.notifier).state = AuthModel( + token: resp.token, + model: resp.model, + ); + + // print(shared.getString(Constant.bearerName)); + } catch (e) { + if (e is BaseRepositoryException) { + state = LoginStateError(message: e.message ?? ""); + } else { + state = LoginStateError(message: e.toString()); + } + } + } +} + +// 1. state +abstract class LoginState extends Equatable { + final DateTime date; + const LoginState(this.date); + @override + List get props => [date]; +} + +class LoginStateInit extends LoginState { + LoginStateInit() : super(DateTime.now()); +} + +class LoginStateLoading extends LoginState { + LoginStateLoading() : super(DateTime.now()); +} + +class LoginStateError extends LoginState { + final String? message; + LoginStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class LoginStateDone extends LoginState { + final AuthModel model; + LoginStateDone({ + required this.model, + }) : super(DateTime.now()); +} diff --git a/lib/screen/login/login_screen.dart b/lib/screen/login/login_screen.dart new file mode 100644 index 0000000..c05f367 --- /dev/null +++ b/lib/screen/login/login_screen.dart @@ -0,0 +1,325 @@ +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 '../../app/constant.dart'; +import '../../app/route.dart'; +import '../../models/auth_model.dart'; +import '../../provider/current_user_provider.dart'; +import '../../widget/sas_textfield.dart'; +import 'login_provider.dart'; + +class LoginScreen extends HookConsumerWidget { + const LoginScreen({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final isPasswordObscured = useState(true); + final ctrlUsername = useTextEditingController(text: ""); + final ctrlPassword = useTextEditingController(text: ""); + final focusNodeUsername = useFocusNode(); + final focusNodePassword = useFocusNode(); + + final usernamehasFocus = useState(false); + final passwordhasFocus = useState(false); + + final isRememberMe = useState(false); + final errorMessage = useState(""); + final isLoading = useState(false); + final isSuccess = useState(false); + final isValidEntry = useState(false); + + final usernameFromSharePref = useState(""); + final passwordFromSharePref = useState(""); + + focusNodeUsername.addListener(() { + if (focusNodeUsername.hasFocus) { + usernamehasFocus.value = true; + passwordhasFocus.value = false; + } else { + usernamehasFocus.value = false; + } + }); + + focusNodePassword.addListener(() { + if (focusNodePassword.hasFocus) { + usernamehasFocus.value = false; + passwordhasFocus.value = true; + } else { + passwordhasFocus.value = false; + } + }); + + // aksi login + ref.listen(loginProvider, (prev, next) async { + if (next is LoginStateLoading) { + isLoading.value = true; + } else if (next is LoginStateError) { + isLoading.value = false; + errorMessage.value = next.message ?? ""; + // print(errorMessage.value); + Timer(const Duration(seconds: 3), () { + errorMessage.value = ""; + }); + } else if (next is LoginStateDone) { + isLoading.value = false; + isSuccess.value = true; + Navigator.of(context) + .pushNamedAndRemoveUntil(menuRoute, (route) => false); + } + }); + + ctrlUsername.addListener( + () { + if (ctrlUsername.text.isEmpty || ctrlPassword.text.isEmpty) { + isValidEntry.value = false; + } else { + isValidEntry.value = true; + } + }, + ); + + ctrlPassword.addListener( + () { + if (ctrlUsername.text.isEmpty || ctrlPassword.text.isEmpty) { + isValidEntry.value = false; + } else { + isValidEntry.value = true; + } + }, + ); + + return GestureDetector( + onTap: () => FocusManager.instance.primaryFocus?.unfocus(), + child: Scaffold( + resizeToAvoidBottomInset: true, + backgroundColor: Constant.backgroundWhite, + body: SizedBox( + width: Constant.getActualX(context: context, x: 390), + height: Constant.getActualY(context: context, y: 844), + child: ListView( + children: [ + // bg login + Padding( + padding: EdgeInsets.only( + top: Constant.getActualY(context: context, y: 80), + left: Constant.getActualX(context: context, x: 16), + right: Constant.getActualX(context: context, x: 16), + ), + child: Image.asset( + "assets/bg_login.png", + // fit: BoxFit.fill, + // scale: 1, + ), + ), + SizedBox( + height: Constant.getActualY(context: context, y: 10), + ), + + // Error From Backend Start + if (errorMessage.value != "") + Padding( + padding: EdgeInsets.only( + top: Constant.getActualY(context: context, y: 27), + left: Constant.getActualX(context: context, x: 38), + right: Constant.getActualX(context: context, x: 32), + ), + child: Align( + alignment: Alignment.center, + child: Text( + "Peringatan : ${errorMessage.value}", + style: Constant.body1(context: context) + .copyWith(color: Constant.primaryRed), + ), + ), + ), + // Error From Backend End + + // loading + if (isLoading.value) + Padding( + padding: EdgeInsets.only( + top: Constant.getActualY(context: context, y: 10), + left: Constant.getActualX(context: context, x: 38), + right: Constant.getActualX(context: context, x: 32), + ), + child: Center( + child: SizedBox( + width: Constant.getActualX(context: context, x: 40), + height: Constant.getActualY(context: context, y: 40), + child: CircularProgressIndicator( + color: Constant.primaryBlue, + ), + ), + ), + ), + + SizedBox( + height: Constant.getActualY(context: context, y: 10), + ), + + Padding( + padding: EdgeInsets.only( + top: Constant.getActualY(context: context, y: 27), + left: Constant.getActualX(context: context, x: 38), + right: Constant.getActualX(context: context, x: 32), + ), + child: SizedBox( + width: Constant.getActualX(context: context, x: 320), + height: Constant.getActualY(context: context, y: 412), + // color: Colors.amber, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Login", + style: Constant.heading2(context: context).copyWith( + color: Constant.textPrimary, + fontWeight: FontWeight.w600), + ), + SizedBox( + height: Constant.getActualY(context: context, y: 20), + ), + SasTextField( + hintText: "Username", + labelText: "Username", + controller: ctrlUsername, + focusNode: focusNodeUsername, + hasFocus: usernamehasFocus.value, + style: Constant.body1(context: context) + .copyWith(color: Constant.textBlack), + ), + SizedBox( + height: Constant.getActualY(context: context, y: 16), + ), + SasTextFieldPassword( + hintText: "Password", + labelText: "Password", + controller: ctrlPassword, + focusNode: focusNodePassword, + hasFocus: passwordhasFocus.value, + onToggle: () { + isPasswordObscured.value = !isPasswordObscured.value; + }, + obscureText: + (isPasswordObscured.value == true) ? true : false, + ), + SizedBox( + height: Constant.getActualY(context: context, y: 16), + ), + SizedBox( + width: Constant.getActualX(context: context, x: 320), + height: Constant.getActualY(context: context, y: 20), + // color: Colors.green, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Checkbox( + value: isRememberMe.value, + onChanged: (value) { + if (value != null) { + isRememberMe.value = value; + // checkIsLogin(isRememberMe.value); + } + }, + ), + const SizedBox(width: 8.0), + Text( + "Ingat saya", + style: + Constant.caption1(context: context).copyWith( + color: Constant.textPrimary, + fontWeight: FontWeight.w400, + ), + ), + ], + ), + ), + SizedBox( + height: Constant.getActualY(context: context, y: 32), + ), + // button login + SizedBox( + width: Constant.getActualX(context: context, x: 320), + height: Constant.getActualY(context: context, y: 48), + child: ElevatedButton( + onPressed: (isLoading.value == true) + ? null + : () { + if (ctrlUsername.text.isEmpty || + ctrlPassword.text.isEmpty) { + isLoading.value = false; + errorMessage.value = 'Inputan harus diisi'; + Timer(const Duration(seconds: 3), () { + isLoading.value = false; + errorMessage.value = ""; + }); + } else { + ref.read(loginProvider.notifier).login( + username: ctrlUsername.text, + password: ctrlPassword.text, + isRememberMe: isRememberMe.value, + ); + } + }, + style: ButtonStyle( + backgroundColor: MaterialStateColor.resolveWith( + (st) => Constant.primaryMain), + shape: MaterialStateProperty.all< + RoundedRectangleBorder>( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + // side: BorderSide(color: Colors.red), + ), + ), + shadowColor: MaterialStateProperty.all( + const Color(0xffff48423d)), + elevation: MaterialStateProperty.all(4.0), + ), + child: Align( + alignment: Alignment.center, + child: Text( + 'Login', + style: Constant.buttonLarge(context: context) + .copyWith( + color: Constant.backgroundWhite, + fontWeight: FontWeight.w700, + ), + ), + ), + ), + ), + SizedBox( + height: Constant.getActualY(context: context, y: 16), + ), + // Center( + // child: InkWell( + // onTap: () { + // Navigator.of(context).pushNamedAndRemoveUntil( + // problemLoginRoute, + // (route) => true, + // ); + // }, + // child: Text( + // "Problem login", + // style: Constant.caption1(context: context).copyWith( + // color: Constant.errorDark, + // fontWeight: FontWeight.w400), + // ), + // ), + // ), + ], + ), + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/screen/login/logout_provider.dart b/lib/screen/login/logout_provider.dart new file mode 100644 index 0000000..da7f7a3 --- /dev/null +++ b/lib/screen/login/logout_provider.dart @@ -0,0 +1,64 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../provider/dio_provider.dart'; +import '../../repository/auth_repository.dart'; +import '../../repository/base_repository.dart'; + +// 3. state provider +final logoutProvider = StateNotifierProvider( + (ref) => LogoutNotifier(ref: ref)); + +// 2. notifier +class LogoutNotifier extends StateNotifier { + final Ref ref; + LogoutNotifier({required this.ref}) : super(LogoutStateInit()); + void logout({ + required String M_UserID, + required String M_UserUsername, + }) async { + try { + state = LogoutStateLoading(); + final resp = await AuthRepository(dio: ref.read(dioProvider)) + .logout(M_UserID: M_UserID, M_UserUsername: M_UserUsername); + + // print(resp); + state = LogoutStateDone(message: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = LogoutStateError(message: e.message ?? ""); + } else { + state = LogoutStateError(message: e.toString()); + } + } + } +} + +// 1. state +abstract class LogoutState extends Equatable { + final DateTime date; + const LogoutState(this.date); + @override + List get props => [date]; +} + +class LogoutStateInit extends LogoutState { + LogoutStateInit() : super(DateTime.now()); +} + +class LogoutStateLoading extends LogoutState { + LogoutStateLoading() : super(DateTime.now()); +} + +class LogoutStateError extends LogoutState { + final String message; + LogoutStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class LogoutStateDone extends LogoutState { + final String message; + LogoutStateDone({ + required this.message, + }) : super(DateTime.now()); +} diff --git a/lib/screen/menu_screen/menu_screen.dart b/lib/screen/menu_screen/menu_screen.dart new file mode 100644 index 0000000..1ca4f5c --- /dev/null +++ b/lib/screen/menu_screen/menu_screen.dart @@ -0,0 +1,95 @@ +import 'package:fancy_bottom_navigation_2/fancy_bottom_navigation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import '/screen/history_screen/history_screen.dart'; +import '/screen/profile_screen/profile_screen.dart'; +import '/screen/work_list_screen/work_list_screen.dart'; +import 'package:loader_overlay/loader_overlay.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; + +import '../../app/constant.dart'; +import '../../provider/current_user_provider.dart'; +import '../home_screen/home_screen.dart'; +import '../home_screen/pending_work_provider.dart'; +import '../home_screen/total_work_provider.dart'; +import '../work_list_screen/work_status_provider.dart'; +import '../work_list_screen/work_supervisor_moder.dart'; +import '../work_list_screen/work_type_provider.dart'; + +class MenuScreen extends HookConsumerWidget { + const MenuScreen({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final current_page = useState(0); + final mUsername = + ref.watch(currentUserProvider)?.model.user?.mStaffName ?? "0"; + + final mCourirID = + ref.watch(currentUserProvider)?.model.user?.mCourierID ?? "0"; + + changeMenu() { + switch (current_page.value) { + case 0: + return HomeScreen(); + break; + case 1: + return WorkListScreen(); + break; + case 2: + return HistoryScreen(); + break; + case 3: + return ProfilScreen(); + break; + default: + return HomeScreen(); + } + } + + return LoaderOverlay( + useDefaultLoading: false, + overlayColor: Colors.black, + overlayWidget: Center( + child: LoadingAnimationWidget.inkDrop( + color: Constant.primaryBlue, size: 50), + ), + child: Scaffold( + resizeToAvoidBottomInset: false, + backgroundColor: + current_page.value == 1 ? const Color(0XFFF4F6F8) : Colors.white, + bottomNavigationBar: Container( + height: Constant.getActualY(context: context, y: 67), + child: SizedBox( + height: Constant.getActualY(context: context, y: 67), + child: FancyBottomNavigation( + tabs: [ + TabData(iconData: Icons.home_outlined, title: "Beranda"), + TabData( + iconData: Icons.work_history_outlined, + title: "List Pekerjaan"), + TabData(iconData: Icons.map_outlined, title: "Riwayat"), + TabData(iconData: Icons.person_outlined, title: "Profil") + ], + onTabChangedListener: (position) { + current_page.value = position; + + // if (position == 0) { + // ref.read(TotalWorkProvider.notifier).getData(id: mCourirID); + // ref.read(PendingWorkProvider.notifier).getData(id: mCourirID); + // ref.read(WorkTypeProvider.notifier).getData(); + // ref.read(WorkStatusProvider.notifier).getData(); + // ref.read(WorkSupervisorProvider.notifier).getData(); + // } + }, + ), + ), + ), + body: changeMenu(), + ), + ); + } +} diff --git a/lib/screen/personal_information_screen/personal_information_provider.dart b/lib/screen/personal_information_screen/personal_information_provider.dart new file mode 100644 index 0000000..2feb5b7 --- /dev/null +++ b/lib/screen/personal_information_screen/personal_information_provider.dart @@ -0,0 +1,67 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '/models/personal_information_model.dart'; +import '../../repository/personal_information_repository.dart'; +import '../../repository/work_list_repository.dart'; + +import '../../models/pending_work_model.dart'; +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; +import '../../repository/home_screen_repository.dart'; + +abstract class PersonalInformationState extends Equatable { + final DateTime date; + const PersonalInformationState(this.date); + @override + List get props => [date]; +} + +class PersonalInformationStateInit extends PersonalInformationState { + PersonalInformationStateInit() : super(DateTime.now()); +} + +class PersonalInformationStateLoading extends PersonalInformationState { + PersonalInformationStateLoading() : super(DateTime.now()); +} + +class PersonalInformationStateError extends PersonalInformationState { + final String message; + PersonalInformationStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class PersonalInformationStateDone extends PersonalInformationState { + final List model; + PersonalInformationStateDone({ + required this.model, + }) : super(DateTime.now()); +} + +//notifier +class PersonalInformationNotifier extends StateNotifier { + final Ref ref; + PersonalInformationNotifier({ + required this.ref, + }) : super(PersonalInformationStateInit()); + + void getData( + {required String mCourierID}) async { + try { + state = PersonalInformationStateLoading(); + final dio = ref.read(dioProvider); + final resp = await PersonalInformationRepository(dio: dio).getPersonalInformation(mCourierID: mCourierID); + state = PersonalInformationStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = PersonalInformationStateError(message: e.message.toString()); + } else { + state = PersonalInformationStateError(message: e.toString()); + } + } + } +} + +//provider +final PersonalInformationProvider = StateNotifierProvider( + (ref) => PersonalInformationNotifier(ref: ref)); diff --git a/lib/screen/personal_information_screen/personal_information_screen.dart b/lib/screen/personal_information_screen/personal_information_screen.dart new file mode 100644 index 0000000..e1db5ea --- /dev/null +++ b/lib/screen/personal_information_screen/personal_information_screen.dart @@ -0,0 +1,185 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import '/app/route.dart'; + +import '../../app/constant.dart'; +import '../../provider/current_user_provider.dart'; +import '../../widget/snackbar_widget.dart'; +import 'personal_information_provider.dart'; + +class PersonalInformationScreen extends HookConsumerWidget { + const PersonalInformationScreen({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final name = ref.watch(currentUserProvider)?.model.user?.mStaffName ?? "0"; + final namaController = useTextEditingController(text: name); + final emailController = useTextEditingController(text: "-"); + final nikController = useTextEditingController(text: "-"); + final isLoading = useState(false); + + final mCourirID = + ref.watch(currentUserProvider)?.model.user?.mCourierID ?? "0"; + + fetchData() { + ref + .read(PersonalInformationProvider.notifier) + .getData(mCourierID: mCourirID); + } + + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + fetchData(); + }); + return () {}; + }, []); + + ref.listen(PersonalInformationProvider, (previous, next) { + if (next is PersonalInformationStateLoading) { + isLoading.value = true; + } else if (next is PersonalInformationStateError) { + isLoading.value = false; + // print(next.message); + SanckbarWidget( + context, next.message.substring(0, 30), snackbarType.error); + } else if (next is PersonalInformationStateDone) { + isLoading.value = false; + if (next.model.isNotEmpty) { + nikController.text = next.model[0].natStaffNIK.toString(); + } else { + nikController.text = "-"; + } + } + }); + + return Scaffold( + body: ListView( + children: [ + Container( + // color: Colors.white, + height: Constant.getActualY(context: context, y: 56), + ), + Container( + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 10)), + // color: Colors.white, + height: Constant.getActualY(context: context, y: 48), + child: Row( + children: [ + IconButton( + onPressed: () { + Navigator.of(context).pop(); + }, + icon: Icon(Icons.arrow_back_ios_rounded), + ), + Text( + "Informasi Pribadi", + style: Constant.body1(context: context).copyWith( + fontWeight: FontWeight.w600, color: Constant.textPrimary), + ), + ], + ), + ), + Container( + // color: Colors.white, + height: Constant.getActualY(context: context, y: 31), + ), + Container( + margin: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 34)), + child: TextFormField( + style: TextStyle(color: Colors.grey), + enabled: false, + controller: namaController, + decoration: InputDecoration( + labelText: "Nama", + hintText: "Nama", + border: + OutlineInputBorder(borderRadius: BorderRadius.circular(8)), + ), + ), + ), + Container( + height: Constant.getActualY(context: context, y: 24), + ), + Container( + margin: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 34)), + // child: TextFormField( + // style: TextStyle(color: Colors.grey), + // enabled: false, + // controller: emailController, + // decoration: InputDecoration( + // labelText: "Email", + // hintText: "Email", + // border: + // OutlineInputBorder(borderRadius: BorderRadius.circular(8)), + // ), + // ), + child: TextFormField( + style: TextStyle(color: Colors.grey), + enabled: false, + controller: nikController, + decoration: InputDecoration( + labelText: "NIK", + hintText: "NIK", + border: + OutlineInputBorder(borderRadius: BorderRadius.circular(8)), + ), + ), + ), + + SizedBox( + height: Constant.getActualY(context: context, y: 20), + ), + // loading + if (isLoading.value) + const Center( + child: CircularProgressIndicator(), + ), + Container( + height: Constant.getActualY(context: context, y: 32), + ), + Container( + margin: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 34)), + child: OutlinedButton( + onPressed: () { + Navigator.pushNamed(context, changePasswordRoute); + }, + style: OutlinedButton.styleFrom( + shape: RoundedRectangleBorder( + side: BorderSide( + style: BorderStyle.solid, color: Constant.primaryBlue), + borderRadius: BorderRadius.circular(8), + ), + ), + child: SizedBox( + height: Constant.getActualY(context: context, y: 46), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + "Ubah Password", + style: Constant.body3(context: context) + .copyWith(color: Constant.textPrimary), + ), + SizedBox( + width: Constant.getActualX(context: context, x: 8), + ), + Icon( + Icons.arrow_forward_ios_rounded, + color: Constant.textPrimary, + ) + ], + ), + ), + ), + ), + ], + )); + } +} diff --git a/lib/screen/problemlogin/problem_login_screen.dart b/lib/screen/problemlogin/problem_login_screen.dart new file mode 100644 index 0000000..e76b9d5 --- /dev/null +++ b/lib/screen/problemlogin/problem_login_screen.dart @@ -0,0 +1,173 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; + +import '../../app/constant.dart'; +import '../../widget/sas_textfield.dart'; + +class ProblemLoginScreen extends HookConsumerWidget { + const ProblemLoginScreen({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final ctrlEmail = useTextEditingController(text: ""); + final ctrlNote = useTextEditingController(text: ""); + final focusNodeEmail = useFocusNode(); + final focusNodeNote = useFocusNode(); + + final emailhasFocus = useState(false); + final notehasFocus = useState(false); + + focusNodeEmail.addListener(() { + if (focusNodeEmail.hasFocus) { + emailhasFocus.value = true; + notehasFocus.value = false; + } else { + emailhasFocus.value = false; + } + }); + + focusNodeNote.addListener(() { + if (focusNodeNote.hasFocus) { + emailhasFocus.value = false; + notehasFocus.value = true; + } else { + notehasFocus.value = false; + } + }); + return GestureDetector( + onTap: () => FocusManager.instance.primaryFocus?.unfocus(), + child: Scaffold( + resizeToAvoidBottomInset: true, + backgroundColor: Constant.backgroundWhite, + body: SizedBox( + width: Constant.getActualX(context: context, x: 390), + height: Constant.getActualY(context: context, y: 844), + child: ListView( + children: [ + // bg login + Padding( + padding: EdgeInsets.only( + top: Constant.getActualY(context: context, y: 70), + left: Constant.getActualX(context: context, x: 16), + right: Constant.getActualX(context: context, x: 16), + ), + child: Image.asset( + "assets/bg_problem_login.png", + // fit: BoxFit.fill, + // scale: 1, + ), + ), + SizedBox( + height: Constant.getActualY(context: context, y: 27), + ), + Padding( + padding: EdgeInsets.only( + top: Constant.getActualY(context: context, y: 27), + left: Constant.getActualX(context: context, x: 38), + right: Constant.getActualX(context: context, x: 32), + ), + child: SizedBox( + width: Constant.getActualX(context: context, x: 320), + height: Constant.getActualY(context: context, y: 360), + // color: Colors.amber, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Problem Login", + style: Constant.heading2(context: context).copyWith( + color: Constant.textPrimary, + fontWeight: FontWeight.w600), + ), + SizedBox( + height: Constant.getActualY(context: context, y: 32), + ), + // CustomTextFieldLogin( + // onChange: (String x) { + // // print(x); + // }, + // isPassword: false, + // hintText: "Email", + // labelText: "Email", + // isError: false, + // ctrl: ctrlEmail, + // focusNode: focusNodeEmail, + // hasFocus: emailhasFocus.value, + // ), + SasTextField( + hintText: "Email", + labelText: "Email", + controller: ctrlEmail, + focusNode: focusNodeEmail, + hasFocus: emailhasFocus.value, + ), + SizedBox( + height: Constant.getActualY(context: context, y: 16), + ), + // CustomTextFieldLogin( + // onChange: (String x) { + // // print(x); + // }, + // isPassword: false, + // hintText: "Error", + // labelText: "Note", + // isError: false, + // ctrl: ctrlNote, + // focusNode: focusNodeNote, + // hasFocus: notehasFocus.value, + // ), + SasTextField( + hintText: "Error", + labelText: "Error", + controller: ctrlNote, + focusNode: focusNodeNote, + hasFocus: notehasFocus.value, + ), + SizedBox( + height: Constant.getActualY(context: context, y: 20), + ), + // button login + SizedBox( + width: Constant.getActualX(context: context, x: 320), + height: Constant.getActualY(context: context, y: 48), + child: ElevatedButton( + onPressed: () {}, + style: ButtonStyle( + backgroundColor: MaterialStateColor.resolveWith( + (st) => Constant.primaryMain), + shape: MaterialStateProperty.all< + RoundedRectangleBorder>( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + // side: BorderSide(color: Colors.red), + ), + ), + shadowColor: + MaterialStateProperty.all(const Color(0xffff48423d)), + elevation: MaterialStateProperty.all(4.0), + ), + child: Align( + alignment: Alignment.center, + child: Text( + 'Send', + style: Constant.buttonLarge(context: context) + .copyWith( + color: Constant.backgroundWhite, + fontWeight: FontWeight.w700, + ), + ), + ), + ), + ), + ], + ), + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/screen/profile_screen/profile_screen.dart b/lib/screen/profile_screen/profile_screen.dart new file mode 100644 index 0000000..88a61f0 --- /dev/null +++ b/lib/screen/profile_screen/profile_screen.dart @@ -0,0 +1,244 @@ +import 'dart:async'; +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import '/app/route.dart'; +import 'package:loader_overlay/loader_overlay.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import '../../app/constant.dart'; +import '../../models/pending_work_model.dart'; +import '../../models/work_total_model.dart'; +import '../../provider/current_user_provider.dart'; +import '../../widget/header_widget.dart'; +import '../../widget/information_card.dart'; +import '../../widget/information_rpt_card.dart'; +import '../../widget/snackbar_widget.dart'; +import '../home_screen/total_work_provider.dart'; +import '../login/logout_provider.dart'; + +class ProfilScreen extends HookConsumerWidget { + const ProfilScreen({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final totalWork = useState(WorkTotalModel()); + final totalWorkLoading = useState(false); + final mStaffName = + ref.watch(currentUserProvider)?.model.user?.mStaffName ?? "0"; + + final mCourirID = + ref.watch(currentUserProvider)?.model.user?.mCourierID ?? "0"; + final mUsername = + ref.watch(currentUserProvider)?.model.user?.mUserUsername ?? "0"; + + final mUserID = ref.watch(currentUserProvider)?.model.user?.mUserID ?? "0"; + + final logoutLoading = useState(false); + final errorMessage = useState(""); + + // aksi logout + ref.listen(logoutProvider, (prev, next) async { + if (next is LogoutStateLoading) { + logoutLoading.value = true; + context.loaderOverlay.show(); + } else if (next is LogoutStateError) { + logoutLoading.value = false; + errorMessage.value = next.message; + SanckbarWidget(context, next.message, snackbarType.error); + context.loaderOverlay.hide(); + } else if (next is LogoutStateDone) { + logoutLoading.value = false; + final shared = await SharedPreferences.getInstance(); + final token = shared.get(Constant.tokenName).toString(); + // print(token); + if (token.isNotEmpty) { + shared.remove(token); + shared.remove("passwordX"); + shared.remove("usernameX"); + shared.clear(); + // Navigator.popAndPushNamed(context, loginRoute); + SanckbarWidget(context, "Berhasil Sign Out", snackbarType.success); + context.loaderOverlay.hide(); + + Navigator.of(context) + .pushNamedAndRemoveUntil(loginRoute, (route) => false); + } + } + }); + fetchData() { + // ref.read(TotalWorkProvider.notifier).getData(id: mCourirID); + } + + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + fetchData(); + }); + return () {}; + }, []); + + ref.listen( + TotalWorkProvider, + (previous, next) { + if (next is TotalWorkStateLoading) { + totalWorkLoading.value = true; + } else if (next is TotalWorkStateError) { + print(next.message); + totalWorkLoading.value = false; + SanckbarWidget(context, next.message, snackbarType.error); + } else if (next is TotalWorkStateDone) { + // print(jsonEncode(next.model)); + totalWork.value = next.model; + totalWorkLoading.value = false; + } + }, + ); + return RefreshIndicator( + onRefresh: () async { + fetchData(); + }, + child: ListView( + children: [ + SizedBox( + height: Constant.getActualY(context: context, y: 56), + ), + const HeaderWidget(teks: "Profil"), + SizedBox( + height: Constant.getActualY(context: context, y: 31), + ), + Container( + width: Constant.getActualY(context: context, y: 121), + height: Constant.getActualY(context: context, y: 121), + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all(color: Color(0XFF3366FF), width: 2)), + child: Stack( + alignment: Alignment.center, + children: [ + Container( + width: Constant.getActualY(context: context, y: 100), + height: Constant.getActualY(context: context, y: 100), + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all(color: Color(0XFF3366FF), width: 2), + ), + child: Container( + width: Constant.getActualY(context: context, y: 100), + height: Constant.getActualY(context: context, y: 100), + child: CircleAvatar( + backgroundColor: Colors.transparent, + // backgroundImage: NetworkImage( + // "https://images.pexels.com/photos/220453/pexels-photo-220453.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500"), + // ), + child: Icon( + Icons.person, + color: Colors.grey, + size: Constant.getActualY(context: context, y: 80), + ), + ), + ), + // camera commented + // Positioned( + // bottom: 1, + // right: Constant.getActualX(context: context, x: 140), + // child: InkWell( + // onTap: () { + // print("a"); + // // context.loaderOverlay.show(); + // }, + // child: Container( + // width: Constant.getActualY(context: context, y: 34), + // height: Constant.getActualY(context: context, y: 34), + // decoration: BoxDecoration( + // color: Color(0XFF3366FF), + // shape: BoxShape.circle, + // border: Border.all( + // color: Constant.primaryBlue, width: 2)), + // child: Icon( + // Icons.camera_alt_outlined, + // color: Colors.white, + // )), + // ), + ), + ], + ), + ), + SizedBox( + height: Constant.getActualY(context: context, y: 28), + ), + Align( + alignment: Alignment.center, + child: Text( + mStaffName, + style: Constant.body1(context: context) + .copyWith(fontWeight: FontWeight.w600), + )), + SizedBox( + height: Constant.getActualY(context: context, y: 40), + ), + Container( + margin: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 34)), + child: InformationRptCard(mCourirID), + // InformationCard( + // elevation: true, + // data: totalWork.value, + // loading: totalWorkLoading.value, + // ) + ), + SizedBox( + height: Constant.getActualY(context: context, y: 40), + ), + Container( + margin: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 34)), + child: ListTile( + onTap: () { + Navigator.pushNamed(context, personalInformationRoute); + }, + leading: Image.asset( + "assets/icon_profile_profile.png", + width: Constant.getActualX(context: context, x: 25), + height: Constant.getActualY(context: context, y: 25), + ), + trailing: Icon(Icons.arrow_forward_ios_rounded, + color: Constant.textPrimary), + title: Text( + "Informasi Pribadi", + style: Constant.body1(context: context) + .copyWith(fontWeight: FontWeight.w600), + ), + ), + ), + Container( + margin: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 34)), + child: ListTile( + onTap: () { + ref.read(logoutProvider.notifier).logout( + M_UserID: mUserID, + M_UserUsername: mUsername, + ); + }, + leading: Image.asset( + "assets/icon_signout_profile.png", + width: Constant.getActualX(context: context, x: 25), + height: Constant.getActualY(context: context, y: 25), + ), + trailing: null, + title: Text( + "Sign Out", + style: Constant.body1(context: context) + .copyWith(fontWeight: FontWeight.w600), + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/screen/splash_screen/splash_screen.dart b/lib/screen/splash_screen/splash_screen.dart new file mode 100644 index 0000000..e4efc87 --- /dev/null +++ b/lib/screen/splash_screen/splash_screen.dart @@ -0,0 +1,101 @@ +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 '/app/route.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import '../../app/constant.dart'; +import '../../models/auth_model.dart'; +import '../../provider/current_user_provider.dart'; + +class SplashScreen extends HookConsumerWidget { + const SplashScreen({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + // setelah 3 detik redirect ke login route + // Timer(const Duration(seconds: 3), () { + // Navigator.of(context).pushNamedAndRemoveUntil( + // loginRoute, + // (route) => false, + // ); + // }); + useEffect(() { + () async { + final shared = await SharedPreferences.getInstance(); + bool remeberMe = shared.getBool("isRememberMeX") ?? false; + + if (remeberMe) { + final jsonAuth = shared.get(Constant.tokenName).toString(); + final auth = jsonDecode(jsonAuth); + if (auth == null) return; + final authmodel = AuthModel( + token: auth['model']['token'], + model: AuthKurirModel.fromJson(auth['model']), + ); + final token = authmodel.token; + final authKurirModel = authmodel.model; + + final authModel = AuthModel( + token: token, + model: authKurirModel, + ); + + ref.read(currentUserProvider.notifier).state = authModel; + Navigator.of(context) + .pushNamedAndRemoveUntil(menuRoute, (route) => false); + return; + } else { + Navigator.of(context) + .pushNamedAndRemoveUntil(loginRoute, (route) => false); + return; + } + }(); + }, []); + + return Scaffold( + backgroundColor: Constant.backgroundWhite, + body: SafeArea( + child: Column( + children: [ + Padding( + padding: EdgeInsets.only( + top: Constant.getActualY(context: context, y: 103), + left: Constant.getActualX(context: context, x: 69), + right: Constant.getActualX(context: context, x: 70), + bottom: Constant.getActualY(context: context, y: 92)), + child: SizedBox( + width: Constant.getActualX(context: context, x: 251), + height: Constant.getActualY(context: context, y: 78), + child: Image.asset( + "assets/logo_kdr.png", + fit: BoxFit.fitHeight, + // scale: 1, + ), + ), + ), + Padding( + padding: EdgeInsets.only( + left: Constant.getActualX(context: context, x: 30), + right: Constant.getActualX(context: context, x: 29), + ), + child: SizedBox( + width: Constant.getActualX(context: context, x: 331), + height: Constant.getActualY(context: context, y: 366), + child: Image.asset( + "assets/bg_splashscreen.png", + fit: BoxFit.fitHeight, + // scale: 1, + ), + // color: Colors.green, + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/screen/test/test_home_screen.dart b/lib/screen/test/test_home_screen.dart new file mode 100644 index 0000000..4c2f530 --- /dev/null +++ b/lib/screen/test/test_home_screen.dart @@ -0,0 +1,129 @@ +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 '../../app/constant.dart'; +import '../../app/route.dart'; +import '../../models/auth_model.dart'; +import '../../provider/current_user_provider.dart'; +import '../login/logout_provider.dart'; + +class TestHomeScreen extends HookConsumerWidget { + const TestHomeScreen({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + // useEffect(() { + // WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + // Timer(const Duration(milliseconds: 500), () async { + // final shared = await SharedPreferences.getInstance(); + // final jsonAuth = shared.get(Constant.tokenName).toString(); + // final auth = jsonDecode(jsonAuth); + // if (auth == null) return; + + // final mUserID = + // ref.watch(currentUserProvider)?.model.user?.mUserID ?? "0"; + + // if (mUserID == "0") { + // Navigator.of(context) + // .pushNamedAndRemoveUntil(loginRoute, (route) => false); + // return; + // } + // }); + // }); + // return () {}; + // }, []); + + final mUsername = + ref.watch(currentUserProvider)?.model.user?.mUserUsername ?? "0"; + + final mUserID = ref.watch(currentUserProvider)?.model.user?.mUserID ?? "0"; + + final isLoading = useState(false); + final errorMessage = useState(""); + + // aksi logout + ref.listen(logoutProvider, (prev, next) async { + if (next is LogoutStateLoading) { + isLoading.value = true; + } else if (next is LogoutStateError) { + isLoading.value = false; + errorMessage.value = next.message; + Timer(const Duration(seconds: 3), () { + errorMessage.value = ""; + }); + } else if (next is LogoutStateDone) { + isLoading.value = false; + final shared = await SharedPreferences.getInstance(); + final token = shared.get(Constant.tokenName).toString(); + // print(token); + if (token.isNotEmpty) { + shared.remove(token); + shared.remove("passwordX"); + shared.remove("usernameX"); + shared.clear(); + // Navigator.popAndPushNamed(context, loginRoute); + + Navigator.of(context) + .pushNamedAndRemoveUntil(loginRoute, (route) => false); + } + } + }); + + return Scaffold( + appBar: AppBar( + title: const Text('Test Home Screen'), + ), + body: SafeArea( + child: ListView( + children: [ + Text('Username $mUsername'), + Text('user id $mUserID'), + const SizedBox( + height: 20, + ), + SizedBox( + width: Constant.getActualX(context: context, x: 320), + height: Constant.getActualY(context: context, y: 48), + child: ElevatedButton( + onPressed: () { + ref.read(logoutProvider.notifier).logout( + M_UserID: mUserID, + M_UserUsername: mUsername, + ); + }, + style: ButtonStyle( + backgroundColor: MaterialStateColor.resolveWith( + (st) => Constant.primaryMain), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + // side: BorderSide(color: Colors.red), + ), + ), + shadowColor: + MaterialStateProperty.all(const Color(0xffff48423d)), + elevation: MaterialStateProperty.all(4.0), + ), + child: Align( + alignment: Alignment.center, + child: Text( + 'Logout', + style: Constant.buttonLarge(context: context).copyWith( + color: Constant.backgroundWhite, + fontWeight: FontWeight.w700, + ), + ), + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/screen/work_list_screen/work_list_provider.dart b/lib/screen/work_list_screen/work_list_provider.dart new file mode 100644 index 0000000..b55aab2 --- /dev/null +++ b/lib/screen/work_list_screen/work_list_provider.dart @@ -0,0 +1,70 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '/models/work_list_model.dart'; +import '../../repository/work_list_repository.dart'; + +import '../../models/pending_work_model.dart'; +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; +import '../../repository/home_screen_repository.dart'; + +abstract class WorkListState extends Equatable { + final DateTime date; + const WorkListState(this.date); + @override + List get props => [date]; +} + +class WorkListStateInit extends WorkListState { + WorkListStateInit() : super(DateTime.now()); +} + +class WorkListStateLoading extends WorkListState { + WorkListStateLoading() : super(DateTime.now()); +} + +class WorkListStateError extends WorkListState { + final String message; + WorkListStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class WorkListStateDone extends WorkListState { + final List model; + WorkListStateDone({ + required this.model, + }) : super(DateTime.now()); +} + +//notifier +class WorkListNotifier extends StateNotifier { + final Ref ref; + WorkListNotifier({ + required this.ref, + }) : super(WorkListStateInit()); + + void getData( + {required String id, + required String tipeId, + required String supervisor, + required String status}) async { + try { + state = WorkListStateLoading(); + final dio = ref.read(dioProvider); + final resp = await WorkListRepository(dio: dio).getListWork( + id: id, status: status, supervisor: supervisor, tipeId: tipeId); + state = WorkListStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = WorkListStateError(message: e.message.toString()); + } else { + state = WorkListStateError(message: e.toString()); + } + } + } +} + +//provider +final WorkListProvider = StateNotifierProvider( + (ref) => WorkListNotifier(ref: ref)); diff --git a/lib/screen/work_list_screen/work_list_screen.dart b/lib/screen/work_list_screen/work_list_screen.dart new file mode 100644 index 0000000..380a58d --- /dev/null +++ b/lib/screen/work_list_screen/work_list_screen.dart @@ -0,0 +1,547 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import '/models/pending_work_model.dart'; +import '/models/work_list_model.dart'; +import '/models/work_type_model.dart'; +import '/widget/snackbar_widget.dart'; +import '/widget/work_card.dart'; +import '/screen/work_list_screen/work_list_provider.dart'; +import '/screen/work_list_screen/work_status_provider.dart'; +import '/screen/work_list_screen/work_supervisor_moder.dart'; +import '/screen/work_list_screen/work_type_provider.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; + +import '../../app/constant.dart'; +import '../../models/work_status_model.dart'; +import '../../models/work_supervisor_model.dart'; +import '../../provider/current_user_provider.dart'; +import '../../widget/header_widget.dart'; + +// ignore: camel_case_types +enum arrStatus { baru, pending, done } + +class WorkListScreen extends HookConsumerWidget { + const WorkListScreen({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final selectedStatus = useState(""); + final selectedTipe = useState>(List.empty()); + final selectedSupervisor = useState>(List.empty()); + final workTypeData = useState>([]); + final workTypeLoading = useState(false); + final workStatusData = useState>([]); + final workStatusLoading = useState(false); + final workSupervisorData = useState>([]); + final workSupervisorLoading = useState(false); + final workListData = useState>([]); + final workListLoading = useState(false); + final change = useState(1); + + final mCourirID = + ref.watch(currentUserProvider)?.model.user?.mCourierID ?? "0"; + fetchData() { + ref.read(WorkTypeProvider.notifier).getData(); + ref.read(WorkStatusProvider.notifier).getData(); + ref.read(WorkSupervisorProvider.notifier).getData(); + // ref + // .read(WorkListProvider.notifier) + // .getData(id: mCourirID, tipeId: "x", status: "N-p", supervisor: "N"); + ref.read(WorkListProvider.notifier).getData( + id: mCourirID, + tipeId: "1-2-3-4-5", + status: "N'-'P'-'D", + supervisor: "N'-'Y'-'X"); + } + + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + fetchData(); + }); + return () {}; + }, []); + + filterData() { + var status = + selectedStatus.value.isNotEmpty ? selectedStatus.value : "N'-'P'-'D"; + var supervisor = selectedSupervisor.value.isNotEmpty + ? selectedSupervisor.value.join("'-'") + : "N'-'Y'-'X"; + var tipe = selectedTipe.value.isNotEmpty + ? selectedTipe.value.join('-') + : "1-2-3-4-5"; + // print("selected status value : ${status}"); + // print("selected supervisor value :" + supervisor); + // print("selected tipe value :" + tipe); + ref.read(WorkListProvider.notifier).getData( + id: mCourirID, tipeId: tipe, status: status, supervisor: supervisor); + } + + ref.listen(WorkTypeProvider, (previous, next) { + if (next is WorkTypeStateLoading) { + workTypeLoading.value = true; + } else if (next is WorkTypeStateError) { + workTypeLoading.value = false; + // print(next.message); + SanckbarWidget(context, next.message, snackbarType.error); + } else if (next is WorkTypeStateDone) { + workTypeLoading.value = false; + workTypeData.value = next.model; + } + }); + ref.listen(WorkStatusProvider, (previous, next) { + if (next is WorkStatusStateLoading) { + workStatusLoading.value = true; + } else if (next is WorkStatusStateError) { + workStatusLoading.value = false; + // print(next.message); + SanckbarWidget(context, next.message, snackbarType.error); + } else if (next is WorkStatusStateDone) { + // print(jsonEncode(next.model)); + workStatusLoading.value = false; + var newData = next.model; + var newStatus = []; + next.model.forEach((e) { + newStatus.add(e.statusid); + }); + newData.insert( + 0, + WorkStatusModel( + statusid: newStatus.join("'-'"), statusname: "Semua")); + selectedStatus.value = newStatus.join("'-'"); + workStatusData.value = newData; + } + }); + ref.listen(WorkSupervisorProvider, (previous, next) { + if (next is WorkSupervisorStateLoading) { + workSupervisorLoading.value = true; + } else if (next is WorkSupervisorStateError) { + workSupervisorLoading.value = false; + // print(next.message); + SanckbarWidget(context, next.message, snackbarType.error); + } else if (next is WorkSupervisorStateDone) { + // print(jsonEncode(next.model)); + workSupervisorLoading.value = false; + workSupervisorData.value = next.model; + } + }); + ref.listen(WorkListProvider, (previous, next) { + if (next is WorkListStateLoading) { + workListLoading.value = true; + } else if (next is WorkListStateError) { + workListLoading.value = false; + // print(next.message); + + SanckbarWidget(context, next.message, snackbarType.error); + } else if (next is WorkListStateDone) { + // print(jsonEncode(next.model)); + workListLoading.value = false; + workListData.value = next.model; + } + }); + selectedStatus.addListener(() { + // print("change"); + change.value = change.value++; + }); + return RefreshIndicator( + onRefresh: () async { + filterData(); + }, + child: Container( + color: const Color(0XFFF4F6F8), + child: Column( + children: [ + Container( + color: Colors.white, + height: Constant.getActualY(context: context, y: 56), + ), + const HeaderWidget( + teks: "List Pekerjaan", + ), + Row( + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + SizedBox( + // color: Colors.red, + child: InkWell( + // splashColor: Colors.red, + onTap: () { + showDialog( + context: context, + barrierDismissible: false, + builder: (context) { + return AlertDialog( + content: HookBuilder(builder: (context) { + final a = useState(1); + return SizedBox( + width: Constant.getActualX( + context: context, x: 280), + height: Constant.getActualY( + context: context, y: 692), + child: Column( + children: [ + SizedBox( + height: Constant.getActualY( + context: context, y: 36), + width: Constant.getActualX( + context: context, x: 280), + // color: Colors.red, + child: Row( + // crossAxisAlignment: + // CrossAxisAlignment.end, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + "Filter", + style: Constant.body1( + context: context) + .copyWith( + fontWeight: + FontWeight.w600, + color: Constant + .textPrimary), + ), + IconButton( + iconSize: 20, + splashRadius: 20, + onPressed: () { + filterData(); + Navigator.pop(context); + }, + icon: const Icon(Icons.clear)) + ], + ), + ), + const Divider(), + SizedBox( + height: Constant.getActualY( + context: context, y: 24), + ), + Container( + height: Constant.getActualY( + context: context, y: 590), + margin: EdgeInsets.only( + left: Constant.getActualX( + context: context, x: 48)), + // color: Colors.red, + child: ListView( + children: [ + Text( + "Tipe", + style: Constant.body1( + context: context) + .copyWith( + fontWeight: + FontWeight.w600, + color: Constant + .textPrimary), + ), + SizedBox( + height: Constant.getActualY( + context: context, y: 8), + ), + workTypeLoading.value + ? LoadingAnimationWidget + .progressiveDots( + color: Constant + .primaryBlue, + size: 50) + : Column( + children: + workTypeData.value + .map( + (e) => SizedBox( + child: Row( + children: [ + Checkbox( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + 5)), + value: selectedTipe.value.contains(e + .id), + onChanged: + (value) { + var sp = + selectedTipe.value.toSet().toList(); + if (value == + true) { + sp.add(int.parse(e.id.toString())); + } else { + sp.remove(int.parse(e.id.toString())); + } + selectedTipe.value = + sp; + // print(value); + // print(e.id); + + a.value = + a.value++; + }), + SizedBox( + height: Constant.getActualY( + context: + context, + y: 8), + ), + Expanded( + child: + Text( + e.name + .toString(), + maxLines: + 2, + overflow: + TextOverflow.ellipsis, + style: Constant.body3(context: context).copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ), + ], + ), + ), + ) + .toList(), + ), + SizedBox( + height: Constant.getActualY( + context: context, y: 24), + ), + Text( + "Status", + style: Constant.body1( + context: context) + .copyWith( + fontWeight: + FontWeight.w600, + color: Constant + .textPrimary), + ), + SizedBox( + height: Constant.getActualY( + context: context, y: 8), + ), + workStatusLoading.value + ? LoadingAnimationWidget + .progressiveDots( + color: Constant + .primaryBlue, + size: 50) + : Column( + children: workStatusData + .value + .map((e) { + return SizedBox( + child: Row( + children: [ + Radio( + value: e + .statusid, + groupValue: + selectedStatus + .value, + onChanged: + (baru) { + selectedStatus + .value = + baru!; + + a.value = a + .value++; + }), + SizedBox( + height: Constant + .getActualY( + context: + context, + y: 8), + ), + Text( + "${e.statusname}", + style: Constant.body3( + context: + context) + .copyWith( + fontWeight: + FontWeight + .w400, + color: Constant + .textPrimary), + ), + ], + ), + ); + }).toList(), + ), + SizedBox( + height: Constant.getActualY( + context: context, y: 24), + ), + Text( + "Supervisor", + style: Constant.body1( + context: context) + .copyWith( + fontWeight: + FontWeight.w600, + color: Constant + .textPrimary), + ), + SizedBox( + height: Constant.getActualY( + context: context, y: 8), + ), + workSupervisorLoading.value + ? LoadingAnimationWidget + .progressiveDots( + color: Constant + .primaryBlue, + size: 50) + : Column( + children: + workSupervisorData + .value + .map( + (e) => SizedBox( + child: Row( + children: [ + Checkbox( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + 5)), + value: selectedSupervisor.value.contains(e + .supervisorid), + onChanged: + (value) { + var sp = + selectedSupervisor.value.toSet().toList(); + if (value == + true) { + sp.add(e.supervisorid!); + } else { + sp.remove(e.supervisorid!); + } + selectedSupervisor.value = + sp; + // print(value); + // print(e.supervisorid); + + a.value = + a.value++; + }), + SizedBox( + height: Constant.getActualY( + context: + context, + y: 8), + ), + Flexible( + child: + Text( + e.supervisorname + .toString(), + style: Constant.body3(context: context).copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + maxLines: + 5, + overflow: + TextOverflow.ellipsis, + ), + ), + SizedBox( + height: Constant.getActualY( + context: + context, + y: 8), + ), + if (e.supervisorid == + "Y") + Icon( + size: + 25, + Icons + .check_circle_outline_rounded, + color: + Constant.primaryGreen, + ), + if (e.supervisorid == + 'N') + Icon( + size: + 25, + Icons + .help_outline_rounded, + color: + Constant.primaryYellow, + ), + if (e.supervisorid == + "X") + Icon( + size: + 25, + Icons + .cancel_outlined, + color: + Constant.primaryRed, + ) + ], + ), + ), + ) + .toList(), + ), + ], + ), + ) + ], + ), + ); + }), + ); + }, + ); + }, + child: Row( + children: [ + Text( + "Filters", + style: Constant.caption1(context: context) + .copyWith(color: Constant.textPrimary), + ), + Icon( + size: + Constant.getActualY(context: context, y: 20), + Icons.filter_alt_outlined, + color: Constant.textPrimary, + ) + ], + ))), + ], + ), + Container( + height: Constant.getActualY(context: context, y: 641), + // color: Colors.red, + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 32), + vertical: 1), + child: workListLoading.value + ? LoadingAnimationWidget.fourRotatingDots( + color: Constant.primaryBlue, size: 50) + : ListView.builder( + itemCount: workListData.value.length, + itemBuilder: (context, index) { + var data = workListData.value[index]; + return WorkCardWidget(data: data); + }, + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/screen/work_list_screen/work_status_provider.dart b/lib/screen/work_list_screen/work_status_provider.dart new file mode 100644 index 0000000..03ed843 --- /dev/null +++ b/lib/screen/work_list_screen/work_status_provider.dart @@ -0,0 +1,65 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../repository/filter_repository.dart'; + +import '../../models/work_status_model.dart'; + +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; + +abstract class WorkStatusState extends Equatable { + final DateTime date; + const WorkStatusState(this.date); + @override + List get props => [date]; +} + +class WorkStatusStateInit extends WorkStatusState { + WorkStatusStateInit() : super(DateTime.now()); +} + +class WorkStatusStateLoading extends WorkStatusState { + WorkStatusStateLoading() : super(DateTime.now()); +} + +class WorkStatusStateError extends WorkStatusState { + final String message; + WorkStatusStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class WorkStatusStateDone extends WorkStatusState { + final List model; + WorkStatusStateDone({ + required this.model, + }) : super(DateTime.now()); +} + +//notifier +class WorkStatusNotifier extends StateNotifier { + final Ref ref; + WorkStatusNotifier({ + required this.ref, + }) : super(WorkStatusStateInit()); + + void getData() async { + try { + state = WorkStatusStateLoading(); + final dio = ref.read(dioProvider); + final resp = await FilterRepository(dio: dio).getStatus(); + state = WorkStatusStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = WorkStatusStateError(message: e.message.toString()); + } else { + state = WorkStatusStateError(message: e.toString()); + } + } + } +} + +//provider +final WorkStatusProvider = + StateNotifierProvider( + (ref) => WorkStatusNotifier(ref: ref)); diff --git a/lib/screen/work_list_screen/work_supervisor_moder.dart b/lib/screen/work_list_screen/work_supervisor_moder.dart new file mode 100644 index 0000000..9593fc3 --- /dev/null +++ b/lib/screen/work_list_screen/work_supervisor_moder.dart @@ -0,0 +1,64 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../repository/filter_repository.dart'; + +import '../../models/work_supervisor_model.dart'; +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; + +abstract class WorkSupervisorState extends Equatable { + final DateTime date; + const WorkSupervisorState(this.date); + @override + List get props => [date]; +} + +class WorkSupervisorStateInit extends WorkSupervisorState { + WorkSupervisorStateInit() : super(DateTime.now()); +} + +class WorkSupervisorStateLoading extends WorkSupervisorState { + WorkSupervisorStateLoading() : super(DateTime.now()); +} + +class WorkSupervisorStateError extends WorkSupervisorState { + final String message; + WorkSupervisorStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class WorkSupervisorStateDone extends WorkSupervisorState { + final List model; + WorkSupervisorStateDone({ + required this.model, + }) : super(DateTime.now()); +} + +//notifier +class WorkSupervisorNotifier extends StateNotifier { + final Ref ref; + WorkSupervisorNotifier({ + required this.ref, + }) : super(WorkSupervisorStateInit()); + + void getData() async { + try { + state = WorkSupervisorStateLoading(); + final dio = ref.read(dioProvider); + final resp = await FilterRepository(dio: dio).getSupervisor(); + state = WorkSupervisorStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = WorkSupervisorStateError(message: e.message.toString()); + } else { + state = WorkSupervisorStateError(message: e.toString()); + } + } + } +} + +//provider +final WorkSupervisorProvider = + StateNotifierProvider( + (ref) => WorkSupervisorNotifier(ref: ref)); diff --git a/lib/screen/work_list_screen/work_type_provider.dart b/lib/screen/work_list_screen/work_type_provider.dart new file mode 100644 index 0000000..1b79567 --- /dev/null +++ b/lib/screen/work_list_screen/work_type_provider.dart @@ -0,0 +1,65 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../repository/filter_repository.dart'; + +import '../../models/pending_work_model.dart'; +import '../../models/work_type_model.dart'; +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; +import '../../repository/home_screen_repository.dart'; + +abstract class WorkTypeState extends Equatable { + final DateTime date; + const WorkTypeState(this.date); + @override + List get props => [date]; +} + +class WorkTypeStateInit extends WorkTypeState { + WorkTypeStateInit() : super(DateTime.now()); +} + +class WorkTypeStateLoading extends WorkTypeState { + WorkTypeStateLoading() : super(DateTime.now()); +} + +class WorkTypeStateError extends WorkTypeState { + final String message; + WorkTypeStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class WorkTypeStateDone extends WorkTypeState { + final List model; + WorkTypeStateDone({ + required this.model, + }) : super(DateTime.now()); +} + +//notifier +class WorkTypeNotifier extends StateNotifier { + final Ref ref; + WorkTypeNotifier({ + required this.ref, + }) : super(WorkTypeStateInit()); + + void getData() async { + try { + state = WorkTypeStateLoading(); + final dio = ref.read(dioProvider); + final resp = await FilterRepository(dio: dio).getType(); + state = WorkTypeStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = WorkTypeStateError(message: e.message.toString()); + } else { + state = WorkTypeStateError(message: e.toString()); + } + } + } +} + +//provider +final WorkTypeProvider = StateNotifierProvider( + (ref) => WorkTypeNotifier(ref: ref)); diff --git a/lib/screen/work_process_screen/detail_work_provider.dart b/lib/screen/work_process_screen/detail_work_provider.dart new file mode 100644 index 0000000..72ea256 --- /dev/null +++ b/lib/screen/work_process_screen/detail_work_provider.dart @@ -0,0 +1,74 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '/models/detail_history_model.dart'; +import '/models/detail_work_model.dart'; +import '/models/history_model.dart'; +import '../../repository/history_screen_repository.dart'; +import '../../repository/work_list_repository.dart'; +import '/screen/history_screen/history_screen.dart'; + +import '../../models/pending_work_model.dart'; +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; +import '../../repository/home_screen_repository.dart'; + +abstract class DetailWorkState extends Equatable { + final DateTime date; + const DetailWorkState(this.date); + @override + List get props => [date]; +} + +class DetailWorkStateInit extends DetailWorkState { + DetailWorkStateInit() : super(DateTime.now()); +} + +class DetailWorkStateLoading extends DetailWorkState { + DetailWorkStateLoading() : super(DateTime.now()); +} + +class DetailWorkStateError extends DetailWorkState { + final String message; + DetailWorkStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class DetailWorkStateDone extends DetailWorkState { + final DetailWorkModel model; + DetailWorkStateDone({ + required this.model, + }) : super(DateTime.now()); +} + +//notifier +class DetailWorkNotifier extends StateNotifier { + final Ref ref; + DetailWorkNotifier({ + required this.ref, + }) : super(DetailWorkStateInit()); + + void getData( + {required String id, + required String tipeId, + required String deliveryId}) async { + try { + state = DetailWorkStateLoading(); + final dio = ref.read(dioProvider); + final resp = await WorkListRepository(dio: dio) + .getDetail(id: id, tipeId: tipeId, deliveryId: deliveryId); + state = DetailWorkStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = DetailWorkStateError(message: e.message.toString()); + } else { + state = DetailWorkStateError(message: e.toString()); + } + } + } +} + +//provider +final DetailWorkProvider = + StateNotifierProvider( + (ref) => DetailWorkNotifier(ref: ref)); diff --git a/lib/screen/work_process_screen/other_deliver_person_provider.dart b/lib/screen/work_process_screen/other_deliver_person_provider.dart new file mode 100644 index 0000000..9d6c13c --- /dev/null +++ b/lib/screen/work_process_screen/other_deliver_person_provider.dart @@ -0,0 +1,71 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../repository/get_other_repository.dart'; + +import '../../repository/result_delivery_repository.dart'; + +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; + +abstract class OtherDeliverPersonState extends Equatable { + final DateTime date; + const OtherDeliverPersonState(this.date); + @override + List get props => [date]; +} + +class OtherDeliverPersonStateInit extends OtherDeliverPersonState { + OtherDeliverPersonStateInit() : super(DateTime.now()); +} + +class OtherDeliverPersonStateLoading extends OtherDeliverPersonState { + OtherDeliverPersonStateLoading() : super(DateTime.now()); +} + +class OtherDeliverPersonStateError extends OtherDeliverPersonState { + final String message; + OtherDeliverPersonStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class OtherDeliverPersonStateDone extends OtherDeliverPersonState { + final bool model; + OtherDeliverPersonStateDone({ + required this.model, + }) : super(DateTime.now()); +} + +//notifier +class OtherDeliverPersonNotifier + extends StateNotifier { + final Ref ref; + OtherDeliverPersonNotifier({ + required this.ref, + }) : super(OtherDeliverPersonStateInit()); + + void setData({ + required String id, + required String name, + required String note, + }) async { + try { + state = OtherDeliverPersonStateLoading(); + final dio = ref.read(dioProvider); + final resp = await GetOtherRepository(dio: dio) + .setDeliveryPerson(id: id, name: name, note: note); + state = OtherDeliverPersonStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = OtherDeliverPersonStateError(message: e.message.toString()); + } else { + state = OtherDeliverPersonStateError(message: e.toString()); + } + } + } +} + +//provider +final OtherDeliverPersonProvider = + StateNotifierProvider( + (ref) => OtherDeliverPersonNotifier(ref: ref)); diff --git a/lib/screen/work_process_screen/other_delivery_location_provider.dart b/lib/screen/work_process_screen/other_delivery_location_provider.dart new file mode 100644 index 0000000..d03c835 --- /dev/null +++ b/lib/screen/work_process_screen/other_delivery_location_provider.dart @@ -0,0 +1,78 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../repository/get_other_repository.dart'; +import '../../repository/get_sample_repository.dart'; +import '../../repository/result_delivery_repository.dart'; + +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; + +abstract class OtherDeliveryLocationState extends Equatable { + final DateTime date; + const OtherDeliveryLocationState(this.date); + @override + List get props => [date]; +} + +class OtherDeliveryLocationStateInit extends OtherDeliveryLocationState { + OtherDeliveryLocationStateInit() : super(DateTime.now()); +} + +class OtherDeliveryLocationStateLoading extends OtherDeliveryLocationState { + OtherDeliveryLocationStateLoading() : super(DateTime.now()); +} + +class OtherDeliveryLocationStateError extends OtherDeliveryLocationState { + final String message; + OtherDeliveryLocationStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class OtherDeliveryLocationStateDone extends OtherDeliveryLocationState { + final bool model; + OtherDeliveryLocationStateDone({ + required this.model, + }) : super(DateTime.now()); +} + +//notifier +class OtherDeliveryLocationNotifier + extends StateNotifier { + final Ref ref; + OtherDeliveryLocationNotifier({ + required this.ref, + }) : super(OtherDeliveryLocationStateInit()); + + void setData( + {required String id, + required String step, + required String lat, + required String long, + required String address, + required String branchID}) async { + try { + state = OtherDeliveryLocationStateLoading(); + final dio = ref.read(dioProvider); + final resp = await GetOtherRepository(dio: dio).setLocation( + id: id, + step: step, + lat: lat, + long: long, + address: address, + branchID: branchID); + state = OtherDeliveryLocationStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = OtherDeliveryLocationStateError(message: e.message.toString()); + } else { + state = OtherDeliveryLocationStateError(message: e.toString()); + } + } + } +} + +//provider +final OtherDeliveryLocationProvider = StateNotifierProvider< + OtherDeliveryLocationNotifier, OtherDeliveryLocationState>( + (ref) => OtherDeliveryLocationNotifier(ref: ref)); diff --git a/lib/screen/work_process_screen/other_receive_person_provider.dart b/lib/screen/work_process_screen/other_receive_person_provider.dart new file mode 100644 index 0000000..492c5e3 --- /dev/null +++ b/lib/screen/work_process_screen/other_receive_person_provider.dart @@ -0,0 +1,71 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../repository/get_other_repository.dart'; + +import '../../repository/result_delivery_repository.dart'; + +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; + +abstract class OtherReceivePersonState extends Equatable { + final DateTime date; + const OtherReceivePersonState(this.date); + @override + List get props => [date]; +} + +class OtherReceivePersonStateInit extends OtherReceivePersonState { + OtherReceivePersonStateInit() : super(DateTime.now()); +} + +class OtherReceivePersonStateLoading extends OtherReceivePersonState { + OtherReceivePersonStateLoading() : super(DateTime.now()); +} + +class OtherReceivePersonStateError extends OtherReceivePersonState { + final String message; + OtherReceivePersonStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class OtherReceivePersonStateDone extends OtherReceivePersonState { + final bool model; + OtherReceivePersonStateDone({ + required this.model, + }) : super(DateTime.now()); +} + +//notifier +class OtherReceivePersonNotifier + extends StateNotifier { + final Ref ref; + OtherReceivePersonNotifier({ + required this.ref, + }) : super(OtherReceivePersonStateInit()); + + void setData({ + required String id, + required String name, + required String note, + }) async { + try { + state = OtherReceivePersonStateLoading(); + final dio = ref.read(dioProvider); + final resp = await GetOtherRepository(dio: dio) + .setReceivePerson(id: id, name: name, note: note); + state = OtherReceivePersonStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = OtherReceivePersonStateError(message: e.message.toString()); + } else { + state = OtherReceivePersonStateError(message: e.toString()); + } + } + } +} + +//provider +final OtherReceivePersonProvider = + StateNotifierProvider( + (ref) => OtherReceivePersonNotifier(ref: ref)); diff --git a/lib/screen/work_process_screen/result_deliver_person_provider.dart b/lib/screen/work_process_screen/result_deliver_person_provider.dart new file mode 100644 index 0000000..e4c9fe8 --- /dev/null +++ b/lib/screen/work_process_screen/result_deliver_person_provider.dart @@ -0,0 +1,70 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../repository/result_delivery_repository.dart'; + +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; + +abstract class ResultDeliverPersonState extends Equatable { + final DateTime date; + const ResultDeliverPersonState(this.date); + @override + List get props => [date]; +} + +class ResultDeliverPersonStateInit extends ResultDeliverPersonState { + ResultDeliverPersonStateInit() : super(DateTime.now()); +} + +class ResultDeliverPersonStateLoading extends ResultDeliverPersonState { + ResultDeliverPersonStateLoading() : super(DateTime.now()); +} + +class ResultDeliverPersonStateError extends ResultDeliverPersonState { + final String message; + ResultDeliverPersonStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class ResultDeliverPersonStateDone extends ResultDeliverPersonState { + final bool model; + ResultDeliverPersonStateDone({ + required this.model, + }) : super(DateTime.now()); +} + +//notifier +class ResultDeliverPersonNotifier + extends StateNotifier { + final Ref ref; + ResultDeliverPersonNotifier({ + required this.ref, + }) : super(ResultDeliverPersonStateInit()); + + void setData({ + required String id, + required String name, + required String note, + }) async { + try { + state = ResultDeliverPersonStateLoading(); + final dio = ref.read(dioProvider); + final resp = await ResultDeliveryRepository(dio: dio) + .setDeliveryPerson(id: id, name: name, note: note); + state = ResultDeliverPersonStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = ResultDeliverPersonStateError(message: e.message.toString()); + } else { + state = ResultDeliverPersonStateError(message: e.toString()); + } + } + } +} + +//provider +final ResultDeliverPersonProvider = StateNotifierProvider< + ResultDeliverPersonNotifier, + ResultDeliverPersonState>((ref) => ResultDeliverPersonNotifier(ref: ref)); diff --git a/lib/screen/work_process_screen/result_delivery_location_provider.dart b/lib/screen/work_process_screen/result_delivery_location_provider.dart new file mode 100644 index 0000000..ce715af --- /dev/null +++ b/lib/screen/work_process_screen/result_delivery_location_provider.dart @@ -0,0 +1,76 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../repository/result_delivery_repository.dart'; + +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; + +abstract class ResultDeliveryLocationState extends Equatable { + final DateTime date; + const ResultDeliveryLocationState(this.date); + @override + List get props => [date]; +} + +class ResultDeliveryLocationStateInit extends ResultDeliveryLocationState { + ResultDeliveryLocationStateInit() : super(DateTime.now()); +} + +class ResultDeliveryLocationStateLoading extends ResultDeliveryLocationState { + ResultDeliveryLocationStateLoading() : super(DateTime.now()); +} + +class ResultDeliveryLocationStateError extends ResultDeliveryLocationState { + final String message; + ResultDeliveryLocationStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class ResultDeliveryLocationStateDone extends ResultDeliveryLocationState { + final bool model; + ResultDeliveryLocationStateDone({ + required this.model, + }) : super(DateTime.now()); +} + +//notifier +class ResultDeliveryLocationNotifier + extends StateNotifier { + final Ref ref; + ResultDeliveryLocationNotifier({ + required this.ref, + }) : super(ResultDeliveryLocationStateInit()); + + void setData( + {required String id, + required String step, + required String lat, + required String long, + required String address, + required String branchID}) async { + try { + state = ResultDeliveryLocationStateLoading(); + final dio = ref.read(dioProvider); + final resp = await ResultDeliveryRepository(dio: dio).setLocation( + id: id, + step: step, + lat: lat, + long: long, + address: address, + branchID: branchID); + state = ResultDeliveryLocationStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = ResultDeliveryLocationStateError(message: e.message.toString()); + } else { + state = ResultDeliveryLocationStateError(message: e.toString()); + } + } + } +} + +//provider +final ResultDeliveryLocationProvider = StateNotifierProvider< + ResultDeliveryLocationNotifier, ResultDeliveryLocationState>( + (ref) => ResultDeliveryLocationNotifier(ref: ref)); diff --git a/lib/screen/work_process_screen/result_receive_person_provider.dart b/lib/screen/work_process_screen/result_receive_person_provider.dart new file mode 100644 index 0000000..e57f5be --- /dev/null +++ b/lib/screen/work_process_screen/result_receive_person_provider.dart @@ -0,0 +1,70 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../repository/result_delivery_repository.dart'; + +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; + +abstract class ResultReceivePersonState extends Equatable { + final DateTime date; + const ResultReceivePersonState(this.date); + @override + List get props => [date]; +} + +class ResultReceivePersonStateInit extends ResultReceivePersonState { + ResultReceivePersonStateInit() : super(DateTime.now()); +} + +class ResultReceivePersonStateLoading extends ResultReceivePersonState { + ResultReceivePersonStateLoading() : super(DateTime.now()); +} + +class ResultReceivePersonStateError extends ResultReceivePersonState { + final String message; + ResultReceivePersonStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class ResultReceivePersonStateDone extends ResultReceivePersonState { + final bool model; + ResultReceivePersonStateDone({ + required this.model, + }) : super(DateTime.now()); +} + +//notifier +class ResultReceivePersonNotifier + extends StateNotifier { + final Ref ref; + ResultReceivePersonNotifier({ + required this.ref, + }) : super(ResultReceivePersonStateInit()); + + void setData({ + required String id, + required String name, + required String note, + }) async { + try { + state = ResultReceivePersonStateLoading(); + final dio = ref.read(dioProvider); + final resp = await ResultDeliveryRepository(dio: dio) + .setReceivePerson(id: id, name: name, note: note); + state = ResultReceivePersonStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = ResultReceivePersonStateError(message: e.message.toString()); + } else { + state = ResultReceivePersonStateError(message: e.toString()); + } + } + } +} + +//provider +final ResultReceivePersonProvider = StateNotifierProvider< + ResultReceivePersonNotifier, + ResultReceivePersonState>((ref) => ResultReceivePersonNotifier(ref: ref)); diff --git a/lib/screen/work_process_screen/sample_deliver_person_provider.dart b/lib/screen/work_process_screen/sample_deliver_person_provider.dart new file mode 100644 index 0000000..262696b --- /dev/null +++ b/lib/screen/work_process_screen/sample_deliver_person_provider.dart @@ -0,0 +1,92 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../repository/get_sample_repository.dart'; + +import '../../repository/result_delivery_repository.dart'; + +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; + +abstract class SampleDeliverPersonState extends Equatable { + final DateTime date; + const SampleDeliverPersonState(this.date); + @override + List get props => [date]; +} + +class SampleDeliverPersonStateInit extends SampleDeliverPersonState { + SampleDeliverPersonStateInit() : super(DateTime.now()); +} + +class SampleDeliverPersonStateLoading extends SampleDeliverPersonState { + SampleDeliverPersonStateLoading() : super(DateTime.now()); +} + +class SampleDeliverPersonStateError extends SampleDeliverPersonState { + final String message; + SampleDeliverPersonStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class SampleDeliverPersonStateDone extends SampleDeliverPersonState { + final bool model; + SampleDeliverPersonStateDone({ + required this.model, + }) : super(DateTime.now()); +} + +//notifier +class SampleDeliverPersonNotifier + extends StateNotifier { + final Ref ref; + SampleDeliverPersonNotifier({ + required this.ref, + }) : super(SampleDeliverPersonStateInit()); + + void setData({ + required String id, + required String name, + required String note, + // Inputan INFO + required String jumlahPasien, + required String edtaRutin, + required String darahCitras, + required String serum, + required String urine, + required String pleura, + required String otherTextBahan, + required String totalOther, + }) async { + try { + state = SampleDeliverPersonStateLoading(); + final dio = ref.read(dioProvider); + final resp = await GetSampleRepository(dio: dio).setDeliveryPerson( + id: id, + name: name, + note: note, + // INPUTAN INFO + jumlahPasien: jumlahPasien, + edtaRutin: edtaRutin, + darahCitras: darahCitras, + serum: serum, + urine: urine, + pleura: pleura, + otherTextBahan: otherTextBahan, + totalOther: totalOther + ); + state = SampleDeliverPersonStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = SampleDeliverPersonStateError(message: e.message.toString()); + } else { + state = SampleDeliverPersonStateError(message: e.toString()); + } + } + } +} + +//provider +final SampleDeliverPersonProvider = StateNotifierProvider< + SampleDeliverPersonNotifier, + SampleDeliverPersonState>((ref) => SampleDeliverPersonNotifier(ref: ref)); diff --git a/lib/screen/work_process_screen/sample_delivery_location_provider.dart b/lib/screen/work_process_screen/sample_delivery_location_provider.dart new file mode 100644 index 0000000..50264de --- /dev/null +++ b/lib/screen/work_process_screen/sample_delivery_location_provider.dart @@ -0,0 +1,77 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../repository/get_sample_repository.dart'; +import '../../repository/result_delivery_repository.dart'; + +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; + +abstract class SampleDeliveryLocationState extends Equatable { + final DateTime date; + const SampleDeliveryLocationState(this.date); + @override + List get props => [date]; +} + +class SampleDeliveryLocationStateInit extends SampleDeliveryLocationState { + SampleDeliveryLocationStateInit() : super(DateTime.now()); +} + +class SampleDeliveryLocationStateLoading extends SampleDeliveryLocationState { + SampleDeliveryLocationStateLoading() : super(DateTime.now()); +} + +class SampleDeliveryLocationStateError extends SampleDeliveryLocationState { + final String message; + SampleDeliveryLocationStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class SampleDeliveryLocationStateDone extends SampleDeliveryLocationState { + final bool model; + SampleDeliveryLocationStateDone({ + required this.model, + }) : super(DateTime.now()); +} + +//notifier +class SampleDeliveryLocationNotifier + extends StateNotifier { + final Ref ref; + SampleDeliveryLocationNotifier({ + required this.ref, + }) : super(SampleDeliveryLocationStateInit()); + + void setData( + {required String id, + required String step, + required String lat, + required String long, + required String address, + required String branchID}) async { + try { + state = SampleDeliveryLocationStateLoading(); + final dio = ref.read(dioProvider); + final resp = await GetSampleRepository(dio: dio).setLocation( + id: id, + step: step, + lat: lat, + long: long, + address: address, + branchID: branchID); + state = SampleDeliveryLocationStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = SampleDeliveryLocationStateError(message: e.message.toString()); + } else { + state = SampleDeliveryLocationStateError(message: e.toString()); + } + } + } +} + +//provider +final SampleDeliveryLocationProvider = StateNotifierProvider< + SampleDeliveryLocationNotifier, SampleDeliveryLocationState>( + (ref) => SampleDeliveryLocationNotifier(ref: ref)); diff --git a/lib/screen/work_process_screen/sample_receive_person_provider.dart b/lib/screen/work_process_screen/sample_receive_person_provider.dart new file mode 100644 index 0000000..4d6a8c7 --- /dev/null +++ b/lib/screen/work_process_screen/sample_receive_person_provider.dart @@ -0,0 +1,75 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../repository/get_sample_repository.dart'; + +import '../../repository/result_delivery_repository.dart'; + +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; + +abstract class SampleReceivePersonState extends Equatable { + final DateTime date; + const SampleReceivePersonState(this.date); + @override + List get props => [date]; +} + +class SampleReceivePersonStateInit extends SampleReceivePersonState { + SampleReceivePersonStateInit() : super(DateTime.now()); +} + +class SampleReceivePersonStateLoading extends SampleReceivePersonState { + SampleReceivePersonStateLoading() : super(DateTime.now()); +} + +class SampleReceivePersonStateError extends SampleReceivePersonState { + final String message; + SampleReceivePersonStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class SampleReceivePersonStateDone extends SampleReceivePersonState { + final bool model; + SampleReceivePersonStateDone({ + required this.model, + }) : super(DateTime.now()); +} + +//notifier +class SampleReceivePersonNotifier + extends StateNotifier { + final Ref ref; + SampleReceivePersonNotifier({ + required this.ref, + }) : super(SampleReceivePersonStateInit()); + + void setData( + {required String id, + required String name, + required String note, + required String suhuSample}) async { + try { + state = SampleReceivePersonStateLoading(); + final dio = ref.read(dioProvider); + final resp = await GetSampleRepository(dio: dio).setReceivePerson( + id: id, + name: name, + note: note, + suhuSample: suhuSample, + ); + state = SampleReceivePersonStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = SampleReceivePersonStateError(message: e.message.toString()); + } else { + state = SampleReceivePersonStateError(message: e.toString()); + } + } + } +} + +//provider +final SampleReceivePersonProvider = StateNotifierProvider< + SampleReceivePersonNotifier, + SampleReceivePersonState>((ref) => SampleReceivePersonNotifier(ref: ref)); diff --git a/lib/screen/work_process_screen/work_input_suhu_provider.dart b/lib/screen/work_process_screen/work_input_suhu_provider.dart new file mode 100644 index 0000000..a911949 --- /dev/null +++ b/lib/screen/work_process_screen/work_input_suhu_provider.dart @@ -0,0 +1,69 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../models/response_save_work_suhu_model.dart'; +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; +import '../../repository/work_input_suhu_repository.dart'; + +// 3. state provider +final workInputSuhuSave = StateNotifierProvider((ref) => WorkInputSuhuNotifier(ref: ref)); + +// 2. notifier +class WorkInputSuhuNotifier + extends StateNotifier { + final Ref ref; + WorkInputSuhuNotifier({required this.ref}) + : super(WorkInputSuhuStateInit()); + void workInputSuhu({ + required String deliveryId, + required String suhu + }) async { + try { + state = WorkInputSuhuStateLoading(); + final resp = await WorkInputSuhuRepository(dio: ref.read(dioProvider)) + .inputWorkSuhu( + deliveryId: deliveryId, + suhu: suhu, + ); + + state = WorkInputSuhuStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = WorkInputSuhuStateError(message: e.message ?? ""); + } else { + state = WorkInputSuhuStateError(message: e.toString()); + } + } + } +} + +// 1. state +abstract class WorkInputSuhuState extends Equatable { + final DateTime date; + const WorkInputSuhuState(this.date); + @override + List get props => [date]; +} + +class WorkInputSuhuStateInit extends WorkInputSuhuState { + WorkInputSuhuStateInit() : super(DateTime.now()); +} + +class WorkInputSuhuStateLoading extends WorkInputSuhuState { + WorkInputSuhuStateLoading() : super(DateTime.now()); +} + +class WorkInputSuhuStateError extends WorkInputSuhuState { + final String? message; + WorkInputSuhuStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class WorkInputSuhuStateDone extends WorkInputSuhuState { + final ResponseSaveWorkSuhuModel model; + WorkInputSuhuStateDone({ + required this.model, + }) : super(DateTime.now()); +} diff --git a/lib/screen/work_process_screen/work_process_screen.dart b/lib/screen/work_process_screen/work_process_screen.dart new file mode 100644 index 0000000..1ebe227 --- /dev/null +++ b/lib/screen/work_process_screen/work_process_screen.dart @@ -0,0 +1,3220 @@ +import 'dart:async'; +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:geolocator/geolocator.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import '/models/detail_work_model.dart'; +import '/screen/kurir_konfirmasi_screen/kurir_tolak_provider.dart'; +import '/screen/work_process_screen/detail_work_provider.dart'; +import '/screen/work_process_screen/other_deliver_person_provider.dart'; +import '/screen/work_process_screen/other_delivery_location_provider.dart'; +import '/screen/work_process_screen/other_receive_person_provider.dart'; +import '/screen/work_process_screen/result_deliver_person_provider.dart'; +import '/screen/work_process_screen/result_delivery_location_provider.dart'; +import '/screen/work_process_screen/result_receive_person_provider.dart'; +import '/screen/work_process_screen/sample_deliver_person_provider.dart'; +import '/screen/work_process_screen/sample_delivery_location_provider.dart'; +import '/screen/work_process_screen/sample_receive_person_provider.dart'; +import '/widget/map_dialog.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; +import 'package:maps_launcher/maps_launcher.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:geocoding/geocoding.dart'; + +import '../../app/constant.dart'; +import '../../widget/konfirmasi_kurir_dialog.dart'; +import '../../widget/patient_card.dart'; +import '../../widget/snackbar_widget.dart'; +import 'work_input_suhu_provider.dart'; +import 'work_tunda_provider.dart'; + +// 1 Lokasi Awal (Pengambilan) +// 2 Lokasi Akhir (Pengambilan) +// 3 Lokasi Awal (Pengantaran) +// 4 Lokasi Akhir (Pengantaran) +class DetailWorkProp { + final int tipeId; + final String kurirId; + final String deliveryId; + final String nomorLab; + final String tujuan; + final String catatanFo; + + DetailWorkProp(this.tipeId, this.kurirId, this.deliveryId, this.nomorLab, + this.tujuan, this.catatanFo); +} + +enum getLocationStepEnum { + inputSuhu, + workKonfirmasi, + pengambilanAwal, + pengambilanAkhir, + catatanPengambilan, + pengantaranAwal, + pengantaranAkhir, + catatanPengantaran, + done +} + +class WorkProcessScreen extends HookConsumerWidget { + const WorkProcessScreen({super.key, required this.data}); + + final DetailWorkProp data; + + @override + Widget build(BuildContext context, WidgetRef ref) { + print("tipe id : ${data.tipeId}"); + final detailData = useState(DetailWorkModel()); + final petugasPenyerahanController = useTextEditingController(text: ""); + final catatanController = useTextEditingController(text: ""); + final buttonLocation = useState(true); + final buttonSave = useState(false); + final petugasPenyerahanTemp = useState(""); + final pengambilanAwalLoc = + useState>({"long": "", "lat": ""}); + final pengambilanAkhirLoc = + useState>({"long": "", "lat": ""}); + final pengantaranAwalLoc = + useState>({"long": "", "lat": ""}); + final pengantaranAkhirLoc = + useState>({"long": "", "lat": ""}); + final kurirKonfirmasi = useState(false); + final sudahAdaLokasiAwal = useState(false); + final noteKurir = useTextEditingController(text: ""); + final petugasPengambilan = useState(""); + final petugasPengantaran = useState(""); + final catatanPengambilan = useState(""); + final catatanPengantaran = useState(""); + final getLocationStep = useState(getLocationStepEnum.pengambilanAwal); + // final getLocationStep = useState(getLocationStepEnum.inputSuhu); + final getLocationLoading = useState(false); + final detailLoading = useState(false); + final address = useState(""); + final tempLocation = useState>({"long": "", "lat": ""}); + + // WORK TUNDA + final buttonTundaShow = useState(false); + final ctrlNoteTunda = useTextEditingController(text: ""); + final errorMessage = useState(""); + + final hideInfo = useState(false); + final showSuhuBoxBottom = useState(false); + final showPetugasPenyerahan = useState(false); + final showCatatan = useState(false); + + // INPUTAN INFO + final jumlahPasienController = useTextEditingController(text: "0"); + final edtaRutinController = useTextEditingController(text: "0"); + final darahCitrasController = useTextEditingController(text: "0"); + final serumController = useTextEditingController(text: "0"); + final urineController = useTextEditingController(text: "0"); + final pleuraController = useTextEditingController(text: "0"); + final otherTextBahanController = useTextEditingController(text: ""); + final totalOtherController = useTextEditingController(text: "0"); + final suhuBoxController = useTextEditingController(text: ""); + final suhuSampleController = useTextEditingController(text: ""); + + fetchData() { + ref.read(DetailWorkProvider.notifier).getData( + id: data.kurirId, + tipeId: data.tipeId.toString(), + deliveryId: data.deliveryId); + } + + // useEffect(() { + // WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + + // }); + // return () {}; + // }, []); + ref.listen(DetailWorkProvider, (previous, next) { + if (next is DetailWorkStateLoading) { + detailLoading.value = true; + } else if (next is DetailWorkStateError) { + detailLoading.value = false; + String msg = next.message; + + SanckbarWidget(context, msg, snackbarType.error); + } else if (next is DetailWorkStateDone) { + detailData.value = next.model; + + // print(next.model.petugasPenyerahan); + if (data.tipeId != 5) { + next.model.suhuBox = "0"; + } + if (next.model.lokasiPengambilanAwal != "N") { + kurirKonfirmasi.value = true; + } + if (next.model.tipeid == "1" || + next.model.tipeid == "2" || + next.model.tipeid == "3") { + petugasPenyerahanTemp.value = next.model.petugasPenyerahan.toString(); + next.model.petugasPenyerahan = ""; + } + + if (next.model.lokasiPengambilanAwal != "N" && + kurirKonfirmasi.value == false) { + sudahAdaLokasiAwal.value = true; + kurirKonfirmasi.value = true; + getLocationStep.value = getLocationStepEnum.pengambilanAwal; + // getLocationStep.value = getLocationStepEnum.inputSuhu; + print("masuk pengambilan awal awal"); + } else if (next.model.lokasiPengambilanAwal == "N" && + next.model.lokasiPengambilanAkhir == "N" && + (next.model.petugasPenyerahan == null || + next.model.petugasPenyerahan == "" || + next.model.petugasPenyerahan == "-") && + (next.model.notePenyerahan == null || + next.model.notePenyerahan == "" || + next.model.notePenyerahan == "-") && + next.model.lokasiPengantaranAwal == 'N' && + next.model.lokasiPengantaranAkhir == 'N' && + (next.model.penerima == null || + next.model.penerima == "" || + next.model.penerima == "-") && + (next.model.notePenerimaan == null || + next.model.notePenerimaan == "-" || + next.model.notePenerimaan == "") && + (next.model.suhuBox == null || + next.model.suhuBox == "-" || + next.model.suhuBox == "") && + (kurirKonfirmasi.value == true)) { + // getLocationStep.value = getLocationStepEnum.inputSuhu; + getLocationStep.value = getLocationStepEnum.pengambilanAwal; + print("Masuk Input Suhu"); + } else if (next.model.lokasiPengambilanAwal == "N" && + next.model.lokasiPengambilanAkhir == "N" && + (next.model.petugasPenyerahan == null || + next.model.petugasPenyerahan == "" || + next.model.petugasPenyerahan == "-") && + (next.model.notePenyerahan == null || + next.model.notePenyerahan == "" || + next.model.notePenyerahan == "-") && + next.model.lokasiPengantaranAwal == 'N' && + next.model.lokasiPengantaranAkhir == 'N' && + kurirKonfirmasi.value == true && + (next.model.penerima == null || + next.model.penerima == "" || + next.model.penerima == "-") && + (next.model.notePenerimaan == null || + next.model.notePenerimaan == "-" || + next.model.notePenerimaan == "")) { + getLocationStep.value = getLocationStepEnum.pengambilanAwal; + // set suhu box + suhuBoxController.text = next.model.suhuBox.toString(); + print("Masuk pengambilan awal"); + + // set tunda button + buttonTundaShow.value = false; + print("button tunda flag ${buttonTundaShow.value}"); + } else if (next.model.lokasiPengambilanAwal == "Y" && + next.model.lokasiPengambilanAkhir == "N" && + (next.model.petugasPenyerahan == null || + next.model.petugasPenyerahan == "" || + next.model.petugasPenyerahan == "-") && + (next.model.notePenyerahan == null || + next.model.notePenyerahan == "" || + next.model.notePenyerahan == "-") && + next.model.lokasiPengantaranAwal == 'N' && + next.model.lokasiPengantaranAkhir == 'N' && + kurirKonfirmasi.value == true && + (next.model.penerima == null || + next.model.penerima == "" || + next.model.penerima == "-") && + (next.model.notePenerimaan == null || + next.model.notePenerimaan == "-" || + next.model.notePenerimaan == "")) { + getLocationStep.value = getLocationStepEnum.pengambilanAkhir; + pengambilanAwalLoc.value = { + "long": next.model.lngPengambilanAwal.toString(), + "lat": next.model.latPengambilanAwal.toString(), + }; + // set suhu box + suhuBoxController.text = next.model.suhuBox.toString(); + print("Masuk pengambilan akhir"); + // if (detailData.value.tipeid == "1" || + // detailData.value.tipeid == "2" || + // detailData.value.tipeid == "3") { + // petugasPenyerahanController.text = + // detailData.value.petugasPenyerahan ?? ""; + // } + + // set button tunda + buttonTundaShow.value = false; + } else if (next.model.lokasiPengambilanAwal == "Y" && + next.model.lokasiPengambilanAkhir == "Y" && + (next.model.petugasPenyerahan == null || + next.model.petugasPenyerahan == "" || + next.model.petugasPenyerahan == "-") && + (next.model.notePenyerahan == null || + next.model.notePenyerahan == "" || + next.model.notePenyerahan == "-") && + next.model.lokasiPengantaranAwal == 'N' && + next.model.lokasiPengantaranAkhir == 'N' && + kurirKonfirmasi.value == true && + (next.model.penerima == null || + next.model.penerima == "" || + next.model.penerima == "-") && + (next.model.notePenerimaan == null || + next.model.notePenerimaan == "-" || + next.model.notePenerimaan == "")) { + pengambilanAwalLoc.value = { + "long": next.model.lngPengambilanAwal.toString(), + "lat": next.model.latPengambilanAwal.toString(), + }; + pengambilanAkhirLoc.value = { + "long": next.model.lngPengambilanAkhir.toString(), + "lat": next.model.latPengambilanAkhir.toString(), + }; + // set suhu box + suhuBoxController.text = next.model.suhuBox.toString(); + print("Masuk catatan pengambilan"); + + // button tunda show + buttonTundaShow.value = true; + buttonLocation.value = false; + buttonSave.value = true; + getLocationStep.value = getLocationStepEnum.catatanPengambilan; + if (next.model.tipeid == "1" || + next.model.tipeid == "2" || + next.model.tipeid == "3") { + petugasPenyerahanController.text = petugasPenyerahanTemp.value; + } + + // setelah set map lokasi akhir + } else if (next.model.lokasiPengambilanAwal == "Y" && + next.model.lokasiPengambilanAkhir == "Y" && + (next.model.petugasPenyerahan != null || + next.model.petugasPenyerahan != "" || + next.model.petugasPenyerahan != "-") && + (next.model.notePenyerahan != null || + next.model.notePenyerahan != "" || + next.model.notePenyerahan != "-") && + next.model.lokasiPengantaranAwal == 'N' && + next.model.lokasiPengantaranAkhir == 'N' && + kurirKonfirmasi.value == true && + (next.model.penerima == null || + next.model.penerima == "" || + next.model.penerima == "-") && + (next.model.notePenerimaan == null || + next.model.notePenerimaan == "-" || + next.model.notePenerimaan == "")) { + pengambilanAwalLoc.value = { + "long": next.model.lngPengambilanAwal.toString(), + "lat": next.model.latPengambilanAwal.toString(), + }; + pengambilanAkhirLoc.value = { + "long": next.model.lngPengambilanAkhir.toString(), + "lat": next.model.latPengambilanAkhir.toString(), + }; + catatanPengambilan.value = next.model.notePenyerahan.toString(); + petugasPengambilan.value = next.model.petugasPenyerahan.toString(); + getLocationStep.value = getLocationStepEnum.pengantaranAwal; + + // set suhu box + suhuBoxController.text = next.model.suhuBox.toString(); + + // SET INPUTAN INFO + jumlahPasienController.text = next.model.totalPasien.toString(); + edtaRutinController.text = next.model.totalEdta.toString(); + darahCitrasController.text = next.model.totalCitras.toString(); + serumController.text = next.model.totalSerum.toString(); + urineController.text = next.model.totalUrine.toString(); + pleuraController.text = next.model.totalPleura.toString(); + otherTextBahanController.text = next.model.otherSample.toString(); + totalOtherController.text = next.model.totalOther.toString(); + buttonTundaShow.value = true; + print("Masuk pengantaran awal"); + buttonLocation.value = true; + buttonSave.value = false; + } else if (next.model.lokasiPengambilanAwal == "Y" && + next.model.lokasiPengambilanAkhir == "Y" && + (next.model.petugasPenyerahan != null || + next.model.petugasPenyerahan != "" || + next.model.petugasPenyerahan != "-") && + (next.model.notePenyerahan != null || + next.model.notePenyerahan != "" || + next.model.notePenyerahan != "-") && + next.model.lokasiPengantaranAwal == 'Y' && + next.model.lokasiPengantaranAkhir == 'N' && + kurirKonfirmasi.value == true && + (next.model.penerima == null || + next.model.penerima == "" || + next.model.penerima == "-") && + (next.model.notePenerimaan == null || + next.model.notePenerimaan == "-" || + next.model.notePenerimaan == "")) { + buttonTundaShow.value = true; + pengambilanAwalLoc.value = { + "long": next.model.lngPengambilanAwal.toString(), + "lat": next.model.latPengambilanAwal.toString(), + }; + pengambilanAkhirLoc.value = { + "long": next.model.lngPengambilanAkhir.toString(), + "lat": next.model.latPengambilanAkhir.toString(), + }; + catatanPengambilan.value = next.model.notePenyerahan.toString(); + petugasPengambilan.value = next.model.petugasPenyerahan.toString(); + pengantaranAwalLoc.value = { + "long": next.model.lngPengantaranAwal.toString(), + "lat": next.model.latPengantaranAwal.toString(), + }; + + // set suhu box + suhuBoxController.text = next.model.suhuBox.toString(); + + // SET INPUTAN INFO + jumlahPasienController.text = next.model.totalPasien.toString(); + edtaRutinController.text = next.model.totalEdta.toString(); + darahCitrasController.text = next.model.totalCitras.toString(); + serumController.text = next.model.totalSerum.toString(); + urineController.text = next.model.totalUrine.toString(); + pleuraController.text = next.model.totalPleura.toString(); + otherTextBahanController.text = next.model.otherSample.toString(); + totalOtherController.text = next.model.totalOther.toString(); + + print("Masuk pengantaran akhir"); + + getLocationStep.value = getLocationStepEnum.pengantaranAkhir; + } else if (next.model.lokasiPengambilanAwal == "Y" && + next.model.lokasiPengambilanAkhir == "Y" && + (next.model.petugasPenyerahan != null || + next.model.petugasPenyerahan != "" || + next.model.petugasPenyerahan != "-") && + (next.model.notePenyerahan != null || + next.model.notePenyerahan != "" || + next.model.notePenyerahan != "-") && + next.model.lokasiPengantaranAwal == 'Y' && + next.model.lokasiPengantaranAkhir == 'Y' && + kurirKonfirmasi.value == true && + (next.model.penerima == null || + next.model.penerima == "" || + next.model.penerima == "-") && + (next.model.notePenerimaan == null || + next.model.notePenerimaan == "-" || + next.model.notePenerimaan == "")) { + pengambilanAwalLoc.value = { + "long": next.model.lngPengambilanAwal.toString(), + "lat": next.model.latPengambilanAwal.toString(), + }; + pengambilanAkhirLoc.value = { + "long": next.model.lngPengambilanAkhir.toString(), + "lat": next.model.latPengambilanAkhir.toString(), + }; + catatanPengambilan.value = next.model.notePenyerahan.toString(); + petugasPengambilan.value = next.model.petugasPenyerahan.toString(); + pengantaranAwalLoc.value = { + "long": next.model.lngPengantaranAwal.toString(), + "lat": next.model.latPengantaranAwal.toString(), + }; + pengantaranAkhirLoc.value = { + "long": next.model.lngPengantaranAkhir.toString(), + "lat": next.model.latPengantaranAkhir.toString(), + }; + getLocationStep.value = getLocationStepEnum.catatanPengantaran; + // HIDE BAGIAN INFO + hideInfo.value = false; + + // SHOW SUHU BOTTOM TRUE + showSuhuBoxBottom.value = true; + + // set suhu box + suhuBoxController.text = next.model.suhuBox.toString(); + print("Masuk catatan pengantaran "); + + buttonLocation.value = false; + buttonSave.value = true; + + // setelah set map lokasi akhir + buttonTundaShow.value = true; + } else if (next.model.lokasiPengambilanAwal == "Y" && + next.model.lokasiPengambilanAkhir == "Y" && + (next.model.petugasPenyerahan != null || + next.model.petugasPenyerahan != "" || + next.model.petugasPenyerahan != "-") && + (next.model.notePenyerahan != null || + next.model.notePenyerahan != "" || + next.model.notePenyerahan != "-") && + next.model.lokasiPengantaranAwal == 'Y' && + next.model.lokasiPengantaranAkhir == 'Y' && + kurirKonfirmasi.value == true && + (next.model.penerima != null || + next.model.penerima != "" || + next.model.penerima != "-") && + (next.model.notePenerimaan != null || + next.model.notePenerimaan != "-" || + next.model.notePenerimaan != "")) { + pengambilanAwalLoc.value = { + "long": next.model.lngPengambilanAwal.toString(), + "lat": next.model.latPengambilanAwal.toString(), + }; + pengambilanAkhirLoc.value = { + "long": next.model.lngPengambilanAkhir.toString(), + "lat": next.model.latPengambilanAkhir.toString(), + }; + catatanPengambilan.value = next.model.notePenyerahan.toString(); + petugasPengambilan.value = next.model.petugasPenyerahan.toString(); + pengantaranAwalLoc.value = { + "long": next.model.lngPengantaranAwal.toString(), + "lat": next.model.latPengantaranAwal.toString(), + }; + pengantaranAkhirLoc.value = { + "long": next.model.lngPengantaranAkhir.toString(), + "lat": next.model.latPengantaranAkhir.toString(), + }; + catatanPengantaran.value = next.model.notePenerimaan.toString(); + petugasPengantaran.value = next.model.penerima.toString(); + buttonLocation.value = false; + buttonSave.value = false; + + // set suhu box + suhuBoxController.text = next.model.suhuBox.toString(); + catatanPengantaran.value = next.model.notePenerimaan.toString(); + petugasPengantaran.value = next.model.penerima.toString(); + suhuSampleController.text = next.model.suhuSample.toString(); + buttonLocation.value = false; + buttonSave.value = false; + buttonTundaShow.value = false; + // HIDE BAGIAN INFO + hideInfo.value = false; + // SHOW SUHU BOTTOM TRUE + showSuhuBoxBottom.value = true; + + getLocationStep.value = getLocationStepEnum.done; + print("Masuk done"); + } else { + print("tidak masuk if else inti data"); + } + + detailLoading.value = false; + } + }); + + // getLocationStep.addListener( + // () { + // print("step" + getLocationStep.value.toString()); + // }, + // ); + + getAddress(double lat, double long) async { + List pm = await placemarkFromCoordinates(lat, long); + + // var address = + Placemark placemark = pm[0]; + String addressD = + "${placemark.street}, ${placemark.subLocality}, ${placemark.locality}, ${placemark.postalCode}, ${placemark.administrativeArea}"; + address.value = addressD; + print(address.value); + } + + Future _determinePosition(context) async { + bool serviceEnabled; + LocationPermission permission; + try { + // Test if location services are enabled. + serviceEnabled = await Geolocator.isLocationServiceEnabled(); + if (!serviceEnabled) { + permission = await Geolocator.requestPermission(); + } + + permission = await Geolocator.checkPermission(); + if (permission == LocationPermission.denied) { + permission = await Geolocator.requestPermission(); + if (permission == LocationPermission.denied) { + // Permissions are denied, next time you could try + // requesting permissions again (this is also where + // Android's shouldShowRequestPermissionRationale + // returned true. According to Android guidelines + // your App should show an explanatory UI now. + permission = await Geolocator.requestPermission(); + } + } + + if (permission == LocationPermission.deniedForever) { + // Permissions are denied forever, handle appropriately. + permission = await Geolocator.requestPermission(); + } + + // When we reach here, permissions are granted and we can + // continue accessing the position of the device. + // print( + getLocationLoading.value = true; + Position p = await Geolocator.getCurrentPosition( + desiredAccuracy: LocationAccuracy.high); + await getAddress(p.latitude, p.longitude); + // print( + // "Current location Latitude :${p.latitude} & Longitude ${p.longitude}"); + getLocationLoading.value = false; + return p; + } catch (e) { + print(e); + getLocationLoading.value = false; + return Position( + altitudeAccuracy: 0, + headingAccuracy: 0, + longitude: 0, + latitude: 0, + timestamp: DateTime.now(), + accuracy: 0, + altitude: 0, + heading: 0, + speed: 0, + speedAccuracy: 0); + } + } + + getLocationStep.addListener(() { + print(getLocationStep.value); + }); + + getLocation(context) async { + if (getLocationStep.value == getLocationStepEnum.pengambilanAwal) { + Position p = await _determinePosition(context); + if (p.latitude != 0 && p.longitude != 0) { + confirm() async { + tempLocation.value = { + "long": p.longitude.toString(), + "lat": p.latitude.toString() + }; + // pengambilanAwalLoc.value = { + // "long": p.longitude.toString(), + // "lat": p.latitude.toString() + // }; + if (data.tipeId == 5) { + if (suhuBoxController.text.isEmpty) { + SanckbarWidget(context, "Input Suhu Box Terlebih Dahulu", + snackbarType.warning); + return; + } else { + ref.read(SampleDeliveryLocationProvider.notifier).setData( + id: data.deliveryId.toString(), + step: "1", + lat: p.latitude.toString(), + long: p.longitude.toString(), + address: address.value, + branchID: detailData.value.branchID!); + } + } else if (data.tipeId == 4) { + ref.read(OtherDeliveryLocationProvider.notifier).setData( + id: data.deliveryId.toString(), + step: "1", + lat: p.latitude.toString(), + long: p.longitude.toString(), + address: address.value, + branchID: detailData.value.branchID!); + } else { + ref.read(ResultDeliveryLocationProvider.notifier).setData( + id: data.deliveryId.toString(), + step: "1", + lat: p.latitude.toString(), + long: p.longitude.toString(), + address: address.value, + branchID: detailData.value.branchID!); + } + // if (pengambilanAwalLoc.value["lat"] != "" || + // pengambilanAwalLoc.value['long'] != "") { + // getLocationStep.value = getLocationStepEnum.pengambilanAkhir; + // } + Navigator.pop(context); + // SanckbarWidget(context, "Lokasi pengambilan awal berhasil disimpan", + // snackbarType.success); + // print( + // "Pengambilan awal Latitude :${p.latitude} & Longitude ${p.longitude}"); + } + + notConfirm() { + getLocation(context); + } + + showMapDialog( + context: context, + lat: p.latitude, + long: p.longitude, + address: address.value, + confirm: confirm, + notConfirm: notConfirm); + } + } else if (getLocationStep.value == + getLocationStepEnum.pengambilanAkhir) { + Position p = await _determinePosition(context); + confirm() async { + if (p.latitude != 0 && p.longitude != 0) { + if (data.tipeId == 5) { + ref.read(SampleDeliveryLocationProvider.notifier).setData( + id: data.deliveryId.toString(), + step: "2", + lat: p.latitude.toString(), + long: p.longitude.toString(), + address: address.value, + branchID: detailData.value.branchID!); + } else if (data.tipeId == 4) { + ref.read(OtherDeliveryLocationProvider.notifier).setData( + id: data.deliveryId.toString(), + step: "2", + lat: p.latitude.toString(), + long: p.longitude.toString(), + address: address.value, + branchID: detailData.value.branchID!); + } else { + ref.read(ResultDeliveryLocationProvider.notifier).setData( + id: data.deliveryId.toString(), + step: "2", + lat: p.latitude.toString(), + long: p.longitude.toString(), + address: address.value, + branchID: detailData.value.branchID!); + } + tempLocation.value = { + "long": p.longitude.toString(), + "lat": p.latitude.toString() + }; + // pengambilanAkhirLoc.value = { + // "long": p.longitude.toString(), + // "lat": p.latitude.toString() + // }; + } + + // if (pengambilanAkhirLoc.value["lat"] != "" || + // pengambilanAkhirLoc.value['long'] != "") { + // // getAddress(p.latitude, p.longitude); + // getLocationStep.value = getLocationStepEnum.catatanPengambilan; + // buttonLocation.value = false; + // buttonSave.value = true; + // } + Navigator.pop(context); + // SanckbarWidget(context, "Lokasi pengambilan akhir berhasil disimpan", + // snackbarType.success); + // print( + // "Pengambilan akhir Latitude :${p.latitude} & Longitude ${p.longitude}"); + } + + notConfirm() { + getLocation(context); + } + + showMapDialog( + context: context, + lat: p.latitude, + long: p.longitude, + address: address.value, + confirm: confirm, + notConfirm: notConfirm); + } else if (getLocationStep.value == getLocationStepEnum.pengantaranAwal) { + Position p = await _determinePosition(context); + notConfirm() { + getLocation(context); + } + + confirm() async { + if (p.latitude != 0 && p.longitude != 0) { + if (data.tipeId == 5) { + ref.read(SampleDeliveryLocationProvider.notifier).setData( + id: data.deliveryId.toString(), + step: "3", + lat: p.latitude.toString(), + long: p.longitude.toString(), + address: address.value, + branchID: detailData.value.branchID!); + } else if (data.tipeId == 4) { + ref.read(OtherDeliveryLocationProvider.notifier).setData( + id: data.deliveryId.toString(), + step: "3", + lat: p.latitude.toString(), + long: p.longitude.toString(), + address: address.value, + branchID: detailData.value.branchID!); + } else { + ref.read(ResultDeliveryLocationProvider.notifier).setData( + id: data.deliveryId.toString(), + step: "3", + lat: p.latitude.toString(), + long: p.longitude.toString(), + address: address.value, + branchID: detailData.value.branchID!); + } + tempLocation.value = { + "long": p.longitude.toString(), + "lat": p.latitude.toString() + }; + // pengantaranAwalLoc.value = { + // "long": p.longitude.toString(), + // "lat": p.latitude.toString() + // }; + } + // if (pengantaranAwalLoc.value["lat"] != "" || + // pengantaranAwalLoc.value['long'] != "") { + // getLocationStep.value = getLocationStepEnum.pengantaranAkhir; + // } + // // getAddress(p.latitude, p.longitude); + Navigator.pop(context); + // SanckbarWidget(context, "Lokasi pengantaran awal berhasil disimpan", + // snackbarType.success); + // print( + // "pengantaran awal Latitude :${p.latitude} & Longitude ${p.longitude}"); + } + + showMapDialog( + context: context, + lat: p.latitude, + long: p.longitude, + address: address.value, + confirm: confirm, + notConfirm: notConfirm); + } else if (getLocationStep.value == + getLocationStepEnum.pengantaranAkhir) { + Position p = await _determinePosition(context); + notConfirm() { + getLocation(context); + } + + confirm() async { + if (p.latitude != 0 && p.longitude != 0) { + if (data.tipeId == 5) { + ref.read(SampleDeliveryLocationProvider.notifier).setData( + id: data.deliveryId.toString(), + step: "4", + lat: p.latitude.toString(), + long: p.longitude.toString(), + address: address.value, + branchID: detailData.value.branchID!); + } else if (data.tipeId == 4) { + ref.read(OtherDeliveryLocationProvider.notifier).setData( + id: data.deliveryId.toString(), + step: "4", + lat: p.latitude.toString(), + long: p.longitude.toString(), + address: address.value, + branchID: detailData.value.branchID!); + } else { + ref.read(ResultDeliveryLocationProvider.notifier).setData( + id: data.deliveryId.toString(), + step: "4", + lat: p.latitude.toString(), + long: p.longitude.toString(), + address: address.value, + branchID: detailData.value.branchID!); + } + tempLocation.value = { + "long": p.longitude.toString(), + "lat": p.latitude.toString() + }; + // pengantaranAkhirLoc.value = { + // "long": p.longitude.toString(), + // "lat": p.latitude.toString() + // }; + } + + // if (pengantaranAkhirLoc.value["lat"] != "" || + // pengantaranAkhirLoc.value['long'] != "") { + // getLocationStep.value = getLocationStepEnum.catatanPengantaran; + // buttonLocation.value = false; + // buttonSave.value = true; + // } + // // getAddress(p.latitude, p.longitude); + Navigator.pop(context); + // SanckbarWidget(context, "Lokasi pengantaran akhir berhasil disimpan", + // snackbarType.success); + // print( + // "Pengantaran akhir Latitude :${p.latitude} & Longitude ${p.longitude}"); + } + + showMapDialog( + context: context, + lat: p.latitude, + long: p.longitude, + address: address.value, + confirm: confirm, + notConfirm: notConfirm); + } + } + + saveCatatan() { + if (getLocationStep.value == getLocationStepEnum.catatanPengambilan) { + if (data.tipeId == 5) { + ref.read(SampleDeliverPersonProvider.notifier).setData( + id: data.deliveryId, + name: petugasPenyerahanController.text, + note: catatanController.text, + // INPUTAN INFO + jumlahPasien: jumlahPasienController.text, + edtaRutin: edtaRutinController.text, + darahCitras: darahCitrasController.text, + serum: serumController.text, + urine: urineController.text, + pleura: pleuraController.text, + otherTextBahan: otherTextBahanController.text, + totalOther: totalOtherController.text, + ); + if (data.tipeId == 5) { + showPetugasPenyerahan.value = false; + showCatatan.value = false; + } + } else if (data.tipeId == 4) { + ref.read(OtherDeliverPersonProvider.notifier).setData( + id: data.deliveryId, + name: petugasPenyerahanController.text, + note: catatanController.text); + } else { + ref.read(ResultDeliverPersonProvider.notifier).setData( + id: data.deliveryId, + name: petugasPenyerahanController.text, + note: catatanController.text); + } + } else if (getLocationStep.value == + getLocationStepEnum.catatanPengantaran) { + if (data.tipeId == 5) { + // ADD SUHU SAMPLE BOTTOM + if (suhuSampleController.text.isEmpty) { + SanckbarWidget(context, "Suhu Box Sample Silahkan Diisi", + snackbarType.warning); + return; + } else { + ref.read(SampleReceivePersonProvider.notifier).setData( + id: data.deliveryId, + name: petugasPenyerahanController.text, + note: catatanController.text, + suhuSample: suhuSampleController.text, + ); + + if (data.tipeId == 5) { + showPetugasPenyerahan.value = false; + showCatatan.value = false; + } + } + } else if (data.tipeId == 4) { + ref.read(OtherReceivePersonProvider.notifier).setData( + id: data.deliveryId, + name: petugasPenyerahanController.text, + note: catatanController.text); + } else { + ref.read(ResultReceivePersonProvider.notifier).setData( + id: data.deliveryId, + name: petugasPenyerahanController.text, + note: catatanController.text); + } + } + } + + setCatatanToState() { + if (getLocationStep.value == getLocationStepEnum.catatanPengambilan) { + // button tunda show + buttonTundaShow.value = true; + catatanPengambilan.value = catatanController.text; + petugasPengambilan.value = petugasPenyerahanController.text; + getLocationStep.value = getLocationStepEnum.pengantaranAwal; + buttonLocation.value = true; + buttonSave.value = false; + catatanController.value = TextEditingValue.empty; + petugasPenyerahanController.text = ""; + if (detailData.value.tipeid == "1" || + detailData.value.tipeid == "2" || + detailData.value.tipeid == "3") { + petugasPenyerahanController.text = petugasPenyerahanTemp.value; + } + SanckbarWidget(context, "Data berhasil disimpan", snackbarType.success); + } else if (getLocationStep.value == + getLocationStepEnum.catatanPengantaran) { + // button tunda show + buttonLocation.value = false; + buttonSave.value = false; + catatanPengantaran.value = catatanController.text; + petugasPengantaran.value = petugasPenyerahanController.text; + getLocationStep.value = getLocationStepEnum.done; + SanckbarWidget(context, "Data berhasil disimpan", snackbarType.success); + // if (detailData.value.tipeid == "1" || + // detailData.value.tipeid == "2" || + // detailData.value.tipeid == "3") { + // petugasPenyerahanController.text = petugasPenyerahanTemp.value; + // } + buttonTundaShow.value = false; + } + } + + setLocationToState() { + if (getLocationStep.value == getLocationStepEnum.pengambilanAwal) { + pengambilanAwalLoc.value = { + "long": tempLocation.value['long'].toString(), + "lat": tempLocation.value['lat'].toString() + }; + + getLocationStep.value = getLocationStepEnum.pengambilanAkhir; + + // Navigator.pop(context); + SanckbarWidget(context, "Lokasi pengambilan awal berhasil disimpan", + snackbarType.success); + print( + "Pengambilan awal Latitude :${tempLocation.value['lat']} & Longitude ${tempLocation.value['long']}"); + } else if (getLocationStep.value == + getLocationStepEnum.pengambilanAkhir) { + pengambilanAkhirLoc.value = { + "long": tempLocation.value['long'].toString(), + "lat": tempLocation.value['lat'].toString() + }; + + getLocationStep.value = getLocationStepEnum.catatanPengambilan; + buttonLocation.value = false; + buttonSave.value = true; + + // hanya pengambilan bahan + if (data.tipeId == 5) { + showPetugasPenyerahan.value = true; + showCatatan.value = true; + // set button tunda show + buttonTundaShow.value = true; + } + + // setelah set map lokasi akhir + buttonTundaShow.value = true; + + if (detailData.value.tipeid == "1" || + detailData.value.tipeid == "2" || + detailData.value.tipeid == "3") { + petugasPenyerahanController.text = petugasPenyerahanTemp.value; + } + + // Navigator.pop(context); + SanckbarWidget(context, "Lokasi pengambilan akhir berhasil disimpan", + snackbarType.success); + print( + "Pengambilan akhir Latitude :${tempLocation.value['lat']} & Longitude ${tempLocation.value['long']}"); + } else if (getLocationStep.value == getLocationStepEnum.pengantaranAwal) { + pengantaranAwalLoc.value = { + "long": tempLocation.value['long'].toString(), + "lat": tempLocation.value['lat'].toString() + }; + getLocationStep.value = getLocationStepEnum.pengantaranAkhir; + // getAddress(p.latitude, p.longitude); + // Navigator.pop(context); + SanckbarWidget(context, "Lokasi pengantaran awal berhasil disimpan", + snackbarType.success); + print( + "pengantaran awal Latitude :${tempLocation.value['lat']} & Longitude ${tempLocation.value['long']}"); + } else if (getLocationStep.value == + getLocationStepEnum.pengantaranAkhir) { + pengantaranAkhirLoc.value = { + "long": tempLocation.value['long'].toString(), + "lat": tempLocation.value['lat'].toString() + }; + petugasPenyerahanController.text = ""; + getLocationStep.value = getLocationStepEnum.catatanPengantaran; + buttonLocation.value = false; + buttonSave.value = true; + + // hanya pengambilan bahan + if (data.tipeId == 5) { + // HIDE BAGIAN INFO + hideInfo.value = false; + + // SHOW SUHU BOTTOM TRUE + showSuhuBoxBottom.value = true; + + showPetugasPenyerahan.value = true; + showCatatan.value = true; + } + + // setelah set map lokasi akhir + // buttonTundaShow.value = true; + + // getAddress(p.latitude, p.longitude); + // Navigator.pop(context); + SanckbarWidget(context, "Lokasi pengantaran akhir berhasil disimpan", + snackbarType.success); + print( + "Pengantaran akhir Latitude :${tempLocation.value['lat']} & Longitude ${tempLocation.value['long']}"); + } + } + + ref.listen(kurirTolakProvider, (previus, next) { + if (next is KurirTolakStateLoading) { + kurirKonfirmasi.value = true; + } else if (next is KurirTolakStateError) { + print(next.message); + Navigator.pop(context); + SanckbarWidget(context, next.message, snackbarType.error); + kurirKonfirmasi.value = false; + } else if (next is KurirTolakStateDone) { + Navigator.of(context).pop(); + SanckbarWidget(context, "Konfirmasi pekerjaan berhasil di tolak", + snackbarType.success); + kurirKonfirmasi.value = false; + } + }); + ref.listen(ResultDeliveryLocationProvider, (previous, next) { + if (next is ResultDeliveryLocationStateLoading) { + getLocationLoading.value = true; + } else if (next is ResultDeliveryLocationStateError) { + print(next.message); + Navigator.pop(context); + SanckbarWidget(context, next.message, snackbarType.error); + getLocationLoading.value = false; + } else if (next is ResultDeliveryLocationStateDone) { + setLocationToState(); + + getLocationLoading.value = false; + } + }); + ref.listen(SampleDeliveryLocationProvider, (previous, next) { + if (next is SampleDeliveryLocationStateLoading) { + getLocationLoading.value = true; + } else if (next is SampleDeliveryLocationStateError) { + print(next.message); + Navigator.pop(context); + SanckbarWidget(context, next.message, snackbarType.error); + getLocationLoading.value = false; + } else if (next is SampleDeliveryLocationStateDone) { + // print(jsonEncode(next.model)); + setLocationToState(); + getLocationLoading.value = false; + } + }); + ref.listen(OtherDeliveryLocationProvider, (previous, next) { + if (next is OtherDeliveryLocationStateLoading) { + getLocationLoading.value = true; + } else if (next is OtherDeliveryLocationStateError) { + print(next.message); + Navigator.pop(context); + SanckbarWidget(context, next.message, snackbarType.error); + getLocationLoading.value = false; + } else if (next is OtherDeliveryLocationStateDone) { + // print(next.model); + setLocationToState(); + getLocationLoading.value = false; + } + }); + ref.listen(ResultDeliverPersonProvider, (previous, next) { + if (next is ResultDeliverPersonStateLoading) { + getLocationLoading.value = true; + } else if (next is ResultDeliverPersonStateError) { + print(next.message); + SanckbarWidget(context, next.message, snackbarType.error); + getLocationLoading.value = false; + } else if (next is ResultDeliverPersonStateDone) { + // print(jsonEncode(next.model)); + setCatatanToState(); + if (detailData.value.tipeid == "1" || + detailData.value.tipeid == "2" || + detailData.value.tipeid == "3") { + petugasPenyerahanController.text = petugasPenyerahanTemp.value; + } + getLocationLoading.value = false; + } + }); + ref.listen(SampleDeliverPersonProvider, (previous, next) { + if (next is SampleDeliverPersonStateLoading) { + getLocationLoading.value = true; + } else if (next is SampleDeliverPersonStateError) { + print(next.message); + SanckbarWidget(context, next.message, snackbarType.error); + getLocationLoading.value = false; + } else if (next is SampleDeliverPersonStateDone) { + // print(jsonEncode(next.model)); + setCatatanToState(); + getLocationLoading.value = false; + } + }); + ref.listen(OtherDeliverPersonProvider, (previous, next) { + if (next is OtherDeliverPersonStateLoading) { + getLocationLoading.value = true; + } else if (next is OtherDeliverPersonStateError) { + print(next.message); + SanckbarWidget(context, next.message, snackbarType.error); + getLocationLoading.value = false; + } else if (next is OtherDeliverPersonStateDone) { + // print(jsonEncode(next.model)); + setCatatanToState(); + getLocationLoading.value = false; + } + }); + ref.listen(ResultReceivePersonProvider, (previous, next) { + if (next is ResultReceivePersonStateLoading) { + getLocationLoading.value = true; + } else if (next is ResultReceivePersonStateError) { + print(next.message); + SanckbarWidget(context, next.message, snackbarType.error); + getLocationLoading.value = false; + } else if (next is ResultReceivePersonStateDone) { + // print(jsonEncode(next.model)); + setCatatanToState(); + getLocationLoading.value = false; + } + }); + ref.listen(SampleReceivePersonProvider, (previous, next) { + if (next is SampleReceivePersonStateLoading) { + getLocationLoading.value = true; + } else if (next is SampleReceivePersonStateError) { + print(next.message); + getLocationLoading.value = false; + SanckbarWidget(context, next.message, snackbarType.error); + } else if (next is SampleReceivePersonStateDone) { + // print(jsonEncode(next.model)); + setCatatanToState(); + getLocationLoading.value = false; + } + }); + ref.listen(OtherReceivePersonProvider, (previous, next) { + if (next is OtherReceivePersonStateLoading) { + getLocationLoading.value = true; + } else if (next is OtherReceivePersonStateError) { + print(next.message); + SanckbarWidget(context, next.message, snackbarType.error); + getLocationLoading.value = false; + } else if (next is OtherReceivePersonStateDone) { + // print(jsonEncode(next.model)); + setCatatanToState(); + getLocationLoading.value = false; + } + }); + + void requestLocationPermission() async { + LocationPermission permission = await Geolocator.checkPermission(); + if (permission == LocationPermission.denied) { + permission = await Geolocator.requestPermission(); + if (permission == LocationPermission.denied || + permission == LocationPermission.unableToDetermine) { + permission = await Geolocator.requestPermission(); + } + if (permission == LocationPermission.deniedForever) { + // ignore: use_build_context_synchronously + return showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + content: const SingleChildScrollView( + child: Text("Untuk melanjutkan, aktifkan lokasi Anda")), + actions: [ + TextButton( + child: const Text('Buka pengaturan'), + onPressed: () async { + await openAppSettings(); + }, + ), + ], + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(24))), + ); + }, + ); + } + } + // Izin lokasi diberikan oleh pengguna + } + + // SUHU PROVIDER AND FUNCTION START + saveWorkInputSuhu(String value) { + if (suhuBoxController.text.isEmpty) { + SanckbarWidget(context, "Suhu Harus Di input", snackbarType.warning); + } else { + final param = { + "id": data.deliveryId.toString(), + "suhu": suhuBoxController.text, + }; + + print("param : $param"); + + ref.read(workInputSuhuSave.notifier).workInputSuhu( + deliveryId: data.deliveryId.toString(), + suhu: suhuBoxController.text, + ); + } + } + + // save work input suhu provider + ref.listen(workInputSuhuSave, (prev, next) { + if (next is WorkInputSuhuStateLoading) { + detailLoading.value = true; + } else if (next is WorkInputSuhuStateError) { + detailLoading.value = false; + errorMessage.value = next.message ?? ""; + SanckbarWidget(context, "${next.message}", snackbarType.error); + Timer(const Duration(seconds: 3), () { + errorMessage.value = ""; + }); + } else if (next is WorkInputSuhuStateDone) { + detailLoading.value = false; + if (next.model.status != "OK") { + SanckbarWidget(context, "${next.model.message}", snackbarType.error); + } else { + SanckbarWidget( + context, "Suhu Berhasil Di update", snackbarType.success); + + getLocationStep.value = getLocationStepEnum.pengambilanAwal; + } + } + }); + // SUHU PROVIDER AND FUNCTION END + + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + requestLocationPermission(); + fetchData(); + }); + return () {}; + }, []); + + // WORK TUNDA PROVIDER AND FUNCTION START + saveWorkTunda({required String note}) { + if (ctrlNoteTunda.text.isEmpty) { + SanckbarWidget( + context, "Alasan Tunda Harus Diisi", snackbarType.warning); + } else { + final param = { + "id": data.kurirId.toString(), + "tipeid": data.tipeId.toString(), + "deliveryid": data.deliveryId, + "note": note + }; + + print("param : $param"); + + ref.read(workTundaSave.notifier).WorkTundaSave( + deliveryID: data.deliveryId, + tipeID: data.tipeId.toString(), + courierID: data.kurirId.toString(), + note: note, + ); + } + } + + // save tunda provider + ref.listen(workTundaSave, (prev, next) { + if (next is WorkTundaSaveStateLoading) { + detailLoading.value = true; + } else if (next is WorkTundaSaveStateError) { + detailLoading.value = false; + errorMessage.value = next.message ?? ""; + SanckbarWidget(context, "${next.message}", snackbarType.error); + Timer(const Duration(seconds: 3), () { + errorMessage.value = ""; + }); + } else if (next is WorkTundaSaveStateDone) { + detailLoading.value = false; + if (next.model.status != "OK") { + SanckbarWidget(context, "${next.model.message}", snackbarType.error); + } else { + SanckbarWidget( + context, "Tunda Pekerjaan Berhasil", snackbarType.success); + Navigator.of(context).pop(); + // Navigator.of(context).pop(); + } + } + }); + // WORK TUNDA PROVIDER AND FUNCTION END + + return RefreshIndicator( + onRefresh: () async { + fetchData(); + }, + child: Stack( + children: [ + Scaffold( + resizeToAvoidBottomInset: false, + // extendBody: false, + backgroundColor: Colors.white, + bottomNavigationBar: buttonLocation.value || buttonSave.value + ? Container( + // color: Colors.blue, + padding: EdgeInsets.symmetric( + horizontal: + Constant.getActualX(context: context, x: 35)), + height: Constant.getActualY(context: context, y: 70), + child: Column( + children: [ + if (buttonLocation.value && + !detailLoading.value && + kurirKonfirmasi.value) + SizedBox( + // color: Colors.red, + height: + Constant.getActualY(context: context, y: 46), + child: ElevatedButton( + onPressed: () { + getLocation(context); + }, + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + side: const BorderSide( + width: 0.1, color: Colors.grey), + borderRadius: BorderRadius.circular(8), + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.location_searching_rounded), + SizedBox( + width: Constant.getActualX( + context: context, x: 8), + ), + const Text( + "Gunakan Lokasi Saat Ini", + style: TextStyle( + fontWeight: FontWeight.bold), + ) + ], + )), + ), + if (buttonSave.value && + !detailLoading.value && + kurirKonfirmasi.value) + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + OutlinedButton( + onPressed: () { + Navigator.pop(context); + }, + style: OutlinedButton.styleFrom( + side: BorderSide( + style: BorderStyle.solid, + color: Constant.primaryBlue), + shape: RoundedRectangleBorder( + side: BorderSide( + style: BorderStyle.solid, + color: Constant.primaryBlue), + borderRadius: BorderRadius.circular(8), + ), + ), + child: const Text( + "Batal", + style: + TextStyle(fontWeight: FontWeight.bold), + )), + ElevatedButton( + onPressed: + catatanController.text.isNotEmpty && + petugasPenyerahanController + .text.isNotEmpty + ? () { + saveCatatan(); + } + : null, + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + side: const BorderSide( + width: 0.1, color: Colors.grey), + borderRadius: BorderRadius.circular(8), + ), + ), + child: const Text( + "Simpan", + style: + TextStyle(fontWeight: FontWeight.bold), + )) + ], + ), + if (!buttonSave.value && + !detailLoading.value && + !kurirKonfirmasi.value && + !sudahAdaLokasiAwal.value) + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + OutlinedButton( + onPressed: () { + showConfirmCourierDialog( + context: context, + tipe: "Tolak", + tipeid: data.tipeId, + tipee: "${detailData.value.tipe}", + tujuann: data.tujuan, + noteKurir: noteKurir, + penerimaa: + "${detailData.value.alamatPengantaran}", + okCallback: () { + // print(noteKurir.text); + + if (noteKurir.text.isEmpty) { + SanckbarWidget( + context, + "Alasan Tolak Wajib Diisi", + snackbarType.warning); + return; + } else { + ref + .read(kurirTolakProvider.notifier) + .tolakKurir( + id: data.kurirId, + tipeId: data.tipeId.toString(), + deliveryId: data.deliveryId, + note: noteKurir.text, + ); + } + }, + ); + }, + style: OutlinedButton.styleFrom( + side: BorderSide( + style: BorderStyle.solid, + color: Constant.primaryRed), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + child: const Text("Tolak", + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.red))), + ElevatedButton( + onPressed: () { + showConfirmCourierDialog( + context: context, + tipe: "Terima", + tipeid: data.tipeId, + tipee: "${detailData.value.tipe}", + tujuann: data.tujuan, + penerimaa: + "${detailData.value.alamatPengantaran}", + noteKurir: noteKurir, + okCallback: () { + kurirKonfirmasi.value = true; + getLocationStep.value = + getLocationStepEnum.pengambilanAwal; + + // getLocationStep.value = + // getLocationStepEnum.inputSuhu; + }, + ); + }, + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + side: const BorderSide( + width: 0.1, color: Colors.blue), + borderRadius: BorderRadius.circular(8), + ), + ), + child: const Text( + "Terima", + style: + TextStyle(fontWeight: FontWeight.bold), + )) + ], + ), + ], + ), + ) + : null, + body: detailLoading.value + ? Center( + child: LoadingAnimationWidget.fourRotatingDots( + color: Constant.primaryBlue, size: 50), + ) + : ListView( + children: [ + Container( + // color: Colors.white, + height: Constant.getActualY(context: context, y: 56), + ), + Container( + padding: EdgeInsets.symmetric( + horizontal: + Constant.getActualX(context: context, x: 10)), + // color: Colors.white, + height: Constant.getActualY(context: context, y: 48), + child: Row( + children: [ + IconButton( + onPressed: () { + Navigator.of(context).pop(); + }, + icon: const Icon(Icons.arrow_back_ios_rounded), + ), + Expanded( + child: Text( + "${detailData.value.tipe}", + style: Constant.body1(context: context) + .copyWith( + fontWeight: FontWeight.w600, + color: Constant.textPrimary), + ), + ), + if (buttonTundaShow.value == true) + InkWell( + onTap: () { + print('OPEN DIALOG TUNDA'); + // OPEN DIALOG TUNDA START + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text( + "Tunda Pekerjaan", + style: TextStyle( + fontWeight: FontWeight.bold), + ), + content: SizedBox( + height: Constant.getActualY( + context: context, y: 270), + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + "Anda akan menunda pekerjaan ini ?", + textAlign: TextAlign.left, + style: Constant.body2( + context: context) + .copyWith( + fontWeight: + FontWeight.w400, + color: Constant + .textPrimary), + ), + Container( + margin: + const EdgeInsets.fromLTRB( + 0, 16, 0, 25), + width: double.infinity, + child: Card( + elevation: 2, + shape: RoundedRectangleBorder( + side: BorderSide( + color: Theme.of(context) + .colorScheme + .outline, + ), + borderRadius: + const BorderRadius.all( + Radius.circular( + 12)), + ), + child: Container( + padding: + const EdgeInsets.all( + 16), + child: Column( + children: [ + Row( + children: [ + Expanded( + flex: 3, + child: Text( + "Tipe", + style: Constant + .caption1( + context: + context), + ), + ), + // widget card dialog tunda + if (data.tipeId == + 5) + cardDialogTundaTipe( + context, + detailData + .value.tipe, + Constant + .backgroundBlue, + Constant + .primaryBlue, + ), + if (data.tipeId == + 1) + cardDialogTundaTipe( + context, + detailData + .value.tipe, + Constant + .backgroundOrange, + Constant + .primaryOrange, + ), + if (data.tipeId == + 3) + cardDialogTundaTipe( + context, + detailData + .value.tipe, + Constant + .backgroundGreen, + Constant + .primaryGreen, + ), + if (data.tipeId == + 2) + cardDialogTundaTipe( + context, + detailData + .value.tipe, + Constant + .backgroundPurple, + Constant + .primaryPurple, + ), + if (data.tipeId == + 4) + cardDialogTundaTipe( + context, + detailData + .value.tipe, + Constant + .primaryRed + .withOpacity( + 0.25), + Constant + .primaryRed, + ), + ], + ), + Row( + children: [ + Expanded( + flex: 3, + child: Text( + "Tujuan", + style: Constant + .caption1( + context: + context), + ), + ), + Expanded( + flex: 5, + child: Text( + detailData.value + .alamatPengambilan ?? + "", + style: + TextStyle( + color: Constant + .textGrey, + fontSize: 12, + ), + ), + ) + ], + ), + Row( + children: [ + Expanded( + flex: 3, + child: Text( + "Penerima", + style: Constant + .caption1( + context: + context), + ), + ), + Expanded( + flex: 5, + child: Text( + detailData.value + .alamatPengantaran ?? + "", + style: + TextStyle( + color: Constant + .textGrey, + fontSize: 12, + ), + ), + ) + ], + ), + ], + ), + ), + ), + ), + SizedBox( + // width: Constant.getActualX(context: context, x: 250), + height: Constant.getActualY( + context: context, y: 50), + child: TextFormField( + controller: ctrlNoteTunda, + decoration: InputDecoration( + labelStyle: TextStyle( + color: Colors.grey), + border: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.grey), + borderRadius: + BorderRadius.all( + Radius.circular( + 12))), + labelText: 'Alasan', + ), + ), + ) + ], + ), + ), + actions: [ + Row( + mainAxisAlignment: + MainAxisAlignment.spaceEvenly, + children: [ + OutlinedButton( + onPressed: () { + ctrlNoteTunda.text = ""; + Navigator.pop( + context, 'Cancel'); + }, + style: OutlinedButton.styleFrom( + side: BorderSide( + style: BorderStyle.solid, + color: Constant.textGrey), + shape: RoundedRectangleBorder( + side: BorderSide( + style: + BorderStyle.solid, + color: Constant + .textPrimary), + borderRadius: + BorderRadius.circular( + 8), + ), + ), + child: const Text( + "Batal", + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black, + ), + ), + ), + const Padding( + padding: EdgeInsets.fromLTRB( + 0, 0, 65, 0), + ), + ElevatedButton( + onPressed: () { + // print(ctrlNoteTunda.text); + if (ctrlNoteTunda + .text.isNotEmpty) { + saveWorkTunda( + note: + ctrlNoteTunda.text); + } else { + SanckbarWidget( + context, + "Alasan tunda tidak boleh kososng", + snackbarType.warning); + } + }, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.red, + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.circular( + 8), + ), + ), + child: const Text( + "Tunda", + style: TextStyle( + fontWeight: + FontWeight.bold), + ), + ), + ], + ) + ], + shape: RoundedRectangleBorder( + side: BorderSide( + color: Theme.of(context) + .colorScheme + .outline, + ), + borderRadius: const BorderRadius.all( + Radius.circular(12), + ), + ), + ); + }, + ); + // OPEN DIALOG TUNDA END + }, + child: Container( + width: Constant.getActualY( + context: context, y: 30), + height: Constant.getActualY( + context: context, y: 30), + decoration: BoxDecoration( + color: Color(0xffFF4842), + borderRadius: BorderRadius.circular(12), + ), + child: Icon( + Icons.timer_off, + color: Colors.white, + size: 18, + ), + ), + ), + ], + ), + ), + Container( + padding: EdgeInsets.symmetric( + horizontal: + Constant.getActualX(context: context, x: 35)), + // color: Colors.red, + height: Constant.getActualY( + context: context, + y: buttonLocation.value || buttonSave.value + ? 650 + : 737), + child: SingleChildScrollView( + reverse: MediaQuery.of(context).viewInsets.bottom != 0 + ? true + : false, + child: Padding( + padding: EdgeInsets.only( + bottom: + MediaQuery.of(context).viewInsets.bottom), + child: Column( + // shrinkWrap: true, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (data.tipeId == 5) + headerCard(context, Constant.backgroundBlue, + "Ambil Bahan", Constant.primaryBlue), + if (data.tipeId == 1) + headerCard(context, Constant.backgroundOrange, + "Ambil Hasil", Constant.primaryOrange), + if (data.tipeId == 3) + headerCard(context, Constant.backgroundGreen, + "Ambil Hasil", Constant.primaryGreen), + if (data.tipeId == 2) + headerCard(context, Constant.backgroundPurple, + "Ambil Hasil", Constant.primaryPurple), + if (data.tipeId == 4) + headerCard( + context, + Constant.primaryRed.withOpacity(0.25), + "Ambil Hasil", + Constant.primaryRed), + SizedBox( + height: Constant.getActualY( + context: context, y: 20), + ), + if (data.tipeId == 1) + Column( + children: [ + // Text(address.value), + displayDetailText( + context, "Nomor Lab", data.nomorLab), + SizedBox( + height: Constant.getActualY( + context: context, y: 12), + ), + displayDetailText( + context, "Nama", data.tujuan), + SizedBox( + height: Constant.getActualY( + context: context, y: 12), + ), + displayDetailText(context, "Catatan FO", + detailData.value.foNote ?? ""), + SizedBox( + height: Constant.getActualY( + context: context, y: 12), + ), + displayDetailText( + context, + "Catatan Admin", + detailData.value.adminNote ?? ""), + ], + ), + if (data.tipeId == 2) + Column( + children: [ + displayDetailText( + context, + "No. Surat jalan", + detailData.value.noSuratJalan + .toString()), + SizedBox( + height: Constant.getActualY( + context: context, y: 12), + ), + displayDetailText(context, "Nama", + detailData.value.nama.toString()), + SizedBox( + height: Constant.getActualY( + context: context, y: 12), + ), + displayDetailText( + context, + "Catatan Admin", + detailData.value.adminNote ?? ""), + ], + ), + if (data.tipeId == 3) + Column( + children: [ + displayDetailText( + context, + "No. Surat jalan", + detailData.value.noSuratJalan + .toString()), + SizedBox( + height: Constant.getActualY( + context: context, y: 12), + ), + displayDetailText(context, "Nama", + detailData.value.nama.toString()), + SizedBox( + height: Constant.getActualY( + context: context, y: 12), + ), + displayDetailText( + context, + "Catatan Admin", + detailData.value.adminNote ?? ""), + ], + ), + if (data.tipeId == 4) + Column( + children: [ + displayDetailText( + context, "Catatan", data.catatanFo), + SizedBox( + height: Constant.getActualY( + context: context, y: 12), + ), + displayDetailText( + context, + "Catatan Admin", + detailData.value.adminNote ?? ""), + ], + ), + if (data.tipeId == 5) + Column( + children: [ + displayDetailText( + context, "Tujuan", data.tujuan), + SizedBox( + height: Constant.getActualY( + context: context, y: 12), + ), + displayDetailText( + context, + "Catatan Admin", + detailData.value.adminNote ?? ""), + ], + ), + SizedBox( + height: Constant.getActualY( + context: context, y: 12), + ), + if (data.tipeId == 1 || + data.tipeId == 2 || + data.tipeId == 3) + Text( + "Pengambilan Hasil", + // maxLines: 5, + overflow: TextOverflow.visible, + style: Constant.body1(context: context) + .copyWith( + fontWeight: FontWeight.w600, + color: Constant.textPrimary), + ), + if (data.tipeId == 5) + Text( + "Pengambilan Bahan", + // maxLines: 5, + overflow: TextOverflow.visible, + style: Constant.body1(context: context) + .copyWith( + fontWeight: FontWeight.w600, + color: Constant.textPrimary), + ), + if (data.tipeId == 4) + Text( + "Pengirim", + // maxLines: 5, + overflow: TextOverflow.visible, + style: Constant.body1(context: context) + .copyWith( + fontWeight: FontWeight.w600, + color: Constant.textPrimary), + ), + SizedBox( + height: Constant.getActualY( + context: context, y: 12), + ), + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Text( + "Alamat Pengambilan", + style: Constant.body3(context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ), + Expanded( + child: SelectableText( + ": ${detailData.value.alamatPengambilan}", + style: Constant.body3(context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ) + ], + ), + SizedBox( + height: Constant.getActualY( + context: context, y: 12), + ), + // SUHU + if (data.tipeId == 5 && + kurirKonfirmasi.value == true) + Row( + children: [ + Expanded( + child: TextFormField( + controller: suhuBoxController, + keyboardType: TextInputType.number, + onFieldSubmitted: saveWorkInputSuhu, + enabled: + (suhuBoxController.text.isEmpty) + ? true + : false, + decoration: InputDecoration( + hintText: + "Suhu Box (Dalam Celcius)", + // label: "Petugas Penyerahan" , + labelText: + "Suhu Box (Dalam Celcius)", + border: OutlineInputBorder( + borderRadius: + BorderRadius.circular(8), + ), + ), + ), + ), + ], + ), + // END SUHU + SizedBox( + height: Constant.getActualY( + context: context, y: 12), + ), + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + if (pengambilanAwalLoc.value["lat"] == + "" || + pengambilanAwalLoc.value['long'] == + "") + Icon( + Icons.location_on, + color: getLocationStep.value == + getLocationStepEnum + .pengambilanAwal + ? Constant.primaryBlue + : Colors.grey, + ), + if (pengambilanAwalLoc.value["lat"] == + "" || + pengambilanAwalLoc.value['long'] == + "") + SizedBox( + width: Constant.getActualX( + context: context, x: 4), + ), + Text( + "Lokasi Awal", + style: Constant.body3( + context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ) + ], + ), + if (pengambilanAwalLoc.value["lat"] != "" || + pengambilanAwalLoc.value['long'] != "") + InkWell( + onTap: () { + MapsLauncher.launchCoordinates( + double.parse(pengambilanAwalLoc + .value["lat"]!), + double.parse(pengambilanAwalLoc + .value["long"]!)); + }, + child: Row( + children: [ + Text( + "Sudah ditandai", + style: Constant.body3( + context: context) + .copyWith( + fontWeight: + FontWeight.w600, + color: + Constant.primaryBlue), + ), + SizedBox( + width: Constant.getActualX( + context: context, x: 4), + ), + Icon( + Icons.map_outlined, + color: Constant.primaryBlue, + ), + ], + ), + ), + ], + ), + SizedBox( + height: Constant.getActualY( + context: context, y: 12), + ), + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + if (pengambilanAkhirLoc.value["lat"] == + "" || + pengambilanAkhirLoc.value['long'] == + "") + Icon( + Icons.location_on, + color: getLocationStep.value == + getLocationStepEnum + .pengambilanAkhir + ? Constant.primaryBlue + : Colors.grey, + ), + if (pengambilanAkhirLoc.value["lat"] == + "" || + pengambilanAkhirLoc.value['long'] == + "") + SizedBox( + width: Constant.getActualX( + context: context, x: 4), + ), + Text( + "Lokasi Akhir", + style: Constant.body3( + context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: getLocationStep + .value == + getLocationStepEnum + .pengambilanAkhir + ? Constant.textPrimary + : pengambilanAkhirLoc + .value[ + "lat"] == + "" || + pengambilanAkhirLoc + .value[ + 'long'] == + "" + ? Colors.grey + : Constant + .textPrimary), + ) + ], + ), + if (pengambilanAkhirLoc.value["lat"] != + "" || + pengambilanAkhirLoc.value['long'] != "") + InkWell( + onTap: () { + MapsLauncher.launchCoordinates( + double.parse(pengambilanAkhirLoc + .value["lat"]!), + double.parse(pengambilanAkhirLoc + .value["long"]!)); + }, + child: Row( + children: [ + Text( + "Sudah ditandai", + style: Constant.body3( + context: context) + .copyWith( + fontWeight: + FontWeight.w600, + color: + Constant.primaryBlue), + ), + SizedBox( + width: Constant.getActualX( + context: context, x: 4), + ), + Icon( + Icons.map_outlined, + color: Constant.primaryBlue, + ), + ], + ), + ), + ], + ), + if (petugasPengambilan.value.isNotEmpty || + catatanPengambilan.value.isNotEmpty) + SizedBox( + height: Constant.getActualY( + context: context, y: 12), + ), + if (petugasPengambilan.value.isNotEmpty || + catatanPengambilan.value.isNotEmpty) + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Expanded( + child: Text( + "Petugas Penyerahan", + style: Constant.body3( + context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ), + Expanded( + child: Text( + " : ${petugasPengambilan.value}", + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: Constant.body3( + context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ) + ], + ), + if (petugasPengambilan.value.isNotEmpty || + catatanPengambilan.value.isNotEmpty) + SizedBox( + height: Constant.getActualY( + context: context, y: 12), + ), + if (petugasPengambilan.value.isNotEmpty || + catatanPengambilan.value.isNotEmpty) + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Expanded( + child: Text( + "Catatan", + style: Constant.body3( + context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ), + Expanded( + child: Text( + " : ${catatanPengambilan.value} ", + overflow: TextOverflow.visible, + style: Constant.body3( + context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ) + ], + ), + SizedBox( + height: Constant.getActualY( + context: context, y: 12), + ), + if (data.tipeId == 1 || + data.tipeId == 2 || + data.tipeId == 3) + Text( + "Pengantaran Hasil", + // maxLines: 5, + overflow: TextOverflow.visible, + style: Constant.body1(context: context) + .copyWith( + fontWeight: FontWeight.w600, + color: Constant.textPrimary), + ), + if (data.tipeId == 5) + Text( + "Pengantaran Bahan", + // maxLines: 5, + overflow: TextOverflow.visible, + style: Constant.body1(context: context) + .copyWith( + fontWeight: FontWeight.w600, + color: Constant.textPrimary), + ), + if (data.tipeId == 4) + Text( + "Penerima", + // maxLines: 5, + overflow: TextOverflow.visible, + style: Constant.body1(context: context) + .copyWith( + fontWeight: FontWeight.w600, + color: Constant.textPrimary), + ), + SizedBox( + height: Constant.getActualY( + context: context, y: 12), + ), + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Text( + "Alamat Pengantaran", + style: Constant.body3(context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ), + Expanded( + child: SelectableText( + ": ${detailData.value.alamatPengantaran}", + style: Constant.body3(context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ) + ], + ), + SizedBox( + height: Constant.getActualY( + context: context, y: 12), + ), + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + if (pengantaranAwalLoc.value["lat"] == + "" || + pengantaranAwalLoc.value['long'] == + "") + Icon( + Icons.location_on, + color: getLocationStep.value == + getLocationStepEnum + .pengantaranAwal + ? Constant.primaryBlue + : Colors.grey, + ), + if (pengantaranAwalLoc.value["lat"] == + "" || + pengantaranAwalLoc.value['long'] == + "") + SizedBox( + width: Constant.getActualX( + context: context, x: 4), + ), + Text( + "Lokasi Awal", + style: Constant.body3( + context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: getLocationStep + .value == + getLocationStepEnum + .pengantaranAwal + ? Constant.textPrimary + : pengantaranAwalLoc.value[ + "lat"] == + "" || + pengantaranAwalLoc + .value[ + 'long'] == + "" + ? Colors.grey + : Constant + .textPrimary), + ) + ], + ), + if (pengantaranAwalLoc.value["lat"] != "" || + pengantaranAwalLoc.value['long'] != "") + InkWell( + onTap: () { + MapsLauncher.launchCoordinates( + double.parse(pengantaranAwalLoc + .value["lat"]!), + double.parse(pengantaranAwalLoc + .value["long"]!)); + }, + child: Row( + children: [ + Text( + "Sudah ditandai", + style: Constant.body3( + context: context) + .copyWith( + fontWeight: + FontWeight.w600, + color: + Constant.primaryBlue), + ), + SizedBox( + width: Constant.getActualX( + context: context, x: 4), + ), + Icon( + Icons.map_outlined, + color: Constant.primaryBlue, + ), + ], + ), + ), + ], + ), + SizedBox( + height: Constant.getActualY( + context: context, y: 12), + ), + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + if (pengantaranAkhirLoc.value["lat"] == + "" || + pengantaranAkhirLoc.value['long'] == + "") + Icon( + Icons.location_on, + color: getLocationStep.value == + getLocationStepEnum + .pengantaranAkhir + ? Constant.primaryBlue + : Colors.grey, + ), + if (pengantaranAkhirLoc.value["lat"] == + "" || + pengantaranAkhirLoc.value['long'] == + "") + SizedBox( + width: Constant.getActualX( + context: context, x: 4), + ), + Text( + "Lokasi Akhir", + style: Constant.body3( + context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: getLocationStep + .value == + getLocationStepEnum + .pengantaranAkhir + ? Constant.textPrimary + : pengantaranAwalLoc.value[ + "lat"] == + "" || + pengantaranAwalLoc + .value[ + 'long'] == + "" + ? Colors.grey + : Constant + .textPrimary), + ) + ], + ), + if (pengantaranAkhirLoc.value["lat"] != + "" || + pengantaranAkhirLoc.value['long'] != "") + InkWell( + onTap: () { + MapsLauncher.launchCoordinates( + double.parse(pengantaranAkhirLoc + .value["lat"]!), + double.parse(pengantaranAkhirLoc + .value["long"]!)); + }, + child: Row( + children: [ + Text( + "Sudah ditandai", + style: Constant.body3( + context: context) + .copyWith( + fontWeight: + FontWeight.w600, + color: + Constant.primaryBlue), + ), + SizedBox( + width: Constant.getActualX( + context: context, x: 4), + ), + Icon( + Icons.map_outlined, + color: Constant.primaryBlue, + ), + ], + ), + ), + ], + ), + if (petugasPengantaran.value.isNotEmpty || + catatanPengantaran.value.isNotEmpty) + SizedBox( + height: Constant.getActualY( + context: context, y: 12), + ), + if (petugasPengantaran.value.isNotEmpty || + catatanPengantaran.value.isNotEmpty) + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Expanded( + child: Text( + "Penerima", + style: Constant.body3( + context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ), + Expanded( + child: Text( + " : ${petugasPengantaran.value}", + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: Constant.body3( + context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ) + ], + ), + if (petugasPengantaran.value.isNotEmpty || + catatanPengantaran.value.isNotEmpty) + SizedBox( + height: Constant.getActualY( + context: context, y: 12), + ), + if (petugasPengantaran.value.isNotEmpty || + catatanPengantaran.value.isNotEmpty) + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Expanded( + child: Text( + "Catatan", + style: Constant.body3( + context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ), + Expanded( + child: Text( + " : ${catatanPengantaran.value} ", + overflow: TextOverflow.visible, + style: Constant.body3( + context: context) + .copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ) + ], + ), + if (detailData.value.patients != null) + ExpansionTile( + tilePadding: const EdgeInsets.symmetric( + horizontal: 0), + title: Text( + "List (${detailData.value.patients?.length} pasien)", + style: Constant.body3(context: context) + .copyWith( + fontWeight: FontWeight.w400), + ), + children: detailData.value.patients! + .map((e) => PatientCard( + name: e.pasien.toString(), + noLab: e.tOrderHeaderLabNumber + .toString(), + note: e.courierConfirmNote, + // onClick: () { + // print(e['name']); + // }, + )) + .toList(), + ), + SizedBox( + height: Constant.getActualY( + context: context, y: 20), + ), + if (getLocationStep.value == getLocationStepEnum.catatanPengambilan || + getLocationStep.value == + getLocationStepEnum + .catatanPengantaran || + showPetugasPenyerahan.value) + TextFormField( + controller: petugasPenyerahanController, + enabled: (getLocationStep.value == + getLocationStepEnum.done) + ? false + : true, + decoration: InputDecoration( + hintText: getLocationStep.value == + getLocationStepEnum + .catatanPengambilan + ? "Petugas Penyerahan" + : "Penerima", + // label: "Petugas Penyerahan" , + labelText: getLocationStep.value == + getLocationStepEnum + .catatanPengambilan + ? "Petugas Penyerahan" + : "Penerima", + border: OutlineInputBorder( + borderRadius: + BorderRadius.circular(8)), + ), + ), + if (getLocationStep.value == getLocationStepEnum.catatanPengambilan || + getLocationStep.value == + getLocationStepEnum + .catatanPengantaran || + getLocationStep.value == + getLocationStepEnum.done) + SizedBox( + height: Constant.getActualY( + context: context, y: 12), + ), + if (getLocationStep.value == getLocationStepEnum.catatanPengambilan || + getLocationStep.value == + getLocationStepEnum + .catatanPengantaran || + showCatatan.value) + TextFormField( + controller: catatanController, + onChanged: (value) { + if (data.tipeId == 5 && + getLocationStep.value == + getLocationStepEnum + .catatanPengambilan && + petugasPenyerahanController + .text.isNotEmpty) { + // show info + hideInfo.value = true; + } + }, + keyboardType: TextInputType.multiline, + maxLines: 3, + enabled: (getLocationStep.value == + getLocationStepEnum.done) + ? false + : true, + decoration: InputDecoration( + hintText: "Catatan", + labelText: "Catatan", + border: OutlineInputBorder( + borderRadius: + BorderRadius.circular(8)), + ), + ), + // ADD INFO JIKA data.tipeId == 5 + // if (data.tipeId == 5 && hideInfo.value) + if (data.tipeId == 5 && hideInfo.value) + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: pengambilanBahanInfo( + context, + data.tipeId, + jumlahPasienController, + edtaRutinController, + darahCitrasController, + serumController, + urineController, + pleuraController, + otherTextBahanController, + totalOtherController, + getLocationStep), + ), + // END ADD INFO + + // SUHU BOX BOTTOM START + if (showSuhuBoxBottom.value && data.tipeId == 5) + SizedBox( + height: Constant.getActualY( + context: context, y: 12), + ), + if (showSuhuBoxBottom.value && data.tipeId == 5) + Row( + children: [ + Expanded( + child: TextFormField( + controller: suhuSampleController, + enabled: (getLocationStep.value == + getLocationStepEnum.done) + ? false + : true, + keyboardType: TextInputType.number, + decoration: InputDecoration( + hintText: + "Suhu Box (Dalam Celcius)", + // label: "Petugas Penyerahan" , + labelText: + "Suhu Box (Dalam Celcius)", + border: OutlineInputBorder( + borderRadius: + BorderRadius.circular(8), + ), + ), + ), + ), + ], + ), + // SUHU BOX BOTTOM END + + if (getLocationStep.value == + getLocationStepEnum + .catatanPengambilan || + getLocationStep.value == + getLocationStepEnum.catatanPengantaran) + SizedBox( + height: Constant.getActualY( + context: context, y: 30), + ), + ], + ), + ), + ), + ) + ], + ), + ), + if (getLocationLoading.value) + const Opacity( + opacity: 0.5, + child: ModalBarrier(dismissible: false, color: Colors.black), + ), + if (getLocationLoading.value) + const Center( + child: CircularProgressIndicator(), + ), + ], + ), + ); + } + + Widget displayDetailText(BuildContext context, String teks, String value) { + return Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Text( + teks, + style: Constant.body1(context: context).copyWith( + fontWeight: FontWeight.w600, color: Constant.textPrimary), + ), + ), + Expanded( + child: Text( + " : $value", + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: Constant.body1(context: context).copyWith( + fontWeight: FontWeight.w600, color: Constant.textPrimary), + ), + ) + ], + ); + } + + Widget headerCard( + BuildContext context, Color bgColor, String teks, Color textColor) { + return Container( + height: Constant.getActualY(context: context, y: 44), + decoration: + BoxDecoration(color: bgColor, borderRadius: BorderRadius.circular(8)), + child: Align( + child: Text( + teks, + style: Constant.body1(context: context) + .copyWith(fontWeight: FontWeight.w600, color: textColor), + ), + ), + ); + } + + Widget cardDialogTundaTipe( + context, String? tipe, Color bgColor, Color textColor) { + return Expanded( + flex: 5, + child: Container( + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 3), + ), + decoration: BoxDecoration( + color: bgColor, + borderRadius: BorderRadius.circular(6), + ), + child: SizedBox( + child: Text( + tipe ?? "", + style: TextStyle( + color: textColor, + fontSize: 10, + ), + ), + ), + ), + ); + } + + // ADD WIDGET Hanya jika data.tipeId == 5 + List pengambilanBahanInfo( + BuildContext context, + int? tipeId, + TextEditingController jumlahPasienController, + TextEditingController edtaRutinController, + TextEditingController darahCitrasController, + TextEditingController serumController, + TextEditingController urineController, + TextEditingController pleuraController, + TextEditingController otherTextBahanController, + TextEditingController totalOtherController, + ValueNotifier getLocationStep) { + if (tipeId == 5) { + return [ + SizedBox( + height: Constant.getActualY(context: context, y: 20), + ), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Text( + "Info", + style: Constant.body3(context: context).copyWith( + fontWeight: FontWeight.w400, color: Constant.textGrey), + ), + ), + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 12), + ), + // Card Start + SizedBox( + width: double.infinity, + child: Container( + decoration: BoxDecoration( + color: Colors.white, + border: Border.all(color: Colors.grey, width: 0.1), + boxShadow: [ + BoxShadow( + color: Color(0XFF919EAB).withOpacity(0.12), + offset: Offset(0.0, 12), //(x,y) + blurRadius: 24, + spreadRadius: -4), + BoxShadow( + color: Color(0XFF919EAB).withOpacity(0.2), + offset: Offset(0.0, 0), //(x,y) + blurRadius: 2, + ), + ], + borderRadius: BorderRadius.circular(12), + ), + padding: EdgeInsets.fromLTRB( + Constant.getActualX(context: context, x: 12), + Constant.getActualY(context: context, y: 24), + Constant.getActualX(context: context, x: 12), + Constant.getActualY(context: context, y: 24), + ), + child: Column( + children: [ + // Jumlah Pasien + Row( + children: [ + Expanded( + child: TextFormField( + controller: jumlahPasienController, + enabled: (jumlahPasienController.text.isEmpty || + getLocationStep.value == + getLocationStepEnum.catatanPengambilan) + ? true + : false, + keyboardType: TextInputType.number, + decoration: InputDecoration( + hintText: "Jumlah Pasien", + // label: "Petugas Penyerahan" , + labelText: "Jumlah Pasien", + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + ), + ), + ], + ), + + SizedBox( + height: Constant.getActualY(context: context, y: 20), + ), + + // EDTA Rutin + Row( + children: [ + Expanded( + child: TextFormField( + controller: edtaRutinController, + enabled: (edtaRutinController.text.isEmpty || + getLocationStep.value == + getLocationStepEnum.catatanPengambilan) + ? true + : false, + keyboardType: TextInputType.number, + decoration: InputDecoration( + hintText: "EDTA Rutin", + // label: "Petugas Penyerahan" , + labelText: "EDTA Rutin", + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + ), + ), + ], + ), + + SizedBox( + height: Constant.getActualY(context: context, y: 20), + ), + + // Darah CITRAS + Row( + children: [ + Expanded( + child: TextFormField( + controller: darahCitrasController, + enabled: (darahCitrasController.text.isEmpty || + getLocationStep.value == + getLocationStepEnum.catatanPengambilan) + ? true + : false, + keyboardType: TextInputType.number, + decoration: InputDecoration( + hintText: "Darah CITRAS", + // label: "Petugas Penyerahan" , + labelText: "Darah CITRAS", + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + ), + ), + ], + ), + + SizedBox( + height: Constant.getActualY(context: context, y: 20), + ), + + // SERUM + Row( + children: [ + Expanded( + child: TextFormField( + controller: serumController, + enabled: (serumController.text.isEmpty || + getLocationStep.value == + getLocationStepEnum.catatanPengambilan) + ? true + : false, + keyboardType: TextInputType.number, + decoration: InputDecoration( + hintText: "SERUM", + // label: "Petugas Penyerahan" , + labelText: "SERUM", + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + ), + ), + ], + ), + + SizedBox( + height: Constant.getActualY(context: context, y: 20), + ), + + // URINE + Row( + children: [ + Expanded( + child: TextFormField( + controller: urineController, + enabled: (urineController.text.isEmpty || + getLocationStep.value == + getLocationStepEnum.catatanPengambilan) + ? true + : false, + keyboardType: TextInputType.number, + decoration: InputDecoration( + hintText: "URINE", + // label: "Petugas Penyerahan" , + labelText: "URINE", + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + ), + ), + ], + ), + + SizedBox( + height: Constant.getActualY(context: context, y: 20), + ), + + // PLEURA + Row( + children: [ + Expanded( + child: TextFormField( + // textAlign: TextAlign.right, + controller: pleuraController, + enabled: (pleuraController.text.isEmpty || + getLocationStep.value == + getLocationStepEnum.catatanPengambilan) + ? true + : false, + keyboardType: TextInputType.number, + decoration: InputDecoration( + hintText: "PLEURA", + // label: "Petugas Penyerahan" , + labelText: "PLEURA", + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + ), + ), + ], + ), + + SizedBox( + height: Constant.getActualY(context: context, y: 20), + ), + + // LAIN LAIN + + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Text( + "LAIN-LAIN", + style: Constant.body3(context: context).copyWith( + fontWeight: FontWeight.w400, + color: Constant.textBlack), + ), + ), + ], + ), + + SizedBox( + height: Constant.getActualY(context: context, y: 20), + ), + + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: TextFormField( + controller: otherTextBahanController, + enabled: (otherTextBahanController.text.isEmpty || + getLocationStep.value == + getLocationStepEnum.catatanPengambilan) + ? true + : false, + maxLines: 4, + decoration: InputDecoration( + hintText: "", + // label: "Petugas Penyerahan" , + labelText: "Jenis Bahan", + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + ), + ), + ], + ), + + SizedBox( + height: Constant.getActualY(context: context, y: 20), + ), + + Row( + children: [ + Expanded( + child: TextFormField( + controller: totalOtherController, + enabled: (totalOtherController.text.isEmpty || + getLocationStep.value == + getLocationStepEnum.catatanPengambilan) + ? true + : false, + keyboardType: TextInputType.number, + decoration: InputDecoration( + hintText: "Jumlah", + // label: "Petugas Penyerahan" , + labelText: "Jumlah", + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + ), + ), + ], + ), + ], + ), + ), + ), + // Card End + ]; + } else { + return [const SizedBox.shrink()]; + } + } +} diff --git a/lib/screen/work_process_screen/work_tunda_provider.dart b/lib/screen/work_process_screen/work_tunda_provider.dart new file mode 100644 index 0000000..8246b70 --- /dev/null +++ b/lib/screen/work_process_screen/work_tunda_provider.dart @@ -0,0 +1,77 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '/models/response_save_work_tunda_model.dart'; +import '../../repository/input_lain_lain_repository.dart'; +import '../../repository/work_tunda_repository.dart'; +import '../../models/response_save_pengantaran_hasil_model.dart'; +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; +import '../../repository/pengambilan_bahan_repository.dart'; +import '../../repository/pengantaran_hasil_dokter_repository.dart'; + +// 3. state provider +final workTundaSave = StateNotifierProvider((ref) => WorkTundaSaveNotifier(ref: ref)); + +// 2. notifier +class WorkTundaSaveNotifier + extends StateNotifier { + final Ref ref; + WorkTundaSaveNotifier({required this.ref}) + : super(WorkTundaSaveStateInit()); + void WorkTundaSave({ + required String courierID, + required String tipeID, + required String deliveryID, + required String note + }) async { + try { + state = WorkTundaSaveStateLoading(); + final resp = await WorkTundaRepository(dio: ref.read(dioProvider)) + .inputWorkTunda( + courierID: courierID, + tipeID: tipeID, + deliveryID: deliveryID, + note: note + ); + + state = WorkTundaSaveStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = WorkTundaSaveStateError(message: e.message ?? ""); + } else { + state = WorkTundaSaveStateError(message: e.toString()); + } + } + } +} + +// 1. state +abstract class WorkTundaSaveState extends Equatable { + final DateTime date; + const WorkTundaSaveState(this.date); + @override + List get props => [date]; +} + +class WorkTundaSaveStateInit extends WorkTundaSaveState { + WorkTundaSaveStateInit() : super(DateTime.now()); +} + +class WorkTundaSaveStateLoading extends WorkTundaSaveState { + WorkTundaSaveStateLoading() : super(DateTime.now()); +} + +class WorkTundaSaveStateError extends WorkTundaSaveState { + final String? message; + WorkTundaSaveStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class WorkTundaSaveStateDone extends WorkTundaSaveState { + final ResponseSaveWorkTundaModel model; + WorkTundaSaveStateDone({ + required this.model, + }) : super(DateTime.now()); +} diff --git a/lib/widget/chip_type.dart b/lib/widget/chip_type.dart new file mode 100644 index 0000000..e0ebeec --- /dev/null +++ b/lib/widget/chip_type.dart @@ -0,0 +1,126 @@ +import 'package:flutter/material.dart'; + +import '../../app/constant.dart'; + +class ChipType extends StatelessWidget { + ChipType( + {super.key, + required this.tipe, + required this.name, + required this.isDetailHistory}); + final int tipe; + final String name; + bool isDetailHistory = false; + @override + Widget build(BuildContext context) { + switch (tipe) { + case 5: + return Container( + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 8)), + decoration: BoxDecoration( + color: Constant.backgroundBlue, + borderRadius: BorderRadius.circular(6)), + child: Text( + name, + overflow: TextOverflow.ellipsis, + style: isDetailHistory + ? Constant.body1(context: context).copyWith( + fontWeight: FontWeight.w600, color: Constant.primaryBlue) + : Constant.caption1(context: context).copyWith( + fontWeight: FontWeight.w600, color: Constant.primaryBlue), + ), + ); + + case 1: + return Container( + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 8)), + decoration: BoxDecoration( + color: Constant.backgroundOrange, + borderRadius: BorderRadius.circular(6)), + child: Text( + name, + overflow: TextOverflow.ellipsis, + style: isDetailHistory + ? Constant.body1(context: context).copyWith( + fontWeight: FontWeight.w600, color: Constant.primaryOrange) + : Constant.caption1(context: context).copyWith( + fontWeight: FontWeight.w600, color: Constant.primaryOrange), + ), + ); + + case 3: + return Container( + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 8)), + decoration: BoxDecoration( + color: Constant.backgroundGreen, + borderRadius: BorderRadius.circular(6)), + child: Text( + name, + overflow: TextOverflow.ellipsis, + style: isDetailHistory + ? Constant.body1(context: context).copyWith( + fontWeight: FontWeight.w600, color: Constant.primaryGreen) + : Constant.caption1(context: context).copyWith( + fontWeight: FontWeight.w600, color: Constant.primaryGreen), + ), + ); + + case 2: + return Container( + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 8)), + decoration: BoxDecoration( + color: Constant.backgroundPurple, + borderRadius: BorderRadius.circular(6)), + child: Text( + name, + overflow: TextOverflow.ellipsis, + style: isDetailHistory + ? Constant.body1(context: context).copyWith( + fontWeight: FontWeight.w600, color: Constant.primaryPurple) + : Constant.caption1(context: context).copyWith( + fontWeight: FontWeight.w600, color: Constant.primaryPurple), + ), + ); + + case 4: + return Container( + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 8)), + decoration: BoxDecoration( + color: Constant.primaryRed.withOpacity(0.25), + borderRadius: BorderRadius.circular(6)), + child: Text( + name, + overflow: TextOverflow.ellipsis, + style: isDetailHistory + ? Constant.body1(context: context).copyWith( + fontWeight: FontWeight.w600, color: Constant.primaryRed) + : Constant.caption1(context: context).copyWith( + fontWeight: FontWeight.w600, color: Constant.primaryRed), + ), + ); + + default: + return Container( + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 8)), + decoration: BoxDecoration( + color: Constant.backgroundBlue, + borderRadius: BorderRadius.circular(6)), + child: Text( + name, + overflow: TextOverflow.ellipsis, + style: isDetailHistory + ? Constant.body1(context: context).copyWith( + fontWeight: FontWeight.w600, color: Constant.primaryBlue) + : Constant.caption1(context: context).copyWith( + fontWeight: FontWeight.w600, color: Constant.primaryBlue), + ), + ); + } + } +} diff --git a/lib/widget/custom_textfield.dart b/lib/widget/custom_textfield.dart new file mode 100644 index 0000000..c78ab4c --- /dev/null +++ b/lib/widget/custom_textfield.dart @@ -0,0 +1,248 @@ +import 'package:flutter/material.dart'; + +import '../../app/constant.dart'; + +// login +class CustomTextFieldLogin extends StatelessWidget { + final String hintText; + final String labelText; + final bool isPassword; + final bool obscureText; + final bool isMaxLine; + final void Function()? onToggle; + final TextEditingController? ctrl; + final void Function(String)? onChange; + final void Function(String)? onSubmitted; + final void Function()? onTap; + final void Function()? onEditingComplete; + final FocusNode? focusNode; + final bool isPrefix; + + final bool isError; + final bool isTextArea; + final bool isReadOnly; + + final bool hasFocus; + + const CustomTextFieldLogin( + {Key? key, + required this.hintText, + required this.labelText, + this.isPassword = false, + this.isMaxLine = false, + this.onToggle, + this.obscureText = false, + this.ctrl, + this.onChange, + this.onSubmitted, + this.focusNode, + this.isPrefix = false, + this.isTextArea = false, + this.isError = false, + this.isReadOnly = false, + this.hasFocus = false, + this.onTap, + this.onEditingComplete}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return TextField( + autofocus: false, + showCursor: (hasFocus) ? true : false, + readOnly: isReadOnly, + controller: ctrl, + enableInteractiveSelection: false, + style: + Constant.body1(context: context).copyWith(color: Constant.textBlack), + obscureText: obscureText, + onChanged: onChange, + onSubmitted: onSubmitted, + onTap: onTap, + onEditingComplete: onEditingComplete, + focusNode: focusNode, + maxLines: (isTextArea) ? 4 : 1, + cursorColor: Constant.primaryBlue, + decoration: InputDecoration( + // fillColor: (hasFocus) ? Constant.primaryMain : Constant.textGrey, + filled: true, + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: (hasFocus) ? Constant.primaryMain : Constant.textGrey, + width: 2), + borderRadius: BorderRadius.circular(8), + ), + enabledBorder: (hasFocus) + ? OutlineInputBorder( + // ignore: prefer_const_constructors + borderRadius: BorderRadius.all(Radius.circular(8)), + borderSide: BorderSide( + color: Constant.primaryMain, + width: 1, + ), + ) + : OutlineInputBorder( + // ignore: prefer_const_constructors + borderRadius: BorderRadius.all(Radius.circular(8)), + borderSide: BorderSide( + color: Constant.textGrey, + width: 2, + ), + ), + // hintStyle: Constant.body1_400(context: context) + // .copyWith(color: Constant.textBlack), + // labelStyle: Constant.body3_400(context: context), + hintStyle: Constant.body1(context: context).copyWith( + color: (hasFocus) ? Constant.primaryMain : Constant.textGrey), + // mainkan focus + labelStyle: Constant.body1(context: context).copyWith( + color: (hasFocus) ? Constant.primaryMain : Constant.textGrey), + labelText: labelText, + hintText: hintText, + alignLabelWithHint: true, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(30), + ), + prefixIcon: isPrefix ? const Icon(Icons.search) : null, + prefixStyle: isPrefix ? Constant.body1(context: context) : null, + suffixIcon: isPassword + ? IconButton( + alignment: Alignment.centerRight, + onPressed: onToggle, + icon: Icon( + Icons.remove_red_eye, + color: (hasFocus) ? Constant.primaryMain : Constant.textGrey, + ), + iconSize: Constant.getActualY(context: context, y: 24), + ) + : null, + ), + ); + } +} + +// selain inputan login +class CustomTextFieldInputan extends StatelessWidget { + final String hintText; + final String labelText; + final bool isPassword; + final bool obscureText; + final bool isMaxLine; + final void Function()? onToggle; + final TextEditingController? ctrl; + final void Function(String)? onChange; + final void Function(String)? onSubmitted; + final void Function()? onTap; + final void Function()? onEditingComplete; + final FocusNode? focusNode; + final bool isPrefix; + + final bool isError; + final bool isTextArea; + final bool isReadOnly; + + final bool hasFocus; + + const CustomTextFieldInputan( + {Key? key, + required this.hintText, + required this.labelText, + this.isPassword = false, + this.isMaxLine = false, + this.onToggle, + this.obscureText = false, + this.ctrl, + this.onChange, + this.onSubmitted, + this.focusNode, + this.isPrefix = false, + this.isTextArea = false, + this.isError = false, + this.isReadOnly = false, + this.hasFocus = true, + this.onTap, + this.onEditingComplete}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return TextField( + enabled: (isReadOnly == true) ? false : true, + autofocus: false, + showCursor: (hasFocus) ? true : false, + readOnly: (isReadOnly == true) ? true : false, + controller: ctrl, + enableInteractiveSelection: false, + style: + Constant.body1(context: context).copyWith(color: Constant.textBlack), + obscureText: obscureText, + onChanged: onChange, + onSubmitted: onSubmitted, + onTap: (isReadOnly == true) ? null : onTap, + onEditingComplete: onEditingComplete, + focusNode: focusNode, + maxLines: (isTextArea) ? 4 : 1, + cursorColor: Constant.primaryBlue, + decoration: InputDecoration( + // fillColor: (hasFocus) ? Constant.primaryMain : Constant.textGrey, + // filled: true, + disabledBorder: OutlineInputBorder( + // ignore: prefer_const_constructors + borderRadius: BorderRadius.all(Radius.circular(8)), + borderSide: BorderSide( + color: Constant.textGrey, + width: 1, + ), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: Constant.primaryMain, width: 2), + borderRadius: BorderRadius.circular(8), + ), + enabledBorder: (hasFocus) + ? OutlineInputBorder( + // ignore: prefer_const_constructors + borderRadius: BorderRadius.all(Radius.circular(8)), + borderSide: BorderSide( + color: Constant.primaryMain, + width: 1, + ), + ) + : OutlineInputBorder( + // ignore: prefer_const_constructors + borderRadius: BorderRadius.all(Radius.circular(8)), + borderSide: BorderSide( + color: Constant.textGrey, + width: 1, + ), + ), + // hintStyle: Constant.body1_400(context: context) + // .copyWith(color: Constant.textBlack), + // labelStyle: Constant.body3_400(context: context), + hintStyle: Constant.body1(context: context).copyWith( + color: (hasFocus) ? Constant.primaryMain : Constant.textGrey), + // mainkan focus + labelStyle: Constant.body1(context: context).copyWith( + color: (hasFocus) ? Constant.primaryMain : Constant.textGrey), + labelText: labelText, + hintText: hintText, + alignLabelWithHint: true, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(30), + ), + prefixIcon: isPrefix ? const Icon(Icons.document_scanner) : null, + prefixStyle: isPrefix ? Constant.body1(context: context) : null, + suffixIcon: isPassword + ? IconButton( + alignment: Alignment.centerRight, + onPressed: onToggle, + icon: Icon( + Icons.remove_red_eye, + color: (hasFocus) ? Constant.primaryMain : Constant.textGrey, + ), + iconSize: Constant.getActualY(context: context, y: 24), + ) + : null, + ), + ); + } +} diff --git a/lib/widget/header_widget.dart b/lib/widget/header_widget.dart new file mode 100644 index 0000000..6e59b45 --- /dev/null +++ b/lib/widget/header_widget.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; + +import '../../app/constant.dart'; + +class HeaderWidget extends StatelessWidget { + const HeaderWidget({super.key, required this.teks}); + final String teks; + + @override + Widget build(BuildContext context) { + return Container( + color: Colors.white, + height: Constant.getActualY(context: context, y: 60), + width: double.infinity, + alignment: Alignment.center, + child: Text( + teks, + style: Constant.heading3(context: context) + .copyWith(fontWeight: FontWeight.w600, color: Constant.primaryBlue), + ), + ); + } +} diff --git a/lib/widget/information_card.dart b/lib/widget/information_card.dart new file mode 100644 index 0000000..e8889a4 --- /dev/null +++ b/lib/widget/information_card.dart @@ -0,0 +1,143 @@ +import 'package:flutter/material.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; + +import '../../app/constant.dart'; +import '../../models/work_total_model.dart'; + +class InformationCard extends StatelessWidget { + const InformationCard( + {super.key, + this.bgColor, + this.elevation = false, + required this.data, + required this.loading}); + final Color? bgColor; + final bool? elevation; + final bool loading; + final WorkTotalModel data; + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(40), + color: bgColor ?? Colors.white, + // border: Border.all(width: 0), + boxShadow: [ + if (elevation == true) + BoxShadow( + color: Colors.grey.shade200, + offset: Offset(0.0, 1.0), //(x,y) + blurRadius: 2, + ), + if (elevation == false) + BoxShadow( + color: Color(0XFF0000000F).withOpacity(0.06), + offset: Offset(0.0, 2), //(x,y) + blurRadius: 30, + ), + ], + ), + height: Constant.getActualY(context: context, y: 112), + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 32), + vertical: Constant.getActualY(context: context, y: 12)), + child: SizedBox( + // color: Colors.blue, + height: Constant.getActualY(context: context, y: 88), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Image.asset( + width: Constant.getActualX(context: context, x: 24), + height: Constant.getActualX(context: context, x: 24), + "assets/icon_home_total_yellow.png"), + loading + ? SizedBox( + height: Constant.getActualX(context: context, x: 20), + width: Constant.getActualX(context: context, x: 20), + child: LoadingAnimationWidget.discreteCircle( + color: Constant.primaryBlue, size: 20)) + : Text( + "${data.totalAll}", + textAlign: TextAlign.center, + overflow: TextOverflow.ellipsis, + style: Constant.body3(context: context) + .copyWith(fontWeight: FontWeight.w600), + ), + Text( + "Total", + overflow: TextOverflow.ellipsis, + style: Constant.body3(context: context).copyWith( + fontWeight: FontWeight.w600, + color: Constant.textSecondary), + ) + ], + ), + Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Image.asset( + "assets/icon_home_diterima_yellow.png", + width: Constant.getActualX(context: context, x: 24), + height: Constant.getActualX(context: context, x: 24), + ), + loading + ? SizedBox( + height: Constant.getActualX(context: context, x: 20), + width: Constant.getActualX(context: context, x: 20), + child: LoadingAnimationWidget.discreteCircle( + color: Constant.primaryBlue, size: 20)) + : Text( + "${data.receive}", + overflow: TextOverflow.ellipsis, + style: Constant.body3(context: context).copyWith( + fontWeight: FontWeight.w600, + ), + ), + Text( + "Diterima", + overflow: TextOverflow.ellipsis, + style: Constant.body3(context: context).copyWith( + fontWeight: FontWeight.w600, + color: Constant.textSecondary), + ) + ], + ), + Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Image.asset( + width: Constant.getActualX(context: context, x: 24), + height: Constant.getActualX(context: context, x: 24), + "assets/icon_home_selesai_yellow.png"), + loading + ? SizedBox( + height: Constant.getActualX(context: context, x: 20), + width: Constant.getActualX(context: context, x: 20), + child: LoadingAnimationWidget.discreteCircle( + color: Constant.primaryBlue, size: 20)) + : Text( + "${data.done}", + overflow: TextOverflow.ellipsis, + style: Constant.body3(context: context).copyWith( + fontWeight: FontWeight.w600, + ), + ), + Text( + "Selesai", + overflow: TextOverflow.ellipsis, + style: Constant.body3(context: context).copyWith( + fontWeight: FontWeight.w600, + color: Constant.textSecondary), + ) + ], + ) + ], + ), + ), + ); + } +} diff --git a/lib/widget/information_rpt_card.dart b/lib/widget/information_rpt_card.dart new file mode 100644 index 0000000..7bf7c61 --- /dev/null +++ b/lib/widget/information_rpt_card.dart @@ -0,0 +1,89 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import '/app/constant.dart'; +import '/widget/open_pdf_widget.dart'; +import '/widget/rpt_date.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class InformationRptCard extends HookConsumerWidget { + final String courierID; + const InformationRptCard(this.courierID, {super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + // final Uri url = Uri.parse("http://devone.aplikasi.web.id/"); + // final Uri url = Uri.parse("https://www.google.com/"); + final Uri url = Uri.parse( + "${Constant.baseBirtUrl}app-birt/run?__report=report/one/rekap/rpt_total_order_kurir.rptdesign&__format=pdf&courierID=40"); + Future _launchUrl() async { + print(url); + if (!await launchUrl(url)) { + throw Exception('Could not launch $url'); + } + } + + return Container( + // padding: EdgeInsets.fromLTRB( + // Constant.getActualY(context: context, y: 30), + // Constant.getActualX(context: context, x: 24), + // Constant.getActualX(context: context, x: 10), + // Constant.getActualY(context: context, y: 30)), + height: Constant.getActualY(context: context, y: 180), + decoration: BoxDecoration( + image: DecorationImage(image: AssetImage("assets/kurir_bg_card.png")), + borderRadius: BorderRadius.circular(20)), + child: Row( + children: [ + Expanded( + flex: 5, + child: Padding( + padding: EdgeInsets.fromLTRB( + Constant.getActualY(context: context, y: 30), + Constant.getActualX(context: context, x: 24), + 0, + Constant.getActualY(context: context, y: 30)), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Status Total Pekerjaan", + style: Constant.body1(context: context).copyWith( + fontWeight: FontWeight.w700, color: Colors.white), + ), + ElevatedButton( + onPressed: () { + // Navigator.push( + // context, + // MaterialPageRoute( + // builder: (context) => + // PdfViewerPage(courierID: courierID), + // )); + + // _launchUrl(); + RptDate(context, ref, courierID); + }, + child: Text( + "Lihat RPT", + style: Constant.caption1(context: context).copyWith( + fontWeight: FontWeight.w700, + color: Color(0xff3366FF)), + ), + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12)), + elevation: 0, + backgroundColor: Colors.white)), + ], + ), + )), + Expanded( + flex: 7, + child: Container( + // color: Colors.red, + child: Image.asset("assets/kurir_icon_card.png"))) + ], + ), + ); + } +} diff --git a/lib/widget/konfirmasi_kurir_dialog.dart b/lib/widget/konfirmasi_kurir_dialog.dart new file mode 100644 index 0000000..a457cc0 --- /dev/null +++ b/lib/widget/konfirmasi_kurir_dialog.dart @@ -0,0 +1,339 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import '/widget/chip_type.dart'; + +import '../app/constant.dart'; + +showConfirmCourierDialog( + {required BuildContext context, + required int tipeid, + required String tipe, + required String tipee, + required String tujuann, + required String penerimaa, + required TextEditingController noteKurir, + required VoidCallback okCallback}) { + return showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + insetPadding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 35), + ), + title: const Text( + "Konfirmasi Pekerjaan", + style: TextStyle(fontWeight: FontWeight.bold), + ), + content: tipe == "Tolak" + ? TolakDialog( + tipeid: tipeid, + tipee: tipee, + tujuann: tujuann, + penerimaa: penerimaa, + noteKurir: noteKurir, + ) + : TerimaDialog( + tipeid: tipeid, + tipee: tipee, + tujuann: tujuann, + penerimaa: penerimaa), + actions: [ + Container( + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 15), + vertical: Constant.getActualY(context: context, y: 8)), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + OutlinedButton( + onPressed: () => Navigator.pop(context, 'Cancel'), + style: OutlinedButton.styleFrom( + side: BorderSide( + style: BorderStyle.solid, + color: Constant.textGrey), + shape: RoundedRectangleBorder( + side: BorderSide( + style: BorderStyle.solid, + color: Constant.textPrimary), + borderRadius: BorderRadius.circular(8), + ), + ), + child: const Text( + "Batal", + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black), + )), + tipe == "Tolak" + ? ElevatedButton( + onPressed: () { + okCallback(); + Navigator.of(context).pop(); + }, + // onPressed: {} () => Navigator.pop(context, 'OK'), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.red, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + child: const Text( + "Tolak", + style: TextStyle(fontWeight: FontWeight.bold), + ), + ) + : ElevatedButton( + onPressed: () { + okCallback(); + Navigator.pop(context); + }, + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + child: const Text( + "Terima", + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + ]), + ], + ), + ) + ], + shape: RoundedRectangleBorder( + side: BorderSide( + color: Theme.of(context).colorScheme.outline, + ), + borderRadius: const BorderRadius.all(Radius.circular(12)), + ), + ); + }); +} + +class TerimaDialog extends StatelessWidget { + const TerimaDialog({ + super.key, + required this.tipeid, + required this.tipee, + required this.tujuann, + required this.penerimaa, + }); + final int tipeid; + final String tipee; + final String tujuann; + final String penerimaa; + + @override + Widget build(BuildContext context) { + return Container( + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + "Anda menerima pekerjaan ini ?", + style: Constant.body2(context: context).copyWith( + fontWeight: FontWeight.w400, color: Constant.textPrimary), + ), + Container( + margin: const EdgeInsets.fromLTRB(0, 16, 0, 0), + width: double.infinity, + child: Card( + elevation: 2, + shape: RoundedRectangleBorder( + side: BorderSide( + color: Theme.of(context).colorScheme.outline, + ), + borderRadius: const BorderRadius.all(Radius.circular(12)), + ), + child: Container( + padding: const EdgeInsets.all(16), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + flex: 3, + child: Text("Tipe", + style: Constant.caption1(context: context))), + Expanded( + flex: 5, + child: ChipType( + tipe: tipeid, + name: tipee, + isDetailHistory: false, + ), + ) + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 5), + ), + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + flex: 3, + child: Text("Tujuan", + style: Constant.caption1(context: context))), + Expanded( + flex: 5, + child: Text(tujuann, + style: TextStyle( + color: Constant.textGrey, fontSize: 12))) + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 5), + ), + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + flex: 3, + child: Text("Penerima", + style: Constant.caption1(context: context))), + Expanded( + flex: 5, + child: Text(penerimaa, + style: TextStyle( + color: Constant.textGrey, fontSize: 12))) + ], + ), + ], + ), + ), + ), + ), + ], + ), + ), + ); + } +} + +class TolakDialog extends StatelessWidget { + const TolakDialog({ + super.key, + required this.tipeid, + required this.tipee, + required this.tujuann, + required this.penerimaa, + required this.noteKurir, + }); + final int tipeid; + final String tipee; + final String tujuann; + final String penerimaa; + final TextEditingController noteKurir; + + @override + Widget build(BuildContext context) { + return Container( + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + "Anda menolak pekerjaan ini ?", + style: Constant.body2(context: context).copyWith( + fontWeight: FontWeight.w400, color: Constant.textPrimary), + ), + Container( + margin: const EdgeInsets.fromLTRB(0, 16, 0, 25), + width: double.infinity, + child: Card( + elevation: 2, + shape: RoundedRectangleBorder( + side: BorderSide( + color: Theme.of(context).colorScheme.outline, + ), + borderRadius: const BorderRadius.all(Radius.circular(12)), + ), + child: Container( + padding: const EdgeInsets.all(16), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + flex: 3, + child: Text("Tipe", + style: Constant.caption1(context: context))), + Expanded( + flex: 5, + child: ChipType( + tipe: tipeid, + name: tipee, + isDetailHistory: false, + ), + ) + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 5), + ), + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + flex: 3, + child: Text("Tujuan", + style: Constant.caption1(context: context))), + Expanded( + flex: 5, + child: Text(tujuann, + style: TextStyle( + color: Constant.textGrey, fontSize: 12))) + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 5), + ), + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + flex: 3, + child: Text("Penerima", + style: Constant.caption1(context: context))), + Expanded( + flex: 5, + child: Text(penerimaa, + style: TextStyle( + color: Constant.textGrey, fontSize: 12))) + ], + ), + ], + ), + ), + ), + ), + TextField( + controller: noteKurir, + maxLines: 1, + decoration: const InputDecoration( + labelStyle: TextStyle(color: Colors.grey), + border: OutlineInputBorder( + borderSide: BorderSide(color: Colors.grey), + borderRadius: BorderRadius.all(Radius.circular(12))), + labelText: 'Alasan', + ), + ) + ], + ), + )); + } +} diff --git a/lib/widget/map_dialog.dart b/lib/widget/map_dialog.dart new file mode 100644 index 0000000..ecbc0ec --- /dev/null +++ b/lib/widget/map_dialog.dart @@ -0,0 +1,127 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_map/flutter_map.dart'; +import 'package:latlong2/latlong.dart'; + +import '../app/constant.dart'; + +typedef void MyFunction(); + +showMapDialog({ + required BuildContext context, + required double lat, + required double long, + required MyFunction confirm, + required MyFunction notConfirm, + required String address, +}) { + return showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text( + "Konfirmasi lokasi", + style: Constant.body1(context: context), + ), + content: SizedBox( + width: Constant.getActualX(context: context, x: 280), + height: Constant.getActualY(context: context, y: 200), + child: SizedBox( + width: Constant.getActualX(context: context, x: 200), + height: Constant.getActualY(context: context, y: 100), + // child: FlutterMap( + // options: MapOptions( + // center: LatLng(lat, long), + // zoom: 16, + // ), + // nonRotatedChildren: [ + // Align( + // alignment: Alignment.bottomLeft, + // child: Text( + // address, + // style: Constant.body1(context: context).copyWith( + // backgroundColor: Colors.black.withOpacity(0.5), + // color: Colors.white), + // ), + // ), + // ], + // children: [ + // TileLayer( + // urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', + // userAgentPackageName: 'com.example.app', + // ), + // MarkerLayer( + // markers: [ + // Marker( + // point: LatLng(lat, long), + // width: 100, + // height: 100, + // builder: (context) => const Icon( + // Icons.location_on, + // color: Colors.red, + // size: 50, + // )), + // ], + // ), + // ], + // ), + child: FlutterMap( + options: MapOptions( + initialCenter: LatLng(lat, long), + initialZoom: 16, + ), + children: [ + TileLayer( + urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', + userAgentPackageName: 'com.example.app', + ), + MarkerLayer( + markers: [ + Marker( + point: LatLng(lat, long), + width: 100, + height: 100, + child: const Icon( + Icons.location_on, + color: Colors.red, + size: 50, + ), + ), + ], + ), + Align( + alignment: Alignment.bottomLeft, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + address, + style: Constant.body1(context: context).copyWith( + // backgroundColor: Colors.black.withOpacity(0.5), + backgroundColor: Colors.black.withAlpha(128), + color: Colors.white, + ), + ), + ), + ), + ], + ), + ), + ), + actions: [ + OutlinedButton( + onPressed: () { + Navigator.pop(context); + notConfirm(); + }, + child: const Text("Ulangi"), + ), + SizedBox(width: Constant.getActualX(context: context, x: 5)), + ElevatedButton(onPressed: confirm, child: Text("ya")), + SizedBox(width: Constant.getActualX(context: context, x: 10)), + ], + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(24)), + ), + ); + }, + ); +} diff --git a/lib/widget/open_pdf_widget.dart b/lib/widget/open_pdf_widget.dart new file mode 100644 index 0000000..0ea181a --- /dev/null +++ b/lib/widget/open_pdf_widget.dart @@ -0,0 +1,81 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_pdfview/flutter_pdfview.dart'; +import 'dart:io'; +import 'package:http/http.dart' as http; +import 'package:intl/intl.dart'; +import '/widget/snackbar_widget.dart'; +import 'package:path/path.dart'; +import 'package:path_provider/path_provider.dart'; + +import '../app/constant.dart'; + +class PdfViewerPage extends StatefulWidget { + final String courierID; + final String date; + + const PdfViewerPage({super.key, required this.courierID, required this.date}); + @override + _PdfViewerPageState createState() => _PdfViewerPageState(); +} + +class _PdfViewerPageState extends State { + // final crr = widget.courierID; + late File Pfile; + bool isLoading = false; + + Future loadNetwork() async { + try { + setState(() { + isLoading = true; + }); + // http: //10.9.9.3/birt/run?__report=report/one/rekap/rpt_total_order_kurir.rptdesign&__format=pdf&courierID=44&date=2023-09-07 + var url = + "${Constant.baseBirtUrl}app-birt/run?__report=report/one/rekap/rpt_total_order_kurir.rptdesign&__format=pdf&courierID=${widget.courierID}&date=${widget.date}"; + final response = await http.get(Uri.parse(url)); + print(url); + final bytes = response.bodyBytes; + final filename = basename(url); + final dir = await getApplicationDocumentsDirectory(); + var file = File('${dir.path}/$filename'); + await file.writeAsBytes(bytes, flush: true); + setState(() { + Pfile = file; + }); + + print(Pfile); + setState(() { + isLoading = false; + }); + } catch (e) { + print(e); + } + } + + @override + void initState() { + loadNetwork(); + + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text( + "${widget.date}", + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + body: isLoading + ? Center(child: CircularProgressIndicator()) + : Container( + child: Center( + child: PDFView( + filePath: Pfile.path, + ), + ), + ), + ); + } +} diff --git a/lib/widget/patient_card.dart b/lib/widget/patient_card.dart new file mode 100644 index 0000000..afe89ff --- /dev/null +++ b/lib/widget/patient_card.dart @@ -0,0 +1,130 @@ +import 'package:flutter/material.dart'; + +import '../app/constant.dart'; + +typedef void MyFunction(); + +class PatientCard extends StatelessWidget { + const PatientCard({ + super.key, + required this.name, + required this.noLab, + this.note, + this.onClick, + }); + final String name; + final String noLab; + final String? note; + final MyFunction? onClick; + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: onClick, + child: Container( + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 24), + vertical: Constant.getActualY(context: context, y: 20)), + margin: EdgeInsets.symmetric( + vertical: Constant.getActualY(context: context, y: 4), + horizontal: Constant.getActualX(context: context, x: 1)), + // height: Constant.getActualY(context: context, y: 76), + decoration: BoxDecoration( + color: Colors.white, + border: Border.all(color: Colors.grey, width: 0.1), + boxShadow: [ + BoxShadow( + color: Colors.grey.shade300, + offset: const Offset(0.0, 1), //(x,y) + blurRadius: 2, + ), + ], + borderRadius: BorderRadius.circular(12)), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Text( + "Nomor lab", + style: Constant.caption1(context: context).copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ), + Expanded( + child: Text( + noLab, + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: Constant.caption1(context: context).copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ) + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 4), + ), + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Text( + "Nama", + style: Constant.caption1(context: context).copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ), + Expanded( + child: Text( + name, + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: Constant.caption1(context: context).copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ) + ], + ), + if (note != null) + SizedBox( + height: Constant.getActualY(context: context, y: 4), + ), + if (note != null) + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Text( + "Catatan FO", + style: Constant.caption1(context: context).copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ), + Expanded( + child: Text( + note ?? "", + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: Constant.caption1(context: context).copyWith( + fontWeight: FontWeight.w400, + color: Constant.textPrimary), + ), + ) + ], + ), + ], + ), + ), + ); + } +} diff --git a/lib/widget/pending_card.dart b/lib/widget/pending_card.dart new file mode 100644 index 0000000..e678144 --- /dev/null +++ b/lib/widget/pending_card.dart @@ -0,0 +1,282 @@ +import 'package:dotted_line/dotted_line.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import '../models/work_list_model.dart'; +import '/screen/work_process_screen/work_process_screen.dart'; + +import '../../app/constant.dart'; +import '../../app/route.dart'; +import '../../models/pending_work_model.dart'; +import '../provider/current_user_provider.dart'; +import 'chip_type.dart'; + +class PendingCardWidget extends HookConsumerWidget { + const PendingCardWidget({super.key, required this.data}); + + final PendingWorkModel data; + + @override + Widget build(BuildContext context, WidgetRef ref) { + Widget cardValue(String name, String value) { + return Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Text( + name, + style: Constant.caption1(context: context).copyWith( + fontWeight: FontWeight.w400, color: Constant.textPrimary), + ), + ), + Text( + ": ", + style: Constant.caption2(context: context).copyWith( + fontWeight: FontWeight.w400, color: Constant.textPrimary), + ), + Expanded( + child: Text( + "${value}", + style: Constant.caption1(context: context).copyWith( + fontWeight: FontWeight.w400, color: Constant.textPrimary), + ), + ), + ], + ); + } + + return Container( + margin: EdgeInsets.symmetric( + vertical: Constant.getActualY(context: context, y: 4)), + // color: Colors.blue, + // height: Constant.getActualY(context: context, y: 170), + width: double.infinity, + child: Card( + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + child: Container( + decoration: BoxDecoration( + color: Colors.white, + border: Border.all(color: Colors.grey, width: 0.1), + boxShadow: [ + BoxShadow( + color: Color(0XFF919EAB).withOpacity(0.12), + offset: Offset(0.0, 12), //(x,y) + blurRadius: 24, + spreadRadius: -4), + BoxShadow( + color: Color(0XFF919EAB).withOpacity(0.2), + offset: Offset(0.0, 0), //(x,y) + blurRadius: 2, + ), + ], + borderRadius: BorderRadius.circular(12)), + child: ElevatedButton( + style: ElevatedButton.styleFrom( + elevation: 0.2, + shape: RoundedRectangleBorder( + side: BorderSide(width: 0.1, color: Colors.grey), + borderRadius: BorderRadius.circular(12), + ), + backgroundColor: Colors.white, + foregroundColor: Colors.grey), + onPressed: () { + if (data.supervisor == "Dikonfirmasi") { + Navigator.pushNamed( + context, + workProcessRoute, + arguments: DetailWorkProp( + data.tipeid!, + ref.watch(currentUserProvider)?.model.user?.mCourierID ?? + "0", + data.id.toString(), + data.noLab.toString(), + data.nama.toString(), + data.note.toString()), + ); + } + }, + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 18), + vertical: Constant.getActualY(context: context, y: 20)), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + ChipType( + tipe: data.tipeid!, + name: data.tipe!, + isDetailHistory: false), + if (data.supervisor == "Dikonfirmasi") + Icon( + Icons.check_circle_outline_rounded, + color: Constant.primaryGreen, + size: 15, + ), + if (data.supervisor == "Belum dikonfirmasi") + Icon( + Icons.help_outline_rounded, + color: Constant.primaryOrange, + size: 15, + ), + if (data.supervisor == "Ditoalk") + Icon( + Icons.cancel_outlined, + color: Constant.primaryRed, + size: 15, + ), + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 12), + ), + if (data.tipeid == 1) + Column( + children: [ + cardValue("Nomot Lab", data.noLab ?? ""), + SizedBox( + height: Constant.getActualY(context: context, y: 5), + ), + cardValue("Nama", data.nama ?? ""), + SizedBox( + height: Constant.getActualY(context: context, y: 5), + ), + cardValue("Cabang Pengirim", data.branchname ?? ""), + SizedBox( + height: Constant.getActualY(context: context, y: 5), + ), + cardValue("Alamat Pengantaran", data.alamat ?? ""), + ], + ), + if (data.tipeid == 2) + Column( + children: [ + cardValue("Nomot Surat Jalan", data.noSuratJalan ?? ""), + SizedBox( + height: Constant.getActualY(context: context, y: 5), + ), + cardValue("Nama", data.nama ?? ""), + SizedBox( + height: Constant.getActualY(context: context, y: 5), + ), + cardValue("Cabang Pengirim", data.branchname ?? ""), + SizedBox( + height: Constant.getActualY(context: context, y: 5), + ), + cardValue("Alamat Pengantaran", data.alamat ?? ""), + ], + ), + if (data.tipeid == 3) + Column( + children: [ + cardValue("Nomot Surat Jalan", data.noSuratJalan ?? ""), + SizedBox( + height: Constant.getActualY(context: context, y: 5), + ), + cardValue("Nama", data.nama ?? ""), + SizedBox( + height: Constant.getActualY(context: context, y: 5), + ), + cardValue("Cabang Pengirim", data.branchname ?? ""), + SizedBox( + height: Constant.getActualY(context: context, y: 5), + ), + cardValue("Alamat Pengantaran", data.alamat ?? ""), + ], + ), + if (data.tipeid == 4) + Column( + children: [ + cardValue("Catatan", data.note ?? ""), + SizedBox( + height: Constant.getActualY(context: context, y: 5), + ), + SizedBox( + height: Constant.getActualY(context: context, y: 5), + ), + cardValue("Pengirim", data.branchaddress ?? ""), + SizedBox( + height: Constant.getActualY(context: context, y: 5), + ), + cardValue("Penerima", data.alamat ?? ""), + ], + ), + if (data.tipeid == 5) + Column( + children: [ + cardValue("Tujuan", data.nama ?? ""), + SizedBox( + height: Constant.getActualY(context: context, y: 5), + ), + SizedBox( + height: Constant.getActualY(context: context, y: 5), + ), + cardValue("Alamat", data.branchaddress ?? ""), + SizedBox( + height: Constant.getActualY(context: context, y: 5), + ), + cardValue("Cabang penerima", data.branchname ?? ""), + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 16), + ), + Row( + children: [ + Icon( + Icons.circle_outlined, + color: Colors.grey.shade300, + size: 15, + ), + SizedBox( + width: Constant.getActualX(context: context, x: 12), + ), + Expanded( + flex: 5, + child: DottedLine( + dashColor: Colors.grey.shade300, + )), + SizedBox( + width: Constant.getActualX(context: context, x: 12), + ), + if (data.statusTransaksi == "Baru") + Icon( + Icons.circle_outlined, + color: Constant.primaryBlue, + size: 15, + ), + if (data.statusTransaksi == "Proses") + Icon( + Icons.circle_outlined, + color: Constant.primaryYellow, + size: 15, + ), + if (data.statusTransaksi == "Done" || + data.statusTransaksi == "Selesai") + Icon( + Icons.circle_outlined, + color: Constant.primaryGreen, + size: 15, + ), + SizedBox( + width: Constant.getActualX(context: context, x: 4), + ), + Text( + "${data.statusTransaksi}", + style: Constant.caption2(context: context) + .copyWith(color: Constant.textPrimary), + ), + ], + ) + ], + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/widget/rpt_date.dart b/lib/widget/rpt_date.dart new file mode 100644 index 0000000..674e8d0 --- /dev/null +++ b/lib/widget/rpt_date.dart @@ -0,0 +1,117 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:intl/intl.dart'; + +import 'package:loading_animation_widget/loading_animation_widget.dart'; + +import '../app/constant.dart'; + +import '../provider/current_user_provider.dart'; + +import '../screen/home_screen/pending_work_provider.dart'; + +import '../screen/work_list_screen/work_list_provider.dart'; +import 'open_pdf_widget.dart'; + +Future RptDate(BuildContext context, WidgetRef ref, String courierID) { + return showDialog( + context: context, + barrierDismissible: true, // user must tap button! + builder: (BuildContext context) { + return AlertDialog( + title: Text( + 'Pilih Tanggal', + style: Constant.heading4(context: context).copyWith( + color: Constant.primaryBlue, fontWeight: FontWeight.w600), + ), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(12))), + content: HookConsumer(builder: (context, WidgetRef ref, z) { + final dpKey = useState(1); + final dateInput = useTextEditingController( + text: DateFormat('yyyy-MM-dd').format(DateTime.now())); + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + var ky = dpKey.value; + dpKey.value = ky + 1; + }); + return () {}; + }, []); + + return SingleChildScrollView( + child: Container( + width: double.infinity, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // SizedBox( + // height: Constant.getActualY(context: context, y: 12), + // ), + TextField( + controller: dateInput, + decoration: InputDecoration( + suffixIcon: Icon(Icons.calendar_today), + border: OutlineInputBorder(), + labelText: "Pilih Tanggal" //label text of field + ), + readOnly: true, + onTap: () async { + DateTime? pickedDate = await showDatePicker( + context: context, + initialDate: DateTime.parse(dateInput.text), + firstDate: DateTime(1950), + initialEntryMode: + DatePickerEntryMode.calendarOnly, + //DateTime.now() - not to allow to choose before today. + lastDate: DateTime(2100)); + + if (pickedDate != null) { + print( + pickedDate); //pickedDate output format => 2021-03-10 00:00:00.000 + String formattedDate = + DateFormat('yyyy-MM-dd').format(pickedDate); + print( + formattedDate); //formatted date output using intl package => 2021-03-16 + dateInput.text = + formattedDate; //set output date to TextField value. + } else {} + }, + ), + + SizedBox( + height: Constant.getActualY(context: context, y: 12), + ), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + ElevatedButton( + onPressed: () { + Navigator.pop(context); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => PdfViewerPage( + date: dateInput.text, + courierID: courierID), + )); + // print(dateInput.text); + }, + style: ElevatedButton.styleFrom( + backgroundColor: Constant.primaryBlue, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8))), + child: Text("Buka Rpt")), + ], + ), + ], + ))); + }), + ); + }, + ); +} diff --git a/lib/widget/sas_textfield.dart b/lib/widget/sas_textfield.dart new file mode 100644 index 0000000..ddc716b --- /dev/null +++ b/lib/widget/sas_textfield.dart @@ -0,0 +1,442 @@ +import 'package:flutter/material.dart'; + +import '../../app/constant.dart'; + +// define condition START +// ketika textfield terisi +InputBorder enabledBorderFillTextField = OutlineInputBorder( + borderRadius: const BorderRadius.all(Radius.circular(8)), + borderSide: BorderSide( + color: Constant.primaryMain, + width: 2, + ), +); + +// ketika focus +InputBorder enabledBorderFocusTextField = OutlineInputBorder( + borderRadius: const BorderRadius.all(Radius.circular(8)), + borderSide: BorderSide( + color: Constant.primaryMain, + width: 2, + ), +); + +// ketika no action +InputBorder enabledBorderNoAction = OutlineInputBorder( + borderRadius: const BorderRadius.all(Radius.circular(8)), + borderSide: BorderSide( + color: Constant.textGrey, + width: 2, + ), +); + +// color ketika focus +Color colorFocus = Constant.primaryMain; + +// define condition END + +// color no action +Color colorNoAction = Constant.textGrey; + +// Textfield biasa atau common (widget biasa tanpa prefix dan suffix icon) +class SasTextField extends StatelessWidget { + final String hintText; + final String labelText; + final String errorText; + final void Function()? onToggle; + final TextEditingController? controller; + final void Function(String)? onChange; + final void Function(String)? onSubmitted; + final void Function()? onTap; + final void Function()? onEditingComplete; + final FocusNode? focusNode; + + final bool isError; + final bool isReadOnly; + + final bool hasFocus; + final TextStyle? style; + + const SasTextField( + {Key? key, + required this.hintText, + required this.labelText, + this.onToggle, + required this.controller, + this.onChange, + this.onSubmitted, + this.focusNode, + this.isError = false, + this.isReadOnly = false, + this.hasFocus = false, + this.onTap, + this.onEditingComplete, + this.style, + this.errorText = ""}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return TextField( + autofocus: false, + showCursor: (hasFocus && isReadOnly == false) ? true : false, + readOnly: isReadOnly, + controller: controller, + enableInteractiveSelection: false, + style: style, + onChanged: onChange, + onSubmitted: onSubmitted, + onTap: onTap, + onEditingComplete: onEditingComplete, + focusNode: focusNode, + maxLines: 1, + cursorColor: Constant.primaryBlue, + decoration: InputDecoration( + errorText: (isError) ? errorText : null, + // filled: true, + fillColor: (hasFocus && isReadOnly == false) + ? colorFocus + : ((controller?.text != "") && isReadOnly == false) + ? colorFocus + : colorNoAction, + // filled: true, + focusedBorder: + (hasFocus && isReadOnly == false) ? enabledBorderFocusTextField : enabledBorderNoAction, + enabledBorder: (controller?.text != "" && isReadOnly == false) + ? enabledBorderFillTextField + : (hasFocus && isReadOnly == false) + ? enabledBorderFocusTextField + : enabledBorderNoAction, + hintStyle: Constant.body1(context: context).copyWith( + color: (hasFocus && isReadOnly == false) + ? colorFocus + : ((controller?.text != "") && isReadOnly == false) + ? colorFocus + : colorNoAction, + ), + // mainkan focus + labelStyle: Constant.body1(context: context).copyWith( + color: (hasFocus && isReadOnly == false) + ? colorFocus + : ((controller?.text != "") && isReadOnly == false) + ? colorFocus + : colorNoAction, + ), + labelText: labelText, + hintText: hintText, + alignLabelWithHint: true, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + ); + } +} + +// Textfield password (widget dengan suffix icon eye) +class SasTextFieldPassword extends StatelessWidget { + final String hintText; + final String labelText; + final String errorText; + final bool obscureText; + final bool isMaxLine; + final void Function()? onToggle; + final TextEditingController? controller; + final void Function(String)? onChange; + final void Function(String)? onSubmitted; + final void Function()? onTap; + final void Function()? onEditingComplete; + final FocusNode? focusNode; + + final bool isError; + final bool isReadOnly; + + final bool hasFocus; + final TextStyle? style; + + const SasTextFieldPassword( + {Key? key, + required this.hintText, + required this.labelText, + this.isMaxLine = false, + this.onToggle, + this.obscureText = false, + required this.controller, + this.onChange, + this.onSubmitted, + this.focusNode, + this.isError = false, + this.isReadOnly = false, + this.hasFocus = false, + this.onTap, + this.onEditingComplete, + this.style, + this.errorText = ""}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return TextField( + autofocus: false, + showCursor: (hasFocus) ? true : false, + readOnly: isReadOnly, + controller: controller, + // enableInteractiveSelection: false, + style: style, + obscureText: obscureText, + onChanged: onChange, + onSubmitted: onSubmitted, + onTap: onTap, + onEditingComplete: onEditingComplete, + focusNode: focusNode, + maxLines: 1, + cursorColor: Constant.primaryBlue, + decoration: InputDecoration( + // fillColor: (hasFocus) ? Constant.primaryMain : Constant.textGrey, + errorText: (isError) ? errorText : null, + // filled: true, + fillColor: (hasFocus) + ? colorFocus + : ((controller?.text != "")) + ? colorFocus + : colorNoAction, + // filled: true, + focusedBorder: + (hasFocus) ? enabledBorderFocusTextField : enabledBorderNoAction, + enabledBorder: (controller?.text != "") + ? enabledBorderFillTextField + : (hasFocus) + ? enabledBorderFocusTextField + : enabledBorderNoAction, + hintStyle: Constant.body1(context: context).copyWith( + color: (hasFocus) + ? colorFocus + : ((controller?.text != "")) + ? colorFocus + : colorNoAction, + ), + // mainkan focus + labelStyle: Constant.body1(context: context).copyWith( + color: (hasFocus) + ? colorFocus + : ((controller?.text != "")) + ? colorFocus + : colorNoAction, + ), + labelText: labelText, + hintText: hintText, + alignLabelWithHint: true, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + ), + suffixIcon: IconButton( + alignment: Alignment.centerRight, + onPressed: onToggle, + icon: Icon( + Icons.remove_red_eye, + color: (hasFocus) ? Constant.primaryMain : Constant.textGrey, + ), + iconSize: Constant.getActualY(context: context, y: 24), + ), + ), + ); + } +} + +// Textfield area (widget seperti text area di html) +class SasTextFieldArea extends StatelessWidget { + final String hintText; + final String labelText; + final void Function()? onToggle; + final TextEditingController? controller; + final void Function(String)? onChange; + final void Function(String)? onSubmitted; + final void Function()? onTap; + final void Function()? onEditingComplete; + final FocusNode? focusNode; + + final bool isError; + final bool isReadOnly; + + final bool hasFocus; + final TextStyle? style; + + const SasTextFieldArea( + {Key? key, + required this.hintText, + required this.labelText, + this.onToggle, + required this.controller, + this.onChange, + this.onSubmitted, + this.focusNode, + this.isError = false, + this.isReadOnly = false, + this.hasFocus = false, + this.onTap, + this.onEditingComplete, + this.style}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return TextField( + autofocus: false, + showCursor: (hasFocus && isReadOnly == false) ? true : false, + readOnly: isReadOnly, + controller: controller, + enableInteractiveSelection: false, + style: style, + onChanged: onChange, + onSubmitted: onSubmitted, + onTap: onTap, + onEditingComplete: onEditingComplete, + focusNode: focusNode, + maxLines: 4, + cursorColor: Constant.primaryBlue, + decoration: InputDecoration( + fillColor: (hasFocus && isReadOnly == false) + ? colorFocus + : ((controller?.text != "") && isReadOnly == false) + ? colorFocus + : colorNoAction, + // filled: true, + focusedBorder: + (hasFocus && isReadOnly == false) ? enabledBorderFocusTextField : enabledBorderNoAction, + enabledBorder: (controller?.text != "" && isReadOnly == false) + ? enabledBorderFillTextField + : (hasFocus && isReadOnly == false) + ? enabledBorderFocusTextField + : enabledBorderNoAction, + hintStyle: Constant.body1(context: context).copyWith( + color: (hasFocus && isReadOnly == false) + ? colorFocus + : ((controller?.text != "") && isReadOnly == false) + ? colorFocus + : colorNoAction, + ), + // mainkan focus + labelStyle: Constant.body1(context: context).copyWith( + color: (hasFocus && isReadOnly == false) + ? colorFocus + : ((controller?.text != "") && isReadOnly == false) + ? colorFocus + : colorNoAction, + ), + labelText: labelText, + hintText: hintText, + alignLabelWithHint: true, + ), + ); + } +} + +// Textfield search (widget dengan suffix icon search) +class SasTextFieldSearch extends StatelessWidget { + final String hintText; + final String labelText; + final String errorText; + final bool obscureText; + final bool isMaxLine; + final void Function()? onPressed; + final TextEditingController? controller; + final void Function(String)? onChange; + final void Function(String)? onSubmitted; + final void Function()? onTap; + final void Function()? onEditingComplete; + final FocusNode? focusNode; + + final bool isError; + final bool isReadOnly; + + final bool hasFocus; + final TextStyle? style; + + const SasTextFieldSearch( + {Key? key, + required this.hintText, + required this.labelText, + this.isMaxLine = false, + this.onPressed, + this.obscureText = false, + required this.controller, + this.onChange, + this.onSubmitted, + this.focusNode, + this.isError = false, + this.isReadOnly = false, + this.hasFocus = false, + this.onTap, + this.onEditingComplete, + this.style, + this.errorText = ""}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return TextField( + autofocus: false, + showCursor: (hasFocus) ? true : false, + readOnly: isReadOnly, + controller: controller, + // enableInteractiveSelection: false, + style: style, + // obscureText: obscureText, + onChanged: onChange, + onSubmitted: onSubmitted, + onTap: onTap, + onEditingComplete: onEditingComplete, + focusNode: focusNode, + maxLines: 1, + cursorColor: Constant.primaryBlue, + decoration: InputDecoration( + // fillColor: (hasFocus) ? Constant.primaryMain : Constant.textGrey, + errorText: (isError) ? errorText : null, + // filled: true, + fillColor: (hasFocus) + ? colorFocus + : ((controller?.text != "")) + ? colorFocus + : colorNoAction, + // filled: true, + focusedBorder: + (hasFocus) ? enabledBorderFocusTextField : enabledBorderNoAction, + enabledBorder: (controller?.text != "") + ? enabledBorderFillTextField + : (hasFocus) + ? enabledBorderFocusTextField + : enabledBorderNoAction, + hintStyle: Constant.body1(context: context).copyWith( + color: (hasFocus) + ? colorFocus + : ((controller?.text != "")) + ? colorFocus + : colorNoAction, + ), + // mainkan focus + labelStyle: Constant.body1(context: context).copyWith( + color: (hasFocus) + ? colorFocus + : ((controller?.text != "")) + ? colorFocus + : colorNoAction, + ), + labelText: labelText, + hintText: hintText, + alignLabelWithHint: true, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + ), + suffixIcon: IconButton( + alignment: Alignment.centerRight, + onPressed: onPressed, + icon: Icon( + Icons.search_rounded, + color: (hasFocus) ? Constant.primaryMain : Constant.textGrey, + ), + iconSize: Constant.getActualY(context: context, y: 24), + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/widget/snackbar_widget.dart b/lib/widget/snackbar_widget.dart new file mode 100644 index 0000000..1bab818 --- /dev/null +++ b/lib/widget/snackbar_widget.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; +import 'package:top_snackbar_flutter/custom_snack_bar.dart'; +import 'package:top_snackbar_flutter/top_snack_bar.dart'; + +enum snackbarType { error, info, success, warning } + +SanckbarWidget(BuildContext context, String msg, snackbarType tipe) { + switch (tipe) { + case snackbarType.error: + return showTopSnackBar( + Overlay.of(context), + CustomSnackBar.error( + message: msg, + ), + ); + break; + case snackbarType.success: + return showTopSnackBar( + Overlay.of(context), + CustomSnackBar.success( + message: msg, + ), + ); + break; + case snackbarType.info: + return showTopSnackBar( + Overlay.of(context), + CustomSnackBar.info( + message: msg, + ), + ); + break; + case snackbarType.warning: + return showTopSnackBar( + Overlay.of(context), + CustomSnackBar.info( + backgroundColor: Colors.orangeAccent, + message: msg, + ), + ); + break; + default: + return showTopSnackBar( + Overlay.of(context), + CustomSnackBar.info( + message: msg, + ), + ); + } +} diff --git a/lib/widget/work_card.dart b/lib/widget/work_card.dart new file mode 100644 index 0000000..f632805 --- /dev/null +++ b/lib/widget/work_card.dart @@ -0,0 +1,282 @@ +import 'package:dotted_line/dotted_line.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import '../models/work_list_model.dart'; +import '/screen/work_process_screen/work_process_screen.dart'; + +import '../../app/constant.dart'; +import '../../app/route.dart'; +import '../../models/pending_work_model.dart'; +import '../provider/current_user_provider.dart'; +import 'chip_type.dart'; + +class WorkCardWidget extends HookConsumerWidget { + const WorkCardWidget({super.key, required this.data}); + + final WorkListModel data; + + @override + Widget build(BuildContext context, WidgetRef ref) { + Widget cardValue(String name, String value) { + return Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Text( + name, + style: Constant.caption1(context: context).copyWith( + fontWeight: FontWeight.w400, color: Constant.textPrimary), + ), + ), + Text( + ": ", + style: Constant.caption2(context: context).copyWith( + fontWeight: FontWeight.w400, color: Constant.textPrimary), + ), + Expanded( + child: Text( + "${value}", + style: Constant.caption1(context: context).copyWith( + fontWeight: FontWeight.w400, color: Constant.textPrimary), + ), + ), + ], + ); + } + + return Container( + margin: EdgeInsets.symmetric( + vertical: Constant.getActualY(context: context, y: 4)), + // color: Colors.blue, + // height: Constant.getActualY(context: context, y: 170), + width: double.infinity, + child: Card( + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + child: Container( + decoration: BoxDecoration( + color: Colors.white, + border: Border.all(color: Colors.grey, width: 0.1), + boxShadow: [ + BoxShadow( + color: Color(0XFF919EAB).withOpacity(0.12), + offset: Offset(0.0, 12), //(x,y) + blurRadius: 24, + spreadRadius: -4), + BoxShadow( + color: Color(0XFF919EAB).withOpacity(0.2), + offset: Offset(0.0, 0), //(x,y) + blurRadius: 2, + ), + ], + borderRadius: BorderRadius.circular(12)), + child: ElevatedButton( + style: ElevatedButton.styleFrom( + elevation: 0.2, + shape: RoundedRectangleBorder( + side: BorderSide(width: 0.1, color: Colors.grey), + borderRadius: BorderRadius.circular(12), + ), + backgroundColor: Colors.white, + foregroundColor: Colors.grey), + onPressed: () { + if (data.supervisor == "Dikonfirmasi") { + Navigator.pushNamed( + context, + workProcessRoute, + arguments: DetailWorkProp( + data.tipeid!, + ref.watch(currentUserProvider)?.model.user?.mCourierID ?? + "0", + data.id.toString(), + data.noLab.toString(), + data.nama.toString(), + data.note.toString()), + ); + } + }, + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 18), + vertical: Constant.getActualY(context: context, y: 20)), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + ChipType( + tipe: data.tipeid!, + name: data.tipe!, + isDetailHistory: false), + if (data.supervisor == "Dikonfirmasi") + Icon( + Icons.check_circle_outline_rounded, + color: Constant.primaryGreen, + size: 15, + ), + if (data.supervisor == "Belum dikonfirmasi") + Icon( + Icons.help_outline_rounded, + color: Constant.primaryOrange, + size: 15, + ), + if (data.supervisor == "Ditoalk") + Icon( + Icons.cancel_outlined, + color: Constant.primaryRed, + size: 15, + ), + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 12), + ), + if (data.tipeid == 1) + Column( + children: [ + cardValue("Nomot Lab", data.noLab ?? ""), + SizedBox( + height: Constant.getActualY(context: context, y: 5), + ), + cardValue("Nama", data.nama ?? ""), + SizedBox( + height: Constant.getActualY(context: context, y: 5), + ), + cardValue("Cabang Pengirim", data.branchname ?? ""), + SizedBox( + height: Constant.getActualY(context: context, y: 5), + ), + cardValue("Alamat Pengantaran", data.alamat ?? ""), + ], + ), + if (data.tipeid == 2) + Column( + children: [ + cardValue("Nomot Surat Jalan", data.noSuratJalan ?? ""), + SizedBox( + height: Constant.getActualY(context: context, y: 5), + ), + cardValue("Nama", data.nama ?? ""), + SizedBox( + height: Constant.getActualY(context: context, y: 5), + ), + cardValue("Cabang Pengirim", data.branchname ?? ""), + SizedBox( + height: Constant.getActualY(context: context, y: 5), + ), + cardValue("Alamat Pengantaran", data.alamat ?? ""), + ], + ), + if (data.tipeid == 3) + Column( + children: [ + cardValue("Nomot Surat Jalan", data.noSuratJalan ?? ""), + SizedBox( + height: Constant.getActualY(context: context, y: 5), + ), + cardValue("Nama", data.nama ?? ""), + SizedBox( + height: Constant.getActualY(context: context, y: 5), + ), + cardValue("Cabang Pengirim", data.branchname ?? ""), + SizedBox( + height: Constant.getActualY(context: context, y: 5), + ), + cardValue("Alamat Pengantaran", data.alamat ?? ""), + ], + ), + if (data.tipeid == 4) + Column( + children: [ + cardValue("Catatan", data.note ?? ""), + SizedBox( + height: Constant.getActualY(context: context, y: 5), + ), + SizedBox( + height: Constant.getActualY(context: context, y: 5), + ), + cardValue("Pengirim", data.branchaddress ?? ""), + SizedBox( + height: Constant.getActualY(context: context, y: 5), + ), + cardValue("Penerima", data.alamat ?? ""), + ], + ), + if (data.tipeid == 5) + Column( + children: [ + cardValue("Tujuan", data.nama ?? ""), + SizedBox( + height: Constant.getActualY(context: context, y: 5), + ), + SizedBox( + height: Constant.getActualY(context: context, y: 5), + ), + cardValue("Alamat", data.branchaddress ?? ""), + SizedBox( + height: Constant.getActualY(context: context, y: 5), + ), + cardValue("Cabang penerima", data.branchname ?? ""), + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 16), + ), + Row( + children: [ + Icon( + Icons.circle_outlined, + color: Colors.grey.shade300, + size: 15, + ), + SizedBox( + width: Constant.getActualX(context: context, x: 12), + ), + Expanded( + flex: 5, + child: DottedLine( + dashColor: Colors.grey.shade300, + )), + SizedBox( + width: Constant.getActualX(context: context, x: 12), + ), + if (data.statusTransaksi == "Baru") + Icon( + Icons.circle_outlined, + color: Constant.primaryBlue, + size: 15, + ), + if (data.statusTransaksi == "Proses") + Icon( + Icons.circle_outlined, + color: Constant.primaryYellow, + size: 15, + ), + if (data.statusTransaksi == "Done" || + data.statusTransaksi == "Selesai") + Icon( + Icons.circle_outlined, + color: Constant.primaryGreen, + size: 15, + ), + SizedBox( + width: Constant.getActualX(context: context, x: 4), + ), + Text( + "${data.statusTransaksi}", + style: Constant.caption2(context: context) + .copyWith(color: Constant.textPrimary), + ), + ], + ) + ], + ), + ), + ), + ), + ), + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index 231bcf2..0ae5307 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,14 +1,6 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: - ai_barcode_scanner: - dependency: "direct main" - description: - name: ai_barcode_scanner - sha256: e77a90d9a953ba8d797cf4ff9f705c1842133fffd596f7ccc845dd08650e0543 - url: "https://pub.dev" - source: hosted - version: "0.0.7" archive: dependency: transitive description: @@ -105,6 +97,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.8" + dart_earcut: + dependency: transitive + description: + name: dart_earcut + sha256: e485001bfc05dcbc437d7bfb666316182e3522d4c3f9668048e004d0eb2ce43b + url: "https://pub.dev" + source: hosted + version: "1.2.0" dio: dependency: "direct main" description: @@ -274,10 +274,10 @@ packages: dependency: "direct main" description: name: flutter_map - sha256: "59dfd14267b691bea55760786b47d3172d47cdcc0d79ff930746a5ad123491b8" + sha256: f7d0379477274f323c3f3bc12d369a2b42eb86d1e7bd2970ae1ea3cff782449a url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "8.1.1" flutter_pdfview: dependency: "direct main" description: @@ -286,14 +286,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" - flutter_qr_bar_scanner: - dependency: "direct main" - description: - name: flutter_qr_bar_scanner - sha256: "32f8f8dc93e308f177ce8c0379f575a9a14c2d7cb98d6a136c0108e67e7ac379" - url: "https://pub.dev" - source: hosted - version: "3.0.2" flutter_riverpod: dependency: "direct main" description: @@ -412,10 +404,10 @@ packages: dependency: transitive description: name: http - sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2" + sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f url: "https://pub.dev" source: hosted - version: "0.13.6" + version: "1.3.0" http_parser: dependency: transitive description: @@ -460,10 +452,10 @@ packages: dependency: "direct main" description: name: latlong2 - sha256: "08ef7282ba9f76e8495e49e2dc4d653015ac929dce5f92b375a415d30b407ea0" + sha256: "98227922caf49e6056f91b6c56945ea1c7b166f28ffcd5fb8e72fc0b453cc8fe" url: "https://pub.dev" source: hosted - version: "0.8.2" + version: "0.9.1" leak_tracker: dependency: transitive description: @@ -520,14 +512,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.0" + logger: + dependency: transitive + description: + name: logger + sha256: be4b23575aac7ebf01f225a241eb7f6b5641eeaf43c6a8613510fc2f8cf187d1 + url: "https://pub.dev" + source: hosted + version: "2.5.0" maps_launcher: dependency: "direct main" description: name: maps_launcher - sha256: "57ba3c31db96e30f58c23fcb22a1fac6accc5683535b2cf344c534bbb9f8f910" + sha256: dac4c609720211fa6336b5903d917fe45e545c6b5665978efc3db2a3f436b1ae url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "3.0.0+1" matcher: dependency: transitive description: @@ -568,14 +568,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.5.7" - native_device_orientation: - dependency: transitive - description: - name: native_device_orientation - sha256: "744a03030fad5a332a54833cd34f1e2ee51ae9acf477b4ef85bacc8823af9937" - url: "https://pub.dev" - source: hosted - version: "1.2.1" nested: dependency: transitive description: @@ -644,42 +636,50 @@ packages: dependency: "direct main" description: name: permission_handler - sha256: bc56bfe9d3f44c3c612d8d393bd9b174eb796d706759f9b495ac254e4294baa5 + sha256: "2d070d8684b68efb580a5997eb62f675e8a885ef0be6e754fb9ef489c177470f" url: "https://pub.dev" source: hosted - version: "10.4.5" + version: "12.0.0+1" permission_handler_android: dependency: transitive description: name: permission_handler_android - sha256: "59c6322171c29df93a22d150ad95f3aa19ed86542eaec409ab2691b8f35f9a47" + sha256: "1e3bc410ca1bf84662104b100eb126e066cb55791b7451307f9708d4007350e6" url: "https://pub.dev" source: hosted - version: "10.3.6" + version: "13.0.1" permission_handler_apple: dependency: transitive description: name: permission_handler_apple - sha256: "99e220bce3f8877c78e4ace901082fb29fa1b4ebde529ad0932d8d664b34f3f5" + sha256: f000131e755c54cf4d84a5d8bd6e4149e262cc31c5a8b1d698de1ac85fa41023 url: "https://pub.dev" source: hosted - version: "9.1.4" + version: "9.4.7" + permission_handler_html: + dependency: transitive + description: + name: permission_handler_html + sha256: "38f000e83355abb3392140f6bc3030660cfaef189e1f87824facb76300b4ff24" + url: "https://pub.dev" + source: hosted + version: "0.1.3+5" permission_handler_platform_interface: dependency: transitive description: name: permission_handler_platform_interface - sha256: "6760eb5ef34589224771010805bea6054ad28453906936f843a8cc4d3a55c4a4" + sha256: eb99b295153abce5d683cac8c02e22faab63e50679b937fa1bf67d58bb282878 url: "https://pub.dev" source: hosted - version: "3.12.0" + version: "4.3.0" permission_handler_windows: dependency: transitive description: name: permission_handler_windows - sha256: cc074aace208760f1eee6aa4fae766b45d947df85bc831cde77009cdb4720098 + sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e" url: "https://pub.dev" source: hosted - version: "0.1.3" + version: "0.2.1" petitparser: dependency: transitive description: @@ -744,14 +744,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" - positioned_tap_detector_2: - dependency: transitive - description: - name: positioned_tap_detector_2 - sha256: "52e06863ad3e1f82b058fd05054fc8c9caeeb3b47d5cea7a24bd9320746059c1" - url: "https://pub.dev" - source: hosted - version: "1.0.4" posix: dependency: transitive description: @@ -925,14 +917,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.2.0" - tuple: - dependency: transitive - description: - name: tuple - sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151 - url: "https://pub.dev" - source: hosted - version: "2.0.2" typed_data: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 36e2c10..6f7c5ee 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -42,8 +42,8 @@ dependencies: hooks_riverpod: ^2.3.6 responsive_builder: ^0.7.0 intl: ^0.18.1 - ai_barcode_scanner: ^0.0.6 - flutter_qr_bar_scanner: ^3.0.2 + # ai_barcode_scanner: ^0.0.6 + # flutter_qr_bar_scanner: ^3.0.2 mobile_scanner: ^3.3.0 dropdown_button2: ^2.1.3 fancy_bottom_navigation_2: ^0.3.5 @@ -52,10 +52,14 @@ dependencies: top_snackbar_flutter: ^3.1.0 loader_overlay: ^2.3.0 geolocator: ^9.0.2 - maps_launcher: ^2.2.0 - permission_handler: ^10.4.3 - flutter_map: ^3.0.0 - latlong2: ^0.8.2 + # maps_launcher: ^2.2.0 + maps_launcher: ^3.0.0+1 + # permission_handler: ^10.4.3 + permission_handler: ^12.0.0+1 + # flutter_map: ^3.0.0 + flutter_map: ^8.1.1 + # latlong2: ^0.8.2 + latlong2: ^0.9.1 geocoding: ^2.1.0 flutter_launcher_icons: ^0.13.0 url_launcher: ^6.1.11