clean up
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
557
lib/pages/box/box_editor.dart
Normal file
557
lib/pages/box/box_editor.dart
Normal 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
137
lib/pages/box/box_list.dart
Normal 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,
|
||||
);
|
||||
}),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
115
lib/pages/box/box_list_row.dart
Normal file
115
lib/pages/box/box_list_row.dart
Normal 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),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
307
lib/pages/box/model/box_model.dart
Normal file
307
lib/pages/box/model/box_model.dart
Normal 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 = [];
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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],
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
132
lib/pages/buying_instruction/buying_online.dart
Normal file
132
lib/pages/buying_instruction/buying_online.dart
Normal 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)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
},
|
||||
),
|
||||
)))
|
||||
],
|
||||
))
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
135
lib/pages/chat/bubble.dart
Normal 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();
|
||||
}
|
||||
}
|
||||
191
lib/pages/chat/message_detail.dart
Normal file
191
lib/pages/chat/message_detail.dart
Normal 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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
92
lib/pages/chat/model/message_model.dart
Normal file
92
lib/pages/chat/model/message_model.dart
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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"]);
|
||||
}
|
||||
}
|
||||
151
lib/pages/contact/contact_editor.dart
Normal file
151
lib/pages/contact/contact_editor.dart
Normal 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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
127
lib/pages/contact/contact_page.dart
Normal file
127
lib/pages/contact/contact_page.dart
Normal 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");
|
||||
}
|
||||
}
|
||||
17
lib/pages/contact/model/contact_model.dart
Normal file
17
lib/pages/contact/model/contact_model.dart
Normal 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();
|
||||
}
|
||||
}
|
||||
103
lib/pages/contact/widgets.dart
Normal file
103
lib/pages/contact/widgets.dart
Normal 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,
|
||||
)
|
||||
],
|
||||
),
|
||||
)),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
120
lib/pages/customer/customer_editor.dart
Normal file
120
lib/pages/customer/customer_editor.dart
Normal 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;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
240
lib/pages/customer/customer_list.dart
Normal file
240
lib/pages/customer/customer_list.dart
Normal 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);
|
||||
}
|
||||
}
|
||||
159
lib/pages/customer/invitation_create.dart
Normal file
159
lib/pages/customer/invitation_create.dart
Normal 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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
100
lib/pages/customer/invitation_editor.dart
Normal file
100
lib/pages/customer/invitation_editor.dart
Normal 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;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
98
lib/pages/customer/model/customer_model.dart
Normal file
98
lib/pages/customer/model/customer_model.dart
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
);
|
||||
}),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
114
lib/pages/delivery/delivery_list_row.dart
Normal file
114
lib/pages/delivery/delivery_list_row.dart
Normal 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),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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
|
||||
56
lib/pages/discount/model/discount_model.dart
Normal file
56
lib/pages/discount/model/discount_model.dart
Normal 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 = [];
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
139
lib/pages/faq/faq_detail_page.dart
Normal file
139
lib/pages/faq/faq_detail_page.dart
Normal 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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
248
lib/pages/faq/faq_edit_page.dart
Normal file
248
lib/pages/faq/faq_edit_page.dart
Normal 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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
179
lib/pages/faq/faq_list_page.dart
Normal file
179
lib/pages/faq/faq_list_page.dart
Normal 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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
56
lib/pages/faq/model/faq_model.dart
Normal file
56
lib/pages/faq/model/faq_model.dart
Normal 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
103
lib/pages/faq/widgets.dart
Normal 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,
|
||||
)
|
||||
],
|
||||
),
|
||||
)),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -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);
|
||||
},
|
||||
),
|
||||
)))
|
||||
],
|
||||
))
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
@@ -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]);
|
||||
}),
|
||||
),
|
||||
],
|
||||
@@ -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>[
|
||||
84
lib/pages/fcs_shipment/model/fcs_shipment_model.dart
Normal file
84
lib/pages/fcs_shipment/model/fcs_shipment_model.dart
Normal 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 = [];
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
// },
|
||||
// ),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
)),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
163
lib/pages/invoice/model/invoice_model.dart
Normal file
163
lib/pages/invoice/model/invoice_model.dart
Normal 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 = [];
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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"),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
]),
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
404
lib/pages/main/home_page.dart
Normal file
404
lib/pages/main/home_page.dart
Normal 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);
|
||||
// }
|
||||
}
|
||||
202
lib/pages/main/initial_language_selection.dart
Normal file
202
lib/pages/main/initial_language_selection.dart
Normal 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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
37
lib/pages/main/model/base_model.dart
Normal file
37
lib/pages/main/model/base_model.dart
Normal 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);
|
||||
}
|
||||
}
|
||||
45
lib/pages/main/model/language_model.dart
Normal file
45
lib/pages/main/model/language_model.dart
Normal 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();
|
||||
}
|
||||
}
|
||||
169
lib/pages/main/model/main_model.dart
Normal file
169
lib/pages/main/model/main_model.dart
Normal 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();
|
||||
}
|
||||
}
|
||||
101
lib/pages/main/splash_page.dart
Normal file
101
lib/pages/main/splash_page.dart
Normal 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
452
lib/pages/main/util.dart
Normal 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;
|
||||
}
|
||||
156
lib/pages/main/welcome_page.dart
Normal file
156
lib/pages/main/welcome_page.dart
Normal 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];
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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'))
|
||||
],
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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'))
|
||||
],
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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)),
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
||||
185
lib/pages/market/market_editor.dart
Normal file
185
lib/pages/market/market_editor.dart
Normal 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();
|
||||
})
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
49
lib/pages/market/model/market_model.dart
Normal file
49
lib/pages/market/model/market_model.dart
Normal 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());
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
// ),
|
||||
// ],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user