This commit is contained in:
Sai Naw Wun
2020-10-07 02:33:06 +06:30
parent 01a2798a74
commit 65dda16fe6
475 changed files with 1543 additions and 90780 deletions

View File

@@ -1,363 +0,0 @@
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/language_model.dart';
import 'package:fcs/model/user_model.dart';
import 'package:fcs/vo/user.dart';
import 'package:fcs/widget/local_text.dart';
import 'package:fcs/widget/localization/app_translations.dart';
import 'package:fcs/widget/progress.dart';
import '../fcs/common/helpers/theme.dart' as Theme;
import '../fcs/common/pages/util.dart';
class AddPINEditor extends StatefulWidget {
final User user;
AddPINEditor(
this.user, {
Key key,
}) : super(key: key);
@override
_AddPINEditorState createState() => new _AddPINEditorState();
}
class _AddPINEditorState extends State<AddPINEditor>
with SingleTickerProviderStateMixin {
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
final FocusNode myFocusNodePassword = FocusNode();
final FocusNode myFocusNodeEmail = FocusNode();
bool _obscureTextLogin = true;
bool _obscureTextSignup = true;
bool _obscureTextSignupConfirm = true;
TextEditingController _passwordController = new TextEditingController();
TextEditingController _pinController = new TextEditingController();
final formKey = GlobalKey<FormState>();
bool _isLoading = false;
bool isSwitched = false;
@override
Widget build(BuildContext context) {
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
key: _scaffoldKey,
body: SingleChildScrollView(
child: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height >= 775.0
? MediaQuery.of(context).size.height
: 580.0,
child: Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 35.0, bottom: 10),
child: ListTile(
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () {
Navigator.of(context).pop();
},
),
title: LocalText(
context,
'change.pin.title',
color: Colors.black87,
fontSize: 17,
),
),
),
Expanded(
flex: 2,
child: PageView(
children: <Widget>[
new ConstrainedBox(
constraints: const BoxConstraints.expand(),
child: _buildReset(context),
),
],
),
),
],
),
),
),
),
);
}
@override
void dispose() {
myFocusNodePassword.dispose();
myFocusNodeEmail.dispose();
super.dispose();
}
@override
void initState() {
super.initState();
// SystemChrome.setPreferredOrientations([
// DeviceOrientation.portraitUp,
// DeviceOrientation.portraitDown,
// ]);
}
Widget _buildReset(BuildContext context) {
final switchBtnBox = Row(
children: <Widget>[
Container(
padding: EdgeInsets.only(left: 20),
child: LocalText(
context,
'pin.switch',
fontSize: 15,
)),
Switch(
value: isSwitched,
onChanged: (value) {
setState(() {
isSwitched = value;
if (!isSwitched) {
_pinController.clear();
}
});
},
activeColor: Theme.secondaryColor,
),
],
);
final pinInputBox = Padding(
padding: EdgeInsets.only(left: 25.0, right: 25.0),
child: TextFormField(
controller: _pinController,
keyboardType: TextInputType.number,
style: TextStyle(
fontFamily: "WorkSansSemiBold",
fontSize: 16.0,
color: Colors.black),
decoration: InputDecoration(
border: InputBorder.none,
icon: Image.asset(
'assets/pin.png',
width: 30,
height: 30,
),
labelText: AppTranslations.of(context).text("change.pin"),
labelStyle: Provider.of<LanguageModel>(context).isEng
? TextStyle(fontFamily: "WorkSansSemiBold", color: Colors.grey)
: TextStyle(fontFamily: "MyanmarUnicode", color: Colors.grey),
),
validator: _validatePinCode,
),
);
final passwordInputBox = Padding(
padding: EdgeInsets.only(left: 25.0, right: 25.0),
child: TextFormField(
focusNode: myFocusNodePassword,
controller: _passwordController,
obscureText: _obscureTextSignup,
style: TextStyle(
fontFamily: "WorkSansSemiBold",
fontSize: 16.0,
color: Colors.black),
decoration: InputDecoration(
border: InputBorder.none,
icon: Icon(
FontAwesomeIcons.lock,
color: Colors.black,
),
labelText: AppTranslations.of(context).text("login.password"),
labelStyle: Provider.of<LanguageModel>(context).isEng
? TextStyle(fontFamily: "WorkSansSemiBold", color: Colors.grey)
: TextStyle(fontFamily: "MyanmarUnicode", color: Colors.grey),
suffixIcon: GestureDetector(
onTap: _toggleSignup,
child: Icon(
_obscureTextSignup
? FontAwesomeIcons.eye
: FontAwesomeIcons.eyeSlash,
size: 15.0,
color: Colors.black,
),
),
),
validator: _validatePassword,
),
);
final updatePinBtn = Container(
decoration: new BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(5.0)),
color: Theme.primaryColor,
),
child: MaterialButton(
highlightColor: Colors.transparent,
splashColor: Theme.LoginColors.loginGradientEnd,
child: Padding(
padding:
const EdgeInsets.symmetric(vertical: 10.0, horizontal: 42.0),
child: LocalText(
context,
'pin.add_btn',
color: Colors.white,
fontSize: 18.0,
),
),
onPressed: () => _change(context)),
);
final clearPinBtn = Container(
// margin: EdgeInsets.only(top: 320.0),
decoration: new BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(5.0)),
color: Theme.primaryColor,
),
child: MaterialButton(
highlightColor: Colors.transparent,
splashColor: Theme.LoginColors.loginGradientEnd,
//shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5.0))),
child: Padding(
padding:
const EdgeInsets.symmetric(vertical: 10.0, horizontal: 42.0),
child: LocalText(
context,
'pin.clear_btn',
color: Colors.white,
fontSize: 18.0,
),
),
onPressed: () => _clear(context)),
);
return Container(
child: ListView(
children: <Widget>[
Column(
children: <Widget>[
Form(
key: formKey,
child: Card(
elevation: 2.0,
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
child: Container(
width: 300.0,
child: Column(
children: <Widget>[
switchBtnBox,
isSwitched ? Container() : pinInputBox,
Container(
width: 250.0,
height: 1.0,
color: Colors.grey[400],
),
passwordInputBox
],
),
),
),
),
SizedBox(
height: 15,
),
isSwitched ? Container() : updatePinBtn,
SizedBox(
height: 15,
),
!isSwitched ? Container() : clearPinBtn
],
),
],
),
);
}
void _toggleSignup() {
setState(() {
_obscureTextSignup = !_obscureTextSignup;
});
}
void _change(BuildContext context) async {
if (!formKey.currentState.validate()) {
return;
}
setState(() {
_isLoading = true;
});
UserModel userModel = Provider.of<UserModel>(context);
try {
await userModel.updatePin(_pinController.text, _passwordController.text);
Navigator.pop(context);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
Future.delayed(Duration(seconds: 1), () {
if (mounted) {
setState(() {
_isLoading = false;
});
}
});
}
}
void _clear(BuildContext context) async {
if (!formKey.currentState.validate()) {
return;
}
setState(() {
_isLoading = true;
});
UserModel userModel = Provider.of<UserModel>(context);
try {
await userModel.clearPin(_passwordController.text);
Navigator.pop(context);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
Future.delayed(Duration(seconds: 1), () {
if (mounted) {
setState(() {
_isLoading = false;
});
}
});
}
}
String _validatePassword(value) {
if (value.isEmpty) {
return AppTranslations.of(context).text("login.password_empty");
}
if (value.length < 6) {
return AppTranslations.of(context).text("login.password_size");
}
return null;
}
String _validatePinCode(value) {
if (!isSwitched) {
if (value.isEmpty) {
return AppTranslations.of(context).text("change.pin_empty");
}
if (value.length < 6 || value.length > 6) {
return AppTranslations.of(context).text("change.pin_size");
}
}
return null;
}
}

View File

@@ -1,184 +0,0 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/announcement_model.dart';
import 'package:fcs/model/main_model.dart';
import 'package:fcs/fcs/common/pages/util.dart';
import 'package:fcs/vo/announcement.dart';
import 'package:fcs/vo/popup_menu.dart';
import 'package:fcs/widget/local_text.dart';
import 'package:fcs/widget/popupmenu.dart';
import 'package:fcs/widget/progress.dart';
import 'package:zefyr/zefyr.dart';
import '../fcs/common/helpers/theme.dart';
import 'announcement_editor.dart';
class AnnouncementPage extends StatefulWidget {
final Announcement announcement;
const AnnouncementPage({Key key, this.announcement}) : super(key: key);
@override
_AnnouncementState createState() => _AnnouncementState();
}
class _AnnouncementState extends State<AnnouncementPage> {
ZefyrController _textController;
TextEditingController nameController = new TextEditingController();
FocusNode _focusNode;
NotusDocument document = new NotusDocument();
bool isLoading = false;
Announcement _announcement = new Announcement();
@override
void initState() {
super.initState();
if (widget.announcement != null) {
_announcement = widget.announcement;
nameController.text = _announcement.name;
_textController = ZefyrController(_loadDocument(_announcement));
_focusNode = FocusNode();
}
}
NotusDocument _loadDocument(Announcement announcement) {
NotusDocument doc;
try {
doc = NotusDocument.fromJson(jsonDecode(announcement.text));
} catch (e) {}
if (doc == null) {
doc = NotusDocument();
}
return doc;
}
@override
Widget build(BuildContext context) {
MainModel mainModel = Provider.of<MainModel>(context);
bool isOwnerAndAbove =
mainModel.user != null && mainModel.user.isOwnerAndAbove();
bool hasAdmin = mainModel.user != null && mainModel.user.hasAdmin();
final nameBox = Container(
padding: EdgeInsets.only(top: 20, left: 20, right: 20),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
LocalText(context, 'announcement.name'),
SizedBox(
height: 15,
),
Text(
nameController.text,
style: textStyle,
)
],
),
);
final textBox = Expanded(
child: Container(
padding: EdgeInsets.only(left: 5, right: 20),
child: ZefyrTheme(
data: ZefyrThemeData().copyWith(),
child: ZefyrScaffold(
child: ZefyrEditor(
mode: ZefyrMode.view,
padding: EdgeInsets.all(16),
controller: _textController,
focusNode: _focusNode,
),
)),
));
return LocalProgress(
inAsyncCall: isLoading,
child: Scaffold(
appBar: AppBar(
title: LocalText(context, 'announcement.form.title',
color: Colors.white, fontSize: 20),
backgroundColor: primaryColor,
actions: <Widget>[
isOwnerAndAbove || hasAdmin
? PopupMenuButton<PopupMenu>(
elevation: 3.2,
tooltip: 'This is tooltip',
onSelected: _select,
itemBuilder: (BuildContext context) {
return announcementMenu.map((PopupMenu choice) {
return PopupMenuItem<PopupMenu>(
value: choice,
child: Text(choice.status),
);
}).toList();
})
: Container()
],
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
nameBox,
Container(
padding: EdgeInsets.only(left: 10, right: 10),
child: Divider(
color: Colors.grey,
)),
Container(
padding: EdgeInsets.only(left: 20, right: 20),
child: LocalText(context, 'announcement.desc')),
textBox,
SizedBox(height: 10)
],
),
),
);
}
void _select(PopupMenu choice) async {
if (choice.index == 1) {
Announcement _anno = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
AnnouncementEditor(announcement: _announcement)),
);
if (_anno == null) return;
setState(() {
_announcement = _anno;
nameController.text = _announcement.name;
_textController = ZefyrController(_loadDocument(_announcement));
_focusNode = FocusNode();
});
} else if (choice.index == 2) {
showConfirmDialog(context, "announcement.delete_confirm", () {
_delete(context);
});
}
}
void _delete(BuildContext context) async {
setState(() {
isLoading = true;
});
try {
if (widget.announcement != null) {
await Provider.of<AnnouncementModel>(context)
.deleteAnnouncement(widget.announcement);
}
Navigator.pop(context);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
isLoading = false;
});
}
}
}

View File

@@ -1,152 +0,0 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/announcement_model.dart';
import 'package:fcs/model/language_model.dart';
import 'package:fcs/fcs/common/pages/util.dart';
import 'package:fcs/fcs/common/helpers/theme.dart';
import 'package:fcs/vo/announcement.dart';
import 'package:fcs/widget/local_text.dart';
import 'package:fcs/widget/localization/app_translations.dart';
import 'package:fcs/widget/progress.dart';
import 'package:zefyr/zefyr.dart';
class AnnouncementEditor extends StatefulWidget {
final Announcement announcement;
const AnnouncementEditor({Key key, this.announcement}) : super(key: key);
@override
_AnnouncementEditorState createState() => _AnnouncementEditorState();
}
class _AnnouncementEditorState extends State<AnnouncementEditor> {
final _formKey = GlobalKey<FormState>();
static final _scafoldKey = new GlobalKey<ScaffoldState>();
TextEditingController nameController = new TextEditingController();
bool _isLoading = false;
FocusNode _focusNode;
ZefyrController _textController;
Announcement _announcement = new Announcement();
@override
void initState() {
super.initState();
if (widget.announcement != null) {
_announcement = widget.announcement;
nameController.text = _announcement.name;
NotusDocument doc =
NotusDocument.fromJson(jsonDecode(widget.announcement.text));
_textController = ZefyrController(doc);
_focusNode = FocusNode();
} else {
_textController = ZefyrController(NotusDocument());
_focusNode = FocusNode();
}
}
@override
Widget build(BuildContext context) {
var languageModel = Provider.of<LanguageModel>(context);
final nameBox = Container(
padding: EdgeInsets.only(top: 10),
child: TextFormField(
controller: nameController,
autofocus: false,
decoration: new InputDecoration(
labelText: AppTranslations.of(context).text("announcement.name"),
labelStyle: languageModel.isEng ? labelStyle : labelStyleMM,
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: primaryColor, width: 1.0)),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: primaryColor, width: 1.0)),
),
validator: (value) {
if (value.isEmpty) {
return AppTranslations.of(context).text("announcement.name_empty");
}
return null;
},
),
);
final textEditor = ZefyrField(
height: 200.0,
decoration: InputDecoration(
labelText: AppTranslations.of(context).text('announcement.desc'),
labelStyle: languageModel.isEng ? labelStyle : labelStyleMM,
),
controller: _textController,
focusNode: _focusNode,
autofocus: false,
physics: ClampingScrollPhysics(),
);
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: AppBar(
backgroundColor: primaryColor,
title: LocalText(
context,
"announcement.form.title",
color: Colors.white,
fontSize: 20,
),
actions: <Widget>[
IconButton(
icon: Icon(Icons.save),
onPressed: () {
_save(context);
},
)
],
),
body: ZefyrScaffold(
key: _scafoldKey,
child: Padding(
padding: EdgeInsets.only(left: 20.0, right: 20.0),
child: Form(
key: _formKey,
child: ListView(
children: <Widget>[
nameBox,
SizedBox(height: 10),
textEditor,
],
)),
),
),
),
);
}
void _save(BuildContext context) {
if (!_formKey.currentState.validate()) return;
setState(() {
_isLoading = true;
});
try {
_announcement.name = nameController.text;
final contents = jsonEncode(_textController.document);
_announcement.text = contents;
if (widget.announcement != null) {
Provider.of<AnnouncementModel>(context, listen: false)
.updateAnnouncement(_announcement);
} else {
Provider.of<AnnouncementModel>(context, listen: false)
.createAnnouncement(_announcement);
}
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
Future.delayed(const Duration(milliseconds: 3000), () {
_isLoading = false;
Navigator.pop<Announcement>(context, this._announcement);
});
}
}
}

View File

@@ -1,146 +0,0 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/announcement_model.dart';
import 'package:fcs/model/main_model.dart';
import 'package:fcs/vo/announcement.dart' as Announce;
import 'package:fcs/widget/local_text.dart';
import 'package:fcs/widget/localization/app_translations.dart';
import 'package:fcs/widget/progress.dart';
import '../fcs/common/helpers/theme.dart';
import 'announcement.dart';
import 'announcement_editor.dart';
class AnnouncementList extends StatefulWidget {
@override
_AnnouncementListState createState() => _AnnouncementListState();
}
class _AnnouncementListState extends State<AnnouncementList> {
var timeFormatter = new DateFormat('KK:mm a');
var dateFormatter = new DateFormat('dd MMM');
final double dotSize = 15.0;
bool _isLoading = false;
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
AnnouncementModel announcementModel =
Provider.of<AnnouncementModel>(context);
MainModel mainModel = Provider.of<MainModel>(context);
bool isOwnerAndAbove =
mainModel.user != null && mainModel.user.isOwnerAndAbove();
bool hasAdmin = mainModel.user != null && mainModel.user.hasAdmin();
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: AppBar(
backgroundColor: primaryColor,
title: LocalText(
context,
"announcement.title",
color: Colors.white,
fontSize: 20,
)),
floatingActionButton: isOwnerAndAbove || hasAdmin
? FloatingActionButton(
backgroundColor: primaryColor,
child: Icon(Icons.add),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AnnouncementEditor()),
);
},
)
: Container(),
body: new ListView.separated(
separatorBuilder: (context, index) => Divider(
color: Colors.black,
),
scrollDirection: Axis.vertical,
padding: EdgeInsets.only(left: 15, right: 15, top: 15),
shrinkWrap: true,
itemCount: announcementModel.announcements.length,
itemBuilder: (BuildContext context, int index) {
Announce.Announcement announce =
announcementModel.announcements[index];
return InkWell(
onTap: () {
setState(() {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
AnnouncementPage(announcement: announce)),
);
});
},
child: Row(
children: <Widget>[
Expanded(
child: new Padding(
padding: const EdgeInsets.symmetric(vertical: 10.0),
child: new Row(
children: <Widget>[
new Padding(
padding: new EdgeInsets.symmetric(
horizontal: 32.0 - dotSize / 2),
child: Icon(
Icons.announcement,
color: primaryColor,
size: 30,
),
),
new Expanded(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
announce.name == null
? Container()
: new Text(
announce.name,
style: new TextStyle(
fontSize: 15.0,
color: Colors.black),
),
],
),
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(announce.time == null
? ""
: timeFormatter.format(announce.time)),
),
announce.fromToday()
? Container()
: Text(announce.time == null
? ""
: dateFormatter.format(announce.time)),
],
)
],
),
),
),
],
),
);
}),
),
);
}
}

View File

@@ -1,195 +0,0 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/main_model.dart';
import 'package:fcs/fcs/common/pages/util.dart';
import 'package:fcs/fcs/common/helpers/theme.dart';
import 'package:fcs/vo/bank_account.dart';
import 'package:fcs/widget/local_text.dart';
import 'package:fcs/widget/local_text_field.dart';
import 'package:fcs/widget/progress.dart';
class BankEdit extends StatefulWidget {
final BankAccount bankAccount;
const BankEdit({Key key, this.bankAccount}) : super(key: key);
@override
_BankEditState createState() => _BankEditState();
}
class _BankEditState extends State<BankEdit> {
TextEditingController bankNameController = new TextEditingController();
TextEditingController accountNameController = new TextEditingController();
TextEditingController accountNumberController = new TextEditingController();
bool _isLoading;
bool _isEdit;
File image;
BankAccount bankAccount;
@override
void initState() {
super.initState();
_isLoading = false;
_isEdit = widget.bankAccount != null;
bankAccount = BankAccount();
if (_isEdit) {
bankAccount = widget.bankAccount;
bankNameController.text = bankAccount.bankName;
accountNameController.text = bankAccount.accountName;
accountNumberController.text = bankAccount.accountNumber;
}
}
@override
Widget build(BuildContext context) {
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: AppBar(
title: LocalText(context, 'banks.edit.title',
color: Colors.white, fontSize: 20),
backgroundColor: primaryColor,
actions: <Widget>[
IconButton(
icon: Icon(Icons.save),
onPressed: () {
_save();
},
),
IconButton(
icon: Icon(Icons.delete),
onPressed: () {
_delete();
},
)
],
),
body: Column(
children: <Widget>[
Expanded(
child: ListView(
shrinkWrap: true,
padding: EdgeInsets.only(left: 24.0, right: 24.0),
children: <Widget>[
LocalTextField(
labelKey: "banks.name",
textEditingController: bankNameController),
LocalTextField(
labelKey: "banks.account.name",
textEditingController: accountNameController),
LocalTextField(
labelKey: "banks.account.number",
textEditingController: accountNumberController,
textInputType: TextInputType.number,
),
Padding(
padding: const EdgeInsets.all(18.0),
child: Center(
child: Stack(
children: <Widget>[
Container(
width: 80,
height: 80,
padding: const EdgeInsets.all(2.0),
decoration: BoxDecoration(
border: Border.all(
color: primaryColor,
width: 1.0,
),
),
child: image == null
? Image.network(
_isEdit ? widget.bankAccount.bankLogo : "",
height: 80,
width: 80,
)
: Image.file(image),
),
Positioned(
bottom: -10,
right: -10,
child: IconButton(
color: primaryColor,
icon: const Icon(
Icons.edit,
color: Colors.grey,
),
onPressed: () async {
File _image = await ImagePicker.pickImage(
source: ImageSource.gallery,
maxWidth: 300,
maxHeight: 300,
imageQuality: 80);
if (_image != null) {
setState(() {
this.image = _image;
});
}
}))
],
),
),
)
],
),
),
],
),
),
);
}
_save() async {
if (!_isEdit && image == null) {
showMsgDialog(context, "Error", "Need bank logo!");
return;
}
setState(() {
_isLoading = true;
});
try {
bankAccount.bankName = bankNameController.text;
bankAccount.accountName = accountNameController.text;
bankAccount.accountNumber = accountNumberController.text;
MainModel mainModel = Provider.of<MainModel>(context, listen: false);
if (_isEdit) {
await mainModel.updateBankAccount(bankAccount, image);
} else {
await mainModel.addBankAccount(bankAccount, image);
}
} catch (e) {
showMsgDialog(context, "Error", e.toString());
return;
} finally {
setState(() {
_isLoading = false;
});
}
Navigator.pop(context);
}
_delete() async {
showConfirmDialog(context, "banks.account.delete.confirmation", () async {
setState(() {
_isLoading = true;
});
try {
MainModel mainModel = Provider.of<MainModel>(context, listen: false);
if (_isEdit) {
await mainModel.deleteBankAccount(bankAccount);
}
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
Navigator.pop(context);
}
});
}
}

View File

@@ -1,259 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/main_model.dart';
import 'package:fcs/pages/banks/bank_edit.dart';
import 'package:fcs/fcs/common/helpers/theme.dart';
import 'package:fcs/vo/bank_account.dart';
import 'package:fcs/widget/local_text.dart';
import 'package:fcs/widget/progress.dart';
class BankAccounts extends StatefulWidget {
const BankAccounts({Key key}) : super(key: key);
@override
_BankAccountsState createState() => _BankAccountsState();
}
class _BankAccountsState extends State<BankAccounts> {
bool isLoading = false;
bool isEdit = false;
final TextEditingController bankNameCtl = TextEditingController();
final TextEditingController accountNameCtl = TextEditingController();
final TextEditingController accountNumberCtl = TextEditingController();
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
MainModel mainModel = Provider.of<MainModel>(context);
List<BankAccount> bankAccounts = mainModel.setting.bankAccounts;
bool isOwnerAndAbove =
mainModel.user != null && mainModel.user.isOwnerAndAbove();
bool hasAdmin = mainModel.user != null && mainModel.user.hasAdmin();
return WillPopScope(
onWillPop: () {
if (isEdit) {
setState(() {
isEdit = false;
});
return Future.value(false);
}
return Future.value(true);
},
child: LocalProgress(
inAsyncCall: isLoading,
child: Scaffold(
appBar: AppBar(
centerTitle: true,
automaticallyImplyLeading: !isEdit,
title: LocalText(context, 'banks.title',
color: Colors.white, fontSize: 20),
backgroundColor: primaryColor,
actions: <Widget>[
(isOwnerAndAbove || hasAdmin)
? isEdit
? IconButton(
icon: Icon(Icons.done),
onPressed: () {
setState(() {
isEdit = false;
});
},
)
: IconButton(
icon: Icon(Icons.edit),
onPressed: () {
_edit();
},
)
: Container()
],
),
floatingActionButton: isEdit
? FloatingActionButton(
backgroundColor: primaryColor,
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => BankEdit()),
);
},
child: const Icon(
Icons.add,
color: Colors.white,
),
)
: null,
body: new ListView.builder(
scrollDirection: Axis.vertical,
padding: EdgeInsets.only(top: 15),
shrinkWrap: true,
itemCount: bankAccounts.length,
itemBuilder: (BuildContext context, int index) {
return _item(context, bankAccounts[index]);
}),
),
),
);
}
_item(BuildContext context, BankAccount bankAccount) {
return InkWell(
onTap: isEdit
? () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => BankEdit(
bankAccount: bankAccount,
)),
)
: null,
child: Row(
children: <Widget>[
Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
padding: const EdgeInsets.all(2.0),
decoration: BoxDecoration(
border: Border.all(
color: primaryColor,
width: 1.0,
),
),
child: Image.network(
bankAccount.bankLogo,
height: 80,
width: 80,
),
),
),
],
),
Expanded(
child: new Row(
children: <Widget>[
new Expanded(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(3.0),
child: new Text(
bankAccount.bankName,
style: new TextStyle(
fontSize: 20.0, color: Colors.black),
),
),
Padding(
padding: const EdgeInsets.all(3.0),
child: new Text(
bankAccount.accountName,
style:
new TextStyle(fontSize: 16.0, color: Colors.grey),
),
),
Padding(
padding: const EdgeInsets.all(3.0),
child: Row(
children: <Widget>[
Text(
bankAccount.accountNumber,
style: new TextStyle(
fontSize: 16.0, color: Colors.grey),
),
InkWell(
onTap: () {
Clipboard.setData(ClipboardData(
text: bankAccount.accountNumber));
_showToast(context, bankAccount.bankName);
},
child: Padding(
padding: const EdgeInsets.only(left: 7.0),
child: Icon(
Icons.content_copy,
color: Colors.grey,
),
),
),
],
),
),
],
),
),
],
),
),
],
),
);
}
Future<void> _displayEditDialog(BuildContext context) async {
return showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text('Edit'),
content: Column(
children: <Widget>[
TextField(
controller: bankNameCtl,
keyboardType: TextInputType.numberWithOptions(decimal: true),
decoration: InputDecoration(hintText: "Enter Bank Name"),
),
TextField(
controller: accountNameCtl,
keyboardType: TextInputType.numberWithOptions(decimal: true),
decoration: InputDecoration(hintText: "Enter Account Name"),
),
TextField(
controller: accountNumberCtl,
keyboardType: TextInputType.number,
decoration: InputDecoration(hintText: "Enter Account Number"),
),
IconButton(icon: Icon(Icons.photo_library), onPressed: null)
],
),
actions: <Widget>[
FlatButton(
child: const Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
},
),
FlatButton(
child: const Text('Ok'),
onPressed: () {
Navigator.of(context).pop();
}),
],
);
});
}
void _edit() {
setState(() {
isEdit = true;
});
}
void _showToast(BuildContext context, String bankName) {
final scaffold = Scaffold.of(context);
scaffold.showSnackBar(
SnackBar(
content: Text('copied "$bankName" account number to clipboard'),
backgroundColor: primaryColor,
duration: Duration(seconds: 1),
),
);
}
}

View File

@@ -1,73 +0,0 @@
import 'package:barcode_scan/barcode_scan.dart';
import 'package:flutter/material.dart';
import 'package:flutter_icons/flutter_icons.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/product_model.dart';
import 'package:fcs/fcs/common/helpers/theme.dart';
import 'package:fcs/vo/buyer.dart';
import 'package:fcs/vo/product.dart';
import 'package:fcs/widget/progress.dart';
class BarcodeScreenPage extends StatefulWidget {
final BuyerProduct buyerProduct;
const BarcodeScreenPage({Key key, this.buyerProduct}) : super(key: key);
@override
_BarcodeScreenPageState createState() => _BarcodeScreenPageState();
}
class _BarcodeScreenPageState extends State<BarcodeScreenPage> {
final _formKey = GlobalKey<FormState>();
bool _isLoading = false;
String scanResult;
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: AppBar(
backgroundColor: primaryColor,
title: Text("Bar Code Scranner"),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
InkWell(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"Scan : ",
style: TextStyle(
color: primaryColor,
fontWeight: FontWeight.bold,
fontSize: 20),
),
Icon(
Ionicons.ios_qr_scanner,
size: 50,
),
],
),
onTap: () async {
await scan();
},
)
],
)),
);
}
Future scan() async {
var result = await BarcodeScanner.scan();
print("ScanResult => $result");
setState(() => scanResult = result);
}
}

View File

@@ -1,120 +0,0 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/user_model.dart';
import 'package:fcs/fcs/common/pages/util.dart';
import 'package:fcs/fcs/common/helpers/theme.dart';
import 'package:fcs/vo/popup_menu.dart';
import 'package:fcs/vo/user.dart';
import 'package:fcs/widget/local_text.dart';
import 'package:fcs/widget/popupmenu.dart';
import 'package:fcs/widget/progress.dart';
class BlockList extends StatefulWidget {
@override
_BlockListState createState() => _BlockListState();
}
class _BlockListState extends State<BlockList> {
final double dotSize = 15.0;
bool _isLoading = false;
PopupMenu selectedChoices = blocklistpopup[0];
User user = new User();
@override
Widget build(BuildContext context) {
var userModel = Provider.of<UserModel>(context);
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: AppBar(
backgroundColor: primaryColor,
title: LocalText(context, 'user.block_list',
color: Colors.white, fontSize: 20),
),
body: new ListView.builder(
padding: EdgeInsets.only(left: 15, right: 15, top: 15, bottom: 10),
shrinkWrap: true,
itemCount: userModel.getBlockListUsers().length,
itemBuilder: (BuildContext context, int index) {
return Card(
elevation: 10,
color: Colors.white,
child: Row(
children: <Widget>[
Expanded(
child: InkWell(
onTap: () {},
child: new Padding(
padding: const EdgeInsets.symmetric(vertical: 7.0),
child: new Row(
children: <Widget>[
new Padding(
padding: new EdgeInsets.symmetric(
horizontal: 32.0 - dotSize / 2),
child: Image.asset(
'assets/block.png',
width: 50,
height: 50,
color: primaryColor,
)),
new Text(
userModel.getBlockListUsers()[index].name ==
null
? ""
: userModel.getBlockListUsers()[index].name,
style: new TextStyle(
fontSize: 17.0, color: Colors.black),
),
],
),
),
),
),
PopupMenuButton<PopupMenu>(
elevation: 3.2,
tooltip: 'This is tooltip',
onSelected: _select,
itemBuilder: (BuildContext context) {
this.user = userModel.getBlockListUsers()[index];
return blocklistpopup.map((PopupMenu choice) {
return PopupMenuItem<PopupMenu>(
value: choice,
child: Text(choice.status),
);
}).toList();
})
],
),
);
}),
),
);
}
void _select(PopupMenu choice) async {
selectedChoices = choice;
if (choice.index == 1) {
showConfirmDialog(context, "user.unblock.confirm", () {
_unblock();
});
}
}
_unblock() async {
setState(() {
_isLoading = true;
});
try {
var userModel = Provider.of<UserModel>(context);
await userModel.unblockPhone(this.user.phoneNumber);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
}

View File

@@ -0,0 +1,557 @@
import 'package:fcs/domain/entities/box.dart';
import 'package:fcs/domain/entities/cargo.dart';
import 'package:fcs/domain/entities/package.dart';
import 'package:fcs/domain/vo/shipping_address.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/localization/app_translations.dart';
import 'package:fcs/pages/main/model/main_model.dart';
import 'package:fcs/pages/shipment_address/model/shipment_address_model.dart';
import 'package:fcs/pages/shipment_address/shipping_address_row.dart';
import 'package:fcs/pages/main/util.dart';
import 'package:fcs/pages/widgets/local_text.dart';
import 'package:fcs/pages/widgets/my_data_table.dart';
import 'package:fcs/pages/widgets/progress.dart';
import 'package:flutter/material.dart';
import 'package:flutter_icons/flutter_icons.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.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 BoxEditor extends StatefulWidget {
final Box box;
BoxEditor({this.box});
@override
_BoxEditorState createState() => _BoxEditorState();
}
class _BoxEditorState extends State<BoxEditor> {
TextEditingController _addressEditingController = new TextEditingController();
TextEditingController _fromTimeEditingController =
new TextEditingController();
TextEditingController _toTimeEditingController = new TextEditingController();
TextEditingController _noOfPackageEditingController =
new TextEditingController();
TextEditingController _weightEditingController = new TextEditingController();
Box _box;
bool _isLoading = false;
List<String> _images = [
"assets/photos/1.jpg",
"assets/photos/2.jpg",
"assets/photos/3.jpg"
];
bool isNew;
bool isMixBox = false;
ShippingAddress _shippingAddress = new ShippingAddress();
@override
void initState() {
super.initState();
if (widget.box != null) {
_box = widget.box;
_shippingAddress = _box.shippingAddress;
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 {
List<Package> packages = [
// PackageModel.packages[0],
// PackageModel.packages[1],
// PackageModel.packages[2]
];
List<Cargo> _cargoTypes = [
Cargo(type: 'General Cargo', weight: 25),
Cargo(type: 'Medicine', weight: 20),
Cargo(type: 'Dangerous Cargo', weight: 30)
];
var shipmentModel =
Provider.of<ShipmentAddressModel>(context, listen: false);
_shippingAddress = shipmentModel.shippingAddresses[1];
isNew = true;
_box = Box(
rate: 0,
weight: 75,
width: 0,
height: 0,
length: 0,
packages: packages,
cargoTypes: _cargoTypes,
shipmentWeight: 0);
}
}
@override
void dispose() {
super.dispose();
}
final DateFormat dateFormat = DateFormat("d MMM yyyy");
List<TimelineModel> _models() {
// return [];
return _box.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 mainModel = Provider.of<MainModel>(context);
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("box.edit.title")),
),
body: Card(
child: Column(
children: <Widget>[
widget.box == null
? Center(
child: Container(
padding: EdgeInsets.all(8), child: Text("New Box")))
: Center(child: nameWidget(_box.packageNumber)),
Expanded(
child: ListView(
children: [
ExpansionTile(
title: Text(
'Shipment Information',
style: TextStyle(
color: primaryColor, fontWeight: FontWeight.bold),
),
children: [
Padding(
padding: const EdgeInsets.only(left: 20.0, right: 20),
child: DropdownButtonFormField(
value: _box.shipmentNumber,
decoration: InputDecoration(
fillColor: Colors.white,
labelText: 'FCS Shipment Number',
icon: Icon(Ionicons.ios_airplane,
color: primaryColor)
// prefixIcon: Icon(Icons.play_arrow)
),
items: ["A102", "A103", "A201", "A202"]
.map((e) =>
DropdownMenuItem(child: Text(e), value: e))
.toList(),
onChanged: (map) => {},
),
),
Padding(
padding: const EdgeInsets.only(left: 8.0, right: 20),
child: new Row(
children: <Widget>[
new Checkbox(
value: isMixBox,
activeColor: primaryColor,
onChanged: (bool value) {
setState(() {
isMixBox = value;
});
}),
SizedBox(
width: 5,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Text(
"Mix Box",
style: TextStyle(fontSize: 15.0),
),
],
),
],
),
),
Padding(
padding: const EdgeInsets.only(left: 20.0, right: 20),
child: TextFormField(
initialValue:
isNew ? "FCS-0203-390-2" : "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: _box.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: () {})),
),
),
SizedBox(
height: 30,
)
],
),
ExpansionTile(
title: Text(
'Cargo Types',
style: TextStyle(
color: primaryColor, fontWeight: FontWeight.bold),
),
children: [
// Padding(
// padding: const EdgeInsets.only(left: 20.0, right: 20),
// child: DropdownButtonFormField(
// value: _box.packageType,
// decoration: InputDecoration(
// fillColor: Colors.white,
// labelText: 'Cargo Type',
// icon: Icon(Entypo.box, color: primaryColor)),
// items: ["General", "Medicine", "Dangerous"]
// .map((e) =>
// DropdownMenuItem(child: Text(e), value: e))
// .toList(),
// onChanged: (map) => {},
// ),
// ),
Padding(
padding: const EdgeInsets.only(left: 20.0, right: 20),
child: TextFormField(
initialValue: _box.weight.toString(),
textAlign: TextAlign.end,
decoration: InputDecoration(
fillColor: Colors.white,
labelText: 'Actual Weight',
filled: true,
icon: Icon(FontAwesomeIcons.weightHanging,
color: primaryColor),
)),
),
// Padding(
// padding: const EdgeInsets.only(left: 20.0, right: 20),
// child: TextFormField(
// initialValue: _box.rate.toString(),
// textAlign: TextAlign.end,
// decoration: InputDecoration(
// fillColor: Colors.white,
// labelText: 'Rate',
// filled: true,
// icon: Icon(FontAwesomeIcons.tag,
// color: primaryColor),
// )),
// ),
// Padding(
// padding: const EdgeInsets.only(left: 20.0, right: 20),
// child: TextFormField(
// initialValue: _box.amount.toString(),
// textAlign: TextAlign.end,
// decoration: InputDecoration(
// fillColor: Colors.white,
// labelText: 'Total Amount',
// filled: true,
// icon: Icon(FontAwesomeIcons.moneyBill,
// color: primaryColor),
// )),
// ),
Container(
padding: EdgeInsets.only(top: 10),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: MyDataTable(
headingRowHeight: 40,
columnSpacing: 120,
columns: [
MyDataColumn(
label: LocalText(
context,
"cargo.type",
color: Colors.grey,
),
),
MyDataColumn(
label: LocalText(
context,
"cargo.weight",
color: Colors.grey,
),
),
],
rows: getCargoRows(context),
),
),
),
true
? Container(
padding: EdgeInsets.only(top: 20),
child: Align(
alignment: Alignment.bottomRight,
child: FloatingActionButton.extended(
icon: Icon(Icons.add),
label: Text("Add Cargo"),
backgroundColor: primaryColor,
onPressed: () {
// Navigator.of(context).push(
// BottomUpPageRoute(PackageAddition()));
},
),
),
)
: Container(),
SizedBox(height: 25),
],
),
ExpansionTile(
title: Text(
'Box Dimension',
style: TextStyle(
color: primaryColor, fontWeight: FontWeight.bold),
),
children: [
Padding(
padding: const EdgeInsets.only(left: 20.0, right: 20),
child: fcsInputReadOnly(
"Shipment Weight", FontAwesomeIcons.weightHanging,
value: _box.shipmentWeight.toString()),
),
Padding(
padding: const EdgeInsets.only(left: 20.0, right: 20),
child: TextFormField(
initialValue: _box.width.toString(),
textAlign: TextAlign.end,
decoration: InputDecoration(
fillColor: Colors.white,
labelText: 'Width',
filled: true,
icon: Icon(FontAwesomeIcons.arrowCircleRight,
color: primaryColor),
)),
),
Padding(
padding: const EdgeInsets.only(left: 20.0, right: 20),
child: TextFormField(
initialValue: _box.height.toString(),
textAlign: TextAlign.end,
decoration: InputDecoration(
fillColor: Colors.white,
labelText: 'Height',
filled: true,
icon: Icon(FontAwesomeIcons.arrowAltCircleUp,
color: primaryColor),
)),
),
Padding(
padding: const EdgeInsets.only(left: 20.0, right: 20),
child: TextFormField(
initialValue: _box.length.toString(),
textAlign: TextAlign.end,
decoration: InputDecoration(
fillColor: Colors.white,
labelText: 'Length',
filled: true,
icon: Icon(FontAwesomeIcons.arrowCircleUp,
color: primaryColor),
)),
),
SizedBox(height: 25),
],
),
ExpansionTile(
title: Text(
'Shipping Address',
style: TextStyle(
color: primaryColor, fontWeight: FontWeight.bold),
),
children: [
ShippingAddressRow(shippingAddress: _shippingAddress),
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: () {},
icon: Icon(Icons.add),
label: Text(
'Select \nAddress',
style: TextStyle(fontSize: 12),
),
backgroundColor: primaryColor,
),
),
),
),
SizedBox(height: 25),
],
),
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),
),
],
)
],
),
),
widget.box == null
? Align(
alignment: Alignment.bottomCenter,
child: Center(
child: Container(
width: 250,
child: FlatButton(
child: Text('Create New Box'),
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 packing'),
color: primaryColor,
textColor: Colors.white,
onPressed: () {
Navigator.pop(context);
},
),
))),
widget.box.status == 'Arrived'
? Align(
alignment: Alignment.bottomCenter,
child: Center(
child: Container(
width: 250,
child: FlatButton(
child: Text('Deliver'),
color: primaryColor,
textColor: Colors.white,
onPressed: () {
Navigator.pop(context);
},
),
)))
: Container(),
],
)),
],
),
),
),
);
}
List<MyDataRow> getCargoRows(BuildContext context) {
if (_box == null || _box.cargoTypes == null) {
return [];
}
return _box.cargoTypes.map((c) {
return MyDataRow(
onSelectChanged: (bool selected) {},
cells: [
MyDataCell(new Text(
c.type == null ? "" : c.type,
style: textStyle,
)),
MyDataCell(
new Text(c.weight == null ? "0" : c.weight.toString(),
style: textStyle),
),
],
);
}).toList();
}
List<Widget> getAddressList(
BuildContext context, List<ShippingAddress> addresses) {
return addresses.asMap().entries.map((s) {
return InkWell(
onTap: () {},
child: ShippingAddressRow(shippingAddress: s.value, index: s.key),
);
}).toList();
}
}

137
lib/pages/box/box_list.dart Normal file
View File

@@ -0,0 +1,137 @@
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/localization/app_translations.dart';
import 'package:fcs/pages/box/model/box_model.dart';
import 'package:fcs/pages/widgets/bottom_up_page_route.dart';
import 'package:fcs/pages/widgets/progress.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'box_editor.dart';
import 'box_list_row.dart';
class BoxList extends StatefulWidget {
@override
_BoxListState createState() => _BoxListState();
}
class _BoxListState extends State<BoxList> {
bool _isLoading = false;
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return LocalProgress(
inAsyncCall: _isLoading,
child: DefaultTabController(
length: 2,
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("boxes.title")),
actions: <Widget>[
IconButton(
icon: Icon(
Icons.search,
color: Colors.white,
),
iconSize: 30,
// onPressed: () => showPlacesSearch(context),
),
],
bottom: TabBar(
unselectedLabelColor: Colors.grey,
tabs: [
Tab(
text: "Upcoming",
),
Tab(text: "Delivered"),
],
),
),
floatingActionButton: FloatingActionButton.extended(
onPressed: () {
_newPickup();
},
icon: Icon(Icons.add),
label: Text(AppTranslations.of(context).text("boxes.new")),
backgroundColor: primaryColor,
),
body: TabBarView(
children: [
_upComing(),
_completed(),
],
)),
),
);
}
_newPickup() {
Navigator.push(
context,
BottomUpPageRoute(BoxEditor()),
);
}
Widget _upComing() {
var boxModel = Provider.of<BoxModel>(context);
return Column(
children: <Widget>[
Expanded(
child: new ListView.separated(
separatorBuilder: (context, index) => Divider(
color: Colors.black,
),
scrollDirection: Axis.vertical,
padding: EdgeInsets.only(top: 15),
shrinkWrap: true,
itemCount: boxModel.upcoming.length,
itemBuilder: (BuildContext context, int index) {
return BoxListRow(
box: boxModel.upcoming[index],
isReadOnly: false,
);
}),
),
],
);
}
Widget _completed() {
var boxModel = Provider.of<BoxModel>(context);
return Column(
children: <Widget>[
Expanded(
child: new ListView.separated(
separatorBuilder: (context, index) => Divider(
color: Colors.black,
),
scrollDirection: Axis.vertical,
padding: EdgeInsets.only(top: 15),
shrinkWrap: true,
itemCount: boxModel.completed.length,
itemBuilder: (BuildContext context, int index) {
return BoxListRow(
box: boxModel.completed[index],
isReadOnly: false,
);
}),
),
],
);
}
}

View File

@@ -0,0 +1,115 @@
import 'package:fcs/domain/entities/box.dart';
import 'package:fcs/pages/main/util.dart';
import 'package:fcs/pages/widgets/bottom_up_page_route.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'box_editor.dart';
class BoxListRow extends StatefulWidget {
final bool isReadOnly;
final Box box;
const BoxListRow({this.box, this.isReadOnly});
@override
_BoxListRowState createState() => _BoxListRowState();
}
class _BoxListRowState extends State<BoxListRow> {
final double dotSize = 15.0;
Box _box = new Box();
final DateFormat dateFormat = new DateFormat("dd MMM yyyy");
@override
void initState() {
super.initState();
_box = widget.box;
}
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.only(left: 15, right: 15),
child: InkWell(
onTap: () {
if (widget.isReadOnly) {
// Navigator.push(
// context,
// BottomUpPageRoute(PackageInfo(package: _box)),
// );
} else {
Navigator.push(
context,
BottomUpPageRoute(BoxEditor(box: _box)),
);
}
},
child: Row(
children: <Widget>[
Expanded(
child: new Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: new Row(
children: <Widget>[
new Expanded(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(left: 8.0),
child: new Text(
_box.packageNumber == null
? ''
: _box.packageNumber,
style: new TextStyle(
fontSize: 15.0, color: Colors.black),
),
),
Padding(
padding: const EdgeInsets.only(left: 10.0, top: 10),
child: new Text(
dateFormat.format(_box.arrivedDate),
style: new TextStyle(
fontSize: 15.0, color: Colors.grey),
),
)
],
),
),
],
),
),
),
Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(0),
child: getStatus(_box.status),
),
Padding(
padding: const EdgeInsets.only(left: 8.0, top: 5, bottom: 5),
child: Row(
children: <Widget>[
new Text(
_box.weight == null
? ''
: _box.weight.toString() + 'lb - ',
style:
new TextStyle(fontSize: 15.0, color: Colors.grey),
),
new Text(
_box.price == null ? "" : "\$ " + _box.price.toString(),
style:
new TextStyle(fontSize: 15.0, color: Colors.grey),
),
],
),
),
],
)
],
),
),
);
}
}

View File

@@ -0,0 +1,307 @@
import 'dart:async';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:fcs/domain/entities/box.dart';
import 'package:fcs/domain/entities/cargo.dart';
import 'package:fcs/domain/entities/package.dart';
import 'package:fcs/domain/vo/shipment_status.dart';
import 'package:fcs/domain/vo/shipping_address.dart';
import 'package:fcs/pages/main/model/base_model.dart';
import 'package:logging/logging.dart';
class BoxModel extends BaseModel {
final log = Logger('BoxModel');
StreamSubscription<QuerySnapshot> listener;
static List<ShipmentStatus> statusHistory = [
ShipmentStatus(status: "Packed", date: DateTime(2020, 6, 1), done: true),
ShipmentStatus(status: "Shipped", date: DateTime(2020, 6, 5), done: false),
ShipmentStatus(
status: "Delivered", date: DateTime(2020, 6, 15), done: false)
];
static List<Package> packages = [
// PackageModel.packages[0],
// PackageModel.packages[1],
// PackageModel.packages[2]
];
List<Box> boxes = [
Box(
shipmentNumber: "A202",
receiverNumber: "3",
receiverName: "Ko Myo Min",
boxNumber: "1",
rate: 7,
packageType: "General",
weight: 75,
status: "Packed",
receiverAddress: '1 Bo Yar Nyunt St.\nDagon Tsp, Yangon',
cargoDesc: "Clothes",
arrivedDate: DateTime(2020, 6, 1),
width: 10,
height: 10,
length: 10,
shipmentWeight: 6,
packages: packages,
shipmentHistory: statusHistory,
shippingAddress: ShippingAddress(
fullName: 'U Nyi Nyi',
addressLine1: '154-19 64th Ave.',
addressLine2: 'Flushing',
city: 'NY',
state: 'NY',
phoneNumber: '+1 (292)215-2247'),
cargoTypes: [
Cargo(type: 'General Cargo', weight: 25),
Cargo(type: 'Medicine', weight: 20),
Cargo(type: 'Dangerous Cargo', weight: 30)
]),
Box(
shipmentNumber: "A202",
receiverNumber: "3",
receiverName: "Ko Myo Min",
boxNumber: "2",
rate: 7,
packageType: "General",
weight: 75,
status: "Packed",
cargoDesc: "Clothes",
arrivedDate: DateTime(2020, 6, 1),
width: 10,
height: 20,
length: 30,
shipmentWeight: 36,
shipmentHistory: statusHistory,
packages: packages,
receiverAddress: '1 Bo Yar Nyunt St.\nDagon Tsp, Yangon',
shippingAddress: ShippingAddress(
fullName: 'Mg Myo',
addressLine1: '153-154 5th Thitsar.',
addressLine2: 'South Okkalapa Township',
city: 'Yangon',
state: 'Myanmar',
phoneNumber: '+09 95724 8750'),
cargoTypes: [
Cargo(type: 'General Cargo', weight: 25),
Cargo(type: 'Medicine', weight: 20),
Cargo(type: 'Dangerous Cargo', weight: 30)
]),
Box(
shipmentNumber: "A202",
receiverNumber: "3",
receiverName: "Ko Myo Min",
boxNumber: "3",
rate: 7,
packageType: "General",
weight: 75,
cargoDesc: "Shoes",
status: "Packed",
arrivedDate: DateTime(2020, 6, 1),
width: 10,
height: 10,
length: 10,
shipmentWeight: 6,
shipmentHistory: statusHistory,
packages: packages,
receiverAddress: '1 Bo Yar Nyunt St.\nDagon Tsp, Yangon',
shippingAddress: ShippingAddress(
fullName: 'Mg Myo',
addressLine1: '153-154 5th Thitsar.',
addressLine2: 'South Okkalapa Township',
city: 'Yangon',
state: 'Myanmar',
phoneNumber: '+09 95724 8750'),
cargoTypes: [
Cargo(type: 'General Cargo', weight: 25),
Cargo(type: 'Medicine', weight: 20),
Cargo(type: 'Dangerous Cargo', weight: 30)
]),
Box(
shipmentNumber: "A202",
receiverNumber: "2",
receiverName: "Ma Aye",
boxNumber: "1",
rate: 8,
packageType: "Medicine",
weight: 75,
status: "Packed",
cargoDesc: "Dietary supplement",
arrivedDate: DateTime(2020, 6, 1),
width: 10,
height: 10,
length: 10,
shipmentWeight: 6,
shipmentHistory: statusHistory,
packages: packages,
receiverAddress: '2 Shwe Taung Kyar St, Bahan Tsp, Yangon',
shippingAddress: ShippingAddress(
fullName: 'U Nyi Nyi',
addressLine1: '154-19 64th Ave.',
addressLine2: 'Flushing',
city: 'NY',
state: 'NY',
phoneNumber: '+1 (292)215-2247'),
cargoTypes: [
Cargo(type: 'General Cargo', weight: 25),
Cargo(type: 'Medicine', weight: 20),
Cargo(type: 'Dangerous Cargo', weight: 30)
]),
Box(
shipmentNumber: "A202",
receiverNumber: "2",
receiverName: "Ma Aye",
boxNumber: "3",
rate: 7,
packageType: "General",
cargoDesc: "Handbags",
weight: 75,
status: "Arrived",
arrivedDate: DateTime(2020, 6, 1),
width: 10,
height: 10,
length: 10,
shipmentWeight: 6,
shipmentHistory: statusHistory,
packages: packages,
receiverAddress: '2 Shwe Taung Kyar St, Bahan Tsp, Yangon',
shippingAddress: ShippingAddress(
fullName: 'U Nyi Nyi',
addressLine1: '154-19 64th Ave.',
addressLine2: 'Flushing',
city: 'NY',
state: 'NY',
phoneNumber: '+1 (292)215-2247'),
cargoTypes: [
Cargo(type: 'General Cargo', weight: 25),
Cargo(type: 'Medicine', weight: 20),
Cargo(type: 'Dangerous Cargo', weight: 30)
]),
Box(
shipmentNumber: "A202",
receiverNumber: "2",
receiverName: "Ma Aye",
boxNumber: "2",
rate: 7,
packageType: "General",
cargoDesc: "Handbags",
weight: 75,
status: "Shipped",
arrivedDate: DateTime(2020, 6, 1),
width: 10,
height: 10,
length: 10,
shipmentWeight: 6,
shipmentHistory: statusHistory,
packages: packages,
receiverAddress: '2 Shwe Taung Kyar St, Bahan Tsp, Yangon',
shippingAddress: ShippingAddress(
fullName: 'U Nyi Nyi',
addressLine1: '154-19 64th Ave.',
addressLine2: 'Flushing',
city: 'NY',
state: 'NY',
phoneNumber: '+1 (292)215-2247'),
cargoTypes: [
Cargo(type: 'General Cargo', weight: 25),
Cargo(type: 'Medicine', weight: 20),
Cargo(type: 'Dangerous Cargo', weight: 30)
]),
Box(
shipmentNumber: "A201",
receiverNumber: "1",
receiverName: "Ko Wai",
boxNumber: "1",
rate: 9,
packageType: "Dangerous",
cargoDesc: "Phones and Scooters",
weight: 75,
status: "Delivered",
arrivedDate: DateTime(2020, 5, 21),
width: 10,
height: 10,
length: 10,
shipmentWeight: 6,
shipmentHistory: statusHistory,
packages: packages,
receiverAddress: '3 Kambzwza St, Bahan Tsp, Yangon',
shippingAddress: ShippingAddress(
fullName: 'U Nyi Nyi',
addressLine1: '154-19 64th Ave.',
addressLine2: 'Flushing',
city: 'NY',
state: 'NY',
phoneNumber: '+1 (292)215-2247'),
cargoTypes: [
Cargo(type: 'General Cargo', weight: 25),
Cargo(type: 'Medicine', weight: 20),
Cargo(type: 'Dangerous Cargo', weight: 30)
]),
Box(
shipmentNumber: "A201",
receiverNumber: "1",
receiverName: "Ko Wai",
boxNumber: "2",
rate: 7,
packageType: "General",
cargoDesc: "Construction tools",
weight: 75,
status: "Delivered",
arrivedDate: DateTime(2020, 5, 21),
width: 10,
height: 10,
length: 10,
shipmentWeight: 6,
shipmentHistory: statusHistory,
packages: packages,
receiverAddress: '3 Kambzwza St, Bahan Tsp, Yangon',
shippingAddress: ShippingAddress(
fullName: 'U Nyi Nyi',
addressLine1: '154-19 64th Ave.',
addressLine2: 'Flushing',
city: 'NY',
state: 'NY',
phoneNumber: '+1 (292)215-2247'),
cargoTypes: [
Cargo(type: 'General Cargo', weight: 25),
Cargo(type: 'Medicine', weight: 20),
Cargo(type: 'Dangerous Cargo', weight: 30)
]),
];
List<Box> get completed {
return boxes.where((e) => e.status == "Delivered").toList()
..sort((e1, e2) {
return e2.packageNumber.compareTo(e1.packageNumber);
});
}
List<Box> get processed {
return boxes.where((e) => e.status == "Packed").toList()
..sort((e1, e2) {
return e2.packageNumber.compareTo(e1.packageNumber);
});
}
List<Box> get upcoming {
return boxes
.where((e) =>
e.status == "Packed" ||
// e.status == "Received" ||
e.status == "Shipped" ||
e.status == "Arrived")
.toList()
..sort((e1, e2) {
return e2.packageNumber.compareTo(e1.packageNumber);
});
}
void initUser(user) {
super.initUser(user);
}
@override
logout() async {
if (listener != null) await listener.cancel();
boxes = [];
}
}

View File

@@ -1,281 +0,0 @@
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';
import '../fcs/common/helpers/theme.dart';
import 'document_log_page.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;
});
}
}
}

View File

@@ -1,208 +0,0 @@
import 'package:provider/provider.dart';
import 'package:fcs/model/buyer_model.dart';
import 'package:fcs/pages/search_page.dart';
import 'package:fcs/vo/buyer.dart';
import 'package:fcs/vo/popup_menu.dart';
import 'package:fcs/widget/localization/app_translations.dart';
import 'package:fcs/widget/popupmenu.dart';
import 'package:flutter/material.dart';
import 'package:fcs/widget/progress.dart';
import '../fcs/common/helpers/theme.dart';
import 'buyer_list_row.dart';
class BuyerList extends StatefulWidget {
@override
_BuyerListState createState() => _BuyerListState();
}
class _BuyerListState extends State<BuyerList> {
Buyer buyer;
int _selectedIndex = 0;
bool _isLoading = false;
int _selectedSortIndex;
@override
void initState() {
super.initState();
var buyerModel = Provider.of<BuyerModel>(context, listen: false);
var index = buyerModel.popupMenu.index;
_selectedIndex = index;
var sortIndexndex = buyerModel.sortMenu.index;
_selectedSortIndex = sortIndexndex;
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
BuyerModel buyerModel = Provider.of<BuyerModel>(context);
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: AppBar(
backgroundColor: primaryColor,
title: Text(AppTranslations.of(context).text("buyer.title")),
actions: <Widget>[
IconButton(
icon: Icon(
Icons.search,
color: Colors.white,
),
iconSize: 30,
onPressed: () => showPlacesSearch(context),
),
PopupMenuButton<PopupMenu>(
elevation: 3.2,
onSelected: (selected) {
setState(() {
this._selectedSortIndex = selected.index;
this._selectedIndex = 0;
buyerModel.filterSorting(
_selectedSortIndex, this._selectedIndex);
});
},
icon: Container(
width: 30,
height: 30,
decoration: new BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
),
child: Stack(
fit: StackFit.expand,
children: <Widget>[
Icon(
Icons.sort,
color: primaryColor,
),
_selectedSortIndex != null
? Positioned(
bottom: 0,
right: 0,
child: Container(
width: 10,
height: 10,
decoration: new BoxDecoration(
shape: BoxShape.circle,
color: secondaryColor,
),
),
)
: Container()
],
)),
itemBuilder: (BuildContext context) {
return userMenu.map((PopupMenu choice) {
return PopupMenuItem<PopupMenu>(
value: choice,
child: Container(
padding: EdgeInsets.only(left: 8),
child: Row(
children: <Widget>[
Text(choice.status),
SizedBox(
width: 10,
),
_selectedSortIndex != null &&
_selectedSortIndex == choice.index
? Icon(
Icons.check,
color: Colors.grey,
)
: Container(),
],
),
),
);
}).toList();
}),
PopupMenuButton<PopupMenu>(
elevation: 3.2,
onSelected: (selected) {
String status;
setState(() {
this._selectedIndex = selected.index;
this._selectedSortIndex = null;
if (selected.status == 'All') {
status = null;
} else {
status = selected.status;
}
buyerModel.filterStatus(
status, _selectedIndex, this._selectedSortIndex);
});
},
icon: Container(
width: 30,
height: 30,
decoration: new BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
),
child: Stack(
fit: StackFit.expand,
children: <Widget>[
Icon(
Icons.filter_list,
color: primaryColor,
),
_selectedIndex != 0
? Positioned(
bottom: 0,
right: 0,
child: Container(
width: 10,
height: 10,
decoration: new BoxDecoration(
shape: BoxShape.circle,
color: secondaryColor,
),
),
)
: Container()
],
)),
itemBuilder: (BuildContext context) {
return buyerStatusMenu.map((PopupMenu choice) {
return PopupMenuItem<PopupMenu>(
value: choice,
child: Row(
children: <Widget>[
Text(choice.status),
SizedBox(
width: 10,
),
_selectedIndex != null &&
_selectedIndex == choice.index
? Icon(
Icons.check,
color: Colors.grey,
)
: Container(),
],
),
);
}).toList();
}),
],
),
body: new ListView.builder(
scrollDirection: Axis.vertical,
padding: EdgeInsets.only(top: 15),
shrinkWrap: true,
itemCount: buyerModel.buyers.length,
itemBuilder: (BuildContext context, int index) {
return BuyerListRow(
buyer: buyerModel.buyers[index],
);
}),
),
);
}
}

View File

@@ -1,106 +0,0 @@
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';
import 'buyer_info.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),
),
],
)
],
),
),
),
);
}
}

View File

@@ -0,0 +1,132 @@
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/pages/main/model/main_model.dart';
import 'package:fcs/pages/main/util.dart';
import 'package:fcs/pages/widgets/display_text.dart';
import 'package:fcs/pages/widgets/fcs_id_icon.dart';
import 'package:fcs/pages/widgets/local_text.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class BuyingOnlinePage extends StatefulWidget {
@override
_BuyingOnlinePagetate createState() => _BuyingOnlinePagetate();
}
class _BuyingOnlinePagetate extends State<BuyingOnlinePage>
with SingleTickerProviderStateMixin {
TabController _tabController;
@override
void initState() {
_tabController = TabController(vsync: this, length: 2);
super.initState();
}
@override
Widget build(BuildContext context) {
MainModel mainModel = Provider.of<MainModel>(context);
final phoneNumberBox = DisplayText(
text: mainModel.user.phone,
labelText: getLocalString(context, "contact.phone"),
iconData: Icons.location_on,
);
final nameBox = Center(
child: Text(
mainModel.user.name,
style: TextStyle(fontSize: 18, color: primaryColor),
));
final fcsIdBox = DisplayText(
text: mainModel.user.fcsID,
labelText: getLocalString(context, "customer.fcs.id"),
icon: FcsIDIcon(),
);
final shippingAddressBox = DisplayText(
text: mainModel.setting.usaAddress,
labelText: getLocalString(context, "profile.usa.shipping.address"),
iconData: Icons.location_on,
);
final instructionBox = Container(
padding: EdgeInsets.only(left: 10, top: 30, bottom: 10),
child: Center(
child: Wrap(
children: <Widget>[
LocalText(
context,
'buy_online.buying_instruction',
color: labelColor,
fontSize: 15,
)
],
),
),
);
return Scaffold(
appBar: AppBar(
centerTitle: true,
leading: new IconButton(
icon: new Icon(
Icons.close,
color: primaryColor,
),
onPressed: () => Navigator.of(context).pop(),
),
title: LocalText(
context,
"buy_online.title",
fontSize: 20,
color: primaryColor,
),
backgroundColor: Colors.white,
shadowColor: Colors.transparent,
),
body: ListView(
shrinkWrap: true,
padding: EdgeInsets.only(top: 10, left: 10, right: 10),
children: <Widget>[
nameBox,
phoneNumberBox,
fcsIdBox,
shippingAddressBox,
instructionBox,
new Container(
decoration: new BoxDecoration(color: Colors.white),
child: new TabBar(
// indicatorColor: primaryColor,
labelColor: primaryColor,
labelStyle: TextStyle(fontWeight: FontWeight.bold),
unselectedLabelColor: Colors.grey,
controller: _tabController,
tabs: [
LocalText(context, "buy_online.fullname", color: primaryColor),
LocalText(context, "buy_online.first.last",
color: primaryColor),
],
),
),
new Container(
padding: EdgeInsets.only(top: 10),
height: 500,
width: 500,
child: new TabBarView(
controller: _tabController,
children: <Widget>[
Container(
child: Image.asset('assets/buying_online_with_full_name.png',
fit: BoxFit.contain),
),
Container(
child: Image.asset(
'assets/buying_online_with_first_last_name.png',
fit: BoxFit.contain),
),
],
),
),
SizedBox(height: 10)
],
),
);
}
}

View File

@@ -1,216 +0,0 @@
import 'package:fcs/fcs/common/pages/util.dart';
import 'package:fcs/vo/manual.dart';
import 'package:fcs/widget/bottom_up_page_route.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/main_model.dart';
import 'package:fcs/widget/local_text.dart';
import 'package:fcs/widget/progress.dart';
import '../fcs/common/helpers/theme.dart';
import '../widget/label_widgets.dart';
import '../widget/local_text.dart';
import 'instruction.dart';
import 'manual/manual_page.dart';
class BuyingOnlinePage extends StatefulWidget {
@override
_BuyingOnlinePagetate createState() => _BuyingOnlinePagetate();
}
class _BuyingOnlinePagetate extends State<BuyingOnlinePage>
with SingleTickerProviderStateMixin {
bool _isLoading = false;
List<String> images = [
'assets/Fullname.jpeg',
'assets/FirstName&LastName.jpeg'
];
TabController _tabController;
@override
void initState() {
_tabController = TabController(vsync: this, length: 2);
super.initState();
}
@override
Widget build(BuildContext context) {
MainModel mainModel = Provider.of<MainModel>(context);
final phoneBox =
labeledText(context, mainModel.customer.phoneNumber, "user.phone");
final fcsIdBox = Container(
padding: EdgeInsets.only(top: 15),
child: labeledText(context, mainModel.customer.fcsID, "user.fcs_id"));
final shippingAddressBox = Container(
padding: EdgeInsets.only(top: 15),
child: labeledText(context, mainModel.customer.shippingAddress,
"user.shipping_address"));
final deliveryAddressBox = Container(
padding: EdgeInsets.only(top: 15),
child: labeledText(context, mainModel.customer.deliveryAddress,
"user.deliveryAddress"));
final instructionBox = Container(
padding: EdgeInsets.only(left: 10, top: 30, bottom: 10),
child: Center(
child: Wrap(
children: <Widget>[
LocalText(
context,
'user.buying_instruction',
color: labelColor,
fontSize: 15,
)
],
),
),
);
final amazonbutton = Container(
padding: EdgeInsets.only(left: 10, right: 10, top: 10),
child: Container(
height: 45.0,
decoration: BoxDecoration(
color: primaryColor,
shape: BoxShape.rectangle,
// borderRadius: BorderRadius.all(Radius.circular(10.0))
),
child: ButtonTheme(
minWidth: 900.0,
height: 100.0,
child: FlatButton(
onPressed: () {
Navigator.of(context).push(BottomUpPageRoute(InstructionPage(
name: 'Amazon',
image: "assets/amazon_ins.png",
)));
},
child: LocalText(
context,
'buy.amazon',
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
),
),
);
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: AppBar(
centerTitle: true,
leading: new IconButton(
icon: new Icon(Icons.close),
onPressed: () => Navigator.of(context).pop(),
),
title: LocalText(
context,
"buy_online.title",
fontSize: 20,
color: Colors.white,
),
backgroundColor: primaryColor,
),
body: ListView(
shrinkWrap: true,
padding: EdgeInsets.only(top: 10, left: 10, right: 10),
children: <Widget>[
nameWidget(mainModel.customer.name),
phoneWidget(context, mainModel.customer.phoneNumber),
Row(
children: <Widget>[
SizedBox(
width: 25,
height: 25,
child: FittedBox(
child: Image.asset("assets/logo.jpg"),
fit: BoxFit.fill,
),
),
fcsIdBox,
Padding(
padding: const EdgeInsets.only(left: 7.0, top: 50),
child: Icon(
Icons.content_copy,
color: Colors.grey,
),
)
],
),
Row(
children: <Widget>[
Icon(Icons.location_on),
shippingAddressBox,
Padding(
padding: const EdgeInsets.only(left: 7.0, top: 50),
child: Icon(
Icons.content_copy,
color: Colors.grey,
),
)
],
),
// deliveryAddressBox,
instructionBox,
new Container(
decoration: new BoxDecoration(color: Colors.white),
child: new TabBar(
// indicatorColor: primaryColor,
labelColor: primaryColor,
labelStyle: TextStyle(fontWeight: FontWeight.bold),
unselectedLabelColor: Colors.grey,
controller: _tabController,
tabs: [
new Tab(
text: 'With FULL NAME',
),
new Tab(
text: 'With FIRST NAME&\nLAST NAME',
),
],
),
),
new Container(
padding: EdgeInsets.only(top: 10),
height: 500,
width: 500,
child: new TabBarView(
controller: _tabController,
children: <Widget>[
Container(
child:
Image.asset('assets/Fullname.png', fit: BoxFit.contain),
),
Container(
child: Image.asset('assets/FirstName&LastName.png',
fit: BoxFit.contain),
),
],
),
),
// Container(
// height: 500,
// width: 500,
// child: ListView.builder(
// itemCount: images.length,
// scrollDirection: Axis.horizontal,
// itemBuilder: (context, index) {
// return Container(
// padding: EdgeInsets.only(left: 0, right: 5, top: 5),
// child: Image.asset(images[index], fit: BoxFit.contain),
// );
// }),
// ),
SizedBox(height: 10)
],
),
),
);
}
}

View File

@@ -1,257 +0,0 @@
import 'package:fcs/model/pickup_model.dart';
import 'package:fcs/vo/pickup.dart';
import 'package:provider/provider.dart';
import 'package:fcs/widget/localization/app_translations.dart';
import 'package:flutter/material.dart';
import 'package:fcs/widget/progress.dart';
import '../fcs/common/helpers/theme.dart';
class CalculateShipmentCostEditor extends StatefulWidget {
final PickUp pickUp;
CalculateShipmentCostEditor({this.pickUp});
@override
_CalculateShipmentCostEditorState createState() =>
_CalculateShipmentCostEditorState();
}
class _CalculateShipmentCostEditorState
extends State<CalculateShipmentCostEditor> {
TextEditingController _addressEditingController = new TextEditingController();
TextEditingController _fromTimeEditingController =
new TextEditingController();
TextEditingController _toTimeEditingController = new TextEditingController();
TextEditingController _noOfPackageEditingController =
new TextEditingController();
TextEditingController _weightEditingController = new TextEditingController();
PickUp _pickUp;
bool _isLoading = false;
@override
void initState() {
super.initState();
if (widget.pickUp != null) {
_pickUp = widget.pickUp;
_addressEditingController.text = _pickUp.address;
_fromTimeEditingController.text = _pickUp.fromTime;
_toTimeEditingController.text = _pickUp.toTime;
_noOfPackageEditingController.text = _pickUp.numberOfPackage.toString();
_weightEditingController.text = _pickUp.weight.toString();
}
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
var pickupModel = Provider.of<PickUpModel>(context);
final pickUpAddress = Container(
child: TextFormField(
maxLines: null,
controller: _addressEditingController,
cursorColor: primaryColor,
style: textStyle,
decoration: new InputDecoration(
labelText: 'Pickup Address',
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: primaryColor, width: 1.0)),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: primaryColor, width: 1.0)),
),
));
final pickupTime = Container(
height: 50.0,
child: Row(children: <Widget>[
Container(
width: 70.0,
child: TextFormField(
controller: _fromTimeEditingController,
cursorColor: primaryColor,
textAlign: TextAlign.left,
decoration: InputDecoration(
contentPadding: EdgeInsets.all(10.0),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.grey[300], width: 2),
),
focusedBorder: OutlineInputBorder(
borderSide: const BorderSide(color: primaryColor, width: 2.0),
),
),
)),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(' to '),
),
Container(
width: 70.0,
child: TextFormField(
controller: _toTimeEditingController,
cursorColor: primaryColor,
textAlign: TextAlign.left,
decoration: InputDecoration(
contentPadding: EdgeInsets.all(10.0),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.grey[300], width: 2),
),
focusedBorder: OutlineInputBorder(
borderSide: const BorderSide(color: primaryColor, width: 2.0),
),
),
)),
]),
);
final noOfPackageBox = Container(
height: 50.0,
child: Row(children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Text('Number of Packages '),
),
Expanded(
child: TextFormField(
controller: _noOfPackageEditingController,
cursorColor: primaryColor,
textAlign: TextAlign.left,
decoration: InputDecoration(
contentPadding: EdgeInsets.all(10.0),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.grey[300], width: 2),
),
focusedBorder: OutlineInputBorder(
borderSide: const BorderSide(color: primaryColor, width: 2.0),
),
),
)),
]),
);
final weightBox = Container(
height: 50.0,
child: Row(children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Text('Total Weight (lb) '),
),
Expanded(
child: TextFormField(
controller: _weightEditingController,
cursorColor: primaryColor,
textAlign: TextAlign.left,
decoration: InputDecoration(
contentPadding: EdgeInsets.all(10.0),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.grey[300], width: 2),
),
focusedBorder: OutlineInputBorder(
borderSide: const BorderSide(color: primaryColor, width: 2.0),
),
),
)),
]),
);
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: AppBar(
backgroundColor: primaryColor,
title: Text(AppTranslations.of(context).text("pickup.edit.title")),
),
body: Card(
child: Column(
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: ListView(children: <Widget>[
Text(
"U Aung Zaw",
style: TextStyle(fontSize: 15.0),
),
Text(
"+82054857695",
style: TextStyle(fontSize: 15.0),
),
pickUpAddress,
SizedBox(height: 15),
Text('Pickup Time'),
SizedBox(height: 15),
pickupTime,
SizedBox(height: 15),
noOfPackageBox,
SizedBox(height: 15),
weightBox
]),
)),
widget.pickUp == null
? Align(
alignment: Alignment.bottomCenter,
child: Center(
child: Container(
width: 250,
child: FlatButton(
shape: new RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(10)),
child: Text('Request'),
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(
shape: new RoundedRectangleBorder(
borderRadius:
new BorderRadius.circular(10)),
child: Text('Pickuped'),
color: primaryColor,
textColor: Colors.white,
onPressed: () {
Navigator.pop(context);
},
),
))),
Align(
alignment: Alignment.bottomCenter,
child: Center(
child: Container(
width: 250,
child: FlatButton(
shape: new RoundedRectangleBorder(
borderRadius:
new BorderRadius.circular(10)),
child: Text('Cancel'),
color: primaryColor,
textColor: Colors.white,
onPressed: () {
Navigator.pop(context);
},
),
)))
],
))
],
),
),
),
);
}
}

View File

@@ -1,272 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/language_model.dart';
import 'package:fcs/model/user_model.dart';
import 'package:fcs/vo/user.dart';
import 'package:fcs/widget/local_text.dart';
import 'package:fcs/widget/localization/app_translations.dart';
import 'package:fcs/widget/progress.dart';
import '../fcs/common/helpers/theme.dart' as Theme;
import 'confirm_email.dart';
import '../fcs/common/pages/util.dart';
class ChangePhoneNumber extends StatefulWidget {
final User user;
ChangePhoneNumber(
this.user, {
Key key,
}) : super(key: key);
@override
_ChangePhoneNumberState createState() => new _ChangePhoneNumberState();
}
class _ChangePhoneNumberState extends State<ChangePhoneNumber>
with SingleTickerProviderStateMixin {
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
final FocusNode myFocusNodePhone = FocusNode();
final FocusNode myFocusNodenewPhone = FocusNode();
TextEditingController _phoneController = new TextEditingController();
TextEditingController _newPhoneController = new TextEditingController();
final formKey = GlobalKey<FormState>();
bool _isLoading = false;
@override
Widget build(BuildContext context) {
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
key: _scaffoldKey,
body: SingleChildScrollView(
child: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height >= 775.0
? MediaQuery.of(context).size.height
: 580.0,
child: Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 35.0, bottom: 10),
child: ListTile(
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () {
Navigator.of(context).pop();
},
),
title: LocalText(
context,
'change.phone',
color: Colors.black87,
fontSize: 17,
),
),
),
Expanded(
flex: 2,
child: PageView(
children: <Widget>[
new ConstrainedBox(
constraints: const BoxConstraints.expand(),
child: _buildReset(context),
),
],
),
),
],
),
),
),
),
);
}
@override
void dispose() {
myFocusNodenewPhone.dispose();
myFocusNodePhone.dispose();
super.dispose();
}
@override
void initState() {
super.initState();
_phoneController.text = widget.user.phone;
_newPhoneController.text = "09";
// SystemChrome.setPreferredOrientations([
// DeviceOrientation.portraitUp,
// DeviceOrientation.portraitDown,
// ]);
}
Widget _buildReset(BuildContext context) {
return Container(
child: ListView(
children: <Widget>[
Column(
children: <Widget>[
Form(
key: formKey,
child: Card(
elevation: 2.0,
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
child: Container(
width: 300.0,
child: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.only(left: 25.0, right: 25.0),
child: TextFormField(
focusNode: myFocusNodePhone,
controller: _phoneController,
readOnly: true,
style: TextStyle(
fontFamily: "WorkSansSemiBold",
fontSize: 16.0,
color: Colors.black),
decoration: InputDecoration(
border: InputBorder.none,
icon: Icon(
FontAwesomeIcons.phone,
color: Colors.black,
size: 22.0,
),
labelText: AppTranslations.of(context)
.text("login.phone"),
labelStyle:
Provider.of<LanguageModel>(context).isEng
? TextStyle(
fontFamily: "WorkSansSemiBold",
color: Colors.grey)
: TextStyle(
fontFamily: "MyanmarUnicode",
color: Colors.grey),
),
),
),
Container(
width: 250.0,
height: 1.0,
color: Colors.grey[400],
),
Padding(
padding: EdgeInsets.only(left: 25.0, right: 25.0),
child: TextFormField(
focusNode: myFocusNodenewPhone,
controller: _newPhoneController,
keyboardType: TextInputType.phone,
style: TextStyle(
fontFamily: "WorkSansSemiBold",
fontSize: 16.0,
color: Colors.black),
decoration: InputDecoration(
border: InputBorder.none,
icon: Icon(
FontAwesomeIcons.phone,
color: Colors.black,
size: 22.0,
),
labelText: AppTranslations.of(context)
.text("change.new.phone"),
labelStyle:
Provider.of<LanguageModel>(context).isEng
? TextStyle(
fontFamily: "WorkSansSemiBold",
color: Colors.grey)
: TextStyle(
fontFamily: "MyanmarUnicode",
color: Colors.grey),
),
validator: _validatePhone,
),
),
],
),
),
),
),
SizedBox(
height: 15,
),
Container(
// margin: EdgeInsets.only(top: 320.0),
decoration: new BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(5.0)),
color: Theme.primaryColor,
),
child: MaterialButton(
highlightColor: Colors.transparent,
splashColor: Theme.LoginColors.loginGradientEnd,
//shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5.0))),
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 10.0, horizontal: 42.0),
child: LocalText(
context,
'change',
color: Colors.white,
fontSize: 18.0,
),
),
onPressed: () => _change(context)),
),
],
),
],
),
);
}
void _change(BuildContext context) async {
if (!formKey.currentState.validate()) {
return;
}
var _phone = _newPhoneController.text;
setState(() {
_isLoading = true;
});
UserModel userModel = Provider.of<UserModel>(context);
try {
await userModel.changePhone(widget.user.phoneNumber, _phone);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ConfirmEmail(
id: widget.user.phoneNumber,
phoneNumber: _phone,
)));
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
Future.delayed(Duration(seconds: 1), () {
if (mounted) {
setState(() {
_isLoading = false;
});
}
});
}
}
String _validatePhone(value) {
if (value.isEmpty) {
return AppTranslations.of(context).text("change.phone_empty");
}
if (!value.startsWith("09")) {
return 'Only "09".';
}
return null;
}
}

View File

@@ -1,320 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/language_model.dart';
import 'package:fcs/model/user_model.dart';
import 'package:fcs/vo/user.dart';
import 'package:fcs/widget/local_text.dart';
import 'package:fcs/widget/localization/app_translations.dart';
import 'package:fcs/widget/progress.dart';
import '../fcs/common/helpers/theme.dart' as Theme;
import '../fcs/common/pages/util.dart';
class ChangePassword extends StatefulWidget {
final User user;
ChangePassword(
this.user, {
Key key,
}) : super(key: key);
@override
_ChangePasswordState createState() => new _ChangePasswordState();
}
class _ChangePasswordState extends State<ChangePassword>
with SingleTickerProviderStateMixin {
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
final FocusNode myFocusNodePassword = FocusNode();
final FocusNode myFocusNodeEmail = FocusNode();
bool _obscureTextLogin = true;
bool _obscureTextSignup = true;
bool _obscureTextSignupConfirm = true;
TextEditingController _smsController = new TextEditingController();
TextEditingController _passwordController = new TextEditingController();
TextEditingController _confirmPasswordController =
new TextEditingController();
final formKey = GlobalKey<FormState>();
bool _isLoading = false;
@override
Widget build(BuildContext context) {
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
key: _scaffoldKey,
body: SingleChildScrollView(
child: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height >= 775.0
? MediaQuery.of(context).size.height
: 580.0,
child: Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 35.0, bottom: 10),
child: ListTile(
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () {
Navigator.of(context).pop();
},
),
title: LocalText(
context,
'change.password.title',
color: Colors.black87,
fontSize: 17,
),
),
),
Expanded(
flex: 2,
child: PageView(
children: <Widget>[
new ConstrainedBox(
constraints: const BoxConstraints.expand(),
child: _buildReset(context),
),
],
),
),
],
),
),
),
),
);
}
@override
void dispose() {
myFocusNodePassword.dispose();
myFocusNodeEmail.dispose();
super.dispose();
}
@override
void initState() {
super.initState();
// SystemChrome.setPreferredOrientations([
// DeviceOrientation.portraitUp,
// DeviceOrientation.portraitDown,
// ]);
_smsController.text = "";
}
Widget _buildReset(BuildContext context) {
return Container(
child: ListView(
children: <Widget>[
Column(
children: <Widget>[
Form(
key: formKey,
child: Card(
elevation: 2.0,
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
child: Container(
width: 300.0,
child: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.only(left: 25.0, right: 25.0),
child: TextFormField(
focusNode: myFocusNodePassword,
controller: _passwordController,
obscureText: _obscureTextSignup,
style: TextStyle(
fontFamily: "WorkSansSemiBold",
fontSize: 16.0,
color: Colors.black),
decoration: InputDecoration(
border: InputBorder.none,
icon: Icon(
FontAwesomeIcons.lock,
color: Colors.black,
),
labelText: AppTranslations.of(context)
.text("login.password"),
labelStyle:
Provider.of<LanguageModel>(context).isEng
? TextStyle(
fontFamily: "WorkSansSemiBold",
color: Colors.grey)
: TextStyle(
fontFamily: "MyanmarUnicode",
color: Colors.grey),
suffixIcon: GestureDetector(
onTap: _toggleSignup,
child: Icon(
_obscureTextSignup
? FontAwesomeIcons.eye
: FontAwesomeIcons.eyeSlash,
size: 15.0,
color: Colors.black,
),
),
),
validator: _validatePassword,
),
),
Container(
width: 250.0,
height: 1.0,
color: Colors.grey[400],
),
Padding(
padding: EdgeInsets.only(left: 25.0, right: 25.0),
child: TextFormField(
controller: _confirmPasswordController,
obscureText: _obscureTextSignupConfirm,
style: TextStyle(
fontFamily: "WorkSansSemiBold",
fontSize: 16.0,
color: Colors.black),
decoration: InputDecoration(
border: InputBorder.none,
icon: Icon(
FontAwesomeIcons.lock,
color: Colors.black,
),
labelText: AppTranslations.of(context)
.text("login.confirm_password"),
labelStyle:
Provider.of<LanguageModel>(context).isEng
? TextStyle(
fontFamily: "WorkSansSemiBold",
color: Colors.grey)
: TextStyle(
fontFamily: "MyanmarUnicode",
color: Colors.grey),
suffixIcon: GestureDetector(
onTap: _toggleSignupConfirm,
child: Icon(
_obscureTextSignupConfirm
? FontAwesomeIcons.eye
: FontAwesomeIcons.eyeSlash,
size: 15.0,
color: Colors.black,
),
),
),
validator: _validateConfirmPassword,
),
),
],
),
),
),
),
SizedBox(
height: 15,
),
Container(
// margin: EdgeInsets.only(top: 320.0),
decoration: new BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(5.0)),
color: Theme.primaryColor,
),
child: MaterialButton(
highlightColor: Colors.transparent,
splashColor: Theme.LoginColors.loginGradientEnd,
//shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5.0))),
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 10.0, horizontal: 42.0),
child: LocalText(
context,
'change',
color: Colors.white,
fontSize: 18.0,
),
),
onPressed: () => _change(context)),
),
],
),
],
),
);
}
void _toggleLogin() {
setState(() {
_obscureTextLogin = !_obscureTextLogin;
});
}
void _toggleSignup() {
setState(() {
_obscureTextSignup = !_obscureTextSignup;
});
}
void _toggleSignupConfirm() {
setState(() {
_obscureTextSignupConfirm = !_obscureTextSignupConfirm;
});
}
void _change(BuildContext context) async {
if (!formKey.currentState.validate()) {
return;
}
var password = _passwordController.text;
setState(() {
_isLoading = true;
});
UserModel userModel = Provider.of<UserModel>(context);
try {
await userModel.changePassword(widget.user.phoneNumber, password);
Navigator.pop(context);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
Future.delayed(Duration(seconds: 1), () {
if (mounted) {
setState(() {
_isLoading = false;
});
}
});
}
}
String _validatePassword(value) {
if (value.isEmpty) {
return AppTranslations.of(context).text("login.password_empty");
}
if (value.length < 6) {
return AppTranslations.of(context).text("login.password_size");
}
return null;
}
String _validateConfirmPassword(value) {
if (value.isEmpty) {
return AppTranslations.of(context).text("login.password_empty");
}
if (value.length < 6) {
return AppTranslations.of(context).text("login.password_size");
}
if (value != _passwordController.text) {
return AppTranslations.of(context).text("login.password_mismatch");
}
return null;
}
}

135
lib/pages/chat/bubble.dart Normal file
View File

@@ -0,0 +1,135 @@
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/pages/package/package_info.dart';
import 'package:fcs/pages/main/util.dart';
import 'package:fcs/pages/widgets/fcs_id_icon.dart';
import 'package:fcs/pages/widgets/local_text.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
DateFormat dayFormat = DateFormat("MMM dd yyyy");
DateFormat timeFormat = DateFormat("HH:mm");
typedef CallbackOnViewDetail();
class Bubble extends StatelessWidget {
Bubble(
{this.message,
this.date,
this.delivered,
this.isMine,
this.sender,
this.isSystem,
this.isCustomer,
this.showDate,
this.callbackOnViewDetail});
final CallbackOnViewDetail callbackOnViewDetail;
final DateTime date;
final String message, sender;
final bool delivered, isMine, isSystem, isCustomer, showDate;
@override
Widget build(BuildContext context) {
final bg = isMine ? Colors.greenAccent.shade100 : Colors.white;
final align = isMine ? CrossAxisAlignment.end : CrossAxisAlignment.start;
final icon = delivered ? Icons.done_all : Icons.done;
final radius = isMine
? BorderRadius.only(
topLeft: Radius.circular(25.0),
bottomLeft: Radius.circular(25.0),
bottomRight: Radius.circular(30.0),
)
: BorderRadius.only(
topRight: Radius.circular(25.0),
bottomLeft: Radius.circular(30.0),
bottomRight: Radius.circular(25.0),
);
return Column(
crossAxisAlignment: align,
children: <Widget>[
showDate ? Center(child: Text(dateFormat.format(date))) : Container(),
Container(
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width * 0.8, minWidth: 10),
margin: const EdgeInsets.all(3.0),
padding: const EdgeInsets.all(8.0),
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
blurRadius: .5,
spreadRadius: 1.0,
color: Colors.black.withOpacity(.32))
],
color: bg,
borderRadius: radius,
),
child: Column(
crossAxisAlignment: align,
children: (isMine && isCustomer) || (!isMine && !isCustomer)
? [getMsg(context, icon)]
: isSystem
? [
FcsIDIcon(),
getMsg(context, icon),
FlatButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
color: Colors.blue[50],
onPressed: () => _viewDetail(),
child: Text(
getLocalString(context, "message.view.detail"),
style: TextStyle(
color: primaryColor,
fontWeight: FontWeight.bold)))
]
: [
Text(isCustomer ? "FCS Team" : sender,
style: TextStyle(
color: Colors.black38,
fontSize: 10.0,
)),
getMsg(context, icon),
],
),
)
],
);
}
getMsg(BuildContext context, IconData iconData) {
return Stack(
children: <Widget>[
Padding(
padding: EdgeInsets.only(right: 48.0),
child: Text(message,
style: hasUnicode(message)
? newLabelStyleMM(color: primaryColor)
: newLabelStyle(color: primaryColor))),
Positioned(
bottom: 0.0,
right: 0.0,
child: Row(
children: <Widget>[
Text(timeFormat.format(date),
style: TextStyle(
color: Colors.black38,
fontSize: 10.0,
)),
SizedBox(width: 3.0),
Icon(
iconData,
size: 12.0,
color: Colors.black38,
)
],
),
),
],
);
}
_viewDetail() {
if (callbackOnViewDetail != null) callbackOnViewDetail();
}
}

View File

@@ -0,0 +1,191 @@
import 'package:fcs/domain/constants.dart';
import 'package:fcs/domain/entities/package.dart';
import 'package:fcs/domain/entities/user.dart';
import 'package:fcs/domain/vo/message.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/pages/chat/model/message_model.dart';
import 'package:fcs/pages/customer/customer_editor.dart';
import 'package:fcs/pages/customer/model/customer_model.dart';
import 'package:fcs/pages/main/model/main_model.dart';
import 'package:fcs/pages/package/model/package_model.dart';
import 'package:fcs/pages/package/package_info.dart';
import 'package:fcs/pages/profile/profile_page.dart';
import 'package:fcs/pages/main/util.dart';
import 'package:fcs/pages/widgets/bottom_up_page_route.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'bubble.dart';
class MessageDetail extends StatelessWidget {
final String receiverName;
final String receiverID;
final MessageModel messageModel;
final TextEditingController textEditingController = TextEditingController();
final ScrollController listScrollController = ScrollController();
MessageDetail(
{Key key, this.messageModel, this.receiverName, this.receiverID})
: super(key: key) {
listScrollController.addListener(() {
if (listScrollController.offset >=
listScrollController.position.maxScrollExtent &&
!listScrollController.position.outOfRange) {
if (!messageModel.isEnded) messageModel.load();
}
if (listScrollController.offset <=
listScrollController.position.minScrollExtent &&
!listScrollController.position.outOfRange) {}
});
}
@override
Widget build(BuildContext context) {
String userID = Provider.of<MessageModel>(context).user.id;
return Scaffold(
appBar: AppBar(
backgroundColor: primaryColor,
elevation: .9,
title: Text(
receiverName ?? "FCS Team",
),
actions: <Widget>[],
),
body: Padding(
padding: EdgeInsets.all(16.0),
child: Column(
children: [
Expanded(
child: ListView.builder(
itemBuilder: (context, index) {
var msg = messageModel.messages[index];
Message next;
bool showDate = false;
if (messageModel.messages.length > index + 1) {
next = messageModel.messages[index + 1];
if (!msg.sameDay(next)) {
showDate = true;
}
}
if (messageModel.messages.length - 1 == index &&
messageModel.isEnded) {
showDate = true;
}
return buildBubble(
msg, userID, showDate, () => _viewDetail(context, msg));
},
itemCount: messageModel.messages.length,
reverse: true,
controller: listScrollController,
)),
buildInput(context),
],
),
),
);
}
Widget buildBubble(Message msg, String userID, bool showDate,
CallbackOnViewDetail callback) {
return Bubble(
message: msg.message,
date: msg.date,
delivered: true,
sender: msg.senderName,
isMine: msg.senderID == userID || msg.receiverID == receiverID,
isCustomer: receiverID == null,
showDate: showDate,
isSystem: msg.messageType != null && msg.messageType != "",
callbackOnViewDetail: callback,
);
}
Widget buildInput(BuildContext context) {
return Container(
padding: EdgeInsets.only(top: 3),
child: Row(
children: <Widget>[
Flexible(
child: Container(
child: TextField(
onSubmitted: (value) {
Provider.of<MessageModel>(context, listen: false)
.sendMessage(textEditingController.text, receiverID);
textEditingController.text = "";
},
style: TextStyle(color: primaryColor, fontSize: 15.0),
maxLines: 10,
minLines: 1,
keyboardType: TextInputType.multiline,
controller: textEditingController,
decoration: InputDecoration(
focusedBorder: const OutlineInputBorder(
borderSide:
const BorderSide(color: primaryColor, width: 1.0),
),
border: new OutlineInputBorder(
borderRadius: const BorderRadius.all(
const Radius.circular(10.0),
),
),
hintText: getLocalString(context, "message.hint.input"),
hintStyle: TextStyle(
color: Colors.grey,
),
),
),
),
),
// Button send message
Material(
child: Container(
margin: EdgeInsets.symmetric(horizontal: 8.0),
child: IconButton(
icon: Icon(Icons.send),
onPressed: () {
Provider.of<MessageModel>(context, listen: false)
.sendMessage(textEditingController.text, receiverID);
textEditingController.text = "";
},
color: primaryColor,
),
),
color: Colors.white,
),
],
),
width: double.infinity,
decoration: BoxDecoration(
border: Border(top: BorderSide(color: Colors.grey[700], width: 0.5)),
color: Colors.white),
);
}
_viewDetail(BuildContext context, Message message) async {
if (message.messageType == message_type_package &&
message.messageID != null &&
message.messageID != "") {
PackageModel packageModel =
Provider.of<PackageModel>(context, listen: false);
Package p = await packageModel.getPackage(message.messageID);
Navigator.push<bool>(context, BottomUpPageRoute(PackageInfo(package: p)));
}
if (message.messageType == message_type_profile &&
message.messageID != null &&
message.messageID != "") {
MainModel mainModel = Provider.of<MainModel>(context, listen: false);
if (mainModel.user.isCustomer()) {
Navigator.push<bool>(context, BottomUpPageRoute(Profile()));
} else {
CustomerModel customerModel =
Provider.of<CustomerModel>(context, listen: false);
User user = await customerModel.getUser(message.messageID);
Navigator.of(context)
.push(BottomUpPageRoute(CustomerEditor(customer: user)));
}
}
}
}

View File

@@ -0,0 +1,92 @@
import 'dart:async';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:fcs/data/services/services.dart';
import 'package:fcs/domain/constants.dart';
import 'package:fcs/domain/vo/message.dart';
import 'package:fcs/pages/main/model/base_model.dart';
import 'package:logging/logging.dart';
class MessageModel extends BaseModel {
final log = Logger('MessageModel');
List<Message> messages;
@override
logout() async {
if (listener != null) await listener.cancel();
messages = [];
}
Query query;
DocumentSnapshot prevSnap;
bool isEnded;
bool isLoading;
String userID;
StreamSubscription<QuerySnapshot> listener;
static const int rowPerLoad = 20;
void initQuery(String userID) {
this.messages = [];
this.userID = userID;
this.prevSnap = null;
query = Firestore.instance
.collection("$user_collection/$userID/$messages_collection")
.orderBy('date', descending: true);
load();
}
Future<void> load() async {
Query _query =
prevSnap != null ? query.startAfterDocument(prevSnap) : query;
QuerySnapshot snapshot =
await _query.limit(rowPerLoad).getDocuments(source: Source.server);
int count = snapshot.documents.length;
isEnded = count < rowPerLoad;
prevSnap = count > 0 ? snapshot.documents[count - 1] : prevSnap;
snapshot.documents.forEach((e) {
messages.add(Message.fromMap(e.data, e.documentID));
if (messages.length == 1) {
_initListener(e);
}
});
notifyListeners();
}
void _initListener(DocumentSnapshot snap) {
if (listener != null) listener.cancel();
listener = Firestore.instance
.collection("$user_collection/$userID/$messages_collection")
.endBeforeDocument(snap)
.orderBy('date', descending: true)
.snapshots(includeMetadataChanges: true)
.listen((qs) {
qs.documentChanges.forEach((c) {
switch (c.type) {
case DocumentChangeType.added:
log.info("added!! $c");
messages.insert(
0, Message.fromMap(c.document.data, c.document.documentID));
notifyListeners();
break;
case DocumentChangeType.modified:
log.info("modified!! $c");
break;
default:
}
});
});
}
Future<void> sendMessage(String msg, String receiverID) {
Message message = Message(message: msg, receiverID: receiverID);
return Services.instance.commonService.sendMessage(message);
}
Future<void> seenMessages(String ownerID, bool seenByOwner) {
return Services.instance.commonService.seenMessage(ownerID, seenByOwner);
}
}

View File

@@ -1,114 +0,0 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/user_model.dart';
import 'package:fcs/fcs/common/helpers/theme.dart';
import 'package:fcs/widget/localization/app_translations.dart';
import 'package:fcs/widget/progress.dart';
import '../fcs/common/pages/util.dart';
class ConfirmEmail extends StatefulWidget {
final String id, email, phoneNumber;
const ConfirmEmail({Key key, this.id, this.email, this.phoneNumber})
: super(key: key);
@override
_ConfirmEmailState createState() => _ConfirmEmailState();
}
class _ConfirmEmailState extends State<ConfirmEmail> {
final TextEditingController _sms = new TextEditingController();
bool _isLoading = false;
final _formKey = GlobalKey<FormState>();
@override
void initState() {
super.initState();
}
_confimEmail() async {
UserModel userModel = Provider.of<UserModel>(context);
if (!_formKey.currentState.validate()) {
return;
}
setState(() {
_isLoading = true;
});
try {
await userModel.confirmEmail(
widget.id, widget.email, widget.phoneNumber, _sms.text);
Navigator.pushNamedAndRemoveUntil(context, "/welcome", (r) => false);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
}
Future.delayed(Duration(seconds: 1), () {
if (mounted) {
setState(() {
_isLoading = false;
});
}
});
}
@override
Widget build(BuildContext context) {
final smsInput = TextFormField(
controller: _sms,
keyboardType: TextInputType.number,
autofocus: false,
decoration: new InputDecoration(
labelText: widget.email == null
? AppTranslations.of(context).text("sms.sms")
: AppTranslations.of(context).text("email.code"),
labelStyle: labelStyle,
hintText: 'eg. 123456',
icon: Icon(
Icons.lock,
color: primaryColor,
)),
validator: (value) {
if (value.isEmpty) {
return widget.email == null
? AppTranslations.of(context).text("sms.empty")
: AppTranslations.of(context).text("email.code_empty");
}
return null;
},
);
final enterButton = Padding(
padding: EdgeInsets.symmetric(vertical: 16.0),
child: RaisedButton(
onPressed: () => _confimEmail(),
padding: EdgeInsets.all(12),
color: primaryColor,
child: Text(AppTranslations.of(context).text("sms.enter"),
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold)),
),
);
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: AppBar(
title: Text(widget.email == null
? AppTranslations.of(context).text("input_sms")
: AppTranslations.of(context).text("email.input")),
backgroundColor: primaryColor,
),
body: Center(
child: ListView(
shrinkWrap: true,
padding: EdgeInsets.only(left: 24.0, right: 24.0),
children: <Widget>[
Form(key: _formKey, child: smsInput),
SizedBox(height: 8.0),
enterButton,
],
),
),
),
);
}
}

View File

@@ -1,154 +0,0 @@
import 'package:fcs/model/pickup_model.dart';
import 'package:fcs/widget/label_widgets.dart';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:package_info/package_info.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:fcs/model/main_model.dart';
import 'package:fcs/fcs/common/pages/util.dart';
import 'package:fcs/fcs/common/helpers/theme.dart';
import 'package:fcs/widget/local_text.dart';
import 'package:fcs/widget/progress.dart';
class Contact extends StatefulWidget {
@override
_ContactState createState() => _ContactState();
}
class _ContactState extends State<Contact> {
bool _isLoading = false;
@override
Widget build(BuildContext context) {
var pickupModel = Provider.of<PickUpModel>(context, listen: false);
// MainModel mainModel = Provider.of<MainModel>(context);
// bool isOwner = mainModel.user != null && mainModel.user.isOwner();
// bool hasAdmin = mainModel.user != null && mainModel.user.hasAdmin();
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: AppBar(
backgroundColor: primaryColor,
elevation: 0,
centerTitle: true,
leading: new IconButton(
icon: new Icon(Icons.close),
onPressed: () => Navigator.of(context).pop(),
),
title: LocalText(
context,
"contact",
fontSize: 25,
color: Colors.white,
),
),
body: ListView(
children: <Widget>[
link(pickupModel.profile.usaContactNumber, Icons.phone_forwarded,
onTap: () => _call(pickupModel.profile.usaContactNumber),
label: LocalText(
context,
"contact.usa.phone",
color: primaryColor,
)),
link(pickupModel.profile.mmContactNumber, Icons.phone_forwarded,
onTap: () => _call(
pickupModel.profile.mmContactNumber,
),
label: LocalText(
context,
"contact.mm.phone",
color: primaryColor,
)),
link(
pickupModel.profile.usaAddress,
Icons.location_on,
),
link(pickupModel.profile.mmAddress, Icons.location_on),
link(pickupModel.profile.mail, Icons.email,
onTap: () => _email(pickupModel.profile.mail)),
link(pickupModel.profile.facebook, FontAwesomeIcons.facebook,
onTap: () => _openLink(pickupModel.profile.facebook)),
],
),
),
);
}
Widget link(String text, IconData iconData,
{Function() onTap, Widget label}) {
return Padding(
padding: const EdgeInsets.only(left: 18.0, bottom: 5),
child: InkWell(
onTap: () => onTap != null ? onTap() : null,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Icon(
iconData,
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
label == null
? Container()
: Padding(
padding: EdgeInsets.fromLTRB(10, 10, 10, 0),
child: label,
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
text == null ? "" : text,
overflow: TextOverflow.ellipsis,
maxLines: 5,
style: TextStyle(fontSize: 14.0),
),
),
],
),
SizedBox(
width: 5,
),
onTap == null
? Container()
: Icon(
Icons.open_in_new,
color: Colors.grey,
size: 15,
)
],
),
)),
);
}
Future<String> getVersionNumber() async {
PackageInfo packageInfo = await PackageInfo.fromPlatform();
String version = packageInfo.version + "+" + packageInfo.buildNumber;
return version;
}
_call(String phone) {
showConfirmDialog(
context, "contact.phone.confim", () => launch("tel:$phone"),
translationVariables: ["$phone"]);
}
_email(String email) {
showConfirmDialog(
context, "contact.email.configm", () => launch("mailto:$email"),
translationVariables: ["$email"]);
}
_openLink(String link) {
showConfirmDialog(context, "contact.open.confrim", () => launch("$link"),
translationVariables: ["$link"]);
}
}

View File

@@ -0,0 +1,151 @@
import 'package:fcs/domain/vo/contact.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/localization/app_translations.dart';
import 'package:fcs/pages/contact/model/contact_model.dart';
import 'package:fcs/pages/main/util.dart';
import 'package:fcs/pages/widgets/input_text.dart';
import 'package:fcs/pages/widgets/local_text.dart';
import 'package:fcs/pages/widgets/progress.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:provider/provider.dart';
import 'widgets.dart';
class ContactEditor extends StatefulWidget {
final Contact contact;
const ContactEditor({this.contact});
@override
_ContactEditorState createState() => _ContactEditorState();
}
class _ContactEditorState extends State<ContactEditor> {
TextEditingController _usaPhone = new TextEditingController();
TextEditingController _mmPhone = new TextEditingController();
TextEditingController _usaAddress = new TextEditingController();
TextEditingController _mmAddress = new TextEditingController();
TextEditingController _email = new TextEditingController();
TextEditingController _facebook = new TextEditingController();
bool _isLoading = false;
@override
void initState() {
super.initState();
if (widget.contact != null) {
_usaPhone.text = widget.contact.usaContactNumber;
_mmPhone.text = widget.contact.mmContactNumber;
_usaAddress.text = widget.contact.usaAddress;
_mmAddress.text = widget.contact.mmAddress;
_email.text = widget.contact.emailAddress;
_facebook.text = widget.contact.facebookLink;
}
}
@override
Widget build(BuildContext context) {
final usaPhoneBox = InputText(
labelTextKey: 'contact.usa.phone',
iconData: CupertinoIcons.phone,
controller: _usaPhone);
final mmPhoneBox = InputText(
labelTextKey: 'contact.mm.phone',
iconData: CupertinoIcons.phone,
controller: _mmPhone);
final usaAddreesBox = InputText(
labelTextKey: 'contact.usa.address',
iconData: CupertinoIcons.location,
maxLines: 3,
controller: _usaAddress);
final mmAddressBox = InputText(
labelTextKey: 'contact.mm.address',
iconData: CupertinoIcons.location,
maxLines: 3,
controller: _mmAddress);
final emailBox = InputText(
labelTextKey: 'contact.email',
iconData: CupertinoIcons.mail,
controller: _email);
final faceBookBox = InputText(
labelTextKey: 'contact.facebook',
iconData: FontAwesomeIcons.facebook,
controller: _facebook);
final saveBox = fcsButton(context, getLocalString(context, "btn.save"),
callack: _submit);
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: AppBar(
centerTitle: true,
leading: new IconButton(
icon: new Icon(CupertinoIcons.back, color: primaryColor),
onPressed: () => Navigator.of(context).pop(),
),
shadowColor: Colors.transparent,
backgroundColor: Colors.white,
title: LocalText(
context,
'contact.edit.title',
color: primaryColor,
fontSize: 20,
)),
body: ListView(
children: [
Padding(
padding: const EdgeInsets.only(left: 18.0, right: 18),
child: Column(
children: [
itemTitle(context, "contact.callus"),
usaPhoneBox,
mmPhoneBox,
Divider(),
itemTitle(context, "contact.findus"),
usaAddreesBox,
mmAddressBox,
Divider(),
itemTitle(context, "contact.emailus"),
emailBox,
Divider(),
itemTitle(context, "contact.visitus"),
faceBookBox,
SizedBox(
height: 10,
),
saveBox,
SizedBox(
height: 20,
)
],
),
),
],
),
));
}
_submit() async {
setState(() {
_isLoading = true;
});
try {
widget.contact.usaContactNumber = _usaPhone.text;
widget.contact.mmContactNumber = _mmPhone.text;
widget.contact.usaAddress = _usaAddress.text;
widget.contact.mmAddress = _mmAddress.text;
widget.contact.emailAddress = _email.text;
widget.contact.facebookLink = _facebook.text;
var contactModel = Provider.of<ContactModel>(context, listen: false);
await contactModel.saveContact(widget.contact);
Navigator.pop(context);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
}

View File

@@ -0,0 +1,127 @@
import 'package:fcs/domain/entities/setting.dart';
import 'package:fcs/domain/vo/contact.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/localization/app_translations.dart';
import 'package:fcs/pages/contact/contact_editor.dart';
import 'package:fcs/pages/main/model/main_model.dart';
import 'package:fcs/pages/widgets/local_text.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:package_info/package_info.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
import 'widgets.dart';
class ContactPage extends StatefulWidget {
@override
_ContactPageState createState() => _ContactPageState();
}
class _ContactPageState extends State<ContactPage> {
@override
Widget build(BuildContext context) {
Setting setting = Provider.of<MainModel>(context).setting;
bool isEditable = context.select((MainModel m) => m.contactEditable());
return Scaffold(
appBar: AppBar(
centerTitle: true,
leading: new IconButton(
icon: new Icon(
Icons.close,
color: primaryColor,
),
onPressed: () => Navigator.of(context).pop(),
),
shadowColor: Colors.transparent,
backgroundColor: Colors.white,
title: LocalText(
context,
'contact.title',
color: primaryColor,
fontSize: 20,
),
actions: isEditable
? [
IconButton(
onPressed: () =>
Navigator.of(context).push<void>(CupertinoPageRoute(
builder: (context) => ContactEditor(
contact: Contact.fromSetting(setting)),
)),
icon: Icon(
CupertinoIcons.pen,
color: primaryColor,
))
]
: [],
),
body: ListView(
children: [
itemTitle(context, "contact.callus"),
contactItem(context, setting.usaContactNumber, CupertinoIcons.phone,
onTap: () => _call(setting.usaContactNumber),
labelKey: "contact.usa.phone"),
contactItem(
context,
setting.mmContactNumber,
CupertinoIcons.phone,
onTap: () => _call(
setting.mmContactNumber,
),
labelKey: "contact.mm.phone",
),
itemTitle(context, "contact.findus"),
contactItem(
context,
setting.usaAddress,
CupertinoIcons.location,
labelKey: "contact.usa.address",
),
contactItem(
context,
setting.mmAddress,
CupertinoIcons.location,
labelKey: "contact.mm.address",
),
itemTitle(context, "contact.emailus"),
contactItem(
context,
setting.emailAddress,
CupertinoIcons.mail,
onTap: () => _email(setting.emailAddress),
labelKey: "contact.fcs.email",
),
itemTitle(context, "contact.visitus"),
contactItem(
context,
setting.facebookLink,
FontAwesomeIcons.facebook,
onTap: () => _opencontactItem(setting.facebookLink),
labelKey: "contact.facebook",
),
],
),
);
}
Future<String> getVersionNumber() async {
PackageInfo packageInfo = await PackageInfo.fromPlatform();
String version = packageInfo.version + "+" + packageInfo.buildNumber;
return version;
}
_call(String phone) {
launch("tel:$phone");
}
_email(String email) {
launch("mailto:$email");
}
_opencontactItem(String contactItem) {
launch("$contactItem");
}
}

View File

@@ -0,0 +1,17 @@
import 'dart:async';
import 'package:fcs/data/services/services.dart';
import 'package:fcs/domain/vo/contact.dart';
import 'package:fcs/pages/main/model/base_model.dart';
import 'package:logging/logging.dart';
class ContactModel extends BaseModel {
final log = Logger('ContactModel');
Future<void> saveContact(Contact contact) async {
await request("/contact", "PUT",
payload: contact.toMap(),
token: await Services.instance.authService.getToken());
notifyListeners();
}
}

View File

@@ -0,0 +1,103 @@
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/localization/app_translations.dart';
import 'package:fcs/pages/widgets/local_text.dart';
import 'package:flutter/material.dart';
Widget itemTitle(BuildContext context, String textKey) {
return Padding(
padding: const EdgeInsets.only(left: 18.0, top: 25, bottom: 5),
child: Text(
AppTranslations.of(context).text(textKey),
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 18, color: Colors.black),
),
);
}
Widget subItemTitle(BuildContext context, String textKey, {IconData iconData}) {
return Padding(
padding: const EdgeInsets.only(left: 0, top: 0, bottom: 0),
child: Row(
children: [
Icon(
iconData,
color: primaryColor,
),
SizedBox(width: 10),
Text(
AppTranslations.of(context).text(textKey),
style: TextStyle(
fontWeight: FontWeight.w700, fontSize: 15, color: primaryColor),
),
],
),
);
}
Widget contactItem(BuildContext context, String text, IconData iconData,
{Function() onTap, String labelKey}) {
return Material(
child: Padding(
padding: const EdgeInsets.only(left: 18.0, bottom: 10, right: 18),
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.grey, width: 0.8),
borderRadius: BorderRadius.all(
Radius.circular(5.0) // <--- border radius here
),
),
child: InkWell(
onTap: () => onTap != null ? onTap() : null,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Icon(
iconData,
color: primaryColor,
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
labelKey == null
? Container()
: Padding(
padding: EdgeInsets.fromLTRB(10, 10, 10, 0),
child: LocalText(context, labelKey,
color: primaryColor,
fontWeight: FontWeight.w500,
fontSize: 18),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
text == null ? "" : text,
overflow: TextOverflow.ellipsis,
maxLines: 5,
style: TextStyle(
fontSize: 14.0,
),
),
),
],
),
SizedBox(
width: 5,
),
onTap == null
? Container()
: Icon(
Icons.open_in_new,
color: Colors.grey,
size: 15,
)
],
),
)),
),
),
);
}

View File

@@ -1,274 +0,0 @@
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/language_model.dart';
import 'package:fcs/model/main_model.dart';
import 'package:fcs/pages/phone_input.dart';
import 'package:fcs/fcs/common/pages/util.dart';
import 'package:fcs/fcs/common/helpers/theme.dart';
import 'package:fcs/vo/setting.dart';
import 'package:fcs/widget/local_text.dart';
import 'package:fcs/widget/local_text_field.dart';
import 'package:fcs/widget/localization/app_translations.dart';
import 'package:fcs/widget/progress.dart';
class ContactEditor extends StatefulWidget {
final Setting setting;
const ContactEditor({this.setting});
@override
_ContactEditorState createState() => _ContactEditorState();
}
class _ContactEditorState extends State<ContactEditor> {
TextEditingController _email = new TextEditingController();
TextEditingController _facebook = new TextEditingController();
TextEditingController _website = new TextEditingController();
TextEditingController _address = new TextEditingController();
TextEditingController _deliveryPhone = new TextEditingController();
final _formKey = GlobalKey<FormState>();
bool _isLoading = false;
List<String> phones = new List();
List<String> _initPhones = new List();
@override
void initState() {
super.initState();
if (widget.setting != null) {
this._initPhones = widget.setting.phones;
_email.text = widget.setting.email;
_facebook.text = widget.setting.facebook;
_website.text = widget.setting.website;
_deliveryPhone.text = widget.setting.deliveryPhone;
_address.text = widget.setting.address;
phones.clear();
_initPhones.forEach((p) {
phones.add(p);
});
}
}
@override
Widget build(BuildContext context) {
var languageModel = Provider.of<LanguageModel>(context);
final emailBox = TextFormField(
controller: _email,
autofocus: false,
cursorColor: primaryColor,
style: textStyle,
decoration: new InputDecoration(
labelText: AppTranslations.of(context).text('contact.email'),
labelStyle: languageModel.isEng ? labelStyle : labelStyleMM,
icon: Icon(
Icons.email,
color: primaryColor,
),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: primaryColor, width: 1.0)),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: primaryColor, width: 1.0)),
),
validator: (value) {
if (value.isEmpty) {
return AppTranslations.of(context).text('contact.email.empty');
}
return null;
},
);
final faceBookBox = TextFormField(
controller: _facebook,
autofocus: false,
cursorColor: primaryColor,
style: textStyle,
decoration: new InputDecoration(
labelText: AppTranslations.of(context).text('contact.facebook'),
labelStyle: languageModel.isEng ? labelStyle : labelStyleMM,
icon: Icon(
FontAwesomeIcons.facebook,
color: primaryColor,
),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: primaryColor, width: 1.0)),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: primaryColor, width: 1.0)),
),
validator: (value) {
if (value.isEmpty) {
return AppTranslations.of(context).text('contact.facebook.empty');
}
return null;
},
);
final googleBox = TextFormField(
controller: _website,
autofocus: false,
cursorColor: primaryColor,
style: textStyle,
decoration: new InputDecoration(
labelText: AppTranslations.of(context).text('contact.google'),
labelStyle: languageModel.isEng ? labelStyle : labelStyleMM,
icon: Icon(
FontAwesomeIcons.chrome,
color: primaryColor,
),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: primaryColor, width: 1.0)),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: primaryColor, width: 1.0)),
),
validator: (value) {
if (value.isEmpty) {
return AppTranslations.of(context).text('contact.google.empty');
}
return null;
},
);
final addPhoneNumber = ListTile(
contentPadding: EdgeInsets.only(top: 15),
title: ButtonTheme(
height: 45,
child: RaisedButton(
color: Colors.white,
onPressed: () async {
var phone = await showDialog(
context: context, builder: (_) => PhoneEditor());
_save(phone);
},
child: Text("Add Phone",
style: TextStyle(
fontSize: 18,
)),
),
));
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: AppBar(
backgroundColor: primaryColor,
title: LocalText(
context,
"contact.title",
fontSize: 20,
color: Colors.white,
),
actions: <Widget>[
IconButton(
icon: Icon(Icons.send),
onPressed: () {
if (!_formKey.currentState.validate()) return;
showConfirmDialog(context, "contact.confrim", () {
_submit();
});
})
],
),
body: Form(
key: _formKey,
child: ListView(
shrinkWrap: true,
padding: EdgeInsets.only(left: 24.0, right: 24.0),
children: <Widget>[
this.phones.isNotEmpty
? ConstrainedBox(
constraints: BoxConstraints(maxHeight: 1000),
child: ListView.builder(
shrinkWrap: true,
itemBuilder: (context, index) {
return Stack(
alignment: const Alignment(1.0, 1.0),
children: <Widget>[
new TextField(
controller: new TextEditingController(
text: this.phones[index]),
cursorColor: primaryColor,
readOnly: true,
decoration: new InputDecoration(
border: InputBorder.none,
focusedBorder: InputBorder.none,
icon: Icon(
Icons.phone,
color: primaryColor,
),
),
),
new FlatButton(
onPressed: () {
setState(() {
this.phones.remove(this.phones[index]);
});
},
child: new Icon(
Icons.cancel,
size: 25,
))
],
);
},
itemCount: this.phones.length,
),
)
: Container(),
addPhoneNumber,
LocalTextField(
textEditingController: _deliveryPhone,
icon: Icon(
Icons.phone_forwarded,
color: primaryColor,
),
labelKey: "contact.delivery.phone",
),
emailBox,
faceBookBox,
googleBox,
LocalTextField(
textEditingController: _address,
icon: Icon(
Icons.location_on,
color: primaryColor,
),
labelKey: "contact.address",
maxLines: 3,
),
],
),
),
));
}
_save(String phone) {
if (phone == null) return;
setState(() {
this.phones.add(phone);
});
}
_submit() async {
setState(() {
_isLoading = true;
});
try {
widget.setting.email = _email.text;
widget.setting.facebook = _facebook.text;
widget.setting.website = _website.text;
widget.setting.phones = this.phones;
widget.setting.address = _address.text;
widget.setting.deliveryPhone = _deliveryPhone.text;
var mainModel = Provider.of<MainModel>(context);
await mainModel.updateContact(widget.setting);
Navigator.pop(context);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
}

View File

@@ -0,0 +1,120 @@
import 'package:fcs/domain/entities/user.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/pages/customer/model/customer_model.dart';
import 'package:fcs/pages/main/util.dart';
import 'package:fcs/pages/widgets/display_text.dart';
import 'package:fcs/pages/widgets/fcs_id_icon.dart';
import 'package:fcs/pages/widgets/progress.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
typedef void FindCallBack();
class CustomerEditor extends StatefulWidget {
final User customer;
const CustomerEditor({this.customer});
@override
_CustomerEditorState createState() => _CustomerEditorState();
}
class _CustomerEditorState extends State<CustomerEditor> {
bool _isLoading = false;
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
var phoneNumberBox = Row(
children: <Widget>[
Expanded(
child: DisplayText(
text: widget.customer.phoneNumber,
labelText: getLocalString(context, "customer.phone"),
iconData: Icons.phone,
)),
IconButton(
icon: Icon(Icons.open_in_new, color: primaryColor),
onPressed: () => call(context, widget.customer.phoneNumber)),
],
);
return LocalProgress(
inAsyncCall: _isLoading,
child: SafeArea(
child: Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
shadowColor: Colors.transparent,
centerTitle: true,
leading: new IconButton(
icon: new Icon(
Icons.close,
color: primaryColor,
size: 30,
),
onPressed: () => Navigator.of(context).pop(),
),
title: Text(
widget.customer.name,
style: TextStyle(
fontSize: 20,
color: primaryColor,
),
),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: ListView(
children: <Widget>[
phoneNumberBox,
DisplayText(
text: widget.customer.fcsID,
labelText: getLocalString(context, "customer.fcs.id"),
icon: FcsIDIcon(),
),
DisplayText(
text: widget.customer.status,
labelText: getLocalString(context, "customer.status"),
iconData: Icons.add_alarm,
),
SizedBox(
height: 20,
),
widget.customer.requested
? fcsButton(
context,
getLocalString(
context, "customer.invitation.request.confirm"),
callack: _add)
: Container()
],
),
),
),
));
}
_add() async {
showConfirmDialog(context, "customer.invitation.request.confirm", () async {
setState(() {
_isLoading = true;
});
if (widget.customer == null) return;
CustomerModel customerModel =
Provider.of<CustomerModel>(context, listen: false);
try {
await customerModel.acceptRequest(widget.customer.id);
Navigator.pop(context);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
});
}
}

View File

@@ -0,0 +1,240 @@
import 'package:fcs/domain/constants.dart';
import 'package:fcs/domain/entities/user.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/pages/chat/message_detail.dart';
import 'package:fcs/pages/chat/model/message_model.dart';
import 'package:fcs/pages/customer/customer_editor.dart';
import 'package:fcs/pages/customer/model/customer_model.dart';
import 'package:fcs/pages/main/model/main_model.dart';
import 'package:fcs/pages/user_search/user_serach.dart';
import 'package:fcs/pages/widgets/bottom_up_page_route.dart';
import 'package:fcs/pages/widgets/local_text.dart';
import 'package:fcs/pages/widgets/progress.dart';
import 'package:flutter/cupertino.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:share/share.dart';
import 'invitation_create.dart';
class CustomerList extends StatefulWidget {
@override
_CustomerListState createState() => _CustomerListState();
}
class _CustomerListState extends State<CustomerList> {
var dateFormatter = new DateFormat('dd MMM yyyy - hh:mm:ss a');
final double dotSize = 15.0;
bool _isLoading = false;
@override
Widget build(BuildContext context) {
var customerModel = Provider.of<CustomerModel>(context);
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: AppBar(
centerTitle: true,
leading: new IconButton(
icon: new Icon(CupertinoIcons.back),
onPressed: () => Navigator.of(context).pop(),
),
actions: <Widget>[
IconButton(
icon: Icon(Icons.search, color: Colors.white),
onPressed: () => searchUser(context, callbackUserSelect: (u) {
_select(u);
})),
],
backgroundColor: primaryColor,
title: LocalText(
context,
"customer.list.title",
fontSize: 20,
color: Colors.white,
),
),
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: [
Expanded(
child: ListView.separated(
separatorBuilder: (context, index) => Divider(
color: Colors.grey,
),
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: customerModel.customers.length,
itemBuilder: (BuildContext context, int index) {
User customer = customerModel.customers[index];
return _item(customer);
}),
),
],
),
),
);
}
Widget _item(User customer) {
return InkWell(
onTap: () => _gotoMsg(customer),
child: Padding(
padding: const EdgeInsets.only(left: 12.0, right: 12),
child: Row(
children: <Widget>[
Expanded(
child: new Padding(
padding: const EdgeInsets.symmetric(vertical: 2.0),
child: new Row(
children: <Widget>[
InkWell(
onTap: () => _select(customer),
child: Padding(
padding: const EdgeInsets.all(5.0),
child: Container(
padding: const EdgeInsets.only(
left: 10.0, right: 10, top: 6, bottom: 6),
decoration: BoxDecoration(
color: primaryColor,
borderRadius:
BorderRadius.all(Radius.circular(35.0))),
child: Text(
customer.initial,
style: TextStyle(fontSize: 30, color: Colors.white),
),
),
),
),
new Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 8.0),
child: new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 2.0),
child: new Text(
customer.name,
style: new TextStyle(
fontSize: 20.0, color: primaryColor),
),
),
Padding(
padding: const EdgeInsets.only(top: 2.0),
child: new Text(
customer.getLastMessage,
style: new TextStyle(
fontSize: 15.0, color: Colors.grey),
),
),
],
),
),
),
],
),
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Padding(
padding: const EdgeInsets.only(right: 5),
child: _status(customer.status),
),
Padding(
padding: const EdgeInsets.only(right: 5),
child: Text(customer.getLastMessageTime),
),
getCount(customer),
customer.status == user_invited_status
? FlatButton(
onPressed: () => _share(customer),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0),
side: BorderSide(color: primaryColor)),
child: Row(
children: [
Text(
"Share",
style: TextStyle(fontSize: 12),
),
Icon(Icons.share, color: primaryColor),
],
),
)
: Container(),
],
),
],
),
),
);
}
Widget getCount(User customer) {
return customer.fcsUnseenCount != null && customer.fcsUnseenCount > 0
? Container(
padding: const EdgeInsets.all(8.0),
decoration:
BoxDecoration(shape: BoxShape.circle, color: secondaryColor),
child: Text(customer.getFcsUnseenCount,
style: TextStyle(color: Colors.white)),
)
: Container();
}
Widget _status(String status) {
return user_requested_status == status
? Text(status, style: TextStyle(color: primaryColor, fontSize: 14))
: Container();
}
_select(User customer) {
Navigator.of(context)
.push(BottomUpPageRoute(CustomerEditor(customer: customer)));
}
_gotoMsg(User customer) {
MessageModel messageModel =
Provider.of<MessageModel>(context, listen: false);
messageModel.initQuery(customer.id);
Navigator.of(context)
.push(BottomUpPageRoute(MessageDetail(
receiverID: customer.id,
receiverName: customer.name,
messageModel: messageModel,
)))
.then((value) {
if (customer.fcsUnseenCount > 0) {
messageModel.seenMessages(customer.id, false);
}
});
if (customer.fcsUnseenCount > 0) {
messageModel.seenMessages(customer.id, false);
}
}
_share(User user) async {
MainModel mainModel = Provider.of<MainModel>(context, listen: false);
String appUrl = mainModel.setting.appUrl;
final RenderBox box = context.findRenderObject();
await Share.share(
"Join us on FCS Logistics App. Here is the link:\n $appUrl\n" +
user.share,
subject: "Invitation to FCS Logistics App",
sharePositionOrigin: box.localToGlobal(Offset.zero) & box.size);
}
}

View File

@@ -0,0 +1,159 @@
import 'package:country_code_picker/country_code_picker.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/pages/customer/model/customer_model.dart';
import 'package:fcs/pages/main/util.dart';
import 'package:fcs/pages/widgets/local_text.dart';
import 'package:fcs/pages/widgets/progress.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class InvitationCreate extends StatefulWidget {
@override
_InvitationCreateState createState() => _InvitationCreateState();
}
class _InvitationCreateState extends State<InvitationCreate> {
TextEditingController _nameController = new TextEditingController();
TextEditingController _phoneController = new TextEditingController();
bool _isLoading = false;
String dialCode;
@override
void initState() {
super.initState();
dialCode = "+95";
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
shadowColor: Colors.transparent,
centerTitle: true,
leading: new IconButton(
icon: new Icon(
Icons.close,
color: primaryColor,
),
onPressed: () => Navigator.of(context).pop(),
),
title: LocalText(
context,
"invitation.new",
fontSize: 20,
color: primaryColor,
),
),
body: Container(
padding: EdgeInsets.all(18),
child: ListView(
children: <Widget>[
fcsInput(getLocalString(context, "customer.name"), Icons.person,
controller: _nameController, autoFocus: false),
SizedBox(height: 10),
Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Icon(
Icons.phone,
color: primaryColor,
),
),
Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.grey[400], width: 1),
borderRadius: BorderRadius.all(Radius.circular(12.0))),
child: CountryCodePicker(
onChanged: _countryChange,
initialSelection: dialCode,
countryFilter: ['+95', '+1'],
showCountryOnly: false,
showOnlyCountryWhenClosed: false,
alignLeft: false,
textStyle: TextStyle(
fontSize: 16,
),
),
),
SizedBox(
width: 10,
),
Flexible(
child: Container(
padding: EdgeInsets.only(top: 10, bottom: 10),
child: TextFormField(
controller: _phoneController,
cursorColor: primaryColor,
textAlign: TextAlign.left,
keyboardType: TextInputType.phone,
style: TextStyle(
fontSize: 18,
),
decoration: InputDecoration(
fillColor: Colors.white,
labelText: getLocalString(context, "customer.phone"),
labelStyle:
TextStyle(fontSize: 16, color: Colors.grey),
filled: true,
focusedBorder: UnderlineInputBorder(
borderSide:
BorderSide(color: Colors.grey, width: 1.0)),
),
),
),
),
],
),
SizedBox(height: 20),
fcsButton(context, getLocalString(context, "invite.btn"),
callack: _invite),
],
),
),
),
);
}
_countryChange(CountryCode countryCode) {
setState(() {
dialCode = countryCode.dialCode;
});
}
_invite() async {
String userName = _nameController.text;
String phoneNumber = dialCode + _phoneController.text;
if (userName == null ||
userName == "" ||
phoneNumber == null ||
phoneNumber == "") {
showMsgDialog(context, "Error", "Invalid name or phone number");
return;
}
setState(() {
_isLoading = true;
});
try {
CustomerModel customerModel =
Provider.of<CustomerModel>(context, listen: false);
await customerModel.inviteUser(userName, phoneNumber);
Navigator.pop(context);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
}

View File

@@ -0,0 +1,100 @@
import 'package:fcs/domain/entities/user.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/pages/customer/model/customer_model.dart';
import 'package:fcs/pages/main/util.dart';
import 'package:fcs/pages/widgets/display_text.dart';
import 'package:fcs/pages/widgets/progress.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
typedef void FindCallBack();
class InvitationEditor extends StatefulWidget {
final User customer;
const InvitationEditor({this.customer});
@override
_InvitationEditorState createState() => _InvitationEditorState();
}
class _InvitationEditorState extends State<InvitationEditor> {
bool _isLoading = false;
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
var phoneNumberBox = Row(
children: <Widget>[
Expanded(
child: DisplayText(
text: widget.customer.phoneNumber,
labelText: getLocalString(context, "customer.phone"),
iconData: Icons.phone,
)),
IconButton(
icon: Icon(Icons.open_in_new, color: primaryColor),
onPressed: () => call(context, widget.customer.phoneNumber)),
],
);
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
shadowColor: Colors.transparent,
centerTitle: true,
leading: new IconButton(
icon: new Icon(
Icons.close,
color: primaryColor,
size: 30,
),
onPressed: () => Navigator.of(context).pop(),
),
title: Text(
widget.customer.name,
style: TextStyle(fontSize: 20, color: primaryColor),
),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: <Widget>[
Expanded(
child: ListView(
children: [phoneNumberBox],
),
),
fcsButton(context, getLocalString(context, "btn.delete"),
callack: _delete)
],
),
),
));
}
_delete() async {
showConfirmDialog(context, "invitation.confirm.delete", () async {
setState(() {
_isLoading = true;
});
if (widget.customer == null) return;
CustomerModel customerModel =
Provider.of<CustomerModel>(context, listen: false);
try {
await customerModel.deleteInvite(widget.customer.phoneNumber);
Navigator.pop(context);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
});
}
}

View File

@@ -0,0 +1,98 @@
import 'dart:async';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:fcs/data/services/services.dart';
import 'package:fcs/domain/constants.dart';
import 'package:fcs/domain/entities/user.dart';
import 'package:fcs/pages/main/model/base_model.dart';
import 'package:logging/logging.dart';
class CustomerModel extends BaseModel {
final log = Logger('CustomerModel');
List<User> customers = [];
List<User> invitations = [];
StreamSubscription<QuerySnapshot> customerListener;
StreamSubscription<QuerySnapshot> invitationListener;
@override
void privilegeChanged() {
super.privilegeChanged();
_loadCustomer();
_loadInvitations();
}
@override
logout() async {
if (customerListener != null) customerListener.cancel();
if (invitationListener != null) invitationListener.cancel();
customers = [];
invitations = [];
}
Future<void> inviteUser(String userName, String phoneNumber) {
return Services.instance.userService.inviteUser(userName, phoneNumber);
}
Future<void> deleteInvite(String phoneNumber) {
return Services.instance.userService.deleteInvite(phoneNumber);
}
Future<void> acceptRequest(String userID) {
return Services.instance.userService.acceptRequest(userID);
}
Future<void> _loadCustomer() async {
if (user == null || !user.hasCustomers()) return;
try {
if (customerListener != null) customerListener.cancel();
customerListener = Firestore.instance
.collection("/$user_collection")
.where("is_sys_admin", isEqualTo: false)
.orderBy("message_time", descending: true)
.snapshots()
.listen((QuerySnapshot snapshot) {
customers.clear();
customers = snapshot.documents.map((documentSnapshot) {
var user =
User.fromMap(documentSnapshot.data, documentSnapshot.documentID);
return user;
}).toList();
notifyListeners();
});
} catch (e) {
log.warning("error:$e");
}
}
Future<void> _loadInvitations() async {
if (user == null || !user.hasCustomers()) return;
try {
if (invitationListener != null) invitationListener.cancel();
invitationListener = Firestore.instance
.collection("/$invitations_collection")
.snapshots()
.listen((QuerySnapshot snapshot) {
invitations.clear();
invitations = snapshot.documents.map((documentSnapshot) {
var user =
User.fromMap(documentSnapshot.data, documentSnapshot.documentID);
return user;
}).toList();
notifyListeners();
});
} catch (e) {
log.warning("error:$e");
}
}
Future<User> getUser(String id) async {
String path = "/$user_collection";
var snap = await Firestore.instance.collection(path).document(id).get();
return User.fromMap(snap.data, snap.documentID);
}
}

View File

@@ -1,362 +0,0 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/employee_model.dart';
import 'package:fcs/model/language_model.dart';
import 'package:fcs/model/main_model.dart';
import 'package:fcs/model/user_model.dart';
import 'package:fcs/fcs/common/helpers/theme.dart';
import 'package:fcs/vo/role.dart';
import 'package:fcs/vo/user.dart';
import 'package:fcs/widget/local_text.dart';
import 'package:fcs/widget/localization/app_translations.dart';
import 'package:fcs/widget/progress.dart';
import '../fcs/common/pages/util.dart';
typedef void FindCallBack();
class CustomerEditor extends StatefulWidget {
final User customer;
const CustomerEditor({this.customer});
@override
_CustomerEditorState createState() => _CustomerEditorState();
}
class _CustomerEditorState extends State<CustomerEditor> {
TextEditingController _name = new TextEditingController();
TextEditingController _phone = new TextEditingController();
TextEditingController _phoneInput = new TextEditingController();
TextEditingController _status = new TextEditingController();
final _formKey = GlobalKey<FormState>();
bool _isLoading = false;
String currentBizId;
bool isSend = false;
User user;
User selectedUser;
List<Privilege> privileges = [];
@override
void initState() {
super.initState();
// privileges = Provider.of<UserModel>(context, listen: false).privileges;
if (widget.customer != null) {
_name.text = widget.customer.name;
_phone.text = widget.customer.phone;
_status.text = widget.customer.status;
// privileges.forEach((p) => widget.employee.privilegeIds.contains(p.id)
// ? p.isChecked = true
// : p.isChecked = false);
}
}
List<Widget> showprivilegeList(BuildContext context, UserModel userModel) {
return privileges.map((p) {
return new ListTile(
title: new Row(
children: <Widget>[
new Checkbox(
value: p.isChecked == null ? false : p.isChecked,
activeColor: primaryColor,
onChanged: (bool value) {
setState(() {
p.isChecked = value;
});
}),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Text(
p.name,
style: TextStyle(
fontSize: 15.0,
),
),
// Container(
// width: MediaQuery.of(context).size.width * 0.5,
// child: new Text(
// userModel.getPrivileges[index].desc,
// style:
// TextStyle(fontSize: 12.0, color: Colors.grey[600]),
// ),
// ),
],
),
],
));
}).toList();
}
Widget phoneInputbox(BuildContext context, FindCallBack findCallBack) {
var languageModel = Provider.of<LanguageModel>(context);
return Container(
padding: EdgeInsets.only(top: 10),
child: Stack(
alignment: const Alignment(1.2, 1.0),
children: <Widget>[
TextFormField(
controller: _phoneInput,
autofocus: false,
cursorColor: primaryColor,
keyboardType: TextInputType.phone,
style: textStyle,
decoration: new InputDecoration(
labelText: AppTranslations.of(context).text('employee.phone'),
labelStyle: languageModel.isEng ? labelStyle : labelStyleMM,
icon: Icon(
Icons.phone,
color: primaryColor,
),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: primaryColor, width: 1.0)),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: primaryColor, width: 1.0)),
),
),
new FlatButton(
onPressed: () {
this.isSend = true;
findCallBack();
},
child: new Icon(
Icons.search,
size: 25,
))
],
));
}
@override
Widget build(BuildContext context) {
var userModel = Provider.of<UserModel>(context);
MainModel mainModel = Provider.of<MainModel>(context);
final namebox = TextFormField(
controller: _name,
autofocus: false,
readOnly: true,
cursorColor: primaryColor,
decoration: new InputDecoration(
border: InputBorder.none,
focusedBorder: InputBorder.none,
icon: Icon(
Icons.person,
color: primaryColor,
),
),
);
final displayPhoneNo = TextFormField(
controller: _phone,
autofocus: false,
readOnly: true,
cursorColor: primaryColor,
decoration: new InputDecoration(
border: InputBorder.none,
focusedBorder: InputBorder.none,
icon: Icon(
Icons.phone,
color: primaryColor,
),
),
);
var phoneNumberBox = Row(
children: <Widget>[
Expanded(child: displayPhoneNo),
Expanded(
child: InkWell(
onTap: () => call(context, _phone.text),
child: Icon(
Icons.open_in_new,
color: Colors.grey,
size: 15,
),
),
),
],
);
final statusbox = TextFormField(
controller: _status,
autofocus: false,
readOnly: true,
cursorColor: primaryColor,
decoration: new InputDecoration(
border: InputBorder.none,
focusedBorder: InputBorder.none,
icon: Icon(
Icons.av_timer,
color: primaryColor,
),
),
);
final updateButton = Container(
padding: EdgeInsets.only(top: 40),
child: Container(
height: 45.0,
decoration: BoxDecoration(
color: primaryColor,
shape: BoxShape.rectangle,
),
child: ButtonTheme(
minWidth: 900.0,
height: 100.0,
child: FlatButton(
onPressed: () {},
child: LocalText(
context,
'customer.update',
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
),
),
);
final addButton = Container(
padding: EdgeInsets.only(top: 40),
child: Container(
height: 45.0,
decoration: BoxDecoration(
color: primaryColor,
shape: BoxShape.rectangle,
),
child: ButtonTheme(
minWidth: 900.0,
height: 100.0,
child: FlatButton(
onPressed: () {},
child: LocalText(
context,
'customer.add',
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
),
),
);
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: LocalText(
context,
"customer.form.title",
fontSize: 20,
color: Colors.white,
),
// actions: <Widget>[
// widget.customer == null || !mainModel.showHistoryBtn()
// ? Container()
// : IconButton(
// icon: Icon(Icons.history),
// onPressed: () {
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => DocumentLogPage(
// docID: widget.customer.docID)),
// );
// },
// ),
// ],
),
body: ListView(
shrinkWrap: true,
padding: EdgeInsets.only(left: 24.0, right: 24.0),
children: <Widget>[
widget.customer == null
? phoneInputbox(context, () => _findUser(context))
: phoneNumberBox,
widget.customer == null
? this.isSend ? namebox : Container()
: namebox,
statusbox,
// widget.customer == null ? addButton : updateButton,
SizedBox(
height: 20,
)
],
),
));
}
_add(BuildContext context) async {
if (selectedUser == null) return;
setState(() {
_isLoading = true;
});
var employeeModel = Provider.of<EmployeeModel>(context);
try {
await employeeModel.updatePrivileges(
this.selectedUser.docID, privilegesIDs());
Navigator.pop(context);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
List<String> privilegesIDs() {
return this.privileges.where((p) => p.isChecked).map((p) => p.id).toList();
}
_save() async {
setState(() {
_isLoading = true;
});
if (widget.customer == null) return;
var employeeModel = Provider.of<EmployeeModel>(context);
try {
await employeeModel.updatePrivileges(
widget.customer.docID, privilegesIDs());
Navigator.pop(context);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
_findUser(BuildContext context) async {
var userModel = Provider.of<UserModel>(context);
setState(() {
_isLoading = true;
});
try {
selectedUser = await userModel.findUser(_phoneInput.text);
setState(() {
isSend = true;
_name.text = selectedUser.name;
if (selectedUser.privilegeIds != null) {
privileges.forEach((p) => selectedUser.privilegeIds.contains(p.id)
? p.isChecked = true
: p.isChecked = false);
}
});
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
}

View File

@@ -1,143 +0,0 @@
import 'package:fcs/model/customer_model.dart';
import 'package:fcs/pages/search_page.dart';
import 'package:fcs/widget/bottom_up_page_route.dart';
import 'package:fcs/widget/localization/app_translations.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:fcs/fcs/common/helpers/theme.dart';
import 'package:fcs/widget/progress.dart';
import '../fcs/common/helpers/theme.dart';
import '../vo/user.dart';
import '../widget/local_text.dart';
import 'customer_editor.dart';
import 'invitation_page.dart';
import '../fcs/common/pages/util.dart';
class CustomerList extends StatefulWidget {
@override
_CustomerListState createState() => _CustomerListState();
}
class _CustomerListState extends State<CustomerList> {
var dateFormatter = new DateFormat('dd MMM yyyy - hh:mm:ss a');
final double dotSize = 15.0;
bool _isLoading = false;
@override
Widget build(BuildContext context) {
var customerModel = Provider.of<CustomerModel>(context);
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: AppBar(
centerTitle: true,
leading: new IconButton(
icon: new Icon(Icons.close),
onPressed: () => Navigator.of(context).pop(),
),
actions: <Widget>[
IconButton(
icon: Icon(
Icons.search,
color: Colors.white,
),
iconSize: 30,
onPressed: () => showPlacesSearch(context),
),
],
backgroundColor: primaryColor,
title: LocalText(
context,
'customer.list.title',
color: Colors.white,
fontSize: 20,
),
),
floatingActionButton: FloatingActionButton.extended(
onPressed: () {
Navigator.of(context).push(BottomUpPageRoute(InvitationPage()));
},
icon: Icon(Icons.add),
label: Text(AppTranslations.of(context).text("customer.invite")),
backgroundColor: primaryColor,
),
body: new ListView.separated(
separatorBuilder: (context, index) => Divider(
color: Colors.black,
),
scrollDirection: Axis.vertical,
padding: EdgeInsets.only(left: 15, right: 15, top: 15),
shrinkWrap: true,
itemCount: customerModel.customers.length,
itemBuilder: (BuildContext context, int index) {
User user = customerModel.customers[index];
return Stack(
children: <Widget>[
InkWell(
onTap: () {
Navigator.of(context).push(
BottomUpPageRoute(CustomerEditor(customer: user)));
},
child: Row(
children: <Widget>[
Expanded(
child: new Padding(
padding: const EdgeInsets.symmetric(vertical: 10.0),
child: new Row(
children: <Widget>[
new Padding(
padding: new EdgeInsets.symmetric(
horizontal: 32.0 - dotSize / 2),
child: Icon(
Feather.user,
color: primaryColor,
size: 40,
),
),
new Expanded(
child: new Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
new Text(
user.name,
style: new TextStyle(
fontSize: 15.0,
color: primaryColor),
),
Padding(
padding:
const EdgeInsets.only(top: 8.0),
child: new Text(
user.phoneNumber,
style: new TextStyle(
fontSize: 15.0,
color: Colors.grey),
),
),
],
),
),
],
),
),
),
Padding(
padding: const EdgeInsets.only(right: 10),
child: getStatus(user.status),
),
],
),
),
],
);
}),
),
);
}
}

View File

@@ -1,519 +0,0 @@
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:quiver/async.dart';
import 'package:fcs/model/do_model.dart';
import 'package:fcs/model/language_model.dart';
import 'package:fcs/model/main_model.dart';
import 'package:fcs/model/po_model.dart';
import 'package:fcs/model/product_model.dart';
import 'package:fcs/pages/do/photo_page.dart';
import 'package:fcs/fcs/common/helpers/theme.dart';
import 'package:fcs/vo/do.dart';
import 'package:fcs/widget/img_file.dart';
import 'package:fcs/widget/local_text.dart';
import 'package:fcs/widget/localization/app_translations.dart';
import 'package:fcs/widget/my_data_table.dart';
import 'package:fcs/widget/number_cell.dart';
import 'package:fcs/widget/progress.dart';
import '../../fcs/common/pages/util.dart';
class DeliveryItem extends StatefulWidget {
final DOSubmission doSubmission;
const DeliveryItem({this.doSubmission});
@override
_DeliveryItemState createState() => _DeliveryItemState();
}
class _DeliveryItemState extends State<DeliveryItem> {
var dateFormatter = new DateFormat('dd MMM yyyy');
final numberFormatter = new NumberFormat("#,###");
bool _isLoading = false;
TextEditingController _date = new TextEditingController();
TextEditingController _number = new TextEditingController();
TextEditingController _licence = new TextEditingController();
TextEditingController _driver = new TextEditingController();
TextEditingController _carNo = new TextEditingController();
TextEditingController _type = new TextEditingController();
TextEditingController _name = new TextEditingController();
TextEditingController _bizName = new TextEditingController();
TextEditingController _storage = new TextEditingController();
TextEditingController _comment = new TextEditingController();
DOSubmission doObj = DOSubmission();
int _count;
DateTime _result;
File storageChargeFile;
File receiptImageFile;
@override
void initState() {
super.initState();
var mainModel = Provider.of<MainModel>(context, listen: false);
var doModel = Provider.of<DOModel>(context, listen: false);
doObj = widget.doSubmission;
_date.text = doObj.deliveryDate != null
? dateFormatter.format(doObj.deliveryDate)
: "";
_number.text = doObj.doNumber.toString();
_licence.text = doObj.driverLicenseNumber;
_driver.text = doObj.driverName;
_carNo.text = doObj.carNo;
_type.text = doObj.type;
_name.text = doObj.userName;
_bizName.text = doObj.bizName;
_storage.text = doObj.storageCharge == null
? ""
: numberFormatter.format(doObj.storageCharge);
_comment.text = doObj.comment;
if (doObj.deliveryStatus == 'initiated') {
_count = doModel.timber;
Duration diff = DateTime.now().difference(doObj.deliveryInitiatedTime);
if (diff.inMinutes < mainModel.setting.deliveryStartWaitMin) {
var time = mainModel.setting.deliveryStartWaitMin - diff.inMinutes;
new CountdownTimer(
new Duration(minutes: time), new Duration(seconds: 1))
.listen((data) {
if (mounted) {
setState(() {
_count = data.remaining.inSeconds;
doModel.addTimber(_count);
});
}
});
}
} else {
_count = 0;
}
_load();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
MainModel mainModel = Provider.of<MainModel>(context);
bool isBuyer = mainModel.user.isBuyer();
String formattedTime;
if (doObj.deliveryStatus == 'initiated') {
_result = DateTime(
doObj.deliveryInitiatedTime.year,
doObj.deliveryInitiatedTime.month,
doObj.deliveryInitiatedTime.day,
doObj.deliveryInitiatedTime.hour,
doObj.deliveryInitiatedTime.minute,
_count);
formattedTime = DateFormat.ms().format(_result);
}
final dateBox = Container(
padding: EdgeInsets.only(left: 20, top: 15),
child: Row(
children: <Widget>[
LocalText(context, "do.date"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
_date.text,
style: textStyle,
),
)
],
),
);
final numberBox = Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(
children: <Widget>[
LocalText(context, "do.do_num"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
_number.text,
style: textStyle,
),
)
],
),
);
final driverBox = Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(
children: <Widget>[
LocalText(context, "do.driver"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
_driver.text,
style: textStyle,
),
)
],
),
);
final carNoBox = Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(
children: <Widget>[
LocalText(context, "do.car"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
_carNo.text,
style: textStyle,
),
)
],
),
);
final licenceBox = Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(
children: <Widget>[
LocalText(context, "do.licence"),
ImageFile(
enabled: false,
title: "Image",
initialImgUrl: doObj.driverLicenceUrl,
onFile: (file) {}),
],
),
);
final statusBox = Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(
children: <Widget>[
LocalText(context, "do.status"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
doObj.status,
style: doObj.isPending
? textHighlightBlueStyle
: doObj.isApproved
? textHighlightGreenStyle
: textHighlightRedStyle,
),
),
],
),
);
final deliveryStatusBox = Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(
children: <Widget>[
LocalText(context, "do.delivery.status"),
Container(
padding: EdgeInsets.only(left: 10, right: 15),
child: Text(
doObj.getDeliveryStatus,
style: textStyle,
),
),
doObj.deliveryStatus == 'initiated'
? Text(
"(can start in $formattedTime)",
style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold),
)
: Container()
],
),
);
final typeBox = Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(
children: <Widget>[
LocalText(context, "do.type"),
Container(
padding: EdgeInsets.only(left: 20),
child: Text(
_type.text,
style: textStyle,
),
)
],
),
);
final userNameBox = Container(
padding: EdgeInsets.only(top: 5, left: 20),
child: Row(
children: <Widget>[
LocalText(context, "do.name"),
Container(
padding: EdgeInsets.only(left: 20),
child: Text(
_name.text,
style: textStyle,
),
)
],
),
);
final bizNameBox = Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(
children: <Widget>[
LocalText(context, "do.biz"),
Container(
padding: EdgeInsets.only(left: 20),
child: Text(
_bizName.text,
style: textStyle,
),
)
],
),
);
final receiptImagebox = Container(
padding: EdgeInsets.only(left: 20, top: 0),
child: Row(children: <Widget>[
LocalText(context, "do.receipt"),
Container(
padding: EdgeInsets.only(left: 10),
child: ImageFile(
enabled: true,
initialImgUrl: doObj.doReceiptUrl,
title: "Receipt File",
onFile: (file) {
this.receiptImageFile = file;
},
),
),
]));
final storageBox = Container(
padding: EdgeInsets.only(left: 20),
child: Row(
children: <Widget>[
LocalText(context, "do.storage_charge"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
_storage.text,
style: textStyle,
),
)
],
),
);
final storagePaymentBox = Container(
padding: EdgeInsets.only(left: 20),
child: Row(children: <Widget>[
LocalText(context, "do.storage_receipt"),
ImageFile(
enabled: false,
title: "Receipt File",
initialImgUrl: this.doObj.storageReceiptUrl,
onFile: (file) {
this.storageChargeFile = file;
}),
]));
final commentBox = Container(
padding: EdgeInsets.only(top: 5, left: 20),
child: Row(
children: <Widget>[
LocalText(context, "do.comment"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
_comment.text,
style: textStyle,
),
)
],
),
);
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: AppBar(
backgroundColor: primaryColor,
title: Text(AppTranslations.of(context).text("delivery"),
style: Provider.of<LanguageModel>(context).isEng
? TextStyle(fontSize: 18)
: TextStyle(fontSize: 18, fontFamily: 'MyanmarUnicode')),
actions: <Widget>[
isBuyer
? Container()
: PopupMenuButton(
onSelected: _select,
itemBuilder: (context) => List<PopupMenuEntry>.from([
PopupMenuItem(
enabled: this.doObj.isApproved,
value: 5,
child: Text("End Delivery"),
),
]),
),
],
),
body: Container(
padding: EdgeInsets.only(left: 10, right: 10, top: 10, bottom: 10),
child: Card(
elevation: 23,
child: ListView(
children: <Widget>[
Column(
children: <Widget>[
dateBox,
Divider(),
numberBox,
Divider(),
userNameBox,
Divider(),
bizNameBox,
Divider(),
typeBox,
Divider(),
statusBox,
Divider(),
doObj.comment == null || doObj.comment == ''
? Container()
: commentBox,
doObj.comment == null || doObj.comment == ''
? Container()
: Divider(),
driverBox,
Divider(),
carNoBox,
Divider(),
licenceBox,
Divider(),
receiptImagebox,
Divider(),
doObj.hasStorageCharge() ? storageBox : Container(),
doObj.hasStorageCharge() ? Divider() : Container(),
doObj.hasStorageCharge()
? storagePaymentBox
: Container(),
doObj.isApproved || doObj.isClosed
? deliveryStatusBox
: Container(),
doObj.isApproved || doObj.isClosed
? Divider()
: Container(),
Container(
padding: EdgeInsets.only(top: 10),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: MyDataTable(
headingRowHeight: 40,
columnSpacing: 40,
columns: [
MyDataColumn(
label: LocalText(context, "do.product"),
),
MyDataColumn(
label: LocalText(context, "do.storage"),
),
MyDataColumn(
label: LocalText(context, "do.quantity"),
numeric: true),
],
rows: getProductRow(doObj.doLines),
),
),
),
SizedBox(
height: 15,
)
],
),
],
),
),
)),
);
}
List<MyDataRow> getProductRow(List<DOLine> doLines) {
ProductModel productModel = Provider.of<ProductModel>(context);
if (doLines.isNotEmpty) {
doLines.forEach((d) {
productModel.products.forEach((p) {
if (p.id == d.productID) {
d.displayOrder = p.displayOrder;
} else {
return;
}
});
});
doLines.sort((p1, p2) => p1.displayOrder.compareTo(p2.displayOrder));
}
return doLines.map((d) {
return MyDataRow(
cells: [
MyDataCell(
new Text(
d.productName,
style: textStyle,
),
),
MyDataCell(
new Text(d.storageName, style: textStyle),
),
MyDataCell(NumberCell(d.qty)),
],
);
}).toList();
}
_select(s) {
if (s == 5) {
if (receiptImageFile == null) {
showMsgDialog(context, "Error", "Please insert delivery receipt file");
return;
}
showConfirmDialog(context, "delivery.confirm", () {
_endDelivery(receiptImageFile);
});
}
}
Future<void> _load() async {
POSubmissionModel poModel =
Provider.of<POSubmissionModel>(context, listen: false);
var _doSub = await poModel.loadDOLines(doObj);
setState(() {
doObj.doLines = _doSub.doLines;
});
}
_endDelivery(dynamic receiptFile) async {
Uint8List bytesPhoto = receiptFile.readAsBytesSync() as Uint8List;
setState(() {
_isLoading = true;
});
try {
DOModel doModel = Provider.of<DOModel>(context);
await doModel.endDelivery(doObj, bytesPhoto);
Navigator.pop(context);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
}

View File

@@ -1,40 +1,23 @@
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/localization/app_translations.dart';
import 'package:fcs/pages/box/model/box_model.dart';
import 'package:fcs/pages/widgets/progress.dart';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/delivery_model.dart';
import 'package:fcs/model/language_model.dart';
import 'package:fcs/model/main_model.dart';
import 'package:fcs/pages/delivery/delivery_item.dart';
import 'package:fcs/fcs/common/pages/util.dart';
import 'package:fcs/fcs/common/helpers/theme.dart';
import 'package:fcs/vo/popup_menu.dart';
import 'package:fcs/widget/localization/app_translations.dart';
import 'package:fcs/widget/popupmenu.dart';
import 'package:fcs/widget/progress.dart';
class DeliveryList extends StatefulWidget {
import 'delivery_list_row.dart';
class DeliverList extends StatefulWidget {
@override
_DeliveryListState createState() => _DeliveryListState();
_DeliverListState createState() => _DeliverListState();
}
class _DeliveryListState extends State<DeliveryList> {
var dateFormatter = new DateFormat('dd MMM yyyy');
final double dotSize = 10.0;
DateTime _selectedDate = DateTime.now();
String status;
int _selectedIndex = 0;
int _dateIndex = 0;
class _DeliverListState extends State<DeliverList> {
bool _isLoading = false;
@override
void initState() {
super.initState();
var doModel = Provider.of<DeliveryModel>(context, listen: false);
_selectedIndex = doModel.popupMenu.index;
_dateIndex = doModel.dateIndex;
_selectedDate = doModel.selectedDate;
}
@override
@@ -42,233 +25,104 @@ class _DeliveryListState extends State<DeliveryList> {
super.dispose();
}
Future<Null> _selectDate(BuildContext context) async {
var deliveryModel = Provider.of<DeliveryModel>(context);
final DateTime picked = await showDatePicker(
context: context,
initialDate: _selectedDate,
firstDate: DateTime(2015, 8),
lastDate: DateTime(2101),
builder: (BuildContext context, Widget child) {
return Theme(
data: ThemeData.light().copyWith(
primaryColor: primaryColor, //Head background
accentColor: secondaryColor, //selection color
dialogBackgroundColor: Colors.white, //Background color
),
child: child,
);
},
);
if (picked != null) {
var pickedDate = new DateTime(picked.year, picked.month, picked.day);
var currentDate = new DateTime(
DateTime.now().year, DateTime.now().month, DateTime.now().day);
this._dateIndex = pickedDate == currentDate ? 0 : 1;
setState(() {
_selectedDate = picked;
deliveryModel.filterData(
status, _selectedDate, _selectedIndex, _dateIndex);
});
}
}
@override
Widget build(BuildContext context) {
var deliveryModel = Provider.of<DeliveryModel>(context);
MainModel mainModel = Provider.of<MainModel>(context);
bool isBuyer = mainModel.user.isBuyer();
var languageModle = Provider.of<LanguageModel>(context);
return Scaffold(
appBar: AppBar(
backgroundColor: primaryColor,
title: Text(
AppTranslations.of(context).text("delivery.title"),
style: languageModle.isEng
? TextStyle()
: TextStyle(fontFamily: 'MyanmarUnicode'),
),
actions: <Widget>[
InkWell(
child: Container(
padding: EdgeInsets.only(top: 15),
child: Stack(
children: <Widget>[
Image.asset(
"assets/date_filter.png",
return LocalProgress(
inAsyncCall: _isLoading,
child: DefaultTabController(
length: 2,
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("delivery")),
actions: <Widget>[
IconButton(
icon: Icon(
Icons.search,
color: Colors.white,
width: 25,
),
_dateIndex == 0
? Container()
: Positioned(
bottom: 15,
right: 10,
child: Container(
width: 10,
height: 10,
decoration: new BoxDecoration(
shape: BoxShape.circle,
color: secondaryColor,
),
),
)
iconSize: 30,
// onPressed: () => showPlacesSearch(context),
),
],
bottom: TabBar(
unselectedLabelColor: Colors.grey,
tabs: [
Tab(
text: "Upcoming",
),
Tab(text: "Delivered"),
],
),
),
onTap: () => _selectDate(context),
),
PopupMenuButton<PopupMenu>(
elevation: 3.2,
onSelected: (selected) {
setState(() {
_selectedIndex = selected.index;
});
if (selected.status == 'All') {
status = null;
} else {
status = selected.status;
}
deliveryModel.filterData(
status, _selectedDate, _selectedIndex, _dateIndex);
},
icon: Container(
width: 30,
height: 30,
decoration: new BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
),
child: Stack(
fit: StackFit.expand,
children: <Widget>[
Icon(
Icons.filter_list,
color: primaryColor,
),
_selectedIndex != 0
? Positioned(
bottom: 0,
right: 0,
child: Container(
width: 10,
height: 10,
decoration: new BoxDecoration(
shape: BoxShape.circle,
color: secondaryColor,
),
),
)
: Container()
],
)),
itemBuilder: (BuildContext context) {
return deliveryStatusMenu.map((PopupMenu choice) {
return PopupMenuItem<PopupMenu>(
value: choice,
child: Row(
children: <Widget>[
Text(choice.status),
SizedBox(
width: 10,
),
_selectedIndex != null && _selectedIndex == choice.index
? Icon(
Icons.check,
color: Colors.grey,
)
: Container(),
],
),
);
}).toList();
}),
],
),
body: LocalProgress(
inAsyncCall: _isLoading,
child: new ListView.builder(
scrollDirection: Axis.vertical,
padding: EdgeInsets.only(left: 15, right: 15, top: 15),
shrinkWrap: true,
itemCount: deliveryModel.dos.length,
itemBuilder: (BuildContext context, int index) {
return Card(
elevation: 10,
color: Colors.white,
child: Row(
children: <Widget>[
Expanded(
child: InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DeliveryItem(
doSubmission: deliveryModel.dos[index])),
);
},
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/truck.png",
width: 50,
height: 50,
color: primaryColor,
),
),
new Expanded(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Text(
deliveryModel.dos[index].doNumber,
style: new TextStyle(
fontSize: 12.0, color: Colors.black),
),
new Text(
deliveryModel.dos[index].deliveryDate ==
null
? ""
: dateFormatter.format(deliveryModel
.dos[index].deliveryDate),
style: new TextStyle(
fontSize: 14.0, color: Colors.grey),
),
!isBuyer
? new Text(
deliveryModel.dos[index].userName,
style: new TextStyle(
fontSize: 12.0,
color: Colors.grey),
)
: Container()
],
),
),
Container(
padding: EdgeInsets.only(right: 15),
child:
getStatus(deliveryModel.dos[index].status),
),
],
),
),
),
),
],
),
);
}),
// floatingActionButton: FloatingActionButton.extended(
// onPressed: () {
// _newPickup();
// },
// icon: Icon(Icons.add),
// label: Text(AppTranslations.of(context).text("boxes.new")),
// backgroundColor: primaryColor,
// ),
body: TabBarView(
children: [
_upComing(),
_completed(),
],
)),
),
);
}
Widget _upComing() {
var boxModel = Provider.of<BoxModel>(context);
return Column(
children: <Widget>[
Expanded(
child: new ListView.separated(
separatorBuilder: (context, index) => Divider(
color: Colors.black,
),
scrollDirection: Axis.vertical,
padding: EdgeInsets.only(top: 15),
shrinkWrap: true,
itemCount: boxModel.upcoming.length,
itemBuilder: (BuildContext context, int index) {
return DeliveryListRow(
box: boxModel.upcoming[index],
isReadOnly: false,
);
}),
),
],
);
}
Widget _completed() {
var boxModel = Provider.of<BoxModel>(context);
return Column(
children: <Widget>[
Expanded(
child: new ListView.separated(
separatorBuilder: (context, index) => Divider(
color: Colors.black,
),
scrollDirection: Axis.vertical,
padding: EdgeInsets.only(top: 15),
shrinkWrap: true,
itemCount: boxModel.completed.length,
itemBuilder: (BuildContext context, int index) {
return DeliveryListRow(
box: boxModel.completed[index],
isReadOnly: false,
);
}),
),
],
);
}
}

View File

@@ -0,0 +1,114 @@
import 'package:fcs/domain/entities/box.dart';
import 'package:fcs/pages/box/box_editor.dart';
import 'package:fcs/pages/main/util.dart';
import 'package:fcs/pages/widgets/bottom_up_page_route.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class DeliveryListRow extends StatefulWidget {
final bool isReadOnly;
final Box box;
const DeliveryListRow({this.box, this.isReadOnly});
@override
_DeliveryListRowState createState() => _DeliveryListRowState();
}
class _DeliveryListRowState extends State<DeliveryListRow> {
final double dotSize = 15.0;
Box _box = new Box();
final DateFormat dateFormat = new DateFormat("dd MMM yyyy");
@override
void initState() {
super.initState();
_box = widget.box;
}
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.only(left: 15, right: 15),
child: InkWell(
onTap: () {
if (widget.isReadOnly) {
// Navigator.push(
// context,
// BottomUpPageRoute(PackageInfo(package: _box)),
// );
} else {
Navigator.push(
context,
BottomUpPageRoute(BoxEditor(box: _box)),
);
}
},
child: Row(
children: <Widget>[
Expanded(
child: new Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: new Row(
children: <Widget>[
new Expanded(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(left: 8.0),
child: new Text(
_box.packageNumber == null
? ''
: _box.packageNumber,
style: new TextStyle(
fontSize: 15.0, color: Colors.black),
),
),
Padding(
padding: const EdgeInsets.only(left: 10.0, top: 10),
child: new Text(
dateFormat.format(_box.arrivedDate),
style: new TextStyle(
fontSize: 15.0, color: Colors.grey),
),
)
],
),
),
],
),
),
),
Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(0),
child: getStatus(_box.status),
),
Padding(
padding: const EdgeInsets.only(left: 8.0, top: 5, bottom: 5),
child: Row(
children: <Widget>[
new Text(
_box.weight == null
? ''
: _box.weight.toString() + 'lb - ',
style:
new TextStyle(fontSize: 15.0, color: Colors.grey),
),
new Text(
_box.price == null ? "" : "\$ " + _box.price.toString(),
style:
new TextStyle(fontSize: 15.0, color: Colors.grey),
),
],
),
),
],
)
],
),
),
);
}
}

View File

@@ -1,194 +0,0 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/device_model.dart';
import 'package:fcs/model/main_model.dart';
import 'package:fcs/vo/device.dart';
import 'package:fcs/vo/popup_menu.dart';
import 'package:fcs/widget/local_text.dart';
import 'package:fcs/widget/popupmenu.dart';
import 'package:fcs/widget/progress.dart';
import '../fcs/common/helpers/theme.dart';
import '../fcs/common/pages/util.dart';
class PhoneDeviceList extends StatefulWidget {
@override
_PhoneDeviceListState createState() => _PhoneDeviceListState();
}
class _PhoneDeviceListState extends State<PhoneDeviceList> {
final double dotSize = 15.0;
PopupMenu selectedChoices = deviceMenu[0];
bool _isLoading = false;
PhoneDevice phoneDevice = new PhoneDevice();
@override
Widget build(BuildContext context) {
var deviceModel = Provider.of<PhoneDeviceModel>(context);
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: AppBar(
backgroundColor: primaryColor,
title: LocalText(
context,
'profile.devices',
color: Colors.white,
fontSize: 20,
),
),
body: new ListView.builder(
scrollDirection: Axis.vertical,
padding: EdgeInsets.only(left: 15, right: 15, top: 15),
shrinkWrap: true,
itemCount: deviceModel.devices.length,
itemBuilder: (BuildContext context, int index) {
return Card(
elevation: 10,
color: Colors.white,
child: InkWell(
onTap: () {},
child: Row(
children: <Widget>[
Expanded(
child: new Padding(
padding: const EdgeInsets.symmetric(vertical: 7.0),
child: new Row(
children: <Widget>[
new Padding(
padding: new EdgeInsets.symmetric(
horizontal: 15.0 - dotSize / 2),
child: Padding(
padding: EdgeInsets.all(5.0),
child: Image.asset(
"assets/device.png",
width: 40,
height: 40,
color: primaryColor,
),
),
),
new Expanded(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Text(
deviceModel.devices[index].name,
style: new TextStyle(
fontSize: 13.0, color: Colors.black),
),
],
),
),
],
),
),
),
PopupMenuButton<PopupMenu>(
elevation: 3.2,
onSelected: _select,
itemBuilder: (BuildContext context) {
this.phoneDevice = deviceModel.devices[index];
return deviceMenu.map((PopupMenu choice) {
return PopupMenuItem<PopupMenu>(
enabled: choice.index == 0
? deviceModel.devices[index].isDeviceOn()
? false
: true
: choice.index == 1
? deviceModel.devices[index]
.isDeviceOn()
? true
: false
: true,
value: choice,
child: Text(choice.status),
);
}).toList();
}),
],
),
),
);
}),
),
);
}
void _select(PopupMenu choice) async {
selectedChoices = choice;
if (choice.index == 0) {
showConfirmDialog(context, "device.confirm", () {
_confirm();
});
} else if (choice.index == 1) {
showConfirmDialog(context, "device.logout", () {
_logout();
});
} else if (choice.index == 2) {
showConfirmDialog(context, "device.set_primary", () {
_setPrimaryDevice();
});
}
}
_confirm() async {
setState(() {
_isLoading = true;
});
try {
var deviceModel = Provider.of<PhoneDeviceModel>(context);
var mainModel = Provider.of<MainModel>(context);
await deviceModel.confirmDevice(
mainModel.user.phoneNumber, this.phoneDevice.id);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
_logout() async {
setState(() {
_isLoading = true;
});
try {
var deviceModel = Provider.of<PhoneDeviceModel>(context);
var mainModel = Provider.of<MainModel>(context);
await deviceModel.logoutDevice(
mainModel.user.phoneNumber, this.phoneDevice.id);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
_setPrimaryDevice() async {
setState(() {
_isLoading = true;
});
try {
var deviceModel = Provider.of<PhoneDeviceModel>(context);
var mainModel = Provider.of<MainModel>(context);
await deviceModel.setPrimaryDevice(
mainModel.user.phoneNumber, this.phoneDevice.id);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
}

View File

@@ -1,14 +1,11 @@
import 'package:fcs/model/discount_model.dart';
import 'package:fcs/fcs/common/pages/util.dart';
import 'package:fcs/vo/discount.dart';
import 'package:fcs/domain/entities/discount.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/localization/app_translations.dart';
import 'package:fcs/pages/main/util.dart';
import 'package:fcs/pages/widgets/progress.dart';
import 'package:flutter/material.dart';
import 'package:flutter_icons/flutter_icons.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:provider/provider.dart';
import 'package:fcs/widget/localization/app_translations.dart';
import 'package:fcs/widget/progress.dart';
import '../fcs/common/helpers/theme.dart';
class DiscountEditor extends StatefulWidget {
final Discount discount;

View File

@@ -1,13 +1,13 @@
import 'package:fcs/model/discount_model.dart';
import 'package:fcs/pages/discount_editor.dart';
import 'package:fcs/fcs/common/pages/util.dart';
import 'package:fcs/widget/bottom_up_page_route.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/localization/app_translations.dart';
import 'package:fcs/pages/discount/model/discount_model.dart';
import 'package:fcs/pages/main/util.dart';
import 'package:fcs/pages/widgets/bottom_up_page_route.dart';
import 'package:fcs/pages/widgets/progress.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:fcs/widget/localization/app_translations.dart';
import 'package:fcs/widget/progress.dart';
import '../fcs/common/helpers/theme.dart';
import 'discount_editor.dart';
class DiscountList extends StatefulWidget {
@override

View File

@@ -0,0 +1,56 @@
import 'package:fcs/domain/entities/discount.dart';
import 'package:fcs/pages/main/model/base_model.dart';
class DiscountModel extends BaseModel {
List<Discount> get discounts {
List<Discount> discountList = [
Discount(
code: 'XMQY01',
customer: 'Ko Nyi',
amount: 5000,
status: 'Used',
),
Discount(
code: 'XMQY02',
customer: 'Ko Aung Myo',
amount: 3000,
status: 'Avaliable',
),
Discount(
code: 'XMQY03',
customer: 'Ko Zaw Thu',
amount: 2000,
status: 'Used',
),
Discount(
code: 'XMQY04',
customer: 'Ko Myo Min',
amount: 3000,
status: 'Avaliable',
),
Discount(
code: 'XMQY05',
customer: 'Ko Nyi',
amount: 3000,
status: 'Avaliable',
),
];
return discountList;
}
// List<Discount> get discountByCustomer {
// return invoices.where((e) => e.status == "Avaliable").toList()
// ..sort((e1, e2) {
// return e2.invoiceNumber.compareTo(e1.invoiceNumber);
// });
// }
void initUser(user) {
super.initUser(user);
}
@override
logout() async {
// discounts = [];
}
}

View File

@@ -1,909 +0,0 @@
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:image_picker/image_picker.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:quiver/async.dart';
import 'package:fcs/model/do_model.dart';
import 'package:fcs/model/language_model.dart';
import 'package:fcs/model/log_model.dart';
import 'package:fcs/model/main_model.dart';
import 'package:fcs/model/po_model.dart';
import 'package:fcs/model/product_model.dart';
import 'package:fcs/pages/do/photo_page.dart';
import 'package:fcs/fcs/common/helpers/theme.dart';
import 'package:fcs/vo/do.dart';
import 'package:fcs/vo/po.dart';
import 'package:fcs/widget/img_file.dart';
import 'package:fcs/widget/local_text.dart';
import 'package:fcs/widget/localization/app_translations.dart';
import 'package:fcs/widget/my_data_table.dart';
import 'package:fcs/widget/number_cell.dart';
import 'package:fcs/widget/progress.dart';
import '../document_log_page.dart';
import '../../fcs/common/pages/util.dart';
import 'do_files.dart';
import 'do_storage_item.dart';
class DOApproval extends StatefulWidget {
final DOSubmission doSubmission;
const DOApproval({this.doSubmission});
@override
_DOApprovalState createState() => _DOApprovalState();
}
class _DOApprovalState extends State<DOApproval> {
var dateFormatter = new DateFormat('dd MMM yyyy');
final numberFormatter = new NumberFormat("#,###");
var doDateFormatter = new DateFormat('dd MMM yyyy - hh:mm a');
bool _isLoading = false;
TextEditingController _date = new TextEditingController();
TextEditingController _doDate = new TextEditingController();
TextEditingController _number = new TextEditingController();
TextEditingController _licence = new TextEditingController();
TextEditingController _driver = new TextEditingController();
TextEditingController _carNo = new TextEditingController();
TextEditingController _type = new TextEditingController();
TextEditingController _name = new TextEditingController();
TextEditingController _bizName = new TextEditingController();
TextEditingController _storage = new TextEditingController();
TextEditingController _comment = new TextEditingController();
DOSubmission doObj = DOSubmission();
int _count;
DateTime _result;
DOFiles files = DOFiles();
List<DOLine> doLines = new List();
@override
void initState() {
super.initState();
var mainModel = Provider.of<MainModel>(context, listen: false);
var doModel = Provider.of<DOModel>(context, listen: false);
doObj = widget.doSubmission;
_date.text = doObj.deliveryDate != null
? dateFormatter.format(doObj.deliveryDate)
: "";
_doDate.text =
doObj.doDate != null ? doDateFormatter.format(doObj.doDate) : "";
_number.text = doObj.doNumber.toString();
_licence.text = doObj.driverLicenseNumber;
_driver.text = doObj.driverName;
_carNo.text = doObj.carNo;
_type.text = doObj.type;
_name.text = doObj.userName;
_bizName.text = doObj.bizName;
_storage.text = doObj.storageCharge == null
? ""
: numberFormatter.format(doObj.storageCharge);
_comment.text = doObj.comment;
if (doObj.deliveryStatus == 'initiated') {
_count = doModel.timber;
Duration diff = DateTime.now().difference(doObj.deliveryInitiatedTime);
if (diff.inMinutes < mainModel.setting.deliveryStartWaitMin) {
var time = mainModel.setting.deliveryStartWaitMin - diff.inMinutes;
new CountdownTimer(
new Duration(minutes: time), new Duration(seconds: 1))
.listen((data) {
if (mounted) {
setState(() {
_count = data.remaining.inSeconds;
doModel.addTimber(_count);
});
}
});
}
} else {
_count = 0;
}
_load();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
MainModel mainModel = Provider.of<MainModel>(context);
bool isBuyer = mainModel.user.isBuyer();
var logModel = Provider.of<LogModel>(context);
String formattedTime;
if (doObj.deliveryStatus == 'initiated') {
_result = DateTime(
doObj.deliveryInitiatedTime.year,
doObj.deliveryInitiatedTime.month,
doObj.deliveryInitiatedTime.day,
doObj.deliveryInitiatedTime.hour,
doObj.deliveryInitiatedTime.minute,
_count);
formattedTime = DateFormat.ms().format(_result);
}
final doDateBox = Container(
padding: EdgeInsets.only(left: 20, top: 15),
child: Row(
children: <Widget>[
LocalText(context, "do.do_date"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
_doDate.text,
style: textStyle,
),
)
],
),
);
final dateBox = Container(
padding: EdgeInsets.only(left: 20, top: 8),
child: Row(
children: <Widget>[
LocalText(context, "do.date"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
_date.text,
style: textStyle,
),
)
],
),
);
final numberBox = Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(
children: <Widget>[
LocalText(context, "do.do_num"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
_number.text,
style: textStyle,
),
)
],
),
);
final driverBox = Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(
children: <Widget>[
LocalText(context, "do.driver"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
_driver.text,
style: textStyle,
),
)
],
),
);
final carNoBox = Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(
children: <Widget>[
LocalText(context, "do.car"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
_carNo.text,
style: textStyle,
),
)
],
),
);
final licenceBox = Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(
children: <Widget>[
LocalText(context, "do.licence"),
ImageFile(
enabled: false,
title: "Image",
initialImgUrl: doObj.driverLicenceUrl,
onFile: (file) {}),
],
),
);
final statusBox = Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(
children: <Widget>[
LocalText(context, "do.status"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
doObj.status,
style: doObj.isPending
? textHighlightBlueStyle
: doObj.isApproved
? textHighlightGreenStyle
: textHighlightRedStyle,
),
),
],
),
);
final deliveryStatusBox = Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(
children: <Widget>[
LocalText(context, "do.delivery.status"),
Container(
padding: EdgeInsets.only(left: 10, right: 15),
child: Text(
doObj.getDeliveryStatus,
style: textStyle,
),
),
doObj.deliveryStatus == 'initiated'
? Text(
"(can start in $formattedTime)",
style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold),
)
: Container()
],
),
);
final typeBox = Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(
children: <Widget>[
LocalText(context, "do.type"),
Container(
padding: EdgeInsets.only(left: 20),
child: Text(
_type.text,
style: textStyle,
),
)
],
),
);
final userNameBox = Container(
padding: EdgeInsets.only(top: 5, left: 20),
child: Row(
children: <Widget>[
LocalText(context, "do.name"),
Container(
padding: EdgeInsets.only(left: 20),
child: Text(
_name.text,
style: textStyle,
),
)
],
),
);
final bizNameBox = Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(
children: <Widget>[
LocalText(context, "do.biz"),
Container(
padding: EdgeInsets.only(left: 20),
child: Text(
_bizName.text,
style: textStyle,
),
)
],
),
);
final driverImgUrlBox = Container(
padding: EdgeInsets.only(left: 20),
child: Row(children: <Widget>[
LocalText(context, "do.driver.image"),
Container(
padding: EdgeInsets.only(left: 10),
child: ImageFile(
enabled: !isBuyer && doObj.deliveryStatus == null,
initialImgUrl: doObj.driverImgUrl,
title: "Image",
imageSource: ImageSource.camera,
onFile: (file) {
doObj.driverImg = file;
}),
),
]));
final receiptImagebox = Container(
padding: EdgeInsets.only(left: 20, top: 0),
child: Row(children: <Widget>[
LocalText(context, "do.receipt"),
Container(
padding: EdgeInsets.only(left: 10),
child: ImageFile(
enabled: false,
initialImgUrl: doObj.doReceiptUrl,
title: "Receipt",
),
),
]));
final deliveryInitTimeBox = Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(
children: <Widget>[
LocalText(context, "do.delivery.init.time"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
doObj.deliveryInitTime,
style: textStyle,
),
)
],
),
);
final storageBox = Container(
padding: EdgeInsets.only(left: 20),
child: Row(
children: <Widget>[
LocalText(context, "do.storage_charge"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
_storage.text,
style: textStyle,
),
)
],
),
);
final storagePaymentBox = Container(
padding: EdgeInsets.only(left: 20),
child: Row(children: <Widget>[
LocalText(context, "do.storage_receipt"),
ImageFile(
enabled: mainModel.user.isBuyer() ? true : false,
title: "Receipt File",
initialImgUrl: this.doObj.storageReceiptUrl,
onFile: (file) {
this.files.setStorageChargeFile = file;
}),
]));
final commentBox = Container(
padding: EdgeInsets.only(top: 5, left: 20),
child: Row(
children: <Widget>[
LocalText(context, "do.comment"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
_comment.text,
style: textStyle,
),
)
],
),
);
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: AppBar(
backgroundColor: primaryColor,
title: Text(AppTranslations.of(context).text("do"),
style: Provider.of<LanguageModel>(context).isEng
? TextStyle(fontSize: 18)
: TextStyle(fontSize: 18, fontFamily: 'MyanmarUnicode')),
actions: <Widget>[
mainModel.showHistoryBtn()
? IconButton(
icon: Icon(Icons.history),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
DocumentLogPage(docID: doObj.id)),
);
},
)
: Container(),
isBuyer
? doObj.isPending
? PopupMenuButton(
onSelected: _selectBuyer,
itemBuilder: (context) => List<PopupMenuEntry>.from([
PopupMenuItem(
enabled: this.doObj.isPending &&
this.doObj.storageCharge > 0,
value: 1,
child: Text("Update DO"),
),
PopupMenuItem(
enabled: this.doObj.isPending,
value: 2,
child: Text("Cancel DO"),
),
]))
: Container()
: PopupMenuButton(
onSelected: _select,
itemBuilder: (context) => List<PopupMenuEntry>.from([
PopupMenuItem(
enabled: this.doObj.isPending,
value: 1,
child: Text("Approve DO"),
),
PopupMenuItem(
enabled: this.doObj.isPending,
value: 2,
child: Text("Reject DO"),
),
PopupMenuItem(
enabled:
this.doObj.isApproved && mainModel.user.isOwner(),
value: 6,
child: Text("Cancel DO"),
)
]),
),
],
),
body: Container(
padding:
EdgeInsets.only(left: 10, right: 10, top: 10, bottom: 10),
child: Card(
elevation: 23,
child: ListView(
children: <Widget>[
Column(
children: <Widget>[
doDateBox,
Divider(),
dateBox,
Divider(),
numberBox,
Divider(),
userNameBox,
Divider(),
bizNameBox,
Divider(),
typeBox,
Divider(),
statusBox,
Divider(),
doObj.comment == null || doObj.comment == ''
? Container()
: commentBox,
doObj.comment == null || doObj.comment == ''
? Container()
: Divider(),
driverBox,
Divider(),
carNoBox,
Divider(),
licenceBox,
Divider(),
receiptImagebox,
Divider(),
doObj.hasStorageCharge() ? storageBox : Container(),
doObj.hasStorageCharge() ? Divider() : Container(),
doObj.hasStorageCharge()
? storagePaymentBox
: Container(),
doObj.isApproved || doObj.isClosed
? deliveryStatusBox
: Container(),
doObj.isApproved || doObj.isClosed
? Divider()
: Container(),
Container(
padding: EdgeInsets.only(top: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Center(
child: LocalText(
context,
'do.products',
fontSize: 14,
fontWeight: FontWeight.bold,
underline: true,
color: secondaryColor,
),
),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: MyDataTable(
headingRowHeight: 40,
columnSpacing: 40,
columns: [
MyDataColumn(
label: LocalText(context, "do.product"),
),
MyDataColumn(
label: LocalText(context, "do.storage"),
),
MyDataColumn(
label:
LocalText(context, "do.quantity"),
numeric: true),
],
rows: getProductRow(doObj.doLines),
),
),
],
),
),
SizedBox(
height: 15,
),
getPOProductTable()
],
),
],
),
)),
));
}
List<MyDataRow> getProductRow(List<DOLine> doLines) {
MainModel mainModel = Provider.of<MainModel>(context);
ProductModel productModel = Provider.of<ProductModel>(context);
bool isBuyer = mainModel.user.isBuyer();
if (doLines.isNotEmpty) {
doLines.forEach((d) {
productModel.products.forEach((p) {
if (p.id == d.productID) {
d.displayOrder = p.displayOrder;
} else {
return;
}
});
});
doLines.sort((p1, p2) => p1.displayOrder.compareTo(p2.displayOrder));
}
return doLines.map((d) {
return MyDataRow(
onSelectChanged: (bool selected) async {
if (isBuyer) return;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DOStorageItem(
doLine: d,
onSave: (storageID, storageName) {
setState(() {
d.storageID = storageID;
d.storageName = storageName;
});
},
)),
);
},
cells: [
MyDataCell(
new Text(
d.productName,
style: textStyle,
),
),
MyDataCell(
new Text(d.storageName, style: textStyle),
),
MyDataCell(NumberCell(d.qty)),
],
);
}).toList();
}
Widget getPOProductTable() {
return Container(
padding: EdgeInsets.only(top: 10),
child: Column(
children: <Widget>[
Center(
child: LocalText(context, 'po.info',
fontSize: 14,
fontWeight: FontWeight.bold,
underline: true,
color: secondaryColor),
),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: MyDataTable(
headingRowHeight: 40,
columnSpacing: 20,
columns: [
MyDataColumn(label: LocalText(context, "po.number")),
MyDataColumn(label: LocalText(context, "po.product")),
MyDataColumn(
label: LocalText(context, "do.po_qty"),
numeric: true,
),
MyDataColumn(
label: LocalText(context, "do.po_balance_qty"),
numeric: true,
),
MyDataColumn(
label: LocalText(context, "po.retrieved.amount"),
numeric: true,
),
MyDataColumn(
label: LocalText(context, "do.do_qty"),
numeric: true,
),
],
rows: getPOProductRow(),
),
),
],
),
);
}
List<MyDataRow> getPOProductRow() {
ProductModel productModel = Provider.of<ProductModel>(context);
if (doObj.dopoLies.isNotEmpty) {
doObj.dopoLies.sort((p1, p2) => p1.poNumber.compareTo(p2.poNumber));
doObj.dopoLies.forEach((d) {
productModel.products.forEach((p) {
if (p.id == d.productID) {
d.displayOrder = p.displayOrder;
} else {
return;
}
});
});
doObj.dopoLies.sort((p1, p2) {
if (p1.displayOrder != p2.displayOrder)
return p1.displayOrder.compareTo(p2.displayOrder);
return p1.poNumber.compareTo(p2.poNumber);
});
}
return doObj.dopoLies.map((d) {
return MyDataRow(
cells: [
MyDataCell(
new Text(
d.poNumber,
style: textStyle,
),
),
MyDataCell(
new Text(
d.productName,
style: textStyle,
),
),
MyDataCell(NumberCell(d.poQty)),
MyDataCell(NumberCell(d.poBalAtCreate)),
MyDataCell(NumberCell(d.getPoBalanceQtyAtCreate)),
MyDataCell(NumberCell(d.doQty)),
],
);
}).toList();
}
_select(s) {
if (s == 1) {
showConfirmDialog(context, "do.approve.confirm", () {
_approve();
});
} else if (s == 2) {
showCommentDialog(context, (comment) {
doObj.comment = comment;
_reject();
});
} else if (s == 5) {
showConfirmDialog(context, "do.end.confirm", () {
_endDelivery();
});
} else if (s == 6) {
showConfirmDialog(context, "do.cancel.confirm", () {
_cancelDelivery();
});
}
}
_selectBuyer(s) {
if (s == 1) {
showConfirmDialog(context, "do.confirm", () {
_submit();
});
} else if (s == 2) {
showConfirmDialog(context, "do.cancel.confirm", () {
_cancelDelivery();
});
}
}
Future<void> _load() async {
POSubmissionModel poModel =
Provider.of<POSubmissionModel>(context, listen: false);
DOModel doModel = Provider.of<DOModel>(context, listen: false);
var _doSub = await poModel.loadDOLines(doObj);
_doSub = await doModel.loadDOPOLines(_doSub);
// set po balance
List<String> pos = _doSub.getPOs();
for (var po in pos) {
List<POLine> poLines = await poModel.loadPOLines(po);
_doSub.setDOPOLineBalance(po, poLines);
}
if (mounted) {
setState(() {
doObj.doLines = _doSub.doLines;
doObj.dopoLies = _doSub.dopoLies;
});
}
}
_approve() async {
if (doObj.doLines.any((l) => l.storageID.isEmpty)) {
showMsgDialog(context, "Error", "Storage required for every product");
return;
}
setState(() {
_isLoading = true;
});
try {
DOModel doModel = Provider.of<DOModel>(context);
await doModel.approveDO(doObj);
Navigator.pop(context);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
_reject() async {
setState(() {
_isLoading = true;
});
try {
DOModel doModel = Provider.of<DOModel>(context);
await doModel.rejectDO(doObj);
Navigator.pop(context);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
_initDelivery() async {
if (doObj.driverImg == null) {
showMsgDialog(context, "Error", "Please attach driver image");
return;
}
setState(() {
_isLoading = true;
});
try {
DOModel doModel = Provider.of<DOModel>(context);
await doModel.initDelivery(doObj);
Navigator.pop(context);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
_startDelivery() async {
MainModel mainModel = Provider.of<MainModel>(context);
Duration diff = DateTime.now().difference(doObj.deliveryInitiatedTime);
if (diff.inMinutes < mainModel.setting.deliveryStartWaitMin) {
showMsgDialog(context, "Waiting...",
"Can not start delivery, wait for ${mainModel.setting.deliveryStartWaitMin} minutes");
return;
}
setState(() {
_isLoading = true;
});
try {
DOModel doModel = Provider.of<DOModel>(context);
await doModel.startDelivery(doObj);
Navigator.pop(context);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
_endDelivery() async {
var photo = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => PhotoPage()),
);
if (photo == null) {
return;
}
Uint8List bytesPhoto = photo.readAsBytesSync() as Uint8List;
setState(() {
_isLoading = true;
});
try {
DOModel doModel = Provider.of<DOModel>(context);
await doModel.endDelivery(doObj, bytesPhoto);
Navigator.pop(context);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
_cancelDelivery() async {
setState(() {
_isLoading = true;
});
try {
DOModel doModel = Provider.of<DOModel>(context);
await doModel.cancelDO(doObj);
Navigator.pop(context);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
_submit() async {
if (doObj.hasStorageCharge()) {
if (files.storageChargeFile == null && doObj.storageReceiptUrl == '') {
showMsgDialog(context, "Error", "Please insert storage receipt");
return;
}
}
setState(() {
_isLoading = true;
});
try {
DOModel doModel = Provider.of<DOModel>(context);
await doModel.updateDO(doObj, files);
Navigator.pop<bool>(context, true);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
}

View File

@@ -1,644 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_datetime_picker/flutter_datetime_picker.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/do_model.dart';
import 'package:fcs/model/language_model.dart';
import 'package:fcs/model/main_model.dart';
import 'package:fcs/model/po_model.dart';
import 'package:fcs/model/product_model.dart';
import 'package:fcs/pages/do/do_product_item.dart';
import 'package:fcs/fcs/common/pages/util.dart';
import 'package:fcs/fcs/common/helpers/theme.dart';
import 'package:fcs/vo/do.dart';
import 'package:fcs/vo/po.dart';
import 'package:fcs/widget/img_file.dart';
import 'package:fcs/widget/local_text.dart';
import 'package:fcs/widget/localization/app_translations.dart';
import 'package:fcs/widget/my_data_table.dart';
import 'package:fcs/widget/number_cell.dart';
import 'package:fcs/widget/progress.dart';
import 'do_files.dart';
import 'po_selection.dart';
class DOForm extends StatefulWidget {
final DOSubmission doSubmission;
const DOForm({this.doSubmission});
@override
_DOFormState createState() => _DOFormState();
}
class _DOFormState extends State<DOForm> {
var dateFormatter = new DateFormat('dd MMM yyyy');
final numberFormatter = new NumberFormat("#,###");
TextEditingController _deliveryDate = new TextEditingController();
TextEditingController _licence = new TextEditingController();
TextEditingController _driver = new TextEditingController();
TextEditingController _carNo = new TextEditingController();
TextEditingController _doStatus = new TextEditingController();
TextEditingController _storage = new TextEditingController();
TextEditingController _doNumber = new TextEditingController();
DOSubmission doSubmission = DOSubmission();
bool _isLoading = false;
bool _isNew = true;
final _formKey = GlobalKey<FormState>();
int doTypeValue = 0;
DOLine doLine = new DOLine();
DOFiles files = DOFiles();
@override
void initState() {
super.initState();
if (widget.doSubmission != null) {
this.doSubmission = widget.doSubmission;
this._isNew = false;
_deliveryDate.text =
dateFormatter.format(widget.doSubmission.deliveryDate);
_licence.text = widget.doSubmission.driverLicenseNumber;
_driver.text = widget.doSubmission.driverName;
_carNo.text = widget.doSubmission.carNo;
_doStatus.text = widget.doSubmission.status;
_storage.text = widget.doSubmission.storageCharge == null
? ""
: numberFormatter.format(widget.doSubmission.storageCharge);
_doNumber.text = widget.doSubmission.doNumber;
if (widget.doSubmission.type == 'multiple') {
doTypeValue = 1;
} else {
doTypeValue = 0;
}
this.doLine.action = 'update';
} else {
this.doLine.action = 'create';
doSubmission.type = 'single';
_storage.text = "0";
}
}
@override
Widget build(BuildContext context) {
var languageModel = Provider.of<LanguageModel>(context);
var mainModel = Provider.of<MainModel>(context);
var poModel = Provider.of<POSubmissionModel>(context);
bool isBuyer = mainModel.user.isBuyer();
final doNumberBox = Container(
padding: EdgeInsets.only(left: 20, top: 10),
child: Row(
children: <Widget>[
LocalText(context, "do.do_num"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
_doNumber.text,
style: textStyle,
),
)
],
),
);
final _doTypeValueBox = Container(
padding: EdgeInsets.only(left: 20, top: 0),
child: Row(
children: <Widget>[
LocalText(context, "do.type"),
Container(
width: 130,
child: RadioListTile(
dense: true,
title: LocalText(context, "do.single"),
value: 0,
groupValue: doTypeValue,
onChanged: handleRadioValueChanged,
activeColor: primaryColor,
),
),
Container(
width: 136,
child: RadioListTile(
dense: true,
title: LocalText(context, 'do.multiple'),
value: 1,
groupValue: doTypeValue,
onChanged: handleRadioValueChanged,
activeColor: primaryColor,
))
],
),
);
final deliveryDateBox = Container(
padding: EdgeInsets.only(left: 20, right: 15),
child: InkWell(
onTap: () {
DatePicker.showDatePicker(context,
showTitleActions: true,
currentTime: _deliveryDate.text == ""
? null
: dateFormatter.parse(_deliveryDate.text),
minTime: DateTime.now(),
maxTime: DateTime(2030, 12, 31), onConfirm: (date) {
setState(() {
_deliveryDate.text = dateFormatter.format(date);
doSubmission.deliveryDate =
dateFormatter.parse(_deliveryDate.text);
doSubmission.updateStorageCharge(mainModel.setting);
});
}, locale: LocaleType.en);
},
child: TextFormField(
controller: _deliveryDate,
autofocus: false,
cursorColor: primaryColor,
style: textStyle,
enabled: false,
keyboardType: TextInputType.datetime,
decoration: new InputDecoration(
border: InputBorder.none,
focusedBorder: InputBorder.none,
labelText: AppTranslations.of(context).text("do.date"),
labelStyle: languageModel.isEng ? labelStyle : labelStyleMM,
contentPadding:
EdgeInsets.symmetric(vertical: 10.0, horizontal: 0.0),
icon: Icon(
Icons.date_range,
color: primaryColor,
)),
validator: (value) {
if (value.isEmpty) {
return AppTranslations.of(context).text("do.form.date");
}
return null;
},
),
));
final driverBox = Container(
padding: EdgeInsets.only(left: 20, right: 15),
child: TextFormField(
controller: _driver,
autofocus: false,
style: textStyle,
cursorColor: primaryColor,
decoration: new InputDecoration(
labelText: AppTranslations.of(context).text("do.driver"),
labelStyle: languageModel.isEng ? labelStyle : labelStyleMM,
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.grey, width: 1.0)),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.grey, width: 1.0)),
icon: Icon(
Icons.account_box,
color: primaryColor,
)),
validator: (value) {
if (value.isEmpty) {
return AppTranslations.of(context).text("do.form.driver");
}
return null;
},
));
final carNoBox = Container(
padding: EdgeInsets.only(left: 20, right: 15),
child: TextFormField(
controller: _carNo,
autofocus: false,
style: textStyle,
cursorColor: primaryColor,
decoration: new InputDecoration(
labelText: AppTranslations.of(context).text("do.car"),
labelStyle: languageModel.isEng ? labelStyle : labelStyleMM,
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.grey, width: 1.0)),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.grey, width: 1.0)),
icon: Icon(
Icons.directions_car,
color: primaryColor,
)),
validator: (value) {
if (value.isEmpty) {
return AppTranslations.of(context).text("do.form.car");
}
return null;
},
));
final doStatusBox = Container(
padding: EdgeInsets.only(left: 20, right: 15),
child: TextFormField(
controller: _doStatus,
autofocus: false,
style: textStyle,
readOnly: true,
decoration: new InputDecoration(
border: InputBorder.none,
focusedBorder: InputBorder.none,
icon: Image.asset("assets/status.png",
width: 25, color: primaryColor)),
validator: (value) {
if (value.isEmpty) {
return "Please enter DO Status";
}
return null;
},
));
final storageBox = Container(
padding: EdgeInsets.only(left: 20, top: 10),
child: Row(
children: <Widget>[
LocalText(context, "do.storage_charge"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
doSubmission.storageCharge == null
? ""
: numberFormatter.format(doSubmission.storageCharge),
style: textStyle,
),
)
],
),
);
final storagePaymentBox = Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(children: <Widget>[
LocalText(context, "do.storage_receipt"),
ImageFile(
enabled: isBuyer,
title: "Receipt File",
initialImgUrl: this.doSubmission.storageReceiptUrl,
onFile: (file) {
this.files.setStorageChargeFile = file;
}),
]));
final licesebox = Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(children: <Widget>[
LocalText(context, "do.licence"),
ImageFile(
enabled: isBuyer,
title: "Image",
initialImgUrl: this.doSubmission.driverLicenceUrl,
onFile: (file) {
this.files.setlicenseFile = file;
}),
]));
final poButtun = Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(children: <Widget>[
LocalText(context, "po.title"),
IconButton(
onPressed: () async {
POSelection.showPOSelection(
context, poModel.approvedPOs, doSubmission.pos,
ok: (List<POSubmission> pos) async {
for (var po in pos) {
po.poLines = await poModel.loadPOLines(po.id);
}
setState(() {
doSubmission.pos = pos;
doSubmission.loadPOs();
doSubmission.updateStorageCharge(mainModel.setting);
});
});
},
icon: Icon(Icons.edit)),
]));
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: AppBar(
backgroundColor: primaryColor,
title: Text(AppTranslations.of(context).text("do"),
style: Provider.of<LanguageModel>(context).isEng
? TextStyle(fontSize: 18)
: TextStyle(fontSize: 18, fontFamily: 'MyanmarUnicode')),
actions: <Widget>[
IconButton(
icon: Icon(Icons.send),
onPressed: () {
if (!_formKey.currentState.validate()) return;
showConfirmDialog(context, "do.confirm", () {
_submit(mainModel);
});
},
)
],
),
body: Form(
key: _formKey,
child: Container(
child: ListView(
children: <Widget>[
Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
this.doSubmission.doNumber != null
? doNumberBox
: Container(),
this.doSubmission.hasStorageCharge()
? storageBox
: Container(),
this.doSubmission.hasStorageCharge()
? storagePaymentBox
: Container(),
_doTypeValueBox,
deliveryDateBox,
driverBox,
licesebox,
carNoBox,
poButtun,
widget.doSubmission == null ? Container() : doStatusBox,
getProductTable(),
getPOProductTable(),
],
),
),
],
),
),
)),
);
}
void handleRadioValueChanged(int value) {
var mainModel = Provider.of<MainModel>(context, listen: false);
setState(() {
doSubmission.type = value == 0 ? 'single' : 'multiple';
doTypeValue = value;
doSubmission.loadPOs();
doSubmission.updateStorageCharge(mainModel.setting);
switch (doTypeValue) {
case 0:
break;
case 1:
break;
}
});
}
Widget getProductTable() {
return Container(
padding: EdgeInsets.only(top: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Center(
child: LocalText(
context,
'do.products',
fontSize: 14,
fontWeight: FontWeight.bold,
underline: true,
color: secondaryColor,
),
),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
padding: EdgeInsets.only(top: 10),
child: MyDataTable(
headingRowHeight: 40,
columnSpacing: 20,
columns: [
MyDataColumn(label: LocalText(context, "do.product")),
MyDataColumn(
label: LocalText(context, "po.avail.qty"), numeric: true),
MyDataColumn(
label: LocalText(context, "do.do_qty"),
numeric: true,
),
],
rows: getProductRow(doSubmission.doLines),
),
),
],
),
);
}
List<MyDataRow> getProductRow(List<DOLine> doLines) {
ProductModel productModel = Provider.of<ProductModel>(context);
if (doLines.isNotEmpty) {
doLines.forEach((d) {
productModel.products.forEach((p) {
if (p.id == d.productID) {
d.displayOrder = p.displayOrder;
} else {
return;
}
});
});
doLines.sort((p1, p2) => p1.displayOrder.compareTo(p2.displayOrder));
}
return doLines.map((d) {
return MyDataRow(
onSelectChanged: (bool selected) async {
if (doTypeValue == 0) return;
var doLine = await showDialog(
context: context,
builder: (_) => DOProductItem(
doLine: DOLine(productID: d.productID, qty: d.qty),
));
_updateQty(doLine);
},
cells: [
MyDataCell(
new Text(
d.productName,
style: textStyle,
),
),
MyDataCell(NumberCell(d.poBalQty)),
MyDataCell(
Container(
color: Colors.cyan,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
new Text(d.qty == null ? "0" : d.qty.toString(),
style: textStyle),
],
)),
),
],
);
}).toList();
}
Widget getPOProductTable() {
return Container(
padding: EdgeInsets.only(top: 10),
child: Column(
children: <Widget>[
Center(
child: LocalText(context, 'po.info',
fontSize: 14,
fontWeight: FontWeight.bold,
underline: true,
color: secondaryColor),
),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: MyDataTable(
headingRowHeight: 40,
columnSpacing: 20,
columns: [
MyDataColumn(label: LocalText(context, "po.number")),
MyDataColumn(label: LocalText(context, "po.product")),
MyDataColumn(
label: LocalText(context, "do.po_qty"),
numeric: true,
),
MyDataColumn(
label: LocalText(context, "po.avail.qty"),
numeric: true,
),
MyDataColumn(
label: LocalText(context, "po.retrieved.amount"),
numeric: true,
),
MyDataColumn(
label: LocalText(context, "do.do_qty"),
numeric: true,
),
],
rows: getPOProductRow(),
),
),
],
),
);
}
List<MyDataRow> getPOProductRow() {
ProductModel productModel = Provider.of<ProductModel>(context);
if (doSubmission.dopoLies.isNotEmpty) {
doSubmission.dopoLies
.sort((p1, p2) => p1.poNumber.compareTo(p2.poNumber));
doSubmission.dopoLies.forEach((d) {
productModel.products.forEach((p) {
if (p.id == d.productID) {
d.displayOrder = p.displayOrder;
} else {
return;
}
});
});
doSubmission.dopoLies.sort((p1, p2) {
if (p1.displayOrder != p2.displayOrder)
return p1.displayOrder.compareTo(p2.displayOrder);
return p1.poNumber.compareTo(p2.poNumber);
});
}
return doSubmission.dopoLies.map((d) {
return MyDataRow(
cells: [
MyDataCell(
new Text(
d.poNumber,
style: textStyle,
),
),
MyDataCell(
new Text(
d.productName,
style: textStyle,
),
),
MyDataCell(NumberCell(d.poQty)),
MyDataCell(NumberCell(d.poBalQty)),
MyDataCell(NumberCell(d.getPoBalanceQty)),
MyDataCell(
Container(
color: Colors.grey,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
new Text(d.doQty == null ? "0" : d.doQty.toString(),
style: textStyle),
],
)),
),
],
);
}).toList();
}
_updateQty(DOLine doLine) {
if (doLine == null) return;
try {
var mainModel = Provider.of<MainModel>(context);
setState(() {
doSubmission.updateDoline(doLine.productID, doLine.qty);
doSubmission.updateStorageCharge(mainModel.setting);
});
} catch (e) {
showMsgDialog(context, "Error", e.toString());
}
}
_submit(MainModel mainModel) async {
if (doSubmission.doLines.length == 0) {
showMsgDialog(context, "Error", "No product line");
return;
}
if (files.licenseFile == null) {
showMsgDialog(context, "Error", "Please insert driver licence");
return;
}
int total = 0;
doSubmission.doLines.forEach((doLine) {
total += doLine.qty;
});
if (total <= 0) {
showMsgDialog(context, "Error", "must be greater than zero");
return;
}
doSubmission.carNo = _carNo.text;
doSubmission.driverName = _driver.text;
doSubmission.driverLicenseNumber = _licence.text;
doSubmission.deliveryDate = dateFormatter.parse(_deliveryDate.text);
doSubmission.type = doTypeValue == 0 ? 'single' : 'multiple';
doSubmission.doLines.removeWhere((d) => d.qty == 0);
doSubmission.dopoLies.removeWhere((d) => d.doQty == 0);
setState(() {
_isLoading = true;
});
try {
DOModel doModel = Provider.of<DOModel>(context);
if (_isNew) {
await doModel.createDO(doSubmission, files);
} else {
await doModel.updateDO(doSubmission, files);
}
Navigator.pop<bool>(context, true);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
}

View File

@@ -1,423 +0,0 @@
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/language_model.dart';
import 'package:fcs/model/po_model.dart';
import 'package:fcs/fcs/common/pages/util.dart';
import 'package:fcs/fcs/common/helpers/theme.dart';
import 'package:fcs/vo/do.dart';
import 'package:fcs/vo/po.dart';
import 'package:fcs/widget/img_file.dart';
import 'package:fcs/widget/local_text.dart';
import 'package:fcs/widget/localization/app_translations.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:progress/progress.dart';
import 'package:fcs/widget/my_data_table.dart';
import 'package:fcs/widget/progress.dart';
import 'do_creation_form.dart';
class DoCreation extends StatefulWidget {
final POSubmission poSubmission;
const DoCreation({this.poSubmission});
@override
_DoCreationState createState() => _DoCreationState();
}
class _DoCreationState extends State<DoCreation> {
var dateFormatter = new DateFormat('dd MMM yyyy');
bool _isLoading = false;
TextEditingController _podate = new TextEditingController();
TextEditingController _ponumber = new TextEditingController();
TextEditingController _postatus = new TextEditingController();
POSubmission poSubmission = POSubmission();
@override
void initState() {
super.initState();
this.poSubmission = widget.poSubmission;
_podate.text = dateFormatter.format(this.poSubmission.poDate);
_ponumber.text = this.poSubmission.poNumber.toString();
_postatus.text = this.poSubmission.status;
_isLoading = true;
_load();
}
Future<void> _load() async {
List<POLine> poLines =
await Provider.of<POSubmissionModel>(context, listen: false)
.loadPOLines(poSubmission.id);
var _poDos = await Provider.of<POSubmissionModel>(context, listen: false)
.loadDOs(poSubmission);
setState(() {
this.poSubmission.poLines = poLines;
this.poSubmission.dos = _poDos.dos;
_isLoading = false;
});
}
Widget doDateBox(BuildContext context, DOSubmission doSubmission) {
return Container(
padding: EdgeInsets.only(left: 20, top: 0),
child: Row(
children: <Widget>[
LocalText(context, "do.date"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
dateFormatter.format(doSubmission.deliveryDate),
style: textStyle,
),
)
],
),
);
}
Widget doNumberBox(BuildContext context, DOSubmission doSubmission) {
return Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(
children: <Widget>[
LocalText(context, "do.do_num"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
doSubmission.doNumber,
style: textStyle,
),
)
],
),
);
}
Widget driverBox(BuildContext context, DOSubmission doSubmission) {
return Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(
children: <Widget>[
LocalText(context, "do.driver"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
doSubmission.driverName,
style: textStyle,
),
)
],
),
);
}
Widget carNoBox(BuildContext context, DOSubmission doSubmission) {
return Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(
children: <Widget>[
LocalText(context, "do.car"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
doSubmission.carNo,
style: textStyle,
),
)
],
),
);
}
Widget doStatusBox(BuildContext context, DOSubmission doSubmission) {
return Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(
children: <Widget>[
LocalText(context, "do.status"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
doSubmission.status,
style: textStyle,
),
)
],
),
);
}
Widget driverLicence(BuildContext context, DOSubmission doSubmission) {
return Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(
children: <Widget>[
LocalText(context, "do.licence"),
ImageFile(
enabled: false,
title: "Receipt File",
initialImgUrl: doSubmission.driverLicenceUrl,
onFile: (file) {}),
],
),
);
}
@override
Widget build(BuildContext context) {
var languageModle = Provider.of<LanguageModel>(context);
final poDateBox = Container(
padding: EdgeInsets.only(left: 20, top: 15),
child: Row(
children: <Widget>[
LocalText(context, "po.date"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
_podate.text,
style: textStyle,
),
)
],
),
);
final poNumberBox = Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(
children: <Widget>[
LocalText(context, "do.po_num"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
_ponumber.text,
style: textStyle,
),
)
],
),
);
final poStatusBox = Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(
children: <Widget>[
LocalText(context, "po.status"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
_postatus.text,
style: textStyle,
),
)
],
),
);
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: AppBar(
backgroundColor: primaryColor,
title: Text(AppTranslations.of(context).text("do.title"),
style: languageModle.isEng
? TextStyle(fontSize: 18)
: TextStyle(fontSize: 18, fontFamily: 'MyanmarUnicode')),
),
floatingActionButton: FloatingActionButton(
backgroundColor: primaryColor,
heroTag: "btn2",
onPressed: () async {
if (poSubmission.poLines
.fold<int>(0, (p, e) => p + e.balanceQty) ==
0) {
showMsgDialog(context, "Error", "Zero PO balance qty");
return;
}
final bool successful = await Navigator.push<bool>(
context, MaterialPageRoute(builder: (context) => DOForm()));
if (successful != null && successful) _load();
},
child: Icon(Icons.add),
),
body: Container(
padding: EdgeInsets.only(top: 5),
child: ListView(
children: <Widget>[
Container(
padding: EdgeInsets.only(left: 10, right: 10),
child: Card(
elevation: 23,
child: Column(
children: <Widget>[
poDateBox,
poNumberBox,
poStatusBox,
Container(
padding: EdgeInsets.only(top: 10),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: MyDataTable(
headingRowHeight: 40,
columnSpacing: 15,
columns: [
MyDataColumn(
label: LocalText(context, "po.product"),
),
MyDataColumn(
label: LocalText(context, "do.po_qty"),
),
MyDataColumn(
label:
LocalText(context, "do.po_balance_qty"),
),
],
rows:
getPoProductRow(widget.poSubmission.poLines),
),
),
),
SizedBox(
height: 15,
)
],
),
),
),
Column(
children: getDos(poSubmission.dos),
),
SizedBox(
height: 10,
)
],
),
),
));
}
List<MyDataRow> getPoProductRow(List<POLine> poLines) {
return poLines.map((p) {
return MyDataRow(
cells: [
MyDataCell(
new Text(
p.productName,
style: textStyle,
),
),
MyDataCell(
new Text(p.qty.toString(), style: textStyle),
),
MyDataCell(
new Text(p.balanceQty.toString(), style: textStyle),
),
],
);
}).toList();
}
List<Widget> getDos(List<DOSubmission> dos) {
return dos.map((d) {
return Container(
padding: EdgeInsets.only(left: 10, right: 10),
child: Card(
child: Theme(
data: ThemeData(accentColor: Colors.grey),
child: ExpansionTile(
onExpansionChanged: (e) => _onExpend(context, e, d),
title: ListTile(
title: Text(dateFormatter.format(d.deliveryDate)),
subtitle: Text(d.doNumber),
),
children: <Widget>[
Container(
child: Column(
children: <Widget>[
Align(
alignment: Alignment.topRight,
child: Container(
padding: EdgeInsets.only(right: 20),
child: InkWell(
child: Icon(Icons.edit),
onTap: () async {
final bool successful =
await Navigator.push<bool>(
context,
MaterialPageRoute(
builder: (context) => DOForm(
doSubmission: d,
)));
if (successful != null && successful) _load();
},
)),
),
doDateBox(context, d),
doNumberBox(context, d),
doStatusBox(context, d),
driverBox(context, d),
carNoBox(context, d),
driverLicence(context, d),
Container(
alignment: AlignmentDirectional.centerStart,
padding: EdgeInsets.only(top: 10),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: MyDataTable(
headingRowHeight: 40,
columnSpacing: 15,
columns: [
MyDataColumn(
label: LocalText(context, "do.product"),
),
MyDataColumn(
label: LocalText(context, "do.do_qty"),
),
],
rows: getdoProductRow(d.doLines),
),
),
),
SizedBox(
height: 15,
)
],
),
),
],
),
),
),
);
}).toList();
}
List<MyDataRow> getdoProductRow(List<DOLine> doLines) {
return doLines.map((p) {
return MyDataRow(
cells: [
MyDataCell(
new Text(
p.productName,
style: textStyle,
),
),
MyDataCell(
new Text(p.qty.toString(), style: textStyle),
),
],
);
}).toList();
}
_onExpend(BuildContext context, expended, DOSubmission doSub) async {
if (!expended) return;
POSubmissionModel poModel = Provider.of<POSubmissionModel>(context);
var _doSub = await poModel.loadDOLines(doSub);
setState(() {
doSub = _doSub;
});
}
}

View File

@@ -1,26 +0,0 @@
import 'dart:io';
class DOFiles {
File doPaymentFile, storageChargeFile, licenseFile;
bool doFileChanged = false,
storageFileChanged = false,
licenseFileChanged = false;
set setDoPaymentFile(File file) {
doPaymentFile = file;
doFileChanged = true;
}
set setStorageChargeFile(File file) {
storageChargeFile = file;
storageFileChanged = true;
}
set setlicenseFile(File file) {
licenseFile = file;
licenseFileChanged = true;
}
bool get anyChanged =>
doFileChanged || storageFileChanged || licenseFileChanged;
}

View File

@@ -1,286 +0,0 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/do_model.dart';
import 'package:fcs/model/language_model.dart';
import 'package:fcs/model/main_model.dart';
import 'package:fcs/pages/do/do_creation_form.dart';
import 'package:fcs/fcs/common/pages/util.dart';
import 'package:fcs/fcs/common/helpers/theme.dart';
import 'package:fcs/vo/popup_menu.dart';
import 'package:fcs/widget/localization/app_translations.dart';
import 'package:fcs/widget/popupmenu.dart';
import 'package:fcs/widget/progress.dart';
import 'do_approve.dart';
class DOList extends StatefulWidget {
@override
_DOListState createState() => _DOListState();
}
class _DOListState extends State<DOList> {
var dateFormatter = new DateFormat('dd MMM yyyy');
final double dotSize = 10.0;
DateTime _selectedDate = DateTime.now();
String status;
int _selectedIndex = 0;
int _dateIndex = 0;
bool _isLoading = false;
@override
void initState() {
super.initState();
var doModel = Provider.of<DOModel>(context, listen: false);
_selectedIndex = doModel.popupMenu.index;
_dateIndex = doModel.dateIndex;
_selectedDate = doModel.selectedDate;
}
@override
void dispose() {
super.dispose();
}
Future<Null> _selectDate(BuildContext context) async {
var doModel = Provider.of<DOModel>(context);
final DateTime picked = await showDatePicker(
context: context,
initialDate: _selectedDate,
firstDate: DateTime(2015, 8),
lastDate: DateTime(2101),
builder: (BuildContext context, Widget child) {
return Theme(
data: ThemeData.light().copyWith(
primaryColor: primaryColor, //Head background
accentColor: secondaryColor, //selection color
dialogBackgroundColor: Colors.white, //Background color
),
child: child,
);
},
);
if (picked != null) {
var pickedDate = new DateTime(picked.year, picked.month, picked.day);
var currentDate = new DateTime(
DateTime.now().year, DateTime.now().month, DateTime.now().day);
this._dateIndex = pickedDate == currentDate ? 0 : 1;
setState(() {
_selectedDate = picked;
doModel.filterData(status, _selectedDate, _selectedIndex, _dateIndex);
});
}
}
@override
Widget build(BuildContext context) {
var doModel = Provider.of<DOModel>(context);
MainModel mainModel = Provider.of<MainModel>(context);
bool isBuyer = mainModel.user.isBuyer();
var languageModle = Provider.of<LanguageModel>(context);
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: AppBar(
backgroundColor: primaryColor,
title: Text(
AppTranslations.of(context).text("do.title"),
style: languageModle.isEng
? TextStyle()
: TextStyle(fontFamily: 'MyanmarUnicode'),
),
actions: <Widget>[
InkWell(
child: Container(
padding: EdgeInsets.only(top: 15),
child: Stack(
children: <Widget>[
Image.asset(
"assets/date_filter.png",
color: Colors.white,
width: 25,
),
_dateIndex == 0
? Container()
: Positioned(
bottom: 15,
right: 10,
child: Container(
width: 10,
height: 10,
decoration: new BoxDecoration(
shape: BoxShape.circle,
color: secondaryColor,
),
),
)
],
),
),
onTap: () => _selectDate(context),
),
PopupMenuButton<PopupMenu>(
elevation: 3.2,
onSelected: (selected) {
setState(() {
_selectedIndex = selected.index;
});
if (selected.status == 'All') {
status = null;
} else {
status = selected.status;
}
doModel.filterData(
status, _selectedDate, _selectedIndex, _dateIndex);
},
icon: Container(
width: 30,
height: 30,
decoration: new BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
),
child: Stack(
fit: StackFit.expand,
children: <Widget>[
Icon(
Icons.filter_list,
color: primaryColor,
),
_selectedIndex != 0
? Positioned(
bottom: 0,
right: 0,
child: Container(
width: 10,
height: 10,
decoration: new BoxDecoration(
shape: BoxShape.circle,
color: secondaryColor,
),
),
)
: Container()
],
)),
itemBuilder: (BuildContext context) {
return statusMenu.map((PopupMenu choice) {
return PopupMenuItem<PopupMenu>(
value: choice,
child: Row(
children: <Widget>[
Text(choice.status),
SizedBox(
width: 10,
),
_selectedIndex != null &&
_selectedIndex == choice.index
? Icon(
Icons.check,
color: Colors.grey,
)
: Container(),
],
),
);
}).toList();
}),
],
),
floatingActionButton: mainModel.isBuyer()
? FloatingActionButton(
backgroundColor: primaryColor,
onPressed: () => Navigator.push(
context,
MaterialPageRoute(builder: (context) => DOForm()),
),
child: Icon(Icons.add),
)
: null,
body: new ListView.builder(
scrollDirection: Axis.vertical,
padding: EdgeInsets.only(left: 15, right: 15, top: 15),
shrinkWrap: true,
itemCount: doModel.dos.length,
itemBuilder: (BuildContext context, int index) {
return Card(
elevation: 10,
color: Colors.white,
child: Row(
children: <Widget>[
Expanded(
child: InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DOApproval(
doSubmission: doModel.dos[index])),
);
},
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: Padding(
padding: EdgeInsets.all(5.0),
child: Image.asset(
"assets/do.png",
width: 40,
height: 40,
color: primaryColor,
),
),
),
new Expanded(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Text(
doModel.dos[index].doNumber,
style: new TextStyle(
fontSize: 12.0, color: Colors.black),
),
new Text(
doModel.dos[index].deliveryDate == null
? ""
: dateFormatter.format(
doModel.dos[index].deliveryDate),
style: new TextStyle(
fontSize: 14.0, color: Colors.grey),
),
!isBuyer
? new Text(
doModel.dos[index].userName,
style: new TextStyle(
fontSize: 12.0,
color: Colors.grey),
)
: Container()
],
),
),
Container(
padding: EdgeInsets.only(right: 15),
child: getStatus(doModel.dos[index].status),
),
],
),
),
),
),
],
),
);
}),
),
);
}
}

View File

@@ -1,134 +0,0 @@
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/language_model.dart';
import 'package:fcs/fcs/common/pages/util.dart';
import 'package:fcs/fcs/common/helpers/theme.dart';
import 'package:fcs/vo/do.dart';
import 'package:fcs/widget/local_text.dart';
import 'package:fcs/widget/localization/app_translations.dart';
import 'package:fcs/widget/progress.dart';
class DOProductItem extends StatefulWidget {
final DOLine doLine;
const DOProductItem({Key key, this.doLine}) : super(key: key);
@override
_DOProductItemState createState() => _DOProductItemState();
}
class _DOProductItemState extends State<DOProductItem> {
final _formKey = GlobalKey<FormState>();
TextEditingController _doQty = new TextEditingController();
DOLine doLine;
bool _isLoading = false;
@override
void initState() {
super.initState();
if (widget.doLine != null) {
doLine = widget.doLine;
_doQty.text = widget.doLine.qty.toString();
}
}
@override
Widget build(BuildContext context) {
var languageModel = Provider.of<LanguageModel>(context);
return LocalProgress(
inAsyncCall: _isLoading,
child: AlertDialog(
title: Center(
child: Text(
AppTranslations.of(context).text("do_qty"),
style: TextStyle(
color: primaryColor, fontWeight: FontWeight.bold, fontSize: 20),
)),
content: Form(
key: _formKey,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
new Expanded(
child: new TextFormField(
keyboardType: TextInputType.number,
autofocus: true,
controller: _doQty,
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
cursorColor: primaryColor,
decoration: new InputDecoration(
fillColor: primaryColor,
icon: Icon(
FontAwesomeIcons.sortNumericUpAlt,
color: primaryColor,
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.grey, width: 1.0)),
),
validator: (value) {
if (value.isEmpty) {
return AppTranslations.of(context).text("do.form.volume");
}
return null;
},
))
],
),
),
actions: <Widget>[
FlatButton(
child: LocalText(
context,
'do.cancel',
color: secondaryColor,
),
onPressed: () {
_doQty.clear();
Navigator.of(context).pop();
}),
FlatButton(
color: primaryColor,
child: LocalText(
context,
'do.enter',
color: Colors.white,
fontWeight: FontWeight.bold,
),
onPressed: () async {
if (!_formKey.currentState.validate()) return;
_save();
})
],
),
);
}
_save() {
setState(() {
_isLoading = true;
});
try {
var qty = int.parse(_doQty.text);
if (qty < 0)
throw Exception("invalid number, must be zero or greater than zero");
// if (qty > doLine.poLine.balanceQty)
// throw Exception(
// "invalid number, must be less than or equal to PO balance qty");
this.doLine.qty = qty;
Navigator.pop<DOLine>(context, this.doLine);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
_delete() {
this.doLine.action = "delete";
this.doLine.qty = 0;
Navigator.pop<DOLine>(context, this.doLine);
}
}

View File

@@ -1,209 +0,0 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/storage_model.dart';
import 'package:fcs/fcs/common/helpers/theme.dart';
import 'package:fcs/util.dart';
import 'package:fcs/vo/do.dart';
import 'package:fcs/vo/storage.dart';
import 'package:fcs/widget/local_text.dart';
import 'package:fcs/widget/my_data_table.dart';
import 'package:fcs/widget/number_cell.dart';
import 'package:fcs/widget/progress.dart';
typedef OnSave = void Function(String storageID, String storageName);
class DOStorageItem extends StatefulWidget {
final DOLine doLine;
final OnSave onSave;
const DOStorageItem({Key key, this.doLine, this.onSave}) : super(key: key);
@override
_DOStorageItemState createState() => _DOStorageItemState();
}
class _DOStorageItemState extends State<DOStorageItem> {
final _formKey = GlobalKey<FormState>();
bool _isLoading = false;
String currentStorageID;
TextEditingController _product = new TextEditingController();
TextEditingController _quantity = new TextEditingController();
DOLine doLine = DOLine();
@override
void initState() {
super.initState();
this.doLine = widget.doLine;
if (doLine.storageID != null && doLine.storageID.isNotEmpty) {
this.currentStorageID = doLine.storageID;
}
_product.text = this.doLine.productName;
_quantity.text = this.doLine.qty.toString();
}
Widget showStorages(BuildContext context, StorageModel storageModel) {
return Container(
padding: EdgeInsets.only(top: 10),
child: Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Image.asset(
"assets/inventory.png",
width: 30,
height: 30,
color: primaryColor,
),
SizedBox(
width: 20,
),
new Flexible(
child: Container(
width: 170.0,
child: DropdownButton<String>(
value: currentStorageID,
isExpanded: true,
hint: Text(
'Select Storage',
style: labelStyle,
),
onChanged: changedStorage,
items: storageModel
.getStorage(doLine.productID)
.map<DropdownMenuItem<String>>((Storage storage) {
return new DropdownMenuItem<String>(
value: storage.id,
child: new Text(storage.name, style: textStyle),
);
}).toList(),
),
),
),
],
),
);
}
void changedStorage(selected) {
setState(() {
currentStorageID = selected;
});
}
Widget showStorgeTable(BuildContext context, StorageModel storageModel) {
return Container(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: MyDataTable(
headingRowHeight: 40,
columnSpacing: 100,
columns: [
MyDataColumn(
label: LocalText(context, "storge"),
),
MyDataColumn(
label: LocalText(context, "storage.product.qty"),
),
],
rows: getProductRow(storageModel.getStorage(doLine.productID)),
),
),
);
}
List<MyDataRow> getProductRow(List<Storage> storages) {
return storages.map((s) {
return MyDataRow(
onSelectChanged: (bool selected) {
setState(() {
currentStorageID = s.id;
});
},
cells: [
MyDataCell(
new Text(s.name, style: textStyle),
),
MyDataCell(
NumberCell(s.products
.firstWhere((p) => p.id == doLine.productID)
.quantity),
),
],
);
}).toList();
}
@override
Widget build(BuildContext context) {
var storgeModel = Provider.of<StorageModel>(context);
final productbox = Container(
padding: EdgeInsets.only(top: 10),
child: Row(
children: <Widget>[
LocalText(context, "do.product"),
SizedBox(
width: 20,
),
Text(_product.text, style: textStyle)
],
));
final quantitybox = Container(
padding: EdgeInsets.only(top: 15),
child: Row(
children: <Widget>[
LocalText(context, "do.quantity"),
SizedBox(
width: 20,
),
Text(formatNumber(this.doLine.qty), style: textStyle)
],
),
);
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: AppBar(
backgroundColor: primaryColor,
title: Text("DO"),
actions: <Widget>[
IconButton(
icon: Icon(Icons.save),
onPressed: () {
_save();
},
)
],
),
body: Form(
key: _formKey,
child: Column(
children: <Widget>[
Expanded(
child: ListView(
shrinkWrap: true,
padding: EdgeInsets.only(left: 24.0, right: 24.0, top: 10),
children: <Widget>[
productbox,
quantitybox,
showStorages(context, storgeModel),
showStorgeTable(context, storgeModel)
],
),
),
],
),
)),
);
}
_save() {
this.doLine.storageID = currentStorageID;
var storageName =
Provider.of<StorageModel>(context).getStorageName(currentStorageID);
this.doLine.storageName = storageName;
if (widget.onSave != null)
widget.onSave(this.doLine.storageID, storageName);
Navigator.pop<DOLine>(context, this.doLine);
}
}

View File

@@ -1,165 +0,0 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/language_model.dart';
import 'package:fcs/fcs/common/helpers/theme.dart';
import 'package:fcs/widget/localization/app_translations.dart';
import '../../fcs/common/pages/util.dart';
class PhotoPage extends StatefulWidget {
PhotoPage({Key key}) : super(key: key);
@override
_PhotoPageState createState() => _PhotoPageState();
}
class _PhotoPageState extends State<PhotoPage> {
File receiptImageFile;
@override
Widget build(BuildContext context) {
var languageModel = Provider.of<LanguageModel>(context);
final receiptImageBox = Padding(
padding: EdgeInsets.symmetric(vertical: 5.0),
child: Container(
color: Colors.grey[400],
child: Column(
children: <Widget>[
Container(
padding: EdgeInsets.only(top: 15),
child: Text(
AppTranslations.of(context).text('do.receipt'),
style:
languageModel.isEng ? photoLabelStyle : photoLabelStyleMM,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ButtonTheme.bar(
child: new ButtonBar(
alignment: MainAxisAlignment.start,
children: <Widget>[
new FlatButton(
child: Icon(
Icons.add_photo_alternate,
size: 60.0,
color: Colors.black,
),
onPressed: () {
pickImageFromGallery(ImageSource.gallery);
},
),
],
)),
ButtonTheme.bar(
child: new ButtonBar(
alignment: MainAxisAlignment.start,
children: <Widget>[
new FlatButton(
child: Icon(
Icons.add_a_photo,
size: 50.0,
color: Colors.black,
),
onPressed: () {
pickImageFromCamera(ImageSource.camera);
},
),
],
)),
],
),
receiptShowImage(),
SizedBox(
height: 10,
)
],
),
),
);
return Scaffold(
appBar: AppBar(
backgroundColor: primaryColor,
title: Text(AppTranslations.of(context).text("do.receipt.title"),
style: Provider.of<LanguageModel>(context).isEng
? TextStyle(fontSize: 18)
: TextStyle(fontSize: 18, fontFamily: 'MyanmarUnicode')),
actions: <Widget>[
IconButton(
icon: Icon(Icons.send),
onPressed: () {
if (receiptImageFile == null) {
showMsgDialog(
context, "Error", "Please insert delivery receipt file");
return;
}
Navigator.pop(context, receiptImageFile);
},
)
],
),
body: Column(
children: <Widget>[
Expanded(
child: ListView(
shrinkWrap: true,
padding: EdgeInsets.only(left: 24.0, right: 24.0, top: 20),
children: <Widget>[
receiptImageBox,
],
),
),
SizedBox(
height: 20,
)
],
),
);
}
Widget receiptShowImage() {
return Container(
child: receiptImageFile == null ? initialImage() : receiptEnableImage(),
);
}
Widget initialImage() {
var languageModel = Provider.of<LanguageModel>(context);
return Center(
child: Text(
AppTranslations.of(context).text('do.no.photo'),
style: languageModel.isEng ? photoLabelStyle : photoLabelStyleMM,
),
);
}
Widget receiptEnableImage() {
return Center(
child: Image.file(
receiptImageFile,
),
);
}
pickImageFromGallery(ImageSource source) async {
var tempImage = await ImagePicker.pickImage(
source: source, imageQuality: 80, maxWidth: 300);
setState(() {
receiptImageFile = tempImage;
});
}
pickImageFromCamera(ImageSource source) async {
var tempImage = await ImagePicker.pickImage(
source: source, imageQuality: 80, maxWidth: 300);
setState(() {
receiptImageFile = tempImage;
});
}
}

View File

@@ -1,158 +0,0 @@
import 'package:flutter/material.dart';
import 'package:fcs/fcs/common/helpers/theme.dart';
import 'package:fcs/vo/po.dart';
import 'package:fcs/widget/local_text.dart';
class POSelection extends StatefulWidget {
final List<POSubmission> pos;
final List<POSubmission> selectedPOs;
POSelection({Key key, this.pos, this.selectedPOs}) : super(key: key);
@override
_POSelectionState createState() => new _POSelectionState();
static Future<void> showPOSelection(BuildContext context,
List<POSubmission> pos, List<POSubmission> selectedPOs,
{ok(List<POSubmission> pos)}) async {
List<POSubmission> _selectedPOs = [];
selectedPOs.forEach((i) => _selectedPOs.add(i));
final poselection = POSelection(
pos: pos,
selectedPOs: _selectedPOs,
);
await showDialog(
context: context,
builder: (_) {
return AlertDialog(
title: Center(
child: LocalText(context, "po.title"),
),
content: Container(
width: double.maxFinite,
child: ListView(
shrinkWrap: true,
children: <Widget>[
poselection,
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
FlatButton(
child: LocalText(context, "Cancel"),
onPressed: () {
Navigator.of(context).pop();
}),
FlatButton(
color: primaryColor,
child: LocalText(context, "Ok"),
onPressed: () async {
if (ok != null) ok(_selectedPOs);
Navigator.of(context).pop();
})
],
),
),
],
),
),
);
});
}
}
class _POSelectionState extends State<POSelection> {
List<POSubmission> pos;
@override
void initState() {
super.initState();
pos = widget.pos;
pos.sort((p1, p2) => p1.poNumber.compareTo(p2.poNumber));
}
@override
Widget build(BuildContext context) {
var width = MediaQuery.of(context).size.width * 0.8;
var height = MediaQuery.of(context).size.height * 0.5;
return Column(
children: <Widget>[
FlatButton(
child: Text("Select All"),
onPressed: () {
setState(() {
widget.selectedPOs.clear();
widget.selectedPOs.addAll(pos);
});
}),
Container(
width: width,
height: height,
child: Column(
children: pos.asMap().entries.map((p) {
return InkWell(
onTap: () {
setState(() {
if (widget.selectedPOs.contains(p.value)) {
widget.selectedPOs.remove(p.value);
} else {
widget.selectedPOs.add(p.value);
}
});
},
child: Row(
children: <Widget>[
Checkbox(
onChanged: (v) => {_update(p.key)},
value: widget.selectedPOs.contains(p.value),
),
Text(p.value.poNumber),
],
),
);
}).toList(),
),
// child: ListView.builder(
// itemCount: pos.length,
// scrollDirection: Axis.vertical,
// itemBuilder: (BuildContext ctxt, int index) {
// return InkWell(
// onTap: () {
// setState(() {
// if (widget.selectedPOs.contains(pos[index])) {
// widget.selectedPOs.remove(pos[index]);
// } else {
// widget.selectedPOs.add(pos[index]);
// }
// });
// },
// child: Row(
// children: <Widget>[
// Checkbox(
// onChanged: (v) => {_update(index)},
// value: widget.selectedPOs.contains(pos[index]),
// ),
// Text(pos[index].poNumber),
// ],
// ),
// );
// }),
),
],
);
}
_update(int index) {
setState(() {
if (widget.selectedPOs.contains(pos[index])) {
widget.selectedPOs.remove(pos[index]);
} else {
widget.selectedPOs.add(pos[index]);
}
});
}
}

View File

@@ -1,87 +0,0 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/log_model.dart';
import 'package:fcs/model/user_model.dart';
import 'package:fcs/fcs/common/helpers/theme.dart';
import 'package:fcs/vo/document_log.dart';
import 'package:fcs/vo/role.dart';
import 'package:fcs/widget/local_text.dart';
import 'package:fcs/widget/my_data_table.dart';
class DocumentLogPage extends StatefulWidget {
final String docID;
const DocumentLogPage({Key key, this.docID}) : super(key: key);
@override
_DocumentLogPageState createState() => _DocumentLogPageState();
}
class _DocumentLogPageState extends State<DocumentLogPage> {
var dateFormatter = new DateFormat('dd MMM yyyy\nhh:mm:ss a');
@override
void initState() {
super.initState();
if (widget.docID != null) {
Provider.of<LogModel>(context, listen: false).loadDocLogs(widget.docID);
}
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
var logModel = Provider.of<LogModel>(context);
var userModel = Provider.of<UserModel>(context);
return Scaffold(
appBar: AppBar(
title: LocalText(context, 'document.log.title',
fontSize: 20, color: Colors.white),
backgroundColor: primaryColor,
),
body: Container(
padding: EdgeInsets.only(top: 10),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: MyDataTable(
headingRowHeight: 40,
columnSpacing: 40,
columns: [
MyDataColumn(label: LocalText(context, "document.date")),
MyDataColumn(label: LocalText(context, "document.by")),
MyDataColumn(label: LocalText(context, "document.desc")),
],
rows: getProductRow(logModel.docLogs, userModel.privileges),
),
),
),
);
}
List<MyDataRow> getProductRow(
List<DocLog> docLogs, List<Privilege> privileges) {
return docLogs.map((d) {
return MyDataRow(
cells: [
MyDataCell(
new Text(dateFormatter.format(d.date), style: textStyle),
),
MyDataCell(
new Text(
d.actionerName == null ? '' : d.actionerName,
style: textStyle,
),
),
MyDataCell(
new Text(d.getDesc(privileges) == null ? '' : d.getDesc(privileges),
style: textStyle),
),
],
);
}).toList();
}
}

View File

@@ -1,159 +0,0 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/language_model.dart';
import 'package:fcs/model/main_model.dart';
import 'package:fcs/model/shared_pref.dart';
import 'package:fcs/model/user_model.dart';
import 'package:fcs/fcs/common/helpers/theme.dart';
import 'package:fcs/vo/user.dart';
import 'package:fcs/widget/local_text.dart';
import 'package:fcs/widget/localization/app_translations.dart';
import 'package:fcs/widget/progress.dart';
import 'confirm_email.dart';
import '../fcs/common/pages/util.dart';
class EmailPage extends StatefulWidget {
final User user;
EmailPage({this.user});
@override
_EmailPageState createState() => _EmailPageState();
}
class _EmailPageState extends State<EmailPage> {
final TextEditingController _email = new TextEditingController();
bool _isLoading = false;
final _formKey = GlobalKey<FormState>();
@override
void initState() {
super.initState();
if (widget.user != null) {
_email.text = widget.user.email;
}
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
MainModel mainModel = Provider.of<MainModel>(context);
var languageModel = Provider.of<LanguageModel>(context);
final emailInput = TextFormField(
controller: _email,
autofocus: false,
decoration: new InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.grey, width: 1.0)),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.grey, width: 1.0)),
labelText: AppTranslations.of(context).text("login.email"),
labelStyle: labelStyle,
icon: Icon(
Icons.email,
color: primaryColor,
)),
validator: (value) {
if (value.isEmpty) {
return AppTranslations.of(context).text("login.email.empty");
}
return null;
},
);
final enterButton = Padding(
padding: EdgeInsets.symmetric(vertical: 16.0),
child: RaisedButton(
onPressed: () => _add(mainModel),
padding: EdgeInsets.all(10),
color: primaryColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(7),
),
child: Text(AppTranslations.of(context).text("login.email.add"),
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold)),
),
);
final skipButton = Container(
child: InkWell(
onTap: () {
SharedPref.saveSkippedRecoverEmail(true);
Navigator.pushNamedAndRemoveUntil(
context, "/welcome", (r) => false);
},
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Text(
AppTranslations.of(context).text("login.email.skip"),
style: languageModel.isEng
? TextStyle(fontSize: 18, fontFamily: 'WorkSansMedium')
: TextStyle(fontSize: 18, fontFamily: 'MyanmarUnicode'),
),
Container(
padding: EdgeInsets.only(top: 4),
child: Icon(
Icons.skip_next,
size: 30,
)),
],
)),
);
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: AppBar(
title: LocalText(
context,
'login.email.title',
fontSize: 20,
color: Colors.white,
),
backgroundColor: primaryColor,
),
body: Center(
child: ListView(
shrinkWrap: true,
padding: EdgeInsets.only(left: 24.0, right: 24.0),
children: <Widget>[
Form(key: _formKey, child: emailInput),
SizedBox(height: 8.0),
enterButton,
widget.user != null ? Container() : skipButton
],
),
),
),
);
}
Future<void> _add(MainModel mainModel) async {
if (!_formKey.currentState.validate()) {
return;
}
setState(() {
_isLoading = true;
});
try {
UserModel userModel = Provider.of<UserModel>(context);
await userModel.addEmail(mainModel.user.phoneNumber, _email.text);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ConfirmEmail(
id: mainModel.user.phoneNumber,
email: _email.text,
)));
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
}

View File

@@ -0,0 +1,139 @@
import 'package:fcs/domain/entities/faq.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/pages/faq/faq_edit_page.dart';
import 'package:fcs/pages/faq/model/faq_model.dart';
import 'package:fcs/pages/main/model/language_model.dart';
import 'package:fcs/pages/main/model/main_model.dart';
import 'package:fcs/pages/main/util.dart';
import 'package:fcs/pages/widgets/local_text.dart';
import 'package:fcs/pages/widgets/progress.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class FAQDetailPage extends StatefulWidget {
final FAQ faq;
const FAQDetailPage({this.faq});
@override
_FAQDetailPageState createState() => _FAQDetailPageState();
}
class _FAQDetailPageState extends State<FAQDetailPage> {
bool _isLoading = false;
FAQ faq;
intState() {
super.initState();
}
@override
Widget build(BuildContext context) {
faq = context.select((FAQModel m) => m.getFAQ(widget.faq.id));
if (faq == null) return Text("Deleted");
bool isEditable = context.select((MainModel m) => m.faqEditable());
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
body: CustomScrollView(slivers: [
SliverAppBar(
leading: IconButton(
icon: Icon(
CupertinoIcons.back,
color: primaryColor,
size: 50,
),
onPressed: () => Navigator.of(context).pop(),
),
backgroundColor: Colors.white,
expandedHeight: 100.0,
floating: false,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
centerTitle: true,
titlePadding: EdgeInsets.symmetric(vertical: 10),
),
actions: isEditable
? [
IconButton(
onPressed: () {
showConfirmDialog(context, "faq.edit.delete.confirm",
() {
_delete();
});
},
icon: Icon(
CupertinoIcons.delete,
color: primaryColor,
size: 30,
)),
IconButton(
onPressed: () =>
Navigator.of(context).push<void>(CupertinoPageRoute(
builder: (context) => FAQEditor(faq: faq),
)),
icon: Icon(
CupertinoIcons.pen,
color: primaryColor,
))
]
: [],
),
SliverList(
delegate: SliverChildListDelegate([
Padding(
padding: const EdgeInsets.all(28.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
getQuestion(context, faq),
SizedBox(
height: 50,
),
getAnwser(context, faq)
],
),
),
]))
]),
),
);
}
Widget getQuestion(BuildContext context, FAQ faq) {
bool isEng = Provider.of<LanguageModel>(context).isEng;
return TextLocalStyle(
context,
faq.question(isEng),
fontSize: 22,
fontWeight: FontWeight.bold,
);
}
Widget getAnwser(BuildContext context, FAQ faq) {
bool isEng = Provider.of<LanguageModel>(context).isEng;
return TextLocalStyle(
context,
faq.answer(isEng),
fontSize: 16,
fontWeight: FontWeight.w200,
);
}
_delete() async {
setState(() {
_isLoading = true;
});
try {
FAQModel faqModel = Provider.of<FAQModel>(context, listen: false);
await faqModel.deleteFAQ(faq);
Navigator.pop(context);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
}

View File

@@ -0,0 +1,248 @@
import 'package:fcs/domain/constants.dart';
import 'package:fcs/domain/entities/faq.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/localization/app_translations.dart';
import 'package:fcs/pages/faq/model/faq_model.dart';
import 'package:fcs/pages/faq/widgets.dart';
import 'package:fcs/pages/main/util.dart';
import 'package:fcs/pages/widgets/input_text.dart';
import 'package:fcs/pages/widgets/local_text.dart';
import 'package:fcs/pages/widgets/progress.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_icons/flutter_icons.dart';
import 'package:provider/provider.dart';
const info = "Select additional page";
class FAQEditor extends StatefulWidget {
final FAQ faq;
const FAQEditor({this.faq});
@override
_FAQEditorState createState() => _FAQEditorState();
}
class _FAQEditorState extends State<FAQEditor> {
TextEditingController _sn = new TextEditingController();
TextEditingController _engQ = new TextEditingController();
TextEditingController _mmQ = new TextEditingController();
TextEditingController _engA = new TextEditingController();
TextEditingController _mmA = new TextEditingController();
TextEditingController _pageLabelEng = new TextEditingController();
TextEditingController _pageLabelMm = new TextEditingController();
final _formKey = GlobalKey<FormState>();
bool _isLoading = false;
bool _isNew = false;
String _pageLink = info;
@override
void initState() {
super.initState();
_isNew = widget.faq == null;
if (widget.faq != null) {
_sn.text = widget.faq.sn.toString();
_engQ.text = widget.faq.questionEng;
_mmQ.text = widget.faq.questionMm;
_engA.text = widget.faq.answerEng;
_mmA.text = widget.faq.answerMm;
_pageLabelEng.text = widget.faq.pageLinkLabelEng;
_pageLabelMm.text = widget.faq.pageLinkLabelMm;
_pageLink = widget.faq.pageLink;
}
}
@override
Widget build(BuildContext context) {
final snBox = InputText(
controller: _sn,
labelTextKey: "faq.edit.sn",
maxLines: 1,
withBorder: false,
textInputType: TextInputType.number,
);
final questionEngBox = InputText(
controller: _engQ,
maxLines: 2,
withBorder: true,
);
final answerEngBox = InputText(
controller: _engA,
maxLines: 5,
withBorder: true,
);
final questionMmBox = InputText(
controller: _mmQ,
maxLines: 2,
withBorder: true,
);
final answerMmBox = InputText(
controller: _mmA,
maxLines: 5,
withBorder: true,
);
final pageLinkBox = DropdownButton<String>(
value: _pageLink,
style: TextStyle(color: Colors.deepPurple),
underline: Container(
height: 2,
color: primaryColor,
),
onChanged: (String newValue) {
setState(() {
_pageLink = newValue;
});
},
items: <String>[info, page_buying_instructions, page_payment_methods]
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(
value,
style:
TextStyle(color: value == info ? Colors.black : primaryColor),
),
);
}).toList(),
);
final pageLabelEngBox = InputText(
controller: _pageLabelEng,
labelTextKey: "faq.edit.page.label.eng",
);
final pageLabelMmBox = InputText(
controller: _pageLabelMm,
labelTextKey: "faq.edit.page.label.mm",
);
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
body: CustomScrollView(slivers: [
SliverAppBar(
leading: IconButton(
icon: Icon(
CupertinoIcons.back,
size: 30,
),
onPressed: () => Navigator.of(context).pop(),
),
backgroundColor: primaryColor,
expandedHeight: 150.0,
floating: true,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
centerTitle: true,
titlePadding: EdgeInsets.symmetric(vertical: 10),
title: LocalText(
context,
_isNew ? 'faq.add.title' : 'faq.edit.title',
fontSize: 20,
color: Colors.white,
)),
actions: [
IconButton(
icon: Icon(Icons.delete),
onPressed: _delete,
)
],
),
SliverList(
delegate: SliverChildListDelegate([
Form(
key: _formKey,
child: Padding(
padding: EdgeInsets.only(left: 24.0, right: 24.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
snBox,
Center(child: itemTitle(context, "faq.edit.eng")),
subItemTitle(context, "faq.edit.question",
iconData: SimpleLineIcons.question),
questionEngBox,
subItemTitle(context, "faq.edit.answer",
iconData: MaterialCommunityIcons.message_reply_text),
answerEngBox,
Divider(),
Center(child: itemTitle(context, "faq.edit.mm")),
subItemTitle(context, "faq.edit.question",
iconData: SimpleLineIcons.question),
questionMmBox,
subItemTitle(context, "faq.edit.answer",
iconData: MaterialCommunityIcons.message_reply_text),
answerMmBox,
Divider(),
Center(child: itemTitle(context, "faq.edit.page")),
pageLinkBox,
pageLabelEngBox,
pageLabelMmBox,
fcsButton(context, getLocalString(context, "btn.save"),
callack: _save),
SizedBox(
height: 20,
)
],
),
),
),
]))
])));
}
_save() async {
setState(() {
_isLoading = true;
});
try {
int sn = int.parse(
_sn.text,
onError: (source) => throw Exception("Invalid number"),
);
FAQModel faqModel = Provider.of<FAQModel>(context, listen: false);
FAQ _faq = FAQ(
sn: sn,
questionEng: _engQ.text,
answerEng: _engA.text,
questionMm: _mmQ.text,
answerMm: _mmA.text,
pageLinkLabelEng: _pageLabelEng.text,
pageLinkLabelMm: _pageLabelMm.text,
pageLink: _pageLink);
if (_isNew) {
await faqModel.addFAQ(_faq);
} else {
_faq.id = widget.faq.id;
await faqModel.updateFAQ(_faq);
}
Navigator.pop(context);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
_delete() {
showConfirmDialog(context, "faq.edit.delete.confirm", _deleteFAQ);
}
_deleteFAQ() async {
setState(() {
_isLoading = true;
});
try {
FAQModel faqModel = Provider.of<FAQModel>(context, listen: false);
await faqModel.deleteFAQ(widget.faq);
Navigator.pop(context);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
}

View File

@@ -0,0 +1,179 @@
import 'package:fcs/domain/constants.dart';
import 'package:fcs/domain/entities/faq.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/localization/app_translations.dart';
import 'package:fcs/pages/buying_instruction/buying_online.dart';
import 'package:fcs/pages/faq/faq_edit_page.dart';
import 'package:fcs/pages/main/model/language_model.dart';
import 'package:fcs/pages/main/model/main_model.dart';
import 'package:fcs/pages/payment_methods/payment_method_page.dart';
import 'package:fcs/pages/widgets/bottom_up_page_route.dart';
import 'package:fcs/pages/widgets/fcs_expansion_tile.dart';
import 'package:fcs/pages/widgets/local_text.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'model/faq_model.dart';
const Duration _kExpand = Duration(milliseconds: 200);
class FAQListPage extends StatefulWidget {
@override
_FAQListPageState createState() => _FAQListPageState();
}
class _FAQListPageState extends State<FAQListPage>
with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation<double> _iconTurns;
@override
void initState() {
super.initState();
_controller = AnimationController(duration: _kExpand, vsync: this);
var _halfTween = Tween<double>(begin: 0.0, end: 0.5);
var _easeInTween = CurveTween(curve: Curves.easeIn);
_iconTurns = _controller.drive(_halfTween.chain(_easeInTween));
}
@override
Widget build(BuildContext context) {
FAQModel faqModel = Provider.of<FAQModel>(context);
bool isEditable = context.select((MainModel m) => m.faqEditable());
return Scaffold(
floatingActionButton: isEditable
? FloatingActionButton.extended(
onPressed: () {
Navigator.of(context).push(BottomUpPageRoute(FAQEditor()));
},
icon: Icon(Icons.add),
label: LocalText(context, "faq.add.title", color: Colors.white),
backgroundColor: primaryColor,
)
: Container(),
body: CustomScrollView(
slivers: [
SliverAppBar(
leading: IconButton(
icon: Icon(
CupertinoIcons.back,
size: 30,
),
onPressed: () => Navigator.of(context).pop(),
),
backgroundColor: primaryColor,
expandedHeight: 150.0,
floating: false,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
centerTitle: true,
titlePadding:
EdgeInsets.symmetric(vertical: 10, horizontal: 45),
title: LocalText(
context,
"faq.title",
fontSize: 20,
color: Colors.white,
),
),
actions: isEditable
? [
IconButton(
onPressed: () => setState(() {
isEditMode = !isEditMode;
}),
icon: Icon(
Icons.edit,
color: Colors.white,
))
]
: [],
),
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
return _faqItem(context, faqModel.faqs[index]);
},
childCount: faqModel.faqs.length,
),
)
],
));
}
bool isEditMode = false;
Widget _faqItem(BuildContext context, FAQ faq) {
bool isEng = Provider.of<LanguageModel>(context).isEng;
return Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: FcsExpansionTile(
isEdit: isEditMode,
title: TextLocalStyle(
context,
faq.question(isEng),
fontSize: 16,
fontWeight: FontWeight.w600,
color: primaryColor,
),
onEditPress: () {
Navigator.of(context).push<void>(CupertinoPageRoute(
builder: (context) => FAQEditor(faq: faq),
));
},
children: [getAnwser(context, faq)],
),
),
Divider(
thickness: 2,
),
],
);
}
Widget getAnwser(BuildContext context, FAQ faq) {
bool isEng = Provider.of<LanguageModel>(context).isEng;
return Column(
children: [
TextLocalStyle(
context,
faq.answer(isEng),
fontSize: 16,
fontWeight: FontWeight.w200,
),
_pageLink(
faq.pageLink,
isEng ? faq.pageLinkLabelEng : faq.pageLinkLabelMm,
),
],
);
}
Widget _pageLink(String linkPage, String text) {
return linkPage == null || linkPage == "" || text == null || text == ""
? Container()
: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
FlatButton(
color: primaryColor,
onPressed: () => _selectLinkPage(linkPage),
child: LocalText(context, "", text: text, color: Colors.white),
)
],
);
}
_selectLinkPage(String linkPage) {
if (linkPage == page_payment_methods) {
Navigator.of(context).push(BottomUpPageRoute(PaymentMethodPage()));
} else if (linkPage == page_buying_instructions) {
Navigator.of(context).push(BottomUpPageRoute(BuyingOnlinePage()));
}
}
}

View File

@@ -0,0 +1,56 @@
import 'dart:async';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:fcs/data/services/services.dart';
import 'package:fcs/domain/entities/faq.dart';
import 'package:fcs/pages/main/model/base_model.dart';
import 'package:logging/logging.dart';
class FAQModel extends BaseModel {
final log = Logger('FAQModel');
List<FAQ> faqs = [];
FAQ getFAQ(String id) {
return faqs.firstWhere((e) => e.id == id, orElse: () => null);
}
StreamSubscription<QuerySnapshot> listener;
FAQModel() {
if (listener != null) listener.cancel();
try {
listener = Firestore.instance
.collection("/faqs")
.orderBy("sn", descending: false)
.snapshots()
.listen((snaps) {
faqs.clear();
snaps.documents.forEach((d) {
faqs.add(FAQ.fromMap(d.data, d.documentID));
});
notifyListeners();
});
} catch (e) {
log.warning("error:$e");
}
}
Future<void> addFAQ(FAQ faq) async {
await request("/faqs", "POST",
payload: faq.toMap(),
token: await Services.instance.authService.getToken());
}
Future<void> updateFAQ(FAQ faq) async {
await request("/faqs", "PUT",
payload: faq.toMap(),
token: await Services.instance.authService.getToken());
}
Future<void> deleteFAQ(FAQ faq) async {
await request("/faqs", "DELETE",
payload: faq.toMap(),
token: await Services.instance.authService.getToken());
}
}

103
lib/pages/faq/widgets.dart Normal file
View File

@@ -0,0 +1,103 @@
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/localization/app_translations.dart';
import 'package:fcs/pages/widgets/local_text.dart';
import 'package:flutter/material.dart';
Widget itemTitle(BuildContext context, String textKey) {
return Padding(
padding: const EdgeInsets.only(left: 18.0, top: 15, bottom: 0),
child: Text(
AppTranslations.of(context).text(textKey),
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 18, color: Colors.black),
),
);
}
Widget subItemTitle(BuildContext context, String textKey, {IconData iconData}) {
return Padding(
padding: const EdgeInsets.only(left: 0, top: 0, bottom: 0),
child: Row(
children: [
Icon(
iconData,
color: primaryColor,
),
SizedBox(width: 10),
Text(
AppTranslations.of(context).text(textKey),
style: TextStyle(
fontWeight: FontWeight.w700, fontSize: 15, color: primaryColor),
),
],
),
);
}
Widget contactItem(BuildContext context, String text, IconData iconData,
{Function() onTap, String labelKey}) {
return Material(
child: Padding(
padding: const EdgeInsets.only(left: 18.0, bottom: 10, right: 18),
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.grey, width: 0.8),
borderRadius: BorderRadius.all(
Radius.circular(5.0) // <--- border radius here
),
),
child: InkWell(
onTap: () => onTap != null ? onTap() : null,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Icon(
iconData,
color: primaryColor,
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
labelKey == null
? Container()
: Padding(
padding: EdgeInsets.fromLTRB(10, 10, 10, 0),
child: LocalText(context, labelKey,
color: primaryColor,
fontWeight: FontWeight.w500,
fontSize: 18),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
text == null ? "" : text,
overflow: TextOverflow.ellipsis,
maxLines: 5,
style: TextStyle(
fontSize: 14.0,
),
),
),
],
),
SizedBox(
width: 5,
),
onTap == null
? Container()
: Icon(
Icons.open_in_new,
color: Colors.grey,
size: 15,
)
],
),
)),
),
),
);
}

View File

@@ -1,168 +0,0 @@
import 'package:fcs/model/pickup_model.dart';
import 'package:fcs/fcs/common/pages/util.dart';
import 'package:fcs/vo/pickup.dart';
import 'package:fcs/widget/fcs_text_field.dart';
import 'package:fcs/widget/local_text.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:provider/provider.dart';
import 'package:flutter/material.dart';
import 'package:fcs/widget/progress.dart';
import '../fcs/common/helpers/theme.dart';
class FCSProfilePage extends StatefulWidget {
final PickUp pickUp;
FCSProfilePage({this.pickUp});
@override
_FCSProfilePageState createState() => _FCSProfilePageState();
}
class _FCSProfilePageState extends State<FCSProfilePage> {
TextEditingController _usaAddressEditingController =
new TextEditingController();
TextEditingController _mmAddressEditingController =
new TextEditingController();
TextEditingController _contactEditingController = new TextEditingController();
TextEditingController _mmContactEditingController =
new TextEditingController();
TextEditingController _mailEditingController = new TextEditingController();
TextEditingController _fbLinkEditingController = new TextEditingController();
PickUp _pickUp;
bool _isLoading = false;
@override
void initState() {
super.initState();
var pickupModel = Provider.of<PickUpModel>(context, listen: false);
_usaAddressEditingController.text = pickupModel.profile.usaAddress;
_mmAddressEditingController.text = pickupModel.profile.mmAddress;
_contactEditingController.text = pickupModel.profile.usaContactNumber;
_mmContactEditingController.text = pickupModel.profile.mmContactNumber;
_mailEditingController.text = pickupModel.profile.mail;
_fbLinkEditingController.text = pickupModel.profile.facebook;
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
final usaAddress = fcsInput('USA Delivery Address', Icons.location_on,
controller: _usaAddressEditingController);
final mmAddress = fcsInput('Yangon, Myanmar Office', Icons.location_on,
controller: _mmAddressEditingController);
final contactNumber = fcsInput('USA contact number', Icons.phone,
controller: _contactEditingController);
final mmContactNumber = fcsInput('Myanmar contact number', Icons.phone,
controller: _mmContactEditingController);
final mailBox = fcsInput('Email Address', Icons.mail,
controller: _mailEditingController);
final fbLinkBox = fcsInput('Facebook Link', FontAwesomeIcons.facebook,
controller: _fbLinkEditingController);
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: LocalText(
context,
'fcs.profile',
color: Colors.white,
fontSize: 20,
),
),
body: Card(
child: Column(
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 20.0),
child: ListView(children: <Widget>[
usaAddress,
SizedBox(height: 10),
mmAddress,
SizedBox(height: 10),
contactNumber,
SizedBox(height: 10),
mmContactNumber,
SizedBox(height: 10),
mailBox,
SizedBox(height: 10),
fbLinkBox,
SizedBox(height: 10),
]),
)),
widget.pickUp == null
? Align(
alignment: Alignment.bottomCenter,
child: Center(
child: Container(
width: 250,
child: FlatButton(
child: Text('Update'),
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(
shape: new RoundedRectangleBorder(
borderRadius:
new BorderRadius.circular(10)),
child: Text('Pickuped'),
color: primaryColor,
textColor: Colors.white,
onPressed: () {
Navigator.pop(context);
},
),
))),
Align(
alignment: Alignment.bottomCenter,
child: Center(
child: Container(
width: 250,
child: FlatButton(
shape: new RoundedRectangleBorder(
borderRadius:
new BorderRadius.circular(10)),
child: Text('Cancel'),
color: primaryColor,
textColor: Colors.white,
onPressed: () {
Navigator.pop(context);
},
),
)))
],
))
],
),
),
),
);
}
}

View File

@@ -1,29 +1,28 @@
import 'package:fcs/model/main_model.dart';
import 'package:fcs/model/shipment_model.dart';
import 'package:fcs/fcs/common/pages/util.dart';
import 'package:fcs/vo/shipment.dart';
import 'package:fcs/vo/user.dart';
import 'package:fcs/widget/label_widgets.dart';
import 'package:fcs/domain/entities/shipment.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/localization/app_translations.dart';
import 'package:fcs/pages/fcs_shipment/model/fcs_shipment_model.dart';
import 'package:fcs/pages/main/model/main_model.dart';
import 'package:fcs/pages/widgets/label_widgets.dart';
import 'package:fcs/pages/widgets/progress.dart';
import 'package:flutter_icons/flutter_icons.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:fcs/widget/localization/app_translations.dart';
import 'package:flutter/material.dart';
import 'package:fcs/widget/progress.dart';
import '../fcs/common/helpers/theme.dart';
import '../main/util.dart';
class ShipmentEditor extends StatefulWidget {
class FcsShipmentEditor extends StatefulWidget {
final Shipment shipment;
ShipmentEditor({this.shipment});
FcsShipmentEditor({this.shipment});
@override
_ShipmentEditorState createState() => _ShipmentEditorState();
_FcsShipmentEditorState createState() => _FcsShipmentEditorState();
}
class _ShipmentEditorState extends State<ShipmentEditor> {
class _FcsShipmentEditorState extends State<FcsShipmentEditor> {
var dateFormatter = new DateFormat('dd MMM yyyy');
TextEditingController _shipmentNumberController = new TextEditingController();
TextEditingController _cutoffDateController = new TextEditingController();
@@ -57,45 +56,6 @@ class _ShipmentEditorState extends State<ShipmentEditor> {
super.dispose();
}
Widget _showCustomerData(User customer) {
return Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(left: 10.0, top: 8),
child: Text(
customer.name,
style: TextStyle(
color: Colors.black87,
fontSize: 18,
fontWeight: FontWeight.bold),
),
),
Padding(
padding: const EdgeInsets.only(left: 10.0, top: 8),
child: Text(
customer.phoneNumber,
style: TextStyle(
color: Colors.black87,
fontSize: 18,
fontWeight: FontWeight.bold),
),
),
widget.shipment == null
? Container()
: Padding(
padding: const EdgeInsets.only(left: 10.0, top: 8),
child: Text(
_shipmentNumberController.text,
style: TextStyle(
color: Colors.black87,
fontSize: 15,
fontWeight: FontWeight.bold),
),
),
],
);
}
Widget showShipmentNumber(BuildContext context) {
return Container(
padding: EdgeInsets.only(top: 10),
@@ -112,7 +72,8 @@ class _ShipmentEditorState extends State<ShipmentEditor> {
);
}
Widget showShipmentTypes(BuildContext context, ShipmentModel shipmentModel) {
Widget showShipmentTypes(
BuildContext context, FcsShipmentModel shipmentModel) {
return Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
@@ -154,8 +115,7 @@ class _ShipmentEditorState extends State<ShipmentEditor> {
@override
Widget build(BuildContext context) {
var shipmentModel = Provider.of<ShipmentModel>(context);
MainModel mainModel = Provider.of<MainModel>(context);
var shipmentModel = Provider.of<FcsShipmentModel>(context);
final cargoBtn = Container(
padding: EdgeInsets.only(top: 5),

View File

@@ -1,25 +1,22 @@
import 'package:fcs/model/shipment_model.dart';
import 'package:fcs/widget/bottom_up_page_route.dart';
import 'package:fcs/widget/local_text.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/localization/app_translations.dart';
import 'package:fcs/pages/fcs_shipment/model/fcs_shipment_model.dart';
import 'package:fcs/pages/widgets/bottom_up_page_route.dart';
import 'package:fcs/pages/widgets/local_text.dart';
import 'package:fcs/pages/widgets/progress.dart';
import 'package:provider/provider.dart';
import 'package:fcs/pages/search_page.dart';
import 'package:fcs/vo/buyer.dart';
import 'package:fcs/widget/localization/app_translations.dart';
import 'package:flutter/material.dart';
import 'package:fcs/widget/progress.dart';
import '../fcs/common/helpers/theme.dart';
import 'shipment_editor.dart';
import 'shipment_list_row.dart';
import 'fcs_shipment_editor.dart';
import 'fcs_shipment_list_row.dart';
class ShipmentList extends StatefulWidget {
class FcsShipmentList extends StatefulWidget {
@override
_ShipmentListState createState() => _ShipmentListState();
_FcsShipmentListState createState() => _FcsShipmentListState();
}
class _ShipmentListState extends State<ShipmentList> {
Buyer buyer;
class _FcsShipmentListState extends State<FcsShipmentList> {
bool _isLoading = false;
@override
@@ -55,7 +52,7 @@ class _ShipmentListState extends State<ShipmentList> {
color: Colors.white,
),
iconSize: 30,
onPressed: () => showPlacesSearch(context),
// onPressed: () => showPlacesSearch(context),
),
],
bottom: TabBar(
@@ -85,11 +82,11 @@ class _ShipmentListState extends State<ShipmentList> {
}
_newShipment() {
Navigator.of(context).push(BottomUpPageRoute(ShipmentEditor()));
Navigator.of(context).push(BottomUpPageRoute(FcsShipmentEditor()));
}
Widget _upComing() {
var shipmentModel = Provider.of<ShipmentModel>(context);
var shipmentModel = Provider.of<FcsShipmentModel>(context);
return Column(
children: <Widget>[
Expanded(
@@ -102,7 +99,8 @@ class _ShipmentListState extends State<ShipmentList> {
shrinkWrap: true,
itemCount: shipmentModel.upcoming.length,
itemBuilder: (BuildContext context, int index) {
return ShipmentListRow(shipment: shipmentModel.upcoming[index]);
return FcsShipmentListRow(
shipment: shipmentModel.upcoming[index]);
}),
),
],
@@ -110,7 +108,7 @@ class _ShipmentListState extends State<ShipmentList> {
}
Widget _completed() {
var shipmentModel = Provider.of<ShipmentModel>(context);
var shipmentModel = Provider.of<FcsShipmentModel>(context);
return Column(
children: <Widget>[
Expanded(
@@ -123,7 +121,7 @@ class _ShipmentListState extends State<ShipmentList> {
shrinkWrap: true,
itemCount: shipmentModel.completed.length,
itemBuilder: (BuildContext context, int index) {
return ShipmentListRow(
return FcsShipmentListRow(
shipment: shipmentModel.completed[index]);
}),
),
@@ -132,7 +130,7 @@ class _ShipmentListState extends State<ShipmentList> {
}
Widget _canceled() {
var shipmentModel = Provider.of<ShipmentModel>(context);
var shipmentModel = Provider.of<FcsShipmentModel>(context);
return Column(
children: <Widget>[
Expanded(
@@ -145,7 +143,8 @@ class _ShipmentListState extends State<ShipmentList> {
shrinkWrap: true,
itemCount: shipmentModel.canceled.length,
itemBuilder: (BuildContext context, int index) {
return ShipmentListRow(shipment: shipmentModel.canceled[index]);
return FcsShipmentListRow(
shipment: shipmentModel.canceled[index]);
}),
),
],

View File

@@ -1,22 +1,22 @@
import 'package:fcs/fcs/common/helpers/theme.dart';
import 'package:fcs/vo/shipment.dart';
import 'package:fcs/widget/bottom_up_page_route.dart';
import 'package:fcs/domain/entities/shipment.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/pages/main/util.dart';
import 'package:fcs/pages/widgets/bottom_up_page_route.dart';
import 'package:flutter/material.dart';
import 'package:flutter_icons/flutter_icons.dart';
import 'package:intl/intl.dart';
import 'shipment_editor.dart';
import '../fcs/common/pages/util.dart';
import 'fcs_shipment_editor.dart';
class ShipmentListRow extends StatefulWidget {
class FcsShipmentListRow extends StatefulWidget {
final Shipment shipment;
const ShipmentListRow({this.shipment});
const FcsShipmentListRow({this.shipment});
@override
_ShipmentListRowState createState() => _ShipmentListRowState();
_FcsShipmentListRowState createState() => _FcsShipmentListRowState();
}
class _ShipmentListRowState extends State<ShipmentListRow> {
class _FcsShipmentListRowState extends State<FcsShipmentListRow> {
var dateFormatter = new DateFormat('dd MMM yyyy');
final double dotSize = 15.0;
Shipment _shipment = new Shipment();
@@ -37,7 +37,7 @@ class _ShipmentListRowState extends State<ShipmentListRow> {
child: InkWell(
onTap: () {
Navigator.of(context)
.push(BottomUpPageRoute(ShipmentEditor(shipment: _shipment)));
.push(BottomUpPageRoute(FcsShipmentEditor(shipment: _shipment)));
},
child: Row(
children: <Widget>[

View File

@@ -0,0 +1,84 @@
import 'package:fcs/domain/entities/shipment.dart';
import 'package:fcs/pages/main/model/base_model.dart';
class FcsShipmentModel extends BaseModel {
List<String> shipmentType = ['Air', 'Ship', 'Cargo Truck'];
List<Shipment> shipments = [
Shipment(
shipDate: DateTime(2020, 4, 23),
shipmentNumber: 'A103B',
status: 'In Progress',
arrivalDate: DateTime(2020, 4, 30),
departureDate: DateTime(2020, 4, 23)),
Shipment(
shipDate: DateTime(2020, 4, 2),
shipmentNumber: 'A100A',
status: 'Ready to ship',
arrivalDate: DateTime(2020, 4, 28),
departureDate: DateTime(2020, 4, 15)),
Shipment(
shipDate: DateTime(2020, 4, 2),
shipmentNumber: 'A100B',
status: 'Arrived',
arrivalDate: DateTime(2020, 4, 28),
departureDate: DateTime(2020, 4, 15)),
Shipment(
shipDate: DateTime(2020, 4, 10),
shipmentNumber: 'A102B',
status: 'Canceled',
arrivalDate: DateTime(2020, 4, 20),
departureDate: DateTime(2020, 4, 10)),
Shipment(
shipDate: DateTime(2020, 4, 2),
shipmentNumber: 'A100B',
status: 'Canceled',
arrivalDate: DateTime(2020, 4, 20),
departureDate: DateTime(2020, 4, 23)),
Shipment(
shipDate: DateTime(2020, 4, 10),
shipmentNumber: 'A102B',
status: 'Arrived',
arrivalDate: DateTime(2020, 4, 30),
departureDate: DateTime(2020, 4, 20),
)
];
List<Shipment> get canceled {
List<Shipment> _p = shipments.where((e) => e.status == "Canceled").toList()
..sort((e1, e2) {
return e1.shipDate.compareTo(e2.shipDate);
});
return _p;
}
List<Shipment> get completed {
return shipments.where((e) => e.status == "Arrived").toList()
..sort((e1, e2) {
return e1.shipDate.compareTo(e2.shipDate);
});
}
List<Shipment> get upcoming {
List<Shipment> _shipments = shipments
.where((e) =>
e.status == "In Progress" ||
e.status == "Ready to ship" ||
e.status == "Processed" ||
e.status == "Rescheduled")
.toList();
_shipments.sort((e1, e2) {
return e1.shipDate.compareTo(e2.shipDate);
});
return _shipments;
}
void initUser(user) {
super.initUser(user);
}
@override
logout() async {
shipments = [];
}
}

View File

@@ -1,126 +0,0 @@
import 'package:flutter/material.dart';
import 'package:progress/progress.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/main_model.dart';
import 'package:fcs/model/user_model.dart';
import 'package:fcs/pages/reset_password.dart';
import 'package:fcs/fcs/common/helpers/theme.dart';
import 'package:fcs/widget/local_text.dart';
import 'package:fcs/widget/localization/app_translations.dart';
import 'package:fcs/widget/progress.dart';
import '../fcs/common/pages/util.dart';
class ForgetPassword extends StatefulWidget {
final phoneNumber;
const ForgetPassword({Key key, this.phoneNumber}) : super(key: key);
@override
_ForgetPasswordState createState() => _ForgetPasswordState();
}
class _ForgetPasswordState extends State<ForgetPassword> {
final TextEditingController _email = new TextEditingController();
bool _isLoading = false;
final _formKey = GlobalKey<FormState>();
@override
void initState() {
super.initState();
_email.text = widget.phoneNumber;
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
final emailInput = TextFormField(
controller: _email,
autofocus: false,
decoration: new InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.grey, width: 1.0)),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.grey, width: 1.0)),
labelText: AppTranslations.of(context).text("forget.email"),
labelStyle: labelStyle,
icon: Icon(
Icons.email,
color: primaryColor,
)),
validator: (value) {
if (value.isEmpty) {
return AppTranslations.of(context).text("forget.email.empty");
}
return null;
},
);
final enterButton = Padding(
padding: EdgeInsets.symmetric(vertical: 16.0),
child: RaisedButton(
onPressed: () => _forgetPassword(),
padding: EdgeInsets.all(10),
color: primaryColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(7),
),
child: Text(AppTranslations.of(context).text("forget.enter"),
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold)),
),
);
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: AppBar(
title: LocalText(
context,
'forget.password',
fontSize: 20,
color: Colors.white,
),
backgroundColor: primaryColor,
),
body: Center(
child: ListView(
shrinkWrap: true,
padding: EdgeInsets.only(left: 24.0, right: 24.0),
children: <Widget>[
Form(key: _formKey, child: emailInput),
SizedBox(height: 8.0),
enterButton,
],
),
),
),
);
}
Future<void> _forgetPassword() async {
var phoneNumber = _email.text;
if (phoneNumber.isEmpty) {
showMsgDialog(context, "Error", "Please input email(or)phone number");
return;
}
setState(() {
_isLoading = true;
});
try {
UserModel userModel = Provider.of<UserModel>(context);
await userModel.forgetPassword(phoneNumber);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ResetPasswordPage(phoneNumber)));
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
}

View File

@@ -1,119 +0,0 @@
import 'dart:async';
import 'dart:io';
import 'package:archive/archive.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:http_server/http_server.dart';
import 'package:logging/logging.dart';
import 'package:path_provider/path_provider.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/main_model.dart';
import 'package:fcs/vo/setting.dart';
import 'package:fcs/widget/progress.dart';
typedef void ProfileCallback();
class Help extends StatefulWidget {
const Help({Key key}) : super(key: key);
@override
_HelpState createState() => _HelpState();
}
class _HelpState extends State<Help> {
final log = Logger('Help');
bool isLoading = false;
HttpServer httpServer;
// WebViewController _controller;
String url = "";
@override
void initState() {
super.initState();
MainModel mainModel = Provider.of<MainModel>(context, listen: false);
var isBuyer = mainModel.isBuyer();
this.url = "http://localhost:7777/web/index.html" +
(isBuyer ? "?is_buyer=true" : "?is_buyer=false");
_run(mainModel.setting);
}
void _run(Setting setting) async {
await _download(setting);
final directory = await getApplicationDocumentsDirectory();
var staticFiles = new VirtualDirectory('${directory.path}')
..allowDirectoryListing = true;
HttpServer.bind('0.0.0.0', 7777).then((server) async {
httpServer = server;
log.info('Server running');
server.listen(staticFiles.serveRequest);
// _controller.loadUrl(url);
log.info("locad url:$url");
}, onError: (e) {
log.warning("Error===>:$e");
});
}
Future<void> dispose() async {
super.dispose();
if (httpServer != null) await httpServer.close(force: true);
}
Future<void> _download(Setting setting) async {
final directory = (await getApplicationDocumentsDirectory()).path;
var file = File('$directory/${setting.helpFileName()}');
if (await file.exists()) {
return;
}
String url = setting.helpURL;
var req = await http.Client().get(Uri.parse(url));
File zippedFile = await file.writeAsBytes(req.bodyBytes);
File prev = File('$directory/web');
if (await prev.exists()) {
await prev.delete(recursive: true);
}
var bytes = zippedFile.readAsBytesSync();
var archive = ZipDecoder().decodeBytes(bytes);
for (var file in archive) {
var fileName = '$directory/web/${file.name}';
if (file.isFile) {
var outFile = File(fileName);
outFile = await outFile.create(recursive: true);
await outFile.writeAsBytes(file.content);
}
}
}
@override
Widget build(BuildContext context) {
return LocalProgress(
inAsyncCall: isLoading,
child: Scaffold(
appBar: AppBar(
iconTheme: IconThemeData(
color: Colors.black, //change your color here
),
centerTitle: true,
backgroundColor: Colors.white,
title: ClipRRect(
child: Image.asset("assets/logo.png", height: 40),
borderRadius: new BorderRadius.circular(15.0),
)),
body:Text("abc"),
// body: WebView(
// initialUrl: url,
// javascriptMode: JavascriptMode.unrestricted,
// onWebViewCreated: (WebViewController webViewController) {
// _controller = webViewController;
// },
// ),
),
);
}
}

View File

@@ -1,66 +0,0 @@
import 'package:fcs/fcs/common/helpers/theme.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/language_model.dart';
import 'package:fcs/model/main_model.dart';
import 'package:fcs/model/manual_model.dart';
import 'package:fcs/pages/manual/manual_item_title_dialog.dart';
import 'package:fcs/fcs/common/pages/util.dart';
import 'package:fcs/vo/manual.dart';
import 'package:fcs/widget/local_text.dart';
import 'package:fcs/widget/progress.dart';
class InstructionPage extends StatefulWidget {
final String image;
final String name;
const InstructionPage({Key key, this.image, this.name}) : super(key: key);
@override
_InstructionPageState createState() => _InstructionPageState();
}
class _InstructionPageState extends State<InstructionPage> {
TextEditingController _manualVersionController = TextEditingController();
final double dotSize = 10.0;
List<ManualItem> helpList = new List();
bool isEng;
String versionName;
bool _isLoading = false;
@override
void initState() {
helpList.clear();
var manualModel = Provider.of<ManualModel>(context, listen: false);
var mainModel = Provider.of<MainModel>(context, listen: false);
versionName = manualModel.version;
helpList = manualModel.getHelpList(mainModel.isBuyer());
super.initState();
}
@override
Widget build(BuildContext context) {
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: AppBar(
centerTitle: true,
leading: new IconButton(
icon: new Icon(Icons.close, color: Colors.white),
onPressed: () => Navigator.of(context).pop(),
),
title: Text(widget.name),
backgroundColor: primaryColor,
),
body: Container(
padding: EdgeInsets.only(left: 5, right: 5, top: 5),
child: Card(
elevation: 0,
child: Expanded(
child: FittedBox(
child: Image.asset(widget.image), fit: BoxFit.contain),
)),
),
),
);
}
}

View File

@@ -1,67 +0,0 @@
import 'package:fcs/fcs/common/pages/util.dart';
import 'package:fcs/widget/localization/app_translations.dart';
import 'package:flutter/material.dart';
import 'package:fcs/widget/progress.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import '../fcs/common/helpers/theme.dart';
class InvitationPage extends StatefulWidget {
@override
_InvitationPageState createState() => _InvitationPageState();
}
class _InvitationPageState extends State<InvitationPage> {
TextEditingController _nameController = new TextEditingController();
TextEditingController _phoneController = new TextEditingController();
bool _isLoading = false;
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
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("customer.form.title")),
),
body: Container(
padding: EdgeInsets.all(18),
child: Column(
children: <Widget>[
Expanded(
child: ListView(
children: <Widget>[
fcsInput("Name", Icons.person, controller: _nameController),
fcsInput("Phone Number", Icons.phone,
controller: _phoneController),
SizedBox(height: 30),
],
),
),
fcsButton(context, "Invite", callack: () {}),
SizedBox(height: 10)
],
),
),
),
);
}
}

View File

@@ -1,9 +1,8 @@
import 'package:fcs/model_fcs/box_model.dart';
import 'package:fcs/model_fcs/package_model.dart';
import 'package:fcs/fcs/common/helpers/theme.dart';
import 'package:fcs/vo/box.dart';
import 'package:fcs/widget/localization/app_translations.dart';
import 'package:fcs/widget/progress.dart';
import 'package:fcs/domain/entities/box.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/localization/app_translations.dart';
import 'package:fcs/pages/box/model/box_model.dart';
import 'package:fcs/pages/widgets/progress.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

View File

@@ -1,21 +1,12 @@
import 'package:fcs/model/invoice_model.dart';
import 'package:fcs/model/main_model.dart';
import 'package:fcs/model/shipment_model.dart';
import 'package:fcs/model_fcs/package_model.dart';
import 'package:fcs/pages_fcs/package_list_row.dart';
import 'package:fcs/fcs/common/helpers/theme.dart';
import 'package:fcs/widget/bottom_up_page_route.dart';
import 'package:fcs/widget/local_text.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/localization/app_translations.dart';
import 'package:fcs/pages/invoice/model/invoice_model.dart';
import 'package:fcs/pages/widgets/bottom_up_page_route.dart';
import 'package:fcs/pages/widgets/local_text.dart';
import 'package:fcs/pages/widgets/progress.dart';
import 'package:provider/provider.dart';
import 'package:fcs/pages/search_page.dart';
import 'package:fcs/vo/buyer.dart';
import 'package:fcs/widget/localization/app_translations.dart';
import 'package:flutter/material.dart';
import 'package:fcs/widget/progress.dart';
import '../shipment_editor.dart';
import '../shipment_list_row.dart';
import 'invoice_editor.dart';
import 'invoice_list_row.dart';
@@ -39,7 +30,7 @@ class _InvoiceListState extends State<InvoiceList> {
@override
Widget build(BuildContext context) {
var owner = Provider.of<MainModel>(context).isOwner();
var owner = true;
return LocalProgress(
inAsyncCall: _isLoading,
@@ -62,7 +53,7 @@ class _InvoiceListState extends State<InvoiceList> {
color: Colors.white,
),
iconSize: 30,
onPressed: () => showPlacesSearch(context),
// onPressed: () => showPlacesSearch(context),
),
],
bottom: TabBar(
@@ -95,30 +86,6 @@ class _InvoiceListState extends State<InvoiceList> {
Navigator.of(context).push(BottomUpPageRoute(InvoiceEditor()));
}
Widget _packages() {
var packageModel = Provider.of<PackageModel>(context);
return Column(
children: <Widget>[
Expanded(
child: new ListView.separated(
separatorBuilder: (context, index) => Divider(
color: Colors.black,
),
scrollDirection: Axis.vertical,
padding: EdgeInsets.only(top: 15),
shrinkWrap: true,
itemCount: packageModel.completed.length,
itemBuilder: (BuildContext context, int index) {
return PackageListRow(
package: packageModel.completed[index],
isReadOnly: true,
);
}),
),
],
);
}
Widget _pending() {
var invoiceModel = Provider.of<InvoiceModel>(context);
return Column(

View File

@@ -1,26 +1,23 @@
import 'package:fcs/model/discount_model.dart';
import 'package:fcs/model/main_model.dart';
import 'package:fcs/model_fcs/box_model.dart';
import 'package:fcs/pages/invoice/package_addition.dart';
import 'package:fcs/fcs/common/helpers/theme.dart';
import 'package:fcs/vo/box.dart';
import 'package:fcs/vo/cargo.dart';
import 'package:fcs/vo/invoice.dart';
import 'package:fcs/vo/package.dart';
import 'package:fcs/widget/bottom_up_page_route.dart';
import 'package:fcs/widget/local_text.dart';
import 'package:fcs/widget/localization/app_translations.dart';
import 'package:fcs/widget/multi_img_controller.dart';
import 'package:fcs/widget/multi_img_file.dart';
import 'package:fcs/widget/my_data_table.dart';
import 'package:fcs/widget/progress.dart';
import 'package:fcs/domain/entities/box.dart';
import 'package:fcs/domain/entities/cargo.dart';
import 'package:fcs/domain/entities/invoice.dart';
import 'package:fcs/domain/entities/payment_method.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/localization/app_translations.dart';
import 'package:fcs/pages/discount/model/discount_model.dart';
import 'package:fcs/pages/main/model/main_model.dart';
import 'package:fcs/pages/widgets/bottom_up_page_route.dart';
import 'package:fcs/pages/widgets/local_text.dart';
import 'package:fcs/pages/widgets/multi_img_controller.dart';
import 'package:fcs/pages/widgets/multi_img_file.dart';
import 'package:fcs/pages/widgets/my_data_table.dart';
import 'package:fcs/pages/widgets/progress.dart';
import 'package:flutter/material.dart';
import 'package:flutter_icons/flutter_icons.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import '../../fcs/common/pages/util.dart';
import 'box_addition.dart';
class InvoiceEditor extends StatefulWidget {
@@ -32,6 +29,29 @@ class InvoiceEditor extends StatefulWidget {
}
class _InvoiceEditorState extends State<InvoiceEditor> {
List<PaymentMethod> get paymentMethods {
List<PaymentMethod> methods = [
PaymentMethod(
name: 'AYA Bank',
accountName: 'FCS',
account: '100 23404320548398',
phone: '+959123456789',
),
PaymentMethod(
name: 'KBZ Bank',
accountName: 'FCS',
account: '100 23404320548398',
phone: '+959123456789',
),
PaymentMethod(
name: 'PayPal',
accountName: 'FCS',
link: 'https://www.paypal.com/donate/buttons',
),
];
return methods;
}
var dateFormatter = new DateFormat('dd MMM yyyy');
TextEditingController _invoiceNumberController = new TextEditingController();
TextEditingController _dateController = new TextEditingController();
@@ -58,7 +78,7 @@ class _InvoiceEditorState extends State<InvoiceEditor> {
];
List<String> _receipts = [
"assets/photos/amazon_ins.png",
"assets/buying_online_with_first_last_name.png",
];
@override
@@ -75,7 +95,7 @@ class _InvoiceEditorState extends State<InvoiceEditor> {
_statusController.text = _invoice.status.toString();
_handlingFeeController.text = '0';
_customFeeController.text = '0';
multiImgController.setImageUrls = _receipts;
// multiImgController.setImageUrls = _receipts;
_descriptionController.text = 'For Electronics goods';
_balanceController.text =
(_invoice.amount - _invoice.receipts[0].amount).toString();
@@ -452,7 +472,7 @@ class _InvoiceEditorState extends State<InvoiceEditor> {
Container(
width: 150.0,
child: DropdownButtonFormField(
items: mainModel.paymentMethods
items: paymentMethods
.map((e) => DropdownMenuItem(
child: Text(e.name), value: e.name))
.toList(),
@@ -598,7 +618,7 @@ class _InvoiceEditorState extends State<InvoiceEditor> {
),
),
),
mainModel.isOwner()
true
? Container(
padding: EdgeInsets.only(top: 20),
child: Align(

View File

@@ -1,22 +1,19 @@
import 'dart:async';
import 'dart:io';
import 'package:fcs/model/main_model.dart';
import 'package:fcs/pages/invoice/payment_pdf_screen.dart';
import 'package:fcs/fcs/common/helpers/theme.dart';
import 'package:fcs/vo/invoice.dart';
import 'package:fcs/widget/bottom_up_page_route.dart';
import 'package:fcs/domain/entities/invoice.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/pages/widgets/bottom_up_page_route.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:intl/intl.dart';
import 'package:path_provider/path_provider.dart';
import 'package:provider/provider.dart';
import '../../fcs/common/pages/util.dart';
import 'invoice_editor.dart';
import 'payment_page.dart';
import 'payment_pdf_screen.dart';
class InvoiceListRow extends StatefulWidget {
final Invoice invoice;
@@ -70,7 +67,7 @@ class _InvoiceListRowState extends State<InvoiceListRow> {
@override
Widget build(BuildContext context) {
var owner = Provider.of<MainModel>(context).isOwner();
var owner = true;
return Container(
padding: EdgeInsets.only(left: 15, right: 15),
child: Row(

View File

@@ -0,0 +1,163 @@
import 'package:fcs/domain/entities/invoice.dart';
import 'package:fcs/domain/entities/package.dart';
import 'package:fcs/domain/entities/receipt.dart';
import 'package:fcs/pages/main/model/base_model.dart';
class InvoiceModel extends BaseModel {
List<Invoice> invoices = [
Invoice(
invoiceNumber: 'A092(A)-30',
invoiceDate: DateTime(2020, 4, 5, 12, 30),
customerName: 'Ko Nyi',
customerPhoneNumber: '+959 888888888',
amount: 500,
status: 'Pending',
packages: [
Package(
shipmentNumber: "A202",
receiverNumber: "3",
boxNumber: "1",
rate: 7,
packageType: "General",
weight: 25,
status: "Received",
receiverAddress: '1 Bo Yar Nyunt St.\nDagon Tsp, Yangon',
arrivedDate: DateTime(2020, 6, 1),
),
Package(
shipmentNumber: "A202",
receiverNumber: "3",
boxNumber: "2",
rate: 7,
packageType: "General",
weight: 20,
status: "Received",
arrivedDate: DateTime(2020, 6, 1),
receiverAddress: '1 Bo Yar Nyunt St.\nDagon Tsp, Yangon'),
],
receipts: [
Receipt(amount: 200, date: DateTime(2020, 6, 1)),
Receipt(amount: 100, date: DateTime(2020, 6, 16)),
]),
Invoice(
invoiceNumber: 'A092(A)-31',
invoiceDate: DateTime(2020, 4, 5, 9, 30),
customerName: 'Ko Aung Myo',
customerPhoneNumber: '+959 444444444',
amount: 300,
status: 'Paid',
packages: [
Package(
shipmentNumber: "A202",
receiverNumber: "3",
boxNumber: "3",
rate: 7,
packageType: "General",
weight: 15,
status: "Received",
arrivedDate: DateTime(2020, 6, 1),
receiverAddress: '1 Bo Yar Nyunt St.\nDagon Tsp, Yangon'),
Package(
shipmentNumber: "A202",
receiverNumber: "2",
boxNumber: "1",
rate: 8,
packageType: "Medicine",
weight: 15,
status: "Processing",
arrivedDate: DateTime(2020, 6, 1),
receiverAddress: '2 Shwe Taung Kyar St, Bahan Tsp, Yangon'),
],
receipts: [
Receipt(amount: 200, date: DateTime(2020, 6, 1)),
]),
Invoice(
invoiceNumber: 'A092(A)-32',
invoiceDate: DateTime(2020, 4, 6, 10, 10),
customerName: 'Ko Zaw Thu',
customerPhoneNumber: '+959 777777777',
amount: 200,
status: 'Paid',
packages: [
Package(
shipmentNumber: "A202",
receiverNumber: "2",
boxNumber: "2",
rate: 7,
packageType: "General",
weight: 55,
status: "Ready to ship",
arrivedDate: DateTime(2020, 6, 1),
receiverAddress: '2 Shwe Taung Kyar St, Bahan Tsp, Yangon'),
Package(
shipmentNumber: "A201",
receiverNumber: "1",
boxNumber: "1",
rate: 9,
packageType: "Dangerous",
weight: 25,
status: "Delivered",
arrivedDate: DateTime(2020, 5, 21),
receiverAddress: '3 Kambzwza St, Bahan Tsp, Yangon'),
],
receipts: [
Receipt(amount: 200, date: DateTime(2020, 6, 1)),
]),
Invoice(
invoiceNumber: 'A092(A)-33',
invoiceDate: DateTime(2020, 4, 6, 12, 15),
customerName: 'Ko Myo Min',
customerPhoneNumber: '+959 555555555',
amount: 300,
status: 'Pending',
receipts: [
Receipt(amount: 200, date: DateTime(2020, 6, 1)),
],
packages: [
Package(
shipmentNumber: "A201",
receiverNumber: "1",
boxNumber: "1",
rate: 9,
packageType: "Dangerous",
weight: 25,
status: "Delivered",
arrivedDate: DateTime(2020, 5, 21),
receiverAddress: '3 Kambzwza St, Bahan Tsp, Yangon'),
Package(
shipmentNumber: "A201",
receiverNumber: "1",
boxNumber: "2",
rate: 7,
packageType: "General",
weight: 5,
status: "Delivered",
arrivedDate: DateTime(2020, 5, 21),
receiverAddress: '3 Kambzwza St, Bahan Tsp, Yangon'),
])
];
List<Invoice> get pending {
List<Invoice> _i = invoices.where((e) => e.status == "Pending").toList()
..sort((e1, e2) {
return e2.invoiceNumber.compareTo(e1.invoiceNumber);
});
return _i;
}
List<Invoice> get paided {
return invoices.where((e) => e.status == "Paid").toList()
..sort((e1, e2) {
return e2.invoiceNumber.compareTo(e1.invoiceNumber);
});
}
void initUser(user) {
super.initUser(user);
}
@override
logout() async {
invoices = [];
}
}

View File

@@ -1,107 +0,0 @@
import 'package:fcs/model_fcs/package_model.dart';
import 'package:fcs/fcs/common/helpers/theme.dart';
import 'package:fcs/vo/package.dart';
import 'package:fcs/widget/localization/app_translations.dart';
import 'package:fcs/widget/progress.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class PackageAddition extends StatefulWidget {
final Package package;
PackageAddition({this.package});
@override
_PackageAdditionState createState() => _PackageAdditionState();
}
class _PackageAdditionState extends State<PackageAddition> {
Package _package = new Package();
bool _isLoading = false;
@override
void initState() {
super.initState();
if (widget.package != null) {
_package = widget.package;
}
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
var packageModel = Provider.of<PackageModel>(context);
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.edit.title")),
),
body: Card(
child: Column(
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: ListView(children: <Widget>[
DropdownButtonFormField(
decoration: InputDecoration(
fillColor: Colors.white,
labelText: 'Package Number',
icon: Icon(Icons.pages)),
items: packageModel.completed
.map((e) => DropdownMenuItem(
child: Text(e.packageNumber), value: e))
.toList(),
onChanged: (map) => {},
),
]),
)),
widget.package == null
? Align(
alignment: Alignment.bottomCenter,
child: Center(
child: Container(
width: 250,
child: FlatButton(
child: Text('Add package'),
color: primaryColor,
textColor: Colors.white,
onPressed: () {
Navigator.pop(context);
},
),
)))
: Align(
alignment: Alignment.bottomCenter,
child: Center(
child: Container(
width: 250,
child: FlatButton(
child: Text('Save package'),
color: primaryColor,
textColor: Colors.white,
onPressed: () {
Navigator.pop(context);
},
),
))),
SizedBox(
height: 30,
)
],
),
),
),
);
}
}

View File

@@ -1,143 +0,0 @@
import 'package:fcs/model/main_model.dart';
import 'package:fcs/model/pickup_model.dart';
import 'package:fcs/fcs/common/helpers/theme.dart';
import 'package:fcs/vo/package.dart';
import 'package:fcs/widget/label_widgets.dart';
import 'package:fcs/widget/localization/app_translations.dart';
import 'package:fcs/widget/progress.dart';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
class PackageInfo extends StatefulWidget {
final Package package;
PackageInfo({this.package});
@override
_PackageInfoState createState() => _PackageInfoState();
}
class _PackageInfoState extends State<PackageInfo> {
var dateFormatter = new DateFormat('dd MMM yyyy');
Package _package;
bool _isLoading = false;
@override
void initState() {
super.initState();
if (widget.package != null) {
_package = widget.package;
}
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
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.edit.title")),
),
body: Card(
child: Column(
children: <Widget>[
Expanded(
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,
),
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"),
),
],
),
)
]),
)),
],
),
),
),
);
}
}

View File

@@ -1,15 +1,12 @@
import 'package:fcs/model/main_model.dart';
import 'package:fcs/fcs/common/pages/util.dart';
import 'package:fcs/fcs/common/helpers/theme.dart';
import 'package:fcs/vo/invoice.dart';
import 'package:fcs/vo/package.dart';
import 'package:fcs/widget/local_text.dart';
import 'package:fcs/widget/localization/app_translations.dart';
import 'package:fcs/widget/multi_img_controller.dart';
import 'package:fcs/widget/multi_img_file.dart';
import 'package:fcs/widget/my_data_table.dart';
import 'package:fcs/widget/number_cell.dart';
import 'package:fcs/widget/progress.dart';
import 'package:fcs/domain/entities/invoice.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/localization/app_translations.dart';
import 'package:fcs/pages/widgets/local_text.dart';
import 'package:fcs/pages/widgets/multi_img_controller.dart';
import 'package:fcs/pages/widgets/multi_img_file.dart';
import 'package:fcs/pages/widgets/my_data_table.dart';
import 'package:fcs/pages/widgets/number_cell.dart';
import 'package:fcs/pages/widgets/progress.dart';
import 'package:flutter/material.dart';
import 'package:flutter_icons/flutter_icons.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
@@ -38,7 +35,7 @@ class _PaymentPageState extends State<PaymentPage> {
Invoice _invoice = new Invoice();
bool _isLoading = false;
List<String> _receipts = [
"assets/photos/amazon_ins.png",
"assets/buying_online_with_first_last_name.png",
];
bool isNew;
@@ -46,7 +43,7 @@ class _PaymentPageState extends State<PaymentPage> {
void initState() {
if (widget.invoice != null) {
_invoice = widget.invoice;
multiImgController.setImageUrls = _receipts;
// multiImgController.setImageUrls = _receipts;
}
super.initState();
}

View File

@@ -1,7 +1,6 @@
import 'dart:async';
import 'package:fcs/fcs/common/helpers/theme.dart';
import 'package:fcs/widget/local_text.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_pdfview/flutter_pdfview.dart';

View File

@@ -1,97 +0,0 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/log_model.dart';
import 'package:fcs/widget/local_text.dart';
import 'package:fcs/widget/localization/app_translations.dart';
import 'package:fcs/widget/progress.dart';
import '../fcs/common/helpers/theme.dart';
class LogList extends StatefulWidget {
@override
_LogListState createState() => _LogListState();
}
class _LogListState extends State<LogList> {
final double dotSize = 15.0;
var dateFormatter = new DateFormat('dd MMM yyyy - hh:mm:ss a');
bool _isLoading = false;
@override
Widget build(BuildContext context) {
LogModel logModel = Provider.of<LogModel>(context);
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: AppBar(
backgroundColor: primaryColor,
title: LocalText(
context,
'log.title',
color: Colors.white,
fontSize: 20,
),
),
body: new ListView.separated(
separatorBuilder: (context, index) => Divider(
color: Colors.black,
),
scrollDirection: Axis.vertical,
padding: EdgeInsets.only(left: 15, right: 15, top: 15),
shrinkWrap: true,
itemCount: logModel.logs.length,
itemBuilder: (BuildContext context, int index) {
return Stack(
children: <Widget>[
InkWell(
onTap: () {},
child: Row(
children: <Widget>[
Expanded(
child: new Padding(
padding: const EdgeInsets.symmetric(vertical: 0.0),
child: new Row(
children: <Widget>[
new Padding(
padding: new EdgeInsets.symmetric(
horizontal: 32.0 - dotSize / 2),
child: Icon(
Icons.message,
color: primaryColor,
),
),
new Expanded(
child: new Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
new Text(
logModel.logs[index].deviceName,
style: new TextStyle(
fontSize: 15.0,
color: Colors.black),
),
new Text(
"Last login at : ${logModel.logs[index].activeTime == null ? "" : dateFormatter.format(logModel.logs[index].activeTime)}",
style: new TextStyle(
fontSize: 13.0, color: Colors.grey),
),
],
),
),
],
),
),
),
],
),
),
],
);
}),
),
);
}
}

View File

@@ -1,757 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/language_model.dart';
import 'package:fcs/model/main_model.dart';
import 'package:fcs/model/user_model.dart';
import 'package:fcs/pages/reset_password.dart';
import 'package:fcs/widget/bubble_indication_painter.dart';
import 'package:fcs/widget/localization/app_translations.dart';
import 'package:fcs/widget/progress.dart';
import '../fcs/common/helpers/theme.dart' as Theme;
import 'forget_password.dart';
import 'sms_page.dart';
import '../fcs/common/pages/util.dart';
class LoginPage extends StatefulWidget {
LoginPage({Key key}) : super(key: key);
@override
_LoginPageState createState() => new _LoginPageState();
}
class _LoginPageState extends State<LoginPage>
with SingleTickerProviderStateMixin {
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
final FocusNode myFocusNodeEmailLogin = FocusNode();
final FocusNode myFocusNodePasswordLogin = FocusNode();
final FocusNode myFocusNodePassword = FocusNode();
final FocusNode myFocusNodeEmail = FocusNode();
final FocusNode myFocusNodeName = FocusNode();
TextEditingController loginPhoneController = new TextEditingController();
TextEditingController loginPasswordController = new TextEditingController();
bool _obscureTextLogin = true;
bool _obscureTextSignup = true;
bool _obscureTextSignupConfirm = true;
TextEditingController signupPhoneNumberController =
new TextEditingController();
TextEditingController signupNameController = new TextEditingController();
TextEditingController signupPasswordController = new TextEditingController();
TextEditingController signupConfirmPasswordController =
new TextEditingController();
PageController _pageController;
Color left = Colors.black;
Color right = Colors.white;
final loginFormKey = GlobalKey<FormState>();
final signupFormKey = GlobalKey<FormState>();
bool _isLoading = false;
@override
Widget build(BuildContext context) {
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
key: _scaffoldKey,
appBar: AppBar(
backgroundColor: Colors.white,
iconTheme: IconThemeData(
color: Colors.grey,
),
elevation: 0,
centerTitle: true,
title: new Image(
height: 30,
fit: BoxFit.scaleDown,
image: new AssetImage('assets/img/logo.png')),
),
body: SingleChildScrollView(
child: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height >= 775.0
? MediaQuery.of(context).size.height
: 580.0,
child: Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 50.0),
child: _buildMenuBar(context),
),
Expanded(
flex: 2,
child: PageView(
controller: _pageController,
onPageChanged: (i) {
if (i == 0) {
setState(() {
right = Colors.white;
left = Colors.black;
});
} else if (i == 1) {
setState(() {
right = Colors.black;
left = Colors.white;
});
}
},
children: <Widget>[
new ConstrainedBox(
constraints: const BoxConstraints.expand(),
child: _buildLogin(context),
),
new ConstrainedBox(
constraints: const BoxConstraints.expand(),
child: _buildSignUp(context),
),
],
),
),
],
),
),
),
),
);
}
@override
void dispose() {
myFocusNodePassword.dispose();
myFocusNodeEmail.dispose();
myFocusNodeName.dispose();
_pageController?.dispose();
super.dispose();
}
@override
void initState() {
super.initState();
// SystemChrome.setPreferredOrientations([
// DeviceOrientation.portraitUp,
// DeviceOrientation.portraitDown,
// ]);
_pageController = PageController();
loginPhoneController.text = "09";
signupPhoneNumberController.text = "09";
}
Widget _buildMenuBar(BuildContext context) {
return Container(
width: 300.0,
height: 40.0,
decoration: BoxDecoration(
color: Color(0x552B2B2B),
borderRadius: BorderRadius.all(Radius.circular(25.0)),
),
child: CustomPaint(
painter: TabIndicationPainter(pageController: _pageController),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Expanded(
child: FlatButton(
onPressed: _onSignInButtonPress,
child: Text(
AppTranslations.of(context).text("login.title"),
style: Provider.of<LanguageModel>(context).isEng
? TextStyle(
color: left,
fontSize: 14.0,
fontFamily: "WorkSansSemiBold")
: TextStyle(
color: left,
fontSize: 15.0,
fontFamily: "MyanmarUnicode"),
),
),
),
//Container(height: 33.0, width: 1.0, color: Colors.white),
Expanded(
child: FlatButton(
onPressed: _onSignUpButtonPress,
child: Text(
AppTranslations.of(context).text("sing.title"),
style: Provider.of<LanguageModel>(context).isEng
? TextStyle(
color: right,
fontSize: 14.0,
fontFamily: "WorkSansSemiBold")
: TextStyle(
color: right,
fontSize: 15.0,
fontFamily: "MyanmarUnicode"),
),
),
),
],
),
),
);
}
Widget _buildLogin(BuildContext context) {
return Container(
child: ListView(
children: <Widget>[
Column(
children: <Widget>[
Form(
key: loginFormKey,
child: Card(
elevation: 2.0,
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
child: Container(
width: 300.0,
child: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.only(left: 25.0, right: 25.0),
child: TextFormField(
focusNode: myFocusNodeEmailLogin,
controller: loginPhoneController,
keyboardType: TextInputType.phone,
style: TextStyle(
fontFamily: "WorkSansSemiBold",
fontSize: 16.0,
color: Colors.black),
decoration: InputDecoration(
border: InputBorder.none,
icon: Icon(
FontAwesomeIcons.phone,
color: Colors.black,
size: 22.0,
),
labelText: AppTranslations.of(context)
.text("login.phone"),
labelStyle:
Provider.of<LanguageModel>(context).isEng
? TextStyle(
fontFamily: "WorkSansSemiBold",
color: Colors.grey)
: TextStyle(
fontFamily: "MyanmarUnicode",
color: Colors.grey),
),
validator: _validatePhone,
),
),
Container(
width: 250.0,
height: 1.0,
color: Colors.grey[400],
),
Padding(
padding: EdgeInsets.only(left: 25.0, right: 25.0),
child: TextFormField(
focusNode: myFocusNodePasswordLogin,
controller: loginPasswordController,
obscureText: _obscureTextLogin,
style: TextStyle(
fontFamily: "WorkSansSemiBold",
fontSize: 16.0,
color: Colors.black),
decoration: InputDecoration(
border: InputBorder.none,
icon: Icon(
FontAwesomeIcons.lock,
size: 22.0,
color: Colors.black,
),
labelText: AppTranslations.of(context)
.text("login.password"),
labelStyle:
Provider.of<LanguageModel>(context).isEng
? TextStyle(
fontFamily: "WorkSansSemiBold",
color: Colors.grey)
: TextStyle(
fontFamily: "MyanmarUnicode",
color: Colors.grey),
suffixIcon: GestureDetector(
onTap: _toggleLogin,
child: Icon(
_obscureTextLogin
? FontAwesomeIcons.eye
: FontAwesomeIcons.eyeSlash,
size: 15.0,
color: Colors.black,
),
),
),
validator: _validatePassword,
),
),
],
),
),
),
),
Container(
decoration: new BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(5.0)),
color: Theme.primaryColor,
),
child: MaterialButton(
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 10.0, horizontal: 42.0),
child: Text(
AppTranslations.of(context).text("login.btn"),
style: Provider.of<LanguageModel>(context).isEng
? TextStyle(
color: Colors.white,
fontSize: 18.0,
fontWeight: FontWeight.bold,
fontFamily: "WorkSansBold")
: TextStyle(
color: Colors.white,
fontSize: 16.0,
fontWeight: FontWeight.bold,
fontFamily: "MyanmarUnicode"),
),
),
onPressed: () => _login(context)),
),
],
),
Padding(
padding: EdgeInsets.only(top: 10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
decoration: BoxDecoration(
gradient: new LinearGradient(
colors: [
Colors.white10,
Colors.white,
],
begin: const FractionalOffset(0.0, 0.0),
end: const FractionalOffset(1.0, 1.0),
stops: [0.0, 1.0],
tileMode: TileMode.clamp),
),
width: 100.0,
height: 1.0,
),
Container(
decoration: BoxDecoration(
gradient: new LinearGradient(
colors: [
Colors.white,
Colors.white10,
],
begin: const FractionalOffset(0.0, 0.0),
end: const FractionalOffset(1.0, 1.0),
stops: [0.0, 1.0],
tileMode: TileMode.clamp),
),
width: 100.0,
height: 1.0,
),
],
),
),
Padding(
padding: EdgeInsets.only(top: 10.0),
child: FlatButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ForgetPassword(
phoneNumber: loginPhoneController.text,
)));
},
child: Text(
AppTranslations.of(context).text("login.forgot_password"),
style: Provider.of<LanguageModel>(context).isEng
? TextStyle(
decoration: TextDecoration.underline,
color: Colors.black,
fontSize: 16.0,
fontFamily: "WorkSansMedium")
: TextStyle(
decoration: TextDecoration.underline,
color: Colors.black,
fontSize: 16.0,
fontFamily: "MyanmarUnicode"),
)),
),
],
),
);
}
Widget _buildSignUp(BuildContext context) {
return Container(
child: ListView(
children: <Widget>[
Column(
children: <Widget>[
Form(
key: signupFormKey,
child: Card(
elevation: 2.0,
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
child: Container(
width: 300.0,
child: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.only(left: 25.0, right: 25.0),
child: TextFormField(
focusNode: myFocusNodeName,
controller: signupNameController,
keyboardType: TextInputType.text,
textCapitalization: TextCapitalization.words,
style: TextStyle(
fontFamily: "WorkSansSemiBold",
fontSize: 16.0,
color: Colors.black),
decoration: InputDecoration(
border: InputBorder.none,
icon: Icon(
FontAwesomeIcons.user,
color: Colors.black,
),
labelText: AppTranslations.of(context)
.text("login.name"),
labelStyle:
Provider.of<LanguageModel>(context).isEng
? TextStyle(
fontFamily: "WorkSansSemiBold",
color: Colors.grey)
: TextStyle(
fontFamily: "MyanmarUnicode",
color: Colors.grey),
hintStyle: TextStyle(
fontFamily: "WorkSansSemiBold",
fontSize: 16.0),
),
validator: (value) {
if (value.isEmpty) {
return AppTranslations.of(context)
.text("login.name_empty");
}
return null;
}),
),
Container(
width: 250.0,
height: 1.0,
color: Colors.grey[400],
),
Padding(
padding: EdgeInsets.only(left: 25.0, right: 25.0),
child: TextFormField(
focusNode: myFocusNodeEmail,
controller: signupPhoneNumberController,
keyboardType: TextInputType.phone,
style: TextStyle(
fontFamily: "WorkSansSemiBold",
fontSize: 16.0,
color: Colors.black),
decoration: InputDecoration(
border: InputBorder.none,
icon: Icon(
FontAwesomeIcons.phone,
color: Colors.black,
),
labelText: AppTranslations.of(context)
.text("login.phone"),
labelStyle:
Provider.of<LanguageModel>(context).isEng
? TextStyle(
fontFamily: "WorkSansSemiBold",
color: Colors.grey)
: TextStyle(
fontFamily: "MyanmarUnicode",
color: Colors.grey),
),
validator: _validatePhone),
),
Container(
width: 250.0,
height: 1.0,
color: Colors.grey[400],
),
Padding(
padding: EdgeInsets.only(left: 25.0, right: 25.0),
child: TextFormField(
focusNode: myFocusNodePassword,
controller: signupPasswordController,
obscureText: _obscureTextSignup,
style: TextStyle(
fontFamily: "WorkSansSemiBold",
fontSize: 16.0,
color: Colors.black),
decoration: InputDecoration(
border: InputBorder.none,
icon: Icon(
FontAwesomeIcons.lock,
color: Colors.black,
),
labelText: AppTranslations.of(context)
.text("login.password"),
labelStyle:
Provider.of<LanguageModel>(context).isEng
? TextStyle(
fontFamily: "WorkSansSemiBold",
color: Colors.grey)
: TextStyle(
fontFamily: "MyanmarUnicode",
color: Colors.grey),
suffixIcon: GestureDetector(
onTap: _toggleSignup,
child: Icon(
_obscureTextSignup
? FontAwesomeIcons.eye
: FontAwesomeIcons.eyeSlash,
size: 15.0,
color: Colors.black,
),
),
),
validator: _validatePassword,
),
),
Container(
width: 250.0,
height: 1.0,
color: Colors.grey[400],
),
Padding(
padding: EdgeInsets.only(left: 25.0, right: 25.0),
child: TextFormField(
controller: signupConfirmPasswordController,
obscureText: _obscureTextSignupConfirm,
style: TextStyle(
fontFamily: "WorkSansSemiBold",
fontSize: 16.0,
color: Colors.black),
decoration: InputDecoration(
border: InputBorder.none,
icon: Icon(
FontAwesomeIcons.lock,
color: Colors.black,
),
labelText: AppTranslations.of(context)
.text("login.confirm_password"),
labelStyle:
Provider.of<LanguageModel>(context).isEng
? TextStyle(
fontFamily: "WorkSansSemiBold",
color: Colors.grey)
: TextStyle(
fontFamily: "MyanmarUnicode",
color: Colors.grey),
suffixIcon: GestureDetector(
onTap: _toggleSignupConfirm,
child: Icon(
_obscureTextSignupConfirm
? FontAwesomeIcons.eye
: FontAwesomeIcons.eyeSlash,
size: 15.0,
color: Colors.black,
),
),
),
validator: _validatePassword,
),
),
],
),
),
),
),
Container(
// margin: EdgeInsets.only(top: 320.0),
decoration: new BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(5.0)),
color: Theme.primaryColor,
),
child: MaterialButton(
highlightColor: Colors.transparent,
splashColor: Theme.LoginColors.loginGradientEnd,
//shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5.0))),
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 10.0, horizontal: 42.0),
child: Text(
AppTranslations.of(context).text("sing.up"),
style: Provider.of<LanguageModel>(context).isEng
? TextStyle(
color: Colors.white,
fontSize: 18.0,
fontFamily: "WorkSansBold")
: TextStyle(
color: Colors.white,
fontSize: 16.0,
fontFamily: "MyanmarUnicode"),
),
),
onPressed: () => _signup(context)),
),
],
),
],
),
);
}
void _onSignInButtonPress() {
_pageController.animateToPage(0,
duration: Duration(milliseconds: 500), curve: Curves.decelerate);
}
void _onSignUpButtonPress() {
_pageController?.animateToPage(1,
duration: Duration(milliseconds: 500), curve: Curves.decelerate);
}
void _toggleLogin() {
setState(() {
_obscureTextLogin = !_obscureTextLogin;
});
}
void _toggleSignup() {
setState(() {
_obscureTextSignup = !_obscureTextSignup;
});
}
void _toggleSignupConfirm() {
setState(() {
_obscureTextSignupConfirm = !_obscureTextSignupConfirm;
});
}
void _signup(BuildContext context) async {
if (!signupFormKey.currentState.validate()) {
return;
}
setState(() {
_isLoading = true;
});
MainModel authModel = Provider.of<MainModel>(context);
var name = signupNameController.text;
var password = signupPasswordController.text;
var confirmPassword = signupConfirmPasswordController.text;
var phoneNumber = signupPhoneNumberController.text;
try {
await authModel.signup(name, password, confirmPassword, phoneNumber);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
SmsCodePage(id: phoneNumber, password: password),
),
);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
Future.delayed(Duration(seconds: 1), () {
if (mounted) {
setState(() {
_isLoading = false;
});
}
});
}
}
void _login(BuildContext context) async {
if (!loginFormKey.currentState.validate()) {
return;
}
setState(() {
_isLoading = true;
});
MainModel mainModel = Provider.of<MainModel>(context);
var phoneNumber = loginPhoneController.text;
var password = loginPasswordController.text;
try {
await mainModel.login(phoneNumber, password);
Navigator.pushNamedAndRemoveUntil(context, "/", (r) => false);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
}
Future.delayed(Duration(seconds: 1), () {
if (mounted) {
setState(() {
_isLoading = false;
});
}
});
}
String _validatePassword(value) {
if (value.isEmpty) {
return AppTranslations.of(context).text("login.password_empty");
}
if (value.length < 6) {
return AppTranslations.of(context).text("login.password_size");
}
return null;
}
String _validatePhone(value) {
if (value.isEmpty) {
return AppTranslations.of(context).text("login.phone_empty");
}
if (!value.startsWith("09") && !value.startsWith("959")) {
return 'Only "09 or 959".';
}
return null;
}
Future<void> _forgetPassword() async {
var phoneNumber = loginPhoneController.text;
if (phoneNumber.isEmpty) {
showMsgDialog(context, "Error", "Please input phone number");
return;
}
setState(() {
_isLoading = true;
});
try {
UserModel userModel = Provider.of<UserModel>(context);
await userModel.forgetPassword(phoneNumber);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ResetPasswordPage(phoneNumber)));
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
}

View File

@@ -0,0 +1,404 @@
import 'dart:async';
import 'dart:io';
import 'package:fcs/data/services/services.dart';
import 'package:fcs/domain/entities/user.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/localization/transalation.dart';
import 'package:fcs/pages/box/box_list.dart';
import 'package:fcs/pages/chat/message_detail.dart';
import 'package:fcs/pages/chat/model/message_model.dart';
import 'package:fcs/pages/customer/customer_list.dart';
import 'package:fcs/pages/customer/model/customer_model.dart';
import 'package:fcs/pages/delivery/delivery_list.dart';
import 'package:fcs/pages/discount/discount_list.dart';
import 'package:fcs/pages/faq/faq_list_page.dart';
import 'package:fcs/pages/fcs_shipment/fcs_shipment_list.dart';
import 'package:fcs/pages/invoice/invoce_list.dart';
import 'package:fcs/pages/main/model/language_model.dart';
import 'package:fcs/pages/main/model/main_model.dart';
import 'package:fcs/pages/package/package_list.dart';
import 'package:fcs/pages/rates/shipment_rates.dart';
import 'package:fcs/pages/shipment/pickup_list.dart';
import 'package:fcs/pages/staff/staff_list.dart';
import 'package:fcs/pages/widgets/task_button.dart';
import 'package:fcs/pages/widgets/badge.dart';
import 'package:fcs/pages/widgets/bottom_up_page_route.dart';
import 'package:fcs/pages/widgets/bottom_widgets.dart';
import 'package:fcs/pages/widgets/right_left_page_rout.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_icons/flutter_icons.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:intl/intl.dart';
import 'package:logging/logging.dart';
import 'package:provider/provider.dart';
import '../profile/profile_page.dart';
import '../signin/signin_page.dart';
import '../widgets/banner.dart';
import '../widgets/offline_redirect.dart';
final msgLog = Logger('backgroundMessageHandler');
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final log = Logger('_HomePageState');
bool login = false;
bool customer = true;
List<bool> isSelected = [true, false];
static FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
@override
void initState() {
super.initState();
MainModel mainModel = Provider.of<MainModel>(context, listen: false);
Services.instance.messagingService.init(
(message) {
print("Message from FCM:$message");
_showNotification(message);
},
onLaunch: (m) => _showNotiContent(m),
onResume: (m) => _showNotiContent(m),
onSetupComplete: (token) {
mainModel.setMessaginToken = token;
});
_initLocalNotifications();
}
String notiUserID, notiUserName;
_showNotiContent(Map<String, dynamic> message) {
try {
Map<String, dynamic> map = Map<String, dynamic>.from(message["data"]);
notiUserID = map['user_id'];
notiUserName = map['user_name'];
_startNotiTimer();
print("Notification:$map");
} catch (e) {
print("Error:$e");
}
}
_startNotiTimer() async {
var _duration = new Duration(milliseconds: 500);
new Timer.periodic(_duration, (t) => displayNoti(t));
}
void displayNoti(Timer timer) async {
MainModel mainModel = Provider.of<MainModel>(context, listen: false);
if (mainModel.isLogin()) {
timer.cancel();
bool isCustomer = mainModel.isCustomer();
String receiverID = isCustomer ? mainModel.user.id : notiUserID;
String receiverName = isCustomer ? mainModel.user.name : notiUserName;
MessageModel messageModel =
Provider.of<MessageModel>(context, listen: false);
messageModel.initQuery(receiverID);
User user = mainModel.user;
if (!isCustomer) {
CustomerModel customerModel =
Provider.of<CustomerModel>(context, listen: false);
user = await customerModel.getUser(receiverID);
}
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MessageDetail(
messageModel: messageModel,
receiverID: receiverID,
receiverName: receiverName,
))).then((value) {
if (user.userUnseenCount > 0) {
messageModel.seenMessages(user.id, true);
}
});
if (user.userUnseenCount > 0) {
messageModel.seenMessages(user.id, true);
}
}
}
_initLocalNotifications() {
var initializationSettingsAndroid =
new AndroidInitializationSettings('@mipmap/ic_launcher');
var initializationSettingsIOS = new IOSInitializationSettings();
var initializationSettings = new InitializationSettings(
initializationSettingsAndroid, initializationSettingsIOS);
_flutterLocalNotificationsPlugin.initialize(initializationSettings);
}
static Future _showNotification(Map<String, dynamic> message) async {
var pushTitle;
var pushText;
var action;
if (Platform.isAndroid) {
var nodeData = message['notification'];
pushTitle = nodeData['title'];
pushText = nodeData['body'];
action = nodeData['action'];
} else {
pushTitle = message['title'];
pushText = message['body'];
action = message['action'];
}
print("AppPushs params pushTitle : $pushTitle");
print("AppPushs params pushText : $pushText");
print("AppPushs params pushAction : $action");
// @formatter:off
var platformChannelSpecificsAndroid = new AndroidNotificationDetails(
'your channel id', 'your channel name', 'your channel description',
playSound: true,
enableVibration: true,
importance: Importance.Max,
priority: Priority.High);
// @formatter:on
var platformChannelSpecificsIos =
new IOSNotificationDetails(presentSound: true);
var platformChannelSpecifics = new NotificationDetails(
platformChannelSpecificsAndroid, platformChannelSpecificsIos);
new Future.delayed(Duration.zero, () {
_flutterLocalNotificationsPlugin.show(
0,
pushTitle,
pushText,
platformChannelSpecifics,
payload: 'No_Sound',
);
});
}
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
User user = Provider.of<MainModel>(context).user;
if (user == null) {
return Container();
}
customer = Provider.of<MainModel>(context).isCustomer();
login = Provider.of<MainModel>(context).isLogin();
LanguageModel languageModel = Provider.of<LanguageModel>(context);
final faqBtn = TaskButton("faq.btn",
icon: MaterialCommunityIcons.frequently_asked_questions,
btnCallback: () => Navigator.of(context).push(CupertinoPageRoute(
builder: (context) => FAQListPage(),
)));
final packagesBtn = TaskButton("package.btn.name",
icon: Octicons.package,
btnCallback: () => Navigator.of(context).push<void>(
CupertinoPageRoute(builder: (context) => PackageList())));
final boxesBtn = TaskButton("boxes.name",
icon: MaterialCommunityIcons.package,
btnCallback: () =>
Navigator.of(context).push(BottomUpPageRoute(BoxList())));
final pickUpBtn = TaskButton("pickup",
icon: SimpleLineIcons.direction,
btnCallback: () =>
Navigator.of(context).push(BottomUpPageRoute(PickUpList())));
final shipmentCostBtn = TaskButton("rate",
icon: FontAwesomeIcons.calculator,
btnCallback: () =>
Navigator.of(context).push(BottomUpPageRoute(ShipmentRates())));
final fcsShipmentBtn = TaskButton("shipment.title",
icon: Ionicons.ios_airplane,
btnCallback: () =>
Navigator.of(context).push(BottomUpPageRoute(FcsShipmentList())));
final notiBtnOrg =
TaskButton("message.btn", icon: Icons.message, btnCallback: () {
MessageModel messageModel =
Provider.of<MessageModel>(context, listen: false);
messageModel.initQuery(user.id);
Navigator.push(
context,
BottomUpPageRoute(MessageDetail(
messageModel: messageModel,
)),
).then((value) {
if (user.userUnseenCount > 0) {
messageModel.seenMessages(user.id, true);
}
});
if (user.userUnseenCount > 0) {
messageModel.seenMessages(user.id, true);
}
});
final notiBtn = badgeCounter(notiBtnOrg, user.userUnseenCount);
final staffBtn = TaskButton(
"staff.title",
icon: MaterialCommunityIcons.worker,
btnCallback: () => Navigator.of(context).push<void>(CupertinoPageRoute(
builder: (context) => StaffList(),
)),
);
final customersBtn = TaskButton("customers.btn",
icon: Feather.users,
btnCallback: () => Navigator.of(context).push<void>(CupertinoPageRoute(
builder: (context) => CustomerList(),
)));
final invoicesBtn = TaskButton("invoices.btn",
icon: FontAwesomeIcons.fileInvoice,
btnCallback: () =>
Navigator.of(context).push(BottomUpPageRoute(InvoiceList())));
final discountBtn = TaskButton("discount.btn",
icon: Entypo.price_ribbon,
btnCallback: () =>
Navigator.of(context).push(BottomUpPageRoute(DiscountList())));
final deliveryBtn = TaskButton("delivery.title",
icon: MaterialCommunityIcons.truck_fast,
btnCallback: () =>
Navigator.of(context).push(BottomUpPageRoute(DeliverList())));
List<Widget> widgets = [];
widgets.add(faqBtn);
if (user != null) {
true ? widgets.add(pickUpBtn) : "";
!customer ? widgets.add(fcsShipmentBtn) : "";
customer ? widgets.add(notiBtn) : "";
user.hasStaffs() ? widgets.add(staffBtn) : "";
widgets.add(shipmentCostBtn);
user.hasPackages() ? widgets.add(packagesBtn) : "";
true ? widgets.add(boxesBtn) : "";
true ? widgets.add(deliveryBtn) : "";
user.hasCustomers() ? widgets.add(customersBtn) : "";
true ? widgets.add(invoicesBtn) : "";
true ? widgets.add(discountBtn) : "";
}
return OfflineRedirect(
child: FlavorBanner(
child: Scaffold(
appBar: AppBar(
elevation: 0,
backgroundColor: primaryColor,
title: ClipRRect(
child: Image.asset("assets/logo.jpg", height: 40),
borderRadius: new BorderRadius.circular(30.0),
),
actions: login
? <Widget>[
ToggleButtons(
children: <Widget>[
Image.asset(
'icons/flags/png/us.png',
package: 'country_icons',
fit: BoxFit.fitWidth,
width: 25,
),
Image.asset(
'icons/flags/png/mm.png',
package: 'country_icons',
fit: BoxFit.fitWidth,
width: 25,
)
],
onPressed: _langChange,
isSelected: languageModel.currentState,
selectedBorderColor: Colors.white24,
),
IconButton(
onPressed: () {
Navigator.of(context)
.push(RightLeftPageRoute(Profile()));
},
iconSize: 30,
icon: Icon(Icons.account_circle),
),
]
: <Widget>[
ToggleButtons(
children: <Widget>[
Image.asset(
'icons/flags/png/us.png',
package: 'country_icons',
fit: BoxFit.fitWidth,
width: 25,
),
Image.asset(
'icons/flags/png/mm.png',
package: 'country_icons',
fit: BoxFit.fitWidth,
width: 25,
)
],
onPressed: _langChange,
isSelected: languageModel.currentState,
),
FlatButton(
onPressed: () {
Navigator.of(context)
.push(BottomUpPageRoute(SigninPage()));
},
child: Text(
"Sign In",
style: siginButtonStyle,
),
),
]),
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color(0xd0272262),
Color(0xfa272262),
],
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Expanded(
child: ListView(children: [
Wrap(
alignment: WrapAlignment.center,
children: widgets,
),
]),
),
BottomWidgets(),
],
))),
),
);
}
_langChange(index) {
var languageModel = Provider.of<LanguageModel>(context, listen: false);
languageModel.saveLanguage(Translation().supportedLanguages[index]);
setState(() {
isSelected.asMap().forEach((i, e) {
isSelected[i] = false;
});
isSelected[index] = !isSelected[index];
});
}
// Widget _buildBtn(String title, {IconData icon, BtnCallback btnCallback}) {
// return TaskButton(titleKey: title, icon: icon, btnCallback: btnCallback);
// }
}

View File

@@ -0,0 +1,202 @@
import 'package:fcs/helpers/shared_pref.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/localization/transalation.dart';
import 'package:fcs/pages/main/model/language_model.dart';
import 'package:fcs/pages/main/model/main_model.dart';
import 'package:fcs/pages/signin/signin_page.dart';
import 'package:fcs/pages/widgets/local_text.dart';
import 'package:fcs/pages/widgets/progress.dart';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:provider/provider.dart';
class InitialLanguageSelectionPage extends StatefulWidget {
@override
_InitialLanguageSelectionPageState createState() =>
_InitialLanguageSelectionPageState();
}
class _InitialLanguageSelectionPageState
extends State<InitialLanguageSelectionPage> {
static final List<String> languagesList = Translation().supportedLanguages;
static final List<String> languageCodesList =
Translation().supportedLanguagesCodes;
final Map<dynamic, dynamic> languagesMap = {
languagesList[0]: languageCodesList[0],
languagesList[1]: languageCodesList[1],
};
String selectedLanguage;
int selectedIndex;
bool _isLoading;
@override
void initState() {
super.initState();
_isLoading = false;
var languageModel = Provider.of<LanguageModel>(context, listen: false);
this.selectedIndex = languageModel.isEng ? 0 : 1;
loadLaunguage(languageModel);
}
loadLaunguage(LanguageModel languageModel) async {
var lan = await languageModel.load();
if (this.selectedLanguage != lan) {
setState(() {
this.selectedLanguage = lan;
});
}
}
@override
Widget build(BuildContext context) {
return LocalProgress(
inAsyncCall: _isLoading,
child: Material(
type: MaterialType.transparency,
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Color(0xff272282), primaryColor],
begin: const FractionalOffset(0.8, 0.9),
end: const FractionalOffset(0.9, 0.0),
stops: [0.0, 1.0],
),
),
child: Align(
alignment: Alignment.center,
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
children: [
Container(
height: 40,
child: LocalText(context, "language.selection.title",
fontSize: 20,
fontWeight: FontWeight.w200,
color: Colors.white),
),
Container(
padding: EdgeInsets.only(top: 0),
child: Card(
color: Color(0xfff4edec),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(20)),
),
child: Container(
padding: EdgeInsets.only(top: 20),
width: 300,
height: 160,
child: Column(
children: languagesList.asMap().entries.map((e) {
var language = e.value;
var key = e.key;
return InkWell(
onTap: () {
_select(key, language);
},
child: Container(
padding: EdgeInsets.all(2),
decoration: key == languagesList.length - 1
? BoxDecoration()
: BoxDecoration(
border: Border(
bottom:
BorderSide(color: Colors.grey[300]),
),
),
child: ListTile(
leading: language == 'English'
? Container(
child: CircleAvatar(
radius: 20,
backgroundImage: AssetImage(
"icons/flags/png/gb.png",
package: 'country_icons',
),
),
)
: Container(
child: CircleAvatar(
radius: 20,
backgroundImage: AssetImage(
"icons/flags/png/mm.png",
package: 'country_icons',
),
),
),
title: Text("$language"),
trailing: Theme(
data: Theme.of(context).copyWith(
unselectedWidgetColor: Colors.grey[400],
),
child: Radio(
value: key,
groupValue: selectedIndex,
onChanged: (int i) =>
_select(key, language),
activeColor: primaryColor,
),
)),
),
);
}).toList()),
),
),
),
SizedBox(height: 20.0),
Container(
padding: EdgeInsets.only(left: 230, top: 20),
child: Container(
width: 50,
height: 50,
child: InkWell(
onTap: () {
_next();
},
child: CircleAvatar(
radius: 25,
backgroundColor: Colors.white,
child: Center(
child: Icon(FontAwesomeIcons.arrowRight,
color: Colors.black87)),
),
),
),
)
],
),
),
),
),
),
);
}
_select(int index, String lang) {
setState(() {
selectedIndex = index;
selectedLanguage = lang;
Translation().onLocaleChanged(Locale(languagesMap[lang]));
Provider.of<LanguageModel>(context, listen: false)
.saveLanguage(selectedLanguage);
});
}
_next() {
setState(() {
_isLoading = true;
});
try {
SharedPref.finishFirstLaunch();
bool isLogin = Provider.of<MainModel>(context, listen: false).isLogin();
String page = isLogin ? "/home" : "/welcome";
Navigator.of(context).pushReplacementNamed(page);
} catch (e) {} finally {
setState(() {
_isLoading = false;
});
}
}
}

View File

@@ -0,0 +1,37 @@
import 'package:fcs/domain/entities/setting.dart';
import 'package:fcs/domain/entities/user.dart';
import 'package:flutter/foundation.dart';
import 'package:fcs/helpers/api_helper.dart';
import 'main_model.dart';
abstract class BaseModel extends ChangeNotifier {
User user;
Setting setting;
MainModel mainModel;
void initUser(User user) async {
this.user = user;
}
void privilegeChanged() {}
void initSetting(Setting setting) async {
this.setting = setting;
}
void logout() {}
// request makes http request
// if token is null
dynamic request(
String path,
method, {
dynamic payload,
String token,
String url,
}) async {
return await requestAPI(path, method,
payload: payload, token: token, url: url);
}
}

View File

@@ -0,0 +1,45 @@
import 'package:fcs/localization/transalation.dart';
import 'package:fcs/helpers/shared_pref.dart';
import 'package:flutter/painting.dart';
import 'base_model.dart';
class LanguageModel extends BaseModel {
String language;
bool get isEng => this.language == "English";
List<bool> get currentState => isEng ? [true, false] : [false, true];
static final List<String> languageCodesList =
Translation().supportedLanguagesCodes;
static final List<String> languagesList = Translation().supportedLanguages;
final Map<dynamic, dynamic> languagesMap = {
languagesList[0]: languageCodesList[0],
languagesList[1]: languageCodesList[1],
};
LanguageModel() {
load();
}
@override
logout() async {}
Future<String> load() async {
var data = await SharedPref.getLang();
String result = languagesList[1]; // defalut to english
if (data != null) {
result = data;
}
this.language = result;
Translation().onLocaleChanged(Locale(languagesMap[this.language]));
notifyListeners();
return result;
}
void saveLanguage(String language) async {
Translation().onLocaleChanged(Locale(languagesMap[language]));
SharedPref.saveLang(language);
this.language = language;
notifyListeners();
}
}

View File

@@ -0,0 +1,169 @@
import 'dart:async';
import 'package:fcs/data/services/services.dart';
import 'package:fcs/domain/entities/auth_result.dart';
import 'package:fcs/domain/entities/setting.dart';
import 'package:fcs/domain/entities/user.dart';
import 'package:fcs/helpers/network_connectivity.dart';
import 'package:fcs/helpers/shared_pref.dart';
import 'package:fcs/pages/main/model/base_model.dart';
import 'package:flutter/foundation.dart';
import 'package:logging/logging.dart';
import 'package:package_info/package_info.dart';
class MainModel extends ChangeNotifier {
final log = Logger('MainModel');
List<BaseModel> models = [];
String messagingToken;
User user;
PackageInfo packageInfo;
set setMessaginToken(token) {
this.messagingToken = token;
uploadMsgToken();
}
Setting setting;
bool isLoaded = false;
bool isOnline = false;
bool isFirstLaunch = false;
MainModel() {
NetworkConnectivity.instance.statusStream.listen((data) {
bool _isOnline = data["isOnline"];
if (_isOnline && !this.isOnline) {
_init();
}
this.isOnline = _isOnline;
notifyListeners();
});
}
bool faqEditable() {
return this.user != null && this.user.hasSupport();
}
bool paymentMethodsEditable() {
return this.user != null && this.user.hasSupport();
}
bool termEditable() {
return this.user != null && this.user.hasSupport();
}
bool contactEditable() {
return this.user != null && this.user.hasSupport();
}
bool isLogin() {
return this.user != null;
}
bool isCustomer() {
return user != null && user.isCustomer();
}
bool isSysAdmin() {
return this.user != null && this.user.hasSysAdmin();
}
bool isAdmin() {
return this.user != null && this.user.hasAdmin();
}
// userListener should never be closed
StreamSubscription<User> userListener;
_init() async {
await _listenSetting();
this.isFirstLaunch = await SharedPref.isFirstLaunch();
this.isFirstLaunch = this.isFirstLaunch ?? true;
this.packageInfo = await PackageInfo.fromPlatform();
if (userListener != null) userListener.cancel();
userListener =
Services.instance.authService.getUserStream().listen((_user) {
if (_user != null) {
models.forEach((m) => m.initUser(_user));
// call diffPrivileges if privilege changed or first time login
if (this.user == null || _user.diffPrivileges(this.user)) {
models.forEach((m) => m.privilegeChanged());
}
if (this.user == null) {
uploadMsgToken();
}
} else {
if (this.user != null) {
models.forEach((m) => m.logout());
}
}
this.user = _user;
isLoaded = true;
notifyListeners();
});
}
void addModel(BaseModel model) {
models.add(model);
}
Future<void> _listenSetting() async {
try {
Services.instance.authService.getSetting().listen((event) {
this.setting = event;
models.forEach((m) => m.initSetting(setting));
notifyListeners();
});
} finally {}
}
bool isSupport() {
if (packageInfo == null || setting == null) return false;
return int.parse(packageInfo.buildNumber) >= setting.supportBuildNum;
}
Future<AuthResult> sendSms(String phoneNumber) {
return Services.instance.authService.sendSmsCodeToPhoneNumber(phoneNumber);
}
Future<AuthResult> signin(String smsCode) async {
AuthResult authResult =
await Services.instance.authService.signInWithSmsCode(smsCode);
return authResult;
}
Future<void> uploadMsgToken() {
if (messagingToken == null || user == null) return null;
return Services.instance.userService.uploadMsgToken(messagingToken);
}
Future<void> removeMsgToken() {
if (messagingToken == null || user == null) return null;
return Services.instance.userService.removeMsgToken(messagingToken);
}
Future<void> signout() async {
await removeMsgToken();
await Services.instance.authService.signout();
models.forEach((m) => m.logout());
}
Future<bool> hasInvite() async {
return Services.instance.authService.hasInvite();
}
Future<void> signup(String userName) async {
await Services.instance.authService.signup(userName);
}
Future<void> joinInvite(String userName) async {
await Services.instance.authService.joinInvite(userName);
notifyListeners();
}
Future<void> updateProfile(String newUserName) async {
await Services.instance.authService.updateProfile(newUserName);
notifyListeners();
}
}

View File

@@ -0,0 +1,101 @@
import 'dart:async';
import 'package:fcs/pages/main/model/main_model.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/pages/widgets/local_text.dart';
import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
import 'package:provider/provider.dart';
class SplashScreen extends StatefulWidget {
@override
_SplashScreenState createState() => new _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
final log = Logger('_SplashScreenState');
String page = "/language_selection";
bool _loaded = false;
bool _isSupport = false;
bool _isOnline = true;
Timer timer;
startTime() async {
var _duration = new Duration(milliseconds: 3000);
this.timer = new Timer.periodic(_duration, navigationPage);
}
void navigationPage(Timer timer) async {
if (_loaded && _isOnline && _isSupport) {
timer.cancel();
Navigator.of(context).pushReplacementNamed(page);
}
}
@override
void initState() {
super.initState();
startTime();
}
void dispose() {
super.dispose();
if (timer.isActive) timer.cancel();
}
@override
Widget build(BuildContext context) {
MainModel mainModel = Provider.of<MainModel>(context);
this._isSupport = mainModel.isSupport();
this._isOnline = mainModel.isOnline;
if (mainModel.isLoaded) {
if (mainModel.isFirstLaunch) {
page = "/language_selection";
} else if (mainModel.isLogin()) {
page = "/home";
} else {
page = "/welcome";
}
this._loaded = mainModel.isLoaded;
}
return new Scaffold(
backgroundColor: Colors.white,
body: new Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Image.asset(
"assets/logo.jpg",
width: 180,
),
SizedBox(height: 50),
Column(
children: <Widget>[
Text(
"FCS Logistics",
style: welcomeSubLabelStyle,
),
],
),
SizedBox(height: 30),
_loaded && !_isOnline
? Column(
children: <Widget>[
LocalText(context, "offline.status"),
],
)
: Container(),
Text(
_loaded && !_isSupport
? "Version outdated, please update your app!"
: "",
style: TextStyle(
color: primaryColor, fontWeight: FontWeight.bold)),
],
),
),
);
}
}

452
lib/pages/main/util.dart Normal file
View File

@@ -0,0 +1,452 @@
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/localization/app_translations.dart';
import 'package:fcs/pages/main/model/language_model.dart';
import 'package:fcs/pages/widgets/local_text.dart';
import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
import '../widgets/label_widgets.dart';
final log = Logger('Util');
Future showMsgDialog(BuildContext context, String title, String msg) {
return showDialog(
context: context,
builder: (_) {
return AlertDialog(
title: new Text(title),
content: new Text(msg),
actions: <Widget>[
new FlatButton(
child: new Text("Close"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
Future<void> showConfirmDialog(
BuildContext context, String translationKey, ok(),
{List<String> translationVariables}) async {
await showDialog(
context: context,
builder: (_) {
return AlertDialog(
title: Center(
child: LocalText(
context,
translationKey,
translationVariables: translationVariables,
color: primaryColor,
),
),
content: Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
FlatButton(
color: Colors.grey[300],
child: Text(
AppTranslations.of(context).text('Cancel'),
style: Provider.of<LanguageModel>(context).isEng
? TextStyle()
: TextStyle(fontFamily: 'Myanmar3'),
),
onPressed: () {
Navigator.of(context).pop();
}),
SizedBox(
width: 0,
),
FlatButton(
color: primaryColor,
child: Text(AppTranslations.of(context).text('Ok'),
style: Provider.of<LanguageModel>(context).isEng
? TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold)
: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontFamily: 'Myanmar3')),
onPressed: () async {
Navigator.of(context).pop();
await ok();
})
],
),
),
);
});
}
void showCommentDialog(BuildContext context, commentCallback(comment)) {
TextEditingController _comment = new TextEditingController();
showDialog(
context: context,
builder: (_) {
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(32.0))),
content: Container(
width: 300.0,
height: 80.0,
child: Container(
child: TextFormField(
controller: _comment,
autofocus: false,
cursorColor: primaryColor,
maxLines: 3,
style: textStyle,
decoration: new InputDecoration(
labelText: "Comment",
labelStyle: labelStyle,
icon: Icon(
Icons.add_comment,
color: primaryColor,
),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: primaryColor, width: 1.0)),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: primaryColor, width: 1.0)),
),
validator: (value) {
if (value.isEmpty) {
return "Please enter comment";
}
return null;
},
),
),
),
actions: <Widget>[
FlatButton(
child: Text(
"Cancel",
style: labelStyle,
),
onPressed: () {
_comment.clear();
Navigator.of(context).pop();
}),
FlatButton(
color: primaryColor,
child: Text("Submit",
style: TextStyle(
color: Colors.white, fontWeight: FontWeight.bold)),
onPressed: () {
commentCallback(_comment.text);
Navigator.of(context).pop();
})
],
);
});
}
Widget getStatus(String status) {
return status == "Delivered"
? Text(status,
style: TextStyle(
color: primaryColor, fontSize: 18, fontWeight: FontWeight.bold))
: status == "rejected"
? Chip(
backgroundColor: Colors.red,
avatar: Icon(
Icons.remove,
color: Colors.white,
size: 14,
),
label: Text(
status,
style: TextStyle(color: Colors.white, fontSize: 12),
))
: status == "In progress"
? Text(
status,
style: TextStyle(color: Colors.white, fontSize: 12),
)
: status == "Pickuped"
? Text(
status,
style: TextStyle(
color: primaryColor,
fontSize: 18,
fontWeight: FontWeight.bold),
)
: status == "Pending" || status == "Rescheduled"
? Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Icon(Icons.schedule),
),
Text(
status,
style: TextStyle(
color: primaryColor,
fontSize: 18,
fontWeight: FontWeight.bold),
)
],
)
: status == "Assigned"
? Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Icon(Icons.check),
),
Text(
status,
style: TextStyle(
color: primaryColor,
fontSize: 18,
fontWeight: FontWeight.bold),
)
],
)
: status == "Canceled"
? Text(
status,
style: TextStyle(
color: primaryColor,
fontSize: 18,
fontWeight: FontWeight.bold),
)
: status == "Delivered" || status == "Avaliable"
? Text(
status,
style: TextStyle(
color: Colors.green, fontSize: 18),
)
: status == "Used"
? Text(
status,
style: TextStyle(
color: Colors.red, fontSize: 18),
)
: status == "Paid"
? Row(
children: <Widget>[
Padding(
padding:
const EdgeInsets.all(8.0),
child: Icon(Icons.check),
),
Text(
status,
style: TextStyle(
color: primaryColor,
fontSize: 18,
fontWeight:
FontWeight.bold),
)
],
)
: Text(
status,
style: TextStyle(
color: primaryColor,
fontSize: 18,
fontWeight: FontWeight.bold),
);
}
call(BuildContext context, String phone) {
showConfirmDialog(context, "contact.phone.confim", () => launch("tel:$phone"),
translationVariables: ["$phone"]);
}
Widget nameWidget(String name) {
return Center(
child: Padding(
padding: const EdgeInsets.only(left: 10.0, top: 8),
child: Text(
name,
style: TextStyle(
color: Colors.black87, fontSize: 18, fontWeight: FontWeight.bold),
),
),
);
}
Widget phoneWidget(BuildContext context, String phone) {
return Container(
padding: EdgeInsets.only(top: 10),
child: Row(
children: <Widget>[
Icon(Icons.phone),
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: labeledText(context, phone, "user.phone"),
),
],
),
);
}
Widget fcsInput(String label, IconData iconData,
{TextEditingController controller,
String value,
bool autoFocus = false,
TextInputType textInputType}) {
return TextFormField(
initialValue: value,
controller: controller,
cursorColor: primaryColor,
maxLines: null,
minLines: 1,
autofocus: autoFocus,
keyboardType: textInputType,
decoration: InputDecoration(
fillColor: Colors.white,
labelText: label,
labelStyle: TextStyle(fontSize: 16, color: Colors.grey),
filled: true,
icon: Icon(
iconData,
color: primaryColor,
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.grey, width: 1.0)),
));
}
Widget fcsInputReadOnly(String label, IconData iconData,
{TextEditingController controller, String value}) {
return TextFormField(
initialValue: value,
controller: controller,
cursorColor: primaryColor,
maxLines: null,
minLines: 1,
readOnly: true,
decoration: InputDecoration(
fillColor: Colors.white,
border: InputBorder.none,
labelText: label,
labelStyle: TextStyle(fontSize: 16, color: Colors.grey),
filled: true,
icon: Icon(
iconData,
color: primaryColor,
),
));
}
Widget fcsDropDown(String label, IconData iconData,
{TextEditingController controller}) {
return Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: Icon(iconData),
),
Expanded(
child: Container(
height: 50.0,
child: Row(children: <Widget>[
Expanded(child: _dropDown()),
]),
)),
],
);
}
Widget _dropDown() {
return DropdownButton<String>(
value: "Ko Nge",
icon: Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
// style: TextStyle(color: Colors.deepPurple),
underline: Container(
height: 2,
color: primaryColor,
),
onChanged: (String newValue) {},
items: <String>['Ko Nge', 'Two', 'Free', 'Four']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
);
}
Widget fcsButton(BuildContext context, String text,
{Function callack, IconData iconData}) {
var languageModel = Provider.of<LanguageModel>(context);
var style = languageModel.isEng
? TextStyle(
fontSize: 16.0, color: Colors.white, fontWeight: FontWeight.bold)
: TextStyle(
fontSize: 16.0,
color: Colors.white,
fontWeight: FontWeight.bold,
fontFamily: "Myanmar3");
return Container(
padding: EdgeInsets.only(left: 10, right: 10, top: 10),
child: Container(
height: 45.0,
decoration: BoxDecoration(
color: primaryColor,
shape: BoxShape.rectangle,
),
child: ButtonTheme(
minWidth: 900.0,
height: 100.0,
child: FlatButton(
onPressed: callack,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
iconData == null
? Container()
: Icon(
iconData,
color: Colors.white,
),
SizedBox(
width: 15,
),
Text(text, style: style),
],
),
),
),
),
);
}
String getLocalString(BuildContext context, String key) {
return AppTranslations.of(context).text(key);
}
void showToast(GlobalKey key, String text) {
final ScaffoldState scaffold = key.currentState;
scaffold.showSnackBar(
SnackBar(
content: Text(text),
backgroundColor: secondaryColor,
duration: Duration(seconds: 1),
),
);
}
bool hasUnicode(String text) {
final int maxBits = 128;
List<int> unicodeSymbols =
text.codeUnits.where((ch) => ch > maxBits).toList();
return unicodeSymbols.length > 0;
}

View File

@@ -0,0 +1,156 @@
import 'package:fcs/localization/transalation.dart';
import 'package:fcs/pages/main/model/language_model.dart';
import 'package:fcs/pages/main/util.dart';
import 'package:fcs/pages/widgets/bottom_widgets.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
import 'package:provider/provider.dart';
import '../../helpers/theme.dart';
import '../signin/signin_page.dart';
import '../widgets/banner.dart';
import '../widgets/bottom_up_page_route.dart';
import '../widgets/offline_redirect.dart';
final msgLog = Logger('backgroundMessageHandler');
class WelcomePage extends StatefulWidget {
@override
_WelcomePageState createState() => _WelcomePageState();
}
typedef BtnCallback();
class _WelcomePageState extends State<WelcomePage> {
final log = Logger('_HomePageWelcomeState');
String pin;
List<bool> isSelected = [true, false];
@override
void initState() {
super.initState();
_loadLang();
}
void _loadLang() {
isSelected =
Provider.of<LanguageModel>(context, listen: false).currentState;
}
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return OfflineRedirect(
child: FlavorBanner(
child: Scaffold(
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment
.bottomCenter, // 10% of the width, so there are ten blinds.
colors: [
Color(0xd0272262),
Color(0xfa272262),
], // whitish to gray
)),
child: Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
ToggleButtons(
children: <Widget>[
Image.asset(
'icons/flags/png/us.png',
package: 'country_icons',
fit: BoxFit.fitWidth,
width: 25,
),
Image.asset(
'icons/flags/png/mm.png',
package: 'country_icons',
fit: BoxFit.fitWidth,
width: 25,
)
],
selectedBorderColor: Colors.white12,
borderColor: Colors.transparent,
onPressed: _langChange,
isSelected: isSelected,
),
FlatButton(
onPressed: () {
Navigator.of(context)
.push(BottomUpPageRoute(SigninPage()));
},
child: Text(
getLocalString(context, "welcome.signin"),
style: siginButtonStyle,
),
),
],
),
Expanded(
child: Column(
children: <Widget>[
Expanded(
child: Center(
child: Text(
getLocalString(context, "welcome.msg"),
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontFamily: "Roboto"),
),
),
),
Container(
padding: EdgeInsets.only(top: 0),
child: CircleAvatar(
radius: (25),
backgroundColor: Colors.white,
child: ClipRRect(
borderRadius: BorderRadius.circular(50),
child: Image.asset("assets/logo.jpg"),
)),
),
Padding(
padding:
const EdgeInsets.only(bottom: 10.0, top: 5),
child: Text(
"by FCS Logistics",
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 11,
fontFamily: "Roboto"),
),
),
],
),
),
BottomWidgets(),
],
))),
),
);
}
_langChange(index) {
var languageModel = Provider.of<LanguageModel>(context, listen: false);
languageModel.saveLanguage(Translation().supportedLanguages[index]);
setState(() {
isSelected.asMap().forEach((i, e) {
isSelected[i] = false;
});
isSelected[index] = !isSelected[index];
});
}
}

View File

@@ -1,227 +0,0 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/language_model.dart';
import 'package:fcs/model/main_model.dart';
import 'package:fcs/model/manual_model.dart';
import 'package:fcs/pages/manual/moveable_stack_item.dart';
import 'package:fcs/fcs/common/helpers/theme.dart';
import 'package:fcs/vo/manual.dart';
import 'package:path/path.dart' as Path;
import 'package:fcs/widget/progress.dart';
class InstructionDataPage extends StatefulWidget {
final SlideData slideData;
final String path;
final int slideIndex;
final int manIndex;
InstructionDataPage(
{this.slideData, this.path, this.slideIndex, this.manIndex});
@override
_InstructionDataPageState createState() => _InstructionDataPageState();
}
class _InstructionDataPageState extends State<InstructionDataPage> {
String selectedLanguage;
File slideImageFile;
bool isEng;
List<bool> _selection = List.generate(2, (_) => false);
String imgName;
bool _isLoading = false;
@override
void initState() {
super.initState();
isEng = true;
}
@override
Widget build(BuildContext context) {
var manualModel = Provider.of<ManualModel>(context);
if (isEng) {
imgName = widget.slideData.image;
} else {
imgName = widget.slideData.imagemm;
}
var screenSize = MediaQuery.of(context).size;
var width = screenSize.width;
var imgWidth = width - (width * 0.15);
var height = screenSize.height;
var imgHeight = height - (height * 0.29);
var toggleButtons = Container(
child: ToggleButtons(
children: <Widget>[
Row(
children: <Widget>[
Image.asset(
"assets/eng_flag.png",
width: 25,
),
Text(
"English",
style:
TextStyle(color: this.isEng ? secondaryColor : Colors.black),
)
],
),
Container(
padding: EdgeInsets.only(left: 10, right: 10),
child: Row(
children: <Widget>[
Image.asset(
"assets/myan_flag.png",
width: 25,
),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
"မြန်မာ",
style: TextStyle(
color: !this.isEng ? secondaryColor : Colors.black,
fontFamily: "MyanmarUnicode"),
),
)
],
),
)
],
renderBorder: false,
isSelected: _selection,
selectedColor: secondaryColor,
onPressed: (int index) {
setState(() {
_selection[index] = !_selection[index];
if (index == 0) {
this.isEng = true;
} else {
this.isEng = false;
}
});
},
));
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: AppBar(
title: Text('Instruction Data'),
actions: <Widget>[toggleButtons],
),
body: Container(
alignment: Alignment.topCenter,
child: Stack(
children: <Widget>[
Container(
child: Stack(
children: instructionData(
context, imgWidth, imgHeight, manualModel),
),
),
Positioned(
top: 0,
right: 0,
child: IconButton(
icon: Icon(Icons.image),
onPressed: () {
pickImageFromGallery(ImageSource.gallery, manualModel);
}),
)
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
addInstructionData();
},
tooltip: 'Pick Image',
child: Icon(Icons.add),
),
),
);
}
addInstructionData() {
var instructionList =
isEng ? widget.slideData.instructions : widget.slideData.instructionsmm;
Instruction inst;
if (instructionList.toList().length == 0) {
inst = Instruction(
id: 1,
text: '',
left: 0.0,
top: 0.0,
);
} else {
dynamic max = instructionList.first;
instructionList.forEach((e) {
if (e.id > max.id) max = e;
});
inst = Instruction(
id: max.id + 1,
text: '',
left: 0.0,
top: 0.0,
);
}
setState(() {
if (isEng) {
widget.slideData.instructions.add(inst);
} else {
widget.slideData.instructionsmm.add(inst);
}
});
}
instructionData(
BuildContext context, double imgW, double imgH, ManualModel manualModel) {
List<Widget> textFields = [];
textFields.add(
Image.file(
File('${widget.path}/manual/img/' + imgName),
alignment: AlignmentDirectional.topCenter,
height: imgH,
width: imgW,
),
);
var instructionList =
isEng ? widget.slideData.instructions : widget.slideData.instructionsmm;
if (instructionList.length != 0) {
instructionList.asMap().forEach((k, instruction) {
MoveableStackItem mitem = MoveableStackItem(
instruction: instruction,
manIndex: widget.manIndex,
slideIndex: widget.slideIndex,
instIndex: k,
isEng: this.isEng,
key: Key(this.isEng.toString() + k.toString()),
);
textFields.add(mitem);
});
}
return textFields;
}
pickImageFromGallery(ImageSource source, ManualModel manualModel) async {
File tempImage = await ImagePicker.pickImage(
source: source, imageQuality: 80, maxWidth: 300);
var fileName = Path.basename(tempImage.path);
var path = '${manualModel.dataDir}';
File newImage = await tempImage.copy('$path/manual/img/$fileName');
var slideData = widget.slideData;
if (this.isEng) {
slideData.image = fileName;
} else {
slideData.imagemm = fileName;
}
manualModel.changeSlideImage(widget.manIndex, widget.slideIndex, slideData);
}
}

View File

@@ -1,78 +0,0 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/manual_model.dart';
import 'package:fcs/vo/manual.dart';
class ManualItemTitleDialog extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _ManualItemTitleDialogState();
}
}
class _ManualItemTitleDialogState extends State<ManualItemTitleDialog> {
TextEditingController _engTextFieldController = TextEditingController();
TextEditingController _mmTextFieldController = TextEditingController();
ManualItem item;
bool buyer = false;
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return AlertDialog(
title: new Text('Enter Title'),
content: Container(
height: 200,
child: Column(
children: <Widget>[
TextField(
controller: _engTextFieldController,
decoration: InputDecoration(hintText: "Enter English"),
maxLines: null,
style: TextStyle(fontSize: 13.0),
),
TextField(
controller: _mmTextFieldController,
decoration: InputDecoration(hintText: "Enter Myanmar"),
maxLines: null,
style: TextStyle(fontSize: 13.0),
),
CheckboxListTile(
title: Text("For Buyer"),
value: buyer,
onChanged: (val) {
setState(() {
buyer = val;
});
},
),
],
),
),
actions: <Widget>[
new FlatButton(
onPressed: () {
_save();
},
child: new Text('Save'))
],
);
}
_save() {
try {
String eng = _engTextFieldController.text;
String mm = _mmTextFieldController.text;
ManualItem item =
ManualItem(title: eng, titlemm: mm, isBuyer: buyer, slides: []);
Navigator.pop<ManualItem>(context, item);
} catch (e) {
// showMsgDialog(context, "Error", e.toString());
}
}
}

View File

@@ -1,201 +0,0 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/language_model.dart';
import 'package:fcs/model/main_model.dart';
import 'package:fcs/model/manual_model.dart';
import 'package:fcs/pages/manual/manual_item_title_dialog.dart';
import 'package:fcs/fcs/common/pages/util.dart';
import 'package:fcs/vo/manual.dart';
import 'package:fcs/widget/local_text.dart';
import 'package:fcs/widget/progress.dart';
import '../../fcs/common/helpers/theme.dart';
import 'slide_page.dart';
class ManualPage extends StatefulWidget {
final String marketplace;
const ManualPage({Key key, this.marketplace}) : super(key: key);
@override
_ManualPageState createState() => _ManualPageState();
}
class _ManualPageState extends State<ManualPage> {
TextEditingController _manualVersionController = TextEditingController();
final double dotSize = 10.0;
List<ManualItem> helpList = new List();
bool isEng;
String versionName;
bool _isLoading = false;
@override
void initState() {
helpList.clear();
var manualModel = Provider.of<ManualModel>(context, listen: false);
var mainModel = Provider.of<MainModel>(context, listen: false);
versionName = manualModel.version;
helpList = manualModel.getHelpList(mainModel.isBuyer());
super.initState();
}
@override
Widget build(BuildContext context) {
var manualModel = Provider.of<ManualModel>(context);
var mainModel = Provider.of<MainModel>(context);
var languageModel = Provider.of<LanguageModel>(context);
isEng = languageModel.isEng;
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: AppBar(
leading: new IconButton(
icon: new Icon(Icons.close, color: Colors.white),
onPressed: () => Navigator.of(context).pop(),
),
title: Text(widget.marketplace == null ? '' : widget.marketplace),
backgroundColor: primaryColor,
),
body: Column(
children: <Widget>[
new Expanded(
child: new ListView.builder(
scrollDirection: Axis.vertical,
padding: EdgeInsets.only(left: 15, right: 15, top: 5),
shrinkWrap: true,
itemCount: helpList.length,
itemBuilder: (BuildContext context, int index) {
return Card(
elevation: 10,
color: Colors.white,
child: InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SlidePage(
helpDetail: helpList[index],
index: index,
)),
);
},
child: Row(children: <Widget>[
Expanded(
child: new Padding(
padding:
const EdgeInsets.symmetric(vertical: 5.0),
child: new Row(
children: <Widget>[
new Padding(
padding: new EdgeInsets.symmetric(
horizontal: 15.0 - dotSize / 2),
child: Container(
child: Image.asset(
"assets/page.png",
width: 50,
height: 50,
)),
),
new Expanded(
child: new Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
Text(
isEng
? helpList[index].title
: helpList[index].titlemm,
style: TextStyle(
fontSize: 15,
color: Colors.grey),
)
],
),
),
],
),
),
),
mainModel.isSysAdmin()
? Container(
padding: EdgeInsets.only(right: 20),
child: InkWell(
onTap: () {
showConfirmDialog(
context, "manual.confirm", () {
setState(() {
manualModel.deleteManualItem(
helpList[index]);
helpList = manualModel.getHelpList(
mainModel.isBuyer());
});
});
},
child: Icon(Icons.delete),
))
: Container()
])),
);
}),
),
],
),
floatingActionButton: mainModel.isSysAdmin()
? FloatingActionButton(
onPressed: () async {
ManualItem newItem = await showDialog(
context: context,
builder: (_) => ManualItemTitleDialog());
if (helpList.toList().length == 0) {
newItem.id = 1;
} else {
dynamic max = helpList.first;
helpList.forEach((e) {
if (e.id > max.id) max = e;
});
newItem.id = max.id + 1;
}
setState(() {
helpList.add(newItem);
});
manualModel.addManualTitle(newItem);
},
child: Icon(Icons.add),
)
: null,
),
);
}
_deleteManualItem(ManualItem item, ManualModel manualModel) {
return showConfirmDialog(context, "manual.confirm", () {
setState(() {
manualModel.deleteManualItem(item);
});
});
}
_inputManualVersion(BuildContext context, ManualModel manModel) async {
return showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: new Text('Version'),
content: TextField(
controller: _manualVersionController,
decoration: InputDecoration(hintText: "Enter manual version"),
),
actions: <Widget>[
new FlatButton(
onPressed: () {
String version = _manualVersionController.text;
String dir = manModel.dataDir;
manModel.uploadStorageManualData(version, dir);
Navigator.of(context).pop();
},
child: new Text('Save'))
],
);
});
}
}

View File

@@ -1,123 +0,0 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/manual_model.dart';
import 'package:fcs/vo/manual.dart';
class MoveableStackItem extends StatefulWidget {
Instruction instruction;
final int instIndex;
final int slideIndex;
final int manIndex;
final bool isEng;
MoveableStackItem(
{this.instIndex,
this.slideIndex,
this.manIndex,
this.instruction,
this.isEng,
Key key})
: super(key: key);
@override
State<StatefulWidget> createState() {
return _MoveableStackItemState();
}
}
class _MoveableStackItemState extends State<MoveableStackItem> {
TextEditingController _textFieldController = TextEditingController();
double xPosition = 0;
double yPosition = 0;
@override
void initState() {
super.initState();
_textFieldController.text = widget.instruction.text;
yPosition =
widget.instruction.top != null ? widget.instruction.top : yPosition;
xPosition =
widget.instruction.left != null ? widget.instruction.left : xPosition;
}
@override
Widget build(BuildContext context) {
var manualModel = Provider.of<ManualModel>(context);
return Positioned(
top: yPosition,
left: xPosition,
child: GestureDetector(
onPanUpdate: (tapInfo) {
setState(() {
xPosition += tapInfo.delta.dx;
yPosition += tapInfo.delta.dy;
});
var data = Instruction(
id: widget.instruction.id,
top: yPosition,
left: xPosition,
text: _textFieldController.text,
);
manualModel.saveInstruction(widget.manIndex, widget.slideIndex,
widget.instIndex, data, widget.instruction, widget.isEng);
},
child: Container(
width: 250,
color: Colors.grey,
child: InkWell(
onTap: () {
_displayDialog(context, manualModel);
},
child: Container(
child: Text(
_textFieldController.text,
maxLines: null,
style:
TextStyle(fontSize: 15.0, fontWeight: FontWeight.w700),
))))),
);
}
_displayDialog(BuildContext context, ManualModel manualModel) async {
return showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: new Text('Instruction'),
content: Container(
height: 100,
child: Column(
children: <Widget>[
TextField(
controller: _textFieldController,
decoration: InputDecoration(hintText: "Enter Instruction"),
maxLines: null,
style: TextStyle(fontSize: 13.0),
),
],
),
),
actions: <Widget>[
new FlatButton(
onPressed: () {
var data = Instruction(
id: widget.instruction.id,
top: yPosition,
left: xPosition,
text: _textFieldController.text,
);
manualModel.saveInstruction(
widget.manIndex,
widget.slideIndex,
widget.instIndex,
data,
widget.instruction,
widget.isEng);
Navigator.pop(context);
},
child: new Text('Save'))
],
);
});
}
}

View File

@@ -1,210 +0,0 @@
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path/path.dart' as Path;
import 'package:provider/provider.dart';
import 'package:fcs/model/manual_model.dart';
import 'package:fcs/pages/manual/instruction_data_page.dart';
import 'package:fcs/vo/manual.dart';
import 'package:fcs/widget/img_file.dart';
import 'package:fcs/widget/local_text.dart';
import 'package:fcs/widget/progress.dart';
class SlideDataPage extends StatefulWidget {
final ManualItem manItem;
final int manIndex;
SlideDataPage({this.manItem, this.manIndex});
@override
_SlideDataPageState createState() => _SlideDataPageState();
}
class _SlideDataPageState extends State<SlideDataPage> {
File slideImageFile;
List<SlideData> slideList = [];
List<SlideData> _initSlideList = [];
File imageFile;
File engImgFile;
File mmImgFile;
bool _isLoading = false;
@override
void initState() {
super.initState();
}
pickImageFromGallery(ImageSource source, ManualModel manualModel) async {
File tempImage = await ImagePicker.pickImage(
source: source, imageQuality: 80, maxWidth: 300);
var fileName = Path.basename(tempImage.path);
var path = '${manualModel.dataDir}';
File newImage = await tempImage.copy('$path/manual/img/$fileName');
var slideData =
SlideData(id: slideList.length, image: fileName, instructions: []);
setState(() {
slideList.add(slideData);
});
// manualModel.saveSlideData(widget.manIndex, slideList.length, slideData);
}
@override
Widget build(BuildContext context) {
var manualModel = Provider.of<ManualModel>(context);
slideList = manualModel.getSlideList(widget.manIndex);
final engImgBox = Container(
padding: EdgeInsets.only(left: 20),
child: Column(children: <Widget>[
Text('English image'),
ImageFile(
imageSource: ImageSource.gallery,
enabled: true,
title: "Image",
onFile: (file) {
engImgFile = file;
})
]));
final mmImgBox = Container(
padding: EdgeInsets.only(left: 20, top: 20),
child: Column(children: <Widget>[
Text('Myanmar image'),
ImageFile(
imageSource: ImageSource.gallery,
enabled: true,
title: "Image",
onFile: (file) {
mmImgFile = file;
}),
]));
final saveImages = Container(
padding: EdgeInsets.only(left: 20, top: 20),
child: FlatButton(
onPressed: () {
saveSlideImages(context, manualModel, widget.manIndex);
},
child: Text('Save')));
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: AppBar(
title: Text('Slide Data'),
),
body: Container(
child: GridView.builder(
itemCount: slideList.length,
itemBuilder: (BuildContext context, int index) {
return Card(
child: InkWell(
onTap: () => {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => InstructionDataPage(
slideData: slideList[index],
path: manualModel.dataDir,
slideIndex: index,
manIndex: widget.manIndex,
)))
},
child: Container(
child: Stack(
fit: StackFit.expand,
children: <Widget>[
Image.file(File('${manualModel.dataDir}/manual/img/' +
slideList[index].image)),
Positioned(
top: 0,
right: 0,
child: IconButton(
icon: Icon(Icons.delete),
onPressed: () {
setState(() {
manualModel.deleteSlideData(
widget.manIndex, slideList[index]);
});
}),
)
],
),
),
),
);
},
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () async => await _dialog(
context, manualModel, engImgBox, mmImgBox, saveImages),
tooltip: 'Pick Image',
child: Icon(Icons.add),
),
),
);
}
Future<void> _dialog(BuildContext context, ManualModel manualModel,
Widget engImg, Widget mmImg, Widget saveImgs) {
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>[engImg, mmImg, saveImgs],
),
],
),
),
),
);
},
);
}
saveSlideImages(
BuildContext context, ManualModel manualModel, int manIndex) async {
var engFileName = Path.basename(engImgFile.path);
var mmFileName = Path.basename(mmImgFile.path);
var path = '${manualModel.dataDir}';
File engImage = await engImgFile.copy('$path/manual/img/$engFileName');
File mmImage = await mmImgFile.copy('$path/manual/img/$mmFileName');
var length = slideList.toList().length;
var slideData;
if (length == 0) {
slideData = SlideData(
id: 1,
image: engFileName,
imagemm: mmFileName,
instructions: [],
instructionsmm: []);
} else {
dynamic max = slideList.first;
slideList.forEach((e) {
if (e.id > max.id) max = e;
});
slideData = SlideData(
id: max.id + 1,
image: engFileName,
imagemm: mmFileName,
instructions: [],
instructionsmm: []);
}
manualModel.saveSlideData(manIndex, length, slideData);
Navigator.pop(context);
}
}

View File

@@ -1,249 +0,0 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:introduction_screen/introduction_screen.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/language_model.dart';
import 'package:fcs/model/main_model.dart';
import 'package:fcs/model/manual_model.dart';
import 'package:fcs/pages/manual/slide_data_page.dart';
import 'package:fcs/vo/manual.dart';
import 'package:fcs/widget/local_text.dart';
import 'package:fcs/widget/progress.dart';
class SlidePage extends StatefulWidget {
final ManualItem helpDetail;
final int index;
SlidePage({this.helpDetail, this.index});
@override
_SlidePageState createState() => _SlidePageState();
}
class _SlidePageState extends State<SlidePage> {
bool isEng;
bool _isLoading = false;
void _onIntroEnd(context) {
Navigator.pop(context);
}
List<Widget> instructionText(BuildContext context, isEng, image, imgHeight,
width, List<Instruction> instructions) {
List<Widget> list = new List<Widget>();
var imgWidth = width - (width * 0.25);
File imgFile = File(image);
list.add(
Card(
color: const Color(0x7f7c94b6),
child: Opacity(
opacity: 0.6,
child: Image.file(
imgFile,
alignment: AlignmentDirectional.topCenter,
height: imgHeight,
width: imgWidth,
)),
),
);
for (var i = 0; i < instructions.length; i++) {
var instruction = instructions[i];
var textPositionTop = (imgHeight / 480) * instruction.top;
var textPositionLeft = (imgWidth / 360) * instruction.left;
list.add(Positioned(
top: double.parse(textPositionTop.toString()),
left: double.parse(textPositionLeft.toString()),
child: instruction.text.length > 1
? Container(
constraints: BoxConstraints(maxWidth: 300),
child: Card(
color: Colors.blue,
child: Padding(
padding: const EdgeInsets.all(3.0),
child: Text(
instruction.text,
style: TextStyle(color: Colors.white),
),
),
))
: Container(
constraints: BoxConstraints(maxWidth: 200),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
alignment: Alignment.topCenter,
width: 30,
height: 30,
decoration: BoxDecoration(
shape: BoxShape.circle, color: Colors.blue),
child: isEng
? Container(
padding: EdgeInsets.only(top: 5),
child: Text(
instruction.text,
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white, fontSize: 14.0),
),
)
: Text(
instruction.text,
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white, fontSize: 14.0),
),
),
],
),
),
));
}
return list;
}
@override
Widget build(BuildContext context) {
var mainModel = Provider.of<MainModel>(context);
var languageModel = Provider.of<LanguageModel>(context);
var manualModel = Provider.of<ManualModel>(context);
isEng = languageModel.isEng;
List<PageViewModel> pageViews = new List();
var screenSize = MediaQuery.of(context).size;
var width = screenSize.width;
var height = screenSize.height;
var imgHeight = height - (height * 0.25);
const bodyStyle = TextStyle(fontSize: 19.0);
const pageDecoration = const PageDecoration(
titleTextStyle: TextStyle(fontSize: 28.0, fontWeight: FontWeight.w700),
bodyTextStyle: bodyStyle,
descriptionPadding: EdgeInsets.fromLTRB(16.0, 0.0, 16.0, 16.0),
pageColor: Colors.white,
imagePadding: EdgeInsets.zero,
);
var pageSlides = widget.helpDetail.slides;
if (pageSlides.length == 0) {
pageViews.add(
PageViewModel(
titleWidget: Row(
children: <Widget>[
Text(
isEng ? widget.helpDetail.title : widget.helpDetail.titlemm,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20,
color: Colors.black,
),
),
mainModel.isSysAdmin()
? FlatButton(
textColor: Colors.blue,
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SlideDataPage(
manItem: widget.helpDetail,
manIndex: widget.index)),
);
},
child: Icon(Icons.edit),
shape: CircleBorder(
side: BorderSide(color: Colors.transparent)),
)
: Container(),
],
),
bodyWidget: Container(
width: width,
alignment: Alignment.center,
),
decoration: pageDecoration),
);
}
for (var i = 0; i < pageSlides.length; i++) {
var instructions;
if (isEng) {
instructions = pageSlides[i].instructions;
} else {
instructions = pageSlides[i].instructionsmm;
}
var imageName;
if (isEng) {
imageName = '${manualModel.dataDir}/manual/img/' + pageSlides[i].image;
} else {
imageName =
'${manualModel.dataDir}/manual/img/' + pageSlides[i].imagemm;
}
pageViews.add(
PageViewModel(
titleWidget: Row(
children: <Widget>[
Text(
isEng ? widget.helpDetail.title : widget.helpDetail.titlemm,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20,
color: Colors.black,
),
),
mainModel.isSysAdmin()
? FlatButton(
textColor: Colors.blue,
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SlideDataPage(
manItem: widget.helpDetail,
manIndex: widget.index)),
);
},
child: Icon(Icons.edit),
shape: CircleBorder(
side: BorderSide(color: Colors.transparent)),
)
: Container(),
],
),
bodyWidget: Container(
width: width,
alignment: Alignment.center,
child: Stack(
alignment: AlignmentDirectional.topStart,
children: instructionText(
context, isEng, imageName, imgHeight, width, instructions),
),
),
decoration: pageDecoration),
);
}
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
body: IntroductionScreen(
pages: pageViews,
onDone: () => _onIntroEnd(context),
showSkipButton: true,
skipFlex: 0,
nextFlex: 0,
skip: const Text('Skip'),
next: const Icon(Icons.arrow_forward),
done: const Text('Done', style: TextStyle(fontWeight: FontWeight.w600)),
dotsDecorator: DotsDecorator(spacing: EdgeInsets.all(1.0)),
)),
);
}
}

View File

@@ -0,0 +1,185 @@
import 'package:fcs/domain/entities/market.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/pages/market/model/market_model.dart';
import 'package:fcs/pages/main/util.dart';
import 'package:fcs/pages/widgets/input_text.dart';
import 'package:fcs/pages/widgets/local_text.dart';
import 'package:fcs/pages/widgets/progress.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
typedef void FindCallBack();
class MarketEditor extends StatefulWidget {
const MarketEditor();
@override
_MarketEditorState createState() => _MarketEditorState();
}
class _MarketEditorState extends State<MarketEditor> {
TextEditingController _marketNameCtl = new TextEditingController();
bool _isLoading = false;
List<String> markets = [];
@override
void initState() {
super.initState();
}
List<Widget> showMarkets(BuildContext context, List<Market> markets) {
return markets.map((p) {
return new ListTile(
title: new Row(
children: <Widget>[
Expanded(
child: new Text(
p.name,
style: TextStyle(
fontSize: 15.0,
),
),
),
Spacer(),
IconButton(
icon: Icon(Icons.remove, color: primaryColor),
onPressed: () => _remove(p),
)
],
));
}).toList();
}
@override
Widget build(BuildContext context) {
List<Market> markets = Provider.of<MarketModel>(context).markets;
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,
"market.edit.title",
fontSize: 20,
color: primaryColor,
),
),
body: Padding(
padding: const EdgeInsets.only(left: 12.0, right: 12),
child: ListView(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Padding(
padding: const EdgeInsets.only(right: 18.0),
child: IconButton(
icon: Icon(Icons.add, color: primaryColor),
onPressed: () => _showDialog(context),
),
),
],
),
Column(
children: showMarkets(context, markets),
),
SizedBox(
height: 10,
)
],
),
),
));
}
_remove(Market market) {
if (market == null) {
showMsgDialog(context, "Esrror", "Invalid market!");
return;
}
showConfirmDialog(
context, "market.remove.confirm", () => _removeMarket(market));
}
_removeMarket(Market market) async {
setState(() {
_isLoading = true;
});
MarketModel marketModel = Provider.of<MarketModel>(context, listen: false);
try {
await marketModel.deleteMarket(market.id);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
_add() async {
if (_marketNameCtl.text == "") {
showMsgDialog(context, "Esrror", "Invalid market name!");
return;
}
setState(() {
_isLoading = true;
});
MarketModel marketModel = Provider.of<MarketModel>(context, listen: false);
try {
await marketModel.addMarket(_marketNameCtl.text);
_marketNameCtl.text = "";
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
_showDialog(BuildContext context) async {
await showDialog<String>(
context: context,
child: new AlertDialog(
contentPadding: const EdgeInsets.all(16.0),
content: new Row(
children: <Widget>[
new Expanded(
child: InputText(
labelTextKey: "market.edit.name",
controller: _marketNameCtl,
autoFocus: true,
),
)
],
),
actions: <Widget>[
new FlatButton(
child: LocalText(context, "btn.cancel", color: primaryColor),
onPressed: () {
Navigator.pop(context);
}),
new FlatButton(
child: LocalText(
context,
"btn.save",
color: primaryColor,
),
onPressed: () {
Navigator.pop(context);
_add();
})
],
),
);
}
}

View File

@@ -0,0 +1,49 @@
import 'dart:async';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:fcs/domain/constants.dart';
import 'package:fcs/domain/entities/market.dart';
import 'package:fcs/helpers/firebase_helper.dart';
import 'package:fcs/pages/main/model/base_model.dart';
import 'package:logging/logging.dart';
class MarketModel extends BaseModel {
final log = Logger('MarketModel');
StreamSubscription<QuerySnapshot> listener;
List<Market> markets = [];
MarketModel() {
_loadMarkets();
}
Future<void> _loadMarkets() async {
try {
if (listener != null) listener.cancel();
listener = Firestore.instance
.collection("/$config_collection/$setting_doc_id/$markets_collection")
.snapshots()
.listen((QuerySnapshot snapshot) {
markets.clear();
markets = snapshot.documents.map((documentSnapshot) {
var user = Market.fromMap(
documentSnapshot.data, documentSnapshot.documentID);
return user;
}).toList();
notifyListeners();
});
} catch (e) {
log.warning("Error!! $e");
}
}
Future<void> addMarket(String marketName) async {
await request("/markets", "POST",
payload: {"name": marketName}, token: await getToken());
}
Future<void> deleteMarket(String id) async {
await request("/markets", "DELETE",
payload: {"id": id}, token: await getToken());
}
}

View File

@@ -1,140 +0,0 @@
import 'package:fcs/model_fcs/message_model.dart';
import 'package:fcs/fcs/common/helpers/theme.dart';
import 'package:fcs/vo/message.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
class Bubble extends StatelessWidget {
Bubble({this.message, this.time, this.delivered, this.isMe});
final String message, time;
final delivered, isMe;
@override
Widget build(BuildContext context) {
final bg = isMe ? Colors.white : Colors.greenAccent.shade100;
final align = isMe ? CrossAxisAlignment.start : CrossAxisAlignment.end;
final icon = delivered ? Icons.done_all : Icons.done;
final radius = isMe
? BorderRadius.only(
topRight: Radius.circular(5.0),
bottomLeft: Radius.circular(10.0),
bottomRight: Radius.circular(5.0),
)
: BorderRadius.only(
topLeft: Radius.circular(5.0),
bottomLeft: Radius.circular(5.0),
bottomRight: Radius.circular(10.0),
);
return Column(
crossAxisAlignment: align,
children: <Widget>[
Container(
margin: const EdgeInsets.all(3.0),
padding: const EdgeInsets.all(8.0),
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
blurRadius: .5,
spreadRadius: 1.0,
color: Colors.black.withOpacity(.12))
],
color: bg,
borderRadius: radius,
),
child: Stack(
children: <Widget>[
Padding(
padding: EdgeInsets.only(right: 48.0),
child: Text(message),
),
Positioned(
bottom: 0.0,
right: 0.0,
child: Row(
children: <Widget>[
Text(time,
style: TextStyle(
color: Colors.black38,
fontSize: 10.0,
)),
SizedBox(width: 3.0),
Icon(
icon,
size: 12.0,
color: Colors.black38,
)
],
),
)
],
),
)
],
);
}
}
class MessageDetail extends StatelessWidget {
final Message msg;
const MessageDetail({Key key, this.msg}) : super(key: key);
@override
Widget build(BuildContext context) {
DateFormat dateFormat = DateFormat("HH:mm");
MessageModel messageModel = Provider.of<MessageModel>(context);
List<Message> messages = messageModel.getMessage(msg.receiverName);
List<Bubble> bubbles = messages
.map((e) => Bubble(
message: e.message,
time: dateFormat.format(e.date),
delivered: true,
isMe: !(e.isMe != null ? e.isMe : true)))
.toList();
return Scaffold(
appBar: AppBar(
backgroundColor: primaryColor,
elevation: .9,
title: Text(
msg.receiverName,
),
actions: <Widget>[],
),
body: Padding(
padding: EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: bubbles,
// children: <Widget>[
// Bubble(
// message: 'Hi there, this is a message',
// time: '12:00',
// delivered: true,
// isMe: false,
// ),
// Bubble(
// message: 'Whatsapp like bubble talk',
// time: '12:01',
// delivered: true,
// isMe: true,
// ),
// Bubble(
// message: 'Nice one, Flutter is awesome',
// time: '12:00',
// delivered: true,
// isMe: true,
// ),
// Bubble(
// message: 'I\'ve told you so dude!',
// time: '12:00',
// delivered: true,
// isMe: false,
// ),
// ],
),
),
);
}
}

View File

@@ -1,316 +0,0 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/language_model.dart';
import 'package:fcs/model/reg_model.dart';
import 'package:fcs/fcs/common/pages/util.dart';
import 'package:fcs/vo/buyer.dart';
import 'package:fcs/widget/img_file.dart';
import 'package:fcs/widget/local_text.dart';
import 'package:fcs/widget/my_data_table.dart';
import 'package:fcs/widget/progress.dart';
import '../fcs/common/helpers/theme.dart';
import '../widget/localization/app_translations.dart';
import 'my_registeration_item.dart';
enum BuyerType { shop, agent }
class MyRegisteration extends StatefulWidget {
final Buyer buyer;
const MyRegisteration({this.buyer});
@override
_MyRegisterationState createState() => _MyRegisterationState();
}
class _MyRegisterationState extends State<MyRegisteration> {
TextEditingController _bizName = new TextEditingController();
TextEditingController _bizAddress = new TextEditingController();
BuyerType buyerType;
Buyer buyer = Buyer();
final formKey = GlobalKey<FormState>();
bool _isLoading = false;
Attachments attachments = Attachments();
bool _isNew = true;
@override
void initState() {
if (widget.buyer != null) {
_isNew = false;
buyerType =
widget.buyer.bizType == "shop" ? BuyerType.shop : BuyerType.agent;
buyer = widget.buyer;
_bizName.text = buyer.bizName;
_bizAddress.text = buyer.bizAddress;
} else {
buyerType = BuyerType.shop;
}
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
var languageModel = Provider.of<LanguageModel>(context);
final companyName = Container(
padding: EdgeInsets.only(top: 0, left: 20, right: 15),
child: TextFormField(
controller: _bizName,
autofocus: false,
cursorColor: primaryColor,
style: textStyle,
decoration: new InputDecoration(
labelText: AppTranslations.of(context).text("reg.biz_name"),
labelStyle: languageModel.isEng ? labelStyle : labelStyleMM,
icon: Icon(
Icons.business,
color: primaryColor,
),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: primaryColor, width: 1.0)),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: primaryColor, width: 1.0)),
),
validator: _validateBizName,
));
final companyAddress = Container(
padding: EdgeInsets.only(top: 0, left: 20, right: 15),
child: TextFormField(
minLines: 2,
maxLines: 3,
controller: _bizAddress,
autofocus: false,
cursorColor: primaryColor,
style: textStyle,
decoration: new InputDecoration(
labelText: AppTranslations.of(context).text("reg.biz_address"),
labelStyle: languageModel.isEng ? labelStyle : labelStyleMM,
icon: Image.asset(
"assets/address.png",
height: 25,
color: primaryColor,
),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: primaryColor, width: 1.0)),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: primaryColor, width: 1.0)),
),
validator: _validateBizAddress,
));
final nricFrontBox = Container(
padding: EdgeInsets.only(left: 20),
child: Row(children: <Widget>[
LocalText(context, 'reg_info.nric_front'),
ImageFile(
enabled: true,
initialImgUrl:
widget.buyer == null ? "" : widget.buyer.nricFrontUrl,
title: "Image",
onFile: (file) {
attachments.nricFront = file;
})
]));
final nricBackBox = Container(
padding: EdgeInsets.only(left: 20, top: 20),
child: Row(children: <Widget>[
LocalText(context, 'reg_info.nric_back'),
ImageFile(
enabled: true,
initialImgUrl:
widget.buyer == null ? '' : widget.buyer.nricBackUrl,
title: "Image",
onFile: (file) {
attachments.nricBack = file;
}),
]));
final nric = Container(
padding: EdgeInsets.only(top: 20, left: 20, right: 15),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[nricFrontBox, nricBackBox],
));
final buyerTypeWidget = Row(children: <Widget>[
Flexible(
child: ListTile(
onTap: () => setState(() {
buyerType = BuyerType.shop;
}),
title: Text(AppTranslations.of(context).text("reg.type_shop")),
leading: Radio(
activeColor: primaryColor,
value: BuyerType.shop,
groupValue: buyerType,
onChanged: (BuyerType value) {
setState(() {
buyerType = value;
});
},
),
),
),
Flexible(
child: ListTile(
onTap: () => setState(() {
buyerType = BuyerType.agent;
}),
title: Text(AppTranslations.of(context).text("reg.type_agent")),
leading: Radio(
activeColor: primaryColor,
value: BuyerType.agent,
groupValue: buyerType,
onChanged: (BuyerType value) {
setState(() {
buyerType = value;
});
},
),
),
),
]);
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: AppBar(
backgroundColor: primaryColor,
title: Text(AppTranslations.of(context).text("reg.title")),
actions: <Widget>[
IconButton(
icon: Icon(Icons.send),
onPressed: () {
showConfirmDialog(context, "reg.confirm", () {
_submit();
});
},
)
],
),
body: Container(
child: Form(
key: formKey,
child: ListView(
children: <Widget>[
companyName,
companyAddress,
buyerTypeWidget,
nric,
],
),
),
),
),
);
}
List<MyDataRow> getProductRow(Buyer buyer) {
return buyer.buyerProducts.map((b) {
return MyDataRow(
onSelectChanged: (bool selected) async {
final BuyerProduct buyerProduct = await Navigator.push<BuyerProduct>(
context,
MaterialPageRoute(
builder: (context) => MyRegisterationItem(
buyerProduct: b,
)),
);
_save(buyerProduct);
},
cells: [
MyDataCell(
new Text(
b.productName,
style: textStyle,
),
),
MyDataCell(
new Text(b.storageCapacityQty.toString(), style: textStyle),
),
MyDataCell(
new Text(b.dailySaleQty.toString(), style: textStyle),
),
],
);
}).toList();
}
String _validateBizName(value) {
if (value.isEmpty) {
return AppTranslations.of(context).text("reg.empty_biz_name");
}
return null;
}
String _validateBizAddress(value) {
if (value.isEmpty) {
return AppTranslations.of(context).text("reg.empty_biz_address");
}
return null;
}
String _validateShops(value) {
if (value.isEmpty) {
return AppTranslations.of(context).text("reg.empty_shops");
}
return null;
}
_save(BuyerProduct buyerProduct) {
if (buyerProduct == null) return;
if (buyerProduct.action == "create") {
if (buyer.buyerProducts.contains(buyerProduct)) {
showMsgDialog(context, "Error", "Duplicate line");
return;
}
buyer.buyerProducts.add(buyerProduct);
} else if (buyerProduct.action == "delete") {
buyer.buyerProducts.remove(buyerProduct);
}
}
_submit() async {
if (!formKey.currentState.validate()) {
return;
}
if (_isNew) {
if (attachments.nricFront == null || attachments.nricBack == null) {
showMsgDialog(
context, "Error", "Required NRIC front and back attachments");
return;
}
}
setState(() {
_isLoading = true;
});
try {
String type = buyerType == BuyerType.agent ? "agent" : "shop";
buyer.bizName = _bizName.text;
buyer.bizAddress = _bizAddress.text;
buyer.bizType = type;
if (_isNew) {
await Provider.of<RegModel>(context).register(buyer, attachments);
} else {
await Provider.of<RegModel>(context).update(buyer, attachments);
}
Navigator.pop(context, true);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
}

View File

@@ -1,121 +0,0 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/main_model.dart';
import 'package:fcs/model/reg_model.dart';
import 'package:fcs/widget/label_widgets.dart';
import 'package:fcs/widget/progress.dart';
import '../fcs/common/helpers/theme.dart';
import '../util.dart';
import '../widget/localization/app_translations.dart';
import 'my_registeration.dart';
enum BuyerType { shop, agent }
class MyRegisterationInfo extends StatefulWidget {
@override
_MyRegisterationInfoState createState() => _MyRegisterationInfoState();
}
class _MyRegisterationInfoState extends State<MyRegisterationInfo> {
bool inProgress = true;
BuyerType buyerType = BuyerType.shop;
TextEditingController _bizName = new TextEditingController();
TextEditingController _bizAddress = new TextEditingController();
TextEditingController _status = new TextEditingController();
@override
Widget build(BuildContext context) {
RegModel regModel = Provider.of<RegModel>(context);
MainModel mainModel = Provider.of<MainModel>(context);
buyerType =
regModel.reg.bizType == "shop" ? BuyerType.shop : BuyerType.agent;
_bizName.text = regModel.reg.bizName;
_bizAddress.text = regModel.reg.bizAddress;
_status.text = regModel.reg.status;
bool isRegBuyer = mainModel.isRegBuyer();
if (regModel.isLoaded) {
setState(() {
this.inProgress = false;
});
}
final bizNameBox =
labeledText(context, regModel.reg.bizName, "reg.biz_name");
final bizAddressBox =
labeledText(context, regModel.reg.bizAddress, "reg.biz_address");
final typeBox =
labeledText(context, regModel.reg.bizType, "buyer.type_biz");
final statusBox = labeledText(context, regModel.reg.status, "reg.status");
final dailyQuotaBox = labeledText(
context, formatNumber(regModel.reg.dailyQuota), "reg.quota",
number: true);
final dailyQuotaUsedBox = labeledText(
context, formatNumber(regModel.reg.dailyQuotaUsed), "reg.quota.used",
number: true);
final maxQuotaBox = labeledText(
context, formatNumber(regModel.reg.maxQuota), "reg.max_quota",
number: true);
final maxQuotaUsedBox = labeledText(
context, formatNumber(regModel.reg.maxQuotaUsed), "reg.max_quota.used",
number: true);
final nricFrontBox =
labeledImg(context, regModel.reg.nricFrontUrl, "reg_info.nric_front");
final nricBackBox =
labeledImg(context, regModel.reg.nricBackUrl, "reg_info.nric_back");
return LocalProgress(
inAsyncCall: inProgress,
child: Scaffold(
appBar: AppBar(
backgroundColor: primaryColor,
title: Text(AppTranslations.of(context).text("reg_info.title")),
actions: <Widget>[
!isRegBuyer
? Container()
: regModel.reg.isApproved()
? Container()
: IconButton(
icon: Icon(Icons.edit),
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (_) => MyRegisteration(
buyer: regModel.reg,
)));
},
),
],
),
body: Container(
padding: EdgeInsets.only(left: 10, right: 10, top: 10, bottom: 10),
child: ListView(
children: <Widget>[
bizNameBox,
Divider(),
bizAddressBox,
Divider(),
typeBox,
Divider(),
statusBox,
Divider(),
dailyQuotaBox,
Divider(),
dailyQuotaUsedBox,
Divider(),
maxQuotaBox,
Divider(),
maxQuotaUsedBox,
Divider(),
nricFrontBox,
Divider(),
nricBackBox,
Divider()
],
),
),
),
);
}
}

View File

@@ -1,194 +0,0 @@
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/product_model.dart';
import 'package:fcs/fcs/common/helpers/theme.dart';
import 'package:fcs/vo/buyer.dart';
import 'package:fcs/vo/product.dart';
import 'package:fcs/widget/progress.dart';
class MyRegisterationItem extends StatefulWidget {
final BuyerProduct buyerProduct;
const MyRegisterationItem({Key key, this.buyerProduct}) : super(key: key);
@override
_MyRegisterationItemState createState() => _MyRegisterationItemState();
}
class _MyRegisterationItemState extends State<MyRegisterationItem> {
final _formKey = GlobalKey<FormState>();
bool _isLoading = false;
TextEditingController _storage = new TextEditingController();
TextEditingController _sales = new TextEditingController();
String currentProductID;
BuyerProduct buyerProduct = BuyerProduct(action: "create");
@override
void initState() {
super.initState();
if (widget.buyerProduct != null) {
buyerProduct = widget.buyerProduct;
buyerProduct.action = "update";
currentProductID = buyerProduct.productID;
_sales.text = buyerProduct.dailySaleQty.toString();
_storage.text = buyerProduct.storageCapacityQty.toString();
}
}
Widget showProducts(BuildContext context, ProductModel productModel) {
return Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Icon(
FontAwesomeIcons.tag,
color: primaryColor,
size: 20,
),
SizedBox(
width: 20,
),
new Flexible(
child: Container(
width: 170.0,
child: DropdownButton<String>(
value: currentProductID,
isExpanded: true,
hint: Text(
'Select Product',
style: labelStyle,
),
onChanged: changedProduct,
items: productModel.products
.map<DropdownMenuItem<String>>((Product product) {
return new DropdownMenuItem<String>(
value: product.id,
child: new Text(product.name, style: textStyle),
);
}).toList(),
),
),
),
],
);
}
void changedProduct(selected) {
setState(() {
currentProductID = selected;
});
}
@override
Widget build(BuildContext context) {
ProductModel productModel = Provider.of<ProductModel>(context);
final volumeBox = Container(
padding: EdgeInsets.only(top: 10),
child: TextFormField(
controller: _storage,
autofocus: false,
cursorColor: primaryColor,
keyboardType: TextInputType.number,
decoration: new InputDecoration(
labelText: "Enter total storage capacity",
labelStyle: labelStyle,
icon: Image.asset(
"assets/volume.png",
width: 23,
color: primaryColor,
),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: primaryColor, width: 1.0)),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: primaryColor, width: 1.0)),
),
validator: (value) {
if (value.isEmpty) {
return "Please total storage capacity";
}
return null;
},
),
);
final amountBox = Container(
child: TextFormField(
controller: _sales,
autofocus: false,
cursorColor: primaryColor,
keyboardType: TextInputType.number,
decoration: new InputDecoration(
labelText: "Enter daily sale quantity",
labelStyle: labelStyle,
icon: Image.asset(
"assets/sales.png",
width: 23,
color: primaryColor,
),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: primaryColor, width: 1.0)),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: primaryColor, width: 1.0)),
),
validator: (value) {
if (value.isEmpty) {
return "Please enter daily sale quantity";
}
return null;
},
),
);
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: AppBar(
backgroundColor: primaryColor,
title: Text("Product registration"),
actions: <Widget>[
IconButton(
icon: Icon(Icons.delete),
onPressed: () {
buyerProduct.action = "delete";
Navigator.pop(context, buyerProduct);
},
),
IconButton(
icon: Icon(Icons.save),
onPressed: () {
if (!_formKey.currentState.validate()) return;
_save();
},
)
],
),
body: Form(
key: _formKey,
child: Column(
children: <Widget>[
Expanded(
child: ListView(
shrinkWrap: true,
padding: EdgeInsets.only(left: 24.0, right: 24.0),
children: <Widget>[
showProducts(context, productModel),
volumeBox,
amountBox,
],
),
),
],
),
)),
);
}
_save() {
if (currentProductID == null) return;
this.buyerProduct.productID = currentProductID;
var productName =
Provider.of<ProductModel>(context).getProductName(currentProductID);
this.buyerProduct.productName = productName;
this.buyerProduct.storageCapacityQty = int.parse(_storage.text);
this.buyerProduct.dailySaleQty = int.parse(_sales.text);
Navigator.pop<BuyerProduct>(context, this.buyerProduct);
}
}

Some files were not shown because too many files have changed in this diff Show More