add structure
This commit is contained in:
363
lib/pages/add_pin_editor.dart
Normal file
363
lib/pages/add_pin_editor.dart
Normal file
@@ -0,0 +1,363 @@
|
||||
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 '../theme/theme.dart' as Theme;
|
||||
import '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;
|
||||
}
|
||||
}
|
||||
184
lib/pages/announcement.dart
Normal file
184
lib/pages/announcement.dart
Normal file
@@ -0,0 +1,184 @@
|
||||
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/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 '../theme/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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
152
lib/pages/announcement_editor.dart
Normal file
152
lib/pages/announcement_editor.dart
Normal file
@@ -0,0 +1,152 @@
|
||||
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/pages/util.dart';
|
||||
import 'package:fcs/theme/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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
145
lib/pages/announcement_list.dart
Normal file
145
lib/pages/announcement_list.dart
Normal file
@@ -0,0 +1,145 @@
|
||||
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 '../theme/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)),
|
||||
],
|
||||
)
|
||||
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
195
lib/pages/banks/bank_edit.dart
Normal file
195
lib/pages/banks/bank_edit.dart
Normal file
@@ -0,0 +1,195 @@
|
||||
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/pages/util.dart';
|
||||
import 'package:fcs/theme/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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
259
lib/pages/banks/banks.dart
Normal file
259
lib/pages/banks/banks.dart
Normal file
@@ -0,0 +1,259 @@
|
||||
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/theme/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),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
120
lib/pages/block_list.dart
Normal file
120
lib/pages/block_list.dart
Normal file
@@ -0,0 +1,120 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/user_model.dart';
|
||||
import 'package:fcs/pages/util.dart';
|
||||
import 'package:fcs/theme/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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
281
lib/pages/buyer_info.dart
Normal file
281
lib/pages/buyer_info.dart
Normal file
@@ -0,0 +1,281 @@
|
||||
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/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 '../theme/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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
208
lib/pages/buyer_list.dart
Normal file
208
lib/pages/buyer_list.dart
Normal file
@@ -0,0 +1,208 @@
|
||||
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 '../theme/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],
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
106
lib/pages/buyer_list_row.dart
Normal file
106
lib/pages/buyer_list_row.dart
Normal file
@@ -0,0 +1,106 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/buyer_model.dart';
|
||||
import 'package:fcs/pages/util.dart';
|
||||
import 'package:fcs/theme/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),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
272
lib/pages/chage_phone_number.dart
Normal file
272
lib/pages/chage_phone_number.dart
Normal file
@@ -0,0 +1,272 @@
|
||||
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 '../theme/theme.dart' as Theme;
|
||||
import 'confirm_email.dart';
|
||||
import '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;
|
||||
}
|
||||
}
|
||||
320
lib/pages/change_password.dart
Normal file
320
lib/pages/change_password.dart
Normal file
@@ -0,0 +1,320 @@
|
||||
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 '../theme/theme.dart' as Theme;
|
||||
import '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;
|
||||
}
|
||||
}
|
||||
114
lib/pages/confirm_email.dart
Normal file
114
lib/pages/confirm_email.dart
Normal file
@@ -0,0 +1,114 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/user_model.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/widget/localization/app_translations.dart';
|
||||
import 'package:fcs/widget/progress.dart';
|
||||
|
||||
import '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, "/home", (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,
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
161
lib/pages/contact.dart
Normal file
161
lib/pages/contact.dart
Normal file
@@ -0,0 +1,161 @@
|
||||
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/pages/util.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/widget/local_text.dart';
|
||||
import 'package:fcs/widget/progress.dart';
|
||||
|
||||
import 'contact_editor.dart';
|
||||
|
||||
class Contact extends StatefulWidget {
|
||||
@override
|
||||
_ContactState createState() => _ContactState();
|
||||
}
|
||||
|
||||
class _ContactState extends State<Contact> {
|
||||
bool _isLoading = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
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: Colors.white,
|
||||
iconTheme: IconThemeData(
|
||||
color: Colors.grey,
|
||||
),
|
||||
elevation: 0,
|
||||
centerTitle: true,
|
||||
title: Image(
|
||||
height: 30,
|
||||
fit: BoxFit.scaleDown,
|
||||
image: new AssetImage('assets/img/logo.png')),
|
||||
actions: <Widget>[
|
||||
isOwner || hasAdmin
|
||||
? IconButton(
|
||||
icon: Icon(Icons.edit),
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
ContactEditor(setting: mainModel.setting)),
|
||||
);
|
||||
},
|
||||
)
|
||||
: Container()
|
||||
],
|
||||
),
|
||||
body: ListView(
|
||||
children: <Widget>[
|
||||
Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(top: 5.0, bottom: 5),
|
||||
child: LocalText(
|
||||
context,
|
||||
"contact.title",
|
||||
fontSize: 25,
|
||||
)),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 15.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children:
|
||||
List.generate(mainModel.setting.phones.length, (index) {
|
||||
return link(mainModel.setting.phones[index], Icons.phone,
|
||||
onTap: () => _call(mainModel.setting.phones[index]));
|
||||
}),
|
||||
),
|
||||
),
|
||||
link(mainModel.setting.deliveryPhone, Icons.phone_forwarded,
|
||||
onTap: () => _call(mainModel.setting.deliveryPhone),
|
||||
label: LocalText(context, "contact.delivery.phone")),
|
||||
link(mainModel.setting.email, Icons.email,
|
||||
onTap: () => _email(mainModel.setting.email)),
|
||||
link(mainModel.setting.facebook, FontAwesomeIcons.facebook,
|
||||
onTap: () => _openLink(mainModel.setting.facebook)),
|
||||
link(mainModel.setting.website, FontAwesomeIcons.chrome,
|
||||
onTap: () => _openLink(mainModel.setting.website)),
|
||||
link(mainModel.setting.address, Icons.location_on),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
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,
|
||||
color: primaryColor,
|
||||
),
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
label == null ? Container() : label,
|
||||
Text(
|
||||
text == null ? "" : text,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 5,
|
||||
),
|
||||
],
|
||||
),
|
||||
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"]);
|
||||
}
|
||||
}
|
||||
274
lib/pages/contact_editor.dart
Normal file
274
lib/pages/contact_editor.dart
Normal file
@@ -0,0 +1,274 @@
|
||||
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/pages/util.dart';
|
||||
import 'package:fcs/theme/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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
521
lib/pages/delivery/delivery_item.dart
Normal file
521
lib/pages/delivery/delivery_item.dart
Normal file
@@ -0,0 +1,521 @@
|
||||
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/theme/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 '../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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
274
lib/pages/delivery/delivery_list.dart
Normal file
274
lib/pages/delivery/delivery_list.dart
Normal file
@@ -0,0 +1,274 @@
|
||||
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/pages/util.dart';
|
||||
import 'package:fcs/theme/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 {
|
||||
@override
|
||||
_DeliveryListState createState() => _DeliveryListState();
|
||||
}
|
||||
|
||||
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;
|
||||
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
|
||||
void dispose() {
|
||||
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",
|
||||
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;
|
||||
}
|
||||
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),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
194
lib/pages/device_list.dart
Normal file
194
lib/pages/device_list.dart
Normal file
@@ -0,0 +1,194 @@
|
||||
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 '../theme/theme.dart';
|
||||
import '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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
904
lib/pages/do/do_approve.dart
Normal file
904
lib/pages/do/do_approve.dart
Normal file
@@ -0,0 +1,904 @@
|
||||
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/theme/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 '../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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
650
lib/pages/do/do_creation_form.dart
Normal file
650
lib/pages/do/do_creation_form.dart
Normal file
@@ -0,0 +1,650 @@
|
||||
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/pages/util.dart';
|
||||
import 'package:fcs/theme/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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
423
lib/pages/do/do_creation_todelete.dart
Normal file
423
lib/pages/do/do_creation_todelete.dart
Normal file
@@ -0,0 +1,423 @@
|
||||
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/pages/util.dart';
|
||||
import 'package:fcs/theme/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;
|
||||
});
|
||||
}
|
||||
}
|
||||
26
lib/pages/do/do_files.dart
Normal file
26
lib/pages/do/do_files.dart
Normal file
@@ -0,0 +1,26 @@
|
||||
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;
|
||||
}
|
||||
286
lib/pages/do/do_list.dart
Normal file
286
lib/pages/do/do_list.dart
Normal file
@@ -0,0 +1,286 @@
|
||||
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/pages/util.dart';
|
||||
import 'package:fcs/theme/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),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
134
lib/pages/do/do_product_item.dart
Normal file
134
lib/pages/do/do_product_item.dart
Normal file
@@ -0,0 +1,134 @@
|
||||
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/pages/util.dart';
|
||||
import 'package:fcs/theme/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);
|
||||
}
|
||||
}
|
||||
209
lib/pages/do/do_storage_item.dart
Normal file
209
lib/pages/do/do_storage_item.dart
Normal file
@@ -0,0 +1,209 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/storage_model.dart';
|
||||
import 'package:fcs/theme/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);
|
||||
}
|
||||
}
|
||||
165
lib/pages/do/photo_page.dart
Normal file
165
lib/pages/do/photo_page.dart
Normal file
@@ -0,0 +1,165 @@
|
||||
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/theme/theme.dart';
|
||||
import 'package:fcs/widget/localization/app_translations.dart';
|
||||
|
||||
import '../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;
|
||||
});
|
||||
}
|
||||
}
|
||||
158
lib/pages/do/po_selection.dart
Normal file
158
lib/pages/do/po_selection.dart
Normal file
@@ -0,0 +1,158 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:fcs/theme/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]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
87
lib/pages/document_log_page.dart
Normal file
87
lib/pages/document_log_page.dart
Normal file
@@ -0,0 +1,87 @@
|
||||
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/theme/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();
|
||||
}
|
||||
}
|
||||
158
lib/pages/email_page.dart
Normal file
158
lib/pages/email_page.dart
Normal file
@@ -0,0 +1,158 @@
|
||||
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/theme/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 '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, "/home", (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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
370
lib/pages/employee_editor.dart
Normal file
370
lib/pages/employee_editor.dart
Normal file
@@ -0,0 +1,370 @@
|
||||
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/log_model.dart';
|
||||
import 'package:fcs/model/main_model.dart';
|
||||
import 'package:fcs/model/user_model.dart';
|
||||
import 'package:fcs/theme/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 'document_log_page.dart';
|
||||
import 'util.dart';
|
||||
|
||||
typedef void FindCallBack();
|
||||
|
||||
class EmployeeEditor extends StatefulWidget {
|
||||
final User employee;
|
||||
const EmployeeEditor({this.employee});
|
||||
@override
|
||||
_EmployeeEditorState createState() => _EmployeeEditorState();
|
||||
}
|
||||
|
||||
class _EmployeeEditorState extends State<EmployeeEditor> {
|
||||
TextEditingController _name = new TextEditingController();
|
||||
TextEditingController _phone = new TextEditingController();
|
||||
TextEditingController _phoneInput = 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).getPrivileges;
|
||||
if (widget.employee != null) {
|
||||
_name.text = widget.employee.name;
|
||||
_phone.text = widget.employee.phone;
|
||||
privileges.forEach((p) => widget.employee.privilegeIds.contains(p.id)
|
||||
? p.isChecked = true
|
||||
: p.isChecked = false);
|
||||
} else {
|
||||
privileges.forEach((p) => p.isChecked = false);
|
||||
}
|
||||
}
|
||||
|
||||
Widget showprivilegeList(BuildContext context, UserModel userModel) {
|
||||
return Container(
|
||||
width: 300,
|
||||
height: 300,
|
||||
child: ListView.builder(
|
||||
itemCount: privileges.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return new ListTile(
|
||||
title: new Row(
|
||||
children: <Widget>[
|
||||
new Checkbox(
|
||||
value: privileges[index].isChecked == null
|
||||
? false
|
||||
: privileges[index].isChecked,
|
||||
activeColor: primaryColor,
|
||||
onChanged: (bool value) {
|
||||
setState(() {
|
||||
privileges[index].isChecked = value;
|
||||
});
|
||||
}),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
new Text(
|
||||
userModel.getPrivileges[index].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]),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
));
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
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)),
|
||||
),
|
||||
validator: (value) {
|
||||
if (value.isEmpty) {
|
||||
return "Please enter phone number";
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
new FlatButton(
|
||||
onPressed: () {
|
||||
if (!_formKey.currentState.validate()) return;
|
||||
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 saveButton = Container(
|
||||
padding: EdgeInsets.only(
|
||||
left: 24.0,
|
||||
right: 24.0,
|
||||
),
|
||||
child: Container(
|
||||
height: 45.0,
|
||||
color: primaryColor,
|
||||
child: ButtonTheme(
|
||||
minWidth: 900.0,
|
||||
height: 100.0,
|
||||
child: FlatButton(
|
||||
onPressed: _save,
|
||||
child: LocalText(
|
||||
context,
|
||||
'employee.save',
|
||||
color: Colors.white,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
final addButton = Container(
|
||||
padding: EdgeInsets.only(
|
||||
left: 24.0,
|
||||
right: 24.0,
|
||||
),
|
||||
child: Container(
|
||||
height: 45.0,
|
||||
color: primaryColor,
|
||||
child: ButtonTheme(
|
||||
minWidth: 900.0,
|
||||
height: 100.0,
|
||||
child: FlatButton(
|
||||
onPressed: () {
|
||||
if (!_formKey.currentState.validate()) return;
|
||||
_add(context);
|
||||
},
|
||||
child: LocalText(
|
||||
context,
|
||||
'employee.add',
|
||||
color: Colors.white,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: primaryColor,
|
||||
title: LocalText(
|
||||
context,
|
||||
"employee.item.title",
|
||||
fontSize: 20,
|
||||
color: Colors.white,
|
||||
),
|
||||
actions: <Widget>[
|
||||
widget.employee == null || !mainModel.showHistoryBtn()
|
||||
? Container()
|
||||
: IconButton(
|
||||
icon: Icon(Icons.history),
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => DocumentLogPage(
|
||||
docID: widget.employee.docID)),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
body: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
padding: EdgeInsets.only(left: 24.0, right: 24.0),
|
||||
children: <Widget>[
|
||||
widget.employee == null
|
||||
? phoneInputbox(context, () => _findUser(context))
|
||||
: phoneNumberBox,
|
||||
widget.employee == null
|
||||
? this.isSend ? namebox : Container()
|
||||
: namebox,
|
||||
showprivilegeList(context, userModel)
|
||||
],
|
||||
),
|
||||
),
|
||||
widget.employee == null ? addButton : saveButton,
|
||||
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.employee == null) return;
|
||||
var employeeModel = Provider.of<EmployeeModel>(context);
|
||||
try {
|
||||
await employeeModel.updatePrivileges(
|
||||
widget.employee.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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
128
lib/pages/employee_list.dart
Normal file
128
lib/pages/employee_list.dart
Normal file
@@ -0,0 +1,128 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import 'package:progress/progress.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/employee_model.dart';
|
||||
import 'package:fcs/model/language_model.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/widget/localization/app_translations.dart';
|
||||
import 'package:fcs/widget/progress.dart';
|
||||
|
||||
import 'employee_editor.dart';
|
||||
|
||||
class EmployeeList extends StatefulWidget {
|
||||
@override
|
||||
_EmployeeListState createState() => _EmployeeListState();
|
||||
}
|
||||
|
||||
class _EmployeeListState extends State<EmployeeList> {
|
||||
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 employeeModel = Provider.of<EmployeeModel>(context);
|
||||
var languageModle = Provider.of<LanguageModel>(context);
|
||||
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: primaryColor,
|
||||
title: Text(
|
||||
AppTranslations.of(context).text("employee.title"),
|
||||
style: languageModle.isEng
|
||||
? TextStyle()
|
||||
: TextStyle(fontFamily: 'MyanmarUnicode'),
|
||||
),
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
backgroundColor: primaryColor,
|
||||
child: Icon(Icons.add),
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => EmployeeEditor()),
|
||||
);
|
||||
},
|
||||
),
|
||||
body: new ListView.builder(
|
||||
padding: EdgeInsets.only(left: 15, right: 15, top: 15),
|
||||
shrinkWrap: true,
|
||||
itemCount: employeeModel.employees.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => EmployeeEditor(
|
||||
employee: employeeModel.employees[index],
|
||||
)),
|
||||
);
|
||||
},
|
||||
child: Card(
|
||||
elevation: 10,
|
||||
color: Colors.white,
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
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: Image.asset(
|
||||
"assets/employee.png",
|
||||
width: 40,
|
||||
height: 40,
|
||||
color: primaryColor,
|
||||
),
|
||||
),
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
employeeModel.employees[index].name == null
|
||||
? ""
|
||||
: employeeModel.employees[index].name,
|
||||
style: new TextStyle(
|
||||
fontSize: 17.0, color: Colors.black),
|
||||
),
|
||||
Text(
|
||||
employeeModel.employees[index].phone ==
|
||||
null
|
||||
? ""
|
||||
: employeeModel
|
||||
.employees[index].phone,
|
||||
style: new TextStyle(
|
||||
fontSize: 14.0, color: Colors.grey),
|
||||
),
|
||||
employeeModel.employees[index].device == null
|
||||
? Text("No login",
|
||||
style: TextStyle(color: Colors.red))
|
||||
: Text("last active",
|
||||
style: TextStyle(color: Colors.green)),
|
||||
Text(
|
||||
"${employeeModel.employees[index].device == null ? "" : employeeModel.employees[index].device}",
|
||||
style: TextStyle(fontSize: 11)),
|
||||
Text(
|
||||
"${employeeModel.employees[index].lastActiveTime == null ? "" : dateFormatter.format(employeeModel.employees[index].lastActiveTime)}")
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
126
lib/pages/forget_password.dart
Normal file
126
lib/pages/forget_password.dart
Normal file
@@ -0,0 +1,126 @@
|
||||
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/theme/theme.dart';
|
||||
import 'package:fcs/widget/local_text.dart';
|
||||
import 'package:fcs/widget/localization/app_translations.dart';
|
||||
import 'package:fcs/widget/progress.dart';
|
||||
|
||||
import '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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
119
lib/pages/help.dart
Normal file
119
lib/pages/help.dart
Normal file
@@ -0,0 +1,119 @@
|
||||
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;
|
||||
// },
|
||||
// ),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
469
lib/pages/home_page.dart
Normal file
469
lib/pages/home_page.dart
Normal file
@@ -0,0 +1,469 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.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 'package:fcs/charts/bar_chart.dart';
|
||||
import 'package:fcs/charts/delivery_do_line.dart';
|
||||
import 'package:fcs/charts/delivery_do_summary.dart';
|
||||
import 'package:fcs/charts/delivery_line.dart';
|
||||
import 'package:fcs/charts/delivery_summary.dart';
|
||||
import 'package:fcs/charts/do_line.dart';
|
||||
import 'package:fcs/charts/po_balance_chart.dart';
|
||||
import 'package:fcs/charts/po_line.dart';
|
||||
import 'package:fcs/charts/quota.dart';
|
||||
import 'package:fcs/charts/revenue_line.dart';
|
||||
import 'package:fcs/model/language_model.dart';
|
||||
import 'package:fcs/model/main_model.dart';
|
||||
import 'package:fcs/model/notification_model.dart';
|
||||
import 'package:fcs/model/product_model.dart';
|
||||
import 'package:fcs/pages/banks/banks.dart';
|
||||
import 'package:fcs/pages/buyer_list.dart';
|
||||
import 'package:fcs/pages/contact.dart';
|
||||
import 'package:fcs/pages/delivery/delivery_list.dart';
|
||||
import 'package:fcs/pages/manual/manual_page.dart';
|
||||
import 'package:fcs/pages/my_registeration_info.dart';
|
||||
import 'package:fcs/pages/notification_list.dart';
|
||||
import 'package:fcs/pages/pin_login_dialog.dart';
|
||||
import 'package:fcs/pages/settings.dart';
|
||||
import 'package:fcs/pages/term.dart';
|
||||
import 'package:fcs/pages/test_list.dart';
|
||||
import 'package:fcs/pages/util.dart';
|
||||
import 'package:fcs/reports/report_list.dart';
|
||||
import 'package:fcs/vo/user.dart';
|
||||
import 'package:fcs/widget/badge.dart';
|
||||
import 'package:fcs/widget/banner.dart';
|
||||
import 'package:fcs/widget/local_text.dart';
|
||||
import 'package:fcs/widget/localization/app_translations.dart';
|
||||
import 'package:fcs/widget/offline_redirect.dart';
|
||||
import 'package:fcs/vo/notification.dart' as Noti;
|
||||
|
||||
import '../theme/theme.dart';
|
||||
import 'announcement_list.dart';
|
||||
import 'do/do_list.dart';
|
||||
import 'employee_list.dart';
|
||||
import 'my_registeration.dart';
|
||||
import 'pd/pd_list.dart';
|
||||
import 'po/po_submission_list.dart';
|
||||
import 'products_list.dart';
|
||||
import 'profile_page.dart';
|
||||
import 'storage/storage_list.dart';
|
||||
import 'user_list.dart';
|
||||
|
||||
final msgLog = Logger('backgroundMessageHandler');
|
||||
|
||||
class HomePage extends StatefulWidget {
|
||||
@override
|
||||
_HomePageState createState() => _HomePageState();
|
||||
}
|
||||
|
||||
typedef BtnCallback();
|
||||
|
||||
class _HomePageState extends State<HomePage> {
|
||||
final log = Logger('_HomePageState');
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
static final List<String> chartDropdownItems = [
|
||||
'Last 7 days',
|
||||
'Last month',
|
||||
'Last three months'
|
||||
];
|
||||
String actualDropdown = chartDropdownItems[0];
|
||||
int actualChart = 0;
|
||||
final numberFormatter = new NumberFormat("#,###");
|
||||
|
||||
String pin;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final helpBtn = _buildBtn("manual.title",
|
||||
imgIcon: Image.asset(
|
||||
"assets/manual.png",
|
||||
width: 40,
|
||||
height: 40,
|
||||
color: primaryColor,
|
||||
),
|
||||
btnCallback: () => Navigator.of(context)
|
||||
.push(MaterialPageRoute(builder: (_) => ManualPage()))
|
||||
// btnCallback: () => Navigator.of(context)
|
||||
// .push(MaterialPageRoute(builder: (_) => TestList()))
|
||||
);
|
||||
final announcementBtn = _buildBtn("announcement.title",
|
||||
icon: Icons.announcement,
|
||||
btnCallback: () => Navigator.of(context)
|
||||
.push(MaterialPageRoute(builder: (_) => AnnouncementList())));
|
||||
|
||||
final buyerBtn = _buildBtn("buyer.title",
|
||||
imgIcon: Image.asset(
|
||||
"assets/buyer.png",
|
||||
width: 40,
|
||||
height: 40,
|
||||
color: primaryColor,
|
||||
),
|
||||
btnCallback: () => Navigator.of(context)
|
||||
.push(MaterialPageRoute(builder: (_) => BuyerList())));
|
||||
|
||||
final reportBtn = _buildBtn("report.title",
|
||||
imgIcon: Image.asset(
|
||||
"assets/report.png",
|
||||
width: 50,
|
||||
height: 50,
|
||||
// color: primaryColor,
|
||||
),
|
||||
btnCallback: () => Navigator.of(context)
|
||||
.push(MaterialPageRoute(builder: (_) => ReportList())));
|
||||
|
||||
final myRegBtn = _buildBtn("myreg.title",
|
||||
imgIcon: Image.asset(
|
||||
"assets/reg.png",
|
||||
width: 40,
|
||||
height: 30,
|
||||
color: primaryColor,
|
||||
),
|
||||
btnCallback: () async {});
|
||||
|
||||
final posBtn = _buildBtn("po.title",
|
||||
imgIcon: Image.asset(
|
||||
"assets/pay.png",
|
||||
width: 40,
|
||||
height: 40,
|
||||
color: primaryColor,
|
||||
),
|
||||
btnCallback: () {});
|
||||
|
||||
final dosBtn = _buildBtn("do.title",
|
||||
imgIcon: Image.asset(
|
||||
"assets/do.png",
|
||||
width: 40,
|
||||
height: 40,
|
||||
color: primaryColor,
|
||||
),
|
||||
btnCallback: () => Navigator.of(context)
|
||||
.push(MaterialPageRoute(builder: (_) => DOList())));
|
||||
|
||||
final deliveryBtn = _buildBtn("delivery.title",
|
||||
imgIcon: Image.asset(
|
||||
"assets/truck.png",
|
||||
width: 50,
|
||||
height: 50,
|
||||
color: primaryColor,
|
||||
),
|
||||
btnCallback: () => Navigator.of(context)
|
||||
.push(MaterialPageRoute(builder: (_) => DeliveryList())));
|
||||
|
||||
final storageBtn = _buildBtn("storage.title",
|
||||
imgIcon: Image.asset(
|
||||
"assets/inventory.png",
|
||||
width: 40,
|
||||
height: 40,
|
||||
color: primaryColor,
|
||||
),
|
||||
btnCallback: () => Navigator.of(context)
|
||||
.push(MaterialPageRoute(builder: (_) => StorageList())));
|
||||
|
||||
final pdosBtn = _buildBtn("pd.title",
|
||||
imgIcon: Image.asset(
|
||||
"assets/pdo.png",
|
||||
width: 40,
|
||||
height: 40,
|
||||
color: primaryColor,
|
||||
),
|
||||
btnCallback: () => Navigator.of(context)
|
||||
.push(MaterialPageRoute(builder: (_) => PDList())));
|
||||
|
||||
final employeeBtn = _buildBtn("employee.title",
|
||||
imgIcon: Image.asset(
|
||||
"assets/employee.png",
|
||||
width: 40,
|
||||
height: 40,
|
||||
color: primaryColor,
|
||||
),
|
||||
btnCallback: () => Navigator.of(context)
|
||||
.push(MaterialPageRoute(builder: (_) => EmployeeList())));
|
||||
|
||||
final termBtn = _buildBtn("term.title",
|
||||
imgIcon: Image.asset(
|
||||
"assets/term.png",
|
||||
width: 40,
|
||||
height: 30,
|
||||
color: primaryColor,
|
||||
), btnCallback: () {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => Term()));
|
||||
});
|
||||
|
||||
final userBtn =
|
||||
_buildBtn("users.title", icon: Icons.group, btnCallback: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => UserList()),
|
||||
);
|
||||
});
|
||||
|
||||
final settingsBtn =
|
||||
_buildBtn("setting.title", icon: Icons.settings, btnCallback: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => Settings()),
|
||||
);
|
||||
});
|
||||
|
||||
final _bankAccountsBtn = _buildBtn("banks.title",
|
||||
icon: FontAwesomeIcons.moneyCheck, btnCallback: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => BankAccounts()),
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
List<Widget> widgets = [helpBtn];
|
||||
widgets.add(announcementBtn);
|
||||
widgets.add(reportBtn);
|
||||
widgets.add(termBtn);
|
||||
widgets.add(_bankAccountsBtn);
|
||||
|
||||
var revenueChart = Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
children: <Widget>[RevenueLineChart()],
|
||||
));
|
||||
|
||||
var productListBox = Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
LocalText(context, 'products.prices', color: primaryColor),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
LocalText(context, 'products.gas',
|
||||
color: Colors.black,
|
||||
fontSize: 19,
|
||||
fontWeight: FontWeight.w700),
|
||||
Consumer<ProductModel>(builder: (context, model, child) {
|
||||
return Text(' ${model.products.length}',
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 19.0));
|
||||
}),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
Material(
|
||||
color: thirdColor,
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(5.0),
|
||||
child: //Icon(Icons.timeline, color: Colors.white, size: 30.0),
|
||||
Image.asset(
|
||||
"assets/product.png",
|
||||
width: 60,
|
||||
height: 70,
|
||||
color: Colors.white,
|
||||
))))
|
||||
]);
|
||||
|
||||
var poqtyByProductChart = Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
children: <Widget>[BarChart()],
|
||||
));
|
||||
|
||||
var poChart = Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
children: <Widget>[POLineChart()],
|
||||
));
|
||||
|
||||
var doChart = Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
children: <Widget>[DOLineChart()],
|
||||
));
|
||||
|
||||
var deliveryChart = Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
children: <Widget>[DeliveryBarChart()],
|
||||
));
|
||||
|
||||
var deliveryDOChart = Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
children: <Widget>[DODeliveryLineChart()],
|
||||
));
|
||||
|
||||
var deliveryDoSummary = Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
children: <Widget>[DeliveryDoSummaryChart()],
|
||||
));
|
||||
var deliverySummary = Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
children: <Widget>[DeliverySummary()],
|
||||
));
|
||||
var poBalancebyBuyerChart = Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
children: <Widget>[POBalanceChart()],
|
||||
));
|
||||
List<Widget> chartWidgets = [];
|
||||
|
||||
return OfflineRedirect(
|
||||
child: FlavorBanner(
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: primaryColor,
|
||||
title: ClipRRect(
|
||||
child: Image.asset("assets/logo.png", height: 40),
|
||||
borderRadius: new BorderRadius.circular(15.0),
|
||||
),
|
||||
actions: <Widget>[
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => Contact()),
|
||||
);
|
||||
},
|
||||
iconSize: 30,
|
||||
icon: Icon(Icons.phone),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => Profile()),
|
||||
);
|
||||
},
|
||||
iconSize: 30,
|
||||
icon: Icon(Icons.account_circle),
|
||||
),
|
||||
]),
|
||||
body: StaggeredGridView.count(
|
||||
crossAxisCount: 3,
|
||||
crossAxisSpacing: 12.0,
|
||||
mainAxisSpacing: 12.0,
|
||||
padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
|
||||
children: <Widget>[
|
||||
_buildTile(
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: productListBox,
|
||||
),
|
||||
onTap: () => Navigator.of(context)
|
||||
.push(MaterialPageRoute(builder: (_) => ProductsList())),
|
||||
),
|
||||
new GridView.count(
|
||||
shrinkWrap: true,
|
||||
scrollDirection: Axis.horizontal,
|
||||
crossAxisCount: 1,
|
||||
crossAxisSpacing: 12,
|
||||
mainAxisSpacing: 12,
|
||||
padding: EdgeInsets.only(bottom: 5, left: 10, right: 5),
|
||||
children: widgets,
|
||||
),
|
||||
_buildTile(
|
||||
PageView(
|
||||
children: chartWidgets,
|
||||
),
|
||||
),
|
||||
],
|
||||
staggeredTiles: [
|
||||
StaggeredTile.extent(5, 110.0),
|
||||
StaggeredTile.extent(3, 110.0),
|
||||
StaggeredTile.extent(3, 250.0),
|
||||
],
|
||||
)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTile(Widget child, {Function() onTap}) {
|
||||
return Material(
|
||||
elevation: 30.0,
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
shadowColor: Color(0x802196F3),
|
||||
child: InkWell(
|
||||
onTap: onTap != null
|
||||
? () => onTap()
|
||||
: () {
|
||||
log.info('Not set yet');
|
||||
},
|
||||
child: child));
|
||||
}
|
||||
|
||||
Widget _buildBtn(String title,
|
||||
{Image imgIcon, IconData icon, BtnCallback btnCallback}) {
|
||||
var languageModel = Provider.of<LanguageModel>(context);
|
||||
return _buildTile(
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(5.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
icon != null
|
||||
? Material(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: Icon(icon, color: primaryColor, size: 35.0)))
|
||||
: Container(
|
||||
padding: EdgeInsets.only(top: 3),
|
||||
child: imgIcon,
|
||||
),
|
||||
Padding(padding: EdgeInsets.only(bottom: 10.0)),
|
||||
Text(AppTranslations.of(context).text(title),
|
||||
style: languageModel.isEng
|
||||
? TextStyle(
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 12.0)
|
||||
: TextStyle(
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 12.0,
|
||||
fontFamily: "MyanmarUnicode")),
|
||||
]),
|
||||
),
|
||||
onTap: btnCallback,
|
||||
);
|
||||
}
|
||||
|
||||
_showNotifications() async {
|
||||
if (!super.mounted) return;
|
||||
await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => NotificationList()),
|
||||
);
|
||||
// Provider.of<NotificationModel>(context, listen: false).seen();
|
||||
}
|
||||
}
|
||||
97
lib/pages/log_list.dart
Normal file
97
lib/pages/log_list.dart
Normal file
@@ -0,0 +1,97 @@
|
||||
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 '../theme/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),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
757
lib/pages/login_page.dart
Normal file
757
lib/pages/login_page.dart
Normal file
@@ -0,0 +1,757 @@
|
||||
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 '../theme/theme.dart' as Theme;
|
||||
import 'forget_password.dart';
|
||||
import 'sms_page.dart';
|
||||
import '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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
227
lib/pages/manual/instruction_data_page.dart
Normal file
227
lib/pages/manual/instruction_data_page.dart
Normal file
@@ -0,0 +1,227 @@
|
||||
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/theme/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);
|
||||
}
|
||||
}
|
||||
78
lib/pages/manual/manual_item_title_dialog.dart
Normal file
78
lib/pages/manual/manual_item_title_dialog.dart
Normal file
@@ -0,0 +1,78 @@
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
234
lib/pages/manual/manual_page.dart
Normal file
234
lib/pages/manual/manual_page.dart
Normal file
@@ -0,0 +1,234 @@
|
||||
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/pages/util.dart';
|
||||
import 'package:fcs/vo/manual.dart';
|
||||
import 'package:fcs/widget/local_text.dart';
|
||||
import 'package:fcs/widget/progress.dart';
|
||||
|
||||
import 'slide_page.dart';
|
||||
|
||||
class ManualPage extends StatefulWidget {
|
||||
@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(
|
||||
backgroundColor: Colors.white,
|
||||
actions: <Widget>[
|
||||
mainModel.isSysAdmin()
|
||||
? Row(
|
||||
children: <Widget>[
|
||||
Text(
|
||||
versionName,
|
||||
style: TextStyle(fontSize: 15, color: Colors.black),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.refresh),
|
||||
color: Colors.blue,
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
manualModel.resetManualItems();
|
||||
helpList =
|
||||
manualModel.getHelpList(mainModel.isBuyer());
|
||||
});
|
||||
}),
|
||||
IconButton(
|
||||
icon: Icon(Icons.cloud_upload),
|
||||
color: Colors.blue,
|
||||
onPressed: () {
|
||||
_inputManualVersion(context, manualModel);
|
||||
})
|
||||
],
|
||||
)
|
||||
: Container(),
|
||||
],
|
||||
iconTheme: IconThemeData(
|
||||
color: Colors.grey,
|
||||
),
|
||||
centerTitle: true,
|
||||
title: Stack(
|
||||
children: <Widget>[
|
||||
LocalText(
|
||||
context,
|
||||
"manual.title",
|
||||
fontSize: 25,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
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'))
|
||||
],
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
123
lib/pages/manual/moveable_stack_item.dart
Normal file
123
lib/pages/manual/moveable_stack_item.dart
Normal file
@@ -0,0 +1,123 @@
|
||||
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'))
|
||||
],
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
210
lib/pages/manual/slide_data_page.dart
Normal file
210
lib/pages/manual/slide_data_page.dart
Normal file
@@ -0,0 +1,210 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
249
lib/pages/manual/slide_page.dart
Normal file
249
lib/pages/manual/slide_page.dart
Normal file
@@ -0,0 +1,249 @@
|
||||
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)),
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
||||
316
lib/pages/my_registeration.dart
Normal file
316
lib/pages/my_registeration.dart
Normal file
@@ -0,0 +1,316 @@
|
||||
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/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 '../theme/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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
117
lib/pages/my_registeration_info.dart
Normal file
117
lib/pages/my_registeration_info.dart
Normal file
@@ -0,0 +1,117 @@
|
||||
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 '../theme/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()
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
194
lib/pages/my_registeration_item.dart
Normal file
194
lib/pages/my_registeration_item.dart
Normal file
@@ -0,0 +1,194 @@
|
||||
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/theme/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);
|
||||
}
|
||||
}
|
||||
205
lib/pages/notification_list.dart
Normal file
205
lib/pages/notification_list.dart
Normal file
@@ -0,0 +1,205 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/notification_model.dart';
|
||||
import 'package:fcs/pages/util.dart';
|
||||
import 'package:fcs/vo/notification.dart' as Noti;
|
||||
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 '../theme/theme.dart';
|
||||
|
||||
class NotificationList extends StatefulWidget {
|
||||
@override
|
||||
_NotificationListState createState() => _NotificationListState();
|
||||
}
|
||||
|
||||
class _NotificationListState extends State<NotificationList> {
|
||||
var timeFormatter = new DateFormat('KK:mm a');
|
||||
var dateFormatter = new DateFormat('dd MMM');
|
||||
final double dotSize = 15.0;
|
||||
int _selectedIndex = 0;
|
||||
bool _isLoading = false;
|
||||
bool _isClicked = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
NotificationModel notificationModel =
|
||||
Provider.of<NotificationModel>(context);
|
||||
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: primaryColor,
|
||||
title: Text(AppTranslations.of(context).text("noti.title")),
|
||||
actions: <Widget>[
|
||||
PopupMenuButton<PopupMenu>(
|
||||
elevation: 3.2,
|
||||
onSelected: (selected) {
|
||||
setState(() {
|
||||
this._selectedIndex = selected.index;
|
||||
});
|
||||
notificationModel.filter(selected.index);
|
||||
},
|
||||
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 notificationMenu.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.separated(
|
||||
separatorBuilder: (context, index) => Divider(
|
||||
color: Colors.black,
|
||||
),
|
||||
scrollDirection: Axis.vertical,
|
||||
padding: EdgeInsets.only(left: 15, right: 15, top: 15),
|
||||
shrinkWrap: true,
|
||||
itemCount: notificationModel.notis.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
Noti.Notification noti = notificationModel.notis[index];
|
||||
return Stack(
|
||||
children: <Widget>[
|
||||
InkWell(
|
||||
onTap: () => _display(noti),
|
||||
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: Icon(Icons.message),
|
||||
),
|
||||
new Expanded(
|
||||
child: new Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
new Text(
|
||||
noti.getDesc,
|
||||
style: new TextStyle(
|
||||
fontSize: 15.0,
|
||||
color: Colors.black),
|
||||
),
|
||||
new Text(
|
||||
noti.itemType == "reg"
|
||||
? ""
|
||||
: noti.itemNumber,
|
||||
style: new TextStyle(
|
||||
fontSize: 13.0, color: Colors.grey),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(timeFormatter.format(noti.time)),
|
||||
),
|
||||
noti.fromToday()
|
||||
? Container()
|
||||
: Text(dateFormatter.format(noti.time)),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
noti.seen
|
||||
? Container()
|
||||
: new Positioned(
|
||||
left: 11,
|
||||
top: 11,
|
||||
child: new Container(
|
||||
padding: EdgeInsets.all(2),
|
||||
decoration: new BoxDecoration(
|
||||
color: Colors.red,
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
constraints: BoxConstraints(
|
||||
minWidth: 18,
|
||||
minHeight: 18,
|
||||
),
|
||||
child: Text(
|
||||
'new',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 14,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_display(Noti.Notification noti) async {
|
||||
if (_isClicked) return;
|
||||
_isClicked = true;
|
||||
await displayNotiContent(context, noti);
|
||||
_isClicked = false;
|
||||
}
|
||||
}
|
||||
33
lib/pages/offline.dart
Normal file
33
lib/pages/offline.dart
Normal file
@@ -0,0 +1,33 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:fcs/widget/local_text.dart';
|
||||
|
||||
|
||||
class Offline extends StatefulWidget {
|
||||
@override
|
||||
_OfflineState createState() => _OfflineState();
|
||||
}
|
||||
|
||||
class _OfflineState extends State<Offline> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final retryButton = Card(
|
||||
elevation: 23,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Icon(Icons.offline_bolt),
|
||||
LocalText(context, "offline.status"),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return Scaffold(
|
||||
body: Center(child: retryButton),
|
||||
);
|
||||
}
|
||||
}
|
||||
227
lib/pages/pd/pd_form.dart
Normal file
227
lib/pages/pd/pd_form.dart
Normal file
@@ -0,0 +1,227 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.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/pd_model.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/vo/pd.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/progress.dart';
|
||||
import '../util.dart';
|
||||
import 'pd_item.dart';
|
||||
|
||||
class PDForm extends StatefulWidget {
|
||||
final PD pd;
|
||||
const PDForm({Key key, this.pd}) : super(key: key);
|
||||
@override
|
||||
_PDFormState createState() => _PDFormState();
|
||||
}
|
||||
|
||||
class _PDFormState extends State<PDForm> {
|
||||
TextEditingController _date = new TextEditingController();
|
||||
TextEditingController _userName = new TextEditingController();
|
||||
|
||||
PD pd = PD();
|
||||
bool _isLoading = false;
|
||||
bool isNew = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (widget.pd != null) {
|
||||
_userName.text = widget.pd.userName;
|
||||
} else {
|
||||
var mainModel = Provider.of<MainModel>(context, listen: false);
|
||||
_userName.text = mainModel.user.name;
|
||||
}
|
||||
|
||||
_load();
|
||||
}
|
||||
|
||||
_load() async {
|
||||
if (widget.pd != null) {
|
||||
this.pd = widget.pd;
|
||||
_date.text = DateFormat('dd MMM yyyy – hh:mm a').format(widget.pd.date);
|
||||
isNew = false;
|
||||
Provider.of<PDModel>(context, listen: false).loadPDLines(pd).then((_pd) {
|
||||
setState(() {
|
||||
this.pd = _pd;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var languageModel = Provider.of<LanguageModel>(context);
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
|
||||
final dateBox = Container(
|
||||
padding: EdgeInsets.only(left: 20, right: 15),
|
||||
child: TextFormField(
|
||||
controller: _date,
|
||||
enabled: false,
|
||||
cursorColor: primaryColor,
|
||||
style: textStyle,
|
||||
decoration: new InputDecoration(
|
||||
labelText: AppTranslations.of(context).text("pd.date"),
|
||||
labelStyle: languageModel.isEng ? labelStyle : labelStyleMM,
|
||||
icon: Icon(
|
||||
Icons.date_range,
|
||||
color: primaryColor,
|
||||
size: 23,
|
||||
)),
|
||||
));
|
||||
|
||||
final nameBox = Container(
|
||||
padding: EdgeInsets.only(left: 20, right: 15),
|
||||
child: TextFormField(
|
||||
controller: _userName,
|
||||
autofocus: false,
|
||||
readOnly: true,
|
||||
style: textStyle,
|
||||
decoration: new InputDecoration(
|
||||
border: InputBorder.none,
|
||||
focusedBorder: InputBorder.none,
|
||||
icon: Icon(
|
||||
Icons.person,
|
||||
color: primaryColor,
|
||||
size: 25,
|
||||
)),
|
||||
));
|
||||
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: primaryColor,
|
||||
title: Text(
|
||||
AppTranslations.of(context).text('pd'),
|
||||
style: languageModel.isEng
|
||||
? TextStyle()
|
||||
: TextStyle(fontFamily: 'MyanmarUnicode'),
|
||||
),
|
||||
actions: <Widget>[
|
||||
isNew
|
||||
? IconButton(
|
||||
icon: Icon(Icons.send),
|
||||
onPressed: () {
|
||||
if (!_formKey.currentState.validate()) return;
|
||||
showConfirmDialog(context, "pd.confirm", () {
|
||||
_submit();
|
||||
});
|
||||
},
|
||||
)
|
||||
: Container()
|
||||
],
|
||||
),
|
||||
floatingActionButton: isNew
|
||||
? FloatingActionButton(
|
||||
backgroundColor: primaryColor,
|
||||
child: Icon(Icons.add),
|
||||
onPressed: () async {
|
||||
final PDLine pdLine = await Navigator.push<PDLine>(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => PDItem()),
|
||||
);
|
||||
_save(pdLine);
|
||||
},
|
||||
)
|
||||
: null,
|
||||
body: Form(
|
||||
key: _formKey,
|
||||
child: ListView(
|
||||
children: <Widget>[
|
||||
isNew ? Container() : dateBox,
|
||||
nameBox,
|
||||
SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: MyDataTable(
|
||||
columns: [
|
||||
MyDataColumn(
|
||||
label: LocalText(context, "pd.product")),
|
||||
MyDataColumn(
|
||||
label: LocalText(context, "pd.storage"),
|
||||
),
|
||||
MyDataColumn(
|
||||
label: LocalText(context, "pd.quantity")),
|
||||
],
|
||||
rows: getProductRow(pd),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
List<MyDataRow> getProductRow(PD pd) {
|
||||
return pd.pdLines.map((p) {
|
||||
return MyDataRow(
|
||||
onSelectChanged: (bool selected) async {
|
||||
if (!isNew) return;
|
||||
var pdLine = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => PDItem(
|
||||
pdLine: p,
|
||||
)),
|
||||
);
|
||||
_save(pdLine);
|
||||
},
|
||||
cells: [
|
||||
MyDataCell(
|
||||
new Text(
|
||||
p.productName,
|
||||
style: textStyle,
|
||||
),
|
||||
),
|
||||
MyDataCell(
|
||||
new Text(p.storageName, style: textStyle),
|
||||
),
|
||||
MyDataCell(
|
||||
new Text(p.quantity.toString(), style: textStyle),
|
||||
),
|
||||
],
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
_save(PDLine pdLine) {
|
||||
if (pdLine == null) return;
|
||||
if (pdLine.action == "create") {
|
||||
if (pd.pdLines.contains(pdLine)) {
|
||||
showMsgDialog(context, "Error", "Duplicate line");
|
||||
return;
|
||||
}
|
||||
pd.pdLines.add(pdLine);
|
||||
} else if (pdLine.action == "delete") {
|
||||
pd.pdLines.remove(pdLine);
|
||||
}
|
||||
}
|
||||
|
||||
_submit() async {
|
||||
if (pd.pdLines.length == 0) {
|
||||
showMsgDialog(context, "Error", "No product line");
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
try {
|
||||
PDModel pdModel = Provider.of<PDModel>(context);
|
||||
await pdModel.createPD(pd);
|
||||
Navigator.pop(context);
|
||||
} catch (e) {
|
||||
showMsgDialog(context, "Error", e.toString());
|
||||
} finally {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
223
lib/pages/pd/pd_item.dart
Normal file
223
lib/pages/pd/pd_item.dart
Normal file
@@ -0,0 +1,223 @@
|
||||
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/product_model.dart';
|
||||
import 'package:fcs/model/storage_model.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/vo/pd.dart';
|
||||
import 'package:fcs/vo/product.dart';
|
||||
import 'package:fcs/vo/storage.dart';
|
||||
import 'package:fcs/widget/localization/app_translations.dart';
|
||||
import 'package:fcs/widget/progress.dart';
|
||||
|
||||
class PDItem extends StatefulWidget {
|
||||
final PDLine pdLine;
|
||||
const PDItem({Key key, this.pdLine}) : super(key: key);
|
||||
@override
|
||||
_PDItemState createState() => _PDItemState();
|
||||
}
|
||||
|
||||
class _PDItemState extends State<PDItem> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
bool _isLoading = false;
|
||||
String currentStorageID;
|
||||
String currentProductID;
|
||||
TextEditingController _quantity = new TextEditingController();
|
||||
PDLine pdLine = PDLine();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (widget.pdLine != null) {
|
||||
this._quantity.text = widget.pdLine.quantity.toString();
|
||||
this.currentProductID = widget.pdLine.productID;
|
||||
this.currentStorageID = widget.pdLine.storageID;
|
||||
this.pdLine = widget.pdLine;
|
||||
this.pdLine.action = "update";
|
||||
} else {
|
||||
this.pdLine.action = "create";
|
||||
}
|
||||
}
|
||||
|
||||
Widget showInventoryList(BuildContext context, StorageModel storageModel) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: <Widget>[
|
||||
Image.asset(
|
||||
"assets/inventory.png",
|
||||
color: primaryColor,
|
||||
width: 25,
|
||||
),
|
||||
SizedBox(
|
||||
width: 20,
|
||||
),
|
||||
new Flexible(
|
||||
child: Container(
|
||||
width: 170.0,
|
||||
child: DropdownButton<String>(
|
||||
value: currentStorageID,
|
||||
isExpanded: true,
|
||||
hint: Text(
|
||||
'Select Storage',
|
||||
style: labelStyle,
|
||||
),
|
||||
onChanged: changedDropDownItem,
|
||||
items: storageModel.storages
|
||||
.map<DropdownMenuItem<String>>((Storage storage) {
|
||||
return new DropdownMenuItem<String>(
|
||||
value: storage.id,
|
||||
child: new Text(storage.name, style: textStyle),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
void changedDropDownItem(selected) {
|
||||
setState(() {
|
||||
currentStorageID = selected;
|
||||
});
|
||||
}
|
||||
|
||||
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) {
|
||||
var storageModel = Provider.of<StorageModel>(context);
|
||||
var productModel = Provider.of<ProductModel>(context);
|
||||
var languageModel = Provider.of<LanguageModel>(context);
|
||||
|
||||
final quantityBox = Container(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: TextFormField(
|
||||
controller: _quantity,
|
||||
keyboardType: TextInputType.number,
|
||||
autofocus: false,
|
||||
cursorColor: primaryColor,
|
||||
decoration: new InputDecoration(
|
||||
labelText: AppTranslations.of(context).text("pd.quantity"),
|
||||
labelStyle: languageModel.isEng ? labelStyle : labelStyleMM,
|
||||
icon: Icon(
|
||||
FontAwesomeIcons.sortNumericUpAlt,
|
||||
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("pd.form.quan");
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: primaryColor,
|
||||
title: Text(
|
||||
AppTranslations.of(context).text('pd.product.title'),
|
||||
style: languageModel.isEng
|
||||
? TextStyle()
|
||||
: TextStyle(fontFamily: 'MyanmarUnicode'),
|
||||
),
|
||||
actions: <Widget>[
|
||||
IconButton(
|
||||
icon: Icon(Icons.delete),
|
||||
onPressed: () {
|
||||
_delete();
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.save),
|
||||
onPressed: () {
|
||||
if (!_formKey.currentState.validate()) return;
|
||||
_save();
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
body: Form(
|
||||
key: _formKey,
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
padding: EdgeInsets.only(left: 24.0, right: 20.0),
|
||||
children: <Widget>[
|
||||
quantityBox,
|
||||
showInventoryList(context, storageModel),
|
||||
showProducts(context, productModel)
|
||||
],
|
||||
),
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
_save() {
|
||||
if (currentProductID == null || currentStorageID == null) return;
|
||||
this.pdLine.storageID = currentStorageID;
|
||||
var storageName =
|
||||
Provider.of<StorageModel>(context).getStorageName(currentStorageID);
|
||||
this.pdLine.storageName = storageName;
|
||||
this.pdLine.productID = currentProductID;
|
||||
var productName =
|
||||
Provider.of<ProductModel>(context).getProductName(currentProductID);
|
||||
this.pdLine.productName = productName;
|
||||
this.pdLine.quantity = int.parse(_quantity.text);
|
||||
Navigator.pop<PDLine>(context, this.pdLine);
|
||||
}
|
||||
|
||||
_delete() {
|
||||
this.pdLine.action = "delete";
|
||||
Navigator.pop<PDLine>(context, this.pdLine);
|
||||
}
|
||||
}
|
||||
187
lib/pages/pd/pd_list.dart
Normal file
187
lib/pages/pd/pd_list.dart
Normal file
@@ -0,0 +1,187 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/language_model.dart';
|
||||
import 'package:fcs/model/pd_model.dart';
|
||||
import 'package:fcs/pages/pd/pd_form.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/widget/localization/app_translations.dart';
|
||||
import 'package:fcs/widget/progress.dart';
|
||||
|
||||
class PDList extends StatefulWidget {
|
||||
@override
|
||||
_PDListState createState() => _PDListState();
|
||||
}
|
||||
|
||||
class _PDListState extends State<PDList> {
|
||||
final double dotSize = 15.0;
|
||||
DateTime _selectedDate = DateTime.now();
|
||||
int _dateIndex = 0;
|
||||
bool _isLoading = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
var pdModel = Provider.of<PDModel>(context, listen: false);
|
||||
// pdModel.loadPDs();
|
||||
_selectedDate = pdModel.selectedDate;
|
||||
_dateIndex = pdModel.dateIndex;
|
||||
}
|
||||
|
||||
Future<Null> _selectDate(BuildContext context) async {
|
||||
var pdModel = Provider.of<PDModel>(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;
|
||||
pdModel.filterDate(_selectedDate, _dateIndex);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var pdModel = Provider.of<PDModel>(context);
|
||||
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: primaryColor,
|
||||
title: Text(
|
||||
AppTranslations.of(context).text('pd.title'),
|
||||
style: Provider.of<LanguageModel>(context).isEng
|
||||
? TextStyle()
|
||||
: TextStyle(fontFamily: 'MyanmarUnicode'),
|
||||
),
|
||||
actions: <Widget>[
|
||||
InkWell(
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(right: 15, 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),
|
||||
),
|
||||
],
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
backgroundColor: primaryColor,
|
||||
child: Icon(Icons.add),
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => PDForm()),
|
||||
);
|
||||
},
|
||||
),
|
||||
body: new ListView.builder(
|
||||
padding: EdgeInsets.only(left: 10, right: 10, top: 15),
|
||||
shrinkWrap: true,
|
||||
itemCount: pdModel.pds.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => PDForm(
|
||||
pd: pdModel.pds[index],
|
||||
)),
|
||||
);
|
||||
},
|
||||
child: Card(
|
||||
elevation: 10,
|
||||
color: Colors.white,
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
new Padding(
|
||||
padding: EdgeInsets.all(10),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
child: Image.asset(
|
||||
"assets/pdo.png",
|
||||
width: 30,
|
||||
color: primaryColor,
|
||||
)),
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: new Text(
|
||||
pdModel.pds[index].date == null
|
||||
? ""
|
||||
: DateFormat('dd MMM yyyy')
|
||||
.format(pdModel.pds[index].date),
|
||||
style: textStyle),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: new Text(
|
||||
pdModel.pds[index].pdNumber == null
|
||||
? ''
|
||||
: pdModel.pds[index].pdNumber,
|
||||
style: new TextStyle(
|
||||
fontSize: 12.0, color: Colors.grey),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
height: 15,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
103
lib/pages/phone_input.dart
Normal file
103
lib/pages/phone_input.dart
Normal file
@@ -0,0 +1,103 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:fcs/pages/util.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/widget/local_text.dart';
|
||||
import 'package:fcs/widget/localization/app_translations.dart';
|
||||
|
||||
class PhoneEditor extends StatefulWidget {
|
||||
@override
|
||||
_PhoneEditorState createState() => _PhoneEditorState();
|
||||
}
|
||||
|
||||
class _PhoneEditorState extends State<PhoneEditor> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
TextEditingController _phone = new TextEditingController();
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_phone.text ='09';
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Center(
|
||||
child: Text(
|
||||
AppTranslations.of(context).text("contact.phone.title"),
|
||||
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: _phone,
|
||||
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
|
||||
cursorColor: primaryColor,
|
||||
decoration: new InputDecoration(
|
||||
fillColor: primaryColor,
|
||||
icon: Icon(
|
||||
Icons.phone,
|
||||
color: primaryColor,
|
||||
),
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.grey, width: 1.0)),
|
||||
),
|
||||
validator: (value) {
|
||||
if (value.isEmpty) {
|
||||
return AppTranslations.of(context)
|
||||
.text("contact.phone.empty");
|
||||
}
|
||||
return null;
|
||||
},
|
||||
))
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: <Widget>[
|
||||
FlatButton(
|
||||
child: LocalText(
|
||||
context,
|
||||
'do.cancel',
|
||||
color: secondaryColor,
|
||||
),
|
||||
onPressed: () {
|
||||
_phone.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() {
|
||||
try {
|
||||
Navigator.pop<String>(context, _phone.text);
|
||||
} catch (e) {
|
||||
showMsgDialog(context, "Error", e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
102
lib/pages/pin_login_dialog.dart
Normal file
102
lib/pages/pin_login_dialog.dart
Normal file
@@ -0,0 +1,102 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_verification_code_input/flutter_verification_code_input.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/main_model.dart';
|
||||
import 'package:fcs/pages/util.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:quiver/async.dart';
|
||||
|
||||
class PinLoginDialog extends StatefulWidget {
|
||||
@override
|
||||
_PinLoginDialogState createState() => _PinLoginDialogState();
|
||||
}
|
||||
|
||||
class _PinLoginDialogState extends State<PinLoginDialog> {
|
||||
String pin;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var mainModel = Provider.of<MainModel>(context);
|
||||
|
||||
return AlertDialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(32.0))),
|
||||
title: Column(
|
||||
children: <Widget>[
|
||||
Image.asset(
|
||||
"assets/pin.png",
|
||||
height: 90,
|
||||
color: primaryColor,
|
||||
),
|
||||
Text(
|
||||
"Enter PIN Code",
|
||||
style: TextStyle(
|
||||
color: primaryColor, fontWeight: FontWeight.bold, fontSize: 20),
|
||||
),
|
||||
],
|
||||
),
|
||||
content: Container(
|
||||
width: double.maxFinite,
|
||||
height: 120.0,
|
||||
child: new ListView(
|
||||
shrinkWrap: true,
|
||||
children: <Widget>[
|
||||
Center(
|
||||
child: VerificationCodeInput(
|
||||
keyboardType: TextInputType.number,
|
||||
length: 6,
|
||||
autofocus: false,
|
||||
itemSize: 40,
|
||||
itemDecoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
textStyle: TextStyle(fontWeight: FontWeight.bold, fontSize: 23),
|
||||
onCompleted: (String value) {
|
||||
this.pin = value;
|
||||
},
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 15,
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
FlatButton(
|
||||
child: Text("Cancel"),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
}),
|
||||
FlatButton(
|
||||
color: primaryColor,
|
||||
child: Text("OK",
|
||||
style: TextStyle(
|
||||
color: Colors.white, fontWeight: FontWeight.bold)),
|
||||
onPressed: () async {
|
||||
if (this.pin == null) return;
|
||||
|
||||
if (mainModel.user.pin == this.pin) {
|
||||
mainModel.resetPinTimer();
|
||||
Navigator.of(context).pop();
|
||||
} else {
|
||||
showMsgDialog(context, "Error", "Invalid PIN Code !");
|
||||
}
|
||||
}),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
27
lib/pages/po/po_files.dart
Normal file
27
lib/pages/po/po_files.dart
Normal file
@@ -0,0 +1,27 @@
|
||||
import 'dart:io';
|
||||
|
||||
class POFiles {
|
||||
File poPaymentFile, storageChargeFile;
|
||||
List<File> poPaymentFilesAdded=[];
|
||||
List<String> poPaymentFilesRemoved=[]; // only url
|
||||
bool poFileChanged = false, storageFileChanged = false;
|
||||
|
||||
set addPoPaymentFile(File file) {
|
||||
poPaymentFilesAdded.add(file);
|
||||
poFileChanged = true;
|
||||
}
|
||||
|
||||
set removePoPaymentFile(String url) {
|
||||
poPaymentFilesRemoved.add(url);
|
||||
poFileChanged = true;
|
||||
}
|
||||
|
||||
set setStorageChargeFile(File file) {
|
||||
storageChargeFile = file;
|
||||
storageFileChanged = true;
|
||||
}
|
||||
|
||||
bool get anyChanged => poFileChanged || storageFileChanged;
|
||||
|
||||
|
||||
}
|
||||
208
lib/pages/po/po_item.dart
Normal file
208
lib/pages/po/po_item.dart
Normal file
@@ -0,0 +1,208 @@
|
||||
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/product_model.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/vo/po.dart';
|
||||
import 'package:fcs/vo/product.dart';
|
||||
import 'package:fcs/widget/local_text.dart';
|
||||
import 'package:fcs/widget/localization/app_translations.dart';
|
||||
import 'package:fcs/widget/progress.dart';
|
||||
|
||||
import '../util.dart';
|
||||
|
||||
class POItem extends StatefulWidget {
|
||||
final POLine poLine;
|
||||
const POItem({Key key, this.poLine}) : super(key: key);
|
||||
@override
|
||||
_POItemState createState() => _POItemState();
|
||||
}
|
||||
|
||||
class _POItemState extends State<POItem> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
bool _isLoading = false;
|
||||
String currentProductID;
|
||||
TextEditingController _qty = new TextEditingController();
|
||||
POLine poLine = POLine();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (widget.poLine != null) {
|
||||
this.poLine = widget.poLine;
|
||||
|
||||
this._qty.text = this.poLine.qty.toString();
|
||||
this.currentProductID = this.poLine.productID;
|
||||
this.poLine.action = "update";
|
||||
} else {
|
||||
this.poLine.action = "create";
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
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) {
|
||||
var productModel = Provider.of<ProductModel>(context);
|
||||
var languageModel = Provider.of<LanguageModel>(context);
|
||||
|
||||
final volumeBox = Container(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: TextFormField(
|
||||
controller: _qty,
|
||||
autofocus: false,
|
||||
cursorColor: primaryColor,
|
||||
keyboardType: TextInputType.number,
|
||||
decoration: new InputDecoration(
|
||||
labelText: AppTranslations.of(context).text("po.volume"),
|
||||
labelStyle: languageModel.isEng ? labelStyle : labelStyleMM,
|
||||
icon: Icon(
|
||||
FontAwesomeIcons.sortNumericUpAlt,
|
||||
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("po.form.volume");
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: primaryColor,
|
||||
title: LocalText(
|
||||
context,
|
||||
"po",
|
||||
color: Colors.white,
|
||||
fontSize: 20,
|
||||
),
|
||||
actions: <Widget>[
|
||||
// IconButton(
|
||||
// icon: Icon(Icons.delete),
|
||||
// onPressed: () {
|
||||
// _delete();
|
||||
// },
|
||||
// ),
|
||||
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>[
|
||||
volumeBox,
|
||||
showProducts(context, productModel)
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
_save() {
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
try {
|
||||
this.poLine.productID = currentProductID;
|
||||
var product =
|
||||
Provider.of<ProductModel>(context).getProduct(currentProductID);
|
||||
this.poLine.productName = product.name;
|
||||
this.poLine.price = product.price;
|
||||
this.poLine.qty = int.parse(_qty.text);
|
||||
this.poLine.amount = this.poLine.price * this.poLine.qty;
|
||||
Navigator.pop<POLine>(context, this.poLine);
|
||||
} catch (e) {
|
||||
showMsgDialog(context, "Error", e.toString());
|
||||
} finally {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_delete() {
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
try {
|
||||
this.poLine.action = "delete";
|
||||
Navigator.pop<POLine>(context, this.poLine);
|
||||
} catch (e) {
|
||||
showMsgDialog(context, "Error", e.toString());
|
||||
} finally {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
601
lib/pages/po/po_submission_form.dart
Normal file
601
lib/pages/po/po_submission_form.dart
Normal file
@@ -0,0 +1,601 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.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/po/po_item.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/vo/attach.dart';
|
||||
import 'package:fcs/vo/po.dart';
|
||||
import 'package:fcs/widget/img_file.dart';
|
||||
import 'package:fcs/widget/label_widgets.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 '../../util.dart';
|
||||
import '../document_log_page.dart';
|
||||
import '../util.dart';
|
||||
import 'po_files.dart';
|
||||
|
||||
class POSubmissionForm extends StatefulWidget {
|
||||
final POSubmission poSubmission;
|
||||
|
||||
POSubmissionForm({this.poSubmission});
|
||||
|
||||
@override
|
||||
_POSubmissionFormState createState() => _POSubmissionFormState();
|
||||
}
|
||||
|
||||
class _POSubmissionFormState extends State<POSubmissionForm> {
|
||||
final numberFormatter = new NumberFormat("#,###");
|
||||
var dateFormatter = new DateFormat('dd MMM yyyy - hh:mm a');
|
||||
MultiImgController multiImgController = MultiImgController();
|
||||
|
||||
TextEditingController _numberController = new TextEditingController();
|
||||
TextEditingController _storage = new TextEditingController();
|
||||
TextEditingController _comment = new TextEditingController();
|
||||
TextEditingController _name = new TextEditingController();
|
||||
TextEditingController _bizName = new TextEditingController();
|
||||
|
||||
List<POLine> poLines = new List();
|
||||
bool _isLoading = false;
|
||||
bool _isNew = true;
|
||||
int _amount = 0;
|
||||
String _date = "", _status = "";
|
||||
POSubmission poSubmission = POSubmission();
|
||||
AttachFile attachFile;
|
||||
POFiles files = POFiles();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (widget.poSubmission != null) {
|
||||
_isNew = false;
|
||||
poSubmission = widget.poSubmission;
|
||||
|
||||
_date = dateFormatter.format(poSubmission.poDate);
|
||||
_numberController.text = poSubmission.poNumber.toString();
|
||||
_comment.text = poSubmission.comment;
|
||||
_name.text = poSubmission.userName;
|
||||
_bizName.text = poSubmission.bizName;
|
||||
poLines = poSubmission.poLines;
|
||||
_status = poSubmission.status;
|
||||
_storage.text = poSubmission.storageCharge.toString();
|
||||
multiImgController.setImageUrls = poSubmission.poReceiptUrls;
|
||||
|
||||
Provider.of<POSubmissionModel>(context, listen: false)
|
||||
.loadPOLines(poSubmission.id)
|
||||
.then((poLines) {
|
||||
setState(() {
|
||||
this.poSubmission.poLines = poLines;
|
||||
_amount = poSubmission.getAmount;
|
||||
});
|
||||
});
|
||||
} else {
|
||||
_amount = 0;
|
||||
_date = dateFormatter.format(DateTime.now());
|
||||
var productModel = Provider.of<ProductModel>(context, listen: false);
|
||||
productModel.products.forEach((p) {
|
||||
var _poLine = POLine(
|
||||
productID: p.id,
|
||||
productName: p.name,
|
||||
price: p.price,
|
||||
balanceQty: 0,
|
||||
qty: 0,
|
||||
amount: 0);
|
||||
poSubmission.poLines.add(_poLine);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
MainModel mainModel = Provider.of<MainModel>(context);
|
||||
bool isBuyer = mainModel.user.isBuyer();
|
||||
var logModel = Provider.of<LogModel>(context);
|
||||
|
||||
final dateBox = Container(
|
||||
padding: EdgeInsets.only(top: 15, left: 20, bottom: 5),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
LocalText(context, "po.date"),
|
||||
Container(
|
||||
padding: EdgeInsets.only(left: 10),
|
||||
child: Text(_date, style: textStyle),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
final numberBox = Container(
|
||||
padding: EdgeInsets.only(top: 5, left: 20, bottom: 5),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
LocalText(context, "po.number"),
|
||||
Container(
|
||||
padding: EdgeInsets.only(left: 10),
|
||||
child: Text(
|
||||
_numberController.text,
|
||||
style: textStyle,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
final userNameBox = Container(
|
||||
padding: EdgeInsets.only(top: 5, left: 20, bottom: 5),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
LocalText(context, "po.name"),
|
||||
Container(
|
||||
padding: EdgeInsets.only(left: 20),
|
||||
child: Text(
|
||||
_name.text,
|
||||
style: textStyle,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
final bizNameBox = Container(
|
||||
padding: EdgeInsets.only(top: 5, left: 20, bottom: 5),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
LocalText(context, "po.biz"),
|
||||
Container(
|
||||
padding: EdgeInsets.only(left: 20),
|
||||
child: Text(
|
||||
_bizName.text,
|
||||
style: textStyle,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
final statusBox = Container(
|
||||
padding: EdgeInsets.only(top: 5, left: 20),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
LocalText(context, "po.status"),
|
||||
Container(
|
||||
padding: EdgeInsets.only(left: 10),
|
||||
child: Text(
|
||||
_status,
|
||||
style: textStyle,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
final commentBox = Container(
|
||||
padding: EdgeInsets.only(top: 5, left: 20),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
LocalText(context, "po.comment"),
|
||||
Container(
|
||||
padding: EdgeInsets.only(left: 10),
|
||||
child: Text(
|
||||
_comment.text,
|
||||
style: textStyle,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
final amountBox = Container(
|
||||
padding: EdgeInsets.only(top: 5, left: 10),
|
||||
child: labeledText(context, formatNumber(_amount), "po.amount",
|
||||
number: true));
|
||||
|
||||
final storageBox = Container(
|
||||
padding: EdgeInsets.only(top: 5, left: 20),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
LocalText(context, "po.storage_charge"),
|
||||
Container(
|
||||
padding: EdgeInsets.only(left: 10),
|
||||
child: Text(
|
||||
_storage.text,
|
||||
style: textStyle,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
final poPaymentBox = Container(
|
||||
padding: EdgeInsets.only(left: 20),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
LocalText(context, "po.po_payment_receipt"),
|
||||
MultiImageFile(
|
||||
enabled: isBuyer
|
||||
? _isNew || this.poSubmission.isPending() ? true : false
|
||||
: false,
|
||||
controller: multiImgController,
|
||||
title: "Receipt File",
|
||||
)
|
||||
]));
|
||||
|
||||
final storagePaymentBox = Container(
|
||||
padding: EdgeInsets.only(left: 20),
|
||||
child: Row(children: <Widget>[
|
||||
LocalText(context, "po.storage_receipt"),
|
||||
ImageFile(
|
||||
enabled: isBuyer
|
||||
? _isNew || this.poSubmission.isPending() ? true : false
|
||||
: false,
|
||||
initialImgUrl: this.poSubmission.storageReceiptUrl,
|
||||
title: "Receipt File",
|
||||
onFile: (file) {
|
||||
this.files.setStorageChargeFile = file;
|
||||
}),
|
||||
]));
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: primaryColor,
|
||||
title: Text(AppTranslations.of(context).text("po"),
|
||||
style: Provider.of<LanguageModel>(context).isEng
|
||||
? TextStyle(fontSize: 18)
|
||||
: TextStyle(fontSize: 18, fontFamily: 'MyanmarUnicode')),
|
||||
actions: <Widget>[
|
||||
_isNew || !mainModel.showHistoryBtn()
|
||||
? Container()
|
||||
: IconButton(
|
||||
icon: Icon(Icons.history),
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
DocumentLogPage(docID: poSubmission.id)),
|
||||
);
|
||||
},
|
||||
),
|
||||
isBuyer && (_isNew || poSubmission.isPending())
|
||||
? IconButton(
|
||||
icon: Icon(Icons.send),
|
||||
onPressed: () {
|
||||
showConfirmDialog(context, "po.confirm", () {
|
||||
_submit();
|
||||
});
|
||||
},
|
||||
)
|
||||
: Container(),
|
||||
isBuyer
|
||||
? Container()
|
||||
: PopupMenuButton(
|
||||
onSelected: _select,
|
||||
itemBuilder: (context) => [
|
||||
PopupMenuItem(
|
||||
enabled: poSubmission.isPending(),
|
||||
value: 1,
|
||||
child: Text("Approve PO"),
|
||||
),
|
||||
PopupMenuItem(
|
||||
enabled: poSubmission.isPending(),
|
||||
value: 2,
|
||||
child: Text("Reject PO"),
|
||||
),
|
||||
PopupMenuItem(
|
||||
enabled: mainModel.user.isOwner() &&
|
||||
poSubmission.isApproved(),
|
||||
value: 3,
|
||||
child: Text("Cancel PO"),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
// floatingActionButton: isBuyer && (_isNew || poSubmission.isPending())
|
||||
// ? FloatingActionButton(
|
||||
// backgroundColor: primaryColor,
|
||||
// child: Icon(Icons.add),
|
||||
// onPressed: () async {
|
||||
// final POLine poLine = await Navigator.push<POLine>(
|
||||
// context,
|
||||
// MaterialPageRoute(builder: (context) => POItem()),
|
||||
// );
|
||||
// _save(poLine);
|
||||
// },
|
||||
// )
|
||||
// : null,
|
||||
body: Container(
|
||||
child: ListView(
|
||||
children: <Widget>[
|
||||
dateBox,
|
||||
Divider(),
|
||||
_isNew ? Container() : numberBox,
|
||||
_isNew ? Container() : Divider(),
|
||||
_isNew ? Container() : userNameBox,
|
||||
_isNew ? Container() : Divider(),
|
||||
_isNew ? Container() : bizNameBox,
|
||||
_isNew ? Container() : Divider(),
|
||||
_isNew ? Container() : statusBox,
|
||||
_isNew ||
|
||||
widget.poSubmission.comment == null ||
|
||||
widget.poSubmission.comment == ''
|
||||
? Container()
|
||||
: Divider(),
|
||||
_isNew ||
|
||||
widget.poSubmission.comment == null ||
|
||||
widget.poSubmission.comment == ''
|
||||
? Container()
|
||||
: commentBox,
|
||||
_isNew ? Container() : Divider(),
|
||||
amountBox,
|
||||
Divider(),
|
||||
poPaymentBox,
|
||||
Divider(),
|
||||
_isNew || !poSubmission.hasStorageCharge()
|
||||
? Container()
|
||||
: storageBox,
|
||||
_isNew || !poSubmission.hasStorageCharge()
|
||||
? Container()
|
||||
: Divider(),
|
||||
_isNew || !poSubmission.hasStorageCharge()
|
||||
? Container()
|
||||
: storagePaymentBox,
|
||||
_isNew || !poSubmission.hasStorageCharge()
|
||||
? Container()
|
||||
: Divider(),
|
||||
Container(
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: MyDataTable(
|
||||
headingRowHeight: 40,
|
||||
columnSpacing: 7,
|
||||
columns: _isNew
|
||||
? [
|
||||
MyDataColumn(
|
||||
label: LocalText(context, "po.product")),
|
||||
MyDataColumn(
|
||||
label: LocalText(context, "po.price"),
|
||||
numeric: true),
|
||||
MyDataColumn(
|
||||
label: LocalText(context, "po.volume"),
|
||||
numeric: true),
|
||||
MyDataColumn(
|
||||
label: LocalText(context, "po.amount"),
|
||||
numeric: true),
|
||||
]
|
||||
: [
|
||||
MyDataColumn(
|
||||
label: LocalText(context, "po.product")),
|
||||
MyDataColumn(
|
||||
label: LocalText(context, "po.price"),
|
||||
numeric: true),
|
||||
MyDataColumn(
|
||||
label: LocalText(context, "po.balance.volume"),
|
||||
numeric: true),
|
||||
MyDataColumn(
|
||||
label: LocalText(context, "po.volume"),
|
||||
numeric: true),
|
||||
MyDataColumn(
|
||||
label: LocalText(context, "po.amount"),
|
||||
numeric: true),
|
||||
],
|
||||
rows: getProductRow(poSubmission.poLines),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_select(s) {
|
||||
if (s == 1) {
|
||||
showConfirmDialog(context, "po.approve.confirm", () {
|
||||
_approve();
|
||||
});
|
||||
} else if (s == 2) {
|
||||
showCommentDialog(context, (comment) {
|
||||
this.poSubmission.comment = comment;
|
||||
_reject();
|
||||
});
|
||||
} else if (s == 3) {
|
||||
showConfirmDialog(context, "po.cancel.confirm", () {
|
||||
_cancel();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
List<MyDataRow> getProductRow(List<POLine> poLines) {
|
||||
MainModel mainModel = Provider.of<MainModel>(context);
|
||||
bool isBuyer = mainModel.user.isBuyer();
|
||||
|
||||
ProductModel productModel = Provider.of<ProductModel>(context);
|
||||
if (poLines.isNotEmpty) {
|
||||
poLines.forEach((d) {
|
||||
productModel.products.forEach((p) {
|
||||
if (p.id == d.productID) {
|
||||
d.displayOrder = p.displayOrder;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
poLines.sort((p1, p2) => p1.displayOrder.compareTo(p2.displayOrder));
|
||||
}
|
||||
|
||||
return poLines.map((p) {
|
||||
return MyDataRow(
|
||||
onSelectChanged: (bool selected) async {
|
||||
if (!isBuyer) return;
|
||||
|
||||
if (_isNew || this.poSubmission.isPending()) {
|
||||
var poLine = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => POItem(
|
||||
poLine: p,
|
||||
)),
|
||||
);
|
||||
_save(poLine);
|
||||
}
|
||||
},
|
||||
cells: _isNew
|
||||
? [
|
||||
MyDataCell(
|
||||
new Text(
|
||||
p.productName,
|
||||
style: textStyle,
|
||||
),
|
||||
),
|
||||
MyDataCell(NumberCell(p.price)),
|
||||
MyDataCell(
|
||||
Container(
|
||||
alignment: Alignment.centerRight,
|
||||
width: 100,
|
||||
child: NumberCell(
|
||||
p.qty,
|
||||
textStyle: textStyleOdd,
|
||||
),
|
||||
),
|
||||
),
|
||||
MyDataCell(NumberCell(p.amount)),
|
||||
]
|
||||
: [
|
||||
MyDataCell(
|
||||
new Text(
|
||||
p.productName,
|
||||
style: textStyle,
|
||||
),
|
||||
),
|
||||
MyDataCell(NumberCell(p.price)),
|
||||
MyDataCell(NumberCell(p.balanceQty)),
|
||||
MyDataCell(NumberCell(p.qty)),
|
||||
MyDataCell(NumberCell(p.amount)),
|
||||
],
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
_save(POLine poLine) {
|
||||
if (poLine == null) return;
|
||||
if (poLine.action == "create") {
|
||||
if (poSubmission.poLines.contains(poLine)) {
|
||||
showMsgDialog(context, "Error", "Duplicate line");
|
||||
return;
|
||||
}
|
||||
poSubmission.poLines.add(poLine);
|
||||
} else if (poLine.action == "delete") {
|
||||
poSubmission.poLines.remove(poLine);
|
||||
}
|
||||
setState(() {
|
||||
_amount = poSubmission.getAmount;
|
||||
});
|
||||
}
|
||||
|
||||
_submit() async {
|
||||
if (poSubmission.poLines.length == 0) {
|
||||
showMsgDialog(context, "Error", "No product line");
|
||||
return;
|
||||
}
|
||||
List<POLine> _poLines = [];
|
||||
poSubmission.poLines.forEach((p) => p.qty <= 0 ? _poLines.add(p) : p);
|
||||
poSubmission.poLines.removeWhere((p) => p.qty <= 0);
|
||||
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
try {
|
||||
POSubmissionModel poModel = Provider.of<POSubmissionModel>(context);
|
||||
if (_isNew) {
|
||||
await poModel.createPO(poSubmission, multiImgController.getAddedFile);
|
||||
} else {
|
||||
if (poSubmission.hasStorageCharge()) {
|
||||
if (files.storageChargeFile == null) {
|
||||
showMsgDialog(
|
||||
context, "Error", "Please insert storage charge file");
|
||||
return;
|
||||
}
|
||||
}
|
||||
await poModel.updatePO(poSubmission, multiImgController.getAddedFile,
|
||||
multiImgController.getDeletedUrl);
|
||||
}
|
||||
Navigator.pop(context);
|
||||
} catch (e) {
|
||||
showMsgDialog(context, "Error", e.toString());
|
||||
} finally {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
_poLines.forEach((e) {
|
||||
if (!poSubmission.poLines.contains(e)) poSubmission.poLines.add(e);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_approve() async {
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
var oldStatus = poSubmission.status;
|
||||
try {
|
||||
POSubmissionModel poModel = Provider.of<POSubmissionModel>(context);
|
||||
|
||||
poSubmission.status = "approved";
|
||||
await poModel.approvePO(poSubmission);
|
||||
Navigator.pop(context);
|
||||
} catch (e) {
|
||||
poSubmission.status = oldStatus;
|
||||
showMsgDialog(context, "Error", e.toString());
|
||||
} finally {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_reject() async {
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
try {
|
||||
POSubmissionModel poModel = Provider.of<POSubmissionModel>(context);
|
||||
await poModel.rejectPO(poSubmission);
|
||||
Navigator.pop(context);
|
||||
} catch (e) {
|
||||
showMsgDialog(context, "Error", e.toString());
|
||||
} finally {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_cancel() async {
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
try {
|
||||
POSubmissionModel poModel = Provider.of<POSubmissionModel>(context);
|
||||
await poModel.cancelPO(poSubmission);
|
||||
Navigator.pop(context);
|
||||
} catch (e) {
|
||||
showMsgDialog(context, "Error", e.toString());
|
||||
} finally {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
291
lib/pages/po/po_submission_list.dart
Normal file
291
lib/pages/po/po_submission_list.dart
Normal file
@@ -0,0 +1,291 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.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/pages/po/po_submission_form.dart';
|
||||
import 'package:fcs/pages/util.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/vo/po.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 SubmissionList extends StatefulWidget {
|
||||
@override
|
||||
_SubmissionListState createState() => _SubmissionListState();
|
||||
}
|
||||
|
||||
class _SubmissionListState extends State<SubmissionList> {
|
||||
var dateFormatter = new DateFormat('dd MMM yyyy');
|
||||
final double dotSize = 10.0;
|
||||
PopupMenu selectedChoices = poMenus[0];
|
||||
POSubmission poSubmission;
|
||||
DateTime _selectedDate = DateTime.now();
|
||||
String status;
|
||||
int _selectedIndex = 0;
|
||||
int _dateIndex = 0;
|
||||
bool _isLoading = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
var poModel = Provider.of<POSubmissionModel>(context, listen: false);
|
||||
_selectedIndex = poModel.popupMenu.index;
|
||||
_dateIndex = poModel.dateIndex;
|
||||
_selectedDate = poModel.selectedDate;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<Null> _selectDate(BuildContext context) async {
|
||||
var poSubmissionModel = Provider.of<POSubmissionModel>(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;
|
||||
poSubmissionModel.filterData(
|
||||
this.status, _selectedDate, this._selectedIndex, this._dateIndex);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var poSubmissionModel = Provider.of<POSubmissionModel>(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("po.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;
|
||||
}
|
||||
poSubmissionModel.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: isBuyer
|
||||
? FloatingActionButton(
|
||||
backgroundColor: primaryColor,
|
||||
heroTag: "btn2",
|
||||
onPressed: () => Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => POSubmissionForm()),
|
||||
),
|
||||
child: Icon(Icons.add),
|
||||
)
|
||||
: null,
|
||||
body: new ListView.builder(
|
||||
scrollDirection: Axis.vertical,
|
||||
padding: EdgeInsets.only(left: 15, right: 15, top: 15),
|
||||
shrinkWrap: true,
|
||||
itemCount: poSubmissionModel.pos.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return Card(
|
||||
elevation: 10,
|
||||
color: Colors.white,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => POSubmissionForm(
|
||||
poSubmission: poSubmissionModel.pos[index],
|
||||
)),
|
||||
);
|
||||
},
|
||||
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: 15.0 - dotSize / 2),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(5.0),
|
||||
child: Image.asset(
|
||||
"assets/pay.png",
|
||||
width: 40,
|
||||
height: 40,
|
||||
color: primaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
new Expanded(
|
||||
child: new Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
new Text(
|
||||
poSubmissionModel.pos[index].poNumber
|
||||
.toString(),
|
||||
style: new TextStyle(
|
||||
fontSize: 12.0, color: Colors.black),
|
||||
),
|
||||
new Text(
|
||||
dateFormatter.format(
|
||||
poSubmissionModel.pos[index].poDate),
|
||||
style: new TextStyle(
|
||||
fontSize: 12.0, color: Colors.grey),
|
||||
),
|
||||
!isBuyer
|
||||
? new Text(
|
||||
poSubmissionModel
|
||||
.pos[index].userName
|
||||
.toString(),
|
||||
style: new TextStyle(
|
||||
fontSize: 12.0,
|
||||
color: Colors.grey),
|
||||
)
|
||||
: Container()
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 18.0),
|
||||
child: getStatus(poSubmissionModel.pos[index].status),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
223
lib/pages/product_edit_item.dart
Normal file
223
lib/pages/product_edit_item.dart
Normal file
@@ -0,0 +1,223 @@
|
||||
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/product_model.dart';
|
||||
import 'package:fcs/pages/util.dart';
|
||||
import 'package:fcs/vo/product.dart';
|
||||
import 'package:fcs/widget/localization/app_translations.dart';
|
||||
import 'package:fcs/widget/progress.dart';
|
||||
|
||||
import '../theme/theme.dart';
|
||||
|
||||
class ProductEditItem extends StatefulWidget {
|
||||
final Product product;
|
||||
|
||||
const ProductEditItem({Key key, this.product}) : super(key: key);
|
||||
@override
|
||||
_ProductEditItemState createState() => _ProductEditItemState();
|
||||
}
|
||||
|
||||
class _ProductEditItemState extends State<ProductEditItem> {
|
||||
TextEditingController nameController = new TextEditingController();
|
||||
TextEditingController priceController = new TextEditingController();
|
||||
TextEditingController orderController = new TextEditingController();
|
||||
int color = primaryColor.value;
|
||||
bool isDisable = false;
|
||||
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
bool _isLoading = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
if (widget.product != null) {
|
||||
this.color = widget.product.color;
|
||||
nameController.text = widget.product.name;
|
||||
priceController.text = widget.product.price.toString();
|
||||
orderController.text = widget.product.displayOrder.toString();
|
||||
if (widget.product.isDisable != null) {
|
||||
isDisable = widget.product.isDisable;
|
||||
} else {
|
||||
isDisable = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var maingModel = Provider.of<LanguageModel>(context);
|
||||
final nameWidget = Container(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: TextFormField(
|
||||
controller: nameController,
|
||||
autofocus: true,
|
||||
cursorColor: primaryColor,
|
||||
style: textStyle,
|
||||
decoration: new InputDecoration(
|
||||
icon: InkWell(
|
||||
child: Icon(
|
||||
FontAwesomeIcons.tag,
|
||||
color: Color(this.color),
|
||||
size: 25,
|
||||
),
|
||||
onTap: () => showColorPicker(context, Color(this.color), (color) {
|
||||
setState(() {
|
||||
this.color = color.value;
|
||||
});
|
||||
}),
|
||||
),
|
||||
labelText: AppTranslations.of(context).text("product.name"),
|
||||
labelStyle: maingModel.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("product.name_empty");
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
final priceInput = Container(
|
||||
child: TextFormField(
|
||||
controller: priceController,
|
||||
style: TextStyle(fontSize: 16.0, fontStyle: FontStyle.normal),
|
||||
keyboardType: TextInputType.number,
|
||||
cursorColor: primaryColor,
|
||||
decoration: new InputDecoration(
|
||||
icon: Icon(
|
||||
FontAwesomeIcons.moneyBill,
|
||||
color: primaryColor,
|
||||
size: 25,
|
||||
),
|
||||
border: InputBorder.none,
|
||||
labelText: AppTranslations.of(context).text("product.new_price"),
|
||||
labelStyle: maingModel.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("product.price_empty");
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
final orderInput = Container(
|
||||
child: TextFormField(
|
||||
controller: orderController,
|
||||
style: TextStyle(fontSize: 16.0, fontStyle: FontStyle.normal),
|
||||
keyboardType: TextInputType.number,
|
||||
cursorColor: primaryColor,
|
||||
decoration: new InputDecoration(
|
||||
icon: Icon(
|
||||
Icons.sort,
|
||||
color: primaryColor,
|
||||
size: 25,
|
||||
),
|
||||
border: InputBorder.none,
|
||||
labelText: AppTranslations.of(context).text("product.order"),
|
||||
labelStyle: maingModel.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("product.order_empty");
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
final disableBox = Container(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: CheckboxListTile(
|
||||
title: Text("Disable"),
|
||||
value: isDisable,
|
||||
activeColor: primaryColor,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
isDisable = value;
|
||||
});
|
||||
},
|
||||
controlAffinity: ListTileControlAffinity.leading,
|
||||
));
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(AppTranslations.of(context).text("product.item"),
|
||||
style: maingModel.isEng
|
||||
? TextStyle(color: Colors.white, fontSize: 20.0)
|
||||
: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 20.0,
|
||||
fontFamily: 'MyanmarUnicode')),
|
||||
actions: <Widget>[
|
||||
IconButton(
|
||||
icon: Icon(Icons.save),
|
||||
onPressed: () {
|
||||
if (!_formKey.currentState.validate()) return;
|
||||
Provider.of<ProductModel>(context, listen: false).saveProduct(
|
||||
widget.product,
|
||||
nameController.text,
|
||||
priceController.text,
|
||||
orderController.text,
|
||||
color,
|
||||
isDisable);
|
||||
Product _product = new Product();
|
||||
if (widget.product != null) {
|
||||
_product = widget.product;
|
||||
_product.name = nameController.text;
|
||||
_product.price = int.parse(priceController.text);
|
||||
_product.color = color;
|
||||
_product.displayOrder = int.parse(orderController.text);
|
||||
_product.isDisable = isDisable;
|
||||
if (_product.id == null) {
|
||||
_product.action = "create";
|
||||
} else {
|
||||
_product.action = "update";
|
||||
}
|
||||
}
|
||||
Navigator.pop<Product>(context, _product);
|
||||
},
|
||||
)
|
||||
],
|
||||
backgroundColor: primaryColor,
|
||||
),
|
||||
body: Form(
|
||||
key: _formKey,
|
||||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.only(
|
||||
left: 25.0,
|
||||
right: 25.0,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
nameWidget,
|
||||
priceInput,
|
||||
orderInput,
|
||||
this.widget.product != null ? disableBox : Container(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
68
lib/pages/products_list.dart
Normal file
68
lib/pages/products_list.dart
Normal file
@@ -0,0 +1,68 @@
|
||||
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/theme/theme.dart';
|
||||
import 'package:fcs/widget/localization/app_translations.dart';
|
||||
import 'package:fcs/widget/products.dart';
|
||||
import 'package:fcs/widget/progress.dart';
|
||||
|
||||
import '../model/product_model.dart';
|
||||
import 'products_list_edit.dart';
|
||||
|
||||
class ProductsList extends StatefulWidget {
|
||||
@override
|
||||
_ProductsListState createState() => _ProductsListState();
|
||||
}
|
||||
|
||||
class _ProductsListState extends State<ProductsList> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var productModel = Provider.of<ProductModel>(context);
|
||||
var mainModel = Provider.of<MainModel>(context);
|
||||
bool _isLoading = false;
|
||||
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: primaryColor,
|
||||
title: Text(AppTranslations.of(context).text("products.title"),
|
||||
style: Provider.of<LanguageModel>(context).isEng
|
||||
? TextStyle()
|
||||
: TextStyle(fontFamily: 'MyanmarUnicode')),
|
||||
actions: <Widget>[
|
||||
mainModel.user != null && mainModel.user.isOwner() ||
|
||||
mainModel.user.hasAdmin()
|
||||
? IconButton(
|
||||
icon: Icon(Icons.edit),
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => ProductsListEdit(
|
||||
products: productModel.productsToEdit,
|
||||
)),
|
||||
);
|
||||
},
|
||||
)
|
||||
: Container()
|
||||
],
|
||||
),
|
||||
backgroundColor: Colors.white,
|
||||
body: ListView(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 5),
|
||||
child: ProductsWidget(isWelcomePage: false),
|
||||
),
|
||||
],
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
||||
175
lib/pages/products_list_edit.dart
Normal file
175
lib/pages/products_list_edit.dart
Normal file
@@ -0,0 +1,175 @@
|
||||
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/product_model.dart';
|
||||
import 'package:fcs/pages/util.dart';
|
||||
import 'package:fcs/vo/product.dart';
|
||||
import 'package:fcs/widget/localization/app_translations.dart';
|
||||
import 'package:fcs/widget/progress.dart';
|
||||
|
||||
import '../theme/theme.dart';
|
||||
import 'product_edit_item.dart';
|
||||
|
||||
class ProductsListEdit extends StatefulWidget {
|
||||
final List<Product> products;
|
||||
|
||||
const ProductsListEdit({Key key, this.products}) : super(key: key);
|
||||
|
||||
@override
|
||||
_ProductsListEditState createState() => _ProductsListEditState();
|
||||
}
|
||||
|
||||
class _ProductsListEditState extends State<ProductsListEdit> {
|
||||
List<Product> products;
|
||||
bool _isLoading = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
products = widget.products;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: primaryColor,
|
||||
title: Text(AppTranslations.of(context).text("products.title"),
|
||||
style: Provider.of<LanguageModel>(context).isEng
|
||||
? TextStyle(color: Colors.white, fontSize: 20.0)
|
||||
: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 20.0,
|
||||
fontFamily: 'MyanmarUnicode')),
|
||||
actions: <Widget>[
|
||||
IconButton(
|
||||
icon: Icon(Icons.send),
|
||||
onPressed: () {
|
||||
_update(context);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
backgroundColor: primaryColor,
|
||||
child: Icon(Icons.add),
|
||||
onPressed: _add,
|
||||
),
|
||||
body: ListView(
|
||||
shrinkWrap: true,
|
||||
padding: EdgeInsets.all(5.0),
|
||||
children: <Widget>[
|
||||
new ListView.builder(
|
||||
padding: EdgeInsets.only(left: 15, right: 15, top: 15),
|
||||
shrinkWrap: true,
|
||||
itemCount: products.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return _row(products[index]);
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_add() {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => ProductEditItem()),
|
||||
);
|
||||
}
|
||||
|
||||
void _update(BuildContext context) async {
|
||||
await showConfirmDialog(context, "product.confirm", () async {
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
try {
|
||||
var productModel = Provider.of<ProductModel>(context);
|
||||
await productModel.updateProducts(this.products);
|
||||
Navigator.pop(context);
|
||||
} catch (e) {
|
||||
showMsgDialog(context, "Error", e.toString());
|
||||
} finally {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Widget _row(Product product) {
|
||||
return Container(
|
||||
child: InkWell(
|
||||
child: Card(
|
||||
elevation: 10,
|
||||
color: Colors.white,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: new Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 3.0),
|
||||
child: new Row(
|
||||
children: <Widget>[
|
||||
InkWell(
|
||||
child: new Padding(
|
||||
padding: new EdgeInsets.symmetric(
|
||||
horizontal: 20.0 - 10 / 2),
|
||||
child: Icon(
|
||||
FontAwesomeIcons.tag,
|
||||
color: Color(product.color),
|
||||
size: 30,
|
||||
),
|
||||
),
|
||||
),
|
||||
new Expanded(
|
||||
child: new Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
product.name,
|
||||
style: product.isDisable == true
|
||||
? TextStyle(
|
||||
decoration:
|
||||
TextDecoration.lineThrough,
|
||||
color: Colors.red)
|
||||
: null,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Column(children: <Widget>[
|
||||
product.id == null
|
||||
? Text("New Product")
|
||||
: Text("Old Price: ${product.oldPirce}"),
|
||||
Text("New Price: ${product.price}")
|
||||
]),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
onTap: () async {
|
||||
Product _p = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => ProductEditItem(
|
||||
product: product,
|
||||
)),
|
||||
);
|
||||
if (_p == null) return;
|
||||
setState(() {
|
||||
product = _p;
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
113
lib/pages/profile_edit.dart
Normal file
113
lib/pages/profile_edit.dart
Normal file
@@ -0,0 +1,113 @@
|
||||
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/pages/util.dart';
|
||||
import 'package:fcs/widget/localization/app_translations.dart';
|
||||
import 'package:fcs/widget/progress.dart';
|
||||
|
||||
import '../theme/theme.dart';
|
||||
|
||||
typedef void ProfileCallback();
|
||||
|
||||
class ProfileEdit extends StatefulWidget {
|
||||
@override
|
||||
_ProfileEditState createState() => _ProfileEditState();
|
||||
}
|
||||
|
||||
class _ProfileEditState extends State<ProfileEdit> {
|
||||
final TextEditingController nameController = new TextEditingController();
|
||||
bool _loading = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
MainModel mainModel = Provider.of<MainModel>(context, listen: false);
|
||||
nameController.text = mainModel.user.name;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var languageModel = Provider.of<LanguageModel>(context);
|
||||
|
||||
final name = Container(
|
||||
padding: EdgeInsets.only(top: 0, left: 20, right: 15, bottom: 30),
|
||||
child: TextFormField(
|
||||
controller: nameController,
|
||||
autofocus: true,
|
||||
cursorColor: primaryColor,
|
||||
style: textStyle,
|
||||
decoration: new InputDecoration(
|
||||
labelText: AppTranslations.of(context).text("profile.name"),
|
||||
labelStyle: languageModel.isEng ? labelStyle : labelStyleMM,
|
||||
icon: Icon(
|
||||
Icons.account_box,
|
||||
color: primaryColor,
|
||||
),
|
||||
enabledBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(color: primaryColor, width: 1.0)),
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(color: primaryColor, width: 1.0)),
|
||||
),
|
||||
));
|
||||
|
||||
final saveBtn = Card(
|
||||
elevation: 23,
|
||||
child: ButtonTheme(
|
||||
minWidth: 200.0,
|
||||
height: 50.0,
|
||||
child: FlatButton.icon(
|
||||
onPressed: () => _save(),
|
||||
label: Text(AppTranslations.of(context).text("btn.save"),
|
||||
style: languageModel.isEng
|
||||
? TextStyle(
|
||||
fontSize: 18.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontStyle: FontStyle.normal)
|
||||
: TextStyle(
|
||||
fontSize: 18.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontStyle: FontStyle.normal,
|
||||
fontFamily: "MyanmarUnicode")),
|
||||
icon: Icon(
|
||||
Icons.save,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return LocalProgress(
|
||||
inAsyncCall: _loading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(
|
||||
AppTranslations.of(context).text("profile.edit_title"),
|
||||
),
|
||||
backgroundColor: primaryColor,
|
||||
),
|
||||
body: Column(
|
||||
children: <Widget>[
|
||||
name,
|
||||
saveBtn,
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_save() async {
|
||||
setState(() {
|
||||
_loading = true;
|
||||
});
|
||||
try {
|
||||
await Provider.of<MainModel>(context, listen: false)
|
||||
.updateProfile(nameController.text);
|
||||
Navigator.pop(context);
|
||||
setState(() {
|
||||
_loading = false;
|
||||
});
|
||||
} catch (e) {
|
||||
showMsgDialog(context, "Error", e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
392
lib/pages/profile_page.dart
Normal file
392
lib/pages/profile_page.dart
Normal file
@@ -0,0 +1,392 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:package_info/package_info.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/profile_edit.dart';
|
||||
import 'package:fcs/pages/util.dart';
|
||||
import 'package:fcs/vo/role.dart';
|
||||
import 'package:fcs/widget/localization/app_translations.dart';
|
||||
import 'package:fcs/widget/localization/transalation.dart';
|
||||
import 'package:fcs/widget/progress.dart';
|
||||
|
||||
import '../theme/theme.dart';
|
||||
import 'profile_setting.dart';
|
||||
|
||||
typedef void ProfileCallback();
|
||||
|
||||
class Profile extends StatefulWidget {
|
||||
@override
|
||||
_ProfileState createState() => _ProfileState();
|
||||
}
|
||||
|
||||
class _ProfileState extends State<Profile> {
|
||||
bool _isLoading = false;
|
||||
String selectedLanguage;
|
||||
TextEditingController bizNameController = new TextEditingController();
|
||||
|
||||
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],
|
||||
};
|
||||
|
||||
buildLanguage(LanguageModel languageModel) async {
|
||||
var lan = await languageModel.load();
|
||||
if (this.selectedLanguage != lan) {
|
||||
setState(() {
|
||||
this.selectedLanguage = lan;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var languageModel = Provider.of<LanguageModel>(context);
|
||||
MainModel mainModel = Provider.of<MainModel>(context);
|
||||
|
||||
buildLanguage(languageModel);
|
||||
_selectedDropdown(String selected) {
|
||||
setState(() {
|
||||
selectedLanguage = selected;
|
||||
languageModel.saveLanguage(selectedLanguage);
|
||||
});
|
||||
}
|
||||
|
||||
final namebox = Container(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: Container(
|
||||
height: 45.0,
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 0.0),
|
||||
child: Text(
|
||||
AppTranslations.of(context).text("profile.name"),
|
||||
style: languageModel.isEng
|
||||
? TextStyle(
|
||||
fontSize: 16.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontStyle: FontStyle.normal)
|
||||
: TextStyle(
|
||||
fontSize: 15.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontStyle: FontStyle.normal,
|
||||
fontFamily: "MyanmarUnicode"),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 30,
|
||||
),
|
||||
Container(
|
||||
child: Center(
|
||||
child: Text(
|
||||
mainModel.user == null ? "" : mainModel.user.name,
|
||||
style:
|
||||
TextStyle(fontSize: 16.0, fontStyle: FontStyle.normal),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
));
|
||||
final phonenumberbox = Container(
|
||||
height: 45.0,
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 0.0),
|
||||
child: Text(
|
||||
AppTranslations.of(context).text("profile.phone"),
|
||||
style: languageModel.isEng
|
||||
? TextStyle(
|
||||
fontSize: 16.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontStyle: FontStyle.normal)
|
||||
: TextStyle(
|
||||
fontSize: 15.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontStyle: FontStyle.normal,
|
||||
fontFamily: "MyanmarUnicode"),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 27,
|
||||
),
|
||||
Container(
|
||||
child: Center(
|
||||
child: Text(
|
||||
mainModel.user == null
|
||||
? ""
|
||||
: mainModel.user.phone == null ? '' : mainModel.user.phone,
|
||||
style: TextStyle(fontSize: 16.0, fontStyle: FontStyle.normal),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
final emailBox = Container(
|
||||
padding: EdgeInsets.only(top: 10, left: 0),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Text(
|
||||
AppTranslations.of(context).text("profile.email"),
|
||||
style: languageModel.isEng
|
||||
? TextStyle(
|
||||
fontSize: 16.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontStyle: FontStyle.normal)
|
||||
: TextStyle(
|
||||
fontSize: 15.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontStyle: FontStyle.normal,
|
||||
fontFamily: "MyanmarUnicode"),
|
||||
),
|
||||
SizedBox(
|
||||
width: 35,
|
||||
),
|
||||
Text(
|
||||
mainModel.user == null
|
||||
? ""
|
||||
: mainModel.user.email == null || mainModel.user.email == ''
|
||||
? ''
|
||||
: mainModel.user.email,
|
||||
style: TextStyle(fontSize: 16.0, fontStyle: FontStyle.normal),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
final languageBox = Container(
|
||||
padding: EdgeInsets.only(bottom: 15, top: 7),
|
||||
child: Container(
|
||||
height: 45.0,
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Text(
|
||||
AppTranslations.of(context).text("profile.language"),
|
||||
style: languageModel.isEng
|
||||
? TextStyle(
|
||||
fontSize: 16.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontStyle: FontStyle.normal)
|
||||
: TextStyle(
|
||||
fontSize: 16.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontStyle: FontStyle.normal,
|
||||
fontFamily: "MyanmarUnicode"),
|
||||
),
|
||||
Container(
|
||||
width: 140,
|
||||
padding: EdgeInsets.only(left: 30),
|
||||
child: Theme(
|
||||
data: new ThemeData(
|
||||
canvasColor: Colors.white,
|
||||
),
|
||||
child: DropdownButton(
|
||||
hint: Text("English"),
|
||||
value: selectedLanguage,
|
||||
isExpanded: true,
|
||||
iconSize: 40,
|
||||
items: languagesList
|
||||
.map<DropdownMenuItem<String>>((String value) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: value,
|
||||
child: Text(value),
|
||||
);
|
||||
}).toList(),
|
||||
onChanged: _selectedDropdown),
|
||||
)),
|
||||
],
|
||||
),
|
||||
));
|
||||
|
||||
final logoutbutton = Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 10.0),
|
||||
child: Card(
|
||||
elevation: 23,
|
||||
child: Container(
|
||||
height: 45.0,
|
||||
child: ButtonTheme(
|
||||
minWidth: 900.0,
|
||||
height: 100.0,
|
||||
child: FlatButton.icon(
|
||||
onPressed: () {
|
||||
showConfirmDialog(context, "profile.logout.confirm",
|
||||
() async {
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
await mainModel.logout();
|
||||
// Navigator.of(context)
|
||||
// .pushNamedAndRemoveUntil("/", ModalRoute.withName('/'));
|
||||
Future.delayed(Duration(seconds: 1), () {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
label: Text(AppTranslations.of(context).text("profile.logout"),
|
||||
style: languageModel.isEng
|
||||
? TextStyle(
|
||||
fontSize: 16.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontStyle: FontStyle.normal)
|
||||
: TextStyle(
|
||||
fontSize: 16.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontStyle: FontStyle.normal,
|
||||
fontFamily: "MyanmarUnicode")),
|
||||
icon: Icon(
|
||||
Icons.exit_to_app,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
Future<String> getVersionNumber() async {
|
||||
PackageInfo packageInfo = await PackageInfo.fromPlatform();
|
||||
String version = packageInfo.version + "+" + packageInfo.buildNumber;
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
final versionbox = Container(
|
||||
padding: EdgeInsets.only(top: 15),
|
||||
child: Container(
|
||||
child: Center(
|
||||
child: FutureBuilder(
|
||||
future: getVersionNumber(),
|
||||
builder: (BuildContext context, AsyncSnapshot<String> snapshot) =>
|
||||
Text(
|
||||
snapshot.hasData ? "v${snapshot.data}" : "Loading ...",
|
||||
style: TextStyle(fontSize: 16.0, fontStyle: FontStyle.normal),
|
||||
),
|
||||
)),
|
||||
));
|
||||
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(
|
||||
AppTranslations.of(context).text("profile.title"),
|
||||
),
|
||||
backgroundColor: primaryColor,
|
||||
actions: <Widget>[
|
||||
IconButton(
|
||||
icon: Icon(Icons.edit),
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => ProfileEdit()),
|
||||
);
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.settings),
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => ProfileSetting()),
|
||||
);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
body: ListView(
|
||||
padding: EdgeInsets.only(
|
||||
left: 25.0,
|
||||
right: 25.0,
|
||||
),
|
||||
shrinkWrap: true,
|
||||
children: <Widget>[
|
||||
namebox,
|
||||
mainModel.isBuyer() ? Container() : getPrivilegeBox(context),
|
||||
phonenumberbox,
|
||||
mainModel.user == null
|
||||
? Container()
|
||||
: mainModel.user.email == null || mainModel.user.email == ''
|
||||
? Container()
|
||||
: emailBox,
|
||||
languageBox,
|
||||
logoutbutton,
|
||||
Divider(color: secondaryColor),
|
||||
versionbox,
|
||||
SizedBox(
|
||||
height: 20,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget getPrivilegeBox(BuildContext context) {
|
||||
var languageModel = Provider.of<LanguageModel>(context);
|
||||
var userModel = Provider.of<UserModel>(context);
|
||||
|
||||
return ListTileTheme(
|
||||
contentPadding: EdgeInsets.all(0),
|
||||
child: ExpansionTile(
|
||||
title: Text(
|
||||
AppTranslations.of(context).text("profile.privilege"),
|
||||
style: languageModel.isEng
|
||||
? TextStyle(
|
||||
fontSize: 16.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontStyle: FontStyle.normal,
|
||||
)
|
||||
: TextStyle(
|
||||
fontSize: 15.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontStyle: FontStyle.normal,
|
||||
fontFamily: "MyanmarUnicode"),
|
||||
),
|
||||
children: <Widget>[
|
||||
Align(
|
||||
alignment: Alignment.topLeft,
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children:
|
||||
getRowPrivilegeWidget(userModel.getUserPrivileges())),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> getRowPrivilegeWidget(List<Privilege> privileges) {
|
||||
return privileges.map((p) {
|
||||
return Container(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(p.name,
|
||||
style: TextStyle(fontSize: 16.0, fontStyle: FontStyle.normal)),
|
||||
SizedBox(
|
||||
width: 30,
|
||||
),
|
||||
Container(
|
||||
child: Text(
|
||||
"- ${p.desc}",
|
||||
style: TextStyle(fontSize: 16.0, fontStyle: FontStyle.normal),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
}
|
||||
201
lib/pages/profile_setting.dart
Normal file
201
lib/pages/profile_setting.dart
Normal file
@@ -0,0 +1,201 @@
|
||||
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 '../theme/theme.dart';
|
||||
import 'add_pin_editor.dart';
|
||||
import 'block_list.dart';
|
||||
import 'chage_phone_number.dart';
|
||||
import 'change_password.dart';
|
||||
import 'device_list.dart';
|
||||
import 'email_page.dart';
|
||||
import 'log_list.dart';
|
||||
|
||||
class ProfileSetting extends StatefulWidget {
|
||||
@override
|
||||
_ProfileSettingtate createState() => _ProfileSettingtate();
|
||||
}
|
||||
|
||||
class _ProfileSettingtate extends State<ProfileSetting> {
|
||||
bool _isLoading = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
MainModel mainModel = Provider.of<MainModel>(context);
|
||||
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: LocalText(
|
||||
context,
|
||||
"setting.title",
|
||||
fontSize: 20,
|
||||
color: Colors.white,
|
||||
),
|
||||
backgroundColor: primaryColor,
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
padding: EdgeInsets.only(
|
||||
left: 25.0,
|
||||
right: 25.0,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
buildSettingTile(
|
||||
context: context,
|
||||
text: 'log.title',
|
||||
image: "assets/message.png",
|
||||
width: 25,
|
||||
height: 25,
|
||||
tap: () {
|
||||
Navigator.push(context,
|
||||
MaterialPageRoute(builder: (context) => LogList()));
|
||||
},
|
||||
),
|
||||
buildSettingTile(
|
||||
context: context,
|
||||
text: 'profile.devices',
|
||||
image: "assets/device.png",
|
||||
width: 29,
|
||||
height: 29,
|
||||
tap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => PhoneDeviceList()));
|
||||
},
|
||||
),
|
||||
buildSettingTile(
|
||||
context: context,
|
||||
text: 'change.password.title',
|
||||
image: "assets/password.png",
|
||||
width: 27,
|
||||
height: 27,
|
||||
tap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
ChangePassword(mainModel.user)));
|
||||
},
|
||||
),
|
||||
buildSettingTile(
|
||||
context: context,
|
||||
text: 'change.phone',
|
||||
image: "assets/phone.png",
|
||||
width: 30,
|
||||
height: 25,
|
||||
tap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
ChangePhoneNumber(mainModel.user)));
|
||||
},
|
||||
),
|
||||
buildSettingTile(
|
||||
context: context,
|
||||
text: 'change.email',
|
||||
image: "assets/email.png",
|
||||
width: 25,
|
||||
height: 25,
|
||||
tap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => EmailPage(
|
||||
user: mainModel.user,
|
||||
)));
|
||||
},
|
||||
),
|
||||
// buildSettingTile(
|
||||
// context: context,
|
||||
// text: 'user.block_list',
|
||||
// image: "assets/block.png",
|
||||
// width: 27,
|
||||
// height: 27,
|
||||
// tap: () {
|
||||
// Navigator.push(context,
|
||||
// MaterialPageRoute(builder: (context) => BlockList()));
|
||||
// },
|
||||
// ),
|
||||
buildSettingTile(
|
||||
context: context,
|
||||
text: 'change.pin.title',
|
||||
image: "assets/pin.png",
|
||||
width: 30,
|
||||
height: 30,
|
||||
tap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => AddPINEditor(
|
||||
mainModel.user,
|
||||
)));
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget buildSettingTile(
|
||||
{@required String text,
|
||||
@required BuildContext context,
|
||||
@required String image,
|
||||
@required double width,
|
||||
@required double height,
|
||||
@required GestureTapCallback tap}) {
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
tap();
|
||||
},
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 8.0, bottom: 5),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 12.0, right: 20.0),
|
||||
child: Image.asset(
|
||||
image,
|
||||
width: width,
|
||||
height: height,
|
||||
color: primaryColor,
|
||||
),
|
||||
),
|
||||
LocalText(
|
||||
context,
|
||||
text,
|
||||
fontSize: 15.0,
|
||||
color: Colors.black87,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
Icon(Icons.keyboard_arrow_right)
|
||||
],
|
||||
),
|
||||
),
|
||||
Divider(
|
||||
color: Colors.grey,
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
179
lib/pages/quota_form.dart
Normal file
179
lib/pages/quota_form.dart
Normal file
@@ -0,0 +1,179 @@
|
||||
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/theme/theme.dart';
|
||||
import 'package:fcs/vo/buyer.dart';
|
||||
import 'package:fcs/widget/local_text.dart';
|
||||
import 'package:fcs/widget/localization/app_translations.dart';
|
||||
import 'package:fcs/widget/progress.dart';
|
||||
|
||||
class QuotaForm extends StatefulWidget {
|
||||
final BuyerProduct buyerProduct;
|
||||
const QuotaForm({Key key, this.buyerProduct}) : super(key: key);
|
||||
@override
|
||||
_QuotaFormState createState() => _QuotaFormState();
|
||||
}
|
||||
|
||||
class _QuotaFormState extends State<QuotaForm> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
TextEditingController _product = new TextEditingController();
|
||||
TextEditingController _storageQty = new TextEditingController();
|
||||
TextEditingController _saleQty = new TextEditingController();
|
||||
TextEditingController _dailyQuota = new TextEditingController();
|
||||
TextEditingController _maxQuota = new TextEditingController();
|
||||
|
||||
BuyerProduct buyerProduct;
|
||||
bool _isLoading = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (widget.buyerProduct != null) {
|
||||
this.buyerProduct = widget.buyerProduct;
|
||||
_product.text = widget.buyerProduct.productName;
|
||||
_storageQty.text = widget.buyerProduct.storageCapacityQty == null
|
||||
? ""
|
||||
: widget.buyerProduct.storageCapacityQty.toString();
|
||||
_saleQty.text = widget.buyerProduct.dailySaleQty == null
|
||||
? ""
|
||||
: widget.buyerProduct.dailySaleQty.toString();
|
||||
_dailyQuota.text = widget.buyerProduct.dailyQuota == null
|
||||
? ""
|
||||
: widget.buyerProduct.dailyQuota.toString();
|
||||
_maxQuota.text = widget.buyerProduct.maxQuota == null
|
||||
? ""
|
||||
: widget.buyerProduct.maxQuota.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var language = Provider.of<LanguageModel>(context);
|
||||
final productbox = Container(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
LocalText(context, "reg.table_product"),
|
||||
SizedBox(
|
||||
width: 30,
|
||||
),
|
||||
Text(_product.text, style: textStyle)
|
||||
],
|
||||
));
|
||||
|
||||
final storageQtybox = Container(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
LocalText(context, "reg.table_storage_vol"),
|
||||
SizedBox(
|
||||
width: 30,
|
||||
),
|
||||
Text(_storageQty.text, style: textStyle)
|
||||
],
|
||||
));
|
||||
|
||||
final saleQtybox = Container(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
LocalText(context, "reg.table_sale_vol"),
|
||||
SizedBox(
|
||||
width: 25,
|
||||
),
|
||||
Text(_saleQty.text, style: textStyle)
|
||||
],
|
||||
));
|
||||
|
||||
final dailyQuotaBox = Container(
|
||||
child: TextFormField(
|
||||
controller: _dailyQuota,
|
||||
autofocus: false,
|
||||
cursorColor: primaryColor,
|
||||
style: textStyle,
|
||||
decoration: new InputDecoration(
|
||||
labelText: AppTranslations.of(context).text('buyer.quota'),
|
||||
labelStyle: language.isEng ? labelStyle : labelStyleMM,
|
||||
icon: Icon(
|
||||
FontAwesomeIcons.sortNumericUpAlt,
|
||||
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 quota";
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
final maxQuotaBox = Container(
|
||||
child: TextFormField(
|
||||
controller: _maxQuota,
|
||||
autofocus: false,
|
||||
cursorColor: primaryColor,
|
||||
style: textStyle,
|
||||
decoration: new InputDecoration(
|
||||
labelText: AppTranslations.of(context).text('buyer.max.quota'),
|
||||
labelStyle: language.isEng ? labelStyle : labelStyleMM,
|
||||
icon: Icon(
|
||||
FontAwesomeIcons.sortNumericUpAlt,
|
||||
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 quota";
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: primaryColor,
|
||||
title: Text("Quota"),
|
||||
actions: <Widget>[
|
||||
IconButton(
|
||||
icon: Icon(Icons.save),
|
||||
onPressed: () {
|
||||
if (!_formKey.currentState.validate()) return;
|
||||
buyerProduct.dailyQuota = int.parse(_dailyQuota.text);
|
||||
buyerProduct.maxQuota = int.parse(_maxQuota.text);
|
||||
Navigator.pop(context, buyerProduct);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
body: Form(
|
||||
key: _formKey,
|
||||
child: Container(
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
padding: EdgeInsets.only(left: 24.0, right: 24.0),
|
||||
children: <Widget>[
|
||||
productbox,
|
||||
storageQtybox,
|
||||
saleQtybox,
|
||||
dailyQuotaBox,
|
||||
maxQuotaBox
|
||||
],
|
||||
),
|
||||
),
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
||||
216
lib/pages/quota_page.dart
Normal file
216
lib/pages/quota_page.dart
Normal file
@@ -0,0 +1,216 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/buyer_model.dart';
|
||||
import 'package:fcs/model/language_model.dart';
|
||||
import 'package:fcs/pages/util.dart';
|
||||
import 'package:fcs/vo/buyer.dart';
|
||||
import 'package:fcs/widget/localization/app_translations.dart';
|
||||
import 'package:fcs/widget/progress.dart';
|
||||
|
||||
import '../theme/theme.dart';
|
||||
|
||||
class QuotaPage extends StatefulWidget {
|
||||
final Buyer buyer;
|
||||
final bool isApproved;
|
||||
const QuotaPage({this.buyer, this.isApproved});
|
||||
@override
|
||||
_QuotaPageState createState() => _QuotaPageState();
|
||||
}
|
||||
|
||||
class _QuotaPageState extends State<QuotaPage> {
|
||||
final formKey = GlobalKey<FormState>();
|
||||
|
||||
TextEditingController _dailyQuota = new TextEditingController();
|
||||
TextEditingController _maxQuota = new TextEditingController();
|
||||
|
||||
TextEditingController _companyName = new TextEditingController();
|
||||
TextEditingController _accountName = new TextEditingController();
|
||||
Buyer buyer = Buyer();
|
||||
bool _isLoading = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (widget.buyer != null) {
|
||||
this.buyer = widget.buyer;
|
||||
_companyName.text = buyer.bizName;
|
||||
_accountName.text = buyer.userName;
|
||||
_dailyQuota.text = buyer.dailyQuota.toString();
|
||||
_maxQuota.text = buyer.maxQuota.toString();
|
||||
Provider.of<BuyerModel>(context, listen: false)
|
||||
.loadBuyerProducts(buyer, force: true)
|
||||
.then((b) {
|
||||
setState(() {
|
||||
buyer = b;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var languageModel = Provider.of<LanguageModel>(context);
|
||||
|
||||
final companyNameBox = Container(
|
||||
padding: EdgeInsets.only(top: 0, left: 20, right: 20),
|
||||
child: TextFormField(
|
||||
controller: _companyName,
|
||||
autofocus: false,
|
||||
cursorColor: primaryColor,
|
||||
readOnly: true,
|
||||
style: textStyle,
|
||||
decoration: new InputDecoration(
|
||||
labelText: AppTranslations.of(context).text("reg.biz_name"),
|
||||
labelStyle: languageModel.isEng ? labelStyle : labelStyleMM,
|
||||
border: InputBorder.none,
|
||||
focusedBorder: InputBorder.none,
|
||||
icon: Icon(
|
||||
Icons.business,
|
||||
color: primaryColor,
|
||||
),
|
||||
),
|
||||
));
|
||||
final accountNameBox = Container(
|
||||
padding: EdgeInsets.only(top: 0, left: 20, right: 20),
|
||||
child: TextFormField(
|
||||
controller: _accountName,
|
||||
autofocus: false,
|
||||
cursorColor: primaryColor,
|
||||
readOnly: true,
|
||||
style: textStyle,
|
||||
decoration: new InputDecoration(
|
||||
labelText: AppTranslations.of(context).text("buyer.account_name"),
|
||||
labelStyle: languageModel.isEng ? labelStyle : labelStyleMM,
|
||||
border: InputBorder.none,
|
||||
focusedBorder: InputBorder.none,
|
||||
icon: Icon(
|
||||
Icons.business,
|
||||
color: primaryColor,
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
final dailyQuotaBox = Container(
|
||||
padding: EdgeInsets.only(top: 0, left: 20, right: 15),
|
||||
child: TextFormField(
|
||||
controller: _dailyQuota,
|
||||
autofocus: false,
|
||||
cursorColor: primaryColor,
|
||||
style: textStyle,
|
||||
keyboardType: TextInputType.number,
|
||||
decoration: new InputDecoration(
|
||||
labelText: AppTranslations.of(context).text("reg.quota"),
|
||||
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: _validateQuota,
|
||||
));
|
||||
|
||||
final maxQuotaBox = Container(
|
||||
padding: EdgeInsets.only(top: 0, left: 20, right: 15),
|
||||
child: TextFormField(
|
||||
controller: _maxQuota,
|
||||
autofocus: false,
|
||||
cursorColor: primaryColor,
|
||||
style: textStyle,
|
||||
keyboardType: TextInputType.number,
|
||||
decoration: new InputDecoration(
|
||||
labelText: AppTranslations.of(context).text("reg.max_quota"),
|
||||
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: _validateQuota,
|
||||
));
|
||||
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: primaryColor,
|
||||
title: Text("Quota"),
|
||||
actions: <Widget>[
|
||||
IconButton(
|
||||
icon: Icon(Icons.send),
|
||||
onPressed: () {
|
||||
showConfirmDialog(context, "buyer.allocate.quota.confirm", () {
|
||||
_allocate();
|
||||
});
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
body: Form(
|
||||
key: formKey,
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(left: 10, right: 10, top: 10, bottom: 10),
|
||||
child: ListView(
|
||||
children: <Widget>[
|
||||
Container(
|
||||
height: 500,
|
||||
child: Card(
|
||||
elevation: 23,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
accountNameBox,
|
||||
companyNameBox,
|
||||
dailyQuotaBox,
|
||||
maxQuotaBox,
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _validateQuota(value) {
|
||||
if (value.isEmpty) {
|
||||
return "Invalid number";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
_allocate() async {
|
||||
if (!formKey.currentState.validate()) {
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
try {
|
||||
buyer.dailyQuota = int.parse(_dailyQuota.text);
|
||||
buyer.maxQuota = int.parse(_maxQuota.text);
|
||||
if (widget.isApproved) {
|
||||
await Provider.of<BuyerModel>(context).allocate(buyer);
|
||||
Navigator.pop(context, true);
|
||||
} else {
|
||||
Navigator.pop<Buyer>(context, this.buyer);
|
||||
}
|
||||
} catch (e) {
|
||||
showMsgDialog(context, "Error", e.toString());
|
||||
} finally {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
260
lib/pages/report_user_editor.dart
Normal file
260
lib/pages/report_user_editor.dart
Normal file
@@ -0,0 +1,260 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/language_model.dart';
|
||||
import 'package:fcs/model/report_user_model.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/vo/report.dart';
|
||||
import 'package:fcs/vo/report_user.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 'util.dart';
|
||||
|
||||
typedef void FindCallBack();
|
||||
|
||||
class ReportUserEditor extends StatefulWidget {
|
||||
final Report report;
|
||||
const ReportUserEditor({this.report});
|
||||
@override
|
||||
_ReportUserEditorState createState() => _ReportUserEditorState();
|
||||
}
|
||||
|
||||
class _ReportUserEditorState extends State<ReportUserEditor> {
|
||||
TextEditingController _name = new TextEditingController();
|
||||
TextEditingController _searchInput = new TextEditingController();
|
||||
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
|
||||
|
||||
bool _isLoading = false;
|
||||
bool isSend = false;
|
||||
User selectedUser;
|
||||
List<User> _users = [];
|
||||
int selectedIndex;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
Widget searchInputBox(BuildContext context, FindCallBack findCallBack) {
|
||||
var languageModel = Provider.of<LanguageModel>(context);
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: 10, left: 15, right: 15),
|
||||
child: Stack(
|
||||
alignment: const Alignment(1.2, 1.0),
|
||||
children: <Widget>[
|
||||
TextFormField(
|
||||
controller: _searchInput,
|
||||
autofocus: false,
|
||||
cursorColor: primaryColor,
|
||||
style: textStyle,
|
||||
decoration: new InputDecoration(
|
||||
labelText:
|
||||
AppTranslations.of(context).text('report.user.search'),
|
||||
labelStyle: languageModel.isEng ? labelStyle : labelStyleMM,
|
||||
enabledBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(color: primaryColor, width: 1.0)),
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(color: primaryColor, width: 1.0)),
|
||||
),
|
||||
),
|
||||
new FlatButton(
|
||||
onPressed: () {
|
||||
findCallBack();
|
||||
},
|
||||
child: new Icon(
|
||||
Icons.search,
|
||||
size: 25,
|
||||
))
|
||||
],
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final namebox = Container(
|
||||
padding: EdgeInsets.only(left: 10, top: 10),
|
||||
child: TextFormField(
|
||||
controller: _name,
|
||||
autofocus: false,
|
||||
readOnly: true,
|
||||
cursorColor: primaryColor,
|
||||
decoration: new InputDecoration(
|
||||
border: InputBorder.none,
|
||||
focusedBorder: InputBorder.none,
|
||||
icon: Icon(
|
||||
Icons.person,
|
||||
color: primaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
key: _scaffoldKey,
|
||||
appBar: AppBar(
|
||||
backgroundColor: primaryColor,
|
||||
title: LocalText(context, "user.title",
|
||||
fontSize: 20, color: Colors.white),
|
||||
actions: <Widget>[
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
Icons.save,
|
||||
color: Colors.white,
|
||||
),
|
||||
onPressed: () {
|
||||
_save(context);
|
||||
})
|
||||
],
|
||||
),
|
||||
body: Column(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
children: <Widget>[
|
||||
searchInputBox(context, () => _findUser(context)),
|
||||
this.isSend ? namebox : Container(),
|
||||
Container(
|
||||
padding: EdgeInsets.only(
|
||||
top: this.isSend ? 10 : 20, left: 20, right: 20),
|
||||
child: Column(
|
||||
children: _getUsers(context),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 20,
|
||||
)
|
||||
],
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
_findUser(BuildContext context) async {
|
||||
var reportUserModel = Provider.of<ReportUserModel>(context);
|
||||
if (_searchInput.text == '') {
|
||||
showMsgDialog(context, "Error", 'Please fill the search field');
|
||||
}
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
|
||||
try {
|
||||
List<User> users = await reportUserModel.findUser(_searchInput.text);
|
||||
if (users.isEmpty) return;
|
||||
setState(() {
|
||||
this._users = users;
|
||||
});
|
||||
} catch (e) {
|
||||
setState(() {
|
||||
this.isSend = false;
|
||||
});
|
||||
showMsgDialog(context, "Error", e.toString());
|
||||
} finally {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
List<Widget> _getUsers(BuildContext context) {
|
||||
return _users.asMap().entries.map((u) {
|
||||
return Container(
|
||||
child: Card(
|
||||
elevation: 10,
|
||||
color: Colors.white,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
this.selectedUser = u.value;
|
||||
isSend = true;
|
||||
_name.text = selectedUser.name;
|
||||
selectedIndex = u.key;
|
||||
});
|
||||
},
|
||||
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: 32.0 - 15.0 / 2),
|
||||
child: Icon(
|
||||
Icons.account_circle,
|
||||
color: Colors.grey,
|
||||
size: 50,
|
||||
),
|
||||
),
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
u.value.name == null ? "" : u.value.name,
|
||||
style: new TextStyle(
|
||||
fontSize: 15.0, color: Colors.black),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
selectedIndex != null && selectedIndex == u.key
|
||||
? Container(
|
||||
padding: EdgeInsets.only(right: 25),
|
||||
child: Icon(Icons.check))
|
||||
: Container()
|
||||
],
|
||||
),
|
||||
),
|
||||
));
|
||||
}).toList();
|
||||
}
|
||||
|
||||
_save(BuildContext context) async {
|
||||
var reportUserModel = Provider.of<ReportUserModel>(context);
|
||||
|
||||
if (selectedUser == null) return;
|
||||
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
|
||||
reportUserModel.getUsersForReport(widget.report.id).then((users) async {
|
||||
if (users.any((u) => u.userID == this.selectedUser.docID)) {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
showMsgDialog(context, "Error", 'Duplicate User');
|
||||
} else {
|
||||
try {
|
||||
ReportUser _reportUser = ReportUser(
|
||||
userID: this.selectedUser.docID,
|
||||
userName: this.selectedUser.name,
|
||||
reportID: widget.report.id,
|
||||
reportName: widget.report.display);
|
||||
|
||||
await reportUserModel.assignUser(_reportUser);
|
||||
reportUserModel.getUsersForReport(widget.report.id).then((users) {
|
||||
Navigator.pop<List<ReportUser>>(context, users);
|
||||
});
|
||||
} catch (e) {
|
||||
showMsgDialog(context, "Error", e.toString());
|
||||
} finally {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
220
lib/pages/report_user_list.dart
Normal file
220
lib/pages/report_user_list.dart
Normal file
@@ -0,0 +1,220 @@
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/report_user_model.dart';
|
||||
import 'package:fcs/vo/report.dart';
|
||||
import 'package:fcs/vo/report_user.dart';
|
||||
import 'package:fcs/widget/local_text.dart';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:fcs/widget/progress.dart';
|
||||
|
||||
import '../theme/theme.dart';
|
||||
import 'report_user_editor.dart';
|
||||
import 'util.dart';
|
||||
|
||||
class ReportUserList extends StatefulWidget {
|
||||
final Report report;
|
||||
|
||||
const ReportUserList({Key key, this.report}) : super(key: key);
|
||||
@override
|
||||
_ReportUserListState createState() => _ReportUserListState();
|
||||
}
|
||||
|
||||
class _ReportUserListState extends State<ReportUserList> {
|
||||
Report _report = new Report();
|
||||
final double dotSize = 15.0;
|
||||
bool _isLoading = false;
|
||||
List<ReportUser> _users = [];
|
||||
bool isForAllUsers = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
if (widget.report != null) {
|
||||
this._report = widget.report;
|
||||
var reportUserModel =
|
||||
Provider.of<ReportUserModel>(context, listen: false);
|
||||
reportUserModel.getUsersForReport(this._report.id).then((users) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
this._users = users;
|
||||
});
|
||||
}
|
||||
});
|
||||
this.isForAllUsers =
|
||||
widget.report.forAllUser == null ? false : widget.report.forAllUser;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final allUserBox = Container(
|
||||
child: new ListTile(
|
||||
title: new Row(
|
||||
children: <Widget>[
|
||||
new Checkbox(
|
||||
value: isForAllUsers,
|
||||
activeColor: primaryColor,
|
||||
onChanged: (bool value) async {
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
try {
|
||||
setState(() {
|
||||
this.isForAllUsers = value;
|
||||
});
|
||||
this._report.forAllUser = this.isForAllUsers;
|
||||
var reportUserModel = Provider.of<ReportUserModel>(context);
|
||||
await reportUserModel.updateReportForAllUsers(this._report);
|
||||
} catch (e) {
|
||||
showMsgDialog(context, "Error", e.toString());
|
||||
} finally {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
new Text(
|
||||
'All Users',
|
||||
style: TextStyle(
|
||||
fontSize: 15.0,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
)));
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: primaryColor,
|
||||
title: LocalText(
|
||||
context,
|
||||
'report.users.title',
|
||||
translationVariables: [this._report.display],
|
||||
color: Colors.white,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
backgroundColor: primaryColor,
|
||||
child: Icon(Icons.add),
|
||||
onPressed: () async {
|
||||
List<ReportUser> _us = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
ReportUserEditor(report: widget.report)),
|
||||
);
|
||||
|
||||
if (_us == null) return;
|
||||
setState(() {
|
||||
_users.clear();
|
||||
_users.addAll(_us);
|
||||
});
|
||||
},
|
||||
),
|
||||
body: ListView(
|
||||
shrinkWrap: true,
|
||||
children: <Widget>[
|
||||
allUserBox,
|
||||
Container(
|
||||
padding: EdgeInsets.only(left: 10, right: 10),
|
||||
child: Column(
|
||||
children: _getUserRow(context),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 15)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> _getUserRow(BuildContext context) {
|
||||
_users.sort((a, b) => a.userName.compareTo(b.userName));
|
||||
return _users.map((u) {
|
||||
return Container(
|
||||
child: Card(
|
||||
elevation: 10,
|
||||
color: Colors.white,
|
||||
child: InkWell(
|
||||
onTap: () {},
|
||||
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.account_circle,
|
||||
color: primaryColor,
|
||||
size: 50,
|
||||
),
|
||||
),
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
u.userName == null ? "" : u.userName,
|
||||
style: new TextStyle(
|
||||
fontSize: 17.0, color: Colors.black),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
padding: EdgeInsets.only(right: 10),
|
||||
icon: Icon(
|
||||
Icons.delete,
|
||||
color: Colors.grey[700],
|
||||
),
|
||||
onPressed: () {
|
||||
showConfirmDialog(context, "report.user_delete_confirm",
|
||||
() {
|
||||
_delete(context, u);
|
||||
});
|
||||
})
|
||||
],
|
||||
),
|
||||
),
|
||||
));
|
||||
}).toList();
|
||||
}
|
||||
|
||||
void _delete(BuildContext context, ReportUser reportUser) async {
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
try {
|
||||
var reportUserModel = Provider.of<ReportUserModel>(context);
|
||||
await reportUserModel.deleteReportUser(reportUser);
|
||||
|
||||
reportUserModel.getUsersForReport(widget.report.id).then((users) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
this._users = users;
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
showMsgDialog(context, "Error", e.toString());
|
||||
} finally {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
368
lib/pages/reset_password.dart
Normal file
368
lib/pages/reset_password.dart
Normal file
@@ -0,0 +1,368 @@
|
||||
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/widget/local_text.dart';
|
||||
import 'package:fcs/widget/localization/app_translations.dart';
|
||||
import 'package:fcs/widget/progress.dart';
|
||||
|
||||
import '../theme/theme.dart' as Theme;
|
||||
import 'util.dart';
|
||||
|
||||
class ResetPasswordPage extends StatefulWidget {
|
||||
final String phoneNumber;
|
||||
ResetPasswordPage(
|
||||
this.phoneNumber, {
|
||||
Key key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
_ResetPasswordPageState createState() => new _ResetPasswordPageState();
|
||||
}
|
||||
|
||||
class _ResetPasswordPageState extends State<ResetPasswordPage>
|
||||
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,
|
||||
'reset.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: myFocusNodeEmail,
|
||||
controller: _smsController,
|
||||
keyboardType: TextInputType.phone,
|
||||
style: TextStyle(
|
||||
fontFamily: "WorkSansSemiBold",
|
||||
fontSize: 16.0,
|
||||
color: Colors.black),
|
||||
decoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
icon: Icon(
|
||||
FontAwesomeIcons.sms,
|
||||
color: Colors.black,
|
||||
),
|
||||
labelText: AppTranslations.of(context)
|
||||
.text("reset.sms"),
|
||||
labelStyle:
|
||||
Provider.of<LanguageModel>(context).isEng
|
||||
? TextStyle(
|
||||
fontFamily: "WorkSansSemiBold",
|
||||
color: Colors.grey)
|
||||
: TextStyle(
|
||||
fontFamily: "MyanmarUnicode",
|
||||
color: Colors.grey),
|
||||
),
|
||||
validator: _validateSMSCode),
|
||||
),
|
||||
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: _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("reset.new_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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
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("reset"),
|
||||
style: Provider.of<LanguageModel>(context).isEng
|
||||
? TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 18.0,
|
||||
fontFamily: "WorkSansBold")
|
||||
: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 18.0,
|
||||
fontFamily: "MyanmarUnicode"),
|
||||
),
|
||||
),
|
||||
onPressed: () => _reset(context)),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _toggleLogin() {
|
||||
setState(() {
|
||||
_obscureTextLogin = !_obscureTextLogin;
|
||||
});
|
||||
}
|
||||
|
||||
void _toggleSignup() {
|
||||
setState(() {
|
||||
_obscureTextSignup = !_obscureTextSignup;
|
||||
});
|
||||
}
|
||||
|
||||
void _toggleSignupConfirm() {
|
||||
setState(() {
|
||||
_obscureTextSignupConfirm = !_obscureTextSignupConfirm;
|
||||
});
|
||||
}
|
||||
|
||||
void _reset(BuildContext context) async {
|
||||
if (!formKey.currentState.validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var smsCode = _smsController.text;
|
||||
var password = _passwordController.text;
|
||||
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
|
||||
UserModel userModel = Provider.of<UserModel>(context);
|
||||
try {
|
||||
await userModel.resetPassword(widget.phoneNumber, password, smsCode);
|
||||
Navigator.pushNamedAndRemoveUntil(context, "/login", (r) => false);
|
||||
} 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.confirm_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;
|
||||
}
|
||||
|
||||
String _validateSMSCode(value) {
|
||||
if (value.isEmpty) {
|
||||
return AppTranslations.of(context).text("login.sms_empty");
|
||||
}
|
||||
if (value.length != 6) {
|
||||
return AppTranslations.of(context).text("login.sms_size");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
113
lib/pages/search_page.dart
Normal file
113
lib/pages/search_page.dart
Normal file
@@ -0,0 +1,113 @@
|
||||
// Copyright 2019 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/buyer_model.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/vo/buyer.dart';
|
||||
|
||||
import 'buyer_list_row.dart';
|
||||
|
||||
Future<Buyer> showPlacesSearch(BuildContext context) async =>
|
||||
await showSearch<Buyer>(
|
||||
context: context,
|
||||
delegate: UserSearchDelegate(),
|
||||
);
|
||||
|
||||
class UserSearchDelegate extends SearchDelegate<Buyer> {
|
||||
@override
|
||||
ThemeData appBarTheme(BuildContext context) {
|
||||
final ThemeData theme = Theme.of(context);
|
||||
return theme.copyWith(
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
hintStyle: TextStyle(
|
||||
color: theme.primaryTextTheme.title.color, fontSize: 16)),
|
||||
primaryColor: primaryColor,
|
||||
primaryIconTheme: theme.primaryIconTheme.copyWith(color: Colors.white),
|
||||
primaryColorBrightness: Brightness.light,
|
||||
primaryTextTheme: theme.textTheme,
|
||||
textTheme: theme.textTheme.copyWith(
|
||||
title: theme.textTheme.title.copyWith(
|
||||
color: theme.primaryTextTheme.title.color, fontSize: 16)),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Widget> buildActions(BuildContext context) {
|
||||
return [
|
||||
IconButton(
|
||||
icon: Icon(Icons.clear),
|
||||
onPressed: () => query = '',
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildLeading(BuildContext context) {
|
||||
return IconButton(
|
||||
icon: Icon(Icons.arrow_back),
|
||||
onPressed: () => close(context, null),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildResults(BuildContext context) {
|
||||
final buyerModel = Provider.of<BuyerModel>(context);
|
||||
return FutureBuilder(
|
||||
future: buyerModel.search(query),
|
||||
builder: (context, AsyncSnapshot<List<Buyer>> snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
if (snapshot.data.length == 0) {
|
||||
return Container(
|
||||
child: Center(
|
||||
child: Text(
|
||||
"Error :No Search Buyer",
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: 15),
|
||||
child: ListView(
|
||||
children:
|
||||
snapshot.data.map((u) => BuyerListRow(buyer: u)).toList(),
|
||||
),
|
||||
);
|
||||
} else if (snapshot.hasError) {
|
||||
return Container(
|
||||
child: Center(
|
||||
child: Text(
|
||||
'${snapshot.error}',
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return Container(
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(
|
||||
valueColor:
|
||||
new AlwaysStoppedAnimation<Color>(primaryColor)),
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildSuggestions(BuildContext context) {
|
||||
return Container(
|
||||
child: Center(
|
||||
child: Opacity(
|
||||
opacity: 0.2,
|
||||
child: Icon(
|
||||
Icons.supervised_user_circle,
|
||||
size: 200,
|
||||
)),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
416
lib/pages/setting_editor.dart
Normal file
416
lib/pages/setting_editor.dart
Normal file
@@ -0,0 +1,416 @@
|
||||
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/util.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/vo/setting.dart';
|
||||
import 'package:fcs/widget/local_text.dart';
|
||||
import 'package:fcs/widget/progress.dart';
|
||||
|
||||
class SettingEidtor extends StatefulWidget {
|
||||
final Setting setting;
|
||||
const SettingEidtor({this.setting});
|
||||
@override
|
||||
_SettingEidtorState createState() => _SettingEidtorState();
|
||||
}
|
||||
|
||||
class _SettingEidtorState extends State<SettingEidtor> {
|
||||
TextEditingController _doExpire = new TextEditingController();
|
||||
TextEditingController _poExpire = new TextEditingController();
|
||||
TextEditingController _poOpend = new TextEditingController();
|
||||
TextEditingController _poClosed = new TextEditingController();
|
||||
TextEditingController _latestDeliveryDay = new TextEditingController();
|
||||
TextEditingController _firstStorageDay = new TextEditingController();
|
||||
TextEditingController _firstStorageCharge = new TextEditingController();
|
||||
TextEditingController _secondStorageDay = new TextEditingController();
|
||||
TextEditingController _secondStorgeCharge = new TextEditingController();
|
||||
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
bool _isLoading = false;
|
||||
List<Day> days = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
days = dayLists;
|
||||
if (widget.setting != null) {
|
||||
_doExpire.text = widget.setting.doExpireInHours.toString();
|
||||
_poExpire.text = widget.setting.poExpireInHours.toString();
|
||||
_poOpend.text = widget.setting.poOpenAt.toString();
|
||||
_poClosed.text = widget.setting.poCloseAt.toString();
|
||||
_latestDeliveryDay.text =widget.setting.latestDeliveryDay.toString();
|
||||
_firstStorageDay.text = widget.setting.firstStorageChargeIn.toString();
|
||||
_firstStorageCharge.text = widget.setting.firstStorageCharge.toString();
|
||||
_secondStorageDay.text = widget.setting.secondStorageChargeIn.toString();
|
||||
_secondStorgeCharge.text = widget.setting.secondStorageCharge.toString();
|
||||
|
||||
days.forEach((d) => widget.setting.poCloseOn.contains(d.id)
|
||||
? d.isChecked = true
|
||||
: d.isChecked = false);
|
||||
}
|
||||
}
|
||||
|
||||
Widget showDayList(BuildContext context, MainModel mainModel) {
|
||||
return Container(
|
||||
margin: EdgeInsets.symmetric(vertical: 5.0),
|
||||
height: 75.0,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 10, top: 10),
|
||||
child: Text(
|
||||
"PO submission closed Day",
|
||||
style: TextStyle(color: Colors.black54),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
itemCount: days.length,
|
||||
scrollDirection: Axis.horizontal,
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return new Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
new Checkbox(
|
||||
value: days[index].isChecked == null
|
||||
? false
|
||||
: days[index].isChecked,
|
||||
activeColor: primaryColor,
|
||||
onChanged: (bool value) {
|
||||
setState(() {
|
||||
days[index].isChecked = value;
|
||||
});
|
||||
}),
|
||||
Container(
|
||||
padding: EdgeInsets.only(top: 13),
|
||||
child: new Text(
|
||||
dayLists[index].name,
|
||||
style: TextStyle(
|
||||
fontSize: 15.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var languageModel = Provider.of<LanguageModel>(context);
|
||||
var mainModel = Provider.of<MainModel>(context);
|
||||
|
||||
final doExpireBox = TextFormField(
|
||||
controller: _doExpire,
|
||||
autofocus: false,
|
||||
cursorColor: primaryColor,
|
||||
keyboardType: TextInputType.phone,
|
||||
style: textStyle,
|
||||
decoration: new InputDecoration(
|
||||
labelText: 'DO expired Time',
|
||||
labelStyle: languageModel.isEng ? labelStyle : labelStyleMM,
|
||||
icon: Icon(
|
||||
FontAwesomeIcons.clock,
|
||||
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 DO expired Time';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
);
|
||||
|
||||
final poExpireBox = TextFormField(
|
||||
controller: _poExpire,
|
||||
autofocus: false,
|
||||
cursorColor: primaryColor,
|
||||
keyboardType: TextInputType.phone,
|
||||
style: textStyle,
|
||||
decoration: new InputDecoration(
|
||||
labelText: 'PO expired Time',
|
||||
labelStyle: languageModel.isEng ? labelStyle : labelStyleMM,
|
||||
icon: Icon(
|
||||
FontAwesomeIcons.clock,
|
||||
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 PO expired Time';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
);
|
||||
|
||||
final poOpenedBox = TextFormField(
|
||||
controller: _poOpend,
|
||||
autofocus: false,
|
||||
cursorColor: primaryColor,
|
||||
keyboardType: TextInputType.phone,
|
||||
style: textStyle,
|
||||
decoration: new InputDecoration(
|
||||
labelText: 'PO submission opened Time',
|
||||
labelStyle: languageModel.isEng ? labelStyle : labelStyleMM,
|
||||
icon: Icon(
|
||||
FontAwesomeIcons.clock,
|
||||
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 PO submission opened Time';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
);
|
||||
|
||||
final poClosedBox = TextFormField(
|
||||
controller: _poClosed,
|
||||
autofocus: false,
|
||||
cursorColor: primaryColor,
|
||||
keyboardType: TextInputType.phone,
|
||||
style: textStyle,
|
||||
decoration: new InputDecoration(
|
||||
labelText: 'PO submission closed Time',
|
||||
labelStyle: languageModel.isEng ? labelStyle : labelStyleMM,
|
||||
icon: Icon(
|
||||
FontAwesomeIcons.clock,
|
||||
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 PO submission closed Time';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
);
|
||||
|
||||
final latestDeliveryDayBox = TextFormField(
|
||||
controller: _latestDeliveryDay,
|
||||
autofocus: false,
|
||||
cursorColor: primaryColor,
|
||||
keyboardType: TextInputType.phone,
|
||||
style: textStyle,
|
||||
decoration: new InputDecoration(
|
||||
labelText: 'Latest Delivery Day',
|
||||
labelStyle: languageModel.isEng ? labelStyle : labelStyleMM,
|
||||
icon: Icon(
|
||||
FontAwesomeIcons.clock,
|
||||
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 Latest Delivery Day';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
);
|
||||
|
||||
final firstStorageDayBox = TextFormField(
|
||||
controller: _firstStorageDay,
|
||||
autofocus: false,
|
||||
cursorColor: primaryColor,
|
||||
keyboardType: TextInputType.phone,
|
||||
style: textStyle,
|
||||
decoration: new InputDecoration(
|
||||
labelText: 'First storage charge starts Day',
|
||||
labelStyle: languageModel.isEng ? labelStyle : labelStyleMM,
|
||||
icon: Icon(
|
||||
FontAwesomeIcons.calendarDay,
|
||||
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 First storage charge starts Day';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
);
|
||||
|
||||
final firstStorgeChargeBox = TextFormField(
|
||||
controller: _firstStorageCharge,
|
||||
autofocus: false,
|
||||
cursorColor: primaryColor,
|
||||
keyboardType: TextInputType.phone,
|
||||
style: textStyle,
|
||||
decoration: new InputDecoration(
|
||||
labelText: 'First storage charge Fees',
|
||||
labelStyle: languageModel.isEng ? labelStyle : labelStyleMM,
|
||||
icon: Icon(
|
||||
FontAwesomeIcons.sortNumericUp,
|
||||
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 First storage charge Fees';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
);
|
||||
|
||||
final secondStorgeDayBox = TextFormField(
|
||||
controller: _secondStorageDay,
|
||||
autofocus: false,
|
||||
cursorColor: primaryColor,
|
||||
keyboardType: TextInputType.phone,
|
||||
style: textStyle,
|
||||
decoration: new InputDecoration(
|
||||
labelText: 'Second storage charge starts Day',
|
||||
labelStyle: languageModel.isEng ? labelStyle : labelStyleMM,
|
||||
icon: Icon(
|
||||
FontAwesomeIcons.calendarDay,
|
||||
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 Second storage charge starts Day';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
);
|
||||
|
||||
final secondStorgeChargeBox = TextFormField(
|
||||
controller: _secondStorgeCharge,
|
||||
autofocus: false,
|
||||
cursorColor: primaryColor,
|
||||
keyboardType: TextInputType.phone,
|
||||
style: textStyle,
|
||||
decoration: new InputDecoration(
|
||||
labelText: 'Second storage charge Fees',
|
||||
labelStyle: languageModel.isEng ? labelStyle : labelStyleMM,
|
||||
icon: Icon(
|
||||
FontAwesomeIcons.sortNumericUp,
|
||||
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 Second storage charge Fees';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
);
|
||||
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: primaryColor,
|
||||
title: LocalText(
|
||||
context,
|
||||
"setting.title",
|
||||
fontSize: 20,
|
||||
color: Colors.white,
|
||||
),
|
||||
actions: <Widget>[
|
||||
IconButton(
|
||||
icon: Icon(Icons.send),
|
||||
onPressed: () {
|
||||
if (!_formKey.currentState.validate()) return;
|
||||
showConfirmDialog(context, "setting.confirm", () {
|
||||
_submit();
|
||||
});
|
||||
})
|
||||
],
|
||||
),
|
||||
body: Form(
|
||||
key: _formKey,
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
padding: EdgeInsets.only(left: 24.0, right: 24.0),
|
||||
children: <Widget>[
|
||||
// doExpireBox,
|
||||
poExpireBox,
|
||||
poOpenedBox,
|
||||
poClosedBox,
|
||||
showDayList(context, mainModel),
|
||||
latestDeliveryDayBox,
|
||||
firstStorageDayBox,
|
||||
firstStorgeChargeBox,
|
||||
secondStorgeDayBox,
|
||||
secondStorgeChargeBox
|
||||
],
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
_submit() async {
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
try {
|
||||
widget.setting.poExpireInHours = int.parse(_poExpire.text);
|
||||
widget.setting.poOpenAt = int.parse(_poOpend.text);
|
||||
widget.setting.poCloseAt = int.parse(_poClosed.text);
|
||||
widget.setting.latestDeliveryDay=int.parse(_latestDeliveryDay.text);
|
||||
widget.setting.firstStorageChargeIn = int.parse(_firstStorageDay.text);
|
||||
widget.setting.firstStorageCharge = int.parse(_firstStorageCharge.text);
|
||||
widget.setting.secondStorageChargeIn = int.parse(_secondStorageDay.text);
|
||||
widget.setting.secondStorageCharge = int.parse(_secondStorgeCharge.text);
|
||||
widget.setting.poCloseOn =
|
||||
this.days.where((d) => d.isChecked == true).map((p) => p.id).toList();
|
||||
var mainModel = Provider.of<MainModel>(context);
|
||||
await mainModel.updateSetting(widget.setting);
|
||||
Navigator.pop(context);
|
||||
} catch (e) {
|
||||
showMsgDialog(context, "Error", e.toString());
|
||||
} finally {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
256
lib/pages/setting_editor_byOwner.dart
Normal file
256
lib/pages/setting_editor_byOwner.dart
Normal file
@@ -0,0 +1,256 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/main_model.dart';
|
||||
import 'package:fcs/pages/util.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/vo/setting.dart';
|
||||
import 'package:fcs/widget/local_text.dart';
|
||||
import 'package:fcs/widget/progress.dart';
|
||||
|
||||
class Time {
|
||||
final int value;
|
||||
Time(this.value);
|
||||
String get getValue =>
|
||||
value > 12 ? (value - 12).toString() + "PM" : value.toString() + "AM";
|
||||
|
||||
@override
|
||||
bool operator ==(other) {
|
||||
if (identical(this, other)) {
|
||||
return true;
|
||||
}
|
||||
return other.value == this.value;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
int result = 17;
|
||||
result = 37 * result + value.hashCode;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
class SettingOwner extends StatefulWidget {
|
||||
final Setting setting;
|
||||
const SettingOwner({this.setting});
|
||||
@override
|
||||
_SettingOwnerState createState() => _SettingOwnerState();
|
||||
}
|
||||
|
||||
class _SettingOwnerState extends State<SettingOwner> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
bool _isLoading = false;
|
||||
List<Day> days = [];
|
||||
int poOpenAt = 0;
|
||||
int poCloseAt = 0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
days = dayLists;
|
||||
if (widget.setting != null) {
|
||||
poOpenAt = widget.setting.poOpenAt;
|
||||
poCloseAt = widget.setting.poCloseAt;
|
||||
days.forEach((d) => widget.setting.poCloseOn.contains(d.id)
|
||||
? d.isChecked = true
|
||||
: d.isChecked = false);
|
||||
}
|
||||
}
|
||||
|
||||
Widget showDayList(BuildContext context, MainModel mainModel) {
|
||||
return Container(
|
||||
margin: EdgeInsets.symmetric(vertical: 5.0),
|
||||
height: 500.0,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 10, top: 10),
|
||||
child: Text(
|
||||
"PO submission closed Day",
|
||||
style: TextStyle(color: Colors.black54),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
itemCount: days.length,
|
||||
scrollDirection: Axis.vertical,
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return new Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
new Checkbox(
|
||||
value: days[index].isChecked == null
|
||||
? false
|
||||
: days[index].isChecked,
|
||||
activeColor: primaryColor,
|
||||
onChanged: (bool value) {
|
||||
setState(() {
|
||||
days[index].isChecked = value;
|
||||
});
|
||||
}),
|
||||
Container(
|
||||
padding: EdgeInsets.only(top: 13),
|
||||
child: new Text(
|
||||
dayLists[index].name,
|
||||
style: TextStyle(
|
||||
fontSize: 15.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var mainModel = Provider.of<MainModel>(context);
|
||||
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: primaryColor,
|
||||
title: LocalText(
|
||||
context,
|
||||
"setting.title",
|
||||
fontSize: 20,
|
||||
color: Colors.white,
|
||||
),
|
||||
actions: <Widget>[
|
||||
IconButton(
|
||||
icon: Icon(Icons.send),
|
||||
onPressed: () {
|
||||
if (!_formKey.currentState.validate()) return;
|
||||
showConfirmDialog(context, "setting.confirm", () {
|
||||
_submit();
|
||||
});
|
||||
})
|
||||
],
|
||||
),
|
||||
body: ListView(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 24),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text("PO submission opened at:"),
|
||||
),
|
||||
getDropDown(poOpenAt, (value) {
|
||||
setState(() {
|
||||
setState(() {
|
||||
this.poOpenAt = value;
|
||||
});
|
||||
});
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 24),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text("PO submission closed at:"),
|
||||
),
|
||||
getDropDown(poCloseAt, (value) {
|
||||
setState(() {
|
||||
setState(() {
|
||||
this.poCloseAt = value;
|
||||
});
|
||||
});
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
Form(
|
||||
key: _formKey,
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
padding: EdgeInsets.only(left: 24.0, right: 24.0),
|
||||
children: <Widget>[
|
||||
showDayList(context, mainModel),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
Widget getDropDown(int initial, Function(int) onChanged) {
|
||||
Time value = Time(initial);
|
||||
return DropdownButton<Time>(
|
||||
value: value,
|
||||
icon: Icon(Icons.arrow_downward),
|
||||
iconSize: 24,
|
||||
elevation: 16,
|
||||
style: TextStyle(color: Colors.deepPurple),
|
||||
underline: Container(
|
||||
height: 2,
|
||||
color: primaryColor,
|
||||
),
|
||||
onChanged: (value) => onChanged(value.value),
|
||||
items: <Time>[
|
||||
Time(0),
|
||||
Time(1),
|
||||
Time(2),
|
||||
Time(3),
|
||||
Time(4),
|
||||
Time(5),
|
||||
Time(6),
|
||||
Time(7),
|
||||
Time(8),
|
||||
Time(9),
|
||||
Time(10),
|
||||
Time(11),
|
||||
Time(12),
|
||||
Time(13),
|
||||
Time(14),
|
||||
Time(15),
|
||||
Time(16),
|
||||
Time(17),
|
||||
Time(18),
|
||||
Time(19),
|
||||
Time(20),
|
||||
Time(21),
|
||||
Time(22),
|
||||
Time(23),
|
||||
].map<DropdownMenuItem<Time>>((Time value) {
|
||||
return DropdownMenuItem<Time>(
|
||||
value: value,
|
||||
child: Text(value.getValue),
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
_submit() async {
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
try {
|
||||
widget.setting.poCloseOn =
|
||||
this.days.where((d) => d.isChecked == true).map((p) => p.id).toList();
|
||||
widget.setting.poOpenAt = poOpenAt;
|
||||
widget.setting.poCloseAt = poCloseAt;
|
||||
var mainModel = Provider.of<MainModel>(context);
|
||||
await mainModel.updateSetting(widget.setting);
|
||||
Navigator.pop(context);
|
||||
} catch (e) {
|
||||
showMsgDialog(context, "Error", e.toString());
|
||||
} finally {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
198
lib/pages/settings.dart
Normal file
198
lib/pages/settings.dart
Normal file
@@ -0,0 +1,198 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/main_model.dart';
|
||||
import 'package:fcs/pages/setting_editor.dart';
|
||||
import 'package:fcs/pages/setting_editor_byOwner.dart';
|
||||
import 'package:fcs/vo/setting.dart';
|
||||
import 'package:fcs/widget/local_text.dart';
|
||||
import 'package:fcs/widget/progress.dart';
|
||||
|
||||
import '../theme/theme.dart';
|
||||
|
||||
class Settings extends StatefulWidget {
|
||||
@override
|
||||
_SettingState createState() => _SettingState();
|
||||
}
|
||||
|
||||
class _SettingState extends State<Settings> {
|
||||
bool _isLoading = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
MainModel mainModel = Provider.of<MainModel>(context);
|
||||
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: LocalText(
|
||||
context,
|
||||
"setting.title",
|
||||
fontSize: 20,
|
||||
color: Colors.white,
|
||||
),
|
||||
backgroundColor: primaryColor,
|
||||
actions: <Widget>[
|
||||
mainModel.isBizAdmin() || mainModel.isSysAdmin()
|
||||
? IconButton(
|
||||
icon: Icon(Icons.edit),
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => SettingEidtor(
|
||||
setting: mainModel.setting,
|
||||
)),
|
||||
);
|
||||
})
|
||||
: Container()
|
||||
],
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
padding: EdgeInsets.only(
|
||||
left: 25.0,
|
||||
right: 25.0,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
ListTile(
|
||||
dense: true,
|
||||
title: Text("PO will expire in"),
|
||||
subtitle: Text("After PO is submitted"),
|
||||
trailing: Text(
|
||||
mainModel.setting.poExpireInHours.toString() + " hours"),
|
||||
),
|
||||
Divider(color: secondaryColor),
|
||||
Container(
|
||||
alignment: Alignment.topRight,
|
||||
child: mainModel.isOwnerAndAbove() || mainModel.isAdmin()
|
||||
? IconButton(
|
||||
padding: EdgeInsets.all(0),
|
||||
visualDensity: VisualDensity.compact,
|
||||
icon: Icon(Icons.edit),
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => SettingOwner(
|
||||
setting: mainModel.setting,
|
||||
)),
|
||||
);
|
||||
})
|
||||
: SizedBox(),
|
||||
),
|
||||
ListTile(
|
||||
dense: true,
|
||||
title: Text("PO submission opened at"),
|
||||
trailing: Text("${mainModel.setting.getPoOpenAt}"),
|
||||
),
|
||||
ListTile(
|
||||
dense: true,
|
||||
title: Text("PO submission closed at"),
|
||||
trailing: Text("${mainModel.setting.getPoCloseAt}"),
|
||||
),
|
||||
ListTile(
|
||||
dense: true,
|
||||
title: Text("PO submission closed on"),
|
||||
subtitle: Text(
|
||||
mainModel.setting.getPoCloseOn,
|
||||
style: textStyleOdd,
|
||||
),
|
||||
),
|
||||
Divider(color: secondaryColor),
|
||||
ListTile(
|
||||
dense: true,
|
||||
title: Text("Latest Delivery Day at"),
|
||||
trailing: Text("${mainModel.setting.latestDeliveryDay} days"),
|
||||
),
|
||||
Divider(color: secondaryColor),
|
||||
ListTile(
|
||||
dense: true,
|
||||
title: Text("First storage charge starts in"),
|
||||
subtitle: Text("After PO is approved"),
|
||||
trailing:
|
||||
Text("${mainModel.setting.firstStorageChargeIn} days"),
|
||||
),
|
||||
ListTile(
|
||||
dense: true,
|
||||
title: Text(
|
||||
"First storage charge ",
|
||||
style: TextStyle(backgroundColor: Colors.greenAccent),
|
||||
),
|
||||
trailing:
|
||||
Text("${mainModel.setting.firstStorageCharge} Kyats/Liter"),
|
||||
),
|
||||
Divider(color: secondaryColor),
|
||||
ListTile(
|
||||
dense: true,
|
||||
title: Text("Second storage charge starts in"),
|
||||
subtitle: Text("After PO is approved"),
|
||||
trailing:
|
||||
Text("${mainModel.setting.secondStorageChargeIn} days"),
|
||||
),
|
||||
ListTile(
|
||||
dense: true,
|
||||
title: Text(
|
||||
"Second storage charge ",
|
||||
style: TextStyle(backgroundColor: Colors.amber),
|
||||
),
|
||||
trailing: Text(
|
||||
"${mainModel.setting.secondStorageCharge} Kyats/Liter"),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget buildSettingTile(
|
||||
{@required String text,
|
||||
@required BuildContext context,
|
||||
@required String image,
|
||||
@required GestureTapCallback tap}) {
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
tap();
|
||||
},
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 8.0, bottom: 5),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 12.0, right: 20.0),
|
||||
child: Image.asset(
|
||||
image,
|
||||
width: 28,
|
||||
height: 28,
|
||||
color: primaryColor,
|
||||
),
|
||||
),
|
||||
LocalText(
|
||||
context,
|
||||
text,
|
||||
fontSize: 15.0,
|
||||
color: Colors.black87,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
Icon(Icons.keyboard_arrow_right)
|
||||
],
|
||||
),
|
||||
),
|
||||
Divider(
|
||||
color: Colors.grey,
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
103
lib/pages/sign.dart
Normal file
103
lib/pages/sign.dart
Normal file
@@ -0,0 +1,103 @@
|
||||
import 'dart:typed_data';
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_signature_pad/flutter_signature_pad.dart';
|
||||
|
||||
class SignPage extends StatefulWidget {
|
||||
SignPage({Key key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_SignPageState createState() => _SignPageState();
|
||||
}
|
||||
|
||||
class _SignPageState extends State<SignPage> {
|
||||
ByteData _img = ByteData(0);
|
||||
var color = Colors.blue;
|
||||
var strokeWidth = 5.0;
|
||||
final _sign = GlobalKey<SignatureState>();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
SystemChrome.setPreferredOrientations([DeviceOrientation.landscapeLeft]);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Column(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: Text("Driver Signature"),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Signature(
|
||||
color: color,
|
||||
key: _sign,
|
||||
onSign: () {
|
||||
final sign = _sign.currentState;
|
||||
},
|
||||
strokeWidth: strokeWidth,
|
||||
),
|
||||
),
|
||||
color: Colors.black12,
|
||||
),
|
||||
),
|
||||
_img.buffer.lengthInBytes == 0
|
||||
? Container()
|
||||
: LimitedBox(
|
||||
maxHeight: 200.0,
|
||||
child: Image.memory(_img.buffer.asUint8List())),
|
||||
Column(
|
||||
children: <Widget>[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: MaterialButton(
|
||||
color: Colors.green,
|
||||
onPressed: () async {
|
||||
final sign = _sign.currentState;
|
||||
final image = await sign.getData();
|
||||
var data = await image.toByteData(
|
||||
format: ui.ImageByteFormat.png);
|
||||
Uint8List buffer = data.buffer.asUint8List();
|
||||
// String path = await _localPath;
|
||||
// File file = await new File(path + "/sign.png")
|
||||
// .writeAsBytes(buffer.asUint8List(
|
||||
// data.offsetInBytes, data.lengthInBytes));
|
||||
Navigator.pop(context, buffer);
|
||||
},
|
||||
child: Text("OK")),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: MaterialButton(
|
||||
color: Colors.grey,
|
||||
onPressed: () {
|
||||
final sign = _sign.currentState;
|
||||
sign.clear();
|
||||
setState(() {
|
||||
_img = ByteData(0);
|
||||
});
|
||||
},
|
||||
child: Text("Clear")),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
103
lib/pages/sms_page.dart
Normal file
103
lib/pages/sms_page.dart
Normal file
@@ -0,0 +1,103 @@
|
||||
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/theme/theme.dart';
|
||||
import 'package:fcs/widget/localization/app_translations.dart';
|
||||
import 'package:fcs/widget/progress.dart';
|
||||
|
||||
import 'util.dart';
|
||||
|
||||
class SmsCodePage extends StatefulWidget {
|
||||
final String id, password;
|
||||
|
||||
const SmsCodePage({Key key, this.id, this.password}) : super(key: key);
|
||||
@override
|
||||
_SmsCodePageState createState() => _SmsCodePageState();
|
||||
}
|
||||
|
||||
class _SmsCodePageState extends State<SmsCodePage> {
|
||||
final TextEditingController _sms = new TextEditingController();
|
||||
bool _isLoading = false;
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
|
||||
_confimSignin() async {
|
||||
MainModel mainModel = Provider.of<MainModel>(context);
|
||||
if (!_formKey.currentState.validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
try {
|
||||
await mainModel.confirmSignup(widget.id, widget.password, _sms.text);
|
||||
await mainModel.login(widget.id, widget.password);
|
||||
Navigator.pushNamedAndRemoveUntil(context, "/", (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: AppTranslations.of(context).text("sms.sms"),
|
||||
labelStyle: labelStyle,
|
||||
hintText: 'eg. 123456',
|
||||
icon: Icon(
|
||||
Icons.lock,
|
||||
color: primaryColor,
|
||||
)),
|
||||
validator: (value) {
|
||||
if (value.isEmpty) {
|
||||
return AppTranslations.of(context).text("sms.empty");
|
||||
}
|
||||
return null;
|
||||
},
|
||||
);
|
||||
|
||||
final singInButton = Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 16.0),
|
||||
child: RaisedButton(
|
||||
onPressed: () => _confimSignin(),
|
||||
padding: EdgeInsets.all(12),
|
||||
color: primaryColor,
|
||||
child: Text(AppTranslations.of(context).text("singin"),
|
||||
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold)),
|
||||
),
|
||||
);
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(AppTranslations.of(context).text("input_sms")),
|
||||
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),
|
||||
singInButton,
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
137
lib/pages/splash.dart
Normal file
137
lib/pages/splash.dart
Normal file
@@ -0,0 +1,137 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io' show Platform;
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_api_availability/google_api_availability.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/main_model.dart';
|
||||
import 'package:fcs/model/shared_pref.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/widget/local_text.dart';
|
||||
import 'package:fcs/widget/localization/app_translations.dart';
|
||||
|
||||
class SplashScreen extends StatefulWidget {
|
||||
@override
|
||||
_SplashScreenState createState() => new _SplashScreenState();
|
||||
}
|
||||
|
||||
class _SplashScreenState extends State<SplashScreen> {
|
||||
final log = Logger('_SplashScreenState');
|
||||
|
||||
bool _loaded = false;
|
||||
bool _isSupport = false;
|
||||
bool _isGoogleService = true;
|
||||
bool _isLogin = false;
|
||||
bool _isAgree = false;
|
||||
bool _hasEmail = false;
|
||||
bool _isOnline = true;
|
||||
Timer timer;
|
||||
|
||||
startTime() async {
|
||||
var _duration = new Duration(milliseconds: 500);
|
||||
this.timer = new Timer.periodic(_duration, navigationPage);
|
||||
}
|
||||
|
||||
void navigationPage(Timer timer) async {
|
||||
if (!_isOnline) {
|
||||
return;
|
||||
}
|
||||
|
||||
// GooglePlayServicesAvailability availability = await GoogleApiAvailability
|
||||
// .instance
|
||||
// .checkGooglePlayServicesAvailability(true);
|
||||
// log.info("GooglePlaysServcie Result1:$availability");
|
||||
// if (availability != GooglePlayServicesAvailability.success &&
|
||||
// Platform.isAndroid) {
|
||||
// timer.cancel();
|
||||
// setState(() {
|
||||
// _isGoogleService = false;
|
||||
// });
|
||||
// return;
|
||||
// }
|
||||
|
||||
if (_loaded) {
|
||||
timer.cancel();
|
||||
|
||||
Navigator.of(context).pushReplacementNamed('/home');
|
||||
// if (_isSupport) {
|
||||
// if (_isLogin) {
|
||||
// if (!_isAgree) {
|
||||
// await Navigator.of(context).pushNamed('/term');
|
||||
// startTime();
|
||||
// } else {
|
||||
// bool skipped = await SharedPref.getSkippedRecoverEmail();
|
||||
// skipped = skipped ?? false;
|
||||
// if (!this._hasEmail && !skipped) {
|
||||
// Navigator.of(context).pushReplacementNamed('/email');
|
||||
// } else {
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// Navigator.of(context).pushReplacementNamed('/welcome');
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@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._loaded = mainModel.isLoaded;
|
||||
this._isSupport = mainModel.isSupport();
|
||||
this._isLogin = mainModel.isLogin();
|
||||
this._isAgree = mainModel.agreedTerm();
|
||||
this._hasEmail = mainModel.hasEmail();
|
||||
this._isOnline = mainModel.isOnline;
|
||||
|
||||
return new Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
body: new Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
new Image.asset(
|
||||
"assets/logo.png",
|
||||
width: 180,
|
||||
),
|
||||
SizedBox(height: 50),
|
||||
CircularProgressIndicator(
|
||||
valueColor: new AlwaysStoppedAnimation<Color>(primaryColor),
|
||||
),
|
||||
SizedBox(height: 30),
|
||||
_isOnline
|
||||
? Container()
|
||||
: Column(
|
||||
children: <Widget>[
|
||||
LocalText(context, "offline.status"),
|
||||
],
|
||||
),
|
||||
Text(_isGoogleService ? "" : "Google Play service not found."),
|
||||
Text(
|
||||
_loaded
|
||||
? (!_isSupport
|
||||
? "Version outdated, please update your app!"
|
||||
: "")
|
||||
: AppTranslations.of(context).text("load"),
|
||||
style: TextStyle(
|
||||
color: primaryColor, fontWeight: FontWeight.bold)),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
249
lib/pages/storage/inventory_item.dart
Normal file
249
lib/pages/storage/inventory_item.dart
Normal file
@@ -0,0 +1,249 @@
|
||||
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/product_model.dart';
|
||||
import 'package:fcs/model/storage_model.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/vo/inventory_line.dart';
|
||||
import 'package:fcs/vo/product.dart';
|
||||
import 'package:fcs/vo/storage.dart';
|
||||
import 'package:fcs/widget/local_text.dart';
|
||||
import 'package:fcs/widget/localization/app_translations.dart';
|
||||
import 'package:fcs/widget/progress.dart';
|
||||
|
||||
class InventoryItem extends StatefulWidget {
|
||||
final InventoryLine inventoryLine;
|
||||
|
||||
const InventoryItem({Key key, this.inventoryLine}) : super(key: key);
|
||||
|
||||
@override
|
||||
_InventoryItemState createState() => _InventoryItemState();
|
||||
}
|
||||
|
||||
class _InventoryItemState extends State<InventoryItem> {
|
||||
InventoryLine inventoryLine = new InventoryLine();
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
bool _isLoading = false;
|
||||
String currentStorageID, currentProductID;
|
||||
TextEditingController _volumeController = new TextEditingController();
|
||||
TextEditingController _oldVolume = new TextEditingController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (widget.inventoryLine != null) {
|
||||
this.inventoryLine = widget.inventoryLine;
|
||||
this.inventoryLine.action = "update";
|
||||
_oldVolume.text = this.inventoryLine.oldQty.toString();
|
||||
} else {
|
||||
this.inventoryLine.action = "create";
|
||||
}
|
||||
|
||||
_volumeController.text =
|
||||
inventoryLine.quantity == null ? "" : inventoryLine.quantity.toString();
|
||||
this.currentStorageID = inventoryLine.storageID;
|
||||
this.currentProductID = inventoryLine.productID;
|
||||
}
|
||||
|
||||
Widget showStorageList(BuildContext context, StorageModel storageModel) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: <Widget>[
|
||||
Image.asset(
|
||||
"assets/inventory.png",
|
||||
color: primaryColor,
|
||||
width: 25,
|
||||
),
|
||||
SizedBox(
|
||||
width: 18,
|
||||
),
|
||||
new Flexible(
|
||||
child: Container(
|
||||
width: 170.0,
|
||||
child: DropdownButton<String>(
|
||||
value: currentStorageID,
|
||||
isExpanded: true,
|
||||
hint: Text(
|
||||
'Select Storage',
|
||||
style: labelStyle,
|
||||
),
|
||||
onChanged: changedDropDownItem,
|
||||
items: storageModel.storages
|
||||
.map<DropdownMenuItem<String>>((Storage storage) {
|
||||
return new DropdownMenuItem<String>(
|
||||
value: storage.id,
|
||||
child: new Text(storage.name, style: textStyle),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
void changedDropDownItem(selected) {
|
||||
setState(() {
|
||||
currentStorageID = selected;
|
||||
});
|
||||
}
|
||||
|
||||
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) {
|
||||
var storageModel = Provider.of<StorageModel>(context);
|
||||
var productModel = Provider.of<ProductModel>(context);
|
||||
var languageModel = Provider.of<LanguageModel>(context);
|
||||
|
||||
final oldVolumeBox = Container(
|
||||
padding: EdgeInsets.only(left: 5, top: 15, bottom: 10),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
LocalText(context, "inventory.old.qty"),
|
||||
Container(
|
||||
padding: EdgeInsets.only(left: 20),
|
||||
child: Text(
|
||||
_oldVolume.text,
|
||||
style: textStyle,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: primaryColor,
|
||||
title: LocalText(
|
||||
context,
|
||||
'inventory.item',
|
||||
fontSize: 20,
|
||||
color: Colors.white,
|
||||
),
|
||||
actions: <Widget>[
|
||||
IconButton(
|
||||
icon: Icon(Icons.delete),
|
||||
onPressed: () {
|
||||
_delete();
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.save),
|
||||
onPressed: () {
|
||||
if (!_formKey.currentState.validate()) return;
|
||||
_save();
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
body: Form(
|
||||
key: _formKey,
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
padding: EdgeInsets.only(left: 24.0, right: 24.0),
|
||||
children: <Widget>[
|
||||
widget.inventoryLine == null ? Container() : oldVolumeBox,
|
||||
showStorageList(context, storageModel),
|
||||
showProducts(context, productModel),
|
||||
Container(
|
||||
child: TextFormField(
|
||||
keyboardType: TextInputType.number,
|
||||
controller: _volumeController,
|
||||
autofocus: false,
|
||||
cursorColor: primaryColor,
|
||||
decoration: new InputDecoration(
|
||||
labelText:
|
||||
AppTranslations.of(context).text("inventory.new.qty"),
|
||||
labelStyle:
|
||||
languageModel.isEng ? labelStyle : labelStyleMM,
|
||||
icon: Icon(
|
||||
FontAwesomeIcons.sortNumericUpAlt,
|
||||
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("inventory.form.qty");
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
_save() {
|
||||
if (currentProductID == null || currentStorageID == null) return;
|
||||
this.inventoryLine.storageID = currentStorageID;
|
||||
var storageName =
|
||||
Provider.of<StorageModel>(context).getStorageName(currentStorageID);
|
||||
this.inventoryLine.storageName = storageName;
|
||||
this.inventoryLine.productID = currentProductID;
|
||||
var productName =
|
||||
Provider.of<ProductModel>(context).getProductName(currentProductID);
|
||||
this.inventoryLine.productName = productName;
|
||||
this.inventoryLine.quantity = int.parse(_volumeController.text);
|
||||
Navigator.pop<InventoryLine>(context, this.inventoryLine);
|
||||
}
|
||||
|
||||
_delete() {
|
||||
this.inventoryLine.action = "delete";
|
||||
Navigator.pop<InventoryLine>(context, this.inventoryLine);
|
||||
}
|
||||
}
|
||||
166
lib/pages/storage/inventory_take.dart
Normal file
166
lib/pages/storage/inventory_take.dart
Normal file
@@ -0,0 +1,166 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/storage_model.dart';
|
||||
import 'package:fcs/pages/storage/inventory_item.dart';
|
||||
import 'package:fcs/pages/util.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/vo/inventory_line.dart';
|
||||
import 'package:fcs/vo/inventory_taking.dart';
|
||||
import 'package:fcs/widget/local_text.dart';
|
||||
import 'package:fcs/widget/my_data_table.dart';
|
||||
import 'package:fcs/widget/progress.dart';
|
||||
|
||||
class TakeInventory extends StatefulWidget {
|
||||
@override
|
||||
_TakeInventoryState createState() => _TakeInventoryState();
|
||||
}
|
||||
|
||||
class _TakeInventoryState extends State<TakeInventory> {
|
||||
InventoryTaking inventoryTaking = InventoryTaking();
|
||||
bool _isLoading = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
var _storages = Provider.of<StorageModel>(context, listen: false).storages;
|
||||
_storages.forEach((s) {
|
||||
s.products.forEach((p) {
|
||||
inventoryTaking.inventoryLines.add(InventoryLine(
|
||||
productID: p.id,
|
||||
productName: p.name,
|
||||
storageID: p.storageID,
|
||||
storageName: p.storageName,
|
||||
oldQty: p.quantity,
|
||||
quantity: p.quantity));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: primaryColor,
|
||||
title: LocalText(
|
||||
context,
|
||||
'inventory.take',
|
||||
fontSize: 20,
|
||||
color: Colors.white,
|
||||
),
|
||||
actions: <Widget>[
|
||||
IconButton(
|
||||
icon: Icon(Icons.send),
|
||||
onPressed: () {
|
||||
showConfirmDialog(context, "inventory.confirm", () {
|
||||
_submit();
|
||||
});
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
backgroundColor: primaryColor,
|
||||
child: Icon(Icons.add),
|
||||
onPressed: () async {
|
||||
final InventoryLine inventoryLine =
|
||||
await Navigator.push<InventoryLine>(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => InventoryItem()),
|
||||
);
|
||||
_save(inventoryLine);
|
||||
},
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: MyDataTable(
|
||||
columns: [
|
||||
MyDataColumn(
|
||||
label: LocalText(context, "inventory.product")),
|
||||
MyDataColumn(
|
||||
label: LocalText(context, "inventory.storage")),
|
||||
MyDataColumn(
|
||||
label: LocalText(context, "inventory.old.qty"),
|
||||
),
|
||||
MyDataColumn(
|
||||
label: LocalText(context, "inventory.new.qty"),
|
||||
),
|
||||
],
|
||||
rows: getInventoryTable(inventoryTaking.inventoryLines),
|
||||
),
|
||||
),
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
List<MyDataRow> getInventoryTable(List<InventoryLine> invLines) {
|
||||
return invLines.map((p) {
|
||||
return MyDataRow(
|
||||
onSelectChanged: (bool selected) async {
|
||||
var inventoryLine = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => InventoryItem(
|
||||
inventoryLine: p,
|
||||
)),
|
||||
);
|
||||
_save(inventoryLine);
|
||||
},
|
||||
cells: [
|
||||
MyDataCell(
|
||||
new Text(
|
||||
p.productName,
|
||||
style: textStyle,
|
||||
),
|
||||
),
|
||||
MyDataCell(
|
||||
new Text(p.storageName.toString(), style: textStyle),
|
||||
),
|
||||
MyDataCell(
|
||||
new Text(p.oldQty.toString(), style: textStyle),
|
||||
),
|
||||
MyDataCell(
|
||||
new Text(p.quantity.toString(), style: textStyle),
|
||||
),
|
||||
],
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
_save(InventoryLine inventoryLine) {
|
||||
if (inventoryLine == null) return;
|
||||
if (inventoryLine.action == "create") {
|
||||
if (inventoryTaking.inventoryLines.contains(inventoryLine)) {
|
||||
showMsgDialog(context, "Error", "Duplicate line");
|
||||
return;
|
||||
}
|
||||
inventoryTaking.inventoryLines.add(inventoryLine);
|
||||
} else if (inventoryLine.action == "delete") {
|
||||
inventoryTaking.inventoryLines.remove(inventoryLine);
|
||||
}
|
||||
}
|
||||
|
||||
_submit() async {
|
||||
if (inventoryTaking.inventoryLines.length == 0) {
|
||||
showMsgDialog(context, "Error", "No inventory line");
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
try {
|
||||
StorageModel storageModel = Provider.of<StorageModel>(context);
|
||||
await storageModel.createInventoryTaking(inventoryTaking);
|
||||
Navigator.pop(context);
|
||||
} catch (e) {
|
||||
showMsgDialog(context, "Error", e.toString());
|
||||
} finally {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
212
lib/pages/storage/inventory_taking_list.dart
Normal file
212
lib/pages/storage/inventory_taking_list.dart
Normal file
@@ -0,0 +1,212 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/language_model.dart';
|
||||
import 'package:fcs/model/storage_model.dart';
|
||||
import 'package:fcs/pages/storage/inventory_take.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/vo/inventory_taking.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/progress.dart';
|
||||
|
||||
class InventoryTakingList extends StatefulWidget {
|
||||
@override
|
||||
_InventoryTakingListState createState() => _InventoryTakingListState();
|
||||
}
|
||||
|
||||
class _InventoryTakingListState extends State<InventoryTakingList> {
|
||||
DateTime _selectedDate = DateTime.now();
|
||||
int _selectedIndex = 0;
|
||||
bool _isLoading = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
var storageModel = Provider.of<StorageModel>(context, listen: false);
|
||||
_selectedIndex = storageModel.selectedIndex;
|
||||
_selectedDate = storageModel.selectedDate;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<Null> _selectDate(BuildContext context) async {
|
||||
StorageModel storageModel = Provider.of<StorageModel>(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._selectedIndex = pickedDate == currentDate ? 0 : 1;
|
||||
setState(() {
|
||||
_selectedDate = picked;
|
||||
storageModel.filterDate(_selectedDate, _selectedIndex);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
StorageModel storageModel = Provider.of<StorageModel>(context);
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: primaryColor,
|
||||
title: Text(AppTranslations.of(context).text("inventory.takings"),
|
||||
style: Provider.of<LanguageModel>(context).isEng
|
||||
? TextStyle(fontSize: 18)
|
||||
: TextStyle(fontSize: 18, fontFamily: 'MyanmarUnicode')),
|
||||
actions: <Widget>[
|
||||
InkWell(
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(right: 15, top: 20),
|
||||
child: Stack(
|
||||
children: <Widget>[
|
||||
Image.asset(
|
||||
"assets/date_filter.png",
|
||||
color: Colors.white,
|
||||
width: 25,
|
||||
),
|
||||
_selectedIndex == 0
|
||||
? Container()
|
||||
: Positioned(
|
||||
bottom: 15,
|
||||
right: 10,
|
||||
child: Container(
|
||||
width: 10,
|
||||
height: 10,
|
||||
decoration: new BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: secondaryColor,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
onTap: () => _selectDate(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
backgroundColor: primaryColor,
|
||||
child: Icon(Icons.add),
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => TakeInventory()),
|
||||
);
|
||||
},
|
||||
),
|
||||
body: new ListView.builder(
|
||||
padding: EdgeInsets.only(left: 10, right: 10, top: 15),
|
||||
shrinkWrap: true,
|
||||
itemCount: storageModel.inventoryTakings.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return Card(
|
||||
elevation: 8,
|
||||
child: ExpansionTile(
|
||||
onExpansionChanged: (e) => _onExpend(
|
||||
context, e, storageModel.inventoryTakings[index]),
|
||||
title: ListTile(
|
||||
leading: Material(
|
||||
color: primaryColor,
|
||||
shape: CircleBorder(),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
child: Icon(Icons.note, color: Colors.white))),
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(storageModel.inventoryTakings[index].userName,
|
||||
style: textStyle),
|
||||
),
|
||||
subtitle: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
DateFormat('dd MMM yyyy – hh:mm a').format(
|
||||
storageModel.inventoryTakings[index].dateTime),
|
||||
style: subTitleStyle),
|
||||
),
|
||||
),
|
||||
children: <Widget>[
|
||||
storageModel.inventoryTakings[index].linesLoaded
|
||||
? Container(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: MyDataTable(
|
||||
headingRowHeight: 40,
|
||||
columnSpacing: 50,
|
||||
columns: [
|
||||
MyDataColumn(
|
||||
label: LocalText(
|
||||
context, "inventory.product")),
|
||||
MyDataColumn(
|
||||
label: LocalText(
|
||||
context, "inventory.storage")),
|
||||
MyDataColumn(
|
||||
label: LocalText(
|
||||
context, "inventory.quantity")),
|
||||
],
|
||||
rows: getInventoryTable(
|
||||
storageModel.inventoryTakings[index]),
|
||||
),
|
||||
),
|
||||
)
|
||||
: Container(child: Text("Loading..."))
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<MyDataRow> getInventoryTable(InventoryTaking inventoryTaking) {
|
||||
return inventoryTaking.inventoryLines.map((p) {
|
||||
return MyDataRow(
|
||||
cells: [
|
||||
MyDataCell(
|
||||
new Text(
|
||||
p.productName,
|
||||
style: textStyle,
|
||||
),
|
||||
),
|
||||
MyDataCell(
|
||||
new Text(p.storageName.toString(), style: textStyle),
|
||||
),
|
||||
MyDataCell(
|
||||
new Text(p.quantity.toString(), style: textStyle),
|
||||
),
|
||||
],
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
_onExpend(BuildContext context, expended, InventoryTaking inventoryTaking) {
|
||||
if (!expended) return;
|
||||
StorageModel storageModel = Provider.of<StorageModel>(context);
|
||||
storageModel.loadInventoryLines(inventoryTaking);
|
||||
}
|
||||
}
|
||||
155
lib/pages/storage/storage_addition.dart
Normal file
155
lib/pages/storage/storage_addition.dart
Normal file
@@ -0,0 +1,155 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/language_model.dart';
|
||||
import 'package:fcs/model/storage_model.dart';
|
||||
import 'package:fcs/pages/util.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/vo/storage.dart';
|
||||
import 'package:fcs/widget/local_text.dart';
|
||||
import 'package:fcs/widget/localization/app_translations.dart';
|
||||
import 'package:fcs/widget/progress.dart';
|
||||
|
||||
class StorageAddition extends StatefulWidget {
|
||||
final Storage storage;
|
||||
|
||||
const StorageAddition({Key key, this.storage}) : super(key: key);
|
||||
@override
|
||||
_StorageAdditionState createState() => _StorageAdditionState();
|
||||
}
|
||||
|
||||
class _StorageAdditionState extends State<StorageAddition> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
TextEditingController nameController = new TextEditingController();
|
||||
bool _isLoading = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (widget.storage != null) {
|
||||
nameController.text = widget.storage.name;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var languageModel = Provider.of<LanguageModel>(context);
|
||||
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: primaryColor,
|
||||
title: LocalText(context, "storage.item.title",color: Colors.white,fontSize: 20,),
|
||||
actions: <Widget>[
|
||||
widget.storage == null
|
||||
? Container()
|
||||
: IconButton(
|
||||
icon: Icon(Icons.delete),
|
||||
onPressed: () {
|
||||
showConfirmDialog(context, "storage.delete_confirm", () {
|
||||
_delete(context);
|
||||
});
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.save),
|
||||
onPressed: () {
|
||||
_save(context);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
body: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
padding: EdgeInsets.only(left: 24.0, right: 24.0),
|
||||
children: <Widget>[
|
||||
Container(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: TextFormField(
|
||||
controller: nameController,
|
||||
autofocus: false,
|
||||
cursorColor: primaryColor,
|
||||
decoration: new InputDecoration(
|
||||
labelText: AppTranslations.of(context)
|
||||
.text("storage.name"),
|
||||
labelStyle:
|
||||
languageModel.isEng ? labelStyle : labelStyleMM,
|
||||
icon: Image.asset(
|
||||
"assets/inventory.png",
|
||||
color: primaryColor,
|
||||
width: 25,
|
||||
),
|
||||
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("storage.form.name");
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
void _save(BuildContext context) async {
|
||||
if (!_formKey.currentState.validate()) return;
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
try {
|
||||
if (widget.storage != null) {
|
||||
widget.storage.name = nameController.text;
|
||||
await Provider.of<StorageModel>(context, listen: false)
|
||||
.updateStorage(widget.storage);
|
||||
} else {
|
||||
await Provider.of<StorageModel>(context, listen: false)
|
||||
.createStorage(Storage(name: nameController.text));
|
||||
}
|
||||
|
||||
Navigator.pop(context);
|
||||
} catch (e) {
|
||||
showMsgDialog(context, "Error", e.toString());
|
||||
} finally {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _delete(BuildContext context) async {
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
try {
|
||||
if (widget.storage != null) {
|
||||
await Provider.of<StorageModel>(context, listen: false)
|
||||
.deleteStorage(widget.storage.id);
|
||||
}
|
||||
Navigator.pop(context);
|
||||
} catch (e) {
|
||||
showMsgDialog(context, "Error", e.toString());
|
||||
} finally {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
191
lib/pages/storage/storage_list.dart
Normal file
191
lib/pages/storage/storage_list.dart
Normal file
@@ -0,0 +1,191 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/storage_model.dart';
|
||||
import 'package:fcs/pages/storage/storage_addition.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/vo/popup_menu.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/popupmenu.dart';
|
||||
import 'package:fcs/widget/progress.dart';
|
||||
|
||||
import 'inventory_taking_list.dart';
|
||||
|
||||
class StorageList extends StatefulWidget {
|
||||
@override
|
||||
_StorageListState createState() => _StorageListState();
|
||||
}
|
||||
|
||||
class _StorageListState extends State<StorageList> {
|
||||
final double dotSize = 15.0;
|
||||
PopupMenu selectedChoices = storageMenus[0];
|
||||
bool _isLoading = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var storageModel = Provider.of<StorageModel>(context);
|
||||
|
||||
void _select(PopupMenu choice) async {
|
||||
selectedChoices = choice;
|
||||
if (choice.status == "Inventory Takings") {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => InventoryTakingList()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: primaryColor,
|
||||
title: LocalText(
|
||||
context,
|
||||
"storage.title",
|
||||
color: Colors.white,
|
||||
fontSize: 20,
|
||||
),
|
||||
actions: <Widget>[
|
||||
PopupMenuButton<PopupMenu>(
|
||||
elevation: 3.2,
|
||||
onSelected: _select,
|
||||
itemBuilder: (BuildContext context) {
|
||||
return storageMenus.map((PopupMenu choice) {
|
||||
return PopupMenuItem<PopupMenu>(
|
||||
value: choice,
|
||||
child: Text(choice.status),
|
||||
);
|
||||
}).toList();
|
||||
})
|
||||
],
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
backgroundColor: primaryColor,
|
||||
child: Icon(Icons.add),
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => StorageAddition()),
|
||||
);
|
||||
},
|
||||
),
|
||||
body: new ListView.builder(
|
||||
padding: EdgeInsets.only(left: 10, right: 10, top: 15),
|
||||
shrinkWrap: true,
|
||||
itemCount: storageModel.storages.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return Card(
|
||||
elevation: 10,
|
||||
color: Colors.white,
|
||||
child: Theme(
|
||||
data: ThemeData(accentColor: Colors.grey),
|
||||
child: ExpansionTile(
|
||||
onExpansionChanged: (e) =>
|
||||
_onExpend(context, e, storageModel.storages[index]),
|
||||
title: Row(
|
||||
children: <Widget>[
|
||||
new Row(
|
||||
children: <Widget>[
|
||||
new Padding(
|
||||
padding: new EdgeInsets.symmetric(
|
||||
horizontal: 10.0 - dotSize / 2),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
child: Image.asset(
|
||||
"assets/inventory.png",
|
||||
width: 40,
|
||||
color: primaryColor,
|
||||
)),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: new Text(
|
||||
storageModel.storages[index].name == null
|
||||
? ""
|
||||
: storageModel.storages[index].name,
|
||||
style: textStyle),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: IconButton(
|
||||
icon: Icon(Icons.edit),
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => StorageAddition(
|
||||
storage:
|
||||
storageModel.storages[index])),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
height: 15,
|
||||
)
|
||||
],
|
||||
),
|
||||
children: <Widget>[
|
||||
storageModel.storages[index].productsLoaded
|
||||
? Container(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: MyDataTable(
|
||||
headingRowHeight: 40,
|
||||
columnSpacing: 50,
|
||||
columns: [
|
||||
MyDataColumn(
|
||||
label: LocalText(
|
||||
context, "inventory.product"),
|
||||
),
|
||||
MyDataColumn(
|
||||
label: LocalText(
|
||||
context, "inventory.quantity"),
|
||||
),
|
||||
],
|
||||
rows: getProductRow(
|
||||
storageModel.storages[index]),
|
||||
),
|
||||
),
|
||||
)
|
||||
: Text("Loading..."),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<MyDataRow> getProductRow(Storage storage) {
|
||||
return storage.products.map((p) {
|
||||
return MyDataRow(
|
||||
cells: [
|
||||
MyDataCell(
|
||||
new Text(
|
||||
p.name,
|
||||
style: textStyle,
|
||||
),
|
||||
),
|
||||
MyDataCell(
|
||||
NumberCell(p.quantity)
|
||||
),
|
||||
],
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
_onExpend(BuildContext context, expended, Storage storage) {
|
||||
if (!expended) return;
|
||||
storage.productsLoaded = false;
|
||||
StorageModel storageModel = Provider.of<StorageModel>(context);
|
||||
storageModel.loadProducts(storage);
|
||||
}
|
||||
}
|
||||
151
lib/pages/term.dart
Normal file
151
lib/pages/term.dart
Normal file
@@ -0,0 +1,151 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/main_model.dart';
|
||||
import 'package:fcs/pages/term_edit.dart';
|
||||
import 'package:fcs/pages/util.dart';
|
||||
import 'package:fcs/widget/local_text.dart';
|
||||
import 'package:fcs/widget/progress.dart';
|
||||
import 'package:zefyr/zefyr.dart';
|
||||
|
||||
import '../theme/theme.dart';
|
||||
|
||||
typedef void ProfileCallback();
|
||||
|
||||
class Term extends StatefulWidget {
|
||||
final bool agreePage;
|
||||
|
||||
const Term({Key key, this.agreePage = false}) : super(key: key);
|
||||
@override
|
||||
_TermState createState() => _TermState();
|
||||
}
|
||||
|
||||
class _TermState extends State<Term> {
|
||||
ZefyrController _controller;
|
||||
FocusNode _focusNode;
|
||||
NotusDocument document = new NotusDocument();
|
||||
bool isLoading = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = ZefyrController(_loadDocument());
|
||||
_focusNode = FocusNode();
|
||||
}
|
||||
|
||||
NotusDocument _loadDocument() {
|
||||
MainModel mainModel = Provider.of<MainModel>(context, listen: false);
|
||||
String term = mainModel.setting.terms;
|
||||
|
||||
NotusDocument doc;
|
||||
try {
|
||||
doc = NotusDocument.fromJson(jsonDecode(term));
|
||||
} 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();
|
||||
|
||||
bool aggreed = mainModel.user.agreeTerms;
|
||||
|
||||
final agreeBtn = Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 10.0),
|
||||
child: Card(
|
||||
elevation: 10,
|
||||
child: Container(
|
||||
height: 45.0,
|
||||
child: ButtonTheme(
|
||||
minWidth: 900.0,
|
||||
height: 100.0,
|
||||
child: FlatButton.icon(
|
||||
onPressed: () {
|
||||
showConfirmDialog(context, "term.iagree", () async {
|
||||
_agree();
|
||||
});
|
||||
},
|
||||
label: LocalText(context, "term.agree_btn"),
|
||||
icon: Icon(
|
||||
Icons.check,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
return LocalProgress(
|
||||
inAsyncCall: isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: !widget.agreePage,
|
||||
title: LocalText(context, 'term.title',
|
||||
color: Colors.white, fontSize: 20),
|
||||
backgroundColor: primaryColor,
|
||||
actions: <Widget>[
|
||||
isOwnerAndAbove || hasAdmin
|
||||
? IconButton(
|
||||
icon: Icon(Icons.edit),
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => TermEdit()),
|
||||
);
|
||||
},
|
||||
)
|
||||
: Container()
|
||||
],
|
||||
),
|
||||
body: Column(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Card(
|
||||
child: ZefyrTheme(
|
||||
data: ZefyrThemeData().copyWith(
|
||||
),
|
||||
child: ZefyrScaffold(
|
||||
child: ZefyrEditor(
|
||||
mode: ZefyrMode.view,
|
||||
padding: EdgeInsets.all(16),
|
||||
controller: _controller,
|
||||
focusNode: _focusNode,
|
||||
),
|
||||
))),
|
||||
)),
|
||||
!aggreed ? agreeBtn : Container(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_agree() async {
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
});
|
||||
try {
|
||||
MainModel mainModel = Provider.of<MainModel>(context);
|
||||
await mainModel.agreeTerms();
|
||||
if (widget.agreePage) {
|
||||
Future.delayed(const Duration(milliseconds: 3000), () {
|
||||
Navigator.pop(context);
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
showMsgDialog(context, "Error", e.toString());
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
123
lib/pages/term_edit.dart
Normal file
123
lib/pages/term_edit.dart
Normal file
@@ -0,0 +1,123 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:quill_delta/quill_delta.dart';
|
||||
import 'package:fcs/model/main_model.dart';
|
||||
import 'package:fcs/pages/util.dart';
|
||||
import 'package:fcs/widget/progress.dart';
|
||||
import 'package:zefyr/zefyr.dart';
|
||||
|
||||
import '../theme/theme.dart';
|
||||
|
||||
typedef void ProfileCallback();
|
||||
|
||||
class TermEdit extends StatefulWidget {
|
||||
@override
|
||||
_TermEditState createState() => _TermEditState();
|
||||
}
|
||||
|
||||
class _TermEditState extends State<TermEdit> {
|
||||
/// Allows to control the editor and the document.
|
||||
ZefyrController _controller;
|
||||
|
||||
/// Zefyr editor like any other input field requires a focus node.
|
||||
FocusNode _focusNode;
|
||||
bool _isLoading;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_isLoading = false;
|
||||
|
||||
// Here we must load the document and pass it to Zefyr controller.
|
||||
final document = _loadDocument();
|
||||
_controller = ZefyrController(document);
|
||||
_focusNode = FocusNode();
|
||||
}
|
||||
|
||||
/// Loads the document to be edited in Zefyr.
|
||||
NotusDocument _loadDocument() {
|
||||
MainModel mainModel = Provider.of<MainModel>(context, listen: false);
|
||||
String term = mainModel.setting.terms;
|
||||
|
||||
NotusDocument doc;
|
||||
try {
|
||||
doc = NotusDocument.fromJson(jsonDecode(term));
|
||||
} catch (e) {}
|
||||
if (doc == null) {
|
||||
doc = NotusDocument();
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
MainModel mainModel = Provider.of<MainModel>(context);
|
||||
|
||||
var singleChildScrollView = SingleChildScrollView(
|
||||
padding: EdgeInsets.only(
|
||||
left: 25.0,
|
||||
right: 25.0,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
width: 300,
|
||||
height: 300,
|
||||
child: Card(child: Markdown(data: mainModel.setting.terms))),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(
|
||||
"Terms",
|
||||
),
|
||||
backgroundColor: primaryColor,
|
||||
actions: <Widget>[
|
||||
IconButton(
|
||||
icon: Icon(Icons.save),
|
||||
onPressed: () {
|
||||
_save();
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
body: ZefyrScaffold(
|
||||
child: ZefyrTheme(
|
||||
data: ZefyrThemeData().copyWith(
|
||||
),
|
||||
child: ZefyrEditor(
|
||||
padding: EdgeInsets.all(16),
|
||||
controller: _controller,
|
||||
focusNode: _focusNode,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_save() {
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
try {
|
||||
final contents = jsonEncode(_controller.document);
|
||||
MainModel mainModel = Provider.of<MainModel>(context, listen: false);
|
||||
mainModel.updateTerms(contents);
|
||||
} catch (e) {
|
||||
showMsgDialog(context, "Error", e.toString());
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
Navigator.pop(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
157
lib/pages/test_list.dart
Normal file
157
lib/pages/test_list.dart
Normal file
@@ -0,0 +1,157 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/test_model.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/vo/popup_menu.dart';
|
||||
|
||||
class TestList extends StatefulWidget {
|
||||
@override
|
||||
_TestListState createState() => _TestListState();
|
||||
}
|
||||
|
||||
class _TestListState extends State<TestList> {
|
||||
bool isLoading = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
var testModel = Provider.of<TestModel>(context, listen: false);
|
||||
testModel.initData();
|
||||
print("List initState!");
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
print("List Dispose!");
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var testModel = Provider.of<TestModel>(context);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: primaryColor,
|
||||
title: Text("Tests"),
|
||||
actions: <Widget>[
|
||||
PopupMenuButton<PopupMenu>(
|
||||
elevation: 3.2,
|
||||
onSelected: (s) {
|
||||
if (s.index == 0) Provider.of<TestModel>(context).populate();
|
||||
if (s.index == 1) Provider.of<TestModel>(context).add();
|
||||
if (s.index == 2) Provider.of<TestModel>(context).update();
|
||||
if (s.index == 3) Provider.of<TestModel>(context).remove();
|
||||
},
|
||||
itemBuilder: (BuildContext context) {
|
||||
return [
|
||||
PopupMenuItem<PopupMenu>(
|
||||
value: PopupMenu(status: "Populate", index: 0),
|
||||
child: Text("Populate"),
|
||||
),
|
||||
PopupMenuItem<PopupMenu>(
|
||||
value: PopupMenu(status: "Add", index: 1),
|
||||
child: Text("Add"),
|
||||
),
|
||||
PopupMenuItem<PopupMenu>(
|
||||
value: PopupMenu(status: "Update", index: 2),
|
||||
child: Text("Update"),
|
||||
),
|
||||
PopupMenuItem<PopupMenu>(
|
||||
value: PopupMenu(status: "Delete", index: 3),
|
||||
child: Text("Delete"),
|
||||
)
|
||||
];
|
||||
})
|
||||
],
|
||||
),
|
||||
body: Column(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: NotificationListener<ScrollNotification>(
|
||||
onNotification: (ScrollNotification scrollInfo) {
|
||||
if (!isLoading &&
|
||||
scrollInfo.metrics.pixels ==
|
||||
scrollInfo.metrics.maxScrollExtent &&
|
||||
!testModel.ended) {
|
||||
// start loading data
|
||||
_loadData(testModel);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
child: ListView.builder(
|
||||
padding: EdgeInsets.only(left: 15, right: 15, top: 15),
|
||||
shrinkWrap: true,
|
||||
itemCount: testModel.ended
|
||||
? testModel.tests.length + 1
|
||||
: testModel.tests.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return (testModel.ended && index == testModel.tests.length)
|
||||
? Center(child: Text("No more item"))
|
||||
: buildCard(testModel.tests[index], index + 1);
|
||||
}),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: isLoading ? 50.0 : 0,
|
||||
color: Colors.transparent,
|
||||
child: Center(
|
||||
child: new CircularProgressIndicator(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Card buildCard(Test test, int index) {
|
||||
return Card(
|
||||
elevation: 10,
|
||||
color: Colors.white,
|
||||
child: new Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10.0),
|
||||
child: new Row(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Icon(
|
||||
Icons.account_circle,
|
||||
color: primaryColor,
|
||||
size: 55,
|
||||
),
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
new Text(
|
||||
"$index - ${test.name}",
|
||||
style: new TextStyle(fontSize: 17.0, color: Colors.black),
|
||||
),
|
||||
new Text(
|
||||
test.age.toString(),
|
||||
style: new TextStyle(fontSize: 12.0, color: Colors.black),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_loadData(TestModel testModel) {
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
});
|
||||
|
||||
Timer(Duration(seconds: 1), () {
|
||||
testModel.load();
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
276
lib/pages/user_editor.dart
Normal file
276
lib/pages/user_editor.dart
Normal file
@@ -0,0 +1,276 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:fcs/model/main_model.dart';
|
||||
import 'package:fcs/model/user_model.dart';
|
||||
import 'package:fcs/pages/util.dart';
|
||||
import 'package:fcs/theme/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/progress.dart';
|
||||
|
||||
class UserEditor extends StatefulWidget {
|
||||
final User user;
|
||||
final bool viewOnly;
|
||||
const UserEditor({this.user, this.viewOnly = false});
|
||||
@override
|
||||
_UserEditorState createState() => _UserEditorState();
|
||||
}
|
||||
|
||||
class _UserEditorState extends State<UserEditor> {
|
||||
TextEditingController _name = new TextEditingController();
|
||||
TextEditingController _phone = new TextEditingController();
|
||||
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
bool _isLoading = false;
|
||||
String currentLevelId;
|
||||
List<Privilege> privileges = [];
|
||||
User _user = new User();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
var userModel = Provider.of<UserModel>(context, listen: false);
|
||||
|
||||
privileges = Provider.of<UserModel>(context, listen: false).getPrivileges;
|
||||
if (widget.user != null) {
|
||||
userModel.getUserList().forEach((u) {
|
||||
if (widget.user.docID == u.docID) {
|
||||
_user = u;
|
||||
}
|
||||
});
|
||||
_name.text = _user.name;
|
||||
_phone.text = _user.phone;
|
||||
this.currentLevelId = _user.userLevelID;
|
||||
privileges.forEach((p) => _user.privilegeIds.contains(p.id)
|
||||
? p.isChecked = true
|
||||
: p.isChecked = false);
|
||||
}
|
||||
}
|
||||
|
||||
Widget showUserLevelList(BuildContext context, UserModel userModel) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: <Widget>[
|
||||
Icon(
|
||||
Icons.security,
|
||||
color: primaryColor,
|
||||
),
|
||||
SizedBox(
|
||||
width: 20,
|
||||
),
|
||||
new Flexible(
|
||||
child: Container(
|
||||
width: 150.0,
|
||||
child: DropdownButton<String>(
|
||||
value: currentLevelId,
|
||||
isExpanded: true,
|
||||
hint: Text(
|
||||
'Select User Level',
|
||||
),
|
||||
onChanged: changedDropDownItem,
|
||||
items: userModel.userLevels
|
||||
.map<DropdownMenuItem<String>>((UserLevel userLevel) {
|
||||
return new DropdownMenuItem<String>(
|
||||
value: userLevel.id,
|
||||
child: new Text(userLevel.name,
|
||||
style:
|
||||
new TextStyle(color: Colors.black87, fontSize: 17)),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
void changedDropDownItem(selected) {
|
||||
setState(() {
|
||||
currentLevelId = selected;
|
||||
});
|
||||
}
|
||||
|
||||
Widget showprivilegeList(BuildContext context, UserModel userModel) {
|
||||
return Container(
|
||||
width: 300,
|
||||
height: 300,
|
||||
child: ListView.builder(
|
||||
itemCount: privileges.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return new ListTile(
|
||||
title: new Row(
|
||||
children: <Widget>[
|
||||
new Checkbox(
|
||||
value: privileges[index].isChecked == null
|
||||
? false
|
||||
: privileges[index].isChecked,
|
||||
activeColor: primaryColor,
|
||||
onChanged: (bool value) {
|
||||
setState(() {
|
||||
privileges[index].isChecked = value;
|
||||
});
|
||||
}),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
new Text(
|
||||
userModel.getPrivileges[index].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]),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
));
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var userModel = Provider.of<UserModel>(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 phoneNoBox = TextFormField(
|
||||
controller: _phone,
|
||||
autofocus: false,
|
||||
readOnly: true,
|
||||
cursorColor: primaryColor,
|
||||
decoration: new InputDecoration(
|
||||
border: InputBorder.none,
|
||||
focusedBorder: InputBorder.none,
|
||||
icon: Icon(
|
||||
Icons.phone,
|
||||
color: primaryColor,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final saveButton = Container(
|
||||
padding: EdgeInsets.only(
|
||||
left: 24.0,
|
||||
right: 24.0,
|
||||
),
|
||||
child: Container(
|
||||
height: 45.0,
|
||||
color: primaryColor,
|
||||
child: ButtonTheme(
|
||||
minWidth: 900.0,
|
||||
height: 100.0,
|
||||
child: FlatButton(
|
||||
onPressed: () {
|
||||
_save(context);
|
||||
},
|
||||
child: LocalText(
|
||||
context,
|
||||
'user.save',
|
||||
color: Colors.white,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: primaryColor,
|
||||
title: LocalText(context, 'user.title',
|
||||
color: Colors.white, fontSize: 20),
|
||||
),
|
||||
body: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
padding: EdgeInsets.only(left: 24.0, right: 24.0),
|
||||
children: <Widget>[
|
||||
namebox,
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(child: phoneNoBox),
|
||||
Expanded(
|
||||
child: InkWell(
|
||||
onTap: () => call(context, _phone.text),
|
||||
child: Icon(
|
||||
Icons.open_in_new,
|
||||
color: Colors.grey,
|
||||
size: 15,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
widget.viewOnly
|
||||
? Container()
|
||||
: showUserLevelList(context, userModel),
|
||||
widget.viewOnly
|
||||
? Container()
|
||||
: showprivilegeList(context, userModel),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 15,
|
||||
),
|
||||
widget.viewOnly ? Container() : saveButton,
|
||||
SizedBox(
|
||||
height: 20,
|
||||
)
|
||||
],
|
||||
),
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
List<String> privilegesIDs() {
|
||||
return this.privileges.where((p) => p.isChecked).map((p) => p.id).toList();
|
||||
}
|
||||
|
||||
_save(BuildContext context) async {
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
try {
|
||||
var userModel = Provider.of<UserModel>(context);
|
||||
await userModel.addLevel(
|
||||
_phone.text, this.currentLevelId, privilegesIDs());
|
||||
Navigator.pop(context);
|
||||
} catch (e) {
|
||||
showMsgDialog(context, "Error", e.toString());
|
||||
}
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
260
lib/pages/user_list.dart
Normal file
260
lib/pages/user_list.dart
Normal file
@@ -0,0 +1,260 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/user_model.dart';
|
||||
import 'package:fcs/pages/util.dart';
|
||||
import 'package:fcs/theme/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';
|
||||
|
||||
import 'user_editor.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import 'user_search_page.dart';
|
||||
|
||||
class UserList extends StatefulWidget {
|
||||
@override
|
||||
_UserListState createState() => _UserListState();
|
||||
}
|
||||
|
||||
class _UserListState extends State<UserList> {
|
||||
var dateFormatter = new DateFormat('dd MMM yyyy - hh:mm:ss a');
|
||||
final double dotSize = 15.0;
|
||||
bool _isLoading = false;
|
||||
PopupMenu selectedChoices = userpopup[0];
|
||||
User user = new User();
|
||||
int _selectedIndex;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
var userModel = Provider.of<UserModel>(context, listen: false);
|
||||
var index = userModel.popupMenu.index;
|
||||
_selectedIndex = index;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var userModel = Provider.of<UserModel>(context);
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: primaryColor,
|
||||
title: LocalText(context, 'users.title',
|
||||
color: Colors.white, fontSize: 20),
|
||||
actions: <Widget>[
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
Icons.search,
|
||||
color: Colors.white,
|
||||
),
|
||||
iconSize: 30,
|
||||
onPressed: () => showUserPlacesSearch(context),
|
||||
),
|
||||
PopupMenuButton<PopupMenu>(
|
||||
elevation: 3.2,
|
||||
onSelected: (selected) {
|
||||
setState(() {
|
||||
this._selectedIndex = selected.index;
|
||||
});
|
||||
userModel.filterSorting(_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,
|
||||
),
|
||||
_selectedIndex != 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,
|
||||
),
|
||||
_selectedIndex != null &&
|
||||
_selectedIndex == choice.index
|
||||
? Icon(
|
||||
Icons.check,
|
||||
color: Colors.grey,
|
||||
)
|
||||
: Container(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList();
|
||||
}),
|
||||
],
|
||||
),
|
||||
body: new ListView.builder(
|
||||
padding: EdgeInsets.only(left: 15, right: 15, top: 15, bottom: 10),
|
||||
shrinkWrap: true,
|
||||
itemCount: userModel.getUserList().length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
User _u = userModel.getUserList()[index];
|
||||
return Card(
|
||||
elevation: 10,
|
||||
color: Colors.white,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => UserEditor(user: _u)),
|
||||
);
|
||||
},
|
||||
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: 20.0 - dotSize / 2),
|
||||
child: Icon(
|
||||
Icons.account_circle,
|
||||
color: primaryColor,
|
||||
size: 55,
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
new Text(
|
||||
(_u.name == null ? "" : _u.name) +
|
||||
(_u.isBlock ? " (Blocked)" : ""),
|
||||
style: new TextStyle(
|
||||
fontSize: 16.0, color: Colors.black),
|
||||
),
|
||||
new Text(
|
||||
_u.phone == null ? "" : _u.phone,
|
||||
style: new TextStyle(
|
||||
fontSize: 14.0, color: Colors.grey),
|
||||
),
|
||||
_u.device == null
|
||||
? Text("No login",
|
||||
style: TextStyle(color: Colors.red))
|
||||
: Text("last active",
|
||||
style:
|
||||
TextStyle(color: Colors.green)),
|
||||
Text(
|
||||
"${_u.device == null ? "" : _u.device}",
|
||||
style: TextStyle(fontSize: 11)),
|
||||
Text(
|
||||
"${_u.lastActiveTime == null ? "" : dateFormatter.format(_u.lastActiveTime)}")
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
PopupMenuButton<PopupMenu>(
|
||||
elevation: 3.2,
|
||||
tooltip: 'This is tooltip',
|
||||
onSelected: _select,
|
||||
itemBuilder: (BuildContext context) {
|
||||
this.user = _u;
|
||||
return userpopup.map((PopupMenu choice) {
|
||||
return PopupMenuItem<PopupMenu>(
|
||||
enabled: choice.index == 1
|
||||
? _u.isBlockUser() ? false : true
|
||||
: _u.isBlockUser() ? true : false,
|
||||
value: choice,
|
||||
child: Text(choice.status),
|
||||
);
|
||||
}).toList();
|
||||
})
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _select(PopupMenu choice) async {
|
||||
selectedChoices = choice;
|
||||
if (choice.index == 1) {
|
||||
showConfirmDialog(context, "user.block.confirm", () {
|
||||
_block();
|
||||
});
|
||||
} else if (choice.index == 2) {
|
||||
showConfirmDialog(context, "user.unblock.confirm", () {
|
||||
_unblock();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_block() async {
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
|
||||
try {
|
||||
var userModel = Provider.of<UserModel>(context);
|
||||
await userModel.blockPhone(this.user.phoneNumber);
|
||||
} catch (e) {
|
||||
showMsgDialog(context, "Error", e.toString());
|
||||
} finally {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
189
lib/pages/user_search_page.dart
Normal file
189
lib/pages/user_search_page.dart
Normal file
@@ -0,0 +1,189 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/user_model.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/vo/user.dart';
|
||||
|
||||
import 'user_editor.dart';
|
||||
|
||||
Future<User> showUserPlacesSearch(BuildContext context) async =>
|
||||
await showSearch<User>(
|
||||
context: context,
|
||||
delegate: UserSearchDelegate(),
|
||||
);
|
||||
|
||||
class UserSearchDelegate extends SearchDelegate<User> {
|
||||
@override
|
||||
ThemeData appBarTheme(BuildContext context) {
|
||||
final ThemeData theme = Theme.of(context);
|
||||
return theme.copyWith(
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
hintStyle: TextStyle(
|
||||
color: theme.primaryTextTheme.title.color, fontSize: 16)),
|
||||
primaryColor: primaryColor,
|
||||
primaryIconTheme: theme.primaryIconTheme.copyWith(color: Colors.white),
|
||||
primaryColorBrightness: Brightness.light,
|
||||
primaryTextTheme: theme.textTheme,
|
||||
textTheme: theme.textTheme.copyWith(
|
||||
title: theme.textTheme.title.copyWith(
|
||||
color: theme.primaryTextTheme.title.color, fontSize: 16)),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Widget> buildActions(BuildContext context) {
|
||||
return [
|
||||
IconButton(
|
||||
icon: Icon(Icons.clear),
|
||||
onPressed: () => query = '',
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildLeading(BuildContext context) {
|
||||
return IconButton(
|
||||
icon: Icon(Icons.arrow_back),
|
||||
onPressed: () => close(context, null),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildResults(BuildContext context) {
|
||||
final userModel = Provider.of<UserModel>(context);
|
||||
return FutureBuilder(
|
||||
future: userModel.searchUser(query),
|
||||
builder: (context, AsyncSnapshot<List<User>> snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
if (snapshot.data.length == 0) {
|
||||
return Container(
|
||||
child: Center(
|
||||
child: Text(
|
||||
"Error :No Search User",
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: 15),
|
||||
child: ListView(
|
||||
children: snapshot.data.map((u) => UserRow(user: u)).toList(),
|
||||
),
|
||||
);
|
||||
} else if (snapshot.hasError) {
|
||||
return Container(
|
||||
child: Center(
|
||||
child: Text(
|
||||
'${snapshot.error}',
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return Container(
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(
|
||||
valueColor:
|
||||
new AlwaysStoppedAnimation<Color>(primaryColor)),
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildSuggestions(BuildContext context) {
|
||||
return Container(
|
||||
child: Center(
|
||||
child: Opacity(
|
||||
opacity: 0.2,
|
||||
child: Icon(
|
||||
Icons.supervised_user_circle,
|
||||
size: 200,
|
||||
)),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class UserRow extends StatefulWidget {
|
||||
final User user;
|
||||
const UserRow({Key key, this.user}) : super(key: key);
|
||||
|
||||
@override
|
||||
_UserRowState createState() => _UserRowState();
|
||||
}
|
||||
|
||||
class _UserRowState extends State<UserRow> {
|
||||
var dateFormatter = new DateFormat('dd MMM yyyy - hh:mm:ss a');
|
||||
|
||||
@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) => UserEditor(user: widget.user)),
|
||||
);
|
||||
},
|
||||
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: 20.0 - 15 / 2),
|
||||
child: Icon(
|
||||
Icons.account_circle,
|
||||
color: primaryColor,
|
||||
size: 55,
|
||||
),
|
||||
),
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
new Text(
|
||||
widget.user.name == null ? "" : widget.user.name,
|
||||
style: new TextStyle(
|
||||
fontSize: 16.0, color: Colors.black),
|
||||
),
|
||||
new Text(
|
||||
widget.user.phone == null ? "" : widget.user.phone,
|
||||
style: new TextStyle(
|
||||
fontSize: 14.0, color: Colors.grey),
|
||||
),
|
||||
widget.user.device == null
|
||||
? Text("No login",
|
||||
style: TextStyle(color: Colors.red))
|
||||
: Text("last active",
|
||||
style: TextStyle(color: Colors.green)),
|
||||
Text(
|
||||
"${widget.user.device == null ? "" : widget.user.device}",
|
||||
style: TextStyle(fontSize: 11)),
|
||||
Text(
|
||||
"${widget.user.lastActiveTime == null ? "" : dateFormatter.format(widget.user.lastActiveTime)}")
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
317
lib/pages/util.dart
Normal file
317
lib/pages/util.dart
Normal file
@@ -0,0 +1,317 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_colorpicker/flutter_colorpicker.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:fcs/model/announcement_model.dart';
|
||||
import 'package:fcs/model/buyer_model.dart';
|
||||
import 'package:fcs/model/do_model.dart';
|
||||
import 'package:fcs/model/language_model.dart';
|
||||
import 'package:fcs/model/notification_model.dart';
|
||||
import 'package:fcs/model/po_model.dart';
|
||||
import 'package:fcs/model/user_model.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/vo/buyer.dart';
|
||||
import 'package:fcs/widget/local_text.dart';
|
||||
import 'package:fcs/widget/localization/app_translations.dart';
|
||||
import 'package:fcs/vo/notification.dart' as Noti;
|
||||
|
||||
import 'announcement.dart';
|
||||
import 'buyer_info.dart';
|
||||
import 'do/do_approve.dart';
|
||||
import 'log_list.dart';
|
||||
import 'my_registeration_info.dart';
|
||||
import 'po/po_submission_form.dart';
|
||||
import 'products_list.dart';
|
||||
import 'user_editor.dart';
|
||||
|
||||
final log = Logger('Util');
|
||||
|
||||
void showMsgDialog(BuildContext context, String title, String msg) {
|
||||
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();
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
var selectedColor;
|
||||
void showColorPicker(BuildContext context, Color color, callback(Color color)) {
|
||||
showDialog(
|
||||
context: context,
|
||||
child: AlertDialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(32.0))),
|
||||
title: const Text('Pick product color'),
|
||||
content: SingleChildScrollView(
|
||||
child: ColorPicker(
|
||||
pickerColor: color.value == 0 ? Colors.red : color,
|
||||
pickerAreaHeightPercent: 0.6,
|
||||
onColorChanged: (Color value) {
|
||||
selectedColor = value;
|
||||
},
|
||||
),
|
||||
),
|
||||
actions: <Widget>[
|
||||
FlatButton(
|
||||
child: const Text('Choose'),
|
||||
onPressed: () {
|
||||
callback(selectedColor == null ? Colors.red : selectedColor);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
FlatButton(
|
||||
child: const Text('Cancel'),
|
||||
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,
|
||||
),
|
||||
),
|
||||
content: Container(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
FlatButton(
|
||||
child: Text(
|
||||
AppTranslations.of(context).text('Cancel'),
|
||||
style: Provider.of<LanguageModel>(context).isEng
|
||||
? TextStyle()
|
||||
: TextStyle(fontFamily: 'MyanmarUnicode'),
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
}),
|
||||
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: 'MyanmarUnicode')),
|
||||
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 == "approved"
|
||||
? Chip(
|
||||
backgroundColor: Colors.green,
|
||||
avatar: Icon(
|
||||
Icons.check,
|
||||
color: Colors.white,
|
||||
size: 14,
|
||||
),
|
||||
label: Text(
|
||||
status,
|
||||
style: TextStyle(color: Colors.white, fontSize: 12),
|
||||
))
|
||||
: 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 == "pending"
|
||||
? Chip(
|
||||
backgroundColor: Colors.blue,
|
||||
avatar: Icon(
|
||||
Icons.timelapse,
|
||||
color: Colors.white,
|
||||
size: 14,
|
||||
),
|
||||
label: Text(
|
||||
status,
|
||||
style: TextStyle(color: Colors.white, fontSize: 12),
|
||||
))
|
||||
: Chip(
|
||||
avatar: Icon(
|
||||
Icons.check,
|
||||
size: 14,
|
||||
),
|
||||
label: Text(status));
|
||||
}
|
||||
|
||||
call(BuildContext context, String phone) {
|
||||
showConfirmDialog(context, "contact.phone.confim", () => launch("tel:$phone"),
|
||||
translationVariables: ["$phone"]);
|
||||
}
|
||||
|
||||
Future<void> displayNotiContent(
|
||||
BuildContext context, Noti.Notification noti) async {
|
||||
if (!noti.seen) {
|
||||
Provider.of<NotificationModel>(context, listen: false).seenID(noti.id);
|
||||
}
|
||||
|
||||
try {
|
||||
if (noti.itemType == "buyer") {
|
||||
BuyerModel buyerModel = Provider.of<BuyerModel>(context, listen: false);
|
||||
Buyer buyer = await buyerModel.getBuyer(noti.itemID);
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => BuyerInfo(
|
||||
buyer: buyer,
|
||||
)),
|
||||
);
|
||||
} else if (noti.itemType == "announcement") {
|
||||
AnnouncementModel announcementModel =
|
||||
Provider.of<AnnouncementModel>(context, listen: false);
|
||||
var announce = await announcementModel.getAnnouncement(noti.itemID);
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => AnnouncementPage(announcement: announce)),
|
||||
);
|
||||
} else if (noti.itemType == "reg") {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MyRegisterationInfo()),
|
||||
);
|
||||
} else if (noti.itemType == "po") {
|
||||
POSubmissionModel poModel =
|
||||
Provider.of<POSubmissionModel>(context, listen: false);
|
||||
var po = await poModel.getPO(noti.itemID);
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => POSubmissionForm(
|
||||
poSubmission: po,
|
||||
)),
|
||||
);
|
||||
} else if (noti.itemType == "do") {
|
||||
DOModel doModel = Provider.of<DOModel>(context, listen: false);
|
||||
var _do = await doModel.getDO(noti.itemID);
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => DOApproval(doSubmission: _do)),
|
||||
);
|
||||
} else if (noti.itemType == "price") {
|
||||
Navigator.of(context)
|
||||
.push(MaterialPageRoute(builder: (_) => ProductsList()));
|
||||
} else if (noti.itemType == 'new_device_login') {
|
||||
Navigator.of(context).push(MaterialPageRoute(builder: (_) => LogList()));
|
||||
} else if (noti.itemType == 'user') {
|
||||
UserModel userModel = Provider.of<UserModel>(context, listen: false);
|
||||
var user = await userModel.getUser(noti.itemID);
|
||||
Navigator.of(context).push(MaterialPageRoute(
|
||||
builder: (_) => UserEditor(
|
||||
user: user,
|
||||
viewOnly: true,
|
||||
)));
|
||||
}
|
||||
} catch (e) {
|
||||
log.warning("Error:$e \n ${noti.toString()}");
|
||||
showMsgDialog(context, "Error", "Notification item not found!");
|
||||
} finally {}
|
||||
}
|
||||
161
lib/pages/welcome_page.dart
Normal file
161
lib/pages/welcome_page.dart
Normal file
@@ -0,0 +1,161 @@
|
||||
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:fcs/model/language_model.dart';
|
||||
import 'package:fcs/widget/banner.dart';
|
||||
import 'package:fcs/widget/localization/transalation.dart';
|
||||
import 'package:fcs/widget/offline_redirect.dart';
|
||||
import 'package:fcs/widget/products.dart';
|
||||
|
||||
import 'contact.dart';
|
||||
import 'login_page.dart';
|
||||
import 'manual/manual_page.dart';
|
||||
|
||||
class WelcomePage extends StatefulWidget {
|
||||
@override
|
||||
_WelcomePageState createState() => _WelcomePageState();
|
||||
}
|
||||
|
||||
class _WelcomePageState extends State<WelcomePage> {
|
||||
String version = "";
|
||||
List<bool> _selection = List.generate(2, (_) => false);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadVersion();
|
||||
var languageModel = Provider.of<LanguageModel>(context, listen: false);
|
||||
languageModel.isEng ? _selection[0] = true : _selection[1] = true;
|
||||
}
|
||||
|
||||
_loadVersion() async {
|
||||
var version = await getVersionNumber();
|
||||
setState(() {
|
||||
this.version = version;
|
||||
});
|
||||
}
|
||||
|
||||
Future<String> getVersionNumber() async {
|
||||
PackageInfo packageInfo = await PackageInfo.fromPlatform();
|
||||
return "v${packageInfo.version}+${packageInfo.buildNumber}";
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var languageModel = Provider.of<LanguageModel>(context);
|
||||
|
||||
var toggleButtons = ToggleButtons(
|
||||
constraints: BoxConstraints(minWidth: 25.0, minHeight: 25.0),
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 8.0),
|
||||
child: Container(
|
||||
decoration: languageModel.isEng
|
||||
? BoxDecoration(
|
||||
border: Border.all(color: Colors.blueAccent, width: 1))
|
||||
: null,
|
||||
child: Image.asset(
|
||||
"assets/eng_flag.png",
|
||||
width: 23,
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
decoration: !languageModel.isEng
|
||||
? BoxDecoration(
|
||||
border: Border.all(color: Colors.blueAccent, width: 1))
|
||||
: null,
|
||||
child: Image.asset(
|
||||
"assets/myan_flag.png",
|
||||
width: 23,
|
||||
),
|
||||
),
|
||||
],
|
||||
renderBorder: false,
|
||||
isSelected: _selection,
|
||||
fillColor: Colors.white,
|
||||
onPressed: (int index) {
|
||||
languageModel.saveLanguage(Translation().supportedLanguages[index]);
|
||||
setState(() {
|
||||
for (int buttonIndex = 0;
|
||||
buttonIndex < _selection.length;
|
||||
buttonIndex++) {
|
||||
if (buttonIndex == index) {
|
||||
_selection[buttonIndex] = true;
|
||||
} else {
|
||||
_selection[buttonIndex] = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
return OfflineRedirect(
|
||||
child: FlavorBanner(
|
||||
child: Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.white,
|
||||
iconTheme: IconThemeData(
|
||||
color: Colors.grey,
|
||||
),
|
||||
elevation: 0,
|
||||
title: Image(
|
||||
height: 30,
|
||||
fit: BoxFit.scaleDown,
|
||||
image: new AssetImage('assets/img/logo.png')),
|
||||
actions: <Widget>[
|
||||
IconButton(
|
||||
icon: Image.asset(
|
||||
"assets/manual.png",
|
||||
width: 30,
|
||||
height: 30,
|
||||
color: Colors.black,
|
||||
),
|
||||
onPressed: () => Navigator.of(context)
|
||||
.push(MaterialPageRoute(builder: (_) => ManualPage())),
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
FontAwesomeIcons.phone,
|
||||
color: Colors.black,
|
||||
size: 20,
|
||||
),
|
||||
onPressed: () => Navigator.of(context)
|
||||
.push(MaterialPageRoute(builder: (_) => Contact())),
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
FontAwesomeIcons.signInAlt,
|
||||
color: Colors.black,
|
||||
size: 20,
|
||||
),
|
||||
onPressed: () => Navigator.of(context)
|
||||
.push(MaterialPageRoute(builder: (_) => LoginPage())),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: ListView(
|
||||
children: <Widget>[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 8.0),
|
||||
child: toggleButtons,
|
||||
),
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 5),
|
||||
child: ProductsWidget(isWelcomePage: false),
|
||||
),
|
||||
SizedBox(height: 30,),
|
||||
Center(child: Text("${this.version}")),
|
||||
],
|
||||
)),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user