add packages
This commit is contained in:
@@ -105,7 +105,15 @@
|
||||
"package.create.packages":"Complete receiving",
|
||||
"package.create.market":"Market",
|
||||
|
||||
"package.edit.title":"PACKAGE",
|
||||
"package.edit.status":"Status",
|
||||
"package.edit.title":"Edit Package",
|
||||
"package.edit.remark":"Remark",
|
||||
"package.edit.desc":"Description",
|
||||
"package.edit.complete.process.btn":"Complete processing",
|
||||
"package.edit.procseeing":"Processing",
|
||||
|
||||
"package.info.title":"Package",
|
||||
|
||||
"package.arrival.date":"Arrival Date",
|
||||
"package.number":"Box Number",
|
||||
"package.rate":"Rate",
|
||||
|
||||
@@ -104,10 +104,17 @@
|
||||
"package.create.phone":"ဖုန်းနံပါတ်",
|
||||
"package.tracking.id":"Tracking ID",
|
||||
"package.create.packages":"အထုပ် အသစ်များ လက်ခံမည်",
|
||||
"package.create.market":"Market",
|
||||
"package.create.market":"အွန်လိုင်စျေးဆိုင်",
|
||||
|
||||
"package.edit.title":"အထုပ် ပြင်ဆင်ခြင်း",
|
||||
"package.edit.remark":"မှတ်ချက်",
|
||||
"package.edit.desc":"ဖော်ပြချက်",
|
||||
"package.edit.complete.process.btn":"မွမ်းမံခြင်း ပြီးဆုံးသည်",
|
||||
"package.edit.status":"အခြေအနေ",
|
||||
"package.edit.procseeing":"မွမ်းမံခြင်း",
|
||||
|
||||
"package.info.title":"အထုပ်",
|
||||
|
||||
"package.new":"New Package",
|
||||
"package.edit.title":"PACKAGE",
|
||||
"package.arrival.date":"Arrival Date",
|
||||
"package.number":"Package Number",
|
||||
"package.rate":"Rate",
|
||||
|
||||
@@ -126,20 +126,23 @@ class AuthFb {
|
||||
log.info("Claims:${idToken.claims}");
|
||||
|
||||
User user = User();
|
||||
user.id = firebaseUser.uid;
|
||||
user.status = idToken.claims["status"];
|
||||
user.status = idToken.claims["st"];
|
||||
user.phoneNumber = firebaseUser.phoneNumber;
|
||||
|
||||
// add privileges
|
||||
String privileges = idToken.claims["privileges"];
|
||||
String privileges = idToken.claims["pr"];
|
||||
if (privileges != null && privileges != "") {
|
||||
user.privileges = privileges.split(":").toList();
|
||||
}
|
||||
User _user = await getUserFromFirestore(user.id);
|
||||
String cid = idToken.claims["cid"];
|
||||
if (cid != null && cid != "") {
|
||||
User _user = await getUserFromFirestore(cid);
|
||||
if (_user != null) {
|
||||
user.id = cid;
|
||||
user.fcsID = _user.fcsID;
|
||||
user.name = _user.name;
|
||||
}
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
@@ -170,6 +173,16 @@ class AuthFb {
|
||||
return getUser(refreshIdToken: true);
|
||||
}
|
||||
|
||||
Future<User> joinInvite(String userName) async {
|
||||
await requestAPI("/join_invite", "POST",
|
||||
payload: {
|
||||
'user_name': userName,
|
||||
},
|
||||
token: await getToken());
|
||||
// refresh token once signup
|
||||
return getUser(refreshIdToken: true);
|
||||
}
|
||||
|
||||
Future<bool> hasInvite() async {
|
||||
var invited =
|
||||
await requestAPI("/check_invitation", "GET", token: await getToken());
|
||||
@@ -210,4 +223,17 @@ class AuthFb {
|
||||
yield setting;
|
||||
}
|
||||
}
|
||||
|
||||
Stream<User> user(String userID) async* {
|
||||
Stream<DocumentSnapshot> snapshot = Firestore.instance
|
||||
.collection(user_collection)
|
||||
.document(userID)
|
||||
.snapshots();
|
||||
|
||||
await for (var snap in snapshot) {
|
||||
User user = User.fromMap(snap.data, snap.documentID);
|
||||
user = await getUser(refreshIdToken: true);
|
||||
yield user;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ class UserDataProvider {
|
||||
}
|
||||
|
||||
Future<void> acceptRequest(String userID) async {
|
||||
return await requestAPI("/invites", "PUT",
|
||||
return await requestAPI("/accept_request", "PUT",
|
||||
payload: {"id": userID}, token: await getToken());
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,11 @@ const privilege_collection = "privileges";
|
||||
const markets_collection = "markets";
|
||||
const packages_collection = "packages";
|
||||
|
||||
const user_requested_status = "requested";
|
||||
const user_invited_status = "invited";
|
||||
|
||||
const pkg_files_path = "/packages";
|
||||
|
||||
const ok_doc_id = "ok";
|
||||
|
||||
const biz_collection = "bizs";
|
||||
|
||||
@@ -10,8 +10,9 @@ class Package {
|
||||
String phoneNumber;
|
||||
String currentStatus;
|
||||
DateTime currentStatusDate;
|
||||
List<Map<String, dynamic>> allStatus;
|
||||
List<String> photoUrls;
|
||||
List<ShipmentStatus> shipmentHistory;
|
||||
String desc;
|
||||
|
||||
String status;
|
||||
String shipmentNumber;
|
||||
@@ -39,8 +40,6 @@ class Package {
|
||||
shipmentNumber + "-" + receiverNumber + " #" + boxNumber;
|
||||
double get price => rate.toDouble() * weight;
|
||||
|
||||
List<ShipmentStatus> shipmentHistory;
|
||||
|
||||
Package({
|
||||
this.id,
|
||||
this.trackingID,
|
||||
@@ -66,20 +65,20 @@ class Package {
|
||||
this.cargoDesc,
|
||||
this.market,
|
||||
this.shipmentHistory,
|
||||
this.allStatus,
|
||||
this.currentStatus,
|
||||
this.currentStatusDate,
|
||||
this.photoUrls,
|
||||
this.desc,
|
||||
});
|
||||
|
||||
factory Package.fromMap(Map<String, dynamic> map, String docID) {
|
||||
var _currentStatusDate = (map['current_status_date'] as Timestamp);
|
||||
|
||||
List<Map<String, dynamic>> _allStatus = List.from(map['all_status'])
|
||||
.map((e) => Map<String, dynamic>.from(e))
|
||||
List<ShipmentStatus> _shipmentStatus = List.from(map['all_status'])
|
||||
.map((e) => ShipmentStatus.fromMap(Map<String, dynamic>.from(e)))
|
||||
.toList();
|
||||
List<String> _photoUrls =
|
||||
map['photo_urls'] == null ? [] : map['photo_urls'].cast<List<String>>();
|
||||
map['photo_urls'] == null ? [] : List.from(map['photo_urls']);
|
||||
|
||||
return Package(
|
||||
id: docID,
|
||||
@@ -89,13 +88,22 @@ class Package {
|
||||
market: map['market'],
|
||||
userName: map['user_name'],
|
||||
phoneNumber: map['phone_number'],
|
||||
remark: map['remark'],
|
||||
desc: map['desc'],
|
||||
currentStatus: map['current_status'],
|
||||
currentStatusDate:
|
||||
_currentStatusDate != null ? _currentStatusDate.toDate() : null,
|
||||
photoUrls: _photoUrls,
|
||||
allStatus: _allStatus);
|
||||
shipmentHistory: _shipmentStatus);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() =>
|
||||
{'id': id, 'tracking_id': trackingID, 'market': market, 'fcs_id': fcsID};
|
||||
Map<String, dynamic> toJson() => {
|
||||
'id': id,
|
||||
'tracking_id': trackingID,
|
||||
'market': market,
|
||||
'fcs_id': fcsID,
|
||||
"desc": desc,
|
||||
"remark": remark,
|
||||
"photo_urls": photoUrls
|
||||
};
|
||||
}
|
||||
|
||||
@@ -94,6 +94,7 @@ class User {
|
||||
bool hasPackages() {
|
||||
return hasSysAdmin() ||
|
||||
hasAdmin() ||
|
||||
status == userStatusJoined ||
|
||||
(privileges != null ? privileges.contains('p') : false);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
|
||||
class ShipmentStatus {
|
||||
String status;
|
||||
DateTime date;
|
||||
bool done;
|
||||
ShipmentStatus({this.status, this.date, this.done});
|
||||
|
||||
factory ShipmentStatus.fromMap(Map<String, dynamic> map) {
|
||||
var _date = (map['date'] as Timestamp);
|
||||
return ShipmentStatus(
|
||||
status: map['status'],
|
||||
date: _date == null ? null : _date.toDate(),
|
||||
done: map['done'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
import 'package:firebase_storage/firebase_storage.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
final log = Logger('firebaseHelper');
|
||||
|
||||
@@ -10,3 +14,19 @@ Future<String> getToken() async {
|
||||
IdTokenResult token = await firebaseUser.getIdToken();
|
||||
return token.token;
|
||||
}
|
||||
|
||||
Future<String> uploadStorage(String path, File file, {String fileName}) async {
|
||||
if (fileName == null) {
|
||||
fileName = Uuid().v4();
|
||||
}
|
||||
StorageReference storageReference =
|
||||
FirebaseStorage.instance.ref().child('$path/$fileName');
|
||||
StorageUploadTask uploadTask = storageReference.putFile(file);
|
||||
await uploadTask.onComplete;
|
||||
String downloadUrl = await storageReference.getDownloadURL();
|
||||
print("name:${await storageReference.getName()}");
|
||||
print("bucket:${await storageReference.getBucket()}");
|
||||
print("path:${await storageReference.getPath()}");
|
||||
print("meta:${await storageReference.getMetadata()}");
|
||||
return downloadUrl;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:fcs/fcs/common/domain/constants.dart';
|
||||
import 'package:fcs/fcs/common/domain/entities/user.dart';
|
||||
import 'package:fcs/fcs/common/helpers/theme.dart';
|
||||
import 'package:fcs/fcs/common/pages/customers/customer_editor.dart';
|
||||
@@ -12,6 +13,7 @@ import 'package:flutter_icons/flutter_icons.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import 'invitation_create.dart';
|
||||
import 'invitation_editor.dart';
|
||||
|
||||
class CustomerList extends StatefulWidget {
|
||||
@@ -55,25 +57,17 @@ class _CustomerListState extends State<CustomerList> {
|
||||
fontSize: 20,
|
||||
),
|
||||
),
|
||||
floatingActionButton: FloatingActionButton.extended(
|
||||
onPressed: () {
|
||||
Navigator.of(context).push(BottomUpPageRoute(InvitationCreate()));
|
||||
},
|
||||
icon: Icon(Icons.add),
|
||||
label: LocalText(context, "invitation.new", color: Colors.white),
|
||||
backgroundColor: primaryColor,
|
||||
),
|
||||
body: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 3, right: 5),
|
||||
child: InkWell(
|
||||
onTap: _invitations,
|
||||
child: Container(
|
||||
color: primaryColor,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(5.0),
|
||||
child: Text(
|
||||
"Invitations",
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: ListView.separated(
|
||||
separatorBuilder: (context, index) => Divider(
|
||||
@@ -152,7 +146,9 @@ class _CustomerListState extends State<CustomerList> {
|
||||
|
||||
Widget _status(String status) {
|
||||
return Text(
|
||||
status == "requested" ? status : "",
|
||||
(user_requested_status == status || user_invited_status == status)
|
||||
? status
|
||||
: "",
|
||||
style: TextStyle(color: primaryColor, fontSize: 14),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'dart:async';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:fcs/fcs/common/domain/entities/auth_result.dart';
|
||||
import 'package:fcs/fcs/common/domain/entities/auth_status.dart';
|
||||
@@ -34,8 +35,10 @@ class MainModel extends ChangeNotifier {
|
||||
this.isOnline = _isOnline;
|
||||
notifyListeners();
|
||||
});
|
||||
Services.instance.authService.onAuthStatus().listen((event) {
|
||||
Services.instance.authService.onAuthStatus().listen((event) async {
|
||||
this.user = event;
|
||||
this.user =
|
||||
await Services.instance.authService.getUser(refreshIdToken: true);
|
||||
_initUser(user);
|
||||
notifyListeners();
|
||||
});
|
||||
@@ -74,7 +77,7 @@ class MainModel extends ChangeNotifier {
|
||||
this.isFirstLaunch = await SharedPref.isFirstLaunch();
|
||||
this.isFirstLaunch = this.isFirstLaunch ?? true;
|
||||
|
||||
_loadUser();
|
||||
// _loadUser();
|
||||
this.packageInfo = await PackageInfo.fromPlatform();
|
||||
}
|
||||
|
||||
@@ -82,9 +85,25 @@ class MainModel extends ChangeNotifier {
|
||||
models.add(model);
|
||||
}
|
||||
|
||||
void _initUser(User user) {
|
||||
models.forEach((m) => m.initUser(user));
|
||||
StreamSubscription<User> userListener;
|
||||
|
||||
void _initUser(User user) {
|
||||
if (user != null) {
|
||||
if (user.id != null && user.id != "") {
|
||||
if (userListener != null) userListener.cancel();
|
||||
userListener = Services.instance.authService
|
||||
.getUserStream(user.id)
|
||||
.listen((event) {
|
||||
this.user = event;
|
||||
models.forEach((m) => m.initUser(user));
|
||||
notifyListeners();
|
||||
});
|
||||
}
|
||||
models.forEach((m) => m.initUser(user));
|
||||
} else {
|
||||
models.forEach((m) => m.logout());
|
||||
}
|
||||
isLoaded = true;
|
||||
// if (firebaseMessaging != null) {
|
||||
// firebaseMessaging.subscribeToTopic(user.docID);
|
||||
// }
|
||||
@@ -104,15 +123,15 @@ class MainModel extends ChangeNotifier {
|
||||
} finally {}
|
||||
}
|
||||
|
||||
void _loadUser() async {
|
||||
try {
|
||||
this.user = await Services.instance.authService.getUser();
|
||||
_initUser(user);
|
||||
} finally {
|
||||
this.isLoaded = true;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
// void _loadUser() async {
|
||||
// try {
|
||||
// this.user = await Services.instance.authService.getUser();
|
||||
// _initUser(user);
|
||||
// } finally {
|
||||
// this.isLoaded = true;
|
||||
// notifyListeners();
|
||||
// }
|
||||
// }
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
@@ -162,6 +181,13 @@ class MainModel extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> joinInvite(String userName) async {
|
||||
await Services.instance.authService.joinInvite(userName);
|
||||
this.user =
|
||||
await Services.instance.authService.getUser(refreshIdToken: true);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> updateProfile(String newUserName) async {
|
||||
await Services.instance.authService.updateProfile(newUserName);
|
||||
this.user =
|
||||
|
||||
@@ -1,279 +0,0 @@
|
||||
import 'package:fcs/fcs/common/helpers/theme.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/buyer_model.dart';
|
||||
import 'package:fcs/model/main_model.dart';
|
||||
import 'package:fcs/pages/quota_page.dart';
|
||||
import 'package:fcs/fcs/common/pages/util.dart';
|
||||
import 'package:fcs/util.dart';
|
||||
import 'package:fcs/vo/buyer.dart';
|
||||
import 'package:fcs/widget/label_widgets.dart';
|
||||
import 'package:fcs/widget/localization/app_translations.dart';
|
||||
import 'package:fcs/widget/progress.dart';
|
||||
|
||||
class BuyerInfo extends StatefulWidget {
|
||||
final Buyer buyer;
|
||||
const BuyerInfo({this.buyer});
|
||||
@override
|
||||
_BuyerInfoState createState() => _BuyerInfoState();
|
||||
}
|
||||
|
||||
class _BuyerInfoState extends State<BuyerInfo> {
|
||||
var dateFormatter = new DateFormat('dd MMM yyyy - hh:mm a');
|
||||
TextEditingController _companyName = new TextEditingController();
|
||||
TextEditingController _comAddress = new TextEditingController();
|
||||
TextEditingController _numOfShops = new TextEditingController();
|
||||
TextEditingController _bizType = new TextEditingController();
|
||||
TextEditingController _accountName = new TextEditingController();
|
||||
TextEditingController _accountNumber = new TextEditingController();
|
||||
|
||||
bool _isLoading = false;
|
||||
Buyer buyer;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (widget.buyer != null) {
|
||||
buyer = widget.buyer;
|
||||
Provider.of<BuyerModel>(context, listen: false)
|
||||
.loadBuyerProducts(buyer)
|
||||
.then((b) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
buyer = b;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var mainModel = Provider.of<MainModel>(context);
|
||||
|
||||
_companyName.text = buyer.bizName;
|
||||
_comAddress.text = buyer.bizAddress;
|
||||
_numOfShops.text = buyer.numOfShops.toString();
|
||||
_bizType.text = buyer.bizType;
|
||||
_accountName.text = buyer.userName;
|
||||
_accountNumber.text = buyer.userID;
|
||||
|
||||
final dateBox =
|
||||
labeledText(context, dateFormatter.format(buyer.regDate), "reg.date");
|
||||
final accountBox =
|
||||
labeledText(context, buyer.userName, "buyer.account_name");
|
||||
final phoneBox = labeledText(context, buyer.phone, "buyer.phone_number");
|
||||
final statusBox = labeledText(context, buyer.status, "reg.status");
|
||||
final bizNameBox = labeledText(context, _companyName.text, "reg.biz_name");
|
||||
final bizAddressBox =
|
||||
labeledText(context, _comAddress.text, "reg.biz_address");
|
||||
final shopNumberBox =
|
||||
labeledText(context, _numOfShops.text, "reg.biz_shops");
|
||||
final typeBox = labeledText(context, _bizType.text, "buyer.type_biz");
|
||||
final dailyQuotaBox = labeledText(
|
||||
context, formatNumber(buyer.dailyQuota), "reg.quota",
|
||||
number: true);
|
||||
final dailyQuotaUsedBox = labeledText(
|
||||
context, formatNumber(buyer.dailyQuotaUsed), "reg.quota.used",
|
||||
number: true);
|
||||
final maxQuotaBox = labeledText(
|
||||
context, formatNumber(buyer.maxQuota), "reg.max_quota",
|
||||
number: true);
|
||||
final maxQuotaUsedBox = labeledText(
|
||||
context, formatNumber(buyer.maxQuotaUsed), "reg.max_quota.used",
|
||||
number: true);
|
||||
final nricFrontBox =
|
||||
labeledImg(context, buyer.nricFrontUrl, "reg_info.nric_front");
|
||||
final nricBackBox =
|
||||
labeledImg(context, buyer.nricBackUrl, "reg_info.nric_back");
|
||||
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: primaryColor,
|
||||
title: Text(AppTranslations.of(context).text("buyer.title")),
|
||||
actions: <Widget>[
|
||||
mainModel.showHistoryBtn()
|
||||
? IconButton(
|
||||
icon: Icon(Icons.history),
|
||||
onPressed: () {
|
||||
// Navigator.push(
|
||||
// context,
|
||||
// MaterialPageRoute(
|
||||
// builder: (context) =>
|
||||
// DocumentLogPage(docID: buyer.id)),
|
||||
// );
|
||||
},
|
||||
)
|
||||
: Container(),
|
||||
PopupMenuButton(
|
||||
onSelected: (s) {
|
||||
if (s == 1) {
|
||||
showConfirmDialog(context, "buyer.delete.confirm", () {
|
||||
_delete();
|
||||
});
|
||||
} else if (s == 2) {
|
||||
showConfirmDialog(context, "buyer.approve.confirm", () {
|
||||
_approve();
|
||||
});
|
||||
} else if (s == 3) {
|
||||
showCommentDialog(context, (comment) {
|
||||
_reject(comment);
|
||||
});
|
||||
} else if (s == 4) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => QuotaPage(
|
||||
buyer: this.buyer,
|
||||
isApproved: true,
|
||||
)),
|
||||
);
|
||||
}
|
||||
},
|
||||
itemBuilder: (context) => [
|
||||
PopupMenuItem(
|
||||
value: 1,
|
||||
child: Text("Delete"),
|
||||
),
|
||||
PopupMenuItem(
|
||||
enabled: buyer.isPending(),
|
||||
value: 2,
|
||||
child: Text("Approve"),
|
||||
),
|
||||
PopupMenuItem(
|
||||
enabled: buyer.isPending(),
|
||||
value: 3,
|
||||
child: Text("Reject"),
|
||||
),
|
||||
PopupMenuItem(
|
||||
enabled: buyer.isApproved(),
|
||||
value: 4,
|
||||
child: Text("Allocate Quota"),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
body: Container(
|
||||
padding: EdgeInsets.only(left: 10, right: 10, top: 10, bottom: 10),
|
||||
child: ListView(
|
||||
children: <Widget>[
|
||||
dateBox,
|
||||
Divider(),
|
||||
accountBox,
|
||||
Divider(),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 8.0),
|
||||
child: phoneBox,
|
||||
),
|
||||
InkWell(
|
||||
onTap: () => call(context, buyer.phone),
|
||||
child: Icon(
|
||||
Icons.open_in_new,
|
||||
color: Colors.grey,
|
||||
size: 15,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Divider(),
|
||||
statusBox,
|
||||
Divider(),
|
||||
bizNameBox,
|
||||
Divider(),
|
||||
bizAddressBox,
|
||||
Divider(),
|
||||
typeBox,
|
||||
Divider(),
|
||||
dailyQuotaBox,
|
||||
Divider(),
|
||||
dailyQuotaUsedBox,
|
||||
Divider(),
|
||||
maxQuotaBox,
|
||||
Divider(),
|
||||
maxQuotaUsedBox,
|
||||
Divider(),
|
||||
nricFrontBox,
|
||||
Divider(),
|
||||
nricBackBox
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_delete() async {
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
|
||||
try {
|
||||
await Provider.of<BuyerModel>(context).delete(buyer);
|
||||
Navigator.pop(context, true);
|
||||
} catch (e) {
|
||||
showMsgDialog(context, "Error", e.toString());
|
||||
} finally {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_approve() async {
|
||||
var _buyer = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => QuotaPage(
|
||||
buyer: this.buyer,
|
||||
isApproved: false,
|
||||
)),
|
||||
);
|
||||
if (_buyer == null) return;
|
||||
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
|
||||
try {
|
||||
this.buyer.dailyQuota = _buyer.dailyQuota;
|
||||
this.buyer.maxQuota = _buyer.maxQuota;
|
||||
await Provider.of<BuyerModel>(context).approve(this.buyer);
|
||||
Navigator.pop(context, true);
|
||||
} catch (e) {
|
||||
showMsgDialog(context, "Error", e.toString());
|
||||
} finally {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_reject(comment) async {
|
||||
if (comment == null || comment == "") {
|
||||
showMsgDialog(context, "Error", "Please enter comment!");
|
||||
return;
|
||||
}
|
||||
buyer.comment = comment;
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
|
||||
try {
|
||||
await Provider.of<BuyerModel>(context).reject(buyer);
|
||||
Navigator.pop(context, true);
|
||||
} catch (e) {
|
||||
showMsgDialog(context, "Error", e.toString());
|
||||
} finally {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
import 'package:fcs/fcs/common/pages/package/buyer_info.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/buyer_model.dart';
|
||||
import 'package:fcs/fcs/common/pages/util.dart';
|
||||
import 'package:fcs/fcs/common/helpers/theme.dart';
|
||||
import 'package:fcs/vo/buyer.dart';
|
||||
|
||||
class BuyerListRow extends StatefulWidget {
|
||||
final Buyer buyer;
|
||||
const BuyerListRow({this.buyer});
|
||||
|
||||
@override
|
||||
_BuyerListRowState createState() => _BuyerListRowState();
|
||||
}
|
||||
|
||||
class _BuyerListRowState extends State<BuyerListRow> {
|
||||
final double dotSize = 15.0;
|
||||
Buyer _buyer = new Buyer();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
BuyerModel buyerModel = Provider.of<BuyerModel>(context, listen: false);
|
||||
if (widget.buyer != null) {
|
||||
buyerModel.buyers.forEach((b) {
|
||||
if (widget.buyer.id == b.id) {
|
||||
_buyer = b;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: EdgeInsets.only(left: 15, right: 15),
|
||||
child: Card(
|
||||
elevation: 10,
|
||||
color: Colors.white,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => BuyerInfo(buyer: _buyer)),
|
||||
);
|
||||
},
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: new Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16.0),
|
||||
child: new Row(
|
||||
children: <Widget>[
|
||||
new Padding(
|
||||
padding: new EdgeInsets.symmetric(
|
||||
horizontal: 32.0 - dotSize / 2),
|
||||
child: Image.asset(
|
||||
"assets/buyer.png",
|
||||
width: 40,
|
||||
height: 40,
|
||||
color: primaryColor,
|
||||
),
|
||||
),
|
||||
new Expanded(
|
||||
child: new Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
new Text(
|
||||
_buyer.userName == null ? '' : _buyer.userName,
|
||||
style: new TextStyle(
|
||||
fontSize: 15.0, color: Colors.black),
|
||||
),
|
||||
new Text(
|
||||
_buyer.bizName == null ? "" : _buyer.bizName,
|
||||
style: new TextStyle(
|
||||
fontSize: 13.0, color: Colors.grey),
|
||||
),
|
||||
new Text(
|
||||
_buyer.phone == null ? "" : _buyer.phone,
|
||||
style: new TextStyle(
|
||||
fontSize: 13.0, color: Colors.grey),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: getStatus(_buyer.status),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'package:path/path.dart' as Path;
|
||||
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:fcs/fcs/common/domain/constants.dart';
|
||||
import 'package:fcs/fcs/common/domain/entities/package.dart';
|
||||
import 'package:fcs/fcs/common/domain/entities/user.dart';
|
||||
import 'package:fcs/fcs/common/helpers/firebase_helper.dart';
|
||||
import 'package:fcs/fcs/common/pages/model/base_model.dart';
|
||||
import 'package:fcs/fcs/common/services/services.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
@@ -33,12 +36,16 @@ class PackageModel extends BaseModel {
|
||||
} else {
|
||||
path = "/$packages_collection";
|
||||
}
|
||||
if (listener != null) listener.cancel();
|
||||
packages = [];
|
||||
|
||||
try {
|
||||
listener = Firestore.instance
|
||||
.collection("$path")
|
||||
.where("is_delivered", isEqualTo: false)
|
||||
.snapshots()
|
||||
.listen((QuerySnapshot snapshot) {
|
||||
print("reloaded....");
|
||||
packages.clear();
|
||||
packages = snapshot.documents.map((documentSnapshot) {
|
||||
var package = Package.fromMap(
|
||||
@@ -60,4 +67,20 @@ class PackageModel extends BaseModel {
|
||||
return Services.instance.packageService
|
||||
.createPackages(packages, user.fcsID);
|
||||
}
|
||||
|
||||
Future<void> completeProcessing(
|
||||
Package package, List<File> files, List<String> deletedUrls) async {
|
||||
if (files != null) {
|
||||
if (files.length > 5) throw Exception("Exceed number of file upload");
|
||||
package.photoUrls = package.photoUrls == null ? [] : package.photoUrls;
|
||||
for (File f in files) {
|
||||
String path = Path.join(pkg_files_path, package.userID, package.id);
|
||||
String url = await uploadStorage(path, f);
|
||||
package.photoUrls.add(url);
|
||||
}
|
||||
package.photoUrls.removeWhere((e) => deletedUrls.contains(e));
|
||||
}
|
||||
await request("/packages", "PUT",
|
||||
payload: package.toJson(), token: await getToken());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,550 +0,0 @@
|
||||
import 'package:fcs/fcs/common/domain/entities/package.dart';
|
||||
import 'package:fcs/fcs/common/domain/vo/shipping_address.dart';
|
||||
import 'package:fcs/fcs/common/helpers/theme.dart';
|
||||
import 'package:fcs/fcs/common/localization/app_translations.dart';
|
||||
import 'package:fcs/fcs/common/pages/model/main_model.dart';
|
||||
import 'package:fcs/fcs/common/pages/package/tracking_id_page.dart';
|
||||
import 'package:fcs/fcs/common/pages/package/shipping_address_editor.dart';
|
||||
import 'package:fcs/fcs/common/pages/package/shipping_address_list.dart';
|
||||
import 'package:fcs/fcs/common/pages/package/shipping_address_row.dart';
|
||||
import 'package:fcs/fcs/common/pages/util.dart';
|
||||
import 'package:fcs/fcs/common/pages/widgets/bottom_up_page_route.dart';
|
||||
import 'package:fcs/fcs/common/pages/widgets/fcs_expansion_tile.dart';
|
||||
import 'package:fcs/fcs/common/pages/widgets/my_data_table.dart';
|
||||
import 'package:fcs/fcs/common/pages/widgets/progress.dart';
|
||||
import 'package:fcs/pages/barcode_screen_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_icons/flutter_icons.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:timeline_list/timeline.dart';
|
||||
import 'package:timeline_list/timeline_model.dart';
|
||||
|
||||
class PackageCreation extends StatefulWidget {
|
||||
final Package package;
|
||||
PackageCreation({this.package});
|
||||
|
||||
@override
|
||||
_PackageCreationState createState() => _PackageCreationState();
|
||||
}
|
||||
|
||||
class _PackageCreationState extends State<PackageCreation> {
|
||||
TextEditingController _addressEditingController = new TextEditingController();
|
||||
TextEditingController _fromTimeEditingController =
|
||||
new TextEditingController();
|
||||
TextEditingController _toTimeEditingController = new TextEditingController();
|
||||
TextEditingController _noOfPackageEditingController =
|
||||
new TextEditingController();
|
||||
TextEditingController _weightEditingController = new TextEditingController();
|
||||
|
||||
Package _package;
|
||||
bool _isLoading = false;
|
||||
List<String> _images = [
|
||||
"assets/photos/1.jpg",
|
||||
"assets/photos/2.jpg",
|
||||
"assets/photos/3.jpg"
|
||||
];
|
||||
bool isNew;
|
||||
ShippingAddress shippingAddress = ShippingAddress(
|
||||
fullName: 'U Nyi Nyi',
|
||||
addressLine1: '154-19 64th Ave.',
|
||||
addressLine2: 'Flushing',
|
||||
city: 'NY',
|
||||
state: 'NY',
|
||||
phoneNumber: '+1 (292)215-2247');
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (widget.package != null) {
|
||||
_package = widget.package;
|
||||
isNew = false;
|
||||
// _addressEditingController.text = _pickUp.address;
|
||||
// _fromTimeEditingController.text = _pickUp.fromTime;
|
||||
// _toTimeEditingController.text = _pickUp.toTime;
|
||||
// _noOfPackageEditingController.text = _pickUp.numberOfPackage.toString();
|
||||
// _weightEditingController.text = _pickUp.weight.toString();
|
||||
} else {
|
||||
isNew = true;
|
||||
_package = Package(rate: 0, weight: 0);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
final DateFormat dateFormat = DateFormat("d MMM yyyy");
|
||||
|
||||
List<TimelineModel> _models() {
|
||||
print('_package.statusHistory=> ${_package.shipmentHistory}');
|
||||
return _package.shipmentHistory
|
||||
.map((e) => TimelineModel(
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(18.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(e.status,
|
||||
style: TextStyle(
|
||||
color: e.done ? primaryColor : Colors.grey,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold)),
|
||||
e.status == "Processed"
|
||||
? Text("(Waiting for payment)",
|
||||
style: TextStyle(
|
||||
color: e.done ? primaryColor : Colors.grey,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold))
|
||||
: Container(),
|
||||
Text(dateFormat.format(e.date)),
|
||||
],
|
||||
),
|
||||
),
|
||||
iconBackground: e.done ? primaryColor : Colors.grey,
|
||||
icon: Icon(
|
||||
e.status == "Shipped"
|
||||
? Ionicons.ios_airplane
|
||||
: e.status == "Delivered"
|
||||
? MaterialCommunityIcons.truck_fast
|
||||
: e.status == "Processed"
|
||||
? MaterialIcons.check
|
||||
: Octicons.package,
|
||||
color: Colors.white,
|
||||
)))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var owner = Provider.of<MainModel>(context).user.hasPackages();
|
||||
|
||||
final trackingIdBox = Padding(
|
||||
padding: const EdgeInsets.only(left: 20.0, right: 20),
|
||||
child: TextFormField(
|
||||
initialValue: isNew ? "" : "zdf-sdfl-37sdfks",
|
||||
decoration: InputDecoration(
|
||||
fillColor: Colors.white,
|
||||
labelText: 'Tracking ID',
|
||||
hintText: 'Tracking ID',
|
||||
filled: true,
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
Ionicons.ios_barcode,
|
||||
color: primaryColor,
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
BottomUpPageRoute(BarcodeScreenPage()),
|
||||
);
|
||||
}),
|
||||
icon: Icon(Octicons.package, color: primaryColor),
|
||||
),
|
||||
),
|
||||
);
|
||||
var images = isNew ? [] : _images;
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
leading: new IconButton(
|
||||
icon: new Icon(Icons.close),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
backgroundColor: primaryColor,
|
||||
title: Text(AppTranslations.of(context).text("package.create.title")),
|
||||
),
|
||||
body: Card(
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
isNew ? Container() : Center(child: nameWidget(_package.market)),
|
||||
Expanded(
|
||||
child: ListView(
|
||||
children: [
|
||||
owner
|
||||
? FcsExpansionTile(
|
||||
title: Text(
|
||||
'Receiving',
|
||||
style: TextStyle(
|
||||
color: primaryColor,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
children: [
|
||||
trackingIdBox,
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 20.0, right: 20),
|
||||
child: TextFormField(
|
||||
initialValue: isNew ? "" : "FCS-0203-390-2",
|
||||
decoration: InputDecoration(
|
||||
fillColor: Colors.white,
|
||||
labelText: 'FCS_ID',
|
||||
hintText: 'FCS_ID',
|
||||
filled: true,
|
||||
icon: Icon(Feather.user,
|
||||
color: primaryColor),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(Icons.search),
|
||||
onPressed: () {})),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 20.0, right: 20),
|
||||
child: TextFormField(
|
||||
initialValue: _package.receiverName,
|
||||
decoration: InputDecoration(
|
||||
fillColor: Colors.white,
|
||||
labelText: 'Customer Name',
|
||||
filled: true,
|
||||
icon: Icon(Feather.user,
|
||||
color: Colors.white),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(Icons.search),
|
||||
onPressed: () {})),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: Container(),
|
||||
owner
|
||||
? isNew
|
||||
? Container()
|
||||
: ExpansionTile(
|
||||
title: Text(
|
||||
'Processing',
|
||||
style: TextStyle(
|
||||
color: primaryColor,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 20.0, right: 20),
|
||||
child: TextFormField(
|
||||
initialValue: isNew
|
||||
? ""
|
||||
: _package.cargoDesc.toString(),
|
||||
decoration: InputDecoration(
|
||||
fillColor: Colors.white,
|
||||
labelText: 'Description',
|
||||
filled: true,
|
||||
icon: Icon(MaterialIcons.description,
|
||||
color: primaryColor),
|
||||
)),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 20.0, right: 20),
|
||||
child: fcsInput(
|
||||
"Remark",
|
||||
MaterialCommunityIcons.note,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
],
|
||||
)
|
||||
: Container(),
|
||||
isNew
|
||||
? Container()
|
||||
: ExpansionTile(
|
||||
title: Text(
|
||||
'Photos',
|
||||
style: TextStyle(
|
||||
color: primaryColor,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
children: <Widget>[
|
||||
Container(
|
||||
height: 130,
|
||||
width: 500,
|
||||
child: ListView.separated(
|
||||
separatorBuilder: (context, index) => Divider(
|
||||
color: Colors.black,
|
||||
),
|
||||
itemCount: images.length + 1,
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemBuilder: (context, index) {
|
||||
if (index == images.length) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Container(
|
||||
width: 200,
|
||||
height: 70,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: primaryColor,
|
||||
width: 2.0,
|
||||
),
|
||||
),
|
||||
child: Icon(SimpleLineIcons.plus),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Container(
|
||||
width: 200,
|
||||
height: 70,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: primaryColor,
|
||||
width: 2.0,
|
||||
),
|
||||
),
|
||||
child: Image.asset(images[index],
|
||||
width: 50, height: 50),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
isNew ? Container() : getShippingAddressList(context),
|
||||
isNew
|
||||
? Container()
|
||||
: ExpansionTile(
|
||||
title: Text(
|
||||
'Status',
|
||||
style: TextStyle(
|
||||
color: primaryColor,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
children: <Widget>[
|
||||
Container(
|
||||
height: 500,
|
||||
padding: EdgeInsets.only(left: 20),
|
||||
child: isNew
|
||||
? Container()
|
||||
: Timeline(
|
||||
children: _models(),
|
||||
position: TimelinePosition.Left),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
owner
|
||||
? widget.package == null
|
||||
? Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: Center(
|
||||
child: Container(
|
||||
width: 250,
|
||||
child: FlatButton(
|
||||
child: Text('Complete receiving'),
|
||||
color: primaryColor,
|
||||
textColor: Colors.white,
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
)))
|
||||
: Container(
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: Center(
|
||||
child: Container(
|
||||
width: 250,
|
||||
child: FlatButton(
|
||||
child: Text('Complete processing'),
|
||||
color: primaryColor,
|
||||
textColor: Colors.white,
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
))),
|
||||
],
|
||||
))
|
||||
: Container()
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget getShippingAddressList(BuildContext context) {
|
||||
return Container(
|
||||
child: ExpansionTile(
|
||||
title: Text(
|
||||
"Shipping Addresses",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontStyle: FontStyle.normal,
|
||||
color: primaryColor),
|
||||
),
|
||||
children: <Widget>[
|
||||
// Column(
|
||||
// children: getAddressList(context, shipmentModel.shippingAddresses),
|
||||
// ),
|
||||
Container(
|
||||
padding: EdgeInsets.only(left: 10, right: 10),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: new Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10.0),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
new Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: new Text(
|
||||
shippingAddress.fullName == null
|
||||
? ''
|
||||
: shippingAddress.fullName,
|
||||
style: new TextStyle(
|
||||
fontSize: 15.0,
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: new Text(
|
||||
shippingAddress.addressLine1 == null
|
||||
? ''
|
||||
: shippingAddress.addressLine1,
|
||||
style: new TextStyle(
|
||||
fontSize: 14.0, color: Colors.grey),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: new Text(
|
||||
shippingAddress.addressLine2 == null
|
||||
? ''
|
||||
: shippingAddress.addressLine2,
|
||||
style: new TextStyle(
|
||||
fontSize: 14.0, color: Colors.grey),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: new Text(
|
||||
shippingAddress.city == null
|
||||
? ''
|
||||
: shippingAddress.city,
|
||||
style: new TextStyle(
|
||||
fontSize: 14.0, color: Colors.grey),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: new Text(
|
||||
shippingAddress.state == null
|
||||
? ''
|
||||
: shippingAddress.state,
|
||||
style: new TextStyle(
|
||||
fontSize: 14.0, color: Colors.grey),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: new Text(
|
||||
shippingAddress.phoneNumber == null
|
||||
? ''
|
||||
: "Phone:${shippingAddress.phoneNumber}",
|
||||
style: new TextStyle(
|
||||
fontSize: 14.0, color: Colors.grey),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: EdgeInsets.only(top: 20, bottom: 15, right: 15),
|
||||
child: Align(
|
||||
alignment: Alignment.bottomRight,
|
||||
child: Container(
|
||||
width: 130,
|
||||
height: 40,
|
||||
child: FloatingActionButton.extended(
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
onPressed: () async {
|
||||
var address = await Navigator.push(
|
||||
context,
|
||||
BottomUpPageRoute(ShippingAddressList()),
|
||||
);
|
||||
print('address => ${address}');
|
||||
setState(() {
|
||||
if (address != null) {
|
||||
this.shippingAddress = address;
|
||||
}
|
||||
});
|
||||
},
|
||||
icon: Icon(Icons.add),
|
||||
label: Text(
|
||||
'Select \nAddress',
|
||||
style: TextStyle(fontSize: 12),
|
||||
),
|
||||
backgroundColor: primaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> getAddressList(
|
||||
BuildContext context, List<ShippingAddress> addresses) {
|
||||
return addresses.asMap().entries.map((s) {
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
BottomUpPageRoute(ShippingAddressEditor(shippingAddress: s.value)),
|
||||
);
|
||||
},
|
||||
child: ShippingAddressRow(shippingAddress: s.value, index: s.key),
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
List<MyDataRow> getAddressRows(List<ShippingAddress> addresses) {
|
||||
return addresses.map((s) {
|
||||
return MyDataRow(
|
||||
onSelectChanged: (selected) {},
|
||||
cells: [
|
||||
MyDataCell(
|
||||
new Text(
|
||||
s.fullName,
|
||||
style: textStyle,
|
||||
),
|
||||
),
|
||||
MyDataCell(
|
||||
new Text(
|
||||
s.phoneNumber,
|
||||
style: textStyle,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
}
|
||||
427
lib/fcs/common/pages/package/package_editor copy.dart
Normal file
427
lib/fcs/common/pages/package/package_editor copy.dart
Normal file
@@ -0,0 +1,427 @@
|
||||
import 'package:fcs/fcs/common/domain/entities/market.dart';
|
||||
import 'package:fcs/fcs/common/domain/entities/package.dart';
|
||||
import 'package:fcs/fcs/common/domain/vo/shipping_address.dart';
|
||||
import 'package:fcs/fcs/common/helpers/theme.dart';
|
||||
import 'package:fcs/fcs/common/localization/app_translations.dart';
|
||||
import 'package:fcs/fcs/common/pages/market/market_editor.dart';
|
||||
import 'package:fcs/fcs/common/pages/market/model/market_model.dart';
|
||||
import 'package:fcs/fcs/common/pages/model/main_model.dart';
|
||||
import 'package:fcs/fcs/common/pages/package/tracking_id_page.dart';
|
||||
import 'package:fcs/fcs/common/pages/package/shipping_address_editor.dart';
|
||||
import 'package:fcs/fcs/common/pages/package/shipping_address_list.dart';
|
||||
import 'package:fcs/fcs/common/pages/package/shipping_address_row.dart';
|
||||
import 'package:fcs/fcs/common/pages/util.dart';
|
||||
import 'package:fcs/fcs/common/pages/widgets/bottom_up_page_route.dart';
|
||||
import 'package:fcs/fcs/common/pages/widgets/display_text.dart';
|
||||
import 'package:fcs/fcs/common/pages/widgets/fcs_expansion_tile.dart';
|
||||
import 'package:fcs/fcs/common/pages/widgets/image_slider.dart';
|
||||
import 'package:fcs/fcs/common/pages/widgets/input_text.dart';
|
||||
import 'package:fcs/fcs/common/pages/widgets/local_text.dart';
|
||||
import 'package:fcs/fcs/common/pages/widgets/my_data_table.dart';
|
||||
import 'package:fcs/fcs/common/pages/widgets/progress.dart';
|
||||
import 'package:fcs/pages/barcode_screen_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_icons/flutter_icons.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:timeline_list/timeline.dart';
|
||||
import 'package:timeline_list/timeline_model.dart';
|
||||
|
||||
class PackageEditorPage extends StatefulWidget {
|
||||
final Package package;
|
||||
PackageEditorPage({this.package});
|
||||
|
||||
@override
|
||||
_PackageEditorPageState createState() => _PackageEditorPageState();
|
||||
}
|
||||
|
||||
class _PackageEditorPageState extends State<PackageEditorPage> {
|
||||
TextEditingController _remarkCtl = new TextEditingController();
|
||||
TextEditingController _descCtl = new TextEditingController();
|
||||
|
||||
Package _package;
|
||||
bool _isLoading = false;
|
||||
List<String> images = [
|
||||
"assets/photos/1.jpg",
|
||||
"assets/photos/2.jpg",
|
||||
"assets/photos/3.jpg"
|
||||
];
|
||||
ShippingAddress shippingAddress = ShippingAddress(
|
||||
fullName: 'U Nyi Nyi',
|
||||
addressLine1: '154-19 64th Ave.',
|
||||
addressLine2: 'Flushing',
|
||||
city: 'NY',
|
||||
state: 'NY',
|
||||
phoneNumber: '+1 (292)215-2247');
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_package = widget.package;
|
||||
selectedMarket = _package.market;
|
||||
}
|
||||
|
||||
final DateFormat dateFormat = DateFormat("d MMM yyyy");
|
||||
|
||||
List<TimelineModel> _models() {
|
||||
if (_package.shipmentHistory == null) return [];
|
||||
return _package.shipmentHistory
|
||||
.map((e) => TimelineModel(
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(18.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(e.status,
|
||||
style: TextStyle(
|
||||
color: e.done ? primaryColor : Colors.grey,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold)),
|
||||
Text(dateFormat.format(e.date)),
|
||||
],
|
||||
),
|
||||
),
|
||||
iconBackground: e.done ? primaryColor : Colors.grey,
|
||||
icon: Icon(
|
||||
e.status == "shipped"
|
||||
? Ionicons.ios_airplane
|
||||
: e.status == "delivered"
|
||||
? MaterialCommunityIcons.truck_fast
|
||||
: e.status == "processed"
|
||||
? MaterialIcons.check
|
||||
: Octicons.package,
|
||||
color: Colors.white,
|
||||
)))
|
||||
.toList();
|
||||
}
|
||||
|
||||
bool isNew = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var owner = Provider.of<MainModel>(context).user.hasPackages();
|
||||
|
||||
final trackingIdBox = DisplayText(
|
||||
text: _package.trackingID,
|
||||
labelText: getLocalString(context, "package.tracking.id"),
|
||||
iconData: MaterialCommunityIcons.barcode_scan,
|
||||
);
|
||||
final customerNameBox = DisplayText(
|
||||
text: _package.userName,
|
||||
labelText: getLocalString(context, "package.create.name"),
|
||||
iconData: Icons.perm_identity,
|
||||
);
|
||||
final completeProcessingBtn = fcsButton(
|
||||
context,
|
||||
getLocalString(context, 'package.edit.complete.process.btn'),
|
||||
callack: _completeProcessing,
|
||||
);
|
||||
final descBox = fcsInput(getLocalString(context, "package.edit.desc"),
|
||||
MaterialCommunityIcons.message_text_outline,
|
||||
controller: _descCtl, autoFocus: true);
|
||||
final remarkBox = fcsInput(
|
||||
getLocalString(context, "package.edit.remark"), Entypo.new_message,
|
||||
controller: _remarkCtl, autoFocus: true);
|
||||
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
leading: new IconButton(
|
||||
icon: new Icon(Icons.close, color: primaryColor, size: 30),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
shadowColor: Colors.transparent,
|
||||
backgroundColor: Colors.white,
|
||||
title: LocalText(
|
||||
context,
|
||||
"package.edit.title",
|
||||
fontSize: 20,
|
||||
color: primaryColor,
|
||||
),
|
||||
),
|
||||
body: ListView(
|
||||
children: [
|
||||
trackingIdBox,
|
||||
customerNameBox,
|
||||
owner
|
||||
? isNew
|
||||
? Container()
|
||||
: ExpansionTile(
|
||||
title: Text(
|
||||
'Processing',
|
||||
style: TextStyle(
|
||||
color: primaryColor, fontWeight: FontWeight.bold),
|
||||
),
|
||||
children: [
|
||||
dropDown(),
|
||||
descBox,
|
||||
remarkBox,
|
||||
],
|
||||
)
|
||||
: Container(),
|
||||
getImgSlider(images),
|
||||
ExpansionTile(
|
||||
title: Text(
|
||||
'Status',
|
||||
style:
|
||||
TextStyle(color: primaryColor, fontWeight: FontWeight.bold),
|
||||
),
|
||||
children: <Widget>[
|
||||
Container(
|
||||
padding: EdgeInsets.only(left: 20),
|
||||
height: 400,
|
||||
child: Timeline(
|
||||
children: _models(), position: TimelinePosition.Left),
|
||||
),
|
||||
],
|
||||
),
|
||||
completeProcessingBtn,
|
||||
SizedBox(
|
||||
height: 20,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String selectedMarket;
|
||||
Widget dropDown() {
|
||||
List<Market> _markets = Provider.of<MarketModel>(context).markets;
|
||||
List<String> markets = _markets.map((e) => e.name).toList();
|
||||
markets.insert(0, MANAGE_MARKET);
|
||||
|
||||
return Row(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 18.0),
|
||||
child: LocalText(
|
||||
context,
|
||||
"package.create.market",
|
||||
color: primaryColor,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: 150,
|
||||
child: DropdownButton<String>(
|
||||
value: selectedMarket,
|
||||
style: TextStyle(color: Colors.black, fontSize: 14),
|
||||
underline: Container(
|
||||
height: 1,
|
||||
color: Colors.grey,
|
||||
),
|
||||
onChanged: (String newValue) {
|
||||
setState(() {
|
||||
if (newValue == MANAGE_MARKET) {
|
||||
selectedMarket = null;
|
||||
_manageMarket();
|
||||
return;
|
||||
}
|
||||
selectedMarket = newValue;
|
||||
});
|
||||
},
|
||||
isExpanded: true,
|
||||
items: markets.map<DropdownMenuItem<String>>((String value) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: value,
|
||||
child: Text(value,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
color: value == MANAGE_MARKET
|
||||
? secondaryColor
|
||||
: primaryColor)),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
_manageMarket() {
|
||||
Navigator.push<Package>(
|
||||
context,
|
||||
BottomUpPageRoute(MarketEditor()),
|
||||
);
|
||||
}
|
||||
|
||||
Widget getShippingAddressList(BuildContext context) {
|
||||
return Container(
|
||||
child: ExpansionTile(
|
||||
title: Text(
|
||||
"Shipping Addresses",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontStyle: FontStyle.normal,
|
||||
color: primaryColor),
|
||||
),
|
||||
children: <Widget>[
|
||||
// Column(
|
||||
// children: getAddressList(context, shipmentModel.shippingAddresses),
|
||||
// ),
|
||||
Container(
|
||||
padding: EdgeInsets.only(left: 10, right: 10),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: new Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10.0),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
new Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: new Text(
|
||||
shippingAddress.fullName == null
|
||||
? ''
|
||||
: shippingAddress.fullName,
|
||||
style: new TextStyle(
|
||||
fontSize: 15.0,
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: new Text(
|
||||
shippingAddress.addressLine1 == null
|
||||
? ''
|
||||
: shippingAddress.addressLine1,
|
||||
style: new TextStyle(
|
||||
fontSize: 14.0, color: Colors.grey),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: new Text(
|
||||
shippingAddress.addressLine2 == null
|
||||
? ''
|
||||
: shippingAddress.addressLine2,
|
||||
style: new TextStyle(
|
||||
fontSize: 14.0, color: Colors.grey),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: new Text(
|
||||
shippingAddress.city == null
|
||||
? ''
|
||||
: shippingAddress.city,
|
||||
style: new TextStyle(
|
||||
fontSize: 14.0, color: Colors.grey),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: new Text(
|
||||
shippingAddress.state == null
|
||||
? ''
|
||||
: shippingAddress.state,
|
||||
style: new TextStyle(
|
||||
fontSize: 14.0, color: Colors.grey),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: new Text(
|
||||
shippingAddress.phoneNumber == null
|
||||
? ''
|
||||
: "Phone:${shippingAddress.phoneNumber}",
|
||||
style: new TextStyle(
|
||||
fontSize: 14.0, color: Colors.grey),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: EdgeInsets.only(top: 20, bottom: 15, right: 15),
|
||||
child: Align(
|
||||
alignment: Alignment.bottomRight,
|
||||
child: Container(
|
||||
width: 130,
|
||||
height: 40,
|
||||
child: FloatingActionButton.extended(
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
onPressed: () async {
|
||||
var address = await Navigator.push(
|
||||
context,
|
||||
BottomUpPageRoute(ShippingAddressList()),
|
||||
);
|
||||
print('address => ${address}');
|
||||
setState(() {
|
||||
if (address != null) {
|
||||
this.shippingAddress = address;
|
||||
}
|
||||
});
|
||||
},
|
||||
icon: Icon(Icons.add),
|
||||
label: Text(
|
||||
'Select \nAddress',
|
||||
style: TextStyle(fontSize: 12),
|
||||
),
|
||||
backgroundColor: primaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> getAddressList(
|
||||
BuildContext context, List<ShippingAddress> addresses) {
|
||||
return addresses.asMap().entries.map((s) {
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
BottomUpPageRoute(ShippingAddressEditor(shippingAddress: s.value)),
|
||||
);
|
||||
},
|
||||
child: ShippingAddressRow(shippingAddress: s.value, index: s.key),
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
List<MyDataRow> getAddressRows(List<ShippingAddress> addresses) {
|
||||
return addresses.map((s) {
|
||||
return MyDataRow(
|
||||
onSelectChanged: (selected) {},
|
||||
cells: [
|
||||
MyDataCell(
|
||||
new Text(
|
||||
s.fullName,
|
||||
style: textStyle,
|
||||
),
|
||||
),
|
||||
MyDataCell(
|
||||
new Text(
|
||||
s.phoneNumber,
|
||||
style: textStyle,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
_completeProcessing() {}
|
||||
}
|
||||
271
lib/fcs/common/pages/package/package_editor.dart
Normal file
271
lib/fcs/common/pages/package/package_editor.dart
Normal file
@@ -0,0 +1,271 @@
|
||||
import 'package:fcs/fcs/common/domain/entities/market.dart';
|
||||
import 'package:fcs/fcs/common/domain/entities/package.dart';
|
||||
import 'package:fcs/fcs/common/domain/vo/shipping_address.dart';
|
||||
import 'package:fcs/fcs/common/helpers/theme.dart';
|
||||
import 'package:fcs/fcs/common/pages/market/market_editor.dart';
|
||||
import 'package:fcs/fcs/common/pages/market/model/market_model.dart';
|
||||
import 'package:fcs/fcs/common/pages/package/model/package_model.dart';
|
||||
import 'package:fcs/fcs/common/pages/package/tracking_id_page.dart';
|
||||
import 'package:fcs/fcs/common/pages/util.dart';
|
||||
import 'package:fcs/fcs/common/pages/widgets/bottom_up_page_route.dart';
|
||||
import 'package:fcs/fcs/common/pages/widgets/display_text.dart';
|
||||
import 'package:fcs/fcs/common/pages/widgets/image_slider.dart';
|
||||
import 'package:fcs/fcs/common/pages/widgets/local_text.dart';
|
||||
import 'package:fcs/fcs/common/pages/widgets/multi_img_controller.dart';
|
||||
import 'package:fcs/fcs/common/pages/widgets/multi_img_file.dart';
|
||||
import 'package:fcs/fcs/common/pages/widgets/progress.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_icons/flutter_icons.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:timeline_list/timeline_model.dart';
|
||||
|
||||
class PackageEditorPage extends StatefulWidget {
|
||||
final Package package;
|
||||
PackageEditorPage({this.package});
|
||||
|
||||
@override
|
||||
_PackageEditorPageState createState() => _PackageEditorPageState();
|
||||
}
|
||||
|
||||
class _PackageEditorPageState extends State<PackageEditorPage> {
|
||||
TextEditingController _remarkCtl = new TextEditingController();
|
||||
TextEditingController _descCtl = new TextEditingController();
|
||||
|
||||
Package _package;
|
||||
bool _isLoading = false;
|
||||
ShippingAddress shippingAddress = ShippingAddress(
|
||||
fullName: 'U Nyi Nyi',
|
||||
addressLine1: '154-19 64th Ave.',
|
||||
addressLine2: 'Flushing',
|
||||
city: 'NY',
|
||||
state: 'NY',
|
||||
phoneNumber: '+1 (292)215-2247');
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_package = widget.package;
|
||||
selectedMarket = _package.market;
|
||||
_descCtl.text = _package.desc;
|
||||
_remarkCtl.text = _package.remark;
|
||||
multiImgController.setImageUrls = _package.photoUrls;
|
||||
}
|
||||
|
||||
final DateFormat dateFormat = DateFormat("d MMM yyyy");
|
||||
|
||||
List<TimelineModel> _models() {
|
||||
if (_package.shipmentHistory == null) return [];
|
||||
return _package.shipmentHistory
|
||||
.map((e) => TimelineModel(
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(18.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(e.status,
|
||||
style: TextStyle(
|
||||
color: e.done ? primaryColor : Colors.grey,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold)),
|
||||
Text(dateFormat.format(e.date)),
|
||||
],
|
||||
),
|
||||
),
|
||||
iconBackground: e.done ? primaryColor : Colors.grey,
|
||||
icon: Icon(
|
||||
e.status == "shipped"
|
||||
? Ionicons.ios_airplane
|
||||
: e.status == "delivered"
|
||||
? MaterialCommunityIcons.truck_fast
|
||||
: e.status == "processed"
|
||||
? MaterialIcons.check
|
||||
: Octicons.package,
|
||||
color: Colors.white,
|
||||
)))
|
||||
.toList();
|
||||
}
|
||||
|
||||
bool isNew = false;
|
||||
MultiImgController multiImgController = MultiImgController();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final trackingIdBox = DisplayText(
|
||||
text: _package.trackingID,
|
||||
labelText: getLocalString(context, "package.tracking.id"),
|
||||
iconData: MaterialCommunityIcons.barcode_scan,
|
||||
);
|
||||
final statusBox = DisplayText(
|
||||
text: _package.currentStatus,
|
||||
labelText: getLocalString(context, "package.edit.status"),
|
||||
iconData: AntDesign.exclamationcircleo,
|
||||
);
|
||||
final customerNameBox = DisplayText(
|
||||
text: _package.userName,
|
||||
labelText: getLocalString(context, "package.create.name"),
|
||||
iconData: Icons.perm_identity,
|
||||
);
|
||||
final completeProcessingBtn = fcsButton(
|
||||
context,
|
||||
getLocalString(context, 'package.edit.complete.process.btn'),
|
||||
callack: _completeProcessing,
|
||||
);
|
||||
final descBox = fcsInput(getLocalString(context, "package.edit.desc"),
|
||||
MaterialCommunityIcons.message_text_outline,
|
||||
controller: _descCtl, autoFocus: false);
|
||||
final remarkBox = fcsInput(
|
||||
getLocalString(context, "package.edit.remark"), Entypo.new_message,
|
||||
controller: _remarkCtl, autoFocus: false);
|
||||
final img = MultiImageFile(
|
||||
enabled: true,
|
||||
controller: multiImgController,
|
||||
title: "Receipt File",
|
||||
);
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
leading: new IconButton(
|
||||
icon: new Icon(Icons.close, color: primaryColor, size: 30),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
shadowColor: Colors.transparent,
|
||||
backgroundColor: Colors.white,
|
||||
title: LocalText(
|
||||
context,
|
||||
"package.edit.title",
|
||||
fontSize: 20,
|
||||
color: primaryColor,
|
||||
),
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: ListView(
|
||||
children: [
|
||||
trackingIdBox,
|
||||
customerNameBox,
|
||||
statusBox,
|
||||
Divider(),
|
||||
Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: LocalText(
|
||||
context,
|
||||
"package.edit.procseeing",
|
||||
color: primaryColor,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 18.0, right: 10),
|
||||
child: Column(
|
||||
children: [
|
||||
marketDropdown(),
|
||||
descBox,
|
||||
remarkBox,
|
||||
img,
|
||||
],
|
||||
),
|
||||
),
|
||||
completeProcessingBtn,
|
||||
SizedBox(
|
||||
height: 20,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String selectedMarket;
|
||||
Widget marketDropdown() {
|
||||
List<Market> _markets = Provider.of<MarketModel>(context).markets;
|
||||
List<String> markets = _markets.map((e) => e.name).toList();
|
||||
markets.insert(0, MANAGE_MARKET);
|
||||
|
||||
return Row(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 18.0),
|
||||
child: LocalText(
|
||||
context,
|
||||
"package.create.market",
|
||||
color: primaryColor,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: 150,
|
||||
child: DropdownButton<String>(
|
||||
value: selectedMarket,
|
||||
style: TextStyle(color: Colors.black, fontSize: 14),
|
||||
underline: Container(
|
||||
height: 1,
|
||||
color: Colors.grey,
|
||||
),
|
||||
onChanged: (String newValue) {
|
||||
setState(() {
|
||||
if (newValue == MANAGE_MARKET) {
|
||||
selectedMarket = null;
|
||||
_manageMarket();
|
||||
return;
|
||||
}
|
||||
selectedMarket = newValue;
|
||||
});
|
||||
},
|
||||
isExpanded: true,
|
||||
items: markets.map<DropdownMenuItem<String>>((String value) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: value,
|
||||
child: Text(value,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
color: value == MANAGE_MARKET
|
||||
? secondaryColor
|
||||
: primaryColor)),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
_manageMarket() {
|
||||
Navigator.push<Package>(
|
||||
context,
|
||||
BottomUpPageRoute(MarketEditor()),
|
||||
);
|
||||
}
|
||||
|
||||
_completeProcessing() async {
|
||||
if (_descCtl.text == "") {
|
||||
showMsgDialog(context, "Error", "Expected some description");
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
PackageModel packageModel =
|
||||
Provider.of<PackageModel>(context, listen: false);
|
||||
try {
|
||||
_package.desc = _descCtl.text;
|
||||
_package.remark = _remarkCtl.text;
|
||||
_package.market = selectedMarket;
|
||||
await packageModel.completeProcessing(_package,
|
||||
multiImgController.getAddedFile, multiImgController.getDeletedUrl);
|
||||
Navigator.pop(context);
|
||||
} catch (e) {
|
||||
showMsgDialog(context, "Error", e.toString());
|
||||
} finally {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,20 @@
|
||||
import 'package:fcs/fcs/common/domain/entities/package.dart';
|
||||
import 'package:fcs/fcs/common/helpers/theme.dart';
|
||||
import 'package:fcs/fcs/common/localization/app_translations.dart';
|
||||
import 'package:fcs/fcs/common/pages/widgets/label_widgets.dart';
|
||||
import 'package:fcs/fcs/common/pages/package/package_editor.dart';
|
||||
import 'package:fcs/fcs/common/pages/util.dart';
|
||||
import 'package:fcs/fcs/common/pages/widgets/bottom_up_page_route.dart';
|
||||
import 'package:fcs/fcs/common/pages/widgets/display_text.dart';
|
||||
import 'package:fcs/fcs/common/pages/widgets/local_text.dart';
|
||||
import 'package:fcs/fcs/common/pages/widgets/multi_img_controller.dart';
|
||||
import 'package:fcs/fcs/common/pages/widgets/multi_img_file.dart';
|
||||
import 'package:fcs/fcs/common/pages/widgets/progress.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:flutter_icons/flutter_icons.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:timeline_list/timeline.dart';
|
||||
import 'package:timeline_list/timeline_model.dart';
|
||||
|
||||
final DateFormat dateFormat = DateFormat("d MMM yyyy");
|
||||
|
||||
class PackageInfo extends StatefulWidget {
|
||||
final Package package;
|
||||
@@ -19,13 +28,13 @@ class _PackageInfoState extends State<PackageInfo> {
|
||||
var dateFormatter = new DateFormat('dd MMM yyyy');
|
||||
Package _package;
|
||||
bool _isLoading = false;
|
||||
MultiImgController multiImgController = MultiImgController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (widget.package != null) {
|
||||
_package = widget.package;
|
||||
}
|
||||
multiImgController.setImageUrls = _package.photoUrls;
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -35,17 +44,70 @@ class _PackageInfoState extends State<PackageInfo> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final trackingIdBox = DisplayText(
|
||||
text: _package.trackingID,
|
||||
labelText: getLocalString(context, "package.tracking.id"),
|
||||
iconData: MaterialCommunityIcons.barcode_scan,
|
||||
);
|
||||
final customerNameBox = DisplayText(
|
||||
text: _package.userName,
|
||||
labelText: getLocalString(context, "package.create.name"),
|
||||
iconData: Icons.perm_identity,
|
||||
);
|
||||
final statusBox = DisplayText(
|
||||
text: _package.currentStatus,
|
||||
labelText: getLocalString(context, "package.edit.status"),
|
||||
iconData: AntDesign.exclamationcircleo,
|
||||
);
|
||||
final marketBox = DisplayText(
|
||||
text: _package.market ?? "-",
|
||||
labelText: getLocalString(context, "package.create.market"),
|
||||
iconData: Icons.store,
|
||||
);
|
||||
final descBox = DisplayText(
|
||||
text: _package.desc ?? "-",
|
||||
labelText: getLocalString(context, "package.edit.desc"),
|
||||
iconData: MaterialCommunityIcons.message_text_outline,
|
||||
);
|
||||
final remarkBox = DisplayText(
|
||||
text: _package.remark ?? "-",
|
||||
labelText: getLocalString(context, "package.edit.remark"),
|
||||
iconData: Entypo.new_message,
|
||||
);
|
||||
final img = MultiImageFile(
|
||||
enabled: false,
|
||||
controller: multiImgController,
|
||||
title: "Receipt File",
|
||||
);
|
||||
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
leading: new IconButton(
|
||||
icon: new Icon(Icons.close),
|
||||
icon: new Icon(Icons.close, color: primaryColor, size: 30),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
backgroundColor: primaryColor,
|
||||
title: Text(AppTranslations.of(context).text("package.edit.title")),
|
||||
shadowColor: Colors.transparent,
|
||||
backgroundColor: Colors.white,
|
||||
title: LocalText(
|
||||
context,
|
||||
"package.info.title",
|
||||
fontSize: 20,
|
||||
color: primaryColor,
|
||||
),
|
||||
actions: <Widget>[
|
||||
IconButton(
|
||||
icon: Icon(Icons.edit, color: primaryColor),
|
||||
onPressed: () => Navigator.push<Package>(
|
||||
context,
|
||||
BottomUpPageRoute(PackageEditorPage(
|
||||
package: widget.package,
|
||||
)),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
body: Card(
|
||||
child: Column(
|
||||
@@ -54,80 +116,32 @@ class _PackageInfoState extends State<PackageInfo> {
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: ListView(children: <Widget>[
|
||||
Container(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Icon(
|
||||
Icons.calendar_today,
|
||||
trackingIdBox,
|
||||
customerNameBox,
|
||||
marketBox,
|
||||
statusBox,
|
||||
_package.photoUrls.length == 0 ? Container() : img,
|
||||
descBox,
|
||||
remarkBox,
|
||||
ExpansionTile(
|
||||
initiallyExpanded: true,
|
||||
title: Text(
|
||||
'Status',
|
||||
style: TextStyle(
|
||||
color: primaryColor, fontWeight: FontWeight.bold),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 8.0, left: 15),
|
||||
child: labeledText(
|
||||
context,
|
||||
dateFormatter.format(_package.arrivedDate),
|
||||
"package.arrival.date"),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Icon(Icons.pages),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 8.0, left: 15),
|
||||
child: labeledText(context, _package.packageNumber,
|
||||
"package.number"),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Icon(FontAwesomeIcons.weightHanging),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 8.0, left: 15),
|
||||
child: labeledText(
|
||||
context,
|
||||
"${_package.weight.toString()} lb",
|
||||
"package.weight"),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Icon(FontAwesomeIcons.tag),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 8.0, left: 15),
|
||||
child: labeledText(context, _package.rate.toString(),
|
||||
"package.rate"),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Icon(FontAwesomeIcons.moneyBill),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 8.0, left: 15),
|
||||
child: labeledText(
|
||||
context,
|
||||
_package.price == null
|
||||
? ""
|
||||
: "\$ " + _package.price.toString(),
|
||||
"package.amount"),
|
||||
children: <Widget>[
|
||||
Container(
|
||||
padding: EdgeInsets.only(left: 20),
|
||||
height: 400,
|
||||
child: Timeline(
|
||||
children: _models(),
|
||||
position: TimelinePosition.Left),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
height: 20,
|
||||
)
|
||||
]),
|
||||
)),
|
||||
@@ -137,4 +151,36 @@ class _PackageInfoState extends State<PackageInfo> {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<TimelineModel> _models() {
|
||||
if (_package.shipmentHistory == null) return [];
|
||||
return _package.shipmentHistory
|
||||
.map((e) => TimelineModel(
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(18.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(e.status,
|
||||
style: TextStyle(
|
||||
color: e.done ? primaryColor : Colors.grey,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold)),
|
||||
Text(dateFormat.format(e.date)),
|
||||
],
|
||||
),
|
||||
),
|
||||
iconBackground: e.done ? primaryColor : Colors.grey,
|
||||
icon: Icon(
|
||||
e.status == "shipped"
|
||||
? Ionicons.ios_airplane
|
||||
: e.status == "delivered"
|
||||
? MaterialCommunityIcons.truck_fast
|
||||
: e.status == "processed"
|
||||
? MaterialIcons.check
|
||||
: Octicons.package,
|
||||
color: Colors.white,
|
||||
)))
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import 'package:fcs/fcs/common/helpers/theme.dart';
|
||||
import 'package:fcs/fcs/common/localization/app_translations.dart';
|
||||
import 'package:fcs/fcs/common/pages/package/model/package_model.dart';
|
||||
import 'package:fcs/fcs/common/pages/package/package_creation.dart';
|
||||
import 'package:fcs/fcs/common/pages/package/package_list_row.dart';
|
||||
import 'package:fcs/fcs/common/pages/package/package_new.dart';
|
||||
import 'package:fcs/fcs/common/pages/package/search_page.dart';
|
||||
import 'package:fcs/fcs/common/pages/package/user_serach.dart';
|
||||
import 'package:fcs/fcs/common/pages/user_search/user_serach.dart';
|
||||
import 'package:fcs/fcs/common/pages/widgets/bottom_up_page_route.dart';
|
||||
import 'package:fcs/fcs/common/pages/widgets/progress.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -70,7 +68,7 @@ class _PackageListState extends State<PackageList> {
|
||||
),
|
||||
floatingActionButton: FloatingActionButton.extended(
|
||||
onPressed: () {
|
||||
_newPickup();
|
||||
_newPackage();
|
||||
},
|
||||
icon: Icon(Icons.add),
|
||||
label:
|
||||
@@ -88,13 +86,12 @@ class _PackageListState extends State<PackageList> {
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return PackageListRow(
|
||||
package: packageModel.packages[index],
|
||||
isReadOnly: false,
|
||||
);
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
||||
_newPickup() {
|
||||
_newPackage() {
|
||||
Navigator.push(
|
||||
context,
|
||||
BottomUpPageRoute(PackageNew()),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:fcs/fcs/common/domain/entities/package.dart';
|
||||
import 'package:fcs/fcs/common/pages/package/package_creation.dart';
|
||||
import 'package:fcs/fcs/common/pages/package/package_editor.dart';
|
||||
import 'package:fcs/fcs/common/pages/package/package_info.dart';
|
||||
import 'package:fcs/fcs/common/pages/util.dart';
|
||||
import 'package:fcs/fcs/common/pages/widgets/bottom_up_page_route.dart';
|
||||
@@ -7,9 +7,8 @@ import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
class PackageListRow extends StatefulWidget {
|
||||
final bool isReadOnly;
|
||||
final Package package;
|
||||
const PackageListRow({this.package, this.isReadOnly});
|
||||
const PackageListRow({this.package});
|
||||
|
||||
@override
|
||||
_PackageListRowtate createState() => _PackageListRowtate();
|
||||
@@ -32,17 +31,10 @@ class _PackageListRowtate extends State<PackageListRow> {
|
||||
padding: EdgeInsets.only(left: 15, right: 15),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
if (widget.isReadOnly) {
|
||||
Navigator.push(
|
||||
context,
|
||||
BottomUpPageRoute(PackageInfo(package: _package)),
|
||||
);
|
||||
} else {
|
||||
Navigator.push(
|
||||
context,
|
||||
BottomUpPageRoute(PackageCreation(package: _package)),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
|
||||
@@ -2,7 +2,7 @@ import 'package:fcs/fcs/common/domain/entities/package.dart';
|
||||
import 'package:fcs/fcs/common/domain/entities/user.dart';
|
||||
import 'package:fcs/fcs/common/helpers/theme.dart';
|
||||
import 'package:fcs/fcs/common/pages/package/tracking_id_page.dart';
|
||||
import 'package:fcs/fcs/common/pages/package/user_serach.dart';
|
||||
import 'package:fcs/fcs/common/pages/user_search/user_serach.dart';
|
||||
import 'package:fcs/fcs/common/pages/staff/model/staff_model.dart';
|
||||
import 'package:fcs/fcs/common/pages/util.dart';
|
||||
import 'package:fcs/fcs/common/pages/widgets/bottom_up_page_route.dart';
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
// Copyright 2019 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:fcs/fcs/common/domain/entities/user.dart';
|
||||
import 'package:fcs/fcs/common/pages/package/model/package_model.dart';
|
||||
import 'package:fcs/fcs/common/pages/package/user_list_row.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/fcs/common/helpers/theme.dart';
|
||||
|
||||
Future<User> searchUser1(BuildContext context) async => await showSearch<User>(
|
||||
context: context,
|
||||
delegate: UserSearchDelegate(),
|
||||
);
|
||||
|
||||
class UserSearchDelegate extends SearchDelegate<User> {
|
||||
@override
|
||||
ThemeData appBarTheme(BuildContext context) {
|
||||
final ThemeData theme = Theme.of(context);
|
||||
return theme.copyWith(
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
hintStyle: TextStyle(
|
||||
color: theme.primaryTextTheme.title.color, fontSize: 16)),
|
||||
primaryColor: primaryColor,
|
||||
primaryIconTheme: theme.primaryIconTheme.copyWith(color: Colors.white),
|
||||
primaryColorBrightness: Brightness.light,
|
||||
primaryTextTheme: theme.textTheme,
|
||||
textTheme: theme.textTheme.copyWith(
|
||||
title: theme.textTheme.title.copyWith(
|
||||
color: theme.primaryTextTheme.title.color, fontSize: 16)),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Widget> buildActions(BuildContext context) {
|
||||
return [
|
||||
IconButton(
|
||||
icon: Icon(Icons.clear),
|
||||
onPressed: () => query = '',
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildLeading(BuildContext context) {
|
||||
return IconButton(
|
||||
icon: Icon(Icons.arrow_back),
|
||||
onPressed: () => close(context, null),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildResults(BuildContext context) {
|
||||
final buyerModel = Provider.of<PackageModel>(context);
|
||||
return FutureBuilder(
|
||||
future: buyerModel.searchUser(query),
|
||||
builder: (context, AsyncSnapshot<List<User>> snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
if (snapshot.data.length == 0) {
|
||||
return Container(
|
||||
child: Center(
|
||||
child: Text(
|
||||
"Error :No Search Buyer",
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: 15),
|
||||
child: ListView(
|
||||
children:
|
||||
snapshot.data.map((u) => UserListRow(user: u)).toList(),
|
||||
),
|
||||
);
|
||||
} else if (snapshot.hasError) {
|
||||
return Container(
|
||||
child: Center(
|
||||
child: Text(
|
||||
'${snapshot.error}',
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return Container(
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(
|
||||
valueColor:
|
||||
new AlwaysStoppedAnimation<Color>(primaryColor)),
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildSuggestions(BuildContext context) {
|
||||
return Container(
|
||||
child: Center(
|
||||
child: Opacity(
|
||||
opacity: 0.2,
|
||||
child: Icon(
|
||||
Icons.supervised_user_circle,
|
||||
size: 200,
|
||||
)),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -113,7 +113,7 @@ class _SignupPageState extends State<SignupPage> {
|
||||
_isLoading = true;
|
||||
});
|
||||
try {
|
||||
await context.read<MainModel>().signup(nameCtl.text);
|
||||
await context.read<MainModel>().joinInvite(nameCtl.text);
|
||||
Navigator.pushNamedAndRemoveUntil(context, "/home", (r) => false);
|
||||
} catch (e) {
|
||||
showMsgDialog(context, "Error", e.toString());
|
||||
|
||||
@@ -12,6 +12,7 @@ import 'package:logging/logging.dart';
|
||||
class StaffModel extends BaseModel {
|
||||
final log = Logger('StaffModel');
|
||||
StreamSubscription<QuerySnapshot> listener;
|
||||
StreamSubscription<QuerySnapshot> privilegeListener;
|
||||
|
||||
List<User> employees = [];
|
||||
List<Privilege> privileges = [];
|
||||
@@ -25,7 +26,9 @@ class StaffModel extends BaseModel {
|
||||
@override
|
||||
logout() async {
|
||||
if (listener != null) listener.cancel();
|
||||
if (privilegeListener != null) privilegeListener.cancel();
|
||||
employees = [];
|
||||
privileges = [];
|
||||
}
|
||||
|
||||
Future<void> _loadEmployees() async {
|
||||
@@ -57,7 +60,7 @@ class StaffModel extends BaseModel {
|
||||
if (user == null || !user.hasStaffs()) return;
|
||||
|
||||
try {
|
||||
Firestore.instance
|
||||
privilegeListener = Firestore.instance
|
||||
.collection("/$privilege_collection")
|
||||
.snapshots()
|
||||
.listen((QuerySnapshot snapshot) {
|
||||
@@ -68,8 +71,6 @@ class StaffModel extends BaseModel {
|
||||
return privilege;
|
||||
}).toList();
|
||||
notifyListeners();
|
||||
}).onError((e) {
|
||||
log.warning("Error! $e");
|
||||
});
|
||||
} catch (e) {
|
||||
log.warning("Error!! $e");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:fcs/fcs/common/domain/entities/user.dart';
|
||||
import 'package:fcs/fcs/common/helpers/theme.dart';
|
||||
import 'package:fcs/fcs/common/pages/package/user_serach.dart';
|
||||
import 'package:fcs/fcs/common/pages/user_search/user_serach.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class UserListRow extends StatefulWidget {
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:fcs/fcs/common/domain/entities/user.dart';
|
||||
import 'package:fcs/fcs/common/helpers/theme.dart';
|
||||
import 'package:fcs/fcs/common/pages/package/model/package_model.dart';
|
||||
import 'package:fcs/fcs/common/pages/package/user_list_row.dart';
|
||||
import 'package:fcs/fcs/common/pages/user_search/user_list_row.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
29
lib/fcs/common/pages/widgets/display_image_source.dart
Normal file
29
lib/fcs/common/pages/widgets/display_image_source.dart
Normal file
@@ -0,0 +1,29 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
class DisplayImageSource {
|
||||
String url;
|
||||
File file;
|
||||
DisplayImageSource({this.url, this.file});
|
||||
|
||||
ImageProvider get imageProvider =>
|
||||
file == null ? NetworkImage(url) : FileImage(file);
|
||||
|
||||
@override
|
||||
bool operator ==(other) {
|
||||
if (identical(this, other)) {
|
||||
return true;
|
||||
}
|
||||
return (other.file == this.file &&
|
||||
(other.file != null || this.file != null)) ||
|
||||
(other.url == this.url && (other.url != null || this.url != null));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
int result = 17;
|
||||
result = 37 * result + file.hashCode;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
53
lib/fcs/common/pages/widgets/image_slider.dart
Normal file
53
lib/fcs/common/pages/widgets/image_slider.dart
Normal file
@@ -0,0 +1,53 @@
|
||||
import 'package:fcs/fcs/common/helpers/theme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_icons/flutter_icons.dart';
|
||||
|
||||
getImgSlider(List<String> images) {
|
||||
return Container(
|
||||
height: 130,
|
||||
width: 500,
|
||||
child: ListView.separated(
|
||||
separatorBuilder: (context, index) => Divider(
|
||||
color: Colors.black,
|
||||
),
|
||||
itemCount: images.length + 1,
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemBuilder: (context, index) {
|
||||
if (index == images.length) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Container(
|
||||
width: 200,
|
||||
height: 70,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: primaryColor,
|
||||
width: 2.0,
|
||||
),
|
||||
),
|
||||
child: Icon(SimpleLineIcons.plus),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Container(
|
||||
width: 200,
|
||||
height: 70,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: primaryColor,
|
||||
width: 2.0,
|
||||
),
|
||||
),
|
||||
child: Image.network(
|
||||
"https://lh3.googleusercontent.com/Fu9J7YpHnHK8QPr3kdAyEbTFyvB3h9Na69-j8CpQqWbMQP9sGplj7hVqQ5beKKLGgdyA8f5zIfqWdp2ITxuqlGkWDVuTyAtj_Rmw=w0",
|
||||
width: 50,
|
||||
height: 50),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
67
lib/fcs/common/pages/widgets/multi_img_controller.dart
Normal file
67
lib/fcs/common/pages/widgets/multi_img_controller.dart
Normal file
@@ -0,0 +1,67 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'display_image_source.dart';
|
||||
|
||||
typedef CallBack = void Function();
|
||||
|
||||
class MultiImgController {
|
||||
List<String> imageUrls;
|
||||
List<DisplayImageSource> addedFiles = [];
|
||||
List<DisplayImageSource> removedFiles = [];
|
||||
|
||||
List<DisplayImageSource> fileContainers = [];
|
||||
CallBack callback;
|
||||
MultiImgController() {
|
||||
fileContainers = [];
|
||||
}
|
||||
|
||||
set setImageUrls(List<String> imageUrls) {
|
||||
if (imageUrls == null) {
|
||||
return;
|
||||
}
|
||||
fileContainers.clear();
|
||||
|
||||
this.imageUrls = imageUrls;
|
||||
imageUrls.forEach((e) {
|
||||
fileContainers.add(DisplayImageSource(url: e));
|
||||
});
|
||||
if (callback != null) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
void onChange(CallBack callBack) {
|
||||
this.callback = callBack;
|
||||
}
|
||||
|
||||
set addFile(DisplayImageSource fileContainer) {
|
||||
// if (fileContainers.contains(fileContainer)) return;
|
||||
addedFiles.add(fileContainer);
|
||||
if (callback != null) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
set removeFile(DisplayImageSource fileContainer) {
|
||||
if (!fileContainers.contains(fileContainer)) return;
|
||||
fileContainers.remove(fileContainer);
|
||||
|
||||
if (addedFiles.contains(fileContainer)) {
|
||||
addedFiles.remove(fileContainer);
|
||||
}
|
||||
if (imageUrls.contains(fileContainer.url)) {
|
||||
removedFiles.add(fileContainer);
|
||||
}
|
||||
if (callback != null) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
List<File> get getAddedFile {
|
||||
return addedFiles.map((e) => e.file).toList();
|
||||
}
|
||||
|
||||
List<String> get getDeletedUrl {
|
||||
return removedFiles.map((e) => e.url).toList();
|
||||
}
|
||||
}
|
||||
266
lib/fcs/common/pages/widgets/multi_img_file.dart
Normal file
266
lib/fcs/common/pages/widgets/multi_img_file.dart
Normal file
@@ -0,0 +1,266 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:fcs/fcs/common/helpers/theme.dart';
|
||||
import 'package:fcs/fcs/common/pages/widgets/show_img.dart';
|
||||
import 'package:fcs/fcs/common/pages/widgets/show_multiple_img.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_icons/flutter_icons.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
|
||||
import 'display_image_source.dart';
|
||||
import 'multi_img_controller.dart';
|
||||
|
||||
typedef OnFile = void Function(File);
|
||||
|
||||
class MultiImageFile extends StatefulWidget {
|
||||
final String title;
|
||||
final bool enabled;
|
||||
final ImageSource imageSource;
|
||||
final MultiImgController controller;
|
||||
|
||||
const MultiImageFile(
|
||||
{Key key,
|
||||
this.title,
|
||||
this.enabled = true,
|
||||
this.controller,
|
||||
this.imageSource = ImageSource.gallery})
|
||||
: super(key: key);
|
||||
@override
|
||||
_MultiImageFileState createState() => _MultiImageFileState();
|
||||
}
|
||||
|
||||
class _MultiImageFileState extends State<MultiImageFile> {
|
||||
List<DisplayImageSource> fileContainers = [];
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
fileContainers = widget.controller.fileContainers;
|
||||
widget.controller.onChange(() {
|
||||
setState(() {
|
||||
this.fileContainers = widget.controller.fileContainers;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
height: 130,
|
||||
width: 500,
|
||||
child: ListView.separated(
|
||||
separatorBuilder: (context, index) => Divider(
|
||||
color: Colors.black,
|
||||
),
|
||||
itemCount:
|
||||
widget.enabled ? fileContainers.length + 1 : fileContainers.length,
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemBuilder: (context, index) {
|
||||
if (index == fileContainers.length) {
|
||||
return InkWell(
|
||||
onTap: () async {
|
||||
bool camera = false, gallery = false;
|
||||
await _dialog(
|
||||
context, () => camera = true, () => gallery = true);
|
||||
if (camera || gallery) {
|
||||
var selectedFile = await ImagePicker().getImage(
|
||||
source: camera ? ImageSource.camera : ImageSource.gallery,
|
||||
imageQuality: 80,
|
||||
maxWidth: 1000);
|
||||
if (selectedFile != null) {
|
||||
_fileAdded(DisplayImageSource(), File(selectedFile.path));
|
||||
}
|
||||
}
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Container(
|
||||
width: 200,
|
||||
height: 130,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: primaryColor,
|
||||
width: 2.0,
|
||||
),
|
||||
),
|
||||
child: Icon(SimpleLineIcons.plus),
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return InkWell(
|
||||
onTap: () => Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => ShowMultiImage(
|
||||
displayImageSources: fileContainers,
|
||||
)),
|
||||
),
|
||||
child: Stack(alignment: Alignment.topLeft, children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Container(
|
||||
width: 200,
|
||||
height: 130,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: primaryColor,
|
||||
width: 2.0,
|
||||
),
|
||||
),
|
||||
child: fileContainers[index].file == null
|
||||
? Image.network(fileContainers[index].url,
|
||||
width: 50, height: 50)
|
||||
: Image.file(fileContainers[index].file,
|
||||
width: 50, height: 50),
|
||||
),
|
||||
),
|
||||
widget.enabled
|
||||
? Positioned(
|
||||
top: 0,
|
||||
right: 0,
|
||||
child: Container(
|
||||
height: 50,
|
||||
width: 50,
|
||||
child: IconButton(
|
||||
icon: Icon(
|
||||
Icons.close,
|
||||
color: primaryColor,
|
||||
),
|
||||
onPressed: () =>
|
||||
{_fileRemove(fileContainers[index])}),
|
||||
),
|
||||
)
|
||||
: Container(),
|
||||
]),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_fileAdded(DisplayImageSource fileContainer, File selectedFile) {
|
||||
fileContainer.file = selectedFile;
|
||||
setState(() {
|
||||
fileContainers.add(fileContainer);
|
||||
widget.controller.addFile = fileContainer;
|
||||
});
|
||||
}
|
||||
|
||||
_fileRemove(DisplayImageSource fileContainer) {
|
||||
setState(() {
|
||||
widget.controller.removeFile = fileContainer;
|
||||
});
|
||||
}
|
||||
|
||||
Widget getFile(DisplayImageSource fileContainer, int index) {
|
||||
return Container(
|
||||
height: 40,
|
||||
padding: EdgeInsets.only(top: 5, bottom: 5),
|
||||
child: fileContainer.file == null && fileContainer.url == null
|
||||
? IconButton(
|
||||
padding: const EdgeInsets.all(3.0),
|
||||
icon: Icon(Icons.attach_file),
|
||||
onPressed: () async {
|
||||
if (!widget.enabled) return;
|
||||
bool camera = false, gallery = false;
|
||||
await _dialog(
|
||||
context, () => camera = true, () => gallery = true);
|
||||
if (camera || gallery) {
|
||||
var selectedFile = await ImagePicker().getImage(
|
||||
source: camera ? ImageSource.camera : ImageSource.gallery,
|
||||
imageQuality: 80,
|
||||
maxWidth: 1000);
|
||||
if (selectedFile != null) {
|
||||
_fileAdded(fileContainer,
|
||||
File.fromRawPath(await selectedFile.readAsBytes()));
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
: Padding(
|
||||
padding: const EdgeInsets.only(left: 3.0),
|
||||
child: InkWell(
|
||||
onTap: () => {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => ShowImage(
|
||||
imageFile: fileContainer.file,
|
||||
url: fileContainer.file == null
|
||||
? fileContainer.url
|
||||
: null,
|
||||
fileName: widget.title)),
|
||||
)
|
||||
},
|
||||
child: Chip(
|
||||
avatar: Icon(Icons.image),
|
||||
onDeleted: !widget.enabled
|
||||
? null
|
||||
: () {
|
||||
_fileRemove(fileContainer);
|
||||
},
|
||||
deleteIcon: Icon(
|
||||
Icons.close,
|
||||
),
|
||||
label: Text(widget.title + " - ${index + 1}"),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _dialog(BuildContext context, cameraPress(), photoPress()) {
|
||||
return showDialog<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
content: Container(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: <Widget>[
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
FontAwesomeIcons.camera,
|
||||
size: 30,
|
||||
color: primaryColor,
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
cameraPress();
|
||||
}),
|
||||
Text("Camera")
|
||||
],
|
||||
),
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
Icons.photo_library,
|
||||
size: 30,
|
||||
color: primaryColor,
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
photoPress();
|
||||
}),
|
||||
Text("Gallery")
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
35
lib/fcs/common/pages/widgets/show_img.dart
Normal file
35
lib/fcs/common/pages/widgets/show_img.dart
Normal file
@@ -0,0 +1,35 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:fcs/fcs/common/helpers/theme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:photo_view/photo_view.dart';
|
||||
|
||||
class ShowImage extends StatefulWidget {
|
||||
final String url;
|
||||
final File imageFile;
|
||||
final String fileName;
|
||||
const ShowImage({Key key, this.imageFile, this.fileName, this.url})
|
||||
: super(key: key);
|
||||
@override
|
||||
_ShowImageState createState() => _ShowImageState();
|
||||
}
|
||||
|
||||
class _ShowImageState extends State<ShowImage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: primaryColor,
|
||||
title: Text(widget.fileName),
|
||||
),
|
||||
body: Center(
|
||||
child: widget.url != null || widget.imageFile != null
|
||||
? PhotoView(
|
||||
imageProvider: widget.url != null
|
||||
? NetworkImage(widget.url)
|
||||
: FileImage(widget.imageFile),
|
||||
minScale: PhotoViewComputedScale.contained * 1)
|
||||
: Container()),
|
||||
);
|
||||
}
|
||||
}
|
||||
42
lib/fcs/common/pages/widgets/show_multiple_img.dart
Normal file
42
lib/fcs/common/pages/widgets/show_multiple_img.dart
Normal file
@@ -0,0 +1,42 @@
|
||||
import 'package:fcs/fcs/common/pages/widgets/display_image_source.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:photo_view/photo_view.dart';
|
||||
import 'package:photo_view/photo_view_gallery.dart';
|
||||
|
||||
class ShowMultiImage extends StatefulWidget {
|
||||
final List<DisplayImageSource> displayImageSources;
|
||||
const ShowMultiImage({Key key, this.displayImageSources}) : super(key: key);
|
||||
@override
|
||||
_ShowMultiImageState createState() => _ShowMultiImageState();
|
||||
}
|
||||
|
||||
class _ShowMultiImageState extends State<ShowMultiImage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Container(
|
||||
child: PhotoViewGallery.builder(
|
||||
scrollPhysics: const BouncingScrollPhysics(),
|
||||
builder: (BuildContext context, int index) {
|
||||
return PhotoViewGalleryPageOptions(
|
||||
imageProvider: widget.displayImageSources[index].imageProvider,
|
||||
initialScale: PhotoViewComputedScale.contained * 0.8,
|
||||
heroAttributes: PhotoViewHeroAttributes(
|
||||
tag: widget.displayImageSources[index].hashCode),
|
||||
);
|
||||
},
|
||||
itemCount: widget.displayImageSources.length,
|
||||
loadingBuilder: (context, event) => Center(
|
||||
child: Container(
|
||||
width: 20.0,
|
||||
height: 20.0,
|
||||
child: CircularProgressIndicator(
|
||||
value: event == null
|
||||
? 0
|
||||
: event.cumulativeBytesLoaded / event.expectedTotalBytes,
|
||||
),
|
||||
),
|
||||
),
|
||||
)));
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,11 @@ class AuthServiceImp implements AuthService {
|
||||
return authFb.getUser(refreshIdToken: refreshIdToken);
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<User> getUserStream(String userID) {
|
||||
return authFb.user(userID);
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<Setting> getSetting() {
|
||||
return authFb.settings();
|
||||
@@ -47,6 +52,11 @@ class AuthServiceImp implements AuthService {
|
||||
return authFb.signup(userName);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<User> joinInvite(String userName) {
|
||||
return authFb.joinInvite(userName);
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<User> onAuthStatus() {
|
||||
return authFb.onAuthStatus;
|
||||
|
||||
@@ -8,8 +8,10 @@ abstract class AuthService {
|
||||
Future<void> signout();
|
||||
Future<User> getUser({bool refreshIdToken = false});
|
||||
Future<User> signup(String userName);
|
||||
Future<User> joinInvite(String userName);
|
||||
Future<void> updateProfile(String newUserName);
|
||||
Future<bool> hasInvite();
|
||||
Stream<User> getUserStream(String userID);
|
||||
Stream<Setting> getSetting();
|
||||
Stream<User> onAuthStatus();
|
||||
Future<String> getToken();
|
||||
|
||||
Reference in New Issue
Block a user