This commit is contained in:
2024-10-12 21:50:35 +06:30
76 changed files with 1919 additions and 862 deletions

View File

@@ -18,6 +18,7 @@
"back.button_confirm":"Are you sure you want to continue without submitting changes?", "back.button_confirm":"Are you sure you want to continue without submitting changes?",
"btn.clear":"Clear Filter", "btn.clear":"Clear Filter",
"btn.filter":"Filter", "btn.filter":"Filter",
"btn.exit_confirm":"Are you sure you want to exit?",
"Buttons End ================================================================":"", "Buttons End ================================================================":"",
"Offline Start ================================================================":"", "Offline Start ================================================================":"",
@@ -104,6 +105,7 @@
"home.invitation.request.msg":"We are working on your invitation request!", "home.invitation.request.msg":"We are working on your invitation request!",
"home.search":"Enter tracking number", "home.search":"Enter tracking number",
"home.search.btn":"Search", "home.search.btn":"Search",
"home.pin.logout.confirm":"Are you sure want to logout?",
"Home End ================================================================":"", "Home End ================================================================":"",
"Invite Start ================================================================":"", "Invite Start ================================================================":"",
@@ -351,7 +353,7 @@
"box.no_package":"There is no packages.", "box.no_package":"There is no packages.",
"box.input_cargo_weight":"Cargo type (lb)", "box.input_cargo_weight":"Cargo type (lb)",
"box.input_surcharge_item":"Surcharge items", "box.input_surcharge_item":"Surcharge items",
"box.select.cargo_type":"Select surcharge item", "box.select.cargo_type":"Add surcharge item",
"box.surcharge.item":"Surcharge items", "box.surcharge.item":"Surcharge items",
"box.package.count":"Packages ({0})", "box.package.count":"Packages ({0})",
"box.bill_to":"Bill to", "box.bill_to":"Bill to",
@@ -370,6 +372,7 @@
"box.print.btn":"Print", "box.print.btn":"Print",
"box.delivery":"Delivery", "box.delivery":"Delivery",
"box.pickup":"Pick-up", "box.pickup":"Pick-up",
"box.add.cargo_type":"Add cargo type",
"Boxes End ================================================================":"", "Boxes End ================================================================":"",
"Delivery Start ================================================================":"", "Delivery Start ================================================================":"",
@@ -393,8 +396,8 @@
"FCSshipment.departure_date":"Departure date", "FCSshipment.departure_date":"Departure date",
"FCSshipment.shipment_type":"Shipment type", "FCSshipment.shipment_type":"Shipment type",
"FCSshipment.consignee":"Consignee", "FCSshipment.consignee":"Consignee",
"FCSshipment.port_of_loading":"Port of loading", "FCSshipment.port_of_loading":"Loading port",
"FCSshipment.final_destination":"Final destination", "FCSshipment.final_destination":"Destination port",
"FCSshipment.status":"Status", "FCSshipment.status":"Status",
"FCSshipment.remark":"Remark", "FCSshipment.remark":"Remark",
"FCSshipment.commercial_invoice":"Download commercial invoice", "FCSshipment.commercial_invoice":"Download commercial invoice",
@@ -415,6 +418,7 @@
"FCSshipment.process.confirm":"Process this shipment?", "FCSshipment.process.confirm":"Process this shipment?",
"FCSshipment.arrive.confirm":"Arrive this shipment?", "FCSshipment.arrive.confirm":"Arrive this shipment?",
"FCSshipment.invoice.confirm":"Invoice this shipment?", "FCSshipment.invoice.confirm":"Invoice this shipment?",
"FCSshipment.delete.confirm":"Delete this shipment?",
"FCS Shipment End ================================================================":"", "FCS Shipment End ================================================================":"",
"Shipment Start ================================================================":"", "Shipment Start ================================================================":"",
@@ -509,6 +513,8 @@
"cargo.qty":"Quantity", "cargo.qty":"Quantity",
"cargo.amount":"Amount", "cargo.amount":"Amount",
"cargo.edit.delete.confirm":"Delete this cargo type?", "cargo.edit.delete.confirm":"Delete this cargo type?",
"cargo.display_index":"Display index",
"cargo.is_default":"Default Cargo",
"Cargo End ================================================================":"", "Cargo End ================================================================":"",
"Invoices Start ================================================================":"", "Invoices Start ================================================================":"",

View File

@@ -17,6 +17,7 @@
"back.button_confirm":"Are you sure you want to continue without submitting changes?", "back.button_confirm":"Are you sure you want to continue without submitting changes?",
"btn.clear":"Clear Filter", "btn.clear":"Clear Filter",
"btn.filter":"Filter", "btn.filter":"Filter",
"btn.exit_confirm":"Are you sure you want to exit?",
"Buttons End ================================================================":"", "Buttons End ================================================================":"",
"Offline Start ================================================================":"", "Offline Start ================================================================":"",
@@ -104,6 +105,7 @@
"home.invitation.request.msg":"ဖိတ်ကြားမှု တောင်းဆိုသည်ကို လုပ်ဆောင်နေပါသည်!", "home.invitation.request.msg":"ဖိတ်ကြားမှု တောင်းဆိုသည်ကို လုပ်ဆောင်နေပါသည်!",
"home.search":"Tracking number ရိုက်ထည့်ပါ", "home.search":"Tracking number ရိုက်ထည့်ပါ",
"home.search.btn":"ရှာမည်", "home.search.btn":"ရှာမည်",
"home.pin.logout.confirm":"အကောင့်ထွက်ရန်သေချာပြီလား?",
"Home End ================================================================":"", "Home End ================================================================":"",
"Invite Start ================================================================":"", "Invite Start ================================================================":"",
@@ -351,7 +353,7 @@
"box.no_package":"There is no packages.", "box.no_package":"There is no packages.",
"box.input_cargo_weight":"Cargo type (lb)", "box.input_cargo_weight":"Cargo type (lb)",
"box.input_surcharge_item":"Surcharge items", "box.input_surcharge_item":"Surcharge items",
"box.select.cargo_type":"Select surcharge item", "box.select.cargo_type":"Add surcharge item",
"box.surcharge.item":"Surcharge items", "box.surcharge.item":"Surcharge items",
"box.package.count":"Packages ({0})", "box.package.count":"Packages ({0})",
"box.bill_to":"Bill to", "box.bill_to":"Bill to",
@@ -370,6 +372,7 @@
"box.print.btn":"ပရင့်ထုတ်မည်", "box.print.btn":"ပရင့်ထုတ်မည်",
"box.delivery":"Delivery", "box.delivery":"Delivery",
"box.pickup":"Pick-up", "box.pickup":"Pick-up",
"box.add.cargo_type":"Add cargo type",
"Boxes End ================================================================":"", "Boxes End ================================================================":"",
"Delivery Start ================================================================":"", "Delivery Start ================================================================":"",
@@ -418,6 +421,7 @@
"FCSshipment.process.confirm":"လုပ်ငန်းစဉ်ကို အတည်ပြုပါ ?", "FCSshipment.process.confirm":"လုပ်ငန်းစဉ်ကို အတည်ပြုပါ ?",
"FCSshipment.arrive.confirm":"ရောက်ရှိကြောင်း အတည်ပြုပါ ?", "FCSshipment.arrive.confirm":"ရောက်ရှိကြောင်း အတည်ပြုပါ ?",
"FCSshipment.invoice.confirm":"Invoice this shipment ?", "FCSshipment.invoice.confirm":"Invoice this shipment ?",
"FCSshipment.delete.confirm":"တင်ပို့ခြင်းကို ဖျက်မလား?",
"FCS Shipment End ================================================================":"", "FCS Shipment End ================================================================":"",
"Shipment Start ================================================================":"", "Shipment Start ================================================================":"",
@@ -512,6 +516,8 @@
"cargo.qty":"အရေအတွက်", "cargo.qty":"အရေအတွက်",
"cargo.amount":"ပမာဏ", "cargo.amount":"ပမာဏ",
"cargo.edit.delete.confirm":"ကုန်ပစ္စည်းကို ဖျက်မလား?", "cargo.edit.delete.confirm":"ကုန်ပစ္စည်းကို ဖျက်မလား?",
"cargo.display_index":"Display index",
"cargo.is_default":"Default Cargo",
"Cargo End ================================================================":"", "Cargo End ================================================================":"",
"Invoices Start ================================================================":"", "Invoices Start ================================================================":"",

View File

@@ -35,6 +35,7 @@ import 'pages/carton/model/package_selection_model.dart';
import 'pages/carton/model/sender_selection_model.dart'; import 'pages/carton/model/sender_selection_model.dart';
import 'pages/carton/model/shipment_selection_model.dart'; import 'pages/carton/model/shipment_selection_model.dart';
import 'pages/delivery/model/delivery_model.dart'; import 'pages/delivery/model/delivery_model.dart';
import 'pages/signin/pinlogin_page.dart';
class App extends StatefulWidget { class App extends StatefulWidget {
final String title; final String title;
@@ -121,6 +122,7 @@ class _AppState extends State<App> {
'/welcome': (_) => WelcomePage(), '/welcome': (_) => WelcomePage(),
'/home': (_) => HomePage(), '/home': (_) => HomePage(),
'/language_selection': (context) => InitialLanguageSelectionPage(), '/language_selection': (context) => InitialLanguageSelectionPage(),
'/pin_login': (_) => PinLoginPage()
}; };
return routes; return routes;
} }

View File

@@ -2,35 +2,25 @@ import 'package:flutter/material.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
enum Flavor { UNSET, DEV, STAGING, PRODUCTION, LOCAL } enum Flavor { UNSET, DEV, STAGING, PRODUCTION, LOCAL }
const FlavorNames = ["Unset", "Development", "Staging", "Production", "Local"]; const FlavorNames = ["Unset", "Development", "Staging", "Production", "Local"];
class Config { class Config {
static Config _instance = Config( static Config _instance = Config(flavor: Flavor.UNSET, apiURL: "");
flavor: Flavor.UNSET,
apiURL: "",
bucketName: "",
reportProjectID: "",
reportURL: "");
final Flavor flavor; final Flavor flavor;
final String name; final String name;
final Color color; final Color color;
final String apiURL; final String apiURL;
final String reportURL;
final Level level; final Level level;
final String reportProjectID;
final String bucketName;
factory Config( factory Config(
{required Flavor flavor, {required Flavor flavor,
required String apiURL, required String apiURL,
required String reportURL, Color color = Colors.blue,
required String reportProjectID, Level level = Level.SEVERE}) {
required String bucketName, _instance = Config._internal(
Color color= Colors.blue, flavor, FlavorNames[flavor.index], color, apiURL, level);
Level level= Level.SEVERE}) {
_instance = Config._internal(flavor, FlavorNames[flavor.index], color,
apiURL, reportURL, level, reportProjectID, bucketName);
Logger.root.level = level; Logger.root.level = level;
Logger.root.onRecord.listen((record) { Logger.root.onRecord.listen((record) {
@@ -41,8 +31,7 @@ class Config {
return _instance; return _instance;
} }
Config._internal(this.flavor, this.name, this.color, this.apiURL, Config._internal(this.flavor, this.name, this.color, this.apiURL, this.level);
this.reportURL, this.level, this.reportProjectID, this.bucketName);
static Config get instance { static Config get instance {
return _instance; return _instance;

View File

@@ -9,6 +9,8 @@ const privilege_collection = "privileges";
const markets_collection = "markets"; const markets_collection = "markets";
const carton_sizes_collection = "carton_sizes"; const carton_sizes_collection = "carton_sizes";
const shipment_type_collection = "shipment_types"; const shipment_type_collection = "shipment_types";
const shipment_consignee_collection = "shipment_consignees";
const shipment_port_collection = "ports";
const packages_collection = "packages"; const packages_collection = "packages";
const messages_collection = "messages"; const messages_collection = "messages";
@@ -81,6 +83,7 @@ const privilege_receiving = "rc";
const privilege_pickup = "pku"; const privilege_pickup = "pku";
const privilege_collect = "col"; const privilege_collect = "col";
const privilege_report = "rpt"; const privilege_report = "rpt";
const privilege_pin ="pin";
// Pickup types // Pickup types
const shipment_local_pickup = "Local pickup"; const shipment_local_pickup = "Local pickup";
@@ -91,6 +94,7 @@ const shipment_courier_dropoff = "Courier drop off";
//Carton types //Carton types
const carton_from_packages = "For packages"; const carton_from_packages = "For packages";
const carton_mix_carton = "For cartons (Mix carton)"; const carton_mix_carton = "For cartons (Mix carton)";
const mix_carton = "Mix carton";
const carton_from_cartons = "From cartons"; const carton_from_cartons = "From cartons";
const carton_from_shipments = "From shipments"; const carton_from_shipments = "From shipments";
@@ -100,7 +104,7 @@ const carton_mix_box = "Mix box";
// carton Size // carton Size
const standardCarton = "Standard carton size"; const standardCarton = "Standard carton size";
const customCarton = "Custom size"; const customCarton = "Custom size";
const packageCartion = "Package"; const packageCarton = "Package";
//Mix types //Mix types
const mix_delivery = "Mix Delivery"; const mix_delivery = "Mix Delivery";
@@ -140,9 +144,9 @@ const payment_confirmed_status = "confirmed";
const payment_canceled_status = "canceled"; const payment_canceled_status = "canceled";
//Delivery types //Delivery types
const delivery_caton = "Delivery"; const delivery_caton = "delivery";
const pickup_carton = "Pick-up"; const pickup_carton = "pickup";
// bill // bill
const billToSender = "Bill to sender"; const billToSender = "sender";
const billToConsignee = "Bill to consignee"; const billToConsignee = "consignee";

View File

@@ -12,6 +12,7 @@ import 'package:fcs/helpers/firebase_helper.dart';
import 'package:firebase_auth/firebase_auth.dart' as fb; import 'package:firebase_auth/firebase_auth.dart' as fb;
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import '../../helpers/shared_pref.dart';
import '../services/services.dart'; import '../services/services.dart';
class AuthFb { class AuthFb {
@@ -108,13 +109,15 @@ class AuthFb {
Future<void> signoutStart() async { Future<void> signoutStart() async {
await userListener?.cancel(); await userListener?.cancel();
await userAuthListener?.cancel(); await userAuthListener?.cancel();
await _pinUserListener?.cancel();
} }
Future<void> signoutEnd() async { Future<void> signoutEnd() async {
await SharedPref.setPinLockOn(false);
await _fb.signOut(); await _fb.signOut();
} }
Future<void> _addUserToStream({bool refreshIdToken = false}) async { Future<User?> _addUserToStream({bool refreshIdToken = false}) async {
fb.User? firebaseUser = _fb.currentUser; fb.User? firebaseUser = _fb.currentUser;
if (firebaseUser == null) return null; if (firebaseUser == null) return null;
Map<dynamic, dynamic>? claims = Map<dynamic, dynamic>? claims =
@@ -128,15 +131,20 @@ class AuthFb {
user = await _getUserFromFirestore(cid); user = await _getUserFromFirestore(cid);
} }
if (user == null) { if (user == null) {
controller.add(null); _addUser(null);
return; return null;
} }
loadUserClaim(claims, user); loadUserClaim(claims, user);
controller.add(user); _addUser(user);
return user;
} }
loadUserClaim(Map claims, User user) { loadUserClaim(Map claims, User user) {
if (pinLoginUser != null) {
user.fcsID = pinLoginUser?.fcsID;
}
// add privileges // add privileges
String? privileges = claims["pr"]; String? privileges = claims["pr"];
if (privileges != null && privileges != "") { if (privileges != null && privileges != "") {
@@ -252,9 +260,9 @@ class AuthFb {
// get privilege from claim // get privilege from claim
Map<dynamic, dynamic> claims = await getClaims(refreshIdToken: true); Map<dynamic, dynamic> claims = await getClaims(refreshIdToken: true);
loadUserClaim(claims, user); loadUserClaim(claims, user);
controller.add(user); _addUser(user);
} catch (e) { } catch (e) {
controller.add(null); _addUser(null);
} }
}); });
} }
@@ -286,12 +294,23 @@ class AuthFb {
log.info("_startAuthListener: $user"); log.info("_startAuthListener: $user");
if (_logIn) { if (_logIn) {
controller.add(user); _addUser(user);
} }
} }
}); });
} }
_addUser(User? user) {
if (user == null) {
pinLoginUser = null;
}
if (pinLoginUser != null) {
controller.add(pinLoginUser);
} else {
controller.add(user);
}
}
Stream<User?> user() { Stream<User?> user() {
// ignore: close_sinks // ignore: close_sinks
StreamSubscription<fb.User?>? authListener; StreamSubscription<fb.User?>? authListener;
@@ -301,7 +320,7 @@ class AuthFb {
authListener = _fb.authStateChanges().listen((firebaseUser) async { authListener = _fb.authStateChanges().listen((firebaseUser) async {
_logIn = firebaseUser != null; _logIn = firebaseUser != null;
if (firebaseUser == null) { if (firebaseUser == null) {
controller.add(null); _addUser(null);
} else { } else {
_addUserToStream(refreshIdToken: true); _addUserToStream(refreshIdToken: true);
_startUserListener(); _startUserListener();
@@ -319,4 +338,51 @@ class AuthFb {
return controller.stream; return controller.stream;
} }
User? pinLoginUser;
StreamSubscription<DocumentSnapshot>? _pinUserListener;
Future<void> pinLogin(
{required String currentUserId,
required String fcsID,
required String pin}) async {
var data = await requestAPI("/pin/login", "POST",
token: await getToken(), payload: {'fcs_id': fcsID, 'pin': pin});
var userId = data['user_id'] ?? '';
if (userId == currentUserId) {
//logout
logoutPinAccount();
await SharedPref.setPinLockOn(false);
}
var pinToken = data['pin_token'] ?? '';
// Get user for pin login
_listenPinUser(userId: userId, pinToken: pinToken);
}
Future<void> _listenPinUser(
{required String userId, required String pinToken}) async {
Stream<DocumentSnapshot> snapshot = FirebaseFirestore.instance
.collection(user_collection)
.doc(userId)
.snapshots();
_pinUserListener?.cancel();
_pinUserListener = snapshot.listen((snap) async {
User user = User.fromMap(snap.data() as Map<String, dynamic>, snap.id);
pinLoginUser = user;
if (pinLoginUser != null) {
pinLoginUser!.pinToken = pinToken;
}
await _addUserToStream(refreshIdToken: true);
});
}
Future<void> logoutPinAccount() async {
_pinUserListener?.cancel();
pinLoginUser = null;
await _addUserToStream(refreshIdToken: true);
}
} }

View File

@@ -25,7 +25,24 @@ class CartonDataProvider {
Future<void> deleteCarton(Carton carton) async { Future<void> deleteCarton(Carton carton) async {
return await requestAPI("/cartons", "DELETE", return await requestAPI("/cartons", "DELETE",
payload: carton.toMap(), token: await getToken()); payload: {'id': carton.id}, token: await getToken());
}
Future<Carton> createMixCarton(Carton carton) async {
var data = await requestAPI("/cartons", "POST",
payload: carton.toMapForMix(), token: await getToken());
return Carton.fromMap(data, data['id']);
}
Future<void> updateMixCarton(Carton carton) async {
return await requestAPI("/cartons", "PUT",
payload: carton.toMapForMix(), token: await getToken());
}
Future<void> uploadCartonImages(Carton carton) async {
return await requestAPI("/cartons/upload_images", "PUT",
payload: {'id': carton.id, "photo_urls": carton.photoUrls},
token: await getToken());
} }
Future<void> deliver(Carton carton) async { Future<void> deliver(Carton carton) async {
@@ -47,7 +64,7 @@ class CartonDataProvider {
.collection(path) .collection(path)
.where("carton_number", isEqualTo: term) .where("carton_number", isEqualTo: term)
.where("is_deleted", isEqualTo: false) .where("is_deleted", isEqualTo: false)
.orderBy("created_at", descending: true) .orderBy("update_time", descending: true)
.get(); .get();
return querySnap.docs.map((e) => Carton.fromMap(e.data(), e.id)).toList(); return querySnap.docs.map((e) => Carton.fromMap(e.data(), e.id)).toList();
} catch (e) { } catch (e) {

View File

@@ -1,12 +1,13 @@
import 'dart:convert'; import 'dart:convert';
import 'package:fcs/config.dart';
import 'package:fcs/constants.dart'; import 'package:fcs/constants.dart';
import 'package:fcs/domain/entities/package.dart'; import 'package:fcs/domain/entities/package.dart';
import 'package:fcs/helpers/api_helper.dart'; import 'package:fcs/helpers/api_helper.dart';
import 'package:fcs/helpers/firebase_helper.dart'; import 'package:fcs/helpers/firebase_helper.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import '../services/services.dart';
class PackageDataProvider { class PackageDataProvider {
final log = Logger('PackageDataProvider'); final log = Logger('PackageDataProvider');
@@ -70,7 +71,7 @@ class PackageDataProvider {
try { try {
var data = await requestAPI( var data = await requestAPI(
"/api/fts/$packages_collection/$escapePackage/$limit", "GET", "/api/fts/$packages_collection/$escapePackage/$limit", "GET",
url: Config.instance.reportURL, token: await getToken()); url: Services.setting.reportURL, token: await getToken());
if (data == null) return []; if (data == null) return [];
@@ -105,7 +106,7 @@ class PackageDataProvider {
}; };
var result = await requestAPI("/api/data/packages", "POST", var result = await requestAPI("/api/data/packages", "POST",
token: await getToken(), token: await getToken(),
url: Config.instance.reportURL, url: Services.setting.reportURL,
payload: jsonEncode(data)); payload: jsonEncode(data));
if (result == null) return packages; if (result == null) return packages;
result.forEach((d) { result.forEach((d) {

View File

@@ -4,8 +4,7 @@ import 'package:fcs/domain/entities/pickup.dart';
import 'package:fcs/helpers/api_helper.dart'; import 'package:fcs/helpers/api_helper.dart';
import 'package:fcs/helpers/firebase_helper.dart'; import 'package:fcs/helpers/firebase_helper.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import '../services/services.dart';
import '../../config.dart';
class PickupDataProvider { class PickupDataProvider {
final log = Logger('PickupDataProvider'); final log = Logger('PickupDataProvider');
@@ -34,7 +33,7 @@ class PickupDataProvider {
}; };
var result = await requestAPI("/api/data/pickups", "POST", var result = await requestAPI("/api/data/pickups", "POST",
token: await getToken(), token: await getToken(),
url: Config.instance.reportURL, url: Services.setting.reportURL,
payload: jsonEncode(data)); payload: jsonEncode(data));
if (result == null) return pickups; if (result == null) return pickups;
result.forEach((d) { result.forEach((d) {

View File

@@ -38,6 +38,7 @@ class RateDataProvider {
.doc(rate_doc_id) .doc(rate_doc_id)
.collection(cargo_types_collection) .collection(cargo_types_collection)
.where("custom_duty", isEqualTo: false) .where("custom_duty", isEqualTo: false)
.orderBy("display_index", descending: false)
.snapshots(); .snapshots();
await for (var snaps in snapshots) { await for (var snaps in snapshots) {

View File

@@ -1,13 +1,14 @@
import 'dart:convert'; import 'dart:convert';
import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:fcs/config.dart';
import 'package:fcs/constants.dart'; import 'package:fcs/constants.dart';
import 'package:fcs/domain/entities/user.dart'; import 'package:fcs/domain/entities/user.dart';
import 'package:fcs/helpers/api_helper.dart'; import 'package:fcs/helpers/api_helper.dart';
import 'package:fcs/helpers/firebase_helper.dart'; import 'package:fcs/helpers/firebase_helper.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import '../services/services.dart';
class UserDataProvider { class UserDataProvider {
final log = Logger('UserDataProvider'); final log = Logger('UserDataProvider');
@@ -66,7 +67,7 @@ class UserDataProvider {
try { try {
var data = await requestAPI( var data = await requestAPI(
"/api/fts/$user_collection/$escapeBuyer/$limit", "GET", "/api/fts/$user_collection/$escapeBuyer/$limit", "GET",
url: Config.instance.reportURL, token: await getToken()); url: Services.setting.reportURL, token: await getToken());
if (data == null) return []; if (data == null) return [];

View File

@@ -74,4 +74,23 @@ class AuthServiceImp implements AuthService {
Future<void> deleteAccount() { Future<void> deleteAccount() {
return authFb.deleteAccount(); return authFb.deleteAccount();
} }
@override
Future<void> pinLogin(
{required String currentUserId,
required String fcsID,
required String pin}) {
return authFb.pinLogin(
currentUserId: currentUserId, fcsID: fcsID, pin: pin);
}
@override
User? getPinLoginUser() {
return authFb.pinLoginUser;
}
@override
Future<void> logoutPinAccount() {
return authFb.logoutPinAccount();
}
} }

View File

@@ -15,4 +15,10 @@ abstract class AuthService {
Stream<User?> getUserStream(); Stream<User?> getUserStream();
Stream<Setting> getSetting(); Stream<Setting> getSetting();
Future<void> deleteAccount(); Future<void> deleteAccount();
Future<void> pinLogin(
{required String currentUserId,
required String fcsID,
required String pin});
User? getPinLoginUser();
Future<void> logoutPinAccount();
} }

View File

@@ -37,4 +37,19 @@ class CartonServiceImp implements CartonService {
Future<List<Carton>> searchCarton(String term) { Future<List<Carton>> searchCarton(String term) {
return cartonDataProvider.searchCarton(term); return cartonDataProvider.searchCarton(term);
} }
@override
Future<Carton> createMixCarton(Carton carton) {
return cartonDataProvider.createMixCarton(carton);
}
@override
Future<void> updateMixCarton(Carton carton) {
return cartonDataProvider.updateMixCarton(carton);
}
@override
Future<void> uploadCartonImages(Carton carton) {
return cartonDataProvider.uploadCartonImages(carton);
}
} }

View File

@@ -6,4 +6,7 @@ abstract class CartonService {
Future<void> deleteCarton(Carton carton); Future<void> deleteCarton(Carton carton);
Future<void> deliver(Carton carton); Future<void> deliver(Carton carton);
Future<List<Carton>> searchCarton(String term); Future<List<Carton>> searchCarton(String term);
Future<Carton> createMixCarton(Carton carton);
Future<void> updateMixCarton(Carton carton);
Future<void> uploadCartonImages(Carton carton);
} }

View File

@@ -24,6 +24,7 @@ import 'package:fcs/data/services/rate_service.dart';
import 'package:fcs/data/services/shipment_imp.dart'; import 'package:fcs/data/services/shipment_imp.dart';
import 'package:fcs/data/services/shipment_service.dart'; import 'package:fcs/data/services/shipment_service.dart';
import '../../domain/entities/setting.dart';
import 'auth_imp.dart'; import 'auth_imp.dart';
import 'auth_service.dart'; import 'auth_service.dart';
import 'common_imp.dart'; import 'common_imp.dart';
@@ -37,6 +38,8 @@ import 'user_service.dart';
class Services { class Services {
static final Services instance = Services._(); static final Services instance = Services._();
static Setting setting = Setting();
late AuthService _authService; late AuthService _authService;
late UserService _userService; late UserService _userService;

View File

@@ -9,6 +9,8 @@ class CargoType {
double customDutyFee; double customDutyFee;
double calRate; double calRate;
double calWeight; double calWeight;
int displayIndex;
bool isDefault;
double get calAmount => calRate * calWeight; double get calAmount => calRate * calWeight;
@@ -22,7 +24,9 @@ class CargoType {
this.isChecked = false, this.isChecked = false,
this.qty = 0, this.qty = 0,
this.isCutomDuty = false, this.isCutomDuty = false,
this.customDutyFee = 0}); this.customDutyFee = 0,
this.displayIndex = 0,
this.isDefault = false});
factory CargoType.fromMap(Map<String, dynamic> map, String id) { factory CargoType.fromMap(Map<String, dynamic> map, String id) {
return CargoType( return CargoType(
@@ -33,8 +37,24 @@ class CargoType {
calWeight: map['cal_weight']?.toDouble() ?? 0, calWeight: map['cal_weight']?.toDouble() ?? 0,
calRate: map['cal_rate']?.toDouble() ?? 0, calRate: map['cal_rate']?.toDouble() ?? 0,
isCutomDuty: map['custom_duty'] ?? false, isCutomDuty: map['custom_duty'] ?? false,
customDutyFee: (map['custom_duty_fee'] ?? 0).toDouble()); customDutyFee: (map['custom_duty_fee'] ?? 0).toDouble(),
//qty: (map['qty'] ?? 0).toInt()); displayIndex: map['display_index'] ?? 0,
isDefault: map['is_defalut'] ?? false);
}
factory CargoType.fromMapForCargo(Map<String, dynamic> map, String id) {
return CargoType(
id: id,
name: map['name'],
weight: map['weight']?.toDouble() ?? 0,
displayIndex: map['display_index'] ?? 0);
}
factory CargoType.fromMapForsurcharge(Map<String, dynamic> map, String id) {
return CargoType(
id: id,
name: map['name'],
qty: map['qty'] == null ? 0 : int.tryParse(map['qty'].toString()) ?? 0);
} }
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() {
@@ -47,10 +67,20 @@ class CargoType {
'cal_rate': calRate, 'cal_rate': calRate,
'custom_duty': isCutomDuty, 'custom_duty': isCutomDuty,
'custom_duty_fee': customDutyFee, 'custom_duty_fee': customDutyFee,
'qty': qty 'qty': qty,
'is_defalut': isDefault,
'display_index': displayIndex
}; };
} }
Map<String, dynamic> toMapForCargo() {
return {"id": id, 'weight': weight};
}
Map<String, dynamic> toMapForSurcharge() {
return {"id": id, 'qty': qty};
}
CargoType clone() { CargoType clone() {
return CargoType.fromMap(toMap(), this.id!); return CargoType.fromMap(toMap(), this.id!);
} }

View File

@@ -10,33 +10,34 @@ import 'package.dart';
class Carton { class Carton {
String? id; String? id;
String? shipmentID; String? cartonNumber;
String? shipmentNumber; String? fcsShipmentID;
String? fcsShipmentNumber;
String? senderID; String? senderID;
String? senderFCSID; String? senderFCSID;
String? senderName; String? senderName;
String? boxNumber; String? consigneeFCSID;
String? status; String? consigneeName;
String? cargoDesc; String? consigneeID;
String? desc;
double width; double width;
double height; double height;
double length; double length;
String? status;
String? cargoDesc;
String? desc;
int? shipmentWeight; int? shipmentWeight;
bool? isChecked; bool? isChecked;
bool? isShipmentCarton; bool? isShipmentCarton;
String? cartonType; String? cartonType;
String? fcsID;
String? userName;
String? userID;
String? fcsShipmentID;
String? fcsShipmentNumber;
String? mixCartonID;
String? mixCartonNumber; String? mixCartonNumber;
String? cartonSizeID;
String? cartonSizeName; String? lastMile;
String? deliveryType;
String? cartonSizeType; String? cartonSizeType;
double cartonWeight; double cartonWeight;
@@ -50,19 +51,18 @@ class Carton {
List<String> photoUrls; List<String> photoUrls;
String? remark; String? remark;
DateTime? arrivedDate; DateTime? arrivedDate;
String? cartonNumber;
List<String> packageIDs; List<String> packageIDs;
List<Package> packages; List<Package> packages;
List<CargoType> cargoTypes = []; List<CargoType> cargoTypes = [];
List<CargoType> surchareItems = [];
DeliveryAddress? deliveryAddress; DeliveryAddress? deliveryAddress;
Shipment? shipment; Shipment? shipment;
//for mix box //for mix carton
String? mixBoxType; List<Carton> cartons;
List<Carton> mixCartons; List<String> cartonIDs;
List<String> mixCartonIDs;
int get amount => (rate * weight); int get amount => (rate * weight);
@@ -123,12 +123,9 @@ class Carton {
Carton( Carton(
{this.id, {this.id,
this.shipmentID,
this.shipmentNumber,
this.senderID, this.senderID,
this.senderFCSID, this.senderFCSID,
this.senderName, this.senderName,
this.boxNumber,
this.desc, this.desc,
this.width = 0, this.width = 0,
this.height = 0, this.height = 0,
@@ -136,9 +133,9 @@ class Carton {
this.shipmentWeight, this.shipmentWeight,
this.isChecked = false, this.isChecked = false,
this.cartonType, this.cartonType,
this.fcsID, this.consigneeFCSID,
this.userID, this.consigneeID,
this.userName, this.consigneeName,
this.rate = 0, this.rate = 0,
this.weight = 0, this.weight = 0,
this.packageType, this.packageType,
@@ -150,47 +147,60 @@ class Carton {
this.shipmentHistory = const [], this.shipmentHistory = const [],
this.packages = const [], this.packages = const [],
this.cargoTypes = const [], this.cargoTypes = const [],
this.surchareItems = const [],
this.cartonNumber, this.cartonNumber,
this.billTo, this.billTo,
this.fcsShipmentID, this.fcsShipmentID,
this.fcsShipmentNumber, this.fcsShipmentNumber,
this.packageIDs = const [], this.packageIDs = const [],
this.mixCartonID,
this.mixCartonNumber, this.mixCartonNumber,
this.isShipmentCarton = false, this.isShipmentCarton = false,
this.deliveryAddress, this.deliveryAddress,
this.cartonSizeID,
this.cartonSizeName,
this.cartonSizeType, this.cartonSizeType,
this.deliveryType, this.lastMile,
this.mixBoxType, this.cartons = const [],
this.mixCartons = const [], this.cartonIDs = const [],
this.mixCartonIDs = const [],
this.cartonWeight = 0, this.cartonWeight = 0,
this.photoUrls = const [], this.photoUrls = const [],
this.isSelected = false}); this.isSelected = false});
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() {
List _cargoTypes = cargoTypes.map((c) => c.toMap()).toList(); var _types = cargoTypes.where((t) => t.weight != 0).toList();
List _packages = packages.map((c) => c.toJson()).toList(); var _cargoTypes = _types.map((c) => c.toMapForCargo()).toList();
List _mixCartons = mixCartons.map((c) => c.toJson()).toList();
var _packagesIds = packages.map((c) => c.id).toList();
var _surchareItems =
surchareItems.map((c) => c.toMapForSurcharge()).toList();
return { return {
'id': id, 'id': id,
'carton_type': cartonType,
'fcs_shipment_id': fcsShipmentID, 'fcs_shipment_id': fcsShipmentID,
'user_id': userID, 'sender_user_id': senderID,
'cargo_types': _cargoTypes, 'consignee_user_id': consigneeID,
'packages': _packages, 'bill_to': billTo,
'last_mile': lastMile,
'length': length, 'length': length,
'width': width, 'width': width,
'height': height, 'height': height,
'delivery_address': deliveryAddress?.toMap(), 'package_ids': _packagesIds,
'cargo_types': _cargoTypes,
'surcharge_items': _surchareItems,
};
}
Map<String, dynamic> toMapForMix() {
var _cartonIds = cartons.map((c) => c.id).toList();
return {
'id': id,
'carton_type': cartonType, 'carton_type': cartonType,
'mix_carton_id': mixCartonID, 'fcs_shipment_id': fcsShipmentID,
'mix_box_type': mixBoxType, 'length': length,
'mix_cartons': _mixCartons, 'width': width,
'sender_id': senderID, 'height': height,
'sender_fcs_id': senderFCSID, 'carton_ids': _cartonIds,
'sender_name': senderName
}; };
} }
@@ -199,75 +209,48 @@ class Carton {
map['arrived_date'] == null ? null : (map['arrived_date'] as Timestamp); map['arrived_date'] == null ? null : (map['arrived_date'] as Timestamp);
var da = map['delivery_address']; var da = map['delivery_address'];
var _da = da != null ? DeliveryAddress.fromMap(da, da["id"]) : null; var _da = da != null ? DeliveryAddress.fromMap(da, da["id"]) : null;
var cargoTypesMaps = var cargoTypesMaps =
List<Map<String, dynamic>>.from(map['cargo_types'] ?? []); List<Map<String, dynamic>>.from(map['cargo_types'] ?? []);
var cargoTypes = var cargoTypes = cargoTypesMaps
cargoTypesMaps.map((e) => CargoType.fromMap(e, e["id"])).toList(); .map((e) => CargoType.fromMapForCargo(e, e["id"]))
var mixCartonsMaps = .toList();
List<Map<String, dynamic>>.from(map['mix_cartons'] ?? []);
var _mixCartons = var surchargeItemMaps =
mixCartonsMaps.map((e) => Carton.fromMap(e, e["id"])).toList(); List<Map<String, dynamic>>.from(map['surcharge_items'] ?? []);
var surchageItems = surchargeItemMaps
.map((e) => CargoType.fromMapForsurcharge(e, e["id"]))
.toList();
List<String> _photoUrls = List<String> _photoUrls =
map['photo_urls'] == null ? [] : List.from(map['photo_urls']); map['photo_urls'] == null ? [] : List.from(map['photo_urls']);
return Carton( return Carton(
id: docID, id: docID,
arrivedDate: _arrivedDate != null ? _arrivedDate.toDate() : null, arrivedDate: _arrivedDate != null ? _arrivedDate.toDate() : null,
shipmentID: map['shipment_id'],
shipmentNumber: map['shipment_number'],
// receiverNumber: map['receiver_number'],
boxNumber: map['box_number'],
length: double.tryParse(map['length'].toString()) ?? 0, length: double.tryParse(map['length'].toString()) ?? 0,
width: double.tryParse(map['width'].toString()) ?? 0, width: double.tryParse(map['width'].toString()) ?? 0,
height: double.tryParse(map['height'].toString()) ?? 0, height: double.tryParse(map['height'].toString()) ?? 0,
userName: map['user_name'],
fcsID: map['fcs_id'],
cartonType: map['carton_type'], cartonType: map['carton_type'],
cartonNumber: map['carton_number'], cartonNumber: map['carton_number'],
userID: map['user_id'],
fcsShipmentID: map['fcs_shipment_id'], fcsShipmentID: map['fcs_shipment_id'],
fcsShipmentNumber: map['fcs_shipment_number'], fcsShipmentNumber: map['fcs_shipment_number'],
isShipmentCarton: map['is_shipment_carton'],
mixCartonID: map['mix_carton_id'],
mixCartonNumber: map['mix_carton_number'],
status: map['status'], status: map['status'],
packageIDs: List<String>.from(map['package_ids'] ?? []), packageIDs: List<String>.from(map['package_ids'] ?? []),
deliveryAddress: _da, deliveryAddress: _da,
cargoTypes: cargoTypes, cargoTypes: cargoTypes,
mixBoxType: map['mix_box_type'], surchareItems: surchageItems,
mixCartons: _mixCartons, senderID: map['sender_user_id'],
senderID: map['sender_id'],
senderFCSID: map['sender_fcs_id'], senderFCSID: map['sender_fcs_id'],
senderName: map['sender_name'], senderName: map['sender_user_name'],
mixCartonIDs: List<String>.from(map['mix_carton_ids'] ?? []), consigneeID: map['consignee_user_id'],
consigneeName: map['consignee_user_name'],
consigneeFCSID: map['consignee_fcs_id'],
cartonIDs: List<String>.from(map['carton_ids'] ?? []),
cartonWeight: (map['carton_weight'] ?? 0).toDouble(), cartonWeight: (map['carton_weight'] ?? 0).toDouble(),
photoUrls: _photoUrls, photoUrls: _photoUrls,
); billTo: map['bill_to'] ?? '',
} lastMile: map['last_mile'] ?? "");
Map<String, dynamic> toJson() {
List _cargoTypes = cargoTypes.map((c) => c.toMap()).toList();
List _packages = packages.map((c) => c.toJson()).toList();
List _mixCartons = mixCartons.map((c) => c.toJson()).toList();
return {
'id': id,
'fcs_shipment_id': fcsShipmentID,
'user_id': userID,
'cargo_types': _cargoTypes,
'packages': _packages,
'length': length,
'width': width,
'height': height,
'delivery_address': deliveryAddress?.toMap(),
'carton_type': cartonType,
'mix_carton_id': mixCartonID,
'mix_box_type': mixBoxType,
'mix_cartons': _mixCartons,
'sender_id': senderID,
'sender_fcs_id': senderFCSID,
'sender_name': senderName,
"photo_urls": photoUrls
};
} }
@override @override

View File

@@ -4,13 +4,16 @@ class FcsShipment {
String? id; String? id;
String? shipmentNumber; String? shipmentNumber;
DateTime? cutoffDate; DateTime? cutoffDate;
DateTime? etaDate;
String? shipmentTypeId; String? shipmentTypeId;
String? shipTypeName; String? shipmentTypeName;
DateTime? arrivalDate;
DateTime? departureDate; DateTime? departureDate;
String? consignee; String? consigneeId;
String? port; String? consigneeName;
String? destination; String? loadingPortId;
String? loadingPortName;
String? destinationPortId;
String? destinationPortName;
String? status; String? status;
String? reportName; String? reportName;
@@ -19,13 +22,16 @@ class FcsShipment {
this.shipmentNumber, this.shipmentNumber,
this.cutoffDate, this.cutoffDate,
this.shipmentTypeId, this.shipmentTypeId,
this.shipTypeName, this.shipmentTypeName,
this.status, this.status,
this.arrivalDate, this.etaDate,
this.departureDate, this.departureDate,
this.consignee, this.consigneeId,
this.port, this.consigneeName,
this.destination, this.loadingPortId,
this.loadingPortName,
this.destinationPortId,
this.destinationPortName,
this.reportName, this.reportName,
}); });
@@ -33,45 +39,45 @@ class FcsShipment {
var _cutoffDate = var _cutoffDate =
map['cutoff_date'] == null ? null : (map['cutoff_date'] as Timestamp); map['cutoff_date'] == null ? null : (map['cutoff_date'] as Timestamp);
var _arrivalDate = var _arrivalDate =
map['arrival_date'] == null ? null : (map['arrival_date'] as Timestamp); map['eta_date'] == null ? null : (map['eta_date'] as Timestamp);
return FcsShipment( return FcsShipment(
id: docID, id: docID,
cutoffDate: _cutoffDate != null ? _cutoffDate.toDate() : null, cutoffDate: _cutoffDate != null ? _cutoffDate.toDate() : null,
arrivalDate: _arrivalDate != null ? _arrivalDate.toDate() : null, etaDate: _arrivalDate != null ? _arrivalDate.toDate() : null,
shipmentNumber: map['shipment_number'], shipmentNumber: map['shipment_number'],
shipTypeName: map['shipment_type_name'],
shipmentTypeId: map['shipment_type_id'] ?? "", shipmentTypeId: map['shipment_type_id'] ?? "",
shipmentTypeName: map['shipment_type_name'],
status: map['status'], status: map['status'],
consignee: map['consignee'], consigneeId: map['shipment_consignee_id'],
port: map['port'], consigneeName: map['shipment_consignee_name'],
destination: map['destination'], loadingPortId: map['loading_port_id'],
); loadingPortName: map['loading_port_name'],
destinationPortId: map['destination_port_id'],
destinationPortName: map['destination_port_name']);
} }
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() {
return { return {
'id': id, 'id': id,
'shipment_number': shipmentNumber, 'shipment_number': shipmentNumber,
'shipment_type_id': shipmentTypeId,
'cutoff_date': cutoffDate?.toUtc().toIso8601String(), 'cutoff_date': cutoffDate?.toUtc().toIso8601String(),
'arrival_date': arrivalDate?.toUtc().toIso8601String(), 'eta_date': etaDate?.toUtc().toIso8601String(),
'consignee': consignee, 'shipment_type_id': shipmentTypeId,
'port': port, 'shipment_consignee_id': consigneeId,
'destination': destination, 'loading_port_id': loadingPortId,
// 'status': status, 'destination_port_id': destinationPortId
// 'report_name': reportName,
}; };
} }
bool isChangedForEdit(FcsShipment fcsShipment) { bool isChangedForEdit(FcsShipment fcsShipment) {
return fcsShipment.shipmentNumber != this.shipmentNumber || return fcsShipment.shipmentNumber != this.shipmentNumber ||
fcsShipment.cutoffDate != this.cutoffDate || fcsShipment.cutoffDate != this.cutoffDate ||
fcsShipment.arrivalDate != this.arrivalDate || fcsShipment.etaDate != this.etaDate ||
fcsShipment.shipmentTypeId != this.shipmentTypeId || fcsShipment.shipmentTypeId != this.shipmentTypeId ||
fcsShipment.consignee != this.consignee || fcsShipment.consigneeId != this.consigneeId ||
fcsShipment.port != this.port || fcsShipment.loadingPortId != this.loadingPortId ||
fcsShipment.destination != this.destination; fcsShipment.destinationPortId != this.destinationPortId;
} }
@override @override

View File

@@ -10,6 +10,9 @@ List<Day> dayLists = [
class Setting { class Setting {
final int supportBuildNum; final int supportBuildNum;
final String reportURL;
final String reportProjectID;
final String bucketName;
// contact page // contact page
String? usaAddress; String? usaAddress;
String? mmAddress; String? mmAddress;
@@ -44,7 +47,10 @@ class Setting {
this.shipmentTypes = const [], this.shipmentTypes = const [],
this.courierWebsite, this.courierWebsite,
this.deactivateTextEn, this.deactivateTextEn,
this.deactivateTextMm}); this.deactivateTextMm,
this.bucketName = '',
this.reportProjectID = '',
this.reportURL = ''});
factory Setting.fromMap(Map<String, dynamic> map) { factory Setting.fromMap(Map<String, dynamic> map) {
return Setting( return Setting(
@@ -62,9 +68,11 @@ class Setting {
termsMm: map['terms_mm_markdown'], termsMm: map['terms_mm_markdown'],
shipmentTypes: List.from(map['shipment_types']), shipmentTypes: List.from(map['shipment_types']),
courierWebsite: map['courier_website'], courierWebsite: map['courier_website'],
deactivateTextEn: map['deactivate_text_en']??"", deactivateTextEn: map['deactivate_text_en'] ?? "",
deactivateTextMm: map['deactivate_text_mm']??"" deactivateTextMm: map['deactivate_text_mm'] ?? "",
); reportProjectID: map['report_project_id'] ?? "",
reportURL: map['report_url'] ?? "",
bucketName: map['bucket_name'] ?? '');
} }
@override @override

View File

@@ -0,0 +1,17 @@
class ShipmentConsignee {
String id;
String name;
ShipmentConsignee({required this.id, required this.name});
factory ShipmentConsignee.fromMap(Map<String, dynamic> map, String id) {
return ShipmentConsignee(id: id, name: map['name'] ?? "");
}
@override
bool operator ==(Object other) =>
other is ShipmentConsignee && other.id == id;
@override
int get hashCode => id.hashCode;
}

View File

@@ -0,0 +1,17 @@
class ShipmentPort {
String id;
String name;
ShipmentPort({required this.id, required this.name});
factory ShipmentPort.fromMap(Map<String, dynamic> map, String id) {
return ShipmentPort(id: id, name: map['name'] ?? "");
}
@override
bool operator ==(Object other) =>
other is ShipmentPort && other.id == id;
@override
int get hashCode => id.hashCode;
}

View File

@@ -7,4 +7,10 @@ class ShipmentType {
factory ShipmentType.fromMap(Map<String, dynamic> map, String id) { factory ShipmentType.fromMap(Map<String, dynamic> map, String id) {
return ShipmentType(id: id, name: map['name'] ?? ""); return ShipmentType(id: id, name: map['name'] ?? "");
} }
@override
bool operator ==(Object other) => other is ShipmentType && other.id == id;
@override
int get hashCode => id.hashCode;
} }

View File

@@ -21,6 +21,7 @@ class User {
String? preferCurrency; String? preferCurrency;
bool enablePinLogin; bool enablePinLogin;
String? pinDigit; String? pinDigit;
List<String> privileges = [];
String get initial => String get initial =>
name != null && name != "" ? name!.substring(0, 1) : "?"; name != null && name != "" ? name!.substring(0, 1) : "?";
@@ -48,7 +49,9 @@ class User {
String get getFcsUnseenCount => String get getFcsUnseenCount =>
fcsUnseenCount > 100 ? "99+" : fcsUnseenCount.toString(); fcsUnseenCount > 100 ? "99+" : fcsUnseenCount.toString();
List<String> privileges = []; // for pin login
String? pinToken;
bool get isPinLogin => pinToken!=null;
String get phone => phoneNumber != null && phoneNumber!.startsWith("959") String get phone => phoneNumber != null && phoneNumber!.startsWith("959")
? "0${phoneNumber!.substring(2)}" ? "0${phoneNumber!.substring(2)}"
@@ -117,7 +120,9 @@ class User {
userUnseenCount: map['user_unseen_count'] ?? 0, userUnseenCount: map['user_unseen_count'] ?? 0,
fcsUnseenCount: map['fcs_unseen_count'] ?? 0, fcsUnseenCount: map['fcs_unseen_count'] ?? 0,
preferCurrency: map['preferred_currency'], preferCurrency: map['preferred_currency'],
lastMessageTime: _date == null ? null : _date.toDate()); lastMessageTime: _date == null ? null : _date.toDate(),
enablePinLogin: map['enable_pin_login'] ?? false,
pinDigit: map['pin'] ?? '');
} }
bool diffPrivileges(User another) { bool diffPrivileges(User another) {

View File

@@ -16,7 +16,7 @@ class LocalPopupMenu {
List<LocalPopupMenu> shipFiteringMenu = <LocalPopupMenu>[ List<LocalPopupMenu> shipFiteringMenu = <LocalPopupMenu>[
LocalPopupMenu(id: 0, text :"All"), LocalPopupMenu(id: 0, text :"All"),
LocalPopupMenu(id: 1, text: "Pending"), LocalPopupMenu(id: 1, text: "Pending"),
LocalPopupMenu(id: 2, text: "Processing"), LocalPopupMenu(id: 2, text: "Processed"),
LocalPopupMenu(id: 3, text: "Shipped"), LocalPopupMenu(id: 3, text: "Shipped"),
LocalPopupMenu(id: 4, text: "Arrived"), LocalPopupMenu(id: 4, text: "Arrived"),
LocalPopupMenu(id: 5, text: "Invoiced"), LocalPopupMenu(id: 5, text: "Invoiced"),

View File

@@ -3,6 +3,7 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_icons_null_safety/flutter_icons_null_safety.dart'; import 'package:flutter_icons_null_safety/flutter_icons_null_safety.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:flutter_vector_icons/flutter_vector_icons.dart' as vector;
class Privilege { class Privilege {
String id; String id;
@@ -49,6 +50,8 @@ class Privilege {
iconData = MaterialCommunityIcons.layers; iconData = MaterialCommunityIcons.layers;
} else if (this.id == privilege_report) { } else if (this.id == privilege_report) {
iconData = Feather.file_text; iconData = Feather.file_text;
} else if (this.id == privilege_pin) {
iconData = vector.MaterialCommunityIcons.account_lock_outline;
} else { } else {
iconData = MaterialCommunityIcons.account_question; iconData = MaterialCommunityIcons.account_question;
} }

View File

@@ -4,6 +4,8 @@ import 'dart:io';
import 'package:device_info_plus/device_info_plus.dart'; import 'package:device_info_plus/device_info_plus.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:fcs/data/services/services.dart';
import 'package:fcs/domain/entities/user.dart';
import 'package:fcs/domain/vo/status.dart'; import 'package:fcs/domain/vo/status.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
@@ -28,10 +30,14 @@ Future<dynamic> requestAPI(String path, method,
if (token != null) { if (token != null) {
headers["Token"] = token; headers["Token"] = token;
} }
User? pinLoginUser = Services.instance.authService.getPinLoginUser();
if (pinLoginUser != null) {
headers["pin_token"] = pinLoginUser.pinToken;
}
if (devInfo.deviceID != null) { if (devInfo.deviceID != null) {
headers["Device"] = devInfo.deviceID ?? "" + ":" + deviceName; headers["Device"] = devInfo.deviceID ?? "" + ":" + deviceName;
} }
headers["Project-ID"] = Config.instance.reportProjectID; headers["Project-ID"] = Services.setting.reportProjectID;
BaseOptions options = new BaseOptions( BaseOptions options = new BaseOptions(
method: method, method: method,
@@ -51,7 +57,7 @@ Future<dynamic> requestAPI(String path, method,
}); });
String curlPath = String curlPath =
"curl -X $method ${list.join("")}${payload != null ? '-d $payload ' : ''}${options.baseUrl}$path"; "curl -X $method ${list.join("")}${payload != null ? '-d \'${jsonEncode(payload)}\' ' : ''}${options.baseUrl}$path";
log.log(curlPath); log.log(curlPath);
@@ -93,7 +99,7 @@ Future<dynamic> requestDownloadAPI(String path, method,
var _downloadData = StringBuffer(); var _downloadData = StringBuffer();
var fileSave = new File(filePath!); var fileSave = new File(filePath!);
var request = await client.getUrl(Uri.parse("$baseUrl$path")); var request = await client.getUrl(Uri.parse("$baseUrl$path"));
request.headers.set("Project-ID", Config.instance.reportProjectID); request.headers.set("Project-ID", Services.setting.reportProjectID);
request.headers request.headers
.set(HttpHeaders.contentTypeHeader, "application/json; charset=UTF-8"); .set(HttpHeaders.contentTypeHeader, "application/json; charset=UTF-8");
if (token != null) { if (token != null) {
@@ -134,7 +140,7 @@ Future<dynamic> requestDownloadPDFAPI(String path, method,
// var _downloadData = StringBuffer(); // var _downloadData = StringBuffer();
var fileSave = new File(filePath!); var fileSave = new File(filePath!);
var request = await client.getUrl(Uri.parse("$baseUrl$path")); var request = await client.getUrl(Uri.parse("$baseUrl$path"));
request.headers.set("Project-ID", Config.instance.reportProjectID); request.headers.set("Project-ID", Services.setting.reportProjectID);
if (token != null) { if (token != null) {
request.headers.set("Token", token); request.headers.set("Token", token);
} }
@@ -182,7 +188,7 @@ Future<dynamic> requestDownload(String path, method,
HttpClient client = new HttpClient(); HttpClient client = new HttpClient();
// var _downloadData = StringBuffer(); // var _downloadData = StringBuffer();
var request = await client.postUrl(Uri.parse("$baseUrl$path")); var request = await client.postUrl(Uri.parse("$baseUrl$path"));
request.headers.set("Project-ID", Config.instance.reportProjectID); request.headers.set("Project-ID", Services.setting.reportProjectID);
if (token != null) { if (token != null) {
request.headers.set("Token", token); request.headers.set("Token", token);
} }
@@ -238,7 +244,7 @@ Future<File> downloadPDF(
await requestDownload("/api/pdf", "POST", await requestDownload("/api/pdf", "POST",
filePath: path, filePath: path,
url: Config.instance.reportURL, url: Services.setting.reportURL,
token: await getToken(), token: await getToken(),
payload: jsonEncode(data), onDownloadDone: (f) async { payload: jsonEncode(data), onDownloadDone: (f) async {
completer.complete(f); completer.complete(f);

View File

@@ -48,7 +48,7 @@ Future<void> generateCartonPdf(Carton carton) async {
style: pw.TextStyle( style: pw.TextStyle(
fontSize: 12, fontSize: 12,
)), )),
pw.Text(carton.userName!, pw.Text(carton.consigneeName!,
style: pw.TextStyle( style: pw.TextStyle(
fontSize: 10, fontSize: 10,
)), )),

View File

@@ -108,4 +108,14 @@ class SharedPref {
static Future<void> clearRecentSearch(String key) async { static Future<void> clearRecentSearch(String key) async {
return await _remove(key); return await _remove(key);
} }
static Future<bool?> getPinLockOn() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
return prefs.getBool('pin_lock');
}
static Future<void> setPinLockOn(bool isLockOn) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setBool('pin_lock', isLockOn);
}
} }

View File

@@ -12,17 +12,18 @@ Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp( await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform, options: DefaultFirebaseOptions.currentPlatform,
); );
FirebaseMessaging.onBackgroundMessage(backgroundMessageHandler); FirebaseMessaging.onBackgroundMessage(backgroundMessageHandler);
Config( Config(
flavor: Flavor.DEV, flavor: Flavor.DEV,
color: Colors.blue, color: Colors.blue,
apiURL: "https://asia-northeast1-fcs-dev1.cloudfunctions.net/API13", apiURL: "http://192.168.100.150:9090",
reportURL: "http://petrok.mokkon.com:7071", // apiURL: "https://asia-northeast1-fcs-dev1.cloudfunctions.net/API13",
reportProjectID: "fcs-dev", // reportURL: "http://petrok.mokkon.com:7071",
bucketName: "gs://fcs-dev1-us", // reportProjectID: "fcs-dev",
// bucketName: "gs://fcs-dev1-us",
level: Level.ALL); level: Level.ALL);
runApp(App(title: "FCS - Dev")); runApp(App(title: "FCS - Dev"));
} }

View File

@@ -9,10 +9,12 @@ void main() {
Config( Config(
flavor: Flavor.DEV, flavor: Flavor.DEV,
color: Colors.blue, color: Colors.blue,
reportURL: "http://petrok.mokkon.com:8091",
reportProjectID: "fcs-dev",
apiURL: "http://192.168.1.155:7777", apiURL: "http://192.168.1.155:7777",
bucketName: "gs://fcs-dev1-us", // reportURL: "http://petrok.mokkon.com:8091",
// reportProjectID: "fcs-dev",
// bucketName: "gs://fcs-dev1-us",
level: Level.ALL); level: Level.ALL);
runApp(App(title: "FCS - Dev local",)); runApp(App(
title: "FCS - Dev local",
));
} }

View File

@@ -19,9 +19,9 @@ Future<void> main() async {
flavor: Flavor.PRODUCTION, flavor: Flavor.PRODUCTION,
color: Colors.blue, color: Colors.blue,
apiURL: "https://asia-northeast1-fcs-prod1.cloudfunctions.net/API13", apiURL: "https://asia-northeast1-fcs-prod1.cloudfunctions.net/API13",
reportURL: "http://petrok.mokkon.com:8092", // reportURL: "http://petrok.mokkon.com:8092",
reportProjectID: "fcs-prod", // reportProjectID: "fcs-prod",
bucketName: "gs://fcs-prod1", // bucketName: "gs://fcs-prod1",
level: Level.ALL); level: Level.ALL);
runApp(App(title: "FCS")); runApp(App(title: "FCS"));
} }

View File

@@ -0,0 +1,114 @@
import 'package:fcs/domain/entities/cargo_type.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/pages/rates/model/shipment_rate_model.dart';
import 'package:fcs/pages/widgets/local_app_bar.dart';
import 'package:fcs/pages/widgets/progress.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../main/util.dart';
class CargoTypeAddition extends StatefulWidget {
final List<CargoType> cargoTypes;
const CargoTypeAddition({super.key, required this.cargoTypes});
@override
_CargoTypeAdditionState createState() => _CargoTypeAdditionState();
}
class _CargoTypeAdditionState extends State<CargoTypeAddition> {
bool _isLoading = false;
List<CargoType> cargoTypes = [];
@override
void initState() {
_init();
super.initState();
}
_init() {
var shipmentRateModel =
Provider.of<ShipmentRateModel>(context, listen: false);
cargoTypes =
shipmentRateModel.rate.cargoTypes.map((e) => e.clone()).toList();
for (var p in cargoTypes) {
p.qty = 0;
if (widget.cargoTypes.any((e) => e.id == p.id)) {
p.isChecked = true;
} else {
p.isChecked = false;
}
widget.cargoTypes.forEach((vp) {
if (p.id == vp.id) {
p.qty = vp.qty;
}
});
}
if (mounted) {
setState(() {});
}
}
_onTap(CargoType cargo) {
if (cargo.isChecked) {
showMsgDialog(context, "Error", "Already exit!");
return;
}
Navigator.pop(context, cargo);
}
@override
Widget build(BuildContext context) {
List<Widget> getCargoRowList(List<CargoType> _c) {
return _c.map((c) {
return ListTile(
onTap: () {
_onTap(c);
},
title: new Text(c.name ?? '', style: textStyle),
trailing: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: primaryColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5),
),
minimumSize: Size(80, 35)),
onPressed: c.isChecked
? null
: () {
_onTap(c);
},
child: Text(
"Add",
style: TextStyle(color: Colors.white, fontSize: 14),
),
));
}).toList();
}
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: LocalAppBar(
labelKey: 'box.add.cargo_type',
backgroundColor: Colors.white,
labelColor: primaryColor,
arrowColor: primaryColor),
body: Container(
padding: EdgeInsets.all(10),
child: ListView(
shrinkWrap: true,
children: <Widget>[
Column(
children: getCargoRowList(cargoTypes),
),
SizedBox(height: 30),
],
),
),
),
);
}
}

View File

@@ -12,7 +12,8 @@ import '../widgets/continue_button.dart';
import '../widgets/display_text.dart'; import '../widgets/display_text.dart';
import '../widgets/local_title.dart'; import '../widgets/local_title.dart';
import '../widgets/previous_button.dart'; import '../widgets/previous_button.dart';
import 'custom_duty_addition.dart'; import 'cargo_type_addition.dart';
import 'surcharge_item_addition.dart';
typedef OnPrevious = Function( typedef OnPrevious = Function(
List<CargoType> cargoTypes, List<CargoType> customDuties); List<CargoType> cargoTypes, List<CargoType> customDuties);
@@ -56,28 +57,20 @@ class _CargoWidgetState extends State<CargoWidget> {
_init() { _init() {
// for cargo types // for cargo types
var model = context.read<ShipmentRateModel>();
_cargoTypes = model.rate.cargoTypes.map((e) => e.clone()).toList();
if (widget.cargoTypes.isNotEmpty) { if (widget.cargoTypes.isNotEmpty) {
_cargoTypes.forEach((mp) { _cargoTypes = List.from(widget.cargoTypes);
mp.weight = 0;
widget.cargoTypes.forEach((vp) {
if (mp.id == vp.id) {
mp.weight = vp.weight;
}
});
});
_cargoTypes.forEach((e) { _cargoTypes.forEach((e) {
var editor = new TextEditingController(); var editor = new TextEditingController();
editor.text = removeTrailingZeros(e.weight); editor.text = removeTrailingZeros(e.weight);
editor.addListener(inputChangeListener); editor.addListener(inputChangeListener);
_cargoTypeControllers.add(editor); _cargoTypeControllers.add(editor);
}); });
_calculateTotalWeght();
double total = _cargoTypes.fold(0, (sum, value) => sum + value.weight);
_totalCtl.text = removeTrailingZeros(total);
} else { } else {
var model = context.read<ShipmentRateModel>();
var cargoes = model.rate.cargoTypes.map((e) => e.clone()).toList();
_cargoTypes = cargoes.where((e) => e.isDefault).toList();
_cargoTypes.forEach((e) { _cargoTypes.forEach((e) {
var editor = new TextEditingController(); var editor = new TextEditingController();
editor.text = ''; editor.text = '';
@@ -103,6 +96,11 @@ class _CargoWidgetState extends State<CargoWidget> {
} }
} }
_calculateTotalWeght() {
double total = _cargoTypes.fold(0, (sum, value) => sum + value.weight);
_totalCtl.text = removeTrailingZeros(total);
}
bool isFieldEmpty(int index) { bool isFieldEmpty(int index) {
return _cargoTypeControllers[index].text.isEmpty; return _cargoTypeControllers[index].text.isEmpty;
} }
@@ -158,6 +156,39 @@ class _CargoWidgetState extends State<CargoWidget> {
], ],
); );
final cargoTitle = LocalTitle(
textKey: "box.input_cargo_weight",
topPadding: 0,
trailing: IconButton(
icon: Icon(
Icons.add_circle,
color: primaryColor,
),
onPressed: () async {
CargoType? cargoType = await Navigator.push(
context,
CupertinoPageRoute(
builder: (context) =>
CargoTypeAddition(cargoTypes: _cargoTypes)));
if (cargoType == null) return;
_cargoTypes.add(cargoType);
_cargoTypeControllers.clear();
_cargoTypes.forEach((e) {
var editor = new TextEditingController();
editor.text = removeTrailingZeros(e.weight);
editor.addListener(inputChangeListener);
_cargoTypeControllers.add(editor);
});
_calculateTotalWeght();
if (mounted) {
setState(() {});
}
}),
);
final cargosBox = Wrap( final cargosBox = Wrap(
alignment: WrapAlignment.spaceBetween, alignment: WrapAlignment.spaceBetween,
runSpacing: 15, runSpacing: 15,
@@ -171,6 +202,8 @@ class _CargoWidgetState extends State<CargoWidget> {
InkResponse( InkResponse(
radius: 25, radius: 25,
onTap: () { onTap: () {
_cargoTypes.removeAt(key);
double totalWeight = double.tryParse(_totalCtl.text) ?? 0; double totalWeight = double.tryParse(_totalCtl.text) ?? 0;
double removeWeight = double removeWeight =
(double.tryParse(_cargoTypeControllers[key].text) ?? (double.tryParse(_cargoTypeControllers[key].text) ??
@@ -182,11 +215,12 @@ class _CargoWidgetState extends State<CargoWidget> {
} }
_cargoTypeControllers[key].clear(); _cargoTypeControllers[key].clear();
if (mounted) { if (mounted) {
setState(() {}); setState(() {});
} }
}, },
child: Icon(MaterialIcons.clear, color: labelColor)), child: Icon(Feather.minus_circle, color: labelColor)),
const SizedBox(width: 10), const SizedBox(width: 10),
Flexible( Flexible(
child: inputTextFieldWidget(context, child: inputTextFieldWidget(context,
@@ -254,14 +288,14 @@ class _CargoWidgetState extends State<CargoWidget> {
color: primaryColor, color: primaryColor,
), ),
onPressed: () async { onPressed: () async {
List<CargoType>? customList = await Navigator.push<List<CargoType>>( CargoType? surchargeItem = await Navigator.push(
context, context,
CupertinoPageRoute( CupertinoPageRoute(
builder: (context) => builder: (context) =>
CustomDutyAddition(customDuties: _surchareItems))); SurchargeItemAddition(items: _surchareItems)));
if (customList == null) return; if (surchargeItem == null) return;
_surchareItems = List.from(customList); _surchareItems.add(surchargeItem);
_surchargeControllers.clear(); _surchargeControllers.clear();
_surchareItems.asMap().entries.forEach((e) { _surchareItems.asMap().entries.forEach((e) {
@@ -315,9 +349,10 @@ class _CargoWidgetState extends State<CargoWidget> {
final continueBtn = ContinueButton( final continueBtn = ContinueButton(
onTap: () { onTap: () {
if (widget.onContinue != null) { if (widget.onContinue != null) {
if (_surchareItems.isNotEmpty &&
if(_surchareItems.isNotEmpty && _surchareItems.any((item)=> item.qty == 0)){ _surchareItems.any((item) => item.qty == 0)) {
showMsgDialog(context, "Error", "Please insert surcharge item quantity"); showMsgDialog(
context, "Error", "Please insert surcharge item quantity");
return; return;
} }
@@ -341,7 +376,7 @@ class _CargoWidgetState extends State<CargoWidget> {
children: [ children: [
const SizedBox(height: 8), const SizedBox(height: 8),
userRow, userRow,
LocalTitle(textKey: "box.input_cargo_weight", topPadding: 5), cargoTitle,
cargosBox, cargosBox,
const SizedBox(height: 15), const SizedBox(height: 15),
Divider(), Divider(),

View File

@@ -56,7 +56,9 @@ class _CartonEditorState extends State<CartonEditor> {
_isNew = false; _isNew = false;
_consignee = User( _consignee = User(
id: _carton!.userID, fcsID: _carton!.fcsID, name: _carton!.userName); id: _carton!.consigneeID,
fcsID: _carton!.consigneeFCSID,
name: _carton!.consigneeName);
_sender = User( _sender = User(
id: _carton!.senderID, id: _carton!.senderID,
fcsID: _carton!.senderFCSID, fcsID: _carton!.senderFCSID,
@@ -129,7 +131,7 @@ class _CartonEditorState extends State<CartonEditor> {
padding: const EdgeInsets.only(right: 5), padding: const EdgeInsets.only(right: 5),
child: InkResponse( child: InkResponse(
radius: 30, radius: 30,
onTap: () { onTap: () async {
//for packages //for packages
if (isFromPackages) { if (isFromPackages) {
if (_sender == null) { if (_sender == null) {
@@ -149,20 +151,32 @@ class _CartonEditorState extends State<CartonEditor> {
return; return;
} }
Navigator.push( Carton? carton = await Navigator.push(
context, context,
CupertinoPageRoute( CupertinoPageRoute(
builder: (context) => CartonPackageForm( builder: (context) => CartonPackageForm(
sender: _sender!, sender: _sender!,
consignee: _consignee!, consignee: _consignee!,
))); )));
if (carton != null) {
setState(() {
_cartons.add(carton);
});
}
} }
// for mix cartion // for mix cartion
else { else {
Navigator.push( Carton? carton = await Navigator.push(
context, context,
CupertinoPageRoute( CupertinoPageRoute(
builder: (context) => const MixCartonForm())); builder: (context) => const MixCartonForm()));
if (carton != null) {
setState(() {
_cartons.add(carton);
});
}
} }
}, },
child: Icon(Icons.add_circle, color: primaryColor), child: Icon(Icons.add_circle, color: primaryColor),

View File

@@ -18,6 +18,7 @@ import 'package:flutter_vector_icons/flutter_vector_icons.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../../domain/entities/carton_size.dart'; import '../../domain/entities/carton_size.dart';
import '../carton_size/model/carton_size_model.dart';
import '../widgets/local_button.dart'; import '../widgets/local_button.dart';
import 'carton_package_editor.dart'; import 'carton_package_editor.dart';
import 'mix_carton/mix_carton_editor.dart'; import 'mix_carton/mix_carton_editor.dart';
@@ -56,27 +57,43 @@ class _CartonInfoState extends State<CartonInfo> {
_init() async { _init() async {
try { try {
_isLoading = true; _isLoading = true;
_carton.cartonType = carton_from_packages;
_carton.billTo = billToConsignee;
_carton.cartonSizeType = customCarton;
_multiImgController.setImageUrls = _carton.photoUrls; _multiImgController.setImageUrls = _carton.photoUrls;
_cargoTypes = _carton.cargoTypes.where((e) => !e.isCutomDuty).toList(); _cargoTypes = _carton.cargoTypes;
_surchareItems = _carton.cargoTypes.where((e) => e.isCutomDuty).toList(); _surchareItems = _carton.surchareItems;
// check carton size type
List<CartonSize> cartonSizes =
context.read<CartonSizeModel>().cartonSizes;
var sameLength = cartonSizes.any((size) => size.length == _carton.length);
var sameWidth = cartonSizes.any((size) => size.width == _carton.width);
var sameHeight = cartonSizes.any((size) => size.height == _carton.height);
bool isStandartSize = sameLength && sameWidth && sameHeight;
if (isStandartSize) {
_carton.cartonSizeType = standardCarton;
standardSize = cartonSizes.firstWhere((size) =>
size.length == _carton.length &&
size.width == _carton.width &&
size.height == _carton.height);
} else if (_carton.length == 0 &&
_carton.width == 0 &&
_carton.height == 0) {
_carton.cartonSizeType = packageCarton;
} else {
_carton.cartonSizeType = customCarton;
}
if (_carton.cartonType == carton_from_packages) { if (_carton.cartonType == carton_from_packages) {
_carton.deliveryType = delivery_caton;
_packages = await context _packages = await context
.read<PackageModel>() .read<PackageModel>()
.getPackagesByIds(_carton.packageIDs); .getPackagesByIds(_carton.packageIDs);
} }
if (_carton.cartonType == carton_mix_carton) { if (_carton.cartonType == mix_carton) {
_mixCartons = await context _mixCartons = await context
.read<CartonModel>() .read<CartonModel>()
.getCartonsByIds(_carton.mixCartonIDs); .getCartonsByIds(_carton.cartonIDs);
} _cargoTypes.sort((a, b) => a.name!.compareTo(b.name!));
_surchareItems.sort((a, b) => a.name!.compareTo(b.name!));
if (mounted) {
setState(() {});
} }
totalWeight = totalWeight =
@@ -86,17 +103,23 @@ class _CartonInfoState extends State<CartonInfo> {
} finally { } finally {
_isLoading = false; _isLoading = false;
} }
if (mounted) {
setState(() {});
}
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var fromPackage = _carton.cartonType == carton_from_packages;
String? boxDimension = _carton.cartonSizeType == standardCarton String? boxDimension = _carton.cartonSizeType == standardCarton
? "${standardSize?.name} - ${standardSize?.length.toInt()}”x${standardSize?.width.toInt()}”x${standardSize?.height.toInt()}" ? "${standardSize?.name} - ${standardSize?.length.toInt()}”x${standardSize?.width.toInt()}”x${standardSize?.height.toInt()}"
: _carton.cartonSizeType == customCarton : _carton.cartonSizeType == customCarton
? "${_carton.length.toInt()}”x${_carton.width.toInt()}”x${_carton.height.toInt()}" ? "${_carton.length.toInt()}”x${_carton.width.toInt()}”x${_carton.height.toInt()}"
: null; : null;
final cartonTypeBox = DisplayText( final cartonNumberBox = DisplayText(
text: _carton.cartonNumber, text: _carton.cartonNumber,
labelTextKey: "box.number", labelTextKey: "box.number",
); );
@@ -111,10 +134,12 @@ class _CartonInfoState extends State<CartonInfo> {
}, },
icon: Icon(AntDesign.qrcode, color: Colors.black)); icon: Icon(AntDesign.qrcode, color: Colors.black));
final cartonSubTypeBox = DisplayText( final cartonTypeBox = DisplayText(
text: _carton.cartonType == carton_from_packages text: _carton.cartonType == carton_from_packages
? "For packages" ? "For packages"
: "Mix carton", : _carton.cartonType == mix_carton
? carton_mix_carton
: '',
labelTextKey: "box.carton.type", labelTextKey: "box.carton.type",
); );
@@ -132,14 +157,18 @@ class _CartonInfoState extends State<CartonInfo> {
labelTextKey: "box.fcs_shipment_num", labelTextKey: "box.fcs_shipment_num",
); );
final deliveryBox = DisplayText( final lastMileBox = DisplayText(
text: _carton.deliveryType, text: _carton.lastMile == delivery_caton
? 'Delivery'
: _carton.lastMile == pickup_carton
? 'Pick-up'
: '',
labelTextKey: "box.delivery_type", labelTextKey: "box.delivery_type",
); );
final cartonSizeBox = DisplayText( final cartonSizeBox = DisplayText(
subText: boxDimension == null ? null : Text("$boxDimension"), subText: Text("${boxDimension ?? 'No defined size'}"),
labelTextKey: "box.carton_size", labelTextKey: "box.select_carton_size",
); );
final senderBox = DisplayText( final senderBox = DisplayText(
@@ -149,8 +178,8 @@ class _CartonInfoState extends State<CartonInfo> {
); );
final consigneeNameBox = DisplayText( final consigneeNameBox = DisplayText(
text: _carton.userName != null ? _carton.userName : "", text: _carton.consigneeName != null ? _carton.consigneeName : "",
subText: Text(_carton.fcsID ?? "", style: textStyle), subText: Text(_carton.consigneeFCSID ?? "", style: textStyle),
labelTextKey: "processing.consignee.name", labelTextKey: "processing.consignee.name",
); );
@@ -169,6 +198,7 @@ class _CartonInfoState extends State<CartonInfo> {
final userRowBox = Row( final userRowBox = Row(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Expanded( Expanded(
child: Row( child: Row(
@@ -205,7 +235,7 @@ class _CartonInfoState extends State<CartonInfo> {
); );
final cargosBox = Padding( final cargosBox = Padding(
padding: const EdgeInsets.only(top: 15), padding: const EdgeInsets.only(top: 20),
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
@@ -214,10 +244,13 @@ class _CartonInfoState extends State<CartonInfo> {
color: Colors.black54, color: Colors.black54,
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.normal), fontWeight: FontWeight.normal),
Padding( _cargoTypes.isNotEmpty
? Padding(
padding: EdgeInsets.only(right: 50), padding: EdgeInsets.only(right: 50),
child: Text("${removeTrailingZeros(totalWeight)} lb", child: Text("${removeTrailingZeros(totalWeight)} lb",
textAlign: TextAlign.end,
style: TextStyle(color: Colors.black54, fontSize: 15))) style: TextStyle(color: Colors.black54, fontSize: 15)))
: const SizedBox()
], ],
), ),
Container( Container(
@@ -239,38 +272,25 @@ class _CartonInfoState extends State<CartonInfo> {
TextStyle(color: Colors.black, fontSize: 15), TextStyle(color: Colors.black, fontSize: 15),
), ),
Text("${removeTrailingZeros(e.weight)} lb", Text("${removeTrailingZeros(e.weight)} lb",
textAlign: TextAlign.end,
style: TextStyle( style: TextStyle(
color: Colors.black, fontSize: 15)) color: Colors.black, fontSize: 15))
], ],
), ),
); );
}).toList()), }).toList()),
const SizedBox(height: 10),
], ],
), ),
), ),
), ),
]), ]),
); );
final surchargeItemBox = Padding(
padding: const EdgeInsets.only(top: 15), final surchargeItemBox =
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
LocalText(context, 'box.surcharge.item',
color: Colors.black54,
fontSize: 16,
fontWeight: FontWeight.normal),
Padding(
padding: EdgeInsets.only(right: 50),
child: Text("${removeTrailingZeros(totalSurchargeCount)} pcs",
style: TextStyle(color: Colors.black54, fontSize: 15)))
],
),
Container( Container(
child: Padding( child: Padding(
padding: const EdgeInsets.only(right: 60), padding: const EdgeInsets.only(right: 50),
child: Column( child: Column(
children: [ children: [
Column( Column(
@@ -283,24 +303,21 @@ class _CartonInfoState extends State<CartonInfo> {
children: [ children: [
Text( Text(
e.name ?? "", e.name ?? "",
style: style: TextStyle(color: Colors.black, fontSize: 15),
TextStyle(color: Colors.black, fontSize: 15),
), ),
Text( Text("${removeTrailingZeros((e.qty).toDouble())} pc",
"${removeTrailingZeros((e.qty).toDouble())} pc", textAlign: TextAlign.end,
style: TextStyle( style:
color: Colors.black, fontSize: 15)) TextStyle(color: Colors.black, fontSize: 15))
], ],
), ),
); );
}).toList()), }).toList()),
const SizedBox(height: 10),
], ],
), ),
), ),
), ),
]), ]);
);
final img = MultiImageFile( final img = MultiImageFile(
enabled: false, enabled: false,
@@ -367,30 +384,29 @@ class _CartonInfoState extends State<CartonInfo> {
padding: const EdgeInsets.only(left: 20, right: 20), padding: const EdgeInsets.only(left: 20, right: 20),
child: ListView(children: <Widget>[ child: ListView(children: <Widget>[
Row(children: [ Row(children: [
Flexible(child: cartonTypeBox,flex: 1), Flexible(child: cartonNumberBox, flex: 1),
Flexible( Flexible(
child: cartonQrBox, child: cartonQrBox,
), ),
]), ]),
Row( Row(
children: [ children: [
Flexible(child: cartonSubTypeBox), Flexible(child: cartonTypeBox),
], ],
), ),
_mixCartons.isEmpty ? userRowBox : const SizedBox(), fromPackage ? userRowBox : const SizedBox(),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Flexible(child: billInfoBox), fromPackage ? Flexible(child: billInfoBox) : SizedBox(),
Flexible(child: shipmentBox), Flexible(child: shipmentBox),
], ],
), ),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
_mixCartons.isEmpty fromPackage
? Flexible(child: deliveryBox) ? Flexible(child: lastMileBox)
: const SizedBox(), : const SizedBox(),
Flexible(child: cartonSizeBox), Flexible(child: cartonSizeBox),
], ],
@@ -425,12 +441,17 @@ class _CartonInfoState extends State<CartonInfo> {
], ],
), ),
), ),
_cargoTypes.isEmpty ? const SizedBox() : cargosBox, cargosBox,
_surchareItems.isEmpty ? const SizedBox() : surchargeItemBox, surchargeItemBox,
const SizedBox(height: 10),
uploadImageBtn, uploadImageBtn,
const SizedBox(height: 30), _carton.photoUrls.isNotEmpty
? const SizedBox(height: 10)
: const SizedBox(),
img, img,
const SizedBox(height: 40), _carton.photoUrls.isNotEmpty
? const SizedBox(height: 40)
: const SizedBox(),
deleteBtn, deleteBtn,
const SizedBox(height: 20) const SizedBox(height: 20)
])))); ]))));
@@ -457,17 +478,22 @@ class _CartonInfoState extends State<CartonInfo> {
} }
_gotoEditor() async { _gotoEditor() async {
bool? updated = _carton.cartonType == carton_mix_carton bool? updated = false;
? await Navigator.push<bool>( if (_carton.cartonType == mix_carton) {
updated = await Navigator.push<bool>(
context, context,
CupertinoPageRoute( CupertinoPageRoute(
builder: (context) => MixCartonEditor(carton: _carton)), builder: (context) => MixCartonEditor(carton: _carton)),
) );
: await Navigator.push<bool>( }
if (_carton.cartonType == carton_from_packages) {
updated = await Navigator.push<bool>(
context, context,
CupertinoPageRoute( CupertinoPageRoute(
builder: (context) => CartonPackageEditor(carton: _carton)), builder: (context) => CartonPackageEditor(carton: _carton)),
); );
}
if (updated ?? false) { if (updated ?? false) {
Carton? c = Carton? c =
@@ -489,9 +515,8 @@ class _CartonInfoState extends State<CartonInfo> {
_isLoading = true; _isLoading = true;
}); });
try { try {
// var cartonModel = Provider.of<CartonModel>(context, listen: false); await context.read<CartonModel>().deleteCarton(widget.carton);
// await cartonModel.deleteCarton(widget.carton); Navigator.pop(context, true);
// Navigator.pop(context, true);
} catch (e) { } catch (e) {
showMsgDialog(context, "Error", e.toString()); showMsgDialog(context, "Error", e.toString());
} finally { } finally {

View File

@@ -1,5 +1,6 @@
// ignore_for_file: deprecated_member_use // ignore_for_file: deprecated_member_use
import 'package:fcs/pages/carton/model/carton_model.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
@@ -14,6 +15,7 @@ import '../../domain/entities/cargo_type.dart';
import '../../domain/entities/carton.dart'; import '../../domain/entities/carton.dart';
import '../../domain/entities/package.dart'; import '../../domain/entities/package.dart';
import '../../domain/entities/user.dart'; import '../../domain/entities/user.dart';
import '../carton_size/model/carton_size_model.dart';
import '../fcs_shipment/model/fcs_shipment_model.dart'; import '../fcs_shipment/model/fcs_shipment_model.dart';
import '../main/util.dart'; import '../main/util.dart';
import '../package/model/package_model.dart'; import '../package/model/package_model.dart';
@@ -53,7 +55,7 @@ class _CartonPackageEditorState extends State<CartonPackageEditor> {
double _width = 0; double _width = 0;
double _height = 0; double _height = 0;
String _selectedDeliveryType = delivery_caton; String _selectedLastMile = delivery_caton;
String _billToValue = billToSender; String _billToValue = billToSender;
FcsShipment? _shipment; FcsShipment? _shipment;
@@ -78,20 +80,41 @@ class _CartonPackageEditorState extends State<CartonPackageEditor> {
id: widget.carton.senderID); id: widget.carton.senderID);
_consignee = User( _consignee = User(
id: widget.carton.userID, id: widget.carton.consigneeID,
name: widget.carton.userName, name: widget.carton.consigneeName,
fcsID: widget.carton.fcsID); fcsID: widget.carton.consigneeFCSID);
_billToValue = widget.carton.billTo ?? billToSender; _billToValue = widget.carton.billTo ?? billToSender;
_selectedDeliveryType = widget.carton.deliveryType ?? delivery_caton; _selectedLastMile = widget.carton.lastMile ?? delivery_caton;
_cartonSizeType = widget.carton.cartonSizeType ?? customCarton; _cargoTypes = widget.carton.cargoTypes;
_length = widget.carton.length; _surchareItems = widget.carton.surchareItems;
_width = widget.carton.width;
_height = widget.carton.height; // check carton size type
_cargoTypes = List<CartonSize> cartonSizes = context.read<CartonSizeModel>().cartonSizes;
widget.carton.cargoTypes.where((e) => !e.isCutomDuty).toList();
_surchareItems = var sameLength =
widget.carton.cargoTypes.where((e) => e.isCutomDuty).toList(); cartonSizes.any((size) => size.length == widget.carton.length);
var sameWidth =
cartonSizes.any((size) => size.width == widget.carton.width);
var sameHeight =
cartonSizes.any((size) => size.height == widget.carton.height);
bool isStandartSize = sameLength && sameWidth && sameHeight;
if (isStandartSize) {
_cartonSizeType = standardCarton;
_standardSize = cartonSizes.firstWhere((size) =>
size.length == widget.carton.length &&
size.width == widget.carton.width &&
size.height == widget.carton.height);
} else if (widget.carton.length == 0 &&
widget.carton.width == 0 &&
widget.carton.height == 0) {
_cartonSizeType = packageCarton;
} else {
_cartonSizeType = customCarton;
_length = widget.carton.length.toDouble();
_width = widget.carton.width.toDouble();
_height = widget.carton.height.toDouble();
}
var s = await context var s = await context
.read<FcsShipmentModel>() .read<FcsShipmentModel>()
@@ -143,8 +166,16 @@ class _CartonPackageEditorState extends State<CartonPackageEditor> {
}, },
), ),
backgroundColor: Colors.white, backgroundColor: Colors.white,
title: LocalText(context, 'box.update_title', title: Column(
children: [
LocalText(context, 'box.update_title',
color: primaryColor, fontSize: 20), color: primaryColor, fontSize: 20),
Text(
widget.carton.cartonNumber ?? '',
style: TextStyle(color: primaryColor, fontSize: 14),
)
],
),
), ),
body: Column( body: Column(
children: [ children: [
@@ -172,7 +203,7 @@ class _CartonPackageEditorState extends State<CartonPackageEditor> {
if (step.stepType == StepType.SIZE) { if (step.stepType == StepType.SIZE) {
return Expanded( return Expanded(
child: CartonSizeWidget( child: CartonSizeWidget(
deliveryType: _selectedDeliveryType, lastMile: _selectedLastMile,
billType: _billToValue, billType: _billToValue,
sender: _sender!, sender: _sender!,
consignee: _consignee!, consignee: _consignee!,
@@ -188,7 +219,7 @@ class _CartonPackageEditorState extends State<CartonPackageEditor> {
onContinue: (deliveryType, billType, shipment, cartonSizeType, onContinue: (deliveryType, billType, shipment, cartonSizeType,
{standardSize, length, width, height}) { {standardSize, length, width, height}) {
setState(() { setState(() {
_selectedDeliveryType = deliveryType; _selectedLastMile = deliveryType;
_billToValue = billType; _billToValue = billType;
_shipment = shipment; _shipment = shipment;
_cartonSizeType = cartonSizeType; _cartonSizeType = cartonSizeType;
@@ -256,7 +287,7 @@ class _CartonPackageEditorState extends State<CartonPackageEditor> {
length: _length, length: _length,
width: _width, width: _width,
height: _height, height: _height,
deliveryType: _selectedDeliveryType, lastMile: _selectedLastMile,
shipment: _shipment!, shipment: _shipment!,
packages: _packages, packages: _packages,
cargoTypes: _cargoTypes, cargoTypes: _cargoTypes,
@@ -279,6 +310,43 @@ class _CartonPackageEditorState extends State<CartonPackageEditor> {
_isLoading = true; _isLoading = true;
}); });
try { try {
double length = 0;
double width = 0;
double height = 0;
if (_cartonSizeType == standardCarton) {
if (_standardSize != null) {
length = _standardSize!.length;
width = _standardSize!.width;
height = _standardSize!.height;
}
} else if (_cartonSizeType == customCarton) {
length = _length;
width = _width;
height = _height;
} else {
length = 0;
width = 0;
height = 0;
}
var carton = Carton(
id: widget.carton.id,
cartonType: carton_from_packages,
senderID: widget.carton.senderID,
consigneeID: widget.carton.consigneeID,
billTo: _billToValue,
lastMile: _selectedLastMile,
fcsShipmentID: _shipment?.id,
length: length,
width: width,
height: height,
packages: _packages,
cargoTypes: _cargoTypes,
surchareItems: _surchareItems);
await context.read<CartonModel>().updateCarton(carton);
Navigator.pop(context, true); Navigator.pop(context, true);
} catch (e) { } catch (e) {
showMsgDialog(context, "Error", e.toString()); showMsgDialog(context, "Error", e.toString());

View File

@@ -1,5 +1,6 @@
// ignore_for_file: deprecated_member_use // ignore_for_file: deprecated_member_use
import 'package:fcs/domain/entities/carton.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
@@ -20,6 +21,7 @@ import '../widgets/step_widget.dart';
import 'cargo_widget.dart'; import 'cargo_widget.dart';
import 'carton_size_widget.dart'; import 'carton_size_widget.dart';
import 'carton_submit.dart'; import 'carton_submit.dart';
import 'model/carton_model.dart';
import 'model/package_selection_model.dart'; import 'model/package_selection_model.dart';
import 'package_selection_widget.dart'; import 'package_selection_widget.dart';
@@ -55,7 +57,7 @@ class _CartonPackageFormState extends State<CartonPackageForm> {
double _width = 0; double _width = 0;
double _height = 0; double _height = 0;
String _selectedDeliveryType = delivery_caton; String _selectedLastMile = delivery_caton;
String _billToValue = billToSender; String _billToValue = billToSender;
FcsShipment? _shipment; FcsShipment? _shipment;
@@ -134,7 +136,7 @@ class _CartonPackageFormState extends State<CartonPackageForm> {
if (step.stepType == StepType.SIZE) { if (step.stepType == StepType.SIZE) {
return Expanded( return Expanded(
child: CartonSizeWidget( child: CartonSizeWidget(
deliveryType: _selectedDeliveryType, lastMile: _selectedLastMile,
billType: _billToValue, billType: _billToValue,
sender: widget.sender, sender: widget.sender,
consignee: widget.consignee, consignee: widget.consignee,
@@ -147,10 +149,10 @@ class _CartonPackageFormState extends State<CartonPackageForm> {
onPrevious: () { onPrevious: () {
Navigator.pop(context); Navigator.pop(context);
}, },
onContinue: (deliveryType, billType, shipment, cartonSizeType, onContinue: (lastMile, billType, shipment, cartonSizeType,
{standardSize, length, width, height}) { {standardSize, length, width, height}) {
setState(() { setState(() {
_selectedDeliveryType = deliveryType; _selectedLastMile = lastMile;
_billToValue = billType; _billToValue = billType;
_shipment = shipment; _shipment = shipment;
_cartonSizeType = cartonSizeType; _cartonSizeType = cartonSizeType;
@@ -217,7 +219,7 @@ class _CartonPackageFormState extends State<CartonPackageForm> {
length: _length, length: _length,
width: _width, width: _width,
height: _height, height: _height,
deliveryType: _selectedDeliveryType, lastMile: _selectedLastMile,
shipment: _shipment!, shipment: _shipment!,
packages: _packages, packages: _packages,
cargoTypes: _cargoTypes, cargoTypes: _cargoTypes,
@@ -240,7 +242,41 @@ class _CartonPackageFormState extends State<CartonPackageForm> {
_isLoading = true; _isLoading = true;
}); });
try { try {
Navigator.pop(context, true); double length = 0;
double width = 0;
double height = 0;
if (_cartonSizeType == standardCarton) {
if (_standardSize != null) {
length = _standardSize!.length;
width = _standardSize!.width;
height = _standardSize!.height;
}
} else if (_cartonSizeType == customCarton) {
length = _length;
width = _width;
height = _height;
} else {
length = 0;
width = 0;
height = 0;
}
var carton = Carton(
cartonType: carton_from_packages,
senderID: widget.sender.id,
consigneeID: widget.consignee.id,
billTo: _billToValue,
lastMile: _selectedLastMile,
fcsShipmentID: _shipment?.id,
length: length,
width: width,
height: height,
packages: _packages,
cargoTypes: _cargoTypes,
surchareItems: _surchareItems);
var c = await context.read<CartonModel>().createCarton(carton);
Navigator.pop(context, c);
} catch (e) { } catch (e) {
showMsgDialog(context, "Error", e.toString()); showMsgDialog(context, "Error", e.toString());
} finally { } finally {

View File

@@ -31,7 +31,7 @@ class CartonSizeWidget extends StatefulWidget {
final OnContinue? onContinue; final OnContinue? onContinue;
final User sender; final User sender;
final User consignee; final User consignee;
final String deliveryType; final String lastMile;
final String billType; final String billType;
final FcsShipment? shipment; final FcsShipment? shipment;
final String cartonSizeType; final String cartonSizeType;
@@ -52,7 +52,7 @@ class CartonSizeWidget extends StatefulWidget {
this.height, this.height,
required this.sender, required this.sender,
required this.consignee, required this.consignee,
required this.deliveryType, required this.lastMile,
required this.billType}) required this.billType})
: super(key: key); : super(key: key);
@@ -61,14 +61,12 @@ class CartonSizeWidget extends StatefulWidget {
} }
class _CartonSizeWidgetState extends State<CartonSizeWidget> { class _CartonSizeWidgetState extends State<CartonSizeWidget> {
// List<String> _deliveryTypes = [delivery_caton, pickup_carton];
FcsShipment? _shipment; FcsShipment? _shipment;
String _cartonSizeType = standardCarton; String _cartonSizeType = standardCarton;
List<FcsShipment> _shipments = []; List<FcsShipment> _shipments = [];
CartonSize? _selectStandardSize; CartonSize? _selectStandardSize;
late String _selectedDeliveryType; late String _selectedLastmile;
late String _billToValue; late String _billToValue;
TextEditingController _widthController = new TextEditingController(); TextEditingController _widthController = new TextEditingController();
@@ -82,13 +80,9 @@ class _CartonSizeWidgetState extends State<CartonSizeWidget> {
} }
_init() async { _init() async {
_selectedDeliveryType = widget.deliveryType; _selectedLastmile = widget.lastMile;
_billToValue = widget.billType; _billToValue = widget.billType;
_cartonSizeType = widget.cartonSizeType; _cartonSizeType = widget.cartonSizeType;
List<CartonSize> cartonSizes = context.read<CartonSizeModel>().cartonSizes;
_selectStandardSize = widget.standardSize ?? cartonSizes.first;
_lengthController.text = _lengthController.text =
widget.length == null ? "0" : removeTrailingZeros(widget.length ?? 0); widget.length == null ? "0" : removeTrailingZeros(widget.length ?? 0);
_widthController.text = _widthController.text =
@@ -96,8 +90,16 @@ class _CartonSizeWidgetState extends State<CartonSizeWidget> {
_heightController.text = _heightController.text =
widget.height == null ? "0" : removeTrailingZeros(widget.height ?? 0); widget.height == null ? "0" : removeTrailingZeros(widget.height ?? 0);
_getStandardCartonSize();
_loadShipment(); _loadShipment();
if (mounted) {
setState(() {});
}
}
_getStandardCartonSize() {
List<CartonSize> cartonSizes = context.read<CartonSizeModel>().cartonSizes;
_selectStandardSize = widget.standardSize ?? cartonSizes.first;
if (mounted) { if (mounted) {
setState(() {}); setState(() {});
} }
@@ -107,7 +109,12 @@ class _CartonSizeWidgetState extends State<CartonSizeWidget> {
var fcsShipments = var fcsShipments =
await context.read<FcsShipmentModel>().getActiveFcsShipments(); await context.read<FcsShipmentModel>().getActiveFcsShipments();
_shipments = fcsShipments; _shipments = fcsShipments;
if (_shipments.contains(widget.shipment)) {
_shipment = widget.shipment; _shipment = widget.shipment;
} else {
_shipment = null;
}
if (mounted) { if (mounted) {
setState(() {}); setState(() {});
} }
@@ -116,6 +123,7 @@ class _CartonSizeWidgetState extends State<CartonSizeWidget> {
@override @override
void didUpdateWidget(covariant CartonSizeWidget oldWidget) { void didUpdateWidget(covariant CartonSizeWidget oldWidget) {
_loadShipment(); _loadShipment();
_getStandardCartonSize();
super.didUpdateWidget(oldWidget); super.didUpdateWidget(oldWidget);
} }
@@ -125,7 +133,7 @@ class _CartonSizeWidgetState extends State<CartonSizeWidget> {
context.watch<CartonSizeModel>().getCartonSizes; context.watch<CartonSizeModel>().getCartonSizes;
bool isStandardSize = _cartonSizeType == standardCarton; bool isStandardSize = _cartonSizeType == standardCarton;
bool isCustomSize = _cartonSizeType == customCarton; bool isCustomSize = _cartonSizeType == customCarton;
bool isNoneDefinedSize = _cartonSizeType == packageCartion; bool isNoneDefinedSize = _cartonSizeType == packageCarton;
final senderBox = DisplayText( final senderBox = DisplayText(
text: widget.sender.name, text: widget.sender.name,
@@ -139,7 +147,7 @@ class _CartonSizeWidgetState extends State<CartonSizeWidget> {
text: widget.consignee.name, text: widget.consignee.name,
labelTextKey: "box.consignee.title", labelTextKey: "box.consignee.title",
iconData: MaterialCommunityIcons.account_arrow_left, iconData: MaterialCommunityIcons.account_arrow_left,
subText: Text(widget.consignee.fcsID!, subText: Text(widget.consignee.fcsID ?? '',
style: TextStyle(fontSize: 13, color: labelColor)), style: TextStyle(fontSize: 13, color: labelColor)),
); );
@@ -161,16 +169,16 @@ class _CartonSizeWidgetState extends State<CartonSizeWidget> {
child: InkWell( child: InkWell(
onTap: () { onTap: () {
setState(() { setState(() {
_selectedDeliveryType = delivery_caton; _selectedLastmile = delivery_caton;
}); });
}, },
child: Row(children: <Widget>[ child: Row(children: <Widget>[
LocalRadio( LocalRadio(
value: delivery_caton, value: delivery_caton,
groupValue: _selectedDeliveryType, groupValue: _selectedLastmile,
onChanged: (p0) { onChanged: (p0) {
setState(() { setState(() {
_selectedDeliveryType = delivery_caton; _selectedLastmile = delivery_caton;
}); });
}, },
), ),
@@ -179,7 +187,7 @@ class _CartonSizeWidgetState extends State<CartonSizeWidget> {
padding: const EdgeInsets.only(left: 10), padding: const EdgeInsets.only(left: 10),
child: LocalText(context, 'box.delivery', child: LocalText(context, 'box.delivery',
fontSize: 15, fontSize: 15,
color: _selectedDeliveryType == delivery_caton color: _selectedLastmile == delivery_caton
? primaryColor ? primaryColor
: Colors.black), : Colors.black),
), ),
@@ -190,16 +198,16 @@ class _CartonSizeWidgetState extends State<CartonSizeWidget> {
child: InkWell( child: InkWell(
onTap: () { onTap: () {
setState(() { setState(() {
_selectedDeliveryType = pickup_carton; _selectedLastmile = pickup_carton;
}); });
}, },
child: Row(children: <Widget>[ child: Row(children: <Widget>[
LocalRadio( LocalRadio(
value: pickup_carton, value: pickup_carton,
groupValue: _selectedDeliveryType, groupValue: _selectedLastmile,
onChanged: (p0) { onChanged: (p0) {
setState(() { setState(() {
_selectedDeliveryType = pickup_carton; _selectedLastmile = pickup_carton;
}); });
}, },
), ),
@@ -208,7 +216,7 @@ class _CartonSizeWidgetState extends State<CartonSizeWidget> {
padding: const EdgeInsets.only(left: 10), padding: const EdgeInsets.only(left: 10),
child: LocalText(context, 'box.pickup', child: LocalText(context, 'box.pickup',
fontSize: 15, fontSize: 15,
color: _selectedDeliveryType == pickup_carton color: _selectedLastmile == pickup_carton
? primaryColor ? primaryColor
: Colors.black), : Colors.black),
), ),
@@ -315,7 +323,7 @@ class _CartonSizeWidgetState extends State<CartonSizeWidget> {
if (widget.onContinue != null) { if (widget.onContinue != null) {
widget.onContinue!( widget.onContinue!(
_selectedDeliveryType, _billToValue, _shipment!, _cartonSizeType, _selectedLastmile, _billToValue, _shipment!, _cartonSizeType,
standardSize: _selectStandardSize, length: l, width: w, height: h); standardSize: _selectStandardSize, length: l, width: w, height: h);
} }
}); });
@@ -463,16 +471,16 @@ class _CartonSizeWidgetState extends State<CartonSizeWidget> {
InkWell( InkWell(
onTap: () { onTap: () {
setState(() { setState(() {
_cartonSizeType = packageCartion; _cartonSizeType = packageCarton;
}); });
}, },
child: Row(children: <Widget>[ child: Row(children: <Widget>[
LocalRadio( LocalRadio(
value: packageCartion, value: packageCarton,
groupValue: _cartonSizeType, groupValue: _cartonSizeType,
onChanged: (p0) { onChanged: (p0) {
setState(() { setState(() {
_cartonSizeType = packageCartion; _cartonSizeType = packageCarton;
}); });
}, },
), ),

View File

@@ -30,7 +30,7 @@ class CartonSubmit extends StatelessWidget {
final double length; final double length;
final double width; final double width;
final double height; final double height;
final String deliveryType; final String lastMile;
final List<CargoType> cargoTypes; final List<CargoType> cargoTypes;
final List<CargoType> surchareItems; final List<CargoType> surchareItems;
final OnCreateCarton? onCreate; final OnCreateCarton? onCreate;
@@ -47,7 +47,7 @@ class CartonSubmit extends StatelessWidget {
this.packages = const [], this.packages = const [],
this.standardSize, this.standardSize,
required this.cartonSizeType, required this.cartonSizeType,
required this.deliveryType, required this.lastMile,
this.length = 0, this.length = 0,
this.width = 0, this.width = 0,
this.height = 0, this.height = 0,
@@ -69,10 +69,7 @@ class CartonSubmit extends StatelessWidget {
final cartonType = Padding( final cartonType = Padding(
padding: const EdgeInsets.only(top: 10), padding: const EdgeInsets.only(top: 10),
child: SubmitTextWidget( child: SubmitTextWidget(
labelKey: 'box.carton.type', labelKey: 'box.carton.type', text: carton_from_packages),
text: carton_from_packages,
// subText: boxDimension
),
); );
final shipmentBox = Padding( final shipmentBox = Padding(
@@ -187,15 +184,15 @@ class CartonSubmit extends StatelessWidget {
padding: const EdgeInsets.only(top: 10), padding: const EdgeInsets.only(top: 10),
child: SubmitTextWidget( child: SubmitTextWidget(
labelKey: 'box.select.delivery', labelKey: 'box.select.delivery',
text: deliveryType, text: lastMile == delivery_caton ? 'Delivery' : 'Pick-up',
), ),
); );
final cartonSizeBox = Padding( final cartonSizeBox = Padding(
padding: const EdgeInsets.only(top: 10), padding: const EdgeInsets.only(top: 10),
child: SubmitTextWidget( child: SubmitTextWidget(
labelKey: 'box.carton_size', labelKey: 'box.select_carton_size',
text: boxDimension??'', text: boxDimension ?? 'No defined size',
), ),
); );

View File

@@ -1,131 +0,0 @@
import 'package:fcs/domain/entities/cargo_type.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/pages/main/util.dart';
import 'package:fcs/pages/rates/model/shipment_rate_model.dart';
import 'package:fcs/pages/widgets/local_app_bar.dart';
import 'package:fcs/pages/widgets/progress.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../widgets/local_button.dart';
class CustomDutyAddition extends StatefulWidget {
final List<CargoType> customDuties;
const CustomDutyAddition({super.key, required this.customDuties});
@override
_CustomDutyAdditionState createState() => _CustomDutyAdditionState();
}
class _CustomDutyAdditionState extends State<CustomDutyAddition> {
bool _isLoading = false;
List<CargoType> customDuties = [];
@override
void initState() {
_init();
super.initState();
}
_init() {
var shipmentRateModel =
Provider.of<ShipmentRateModel>(context, listen: false);
customDuties =
shipmentRateModel.rate.customDuties.map((e) => e.clone()).toList();
for (var p in customDuties) {
p.qty = 0;
if (widget.customDuties.any((e) => e.id == p.id)) {
p.isChecked = true;
} else {
p.isChecked = false;
}
widget.customDuties.forEach((vp) {
if (p.id == vp.id) {
p.qty = vp.qty;
}
});
}
if (mounted) {
setState(() {});
}
}
@override
Widget build(BuildContext context) {
List<Widget> getCargoRowList(List<CargoType> _c) {
return _c.map((c) {
return Container(
child: Container(
padding:
EdgeInsets.only(left: 10.0, right: 5.0, top: 3.0, bottom: 3.0),
child: InkWell(
onTap: () {
setState(() {
c.isChecked = !c.isChecked;
});
},
child: Row(
children: <Widget>[
Checkbox(
value: c.isChecked,
activeColor: primaryColor,
onChanged: (bool? check) {
setState(() {
c.isChecked = check ?? false;
});
}),
new Text(c.name ?? '', style: textStyle),
],
),
),
),
);
}).toList();
}
final saveBtn = Padding(
padding: const EdgeInsets.symmetric(horizontal: 30),
child: LocalButton(
textKey: "box.cargo.select.btn",
callBack: () {
List<CargoType> _cargos =
customDuties.where((c) => c.isChecked).toList();
if (_cargos.isEmpty) {
showMsgDialog(context, 'Error', "Please select the cargo type");
return;
}
Navigator.pop(context, _cargos);
},
),
);
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: LocalAppBar(
labelKey: 'box.select.cargo_type',
backgroundColor: Colors.white,
labelColor: primaryColor,
arrowColor: primaryColor),
body: Container(
padding: EdgeInsets.all(10),
child: ListView(
shrinkWrap: true,
children: <Widget>[
Column(
children: getCargoRowList(customDuties),
),
SizedBox(height: 30),
saveBtn,
SizedBox(height: 20),
],
),
),
),
);
}
}

View File

@@ -12,6 +12,7 @@ import '../../../domain/entities/carton_size.dart';
import '../../../domain/entities/fcs_shipment.dart'; import '../../../domain/entities/fcs_shipment.dart';
import '../../../domain/vo/local_step.dart'; import '../../../domain/vo/local_step.dart';
import '../../../helpers/theme.dart'; import '../../../helpers/theme.dart';
import '../../carton_size/model/carton_size_model.dart';
import '../../main/util.dart'; import '../../main/util.dart';
import '../../widgets/local_text.dart'; import '../../widgets/local_text.dart';
import '../../widgets/progress.dart'; import '../../widgets/progress.dart';
@@ -58,10 +59,33 @@ class _MixCartonEditorState extends State<MixCartonEditor> {
_init() async { _init() async {
context.read<CartonSelectionModel>().clearSelection(); context.read<CartonSelectionModel>().clearSelection();
// check carton size type
List<CartonSize> cartonSizes = context.read<CartonSizeModel>().cartonSizes;
var sameLength =
cartonSizes.any((size) => size.length == widget.carton.length);
var sameWidth =
cartonSizes.any((size) => size.width == widget.carton.width);
var sameHeight =
cartonSizes.any((size) => size.height == widget.carton.height);
bool isStandartSize = sameLength && sameWidth && sameHeight;
if (isStandartSize) {
_cartonSizeType = standardCarton;
_standardSize = cartonSizes.firstWhere((size) =>
size.length == widget.carton.length &&
size.width == widget.carton.width &&
size.height == widget.carton.height);
} else if (widget.carton.length == 0 &&
widget.carton.width == 0 &&
widget.carton.height == 0) {
_cartonSizeType = packageCarton;
} else {
_cartonSizeType = customCarton; _cartonSizeType = customCarton;
_length = widget.carton.length; _length = widget.carton.length.toDouble();
_width = widget.carton.width; _width = widget.carton.width.toDouble();
_height = widget.carton.height; _height = widget.carton.height.toDouble();
}
var s = await context var s = await context
.read<FcsShipmentModel>() .read<FcsShipmentModel>()
@@ -70,7 +94,7 @@ class _MixCartonEditorState extends State<MixCartonEditor> {
_cartons = await context _cartons = await context
.read<CartonModel>() .read<CartonModel>()
.getCartonsByIds(widget.carton.mixCartonIDs); .getCartonsByIds(widget.carton.cartonIDs);
if (mounted) { if (mounted) {
setState(() {}); setState(() {});
@@ -113,8 +137,16 @@ class _MixCartonEditorState extends State<MixCartonEditor> {
}, },
), ),
backgroundColor: Colors.white, backgroundColor: Colors.white,
title: LocalText(context, 'box.update_title', title: Column(
children: [
LocalText(context, 'box.update_title',
color: primaryColor, fontSize: 20), color: primaryColor, fontSize: 20),
Text(
widget.carton.cartonNumber ?? '',
style: TextStyle(color: primaryColor, fontSize: 14),
)
],
),
), ),
body: Column( body: Column(
children: [ children: [
@@ -212,6 +244,35 @@ class _MixCartonEditorState extends State<MixCartonEditor> {
_isLoading = true; _isLoading = true;
}); });
try { try {
double length = 0;
double width = 0;
double height = 0;
if (_cartonSizeType == standardCarton) {
if (_standardSize != null) {
length = _standardSize!.length;
width = _standardSize!.width;
height = _standardSize!.height;
}
} else if (_cartonSizeType == customCarton) {
length = _length;
width = _width;
height = _height;
} else {
length = 0;
width = 0;
height = 0;
}
var carton = Carton(
id: widget.carton.id,
cartonType: mix_carton,
fcsShipmentID: _shipment?.id,
length: length,
width: width,
height: height,
cartons: _cartons);
await context.read<CartonModel>().updateMixCarton(carton);
Navigator.pop(context, true); Navigator.pop(context, true);
} catch (e) { } catch (e) {
showMsgDialog(context, "Error", e.toString()); showMsgDialog(context, "Error", e.toString());

View File

@@ -1,5 +1,6 @@
// ignore_for_file: deprecated_member_use // ignore_for_file: deprecated_member_use
import 'package:fcs/pages/carton/model/carton_model.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
@@ -187,7 +188,35 @@ class _MixCartonFormState extends State<MixCartonForm> {
_isLoading = true; _isLoading = true;
}); });
try { try {
Navigator.pop(context, true); double length = 0;
double width = 0;
double height = 0;
if (_cartonSizeType == standardCarton) {
if (_standardSize != null) {
length = _standardSize!.length;
width = _standardSize!.width;
height = _standardSize!.height;
}
} else if (_cartonSizeType == customCarton) {
length = _length;
width = _width;
height = _height;
} else {
length = 0;
width = 0;
height = 0;
}
var carton = Carton(
cartonType: mix_carton,
fcsShipmentID: _shipment?.id,
length: length,
width: width,
height: height,
cartons: _cartons);
var c = await context.read<CartonModel>().createMixCarton(carton);
Navigator.pop(context, c);
} catch (e) { } catch (e) {
showMsgDialog(context, "Error", e.toString()); showMsgDialog(context, "Error", e.toString());
} finally { } finally {

View File

@@ -48,8 +48,8 @@ class MixCartonSubmit extends StatefulWidget {
class _MixCartonSubmitState extends State<MixCartonSubmit> { class _MixCartonSubmitState extends State<MixCartonSubmit> {
final NumberFormat numberFormatter = NumberFormat("#,###"); final NumberFormat numberFormatter = NumberFormat("#,###");
Map<String?, double> _mapCargosByWeight = {}; Map<String?, double> _mapCargos = {};
Map<String?, double> _mapCargosByCustomDutyFee = {}; Map<String?, double> _mapSurchargeItems = {};
double totalWeight = 0; double totalWeight = 0;
@override @override
@@ -59,30 +59,37 @@ class _MixCartonSubmitState extends State<MixCartonSubmit> {
} }
_init() { _init() {
// get cargos by weight
List<CargoType> _cargoTypes = []; List<CargoType> _cargoTypes = [];
for (var c in widget.cartons) { for (var c in widget.cartons) {
_cargoTypes.addAll(c.cargoTypes); _cargoTypes.addAll(c.cargoTypes);
} }
// get cargos by weight _cargoTypes.sort((a, b) => a.name!.compareTo(b.name!));
Map<String?, List<CargoType>> _cargosByWeight =
groupCargos(_cargoTypes.where((e) => !e.isCutomDuty).toList());
Map<String?, List<CargoType>> _cargosByWeight = groupCargos(_cargoTypes);
_cargosByWeight.forEach((key, value) { _cargosByWeight.forEach((key, value) {
double total = value.fold(0, (sum, item) => sum + item.weight); double total = value.fold(0, (sum, item) => sum + item.weight);
_mapCargosByWeight[key] = total; _mapCargos[key] = total;
}); });
totalWeight = totalWeight = _mapCargos.entries.fold(0, (sum, value) => sum + value.value);
_mapCargosByWeight.entries.fold(0, (sum, value) => sum + value.value);
// get surcharge items
List<CargoType> _surchargeItems = [];
for (var c in widget.cartons) {
_surchargeItems.addAll(c.surchareItems);
}
_surchargeItems.sort((a, b) => a.name!.compareTo(b.name!));
// get cargos by custom duty fee
Map<String?, List<CargoType>> _cargosByCustomDutyFee = Map<String?, List<CargoType>> _cargosByCustomDutyFee =
groupCargos(_cargoTypes.where((e) => e.isCutomDuty).toList()); groupCargos(_surchargeItems);
_cargosByCustomDutyFee.forEach((key, value) { _cargosByCustomDutyFee.forEach((key, value) {
double total = value.fold(0, (sum, item) => sum + item.qty); double total = value.fold(0, (sum, item) => sum + item.qty);
_mapCargosByCustomDutyFee[key] = total; _mapSurchargeItems[key] = total;
}); });
if (mounted) { if (mounted) {
@@ -110,17 +117,14 @@ class _MixCartonSubmitState extends State<MixCartonSubmit> {
final cartonType = Padding( final cartonType = Padding(
padding: const EdgeInsets.only(top: 10), padding: const EdgeInsets.only(top: 10),
child: SubmitTextWidget( child: SubmitTextWidget(
labelKey: 'box.carton.type', labelKey: 'box.carton.type', text: carton_mix_carton),
text: carton_mix_carton,
// subText: boxDimension,
),
); );
final cartonSizeBox = Padding( final cartonSizeBox = Padding(
padding: const EdgeInsets.only(top: 10), padding: const EdgeInsets.only(top: 10),
child: SubmitTextWidget( child: SubmitTextWidget(
labelKey: 'box.carton_size', labelKey: 'box.select_carton_size',
text: boxDimension ?? '', text: boxDimension ?? 'No defined size',
), ),
); );
@@ -198,7 +202,7 @@ class _MixCartonSubmitState extends State<MixCartonSubmit> {
children: [ children: [
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: _mapCargosByWeight.entries.map((e) { children: _mapCargos.entries.map((e) {
return Padding( return Padding(
padding: const EdgeInsets.symmetric(vertical: 3), padding: const EdgeInsets.symmetric(vertical: 3),
child: Row( child: Row(
@@ -219,7 +223,7 @@ class _MixCartonSubmitState extends State<MixCartonSubmit> {
const SizedBox(height: 10), const SizedBox(height: 10),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: _mapCargosByCustomDutyFee.entries.map((e) { children: _mapSurchargeItems.entries.map((e) {
return Padding( return Padding(
padding: const EdgeInsets.symmetric(vertical: 3), padding: const EdgeInsets.symmetric(vertical: 3),
child: Row( child: Row(

View File

@@ -68,8 +68,6 @@ class _TypeWidgetState extends State<TypeWidget> {
} }
_init() async { _init() async {
List<CartonSize> cartonSizes = context.read<CartonSizeModel>().cartonSizes;
_selectStandardSize = widget.standardSize ?? cartonSizes.first;
_cartionSizeType = widget.cartonSizeType; _cartionSizeType = widget.cartonSizeType;
_lengthController.text = _lengthController.text =
@@ -79,6 +77,8 @@ class _TypeWidgetState extends State<TypeWidget> {
_heightController.text = _heightController.text =
widget.height == null ? "0" : removeTrailingZeros(widget.height ?? 0); widget.height == null ? "0" : removeTrailingZeros(widget.height ?? 0);
_getStandardCartonSize();
_loadShipment(); _loadShipment();
if (mounted) { if (mounted) {
@@ -86,11 +86,24 @@ class _TypeWidgetState extends State<TypeWidget> {
} }
} }
_getStandardCartonSize() {
List<CartonSize> cartonSizes = context.read<CartonSizeModel>().cartonSizes;
_selectStandardSize = widget.standardSize ?? cartonSizes.first;
if (mounted) {
setState(() {});
}
}
_loadShipment() async { _loadShipment() async {
var fcsShipments = var fcsShipments =
await context.read<FcsShipmentModel>().getActiveFcsShipments(); await context.read<FcsShipmentModel>().getActiveFcsShipments();
_shipments = fcsShipments; _shipments = fcsShipments;
if (_shipments.contains(widget.shipment)) {
_shipment = widget.shipment; _shipment = widget.shipment;
} else {
_shipment = null;
}
if (mounted) { if (mounted) {
setState(() {}); setState(() {});
} }
@@ -99,6 +112,7 @@ class _TypeWidgetState extends State<TypeWidget> {
@override @override
void didUpdateWidget(covariant TypeWidget oldWidget) { void didUpdateWidget(covariant TypeWidget oldWidget) {
_loadShipment(); _loadShipment();
_getStandardCartonSize();
super.didUpdateWidget(oldWidget); super.didUpdateWidget(oldWidget);
} }
@@ -109,7 +123,7 @@ class _TypeWidgetState extends State<TypeWidget> {
bool isStandardSize = _cartionSizeType == standardCarton; bool isStandardSize = _cartionSizeType == standardCarton;
bool isCustomSize = _cartionSizeType == customCarton; bool isCustomSize = _cartionSizeType == customCarton;
bool isNoneDefinedSize = _cartionSizeType == packageCartion; bool isNoneDefinedSize = _cartionSizeType == packageCarton;
final continueBtn = ContinueButton(onTap: () { final continueBtn = ContinueButton(onTap: () {
double l = double.tryParse(_lengthController.text) ?? 0; double l = double.tryParse(_lengthController.text) ?? 0;
@@ -287,16 +301,16 @@ class _TypeWidgetState extends State<TypeWidget> {
InkWell( InkWell(
onTap: () { onTap: () {
setState(() { setState(() {
_cartionSizeType = packageCartion; _cartionSizeType = packageCarton;
}); });
}, },
child: Row(children: <Widget>[ child: Row(children: <Widget>[
LocalRadio( LocalRadio(
value: packageCartion, value: packageCarton,
groupValue: _cartionSizeType, groupValue: _cartionSizeType,
onChanged: (p0) { onChanged: (p0) {
setState(() { setState(() {
_cartionSizeType = packageCartion; _cartionSizeType = packageCarton;
}); });
}, },
), ),

View File

@@ -6,6 +6,7 @@ import 'package:fcs/data/services/services.dart';
import 'package:fcs/constants.dart'; import 'package:fcs/constants.dart';
import 'package:fcs/domain/entities/carton.dart'; import 'package:fcs/domain/entities/carton.dart';
import 'package:fcs/domain/entities/fcs_shipment.dart'; import 'package:fcs/domain/entities/fcs_shipment.dart';
import 'package:fcs/helpers/shared_pref.dart';
import 'package:fcs/pages/main/model/base_model.dart'; import 'package:fcs/pages/main/model/base_model.dart';
import 'package:fcs/pagination/paginator_listener.dart'; import 'package:fcs/pagination/paginator_listener.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
@@ -18,8 +19,6 @@ class CartonModel extends BaseModel {
final log = Logger('CartonModel'); final log = Logger('CartonModel');
var defaultShipment = FcsShipment(shipmentNumber: "All shipments", id: all); var defaultShipment = FcsShipment(shipmentNumber: "All shipments", id: all);
PaginatorListener<Carton>? cartonsByFilter;
PaginatorListener<Carton>? getBoxes; PaginatorListener<Carton>? getBoxes;
String? filterByStatus; String? filterByStatus;
@@ -49,13 +48,10 @@ class CartonModel extends BaseModel {
@override @override
logout() async { logout() async {
getBoxes?.close(); getBoxes?.close();
cartonsByFilter?.close();
} }
Future<void> _initData() async { Future<void> _initData() async {
logout(); logout();
_loadPaginationCartons();
} }
filterCarton(FcsShipment? fcsShipment, User? consignee, User? sender, filterCarton(FcsShipment? fcsShipment, User? consignee, User? sender,
@@ -121,13 +117,15 @@ class CartonModel extends BaseModel {
Query pageQuery = FirebaseFirestore.instance.collection(path); Query pageQuery = FirebaseFirestore.instance.collection(path);
if (filterByConsingee != null) { if (filterByConsingee != null) {
col = col.where("user_id", isEqualTo: filterByConsingee!.id); col = col.where("consignee_user_id", isEqualTo: filterByConsingee!.id);
pageQuery = pageQuery.where("user_id", isEqualTo: filterByConsingee!.id); pageQuery = pageQuery.where("consignee_user_id",
isEqualTo: filterByConsingee!.id);
} }
if (filterBySender != null) { if (filterBySender != null) {
col = col.where("sender_id", isEqualTo: filterBySender!.id); col = col.where("sender_user_id", isEqualTo: filterBySender!.id);
pageQuery = pageQuery.where("sender_id", isEqualTo: filterBySender!.id); pageQuery =
pageQuery.where("sender_user_id", isEqualTo: filterBySender!.id);
} }
if (filterByStatus != null) { if (filterByStatus != null) {
@@ -140,7 +138,7 @@ class CartonModel extends BaseModel {
pageQuery = pageQuery.where("fcs_shipment_id", isEqualTo: shipment!.id); pageQuery = pageQuery.where("fcs_shipment_id", isEqualTo: shipment!.id);
} }
pageQuery = pageQuery.orderBy("created_at", descending: true); pageQuery = pageQuery.orderBy("update_time", descending: true);
getBoxes?.close(); getBoxes?.close();
getBoxes = PaginatorListener<Carton>( getBoxes = PaginatorListener<Carton>(
@@ -148,30 +146,6 @@ class CartonModel extends BaseModel {
rowPerLoad: 30); rowPerLoad: 30);
} }
_loadPaginationCartons() {
if (user == null || !user!.hasCarton()) return null;
String path = "/$cartons_collection";
Query col = FirebaseFirestore.instance.collection(path);
// .where("carton_type",
// whereIn: [
// carton_from_packages,
// carton_from_cartons
// ]).where("status", isEqualTo: carton_packed_status)
// ;
Query pageQuery = FirebaseFirestore.instance
.collection(path)
// .where("carton_type",
// whereIn: [carton_from_packages, carton_from_cartons])
// .where("status", isEqualTo: carton_packed_status)
.orderBy("created_at", descending: true);
cartonsByFilter?.close();
cartonsByFilter = PaginatorListener<Carton>(
col, pageQuery, (data, id) => Carton.fromMap(data, id),
rowPerLoad: 30);
}
Future<List<Carton>> getCartons(String shipmentID) async { Future<List<Carton>> getCartons(String shipmentID) async {
String path = "/$cartons_collection"; String path = "/$cartons_collection";
var querySnap = await FirebaseFirestore.instance var querySnap = await FirebaseFirestore.instance
@@ -235,7 +209,18 @@ class CartonModel extends BaseModel {
return Services.instance.cartonService.deleteCarton(carton); return Services.instance.cartonService.deleteCarton(carton);
} }
Future<Carton> createMixCarton(Carton carton) {
return Services.instance.cartonService.createMixCarton(carton);
}
Future<void> updateMixCarton(Carton carton) {
return Services.instance.cartonService.updateMixCarton(carton);
}
Future<List<Carton>> searchCarton(String term) async { Future<List<Carton>> searchCarton(String term) async {
if (term != '') {
await SharedPref.saveRecentSearch('carton_search', term);
}
return Services.instance.cartonService.searchCarton(term); return Services.instance.cartonService.searchCarton(term);
} }
@@ -276,9 +261,11 @@ class CartonModel extends BaseModel {
uploadedURL.forEach((url) { uploadedURL.forEach((url) {
carton.photoUrls.add(url); carton.photoUrls.add(url);
}); });
carton.photoUrls.removeWhere((e) => deletedUrls.contains(e));
} }
try { try {
// await Services.instance.packageService.updateReceiving(package); await Services.instance.cartonService.uploadCartonImages(carton);
} catch (e) { } catch (e) {
// delete newly uploaded photos if fails // delete newly uploaded photos if fails
try { try {

View File

@@ -54,7 +54,7 @@ class CartonSelectionModel extends BaseModel {
query = query.where("carton_number", isEqualTo: term); query = query.where("carton_number", isEqualTo: term);
} }
query = query.orderBy("created_at", descending: true); query = query.orderBy("update_time", descending: true);
if (_lastDocument != null) { if (_lastDocument != null) {
query = query.startAfterDocument(_lastDocument!); query = query.startAfterDocument(_lastDocument!);

View File

@@ -2,10 +2,10 @@ import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:fcs/config.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import '../../../constants.dart'; import '../../../constants.dart';
import '../../../data/services/services.dart';
import '../../../domain/entities/user.dart'; import '../../../domain/entities/user.dart';
import '../../../helpers/api_helper.dart'; import '../../../helpers/api_helper.dart';
import '../../../helpers/firebase_helper.dart'; import '../../../helpers/firebase_helper.dart';
@@ -56,7 +56,7 @@ class ConsigneeSelectionModel extends BaseModel {
var result = await requestAPI( var result = await requestAPI(
"/api/fts/$user_collection/$escapeSender/$rowPerPage", "GET", "/api/fts/$user_collection/$escapeSender/$rowPerPage", "GET",
url: Config.instance.reportURL, token: await getToken()); url: Services.setting.reportURL, token: await getToken());
if (result != null) { if (result != null) {
for (var row in result) { for (var row in result) {
@@ -91,8 +91,8 @@ class ConsigneeSelectionModel extends BaseModel {
Query query = FirebaseFirestore.instance Query query = FirebaseFirestore.instance
.collection(path) .collection(path)
.where("is_sys_admin", isEqualTo: false) .where("is_sys_admin", isEqualTo: false)
.where("is_deleted", isEqualTo: false) .where("delete_time", isEqualTo: 0)
.orderBy("message_time", descending: true); .orderBy("update_time", descending: true);
if (_lastDocument != null) { if (_lastDocument != null) {
query = query.startAfterDocument(_lastDocument!); query = query.startAfterDocument(_lastDocument!);

View File

@@ -3,9 +3,8 @@ import 'dart:convert';
import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import '../../../config.dart';
import '../../../constants.dart'; import '../../../constants.dart';
import '../../../data/services/services.dart';
import '../../../domain/entities/user.dart'; import '../../../domain/entities/user.dart';
import '../../../helpers/api_helper.dart'; import '../../../helpers/api_helper.dart';
import '../../../helpers/firebase_helper.dart'; import '../../../helpers/firebase_helper.dart';
@@ -57,7 +56,7 @@ class SenderSelectionModel extends BaseModel {
var result = await requestAPI( var result = await requestAPI(
"/api/fts/$user_collection/$escapeSender/$rowPerPage", "GET", "/api/fts/$user_collection/$escapeSender/$rowPerPage", "GET",
url: Config.instance.reportURL, token: await getToken()); url: Services.setting.reportURL, token: await getToken());
if (result != null) { if (result != null) {
for (var row in result) { for (var row in result) {
@@ -93,8 +92,8 @@ class SenderSelectionModel extends BaseModel {
Query query = FirebaseFirestore.instance Query query = FirebaseFirestore.instance
.collection(path) .collection(path)
.where("is_sys_admin", isEqualTo: false) .where("is_sys_admin", isEqualTo: false)
.where("is_deleted", isEqualTo: false) .where("delete_time", isEqualTo: 0)
.orderBy("message_time", descending: true); .orderBy("update_time", descending: true);
if (_lastDocument != null) { if (_lastDocument != null) {
query = query.startAfterDocument(_lastDocument!); query = query.startAfterDocument(_lastDocument!);

View File

@@ -35,8 +35,11 @@ class PrintQrCodePage extends StatelessWidget {
children: [ children: [
Text(carton.cartonNumber ?? "", Text(carton.cartonNumber ?? "",
style: TextStyle(fontSize: 18, fontFamily: "Roboto")), style: TextStyle(fontSize: 18, fontFamily: "Roboto")),
Text(carton.userName!, carton.consigneeName == null || carton.consigneeName == ''
style: TextStyle(fontSize: 16, fontFamily: "Roboto")), ? const SizedBox()
: Text(carton.consigneeName!,
style:
TextStyle(fontSize: 16, fontFamily: "Roboto")),
Padding( Padding(
padding: const EdgeInsets.only(top: 3), padding: const EdgeInsets.only(top: 3),
child: Text("${carton.actualWeight} lb", child: Text("${carton.actualWeight} lb",

View File

@@ -0,0 +1,114 @@
import 'package:fcs/domain/entities/cargo_type.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/pages/rates/model/shipment_rate_model.dart';
import 'package:fcs/pages/widgets/local_app_bar.dart';
import 'package:fcs/pages/widgets/progress.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../main/util.dart';
class SurchargeItemAddition extends StatefulWidget {
final List<CargoType> items;
const SurchargeItemAddition({super.key, required this.items});
@override
_SurchargeItemAdditionState createState() => _SurchargeItemAdditionState();
}
class _SurchargeItemAdditionState extends State<SurchargeItemAddition> {
bool _isLoading = false;
List<CargoType> surchargeItems = [];
@override
void initState() {
_init();
super.initState();
}
_init() {
var shipmentRateModel =
Provider.of<ShipmentRateModel>(context, listen: false);
surchargeItems =
shipmentRateModel.rate.customDuties.map((e) => e.clone()).toList();
for (var p in surchargeItems) {
p.qty = 0;
if (widget.items.any((e) => e.id == p.id)) {
p.isChecked = true;
} else {
p.isChecked = false;
}
widget.items.forEach((vp) {
if (p.id == vp.id) {
p.qty = vp.qty;
}
});
}
if (mounted) {
setState(() {});
}
}
_onTap(CargoType cargo) {
if (cargo.isChecked) {
showMsgDialog(context, "Error", "Already exit!");
return;
}
Navigator.pop(context, cargo);
}
@override
Widget build(BuildContext context) {
List<Widget> getCargoRowList(List<CargoType> _c) {
return _c.map((c) {
return ListTile(
onTap: () {
_onTap(c);
},
title: new Text(c.name ?? '', style: textStyle),
trailing: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: primaryColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5),
),
minimumSize: Size(80, 35)),
onPressed: c.isChecked
? null
: () {
_onTap(c);
},
child: Text(
"Add",
style: TextStyle(color: Colors.white, fontSize: 14),
),
));
}).toList();
}
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: LocalAppBar(
labelKey: 'box.select.cargo_type',
backgroundColor: Colors.white,
labelColor: primaryColor,
arrowColor: primaryColor),
body: Container(
padding: EdgeInsets.all(10),
child: ListView(
shrinkWrap: true,
children: <Widget>[
Column(
children: getCargoRowList(surchargeItems),
),
SizedBox(height: 30),
],
),
),
),
);
}
}

View File

@@ -56,7 +56,7 @@ class CartonListRow extends StatelessWidget {
Padding( Padding(
padding: const EdgeInsets.only(top: 5), padding: const EdgeInsets.only(top: 5),
child: new Text( child: new Text(
box.userName ?? "", box.consigneeName ?? "",
style: new TextStyle( style: new TextStyle(
fontSize: 15.0, color: Colors.grey), fontSize: 15.0, color: Colors.grey),
), ),

View File

@@ -62,7 +62,7 @@ class CartonListRow extends StatelessWidget {
Padding( Padding(
padding: const EdgeInsets.only(top: 5), padding: const EdgeInsets.only(top: 5),
child: new Text( child: new Text(
carton.userName ?? "", carton.consigneeName ?? "",
style: new TextStyle( style: new TextStyle(
fontSize: 15.0, color: Colors.grey), fontSize: 15.0, color: Colors.grey),
), ),

View File

@@ -7,6 +7,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_vector_icons/flutter_vector_icons.dart'; import 'package:flutter_vector_icons/flutter_vector_icons.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../../helpers/shared_pref.dart';
import '../widgets/suggest_list.dart';
import 'carton_list_row.dart'; import 'carton_list_row.dart';
typedef CallbackCartonSelect(Carton carton); typedef CallbackCartonSelect(Carton carton);
@@ -32,7 +34,7 @@ class PartSearchDelegate extends SearchDelegate<Carton> {
appBarTheme: AppBarTheme(color: primaryColor), appBarTheme: AppBarTheme(color: primaryColor),
iconButtonTheme: IconButtonThemeData( iconButtonTheme: IconButtonThemeData(
style: ButtonStyle( style: ButtonStyle(
iconColor: MaterialStateProperty.all<Color>(Colors.white))), iconColor: WidgetStateProperty.all<Color>(Colors.white))),
inputDecorationTheme: InputDecorationTheme( inputDecorationTheme: InputDecorationTheme(
border: InputBorder.none, border: InputBorder.none,
hintStyle: TextStyle(color: Colors.grey, fontSize: 14)), hintStyle: TextStyle(color: Colors.grey, fontSize: 14)),
@@ -120,15 +122,29 @@ class PartSearchDelegate extends SearchDelegate<Carton> {
@override @override
Widget buildSuggestions(BuildContext context) { Widget buildSuggestions(BuildContext context) {
return Container( return FutureBuilder<List<String>?>(
child: Center( future: SharedPref.getRecentSearch('carton_search', query),
builder: (context, snapshot) {
List<String> _oldFilters = snapshot.data ?? [];
if (_oldFilters.isEmpty) {
return const Center(
child: Opacity( child: Opacity(
opacity: 0.2, opacity: 0.2,
child: Icon(MaterialCommunityIcons.package, child: Icon(MaterialCommunityIcons.package,
size: 200, color: primaryColor)), color: primaryColor, size: 200),
), ),
); );
} }
return SuggestList(
recentSearchList: _oldFilters,
onTap: (String s) {
query = s;
showResults(context);
},
prefKey: 'carton_search');
},
);
}
_scan(BuildContext context) async { _scan(BuildContext context) async {
// PermissionStatus permission = // PermissionStatus permission =

View File

@@ -86,10 +86,10 @@ class _DeliveryInfoState extends State<DeliveryInfo> {
_loadPackages() async { _loadPackages() async {
if (!isFromPackages && !isSmallBag) return; if (!isFromPackages && !isSmallBag) return;
if (_box.cartonType == carton_from_packages && _box.userID == null) return; if (_box.cartonType == carton_from_packages && _box.consigneeID == null) return;
PackageModel packageModel = PackageModel packageModel =
Provider.of<PackageModel>(context, listen: false); Provider.of<PackageModel>(context, listen: false);
List<Package> packages = await packageModel.getPackages(_box.userID!, [ List<Package> packages = await packageModel.getPackages(_box.consigneeID!, [
package_processed_status, package_processed_status,
package_packed_status, package_packed_status,
package_shipped_status, package_shipped_status,
@@ -137,13 +137,13 @@ class _DeliveryInfoState extends State<DeliveryInfo> {
iconData: Ionicons.ios_airplane, iconData: Ionicons.ios_airplane,
); );
final fcsIDBox = DisplayText( final fcsIDBox = DisplayText(
text: _box.fcsID, text: _box.consigneeFCSID,
labelTextKey: "box.fcs.id", labelTextKey: "box.fcs.id",
icon: FcsIDIcon(), icon: FcsIDIcon(),
); );
final customerNameBox = DisplayText( final customerNameBox = DisplayText(
text: _box.userName, text: _box.consigneeName,
labelTextKey: "box.name", labelTextKey: "box.name",
iconData: Icons.person, iconData: Icons.person,
); );

View File

@@ -48,7 +48,7 @@ class DeliveryListRow extends StatelessWidget {
Padding( Padding(
padding: const EdgeInsets.only(top: 5), padding: const EdgeInsets.only(top: 5),
child: new Text( child: new Text(
box.userName ?? "", box.consigneeName ?? "",
style: new TextStyle( style: new TextStyle(
fontSize: 15.0, color: Colors.grey), fontSize: 15.0, color: Colors.grey),
), ),

View File

@@ -1,4 +1,6 @@
import 'package:fcs/domain/entities/fcs_shipment.dart'; import 'package:fcs/domain/entities/fcs_shipment.dart';
import 'package:fcs/domain/entities/shipment_consignee.dart';
import 'package:fcs/domain/entities/shipment_port.dart';
import 'package:fcs/helpers/theme.dart'; import 'package:fcs/helpers/theme.dart';
import 'package:fcs/localization/app_translations.dart'; import 'package:fcs/localization/app_translations.dart';
import 'package:fcs/pages/fcs_shipment/model/fcs_shipment_model.dart'; import 'package:fcs/pages/fcs_shipment/model/fcs_shipment_model.dart';
@@ -30,16 +32,16 @@ class _FcsShipmentEditorState extends State<FcsShipmentEditor> {
var dateFormatter = new DateFormat('dd MMM yyyy'); var dateFormatter = new DateFormat('dd MMM yyyy');
TextEditingController _shipmentNumberController = new TextEditingController(); TextEditingController _shipmentNumberController = new TextEditingController();
TextEditingController _cutoffDateController = new TextEditingController(); TextEditingController _cutoffDateController = new TextEditingController();
TextEditingController _arrivalDateController = new TextEditingController(); TextEditingController _etaDateController = new TextEditingController();
TextEditingController _departureDateControler = new TextEditingController();
TextEditingController _consigneeController = new TextEditingController();
TextEditingController _portController = new TextEditingController();
TextEditingController _destinationController = new TextEditingController();
TextEditingController _statusController = new TextEditingController(); TextEditingController _statusController = new TextEditingController();
FcsShipment _shipment = new FcsShipment(); FcsShipment _shipment = new FcsShipment();
bool _isLoading = false; bool _isLoading = false;
ShipmentType? _currentShipmentType; ShipmentType? _currentShipmentType;
ShipmentConsignee? _currentConsignee;
ShipmentPort? _currentLoadingPort;
ShipmentPort? _currentDestination;
bool _isNew = false; bool _isNew = false;
@override @override
@@ -53,24 +55,38 @@ class _FcsShipmentEditorState extends State<FcsShipmentEditor> {
if (_shipment.cutoffDate != null) if (_shipment.cutoffDate != null)
_cutoffDateController.text = _cutoffDateController.text =
dateFormatter.format(_shipment.cutoffDate!); dateFormatter.format(_shipment.cutoffDate!);
if (_shipment.arrivalDate != null) if (_shipment.etaDate != null)
_arrivalDateController.text = _etaDateController.text = dateFormatter.format(_shipment.etaDate!);
dateFormatter.format(_shipment.arrivalDate!);
if (_shipment.departureDate != null)
_departureDateControler.text =
dateFormatter.format(_shipment.departureDate!);
_statusController.text = _shipment.status ?? "";
_consigneeController.text = _shipment.consignee ?? "";
_portController.text = _shipment.port ?? "";
_destinationController.text = _shipment.destination ?? "";
List<ShipmentType> list = model.shipmentTypes _statusController.text = _shipment.status ?? "";
List<ShipmentType> shipmentTypes = model.shipmentTypes
.where((e) => e.id == _shipment.shipmentTypeId) .where((e) => e.id == _shipment.shipmentTypeId)
.toList(); .toList();
_currentShipmentType =
shipmentTypes.isNotEmpty ? shipmentTypes.first : null;
_currentShipmentType = list.isNotEmpty ? list.first : null; List<ShipmentConsignee> shipmentConsingees = model.shipmentConsingees
.where((e) => e.id == _shipment.consigneeId)
.toList();
_currentConsignee =
shipmentConsingees.isNotEmpty ? shipmentConsingees.first : null;
List<ShipmentPort> loadingPorts = model.shipmentPorts
.where((e) => e.id == _shipment.loadingPortId)
.toList();
_currentLoadingPort = loadingPorts.isNotEmpty ? loadingPorts.first : null;
List<ShipmentPort> loadingDestination = model.shipmentPorts
.where((e) => e.id == _shipment.destinationPortId)
.toList();
_currentDestination =
loadingDestination.isNotEmpty ? loadingDestination.first : null;
} else { } else {
_currentShipmentType = model.shipmentTypes[0]; _currentShipmentType = model.shipmentTypes.first;
_currentConsignee = model.shipmentConsingees.first;
_currentLoadingPort = model.shipmentPorts.first;
_currentDestination = model.shipmentPorts.first;
} }
} }
@@ -82,8 +98,111 @@ class _FcsShipmentEditorState extends State<FcsShipmentEditor> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var languageModel = Provider.of<LanguageModel>(context); var languageModel = Provider.of<LanguageModel>(context);
List<ShipmentType> shipmentTypes = var shipmentModel = context.watch<FcsShipmentModel>();
context.watch<FcsShipmentModel>().shipmentTypes; List<ShipmentType> shipmentTypes = shipmentModel.shipmentTypes;
List<ShipmentConsignee> shipmentConsingees =
shipmentModel.shipmentConsingees;
List<ShipmentPort> shipmentPorts = shipmentModel.shipmentPorts;
final shipmentTypeBox = DropdownButtonFormField(
value: _currentShipmentType == "" ? null : _currentShipmentType,
decoration: InputDecoration(
contentPadding: EdgeInsets.zero,
enabledBorder:
UnderlineInputBorder(borderSide: BorderSide(color: primaryColor)),
focusedBorder:
UnderlineInputBorder(borderSide: BorderSide(color: primaryColor)),
fillColor: Colors.white,
labelStyle: languageModel.isEng
? newLabelStyle(color: Colors.black54, fontSize: 20)
: newLabelStyleMM(color: Colors.black54, fontSize: 20),
labelText:
AppTranslations.of(context)!.text('FCSshipment.shipment_type'),
icon: Icon(Ionicons.ios_airplane, color: primaryColor)),
items: shipmentTypes
.map((e) => DropdownMenuItem(child: Text(e.name), value: e))
.toList(),
onChanged: (selected) => {
setState(() {
_currentShipmentType = selected;
})
},
);
final consigneeBox = DropdownButtonFormField(
value: _currentConsignee == "" ? null : _currentConsignee,
decoration: InputDecoration(
contentPadding: EdgeInsets.zero,
enabledBorder:
UnderlineInputBorder(borderSide: BorderSide(color: primaryColor)),
focusedBorder:
UnderlineInputBorder(borderSide: BorderSide(color: primaryColor)),
fillColor: Colors.white,
labelStyle: languageModel.isEng
? newLabelStyle(color: Colors.black54, fontSize: 20)
: newLabelStyleMM(color: Colors.black54, fontSize: 20),
labelText: AppTranslations.of(context)!.text('FCSshipment.consignee'),
icon: Icon(Icons.work, color: primaryColor)),
items: shipmentConsingees
.map((e) => DropdownMenuItem(child: Text(e.name), value: e))
.toList(),
onChanged: (selected) => {
setState(() {
_currentConsignee = selected;
})
},
);
final loadingPortBox = DropdownButtonFormField(
value: _currentLoadingPort == "" ? null : _currentLoadingPort,
decoration: InputDecoration(
contentPadding: EdgeInsets.zero,
enabledBorder:
UnderlineInputBorder(borderSide: BorderSide(color: primaryColor)),
focusedBorder:
UnderlineInputBorder(borderSide: BorderSide(color: primaryColor)),
fillColor: Colors.white,
labelStyle: languageModel.isEng
? newLabelStyle(color: Colors.black54, fontSize: 20)
: newLabelStyleMM(color: Colors.black54, fontSize: 20),
labelText:
AppTranslations.of(context)!.text('FCSshipment.port_of_loading'),
icon: Icon(FontAwesomeIcons.ship, color: primaryColor)),
items: shipmentPorts
.map((e) => DropdownMenuItem(child: Text(e.name), value: e))
.toList(),
onChanged: (selected) => {
setState(() {
_currentLoadingPort = selected;
})
},
);
final destinationBox = DropdownButtonFormField(
value: _currentDestination == "" ? null : _currentDestination,
decoration: InputDecoration(
contentPadding: EdgeInsets.zero,
enabledBorder:
UnderlineInputBorder(borderSide: BorderSide(color: primaryColor)),
focusedBorder:
UnderlineInputBorder(borderSide: BorderSide(color: primaryColor)),
fillColor: Colors.white,
labelStyle: languageModel.isEng
? newLabelStyle(color: Colors.black54, fontSize: 20)
: newLabelStyleMM(color: Colors.black54, fontSize: 20),
labelText: AppTranslations.of(context)!
.text('FCSshipment.final_destination'),
icon:
Icon(MaterialCommunityIcons.location_enter, color: primaryColor)),
items: shipmentPorts
.map((e) => DropdownMenuItem(child: Text(e.name), value: e))
.toList(),
onChanged: (selected) => {
setState(() {
_currentDestination = selected;
})
},
);
final createBtn = Padding( final createBtn = Padding(
padding: const EdgeInsets.symmetric(horizontal: 30), padding: const EdgeInsets.symmetric(horizontal: 30),
@@ -150,7 +269,7 @@ class _FcsShipmentEditorState extends State<FcsShipmentEditor> {
InputDate( InputDate(
labelTextKey: "FCSshipment.ETA", labelTextKey: "FCSshipment.ETA",
iconData: Icons.date_range, iconData: Icons.date_range,
controller: _arrivalDateController, controller: _etaDateController,
autovalidateMode: AutovalidateMode.onUserInteraction, autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (value) { validator: (value) {
if (value!.isEmpty) { if (value!.isEmpty) {
@@ -160,47 +279,14 @@ class _FcsShipmentEditorState extends State<FcsShipmentEditor> {
}, },
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
DropdownButtonFormField( shipmentTypeBox,
value: const SizedBox(height: 25),
_currentShipmentType == "" ? null : _currentShipmentType, consigneeBox,
decoration: InputDecoration( const SizedBox(height: 25),
contentPadding: EdgeInsets.zero, loadingPortBox,
enabledBorder: UnderlineInputBorder( const SizedBox(height: 25),
borderSide: BorderSide(color: primaryColor)), destinationBox,
focusedBorder: UnderlineInputBorder( SizedBox(height: 30),
borderSide: BorderSide(color: primaryColor)),
fillColor: Colors.white,
labelStyle: languageModel.isEng
? newLabelStyle(color: Colors.black54, fontSize: 20)
: newLabelStyleMM(
color: Colors.black54, fontSize: 20),
labelText: AppTranslations.of(context)!
.text('FCSshipment.shipment_type'),
icon: Icon(Ionicons.ios_airplane, color: primaryColor)),
items: shipmentTypes
.map((e) =>
DropdownMenuItem(child: Text(e.name), value: e))
.toList(),
onChanged: (selected) => {
setState(() {
_currentShipmentType = selected;
})
},
),
const SizedBox(height: 5),
InputText(
labelTextKey: 'FCSshipment.consignee',
iconData: Icons.work,
controller: _consigneeController),
InputText(
labelTextKey: 'FCSshipment.port_of_loading',
iconData: FontAwesomeIcons.ship,
controller: _portController),
InputText(
labelTextKey: 'FCSshipment.final_destination',
iconData: MaterialCommunityIcons.location_enter,
controller: _destinationController),
SizedBox(height: 20),
_isNew ? createBtn : updateBtn, _isNew ? createBtn : updateBtn,
SizedBox(height: 15) SizedBox(height: 15)
]), ]),
@@ -214,16 +300,15 @@ class _FcsShipmentEditorState extends State<FcsShipmentEditor> {
fcsShipment.id = _shipment.id; fcsShipment.id = _shipment.id;
fcsShipment.shipmentNumber = _shipmentNumberController.text; fcsShipment.shipmentNumber = _shipmentNumberController.text;
fcsShipment.shipmentTypeId = _currentShipmentType?.id ?? ""; fcsShipment.shipmentTypeId = _currentShipmentType?.id ?? "";
fcsShipment.consignee = _consigneeController.text; fcsShipment.consigneeId = _currentConsignee?.id ?? "";
fcsShipment.port = _portController.text; fcsShipment.loadingPortId = _currentLoadingPort?.id ?? "";
fcsShipment.destination = _destinationController.text; fcsShipment.destinationPortId = _currentDestination?.id ?? "";
var cutoffDate = _cutoffDateController.text; var cutoffDate = _cutoffDateController.text;
var arrivalDate = _arrivalDateController.text; var etaDate = _etaDateController.text;
fcsShipment.cutoffDate = fcsShipment.cutoffDate =
(cutoffDate == "" ? null : dateFormatter.parse(cutoffDate))!; (cutoffDate == "" ? null : dateFormatter.parse(cutoffDate))!;
fcsShipment.arrivalDate = fcsShipment.etaDate = (etaDate == "" ? null : dateFormatter.parse(etaDate));
(arrivalDate == "" ? null : dateFormatter.parse(arrivalDate))!;
return fcsShipment; return fcsShipment;
} }
@@ -271,11 +356,11 @@ class _FcsShipmentEditorState extends State<FcsShipmentEditor> {
var shipmentModel = Provider.of<FcsShipmentModel>(context, listen: false); var shipmentModel = Provider.of<FcsShipmentModel>(context, listen: false);
return _shipmentNumberController.text != "" || return _shipmentNumberController.text != "" ||
_cutoffDateController.text != "" || _cutoffDateController.text != "" ||
_arrivalDateController.text != "" || _etaDateController.text != "" ||
_consigneeController.text != "" || _currentConsignee != shipmentModel.shipmentConsingees.first ||
_portController.text != "" || _currentShipmentType != shipmentModel.shipmentTypes.first ||
_destinationController.text != "" || _currentLoadingPort != shipmentModel.shipmentPorts.first ||
_currentShipmentType != shipmentModel.shipmentTypes[0]; _currentDestination != shipmentModel.shipmentPorts.first;
} else { } else {
FcsShipment fcsShipment = _getPayload(); FcsShipment fcsShipment = _getPayload();
return widget.shipment!.isChangedForEdit(fcsShipment); return widget.shipment!.isChangedForEdit(fcsShipment);

View File

@@ -20,8 +20,8 @@ import 'package:provider/provider.dart';
import 'fcs_shipment_editor.dart'; import 'fcs_shipment_editor.dart';
class FcsShipmentInfo extends StatefulWidget { class FcsShipmentInfo extends StatefulWidget {
final FcsShipment? fcsShipment; final FcsShipment fcsShipment;
FcsShipmentInfo({this.fcsShipment}); FcsShipmentInfo({required this.fcsShipment});
@override @override
_FcsShipmentInfoState createState() => _FcsShipmentInfoState(); _FcsShipmentInfoState createState() => _FcsShipmentInfoState();
@@ -29,7 +29,7 @@ class FcsShipmentInfo extends StatefulWidget {
class _FcsShipmentInfoState extends State<FcsShipmentInfo> { class _FcsShipmentInfoState extends State<FcsShipmentInfo> {
var dateFormatter = new DateFormat('dd MMM yyyy'); var dateFormatter = new DateFormat('dd MMM yyyy');
FcsShipment? _fcsShipment; late FcsShipment _fcsShipment;
bool _isLoading = false; bool _isLoading = false;
TextEditingController _shipmentNumberController = new TextEditingController(); TextEditingController _shipmentNumberController = new TextEditingController();
TextEditingController _cutoffDateController = new TextEditingController(); TextEditingController _cutoffDateController = new TextEditingController();
@@ -49,21 +49,21 @@ class _FcsShipmentInfoState extends State<FcsShipmentInfo> {
} }
_load() { _load() {
_shipmentNumberController.text = _fcsShipment?.shipmentNumber ?? ""; _shipmentNumberController.text = _fcsShipment.shipmentNumber ?? "";
if (_fcsShipment?.cutoffDate != null) if (_fcsShipment.cutoffDate != null)
_cutoffDateController.text = _cutoffDateController.text =
dateFormatter.format(_fcsShipment!.cutoffDate!); dateFormatter.format(_fcsShipment.cutoffDate!);
if (_fcsShipment?.arrivalDate != null) if (_fcsShipment.etaDate != null)
_arrivalDateController.text = _arrivalDateController.text = dateFormatter.format(_fcsShipment.etaDate!);
dateFormatter.format(_fcsShipment!.arrivalDate!); if (_fcsShipment.departureDate != null)
if (_fcsShipment?.departureDate != null)
_departureDateControler.text = _departureDateControler.text =
dateFormatter.format(_fcsShipment!.departureDate!); dateFormatter.format(_fcsShipment.departureDate!);
_shipmentTypeControler.text = _fcsShipment!.shipTypeName ?? "";
_consigneeController.text = _fcsShipment!.consignee ?? ""; _shipmentTypeControler.text = _fcsShipment.shipmentTypeName ?? "";
_portController.text = _fcsShipment!.port ?? ""; _consigneeController.text = _fcsShipment.consigneeName ?? '';
_destinationController.text = _fcsShipment!.destination ?? ""; _portController.text = _fcsShipment.loadingPortName ?? '';
_statusController.text = _fcsShipment!.status ?? ""; _destinationController.text = _fcsShipment.destinationPortName ?? '';
_statusController.text = _fcsShipment.status ?? "";
} }
@override @override
@@ -176,7 +176,7 @@ class _FcsShipmentInfoState extends State<FcsShipmentInfo> {
labelColor: primaryColor, labelColor: primaryColor,
arrowColor: primaryColor, arrowColor: primaryColor,
actions: [ actions: [
_fcsShipment?.status == fcs_shipment_pending_status _fcsShipment.status == fcs_shipment_pending_status
? IconButton( ? IconButton(
icon: Icon(Icons.edit, color: primaryColor), icon: Icon(Icons.edit, color: primaryColor),
onPressed: _edit, onPressed: _edit,
@@ -212,20 +212,20 @@ class _FcsShipmentInfoState extends State<FcsShipmentInfo> {
portBox, portBox,
destinationBox, destinationBox,
const SizedBox(height: 30), const SizedBox(height: 30),
_fcsShipment?.status == fcs_shipment_pending_status _fcsShipment.status == fcs_shipment_pending_status
? processBtn ? processBtn
: Container(), : Container(),
_fcsShipment?.status == fcs_shipment_pending_status _fcsShipment.status == fcs_shipment_pending_status
? Container( ? Container(
padding: EdgeInsets.only(top: 3), child: cancelBtn) padding: EdgeInsets.only(top: 3), child: cancelBtn)
: Container(), : Container(),
_fcsShipment?.status == fcs_shipment_processed_status _fcsShipment.status == fcs_shipment_processed_status
? shipBtn ? shipBtn
: Container(), : Container(),
_fcsShipment?.status == fcs_shipment_shipped_status _fcsShipment.status == fcs_shipment_shipped_status
? arriveBtn ? arriveBtn
: Container(), : Container(),
_fcsShipment?.status == fcs_shipment_arrived_status _fcsShipment.status == fcs_shipment_arrived_status
? invoiceBtn ? invoiceBtn
: Container(), : Container(),
SizedBox(height: 20) SizedBox(height: 20)
@@ -244,8 +244,8 @@ class _FcsShipmentInfoState extends State<FcsShipmentInfo> {
); );
if (updated ?? false) { if (updated ?? false) {
var shipmentModel = Provider.of<FcsShipmentModel>(context, listen: false); var shipmentModel = Provider.of<FcsShipmentModel>(context, listen: false);
if (_fcsShipment != null && _fcsShipment!.id != null) { if (_fcsShipment.id != null) {
FcsShipment? f = await shipmentModel.getFcsShipment(_fcsShipment!.id!); FcsShipment? f = await shipmentModel.getFcsShipment(_fcsShipment.id!);
if (f == null) return; if (f == null) return;
setState(() { setState(() {
_fcsShipment = f; _fcsShipment = f;
@@ -294,7 +294,7 @@ class _FcsShipmentInfoState extends State<FcsShipmentInfo> {
_isLoading = true; _isLoading = true;
}); });
try { try {
await context.read<FcsShipmentModel>().process(_fcsShipment!.id!); await context.read<FcsShipmentModel>().process(_fcsShipment.id!);
Navigator.pop(context, true); Navigator.pop(context, true);
} catch (e) { } catch (e) {
showMsgDialog(context, "Error", e.toString()); showMsgDialog(context, "Error", e.toString());
@@ -316,7 +316,7 @@ class _FcsShipmentInfoState extends State<FcsShipmentInfo> {
_isLoading = true; _isLoading = true;
}); });
try { try {
await context.read<FcsShipmentModel>().ship(_fcsShipment!.id!); await context.read<FcsShipmentModel>().ship(_fcsShipment.id!);
Navigator.pop(context, true); Navigator.pop(context, true);
} catch (e) { } catch (e) {
showMsgDialog(context, "Error", e.toString()); showMsgDialog(context, "Error", e.toString());
@@ -338,7 +338,7 @@ class _FcsShipmentInfoState extends State<FcsShipmentInfo> {
_isLoading = true; _isLoading = true;
}); });
try { try {
await context.read<FcsShipmentModel>().arrive(_fcsShipment!.id!); await context.read<FcsShipmentModel>().arrive(_fcsShipment.id!);
Navigator.pop(context, true); Navigator.pop(context, true);
} catch (e) { } catch (e) {
showMsgDialog(context, "Error", e.toString()); showMsgDialog(context, "Error", e.toString());
@@ -360,7 +360,7 @@ class _FcsShipmentInfoState extends State<FcsShipmentInfo> {
_isLoading = true; _isLoading = true;
}); });
try { try {
await context.read<FcsShipmentModel>().invoice(_fcsShipment!.id!); await context.read<FcsShipmentModel>().invoice(_fcsShipment.id!);
Navigator.pop(context, true); Navigator.pop(context, true);
} catch (e) { } catch (e) {
showMsgDialog(context, "Error", e.toString()); showMsgDialog(context, "Error", e.toString());
@@ -376,7 +376,7 @@ class _FcsShipmentInfoState extends State<FcsShipmentInfo> {
_isLoading = true; _isLoading = true;
}); });
try { try {
await context.read<FcsShipmentModel>().cancel(_fcsShipment!.id!); await context.read<FcsShipmentModel>().cancel(_fcsShipment.id!);
Navigator.pop(context, true); Navigator.pop(context, true);
} catch (e) { } catch (e) {
showMsgDialog(context, "Error", e.toString()); showMsgDialog(context, "Error", e.toString());
@@ -402,11 +402,11 @@ class _FcsShipmentInfoState extends State<FcsShipmentInfo> {
} else if (id == 4) { } else if (id == 4) {
reportName = "manifest"; reportName = "manifest";
} }
_fcsShipment!.reportName = reportName; _fcsShipment.reportName = reportName;
FcsShipmentModel fcsShipmentModel = FcsShipmentModel fcsShipmentModel =
Provider.of<FcsShipmentModel>(context, listen: false); Provider.of<FcsShipmentModel>(context, listen: false);
String url = await fcsShipmentModel.report(_fcsShipment!); String url = await fcsShipmentModel.report(_fcsShipment);
Navigator.of(context).push(CupertinoPageRoute( Navigator.of(context).push(CupertinoPageRoute(
builder: (context) => PDFScreen( builder: (context) => PDFScreen(
title: "", title: "",

View File

@@ -51,13 +51,13 @@ class _FcsShipmentListState extends State<FcsShipmentList> {
_newShipment(); _newShipment();
}, },
icon: Icon(Icons.add), icon: Icon(Icons.add),
label: label: LocalText(context, "FCSshipment.add", color: Colors.white),
LocalText(context, "FCSshipment.add", color: Colors.white),
backgroundColor: primaryColor), backgroundColor: primaryColor),
body: PaginatorListView<FcsShipment>( body: PaginatorListView<FcsShipment>(
paginatorListener: shipmentModel.fcsShipments!, paginatorListener: shipmentModel.fcsShipments!,
rowBuilder: (p) => FcsShipmentListRow(shipment: p), rowBuilder: (p) => FcsShipmentListRow(shipment: p),
color: primaryColor))); color: primaryColor),
));
} }
_newShipment() { _newShipment() {
@@ -107,7 +107,9 @@ class _FcsShipmentListState extends State<FcsShipmentList> {
value: choice, value: choice,
child: Row( child: Row(
children: <Widget>[ children: <Widget>[
Flexible(child: Text("${choice.text}",style: TextStyle(color: Colors.black))), Flexible(
child: Text("${choice.text}",
style: TextStyle(color: Colors.black))),
const SizedBox( const SizedBox(
width: 10, width: 10,
), ),

View File

@@ -4,9 +4,11 @@ import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:fcs/data/services/services.dart'; import 'package:fcs/data/services/services.dart';
import 'package:fcs/constants.dart'; import 'package:fcs/constants.dart';
import 'package:fcs/domain/entities/fcs_shipment.dart'; import 'package:fcs/domain/entities/fcs_shipment.dart';
import 'package:fcs/domain/entities/shipment_port.dart';
import 'package:fcs/pages/main/model/base_model.dart'; import 'package:fcs/pages/main/model/base_model.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import '../../../domain/entities/shipment_consignee.dart';
import '../../../domain/entities/shipment_type.dart'; import '../../../domain/entities/shipment_type.dart';
import '../../../pagination/paginator_listener.dart'; import '../../../pagination/paginator_listener.dart';
@@ -17,7 +19,26 @@ class FcsShipmentModel extends BaseModel {
int selectedIndex = 0; int selectedIndex = 0;
List<ShipmentType> shipmentTypes = []; List<ShipmentType> shipmentTypes = [];
List<ShipmentConsignee> shipmentConsingees = [];
List<ShipmentPort> shipmentPorts = [];
StreamSubscription<QuerySnapshot>? _shipmentTypeListener; StreamSubscription<QuerySnapshot>? _shipmentTypeListener;
StreamSubscription<QuerySnapshot>? _shipmentConsigneeListener;
StreamSubscription<QuerySnapshot>? _shipmentPortListener;
ShipmentType? getShipmentType(id) {
var list = shipmentTypes.where((e) => e.id == id).toList();
return list.isNotEmpty ? list.first : null;
}
ShipmentConsignee? getShipmentConsignee(id) {
var list = shipmentConsingees.where((e) => e.id == id).toList();
return list.isNotEmpty ? list.first : null;
}
ShipmentPort? getShipmentPort(id) {
var list = shipmentPorts.where((e) => e.id == id).toList();
return list.isNotEmpty ? list.first : null;
}
onChanged(int index) { onChanged(int index) {
selectedIndex = index; selectedIndex = index;
@@ -39,7 +60,7 @@ class FcsShipmentModel extends BaseModel {
pageQuery.where("status", isEqualTo: fcs_shipment_pending_status); pageQuery.where("status", isEqualTo: fcs_shipment_pending_status);
} }
// processing status // processed status
if (index == 2) { if (index == 2) {
col = col.where("status", isEqualTo: fcs_shipment_processed_status); col = col.where("status", isEqualTo: fcs_shipment_processed_status);
pageQuery = pageQuery =
@@ -74,7 +95,7 @@ class FcsShipmentModel extends BaseModel {
pageQuery.where("status", isEqualTo: fcs_shipment_canceled_status); pageQuery.where("status", isEqualTo: fcs_shipment_canceled_status);
} }
pageQuery = pageQuery.orderBy("shipment_number", descending: true); pageQuery = pageQuery.orderBy("update_time", descending: true);
fcsShipments?.close(); fcsShipments?.close();
fcsShipments = PaginatorListener<FcsShipment>( fcsShipments = PaginatorListener<FcsShipment>(
@@ -87,7 +108,7 @@ class FcsShipmentModel extends BaseModel {
try { try {
var snaps = await FirebaseFirestore.instance var snaps = await FirebaseFirestore.instance
.collection("/$fcs_shipment_collection") .collection("/$fcs_shipment_collection")
// .where("status", isEqualTo: fcs_shipment_processed_status) .where("status", isEqualTo: fcs_shipment_processed_status)
.get(const GetOptions(source: Source.server)); .get(const GetOptions(source: Source.server));
fcsShipments = snaps.docs.map((documentSnapshot) { fcsShipments = snaps.docs.map((documentSnapshot) {
var fcs = var fcs =
@@ -136,14 +157,23 @@ class FcsShipmentModel extends BaseModel {
void initUser(user) { void initUser(user) {
super.initUser(user); super.initUser(user);
if (user != null && user.hasFcsShipments()) {
_loadShipmentTypes(); _loadShipmentTypes();
_loadShipmentConsignees();
_loadShipmentPorts();
}
} }
@override @override
logout() async { logout() async {
fcsShipments?.close(); fcsShipments?.close();
_shipmentTypeListener?.cancel(); _shipmentTypeListener?.cancel();
_shipmentConsigneeListener?.cancel();
_shipmentPortListener?.cancel();
shipmentTypes.clear(); shipmentTypes.clear();
shipmentConsingees.clear();
shipmentPorts.clear();
} }
Future<void> _loadShipmentTypes() async { Future<void> _loadShipmentTypes() async {
@@ -155,10 +185,52 @@ class FcsShipmentModel extends BaseModel {
.listen((QuerySnapshot snapshot) { .listen((QuerySnapshot snapshot) {
shipmentTypes.clear(); shipmentTypes.clear();
shipmentTypes = snapshot.docs.map((documentSnapshot) { shipmentTypes = snapshot.docs.map((documentSnapshot) {
var privilege = ShipmentType.fromMap( var type = ShipmentType.fromMap(
documentSnapshot.data() as Map<String, dynamic>, documentSnapshot.data() as Map<String, dynamic>,
documentSnapshot.id); documentSnapshot.id);
return privilege; return type;
}).toList();
notifyListeners();
});
} catch (e) {
log.warning("Error!! $e");
}
}
Future<void> _loadShipmentConsignees() async {
try {
_shipmentConsigneeListener = FirebaseFirestore.instance
.collection(
"/$config_collection/$setting_doc_id/$shipment_consignee_collection")
.snapshots()
.listen((QuerySnapshot snapshot) {
shipmentConsingees.clear();
shipmentConsingees = snapshot.docs.map((documentSnapshot) {
var consignee = ShipmentConsignee.fromMap(
documentSnapshot.data() as Map<String, dynamic>,
documentSnapshot.id);
return consignee;
}).toList();
notifyListeners();
});
} catch (e) {
log.warning("Error!! $e");
}
}
Future<void> _loadShipmentPorts() async {
try {
_shipmentPortListener = FirebaseFirestore.instance
.collection(
"/$config_collection/$setting_doc_id/$shipment_port_collection")
.snapshots()
.listen((QuerySnapshot snapshot) {
shipmentPorts.clear();
shipmentPorts = snapshot.docs.map((documentSnapshot) {
var port = ShipmentPort.fromMap(
documentSnapshot.data() as Map<String, dynamic>,
documentSnapshot.id);
return port;
}).toList(); }).toList();
notifyListeners(); notifyListeners();
}); });
@@ -192,7 +264,7 @@ class FcsShipmentModel extends BaseModel {
Future<void> invoice(String id) { Future<void> invoice(String id) {
return Services.instance.fcsShipmentService return Services.instance.fcsShipmentService
.updateFcsShipmentStatus(id, fcs_shipment_shipped_status); .updateFcsShipmentStatus(id, fcs_shipment_invoiced_status);
} }
Future<void> cancel(String id) { Future<void> cancel(String id) {

View File

@@ -203,9 +203,27 @@ class _HomePageState extends State<HomePage> {
super.dispose(); super.dispose();
} }
_logoutPinAccount() async {
setState(() {
_isLoading = true;
});
try {
await context.read<MainModel>().logoutPinAccount();
Navigator.pushNamedAndRemoveUntil(context, "/pin_login", (r) => false);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
User? user = Provider.of<MainModel>(context).user; var mainModel = context.watch<MainModel>();
User? user = mainModel.user;
if (user == null) { if (user == null) {
Future.microtask( Future.microtask(
@@ -213,7 +231,7 @@ class _HomePageState extends State<HomePage> {
return Container(); return Container();
} }
login = Provider.of<MainModel>(context).isLogin(); login = mainModel.isLogin();
LanguageModel languageModel = Provider.of<LanguageModel>(context); LanguageModel languageModel = Provider.of<LanguageModel>(context);
final faqBtn = TaskButton("faq.btn", final faqBtn = TaskButton("faq.btn",
@@ -357,7 +375,7 @@ class _HomePageState extends State<HomePage> {
selectedColor: Colors.white, selectedColor: Colors.white,
color: Colors.blue, color: Colors.blue,
children: <Widget>[ children: <Widget>[
Icon(MaterialCommunityIcons.account_tie,size: 25), Icon(MaterialCommunityIcons.account_tie, size: 25),
], ],
onPressed: (i) => this.setState(() { onPressed: (i) => this.setState(() {
isFcs[0] = !isFcs[0]; isFcs[0] = !isFcs[0];
@@ -390,8 +408,11 @@ class _HomePageState extends State<HomePage> {
final pinLoginBtn = IconButton( final pinLoginBtn = IconButton(
onPressed: () { onPressed: () {
Navigator.of(context) Navigator.pushAndRemoveUntil(
.push(CupertinoPageRoute(builder: (context) => PinLoginPage())); context,
CupertinoPageRoute(
builder: (BuildContext context) => PinLoginPage()),
(r) => false);
}, },
iconSize: 25, iconSize: 25,
icon: Icon( icon: Icon(
@@ -400,6 +421,19 @@ class _HomePageState extends State<HomePage> {
), ),
); );
final pinLogoutBtn = IconButton(
onPressed: () {
showConfirmDialog(context, "home.pin.logout.confirm", () async {
await _logoutPinAccount();
});
},
iconSize: 25,
icon: Icon(
MaterialCommunityIcons.lock_open_variant_outline,
color: buttonColor,
),
);
var searchInput = Row(children: [ var searchInput = Row(children: [
Expanded( Expanded(
child: Padding( child: Padding(
@@ -478,7 +512,7 @@ class _HomePageState extends State<HomePage> {
profileBtn, profileBtn,
] ]
: <Widget>[ : <Widget>[
pinLoginBtn, mainModel.isPinLogin ? pinLogoutBtn : pinLoginBtn,
fcsToggle, fcsToggle,
profileBtn, profileBtn,
] ]

View File

@@ -17,6 +17,7 @@ class MainModel extends ChangeNotifier {
String? messagingToken; String? messagingToken;
User? user; User? user;
User? _fbUser;
PackageInfo? packageInfo; PackageInfo? packageInfo;
set setMessaginToken(token) { set setMessaginToken(token) {
@@ -29,6 +30,9 @@ class MainModel extends ChangeNotifier {
bool isLoaded = false; bool isLoaded = false;
bool isOnline = false; bool isOnline = false;
bool isFirstLaunch = false; bool isFirstLaunch = false;
bool isLockOn = false;
bool get isPinLogin => user?.isPinLogin ?? false;
MainModel() { MainModel() {
NetworkConnectivity.instance.statusStream.listen((data) { NetworkConnectivity.instance.statusStream.listen((data) {
@@ -83,6 +87,7 @@ class MainModel extends ChangeNotifier {
await _listenSetting(); await _listenSetting();
this.isFirstLaunch = await SharedPref.isFirstLaunch() ?? true; this.isFirstLaunch = await SharedPref.isFirstLaunch() ?? true;
this.packageInfo = await PackageInfo.fromPlatform(); this.packageInfo = await PackageInfo.fromPlatform();
this.isLockOn = await SharedPref.getPinLockOn() ?? false;
userListener?.cancel(); userListener?.cancel();
userListener = userListener =
@@ -91,6 +96,17 @@ class MainModel extends ChangeNotifier {
bool diffPrivilege = bool diffPrivilege =
_user != null && (user == null || user!.diffPrivileges(_user)); _user != null && (user == null || user!.diffPrivileges(_user));
bool loggingOut = user != null && _user == null; bool loggingOut = user != null && _user == null;
if (_user != null) {
if (!_user.isPinLogin) {
_fbUser = _user;
}
}
if ((_fbUser?.id == _user?.id)) {
_user?.pinToken = null;
}
user = _user; user = _user;
if (_user != null) { if (_user != null) {
@@ -101,6 +117,7 @@ class MainModel extends ChangeNotifier {
} }
} }
} }
if (loggingOut) { if (loggingOut) {
for (final m in models) { for (final m in models) {
m.logout(); m.logout();
@@ -124,6 +141,7 @@ class MainModel extends ChangeNotifier {
try { try {
Services.instance.authService.getSetting().listen((event) { Services.instance.authService.getSetting().listen((event) {
this.setting = event; this.setting = event;
Services.setting = event;
models.forEach((m) => m.initSetting(setting)); models.forEach((m) => m.initSetting(setting));
notifyListeners(); notifyListeners();
}); });
@@ -196,4 +214,13 @@ class MainModel extends ChangeNotifier {
Future<void> deleteAccount() async { Future<void> deleteAccount() async {
return await Services.instance.authService.deleteAccount(); return await Services.instance.authService.deleteAccount();
} }
Future<void> pinLogin({required String fcsID, required String pin}) async {
await Services.instance.authService
.pinLogin(fcsID: fcsID, pin: pin, currentUserId: _fbUser?.id ?? '');
}
Future<void> logoutPinAccount() async {
await Services.instance.authService.logoutPinAccount();
}
} }

View File

@@ -53,7 +53,11 @@ class _SplashScreenState extends State<SplashScreen> {
if (mainModel.isFirstLaunch) { if (mainModel.isFirstLaunch) {
page = "/language_selection"; page = "/language_selection";
} else if (mainModel.isLogin()) { } else if (mainModel.isLogin()) {
if (mainModel.isLockOn) {
page = "/pin_login";
} else {
page = "/home"; page = "/home";
}
} else { } else {
page = "/welcome"; page = "/welcome";
} }

View File

@@ -224,6 +224,9 @@ class PackageModel extends BaseModel {
} }
Future<List<Package>> searchPackage(String term) async { Future<List<Package>> searchPackage(String term) async {
if (term != '') {
await SharedPref.saveRecentSearch('package_search', term);
}
Future<List<Package>> packages = Future<List<Package>> packages =
Services.instance.packageService.searchPackage(term); Services.instance.packageService.searchPackage(term);

View File

@@ -133,7 +133,7 @@ class _PackageInfoState extends State<PackageInfo> {
if (d == null) return; if (d == null) return;
_changeDeliverayAddress(d); _changeDeliverayAddress(d);
} }
: () {}, : null,
); );
return LocalProgress( return LocalProgress(

View File

@@ -8,6 +8,9 @@ import 'package:flutter/material.dart';
import 'package:flutter_vector_icons/flutter_vector_icons.dart'; import 'package:flutter_vector_icons/flutter_vector_icons.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../../helpers/shared_pref.dart';
import '../widgets/suggest_list.dart';
Future<Package?> searchPackage(BuildContext context, Future<Package?> searchPackage(BuildContext context,
{CallbackPackageSelect? callbackPackageSelect}) async => {CallbackPackageSelect? callbackPackageSelect}) async =>
await showSearch<Package>( await showSearch<Package>(
@@ -22,7 +25,7 @@ class PackageSearchDelegate extends SearchDelegate<Package> {
PackageSearchDelegate({this.callbackPackageSelect}); PackageSearchDelegate({this.callbackPackageSelect});
@override @override
String get searchFieldLabel => 'Search by Tracking ID/Customer Name'; String get searchFieldLabel => 'Search by Tracking ID/Customer name';
@override @override
ThemeData appBarTheme(BuildContext context) { ThemeData appBarTheme(BuildContext context) {
@@ -31,7 +34,7 @@ class PackageSearchDelegate extends SearchDelegate<Package> {
appBarTheme: AppBarTheme(color: primaryColor), appBarTheme: AppBarTheme(color: primaryColor),
iconButtonTheme: IconButtonThemeData( iconButtonTheme: IconButtonThemeData(
style: ButtonStyle( style: ButtonStyle(
iconColor: MaterialStateProperty.all<Color>(Colors.white))), iconColor: WidgetStateProperty.all<Color>(Colors.white))),
inputDecorationTheme: InputDecorationTheme( inputDecorationTheme: InputDecorationTheme(
border: InputBorder.none, border: InputBorder.none,
hintStyle: TextStyle(color: Colors.grey, fontSize: 14)), hintStyle: TextStyle(color: Colors.grey, fontSize: 14)),
@@ -123,14 +126,29 @@ class PackageSearchDelegate extends SearchDelegate<Package> {
@override @override
Widget buildSuggestions(BuildContext context) { Widget buildSuggestions(BuildContext context) {
return Container( return FutureBuilder<List<String>?>(
child: Center( future: SharedPref.getRecentSearch('package_search', query),
builder: (context, snapshot) {
List<String> _oldFilters = snapshot.data ?? [];
if (_oldFilters.isEmpty) {
return const Center(
child: Opacity( child: Opacity(
opacity: 0.2, opacity: 0.2,
child: Icon(Octicons.package, size: 200, color: primaryColor)), child: Icon(Octicons.package,
color: primaryColor, size: 200),
), ),
); );
} }
return SuggestList(
recentSearchList: _oldFilters,
onTap: (String s) {
query = s;
showResults(context);
},
prefKey: 'package_search');
},
);
}
_scan(BuildContext context) async { _scan(BuildContext context) async {
// PermissionStatus permission = // PermissionStatus permission =

View File

@@ -11,6 +11,8 @@ import 'package:fcs/pagination/paginator_listener.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:path/path.dart' as Path; import 'package:path/path.dart' as Path;
import '../../../helpers/shared_pref.dart';
class PickupModel extends BaseModel { class PickupModel extends BaseModel {
final log = Logger('PickupModel'); final log = Logger('PickupModel');
PaginatorListener<Pickup>? pickups; PaginatorListener<Pickup>? pickups;
@@ -86,6 +88,9 @@ class PickupModel extends BaseModel {
} }
Future<List<Pickup>> searchPickup(String term) async { Future<List<Pickup>> searchPickup(String term) async {
if (term != '') {
await SharedPref.saveRecentSearch('pickup_search', term);
}
Future<List<Pickup>> pickups = Future<List<Pickup>> pickups =
Services.instance.pickupService.searchPickup(term); Services.instance.pickupService.searchPickup(term);
return pickups; return pickups;

View File

@@ -9,6 +9,9 @@ import 'package:flutter/material.dart';
import 'package:flutter_vector_icons/flutter_vector_icons.dart'; import 'package:flutter_vector_icons/flutter_vector_icons.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../../helpers/shared_pref.dart';
import '../widgets/suggest_list.dart';
Future<Pickup?> searchPickup(BuildContext context, Future<Pickup?> searchPickup(BuildContext context,
{CallbackPickupSelect? callbackPickupSelect}) async => {CallbackPickupSelect? callbackPickupSelect}) async =>
await showSearch<Pickup>( await showSearch<Pickup>(
@@ -32,7 +35,7 @@ class PackageSearchDelegate extends SearchDelegate<Pickup> {
appBarTheme: AppBarTheme(color: primaryColor), appBarTheme: AppBarTheme(color: primaryColor),
iconButtonTheme: IconButtonThemeData( iconButtonTheme: IconButtonThemeData(
style: ButtonStyle( style: ButtonStyle(
iconColor: MaterialStateProperty.all<Color>(Colors.white))), iconColor: WidgetStateProperty.all<Color>(Colors.white))),
inputDecorationTheme: InputDecorationTheme( inputDecorationTheme: InputDecorationTheme(
border: InputBorder.none, border: InputBorder.none,
hintStyle: TextStyle(color: Colors.grey, fontSize: 14)), hintStyle: TextStyle(color: Colors.grey, fontSize: 14)),
@@ -124,15 +127,29 @@ class PackageSearchDelegate extends SearchDelegate<Pickup> {
@override @override
Widget buildSuggestions(BuildContext context) { Widget buildSuggestions(BuildContext context) {
return Container( return FutureBuilder<List<String>?>(
child: Center( future: SharedPref.getRecentSearch('pickup_search', query),
builder: (context, snapshot) {
List<String> _oldFilters = snapshot.data ?? [];
if (_oldFilters.isEmpty) {
return const Center(
child: Opacity( child: Opacity(
opacity: 0.2, opacity: 0.2,
child: Icon(SimpleLineIcons.direction, child: Icon(SimpleLineIcons.direction,
size: 200, color: primaryColor)), color: primaryColor, size: 200),
), ),
); );
} }
return SuggestList(
recentSearchList: _oldFilters,
onTap: (String s) {
query = s;
showResults(context);
},
prefKey: 'pickup_search');
},
);
}
_scan(BuildContext context) async { _scan(BuildContext context) async {
// PermissionStatus permission = // PermissionStatus permission =

View File

@@ -129,12 +129,17 @@ class _ProfileState extends State<Profile> {
], ],
); );
final logoutbutton = fcsButton( final logoutbutton =
context, getLocalString(context, "profile.logout"), callack: () { fcsButton(context, getLocalString(context, "profile.logout"),
showConfirmDialog(context, "profile.logout.confirm", () async { callack: mainModel.isPinLogin
? null
: () {
showConfirmDialog(context, "profile.logout.confirm",
() async {
await _logout(); await _logout();
}); });
}, iconData: Icons.exit_to_app); },
iconData: Icons.exit_to_app);
return LocalProgress( return LocalProgress(
inAsyncCall: _isLoading, inAsyncCall: _isLoading,
@@ -355,8 +360,12 @@ class _ProfileState extends State<Profile> {
} }
_showToast(String title) { _showToast(String title) {
final ScaffoldMessengerState scaffold = ScaffoldMessengerState? scaffold = key.currentState;
key.currentState as ScaffoldMessengerState;
if (scaffold == null) {
scaffold = ScaffoldMessenger.of(context);
}
scaffold.showSnackBar( scaffold.showSnackBar(
SnackBar( SnackBar(
content: Text('copied "$title" data to clipboard'), content: Text('copied "$title" data to clipboard'),

View File

@@ -4,9 +4,11 @@ import 'package:fcs/pages/main/util.dart';
import 'package:fcs/pages/widgets/input_text.dart'; import 'package:fcs/pages/widgets/input_text.dart';
import 'package:fcs/pages/widgets/local_app_bar.dart'; import 'package:fcs/pages/widgets/local_app_bar.dart';
import 'package:fcs/pages/widgets/progress.dart'; import 'package:fcs/pages/widgets/progress.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../widgets/local_text.dart';
import 'model/shipment_rate_model.dart'; import 'model/shipment_rate_model.dart';
class CargoEditor extends StatefulWidget { class CargoEditor extends StatefulWidget {
@@ -20,11 +22,13 @@ class CargoEditor extends StatefulWidget {
class _CargoEditorState extends State<CargoEditor> { class _CargoEditorState extends State<CargoEditor> {
TextEditingController _descController = new TextEditingController(); TextEditingController _descController = new TextEditingController();
TextEditingController _rateController = new TextEditingController(); TextEditingController _rateController = new TextEditingController();
TextEditingController _displayIndexController = new TextEditingController();
bool _isLoading = false; bool _isLoading = false;
late CargoType _cargo; late CargoType _cargo;
bool _isNew = false; bool _isNew = false;
final _cargoFormKey = GlobalKey<FormState>(); final _cargoFormKey = GlobalKey<FormState>();
bool _isDefault = false;
@override @override
void initState() { void initState() {
@@ -33,6 +37,8 @@ class _CargoEditorState extends State<CargoEditor> {
_cargo = widget.cargo!; _cargo = widget.cargo!;
_descController.text = _cargo.name ?? ""; _descController.text = _cargo.name ?? "";
_rateController.text = _cargo.rate.toStringAsFixed(2); _rateController.text = _cargo.rate.toStringAsFixed(2);
_displayIndexController.text = _cargo.displayIndex.toString();
_isDefault = _cargo.isDefault;
} else { } else {
_isNew = true; _isNew = true;
} }
@@ -50,16 +56,19 @@ class _CargoEditorState extends State<CargoEditor> {
iconData: Icons.text_format, iconData: Icons.text_format,
controller: _descController, controller: _descController,
autovalidateMode: AutovalidateMode.onUserInteraction, autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (value){ validator: (value) {
if(value==null || value.isEmpty){ if (value == null || value.isEmpty) {
return "Please insert cargo type"; return "Please insert cargo type";
} }
return null; return null;
},); },
);
final rateBox = InputText( final rateBox = InputText(
labelTextKey: 'cargo.rate', labelTextKey: 'cargo.rate',
iconData: Icons.attach_money, iconData: Icons.attach_money,
controller: _rateController, controller: _rateController,
textInputType: TextInputType.number,
autovalidateMode: AutovalidateMode.onUserInteraction, autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (value) { validator: (value) {
if (value == null || value.isEmpty) { if (value == null || value.isEmpty) {
@@ -68,6 +77,51 @@ class _CargoEditorState extends State<CargoEditor> {
return null; return null;
}, },
); );
final displayIndexBox = InputText(
labelTextKey: 'cargo.display_index',
iconData: Icons.numbers,
controller: _displayIndexController,
textInputType: TextInputType.number,
autovalidateMode: AutovalidateMode.onUserInteraction,
// validator: (value) {
// if (value == null || value.isEmpty) {
// return "Please insert display index";
// }
// return null;
// },
);
final defaultBox = Padding(
padding: const EdgeInsets.symmetric(vertical: 20),
child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
Flexible(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocalText(
context,
'cargo.is_default',
color: Colors.black54,
fontSize: 15,
),
],
),
),
Transform.scale(
scale: 0.7,
alignment: Alignment.centerRight,
child: CupertinoSwitch(
activeColor: primaryColor,
value: _isDefault,
onChanged: (v) {
setState(() {
_isDefault = v;
});
}))
]),
);
return LocalProgress( return LocalProgress(
inAsyncCall: _isLoading, inAsyncCall: _isLoading,
child: Scaffold( child: Scaffold(
@@ -105,6 +159,8 @@ class _CargoEditorState extends State<CargoEditor> {
children: <Widget>[ children: <Widget>[
typeBox, typeBox,
rateBox, rateBox,
displayIndexBox,
defaultBox,
SizedBox(height: 30), SizedBox(height: 30),
], ],
), ),
@@ -131,7 +187,12 @@ class _CargoEditorState extends State<CargoEditor> {
var shipmentRateModel = var shipmentRateModel =
Provider.of<ShipmentRateModel>(context, listen: false); Provider.of<ShipmentRateModel>(context, listen: false);
CargoType _cargo = CargoType( CargoType _cargo = CargoType(
name: _descController.text, rate: double.parse(_rateController.text)); name: _descController.text,
rate: double.parse(_rateController.text),
displayIndex: _displayIndexController.text == ''
? 0
: int.parse(_displayIndexController.text),
isDefault: _isDefault);
if (_isNew) { if (_isNew) {
await shipmentRateModel.addCargoType(_cargo); await shipmentRateModel.addCargoType(_cargo);
} else { } else {

View File

@@ -1,13 +1,18 @@
import 'package:fcs/helpers/theme.dart'; import 'package:fcs/helpers/theme.dart';
import 'package:fcs/pages/main/model/language_model.dart';
import 'package:fcs/pages/main/model/main_model.dart';
import 'package:fcs/pages/main/util.dart'; import 'package:fcs/pages/main/util.dart';
import 'package:fcs/pages/widgets/local_button.dart'; import 'package:fcs/pages/widgets/local_button.dart';
import 'package:fcs/pages/widgets/local_text.dart'; import 'package:fcs/pages/widgets/local_text.dart';
import 'package:fcs/pages/widgets/progress.dart'; import 'package:fcs/pages/widgets/progress.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:pin_input_text_field/pin_input_text_field.dart'; import 'package:pin_input_text_field/pin_input_text_field.dart';
import 'package:provider/provider.dart';
import '../../helpers/shared_pref.dart';
import '../../localization/app_translations.dart';
class PinLoginPage extends StatefulWidget { class PinLoginPage extends StatefulWidget {
//final User user;
const PinLoginPage({super.key}); const PinLoginPage({super.key});
@override @override
@@ -18,10 +23,77 @@ class _PinLoginPageState extends State<PinLoginPage> {
bool _isLoading = false; bool _isLoading = false;
String pin = ""; String pin = "";
String prefixText = "FCS-"; String prefixText = "FCS-";
//late User _user;
final _formKey = GlobalKey<FormState>(); final _formKey = GlobalKey<FormState>();
TextEditingController _fcsIdCtl = new TextEditingController(); TextEditingController _fcsIdCtl = new TextEditingController();
@override
void initState() {
_init();
super.initState();
}
_init() async {
await SharedPref.setPinLockOn(true);
}
Future<bool> _onWillPop() async {
// Show the confirmation dialog
return (await showDialog(
context: context,
builder: (context) => AlertDialog(
title: Center(
child:
LocalText(context, 'btn.exit_confirm', color: primaryColor),
),
content: Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
TextButton(
style: TextButton.styleFrom(
backgroundColor: Colors.grey[300],
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0))),
child: Text(
AppTranslations.of(context)!.text('btn.cancel'),
style: Provider.of<LanguageModel>(context).isEng
? TextStyle(color: primaryColor)
: TextStyle(
fontFamily: 'Myanmar3', color: primaryColor),
),
onPressed: () {
Navigator.of(context).pop(false);
}),
SizedBox(
width: 0,
),
TextButton(
style: TextButton.styleFrom(
backgroundColor: primaryColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0))),
child: Text(AppTranslations.of(context)!.text('btn.ok'),
style: Provider.of<LanguageModel>(context).isEng
? TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold)
: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontFamily: 'Myanmar3')),
onPressed: () {
Navigator.of(context).pop(true);
})
],
),
),
),
)) ??
false;
}
Widget build(BuildContext context) { Widget build(BuildContext context) {
final fcsIdBox = TextFormField( final fcsIdBox = TextFormField(
controller: _fcsIdCtl, controller: _fcsIdCtl,
@@ -121,7 +193,11 @@ class _PinLoginPageState extends State<PinLoginPage> {
callBack: _login, callBack: _login,
), ),
); );
return LocalProgress(
// ignore: deprecated_member_use
return WillPopScope(
onWillPop: _onWillPop,
child: LocalProgress(
inAsyncCall: _isLoading, inAsyncCall: _isLoading,
child: new Scaffold( child: new Scaffold(
body: Form( body: Form(
@@ -157,6 +233,7 @@ class _PinLoginPageState extends State<PinLoginPage> {
], ],
), ),
)), )),
),
); );
} }
@@ -171,14 +248,14 @@ class _PinLoginPageState extends State<PinLoginPage> {
return; return;
} }
String fcsId = "$prefixText${_fcsIdCtl.text}"; String fcsId = "$prefixText${_fcsIdCtl.text}";
print(fcsId);
setState(() { setState(() {
_isLoading = true; _isLoading = true;
}); });
try { try {
Navigator.pop(context, true); await context.read<MainModel>().pinLogin(fcsID: fcsId, pin: pin);
Navigator.pushNamedAndRemoveUntil(context, "/home", (r) => false);
} catch (e) { } catch (e) {
showMsgDialog(context, "Error", e.toString()); showMsgDialog(context, "Error", e.toString());
} finally { } finally {

View File

@@ -81,7 +81,7 @@ class StaffModel extends BaseModel {
Future<void> updatePin( Future<void> updatePin(
{required String userID, {required String userID,
required bool enablePin, required bool enablePin,
required int? pin}) async { required String? pin}) async {
await request("/employee/pin", "PUT", await request("/employee/pin", "PUT",
payload: {"id": userID, "enable_pin_login": enablePin, "pin": pin}, payload: {"id": userID, "enable_pin_login": enablePin, "pin": pin},
token: await getToken()); token: await getToken());

View File

@@ -310,7 +310,7 @@ class _StaffPinEditorState extends State<StaffPinEditor> {
await context.read<StaffModel>().updatePin( await context.read<StaffModel>().updatePin(
userID: _staff.id!, userID: _staff.id!,
enablePin: _enablePinLogin, enablePin: _enablePinLogin,
pin: _enablePinLogin ? int.parse(_confirmPin) : null); pin: _enablePinLogin ? _confirmPin : null);
Navigator.pop(context, true); Navigator.pop(context, true);
} catch (e) { } catch (e) {
showMsgDialog(context, "Error", e.toString()); showMsgDialog(context, "Error", e.toString());

View File

@@ -37,7 +37,7 @@ class UserSearchDelegate extends SearchDelegate<User> {
appBarTheme: AppBarTheme(color: primaryColor), appBarTheme: AppBarTheme(color: primaryColor),
iconButtonTheme: IconButtonThemeData( iconButtonTheme: IconButtonThemeData(
style: ButtonStyle( style: ButtonStyle(
iconColor: MaterialStateProperty.all<Color>(Colors.white))), iconColor: WidgetStateProperty.all<Color>(Colors.white))),
inputDecorationTheme: InputDecorationTheme( inputDecorationTheme: InputDecorationTheme(
border: InputBorder.none, border: InputBorder.none,
hintStyle: TextStyle(color: Colors.grey, fontSize: 14)), hintStyle: TextStyle(color: Colors.grey, fontSize: 14)),

View File

@@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
import '../helpers/theme.dart'; import '../helpers/theme.dart';
import 'paginator_listener.dart'; import 'paginator_listener.dart';
@@ -99,7 +100,23 @@ class _PaginatorListViewState<T> extends State<PaginatorListView<T>> {
); );
} }
T t = _paginatorListener.data[index]; T t = _paginatorListener.data[index];
return widget.rowBuilder(t); return Slidable(
enabled: widget.onRemove != null ? true : false,
endActionPane: ActionPane(
extentRatio: 0.25,
motion: const ScrollMotion(),
children: widget.onRemove != null
? [
SlidableAction(
onPressed: ((context) => widget.onRemove!(t)),
backgroundColor: Colors.red,
icon: Icons.delete,
label: 'Delete',
),
]
: [],
),
child: widget.rowBuilder(t));
}), }),
), ),
), ),

View File

@@ -58,6 +58,7 @@ dependencies:
pdf: ^3.10.8 pdf: ^3.10.8
qr_flutter: ^4.1.0 qr_flutter: ^4.1.0
flutter_markdown: ^0.6.20+1 flutter_markdown: ^0.6.20+1
flutter_slidable: ^3.1.1
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: