backup. before shop update

This commit is contained in:
2021-08-31 13:28:33 -04:00
parent c378a6203c
commit 808ffa3211
292 changed files with 51551 additions and 695 deletions

View File

@@ -0,0 +1,143 @@
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import '../../constants.dart';
import '../../dialog/logout_dialog.dart';
import '../../generated/l10n.dart';
import '../../models/user.dart';
import '../../routes.dart';
import '../../store/store.dart';
import '../../widgets/general/navigationbar_logo.dart';
import '../../widgets/general/text_link.dart';
class DesktopAppBarMenu extends StatelessWidget {
User _user;
@override
Widget build(BuildContext context) {
_user = store.state.user;
String currentRoute = ModalRoute.of(context).settings.name;
Widget menuBar = Container(
height: 56.0,
padding: EdgeInsets.only(left: 10.0, right: 10.0, top: 5.0, bottom: 5.0),
color: Colors.blue,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
NavigationBarLogo(),
Expanded(
child: Container(
alignment: Alignment.centerRight,
padding: EdgeInsets.only(left: 8.0, right: 16.0),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(width: 20.0,),
TextLink(
S.of(context).home,
'/',
color: Colors.white,
selected: currentRoute == '/',
clearStack: true,
),
SizedBox(width: 15.0,),
TextLink(
S.of(context).download,
'/download',
color: Colors.white,
selected: currentRoute == '/download',
),
SizedBox(width: 15.0,),
TextLink(
S.of(context).tutorials,
Constants.TUTORIAL_URL,
color: Colors.white,
selected: currentRoute == '/tutorials',
isLink: true,
),
SizedBox(width: 15.0,),
TextLink(
S.of(context).support,
'/my-support/${Constants.BUSINESS_ID}',
color: Colors.white,
selected: currentRoute == '/my-support/${Constants.BUSINESS_ID}',
),
SizedBox(width: 15.0,),
TextLink(
S.of(context).shop,
'/shop',
color: Colors.white,
selected: currentRoute == '/shop',
),
SizedBox(width: 15.0,),
TextLink(
S.of(context).blog,
'/blog/${Constants.BUSINESS_ID}',
color: Colors.white,
selected: currentRoute == '/blog/${Constants.BUSINESS_ID}',
),
SizedBox(width: 15.0,),
_user != null ?
(currentRoute == '/me') ?
MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: EdgeInsets.only(right: 4.0),
child: Text(
S.of(context).logout,
style: TextStyle(
color: Colors.white,
),
),
),
Icon(
Icons.logout,
color: Colors.white,
size: 16.0,
)
],
),
onTap: () {
showDialog(
context: context,
builder: (BuildContext context) {
return logoutDialog(context);
}
);
},
),
) : IconButton(
icon: Icon(
Icons.account_circle,
color: Colors.white,
),
onPressed: () {
Routes.router.navigateTo(context, '/me');
},
) :
TextLink(
S.of(context).login,
'/login',
color: Colors.white,
selected: currentRoute == '/login',
),
],
),
),
),
),
],
),
);
return menuBar;
}
}

View File

@@ -0,0 +1,314 @@
import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import '../../constants.dart';
import '../../events/eventbus.dart';
import '../../events/events.dart';
import '../../generated/l10n.dart';
import '../../models/address.dart';
import '../../models/blog.dart';
import '../../models/ticket.dart';
import '../../pages/edit_address.dart';
import '../../routes.dart';
import '../../store/actions.dart';
import '../../store/store.dart';
import '../../utils/http_util.dart';
import '../../utils/utils.dart';
import '../../widgets/general/bottom_nav.dart';
import '../../widgets/general/breadcrumbs.dart';
import '../../widgets/general/navigationbar.dart';
import '../../utils/util_web.dart' if (dart.library.io) '../../utils/util_io.dart';
class DesktopBlog extends StatefulWidget {
final int businessId;
const DesktopBlog({Key key, this.businessId}) : super(key: key);
@override
State<StatefulWidget> createState() {
return DesktopBlogState();
}
}
class DesktopBlogState extends State<DesktopBlog> {
List<Blog> blogs;
double division = 2;
int _page = 1;
int _pageCount = 1;
bool _isLoading = false;
bool _loadingFinish = false;
RefreshController _refreshController = RefreshController(initialRefresh: true);
void _onRefresh() {
_page = 1;
if (blogs != null) {
blogs.clear();
} else {
blogs = [];
}
_refreshController.resetNoData();
loadBlogs(true);
}
void _onLoadMore() {
// if failed,use loadFailed(),if no data return,use LoadNodata()
if (_pageCount > _page) {
_page += 1;
loadBlogs(false);
} else {
_refreshController.loadNoData();
}
}
double sideSpace = 0;
double mainSpace = 1200;
@override
Widget build(BuildContext context) {
store.dispatch(UpdateContext(context));
if (MediaQuery.of(context).size.width <= 1200) {
mainSpace = MediaQuery.of(context).size.width;
sideSpace = 0;
} else {
mainSpace = 1200;
sideSpace = (MediaQuery.of(context).size.width - 1200) / 2;
}
BuildContext mainContext = context;
return Scaffold(
appBar: NavigationBar(
title: S.of(context).blog,
back: true,
breadCrumbs: [
BreadCrumb(S.of(context).blog, null),
],
breadCrumbHeight: Constants.BREADCRUMB_HEIGHT,
),
body: SmartRefresher(
enablePullDown: true,
enablePullUp: true,
header: WaterDropHeader(),
footer: CustomFooter(
builder: (BuildContext context, LoadStatus mode){
Widget footer;
if(mode == LoadStatus.idle) {
footer = Text(S.of(context).pull_up_to_load_more);
} else if (mode == LoadStatus.loading) {
footer = CircularProgressIndicator();
} else if (mode == LoadStatus.failed) {
footer = Text(S.of(context).load_failed_retry);
} else if (mode == LoadStatus.canLoading) {
footer = Text(S.of(context).release_to_load_more);
} else if (mode == LoadStatus.noMore) {
footer = Text(S.of(context).no_more_record);
} else {
footer = Text('...');
}
return Container(
height: 55.0,
child: Center(child: footer,),
);
},
),
controller: _refreshController,
onRefresh: _onRefresh,
onLoading: _onLoadMore,
child: _buildBody(),
),
bottomNavigationBar: BottomNav(),
);
}
Widget _buildBody() {
if (blogs == null) {
return SizedBox.shrink();
}
Row row = Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: sideSpace,
),
Container(
width: mainSpace,
padding: EdgeInsets.only(
top: 12.0,
bottom: 16.0,
left: 8.0,
right: 8.0,
),
child: blogs == null ? Text('') :
(blogs.length > 0 ?
Wrap(
children: blogs.map((a) => GestureDetector(
child: _getBlog(a),
onTap: () {
Routes.router.navigateTo(context, '/view-blog/${a.id}');
},
)).toList(),
) :
Center(
child: Text(S.of(context).no_blog_yet),
)),
),
Container(
width: sideSpace,
),
],
);
return SingleChildScrollView(
child: row,
);
}
Widget _getBlog(Blog blog) {
return Container(
width: (mainSpace - 16.0) / division,
padding: EdgeInsets.symmetric(vertical: 8.0, horizontal: 8.0),
child: Container(
padding: EdgeInsets.only(top: 20.0, bottom: 20.0, left: 16.0, right: 16.0,),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Colors.black12,
width: 0.6,
),
top: BorderSide(
color: Colors.black12,
width: 0.6,
),
left: BorderSide(
color: Colors.black12,
width: 0.6,
),
right: BorderSide(
color: Colors.black12,
width: 0.6,
),
),
borderRadius: BorderRadius.all(Radius.circular(10.0)),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
(blog.thumbUrl != null) ?
Container(
padding: EdgeInsets.only(right: 10.0),
child: Util.showImage(
'https:${blog.thumbUrl}',
width: 80,
height: 80,
),
) : SizedBox.shrink(),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
child: Text(
blog.title,
style: TextStyle(
fontSize: 19.0,
),
overflow: TextOverflow.ellipsis,
),
),
Container(
child: Text(
Utils.utcDatetimeStringToLocalDatetimeString(context, blog.createdAt,),
style: TextStyle(
fontSize: 13.0,
color: Colors.grey,
),
),
),
],
),
),
],
),
),
);
}
@override
void dispose() {
_refreshController?.dispose();
super.dispose();
}
@override
void initState() {
super.initState();
eventBus.on<OnTicketsUpdated>().listen((event) {
if (mounted) {
setState(() {
blogs = null;
});
}
_refreshController.requestRefresh();
});
}
void loadBlogs(bool isRefresh) {
_loadingFinish = false;
HttpUtil.httpGet(
'v1/blogs',
businessId: widget.businessId,
queryParameters: {
'page': _page.toString(),
'size': Constants.BLOG_PER_PAGE_DESKTOP.toString()
}
).then((value) {
if (mounted) {
if (isRefresh) {
_refreshController.refreshCompleted();
} else {
_refreshController.loadComplete();
}
if (int.parse(value['currentPage'].toString()) >= int.parse(value['pageCount'].toString())) {
_loadingFinish = true;
}
if (_loadingFinish) {
_refreshController.loadNoData();
}
_page = int.parse(value['currentPage'].toString());
_pageCount = int.parse(value['pageCount'].toString());
setState(() {
if (blogs == null) {
blogs = [];
}
blogs.addAll((value['blogs'] as List).map((e) => Blog.fromJson(e)).toList());
});
}
}).catchError((error) {
if (mounted) {
if (isRefresh) {
_refreshController.refreshFailed();
} else {
_refreshController.loadFailed();
}
_isLoading = false;
Utils.showMessageDialog(context, error, onOk: () {
Navigator.of(context).pop();
Navigator.of(context).pop();
});
}
});
}
}

View File

@@ -0,0 +1,225 @@
import 'package:flutter/material.dart';
import '../../constants.dart';
import '../../generated/l10n.dart';
import '../../models/key_value.dart';
import '../../routes.dart';
import '../../store/store.dart';
import '../../utils/http_util.dart';
import '../../utils/utils.dart';
import '../../widgets/general/bottom_nav.dart';
import '../../widgets/general/breadcrumbs.dart';
import '../../widgets/general/navigationbar.dart';
class DesktopBuyService extends StatefulWidget {
final Map<String, dynamic> data;
const DesktopBuyService(this.data, {Key key})
: super(key: key);
@override
State<StatefulWidget> createState() => DesktopBuyServiceState();
}
class DesktopBuyServiceState extends State<DesktopBuyService> {
double sideSpace = 0;
double mainSpace = 1200;
List<KeyValue> plans = [];
KeyValue selectedPlan;
double price = 0.0;
double tax = 0.0;
double paymentAmount = 0.0;
@override
Widget build(BuildContext context) {
if (MediaQuery.of(context).size.width <= 1200) {
mainSpace = MediaQuery.of(context).size.width;
sideSpace = 0;
} else {
mainSpace = 1200;
sideSpace = (MediaQuery.of(context).size.width - 1200) / 2;
}
Row row = Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [],
);
Column col1 = Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: EdgeInsets.only(bottom: 16),
child: Text(
S.of(context).purchase_renew_service,
style: TextStyle(
fontSize: 28,
),
),
),
],
);
col1.children.add(
Utils.buildLine(S.of(context).service_descritpion, widget.data['service_selections']['description'], valueSize: 16),
);
col1.children.add(
Utils.buildLine(S.of(context).your_group, widget.data['group']['name'], valueSize: 16),
);
if (widget.data['exists_service'] == null) {
col1.children.add(
Utils.buildLine(S.of(context).current_plan, 'N/A', valueSize: 16),
);
} else {
col1.children.add(
Utils.buildLine(S.of(context).current_plan, widget.data['exists_service']['description'], valueSize: 16),
);
col1.children.add(
Utils.buildLine(S.of(context).expiration_date,
Utils.utcDatetimeStringToLocalDatetimeString(context,
widget.data['exists_service']['expiration_date']),
valueSize: 16
),
);
}
col1.children.add(
Utils.buildLine(S.of(context).select_a_plan, DropdownButton<KeyValue>(
items: plans.map((e) {
return DropdownMenuItem<KeyValue>(
value: e,
child: Text(e.name),
);
}).toList(),
isExpanded: true,
hint: Text(
S.of(context).select_a_plan,
),
onChanged: (newValue) {
print('newValue $newValue');
setState(() {
selectedPlan = newValue;
price = selectedPlan.value['price'];
tax = selectedPlan.value['price'] * selectedPlan.value['tax'];
paymentAmount = price + tax;
});
},
value: selectedPlan,
), valueSize: 16),
);
col1.children.add(
Utils.buildLine(S.of(context).price, price.toStringAsFixed(2), valueSize: 16)
);
col1.children.add(
Utils.buildLine(S.of(context).tax, tax.toStringAsFixed(2), valueSize: 16)
);
col1.children.add(
Utils.buildLine(S.of(context).total, paymentAmount.toStringAsFixed(2), valueSize: 32)
);
Column col2 = Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: EdgeInsets.only(left: 16, right: 16),
child: ElevatedButton(
child: Container(
padding: EdgeInsets.only(left: 16, right: 16, top: 8, bottom: 8),
child: Text(
S.of(context).pay_now,
style: TextStyle(
fontSize: 20,
),
),
),
onPressed: (paymentAmount > 0) ? () => _submit() : null,
),
),
],
);
row.children.add(
Expanded(
child: Container(
padding: EdgeInsets.only(top: 20, left: 16, right: 16, bottom: 20),
child: col1,
decoration: BoxDecoration(
border: Border(
right: BorderSide(
width: 1,
color: Colors.black12,
),
),
),
),
)
);
row.children.add(
Expanded(
child: Container(
padding: EdgeInsets.only(top: 20, left: 16, right: 16, bottom: 20),
child: col2,
),
)
);
return Scaffold(
appBar: NavigationBar(
title: S.of(context).blog,
back: true,
breadCrumbs: [
BreadCrumb(S.of(context).purchase_renew_service, null),
],
breadCrumbHeight: Constants.BREADCRUMB_HEIGHT,
),
body: SingleChildScrollView(
child: Row(
children: [
Container(
width: sideSpace,
),
Expanded(child: row,),
Container(
width: sideSpace,
),
],
),
),
bottomNavigationBar: BottomNav(),
);
}
void _submit() {
if (store.state.user == null) {
Utils.requireLogin(context, returnUrl: '/buy-service/${widget.data['group']['id']}/${widget.data['service_selections']['code']}');
return;
}
Map<String, dynamic> newData = widget.data;
newData['selected_plan'] = selectedPlan.value;
HttpUtil.httpPost('v1/create-service-buy-renewal-invoice',
(response) {
Routes.router.navigateTo(context, '/paynow/${response.data['order_id']}', replace: true);
},
body: newData,
).onError((error, stackTrace) {
Utils.showMessageDialog(context, error);
});
}
@override
void initState() {
super.initState();
List o = (widget.data['service_selections']['options'] as List);
for (int i = 0; i < o.length; i++) {
Map<String, dynamic> o1 = o[i];
plans.add(new KeyValue(o1['name'], o1));
}
}
}

View File

@@ -0,0 +1,421 @@
import 'package:countdown/countdown.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:flutter_wisetronic/widgets/general/breadcrumbs.dart';
import 'package:fluttertoast/fluttertoast.dart';
import '../../generated/l10n.dart';
import '../../routes.dart';
import '../../store/actions.dart';
import '../../store/store.dart';
import '../../utils/http_util.dart';
import '../../utils/utils.dart';
class DesktopChangeMobileOrEmail extends StatefulWidget {
final bool isMobile;
const DesktopChangeMobileOrEmail(this.isMobile, {Key key}) : super(key: key);
@override
State<StatefulWidget> createState() {
return DesktopChangeMobileOrEmailState();
}
}
class DesktopChangeMobileOrEmailState extends State<DesktopChangeMobileOrEmail> {
final GlobalKey<FormState> _formKey = GlobalKey();
final usernameController = TextEditingController();
bool usernameEnable = true;
final codeController = TextEditingController();
bool enableGetCode;
String getCodeText;
bool canRegister;
var countDownListener;
double sideSpace = 0;
double mainSpace = 1200;
@override
Widget build(BuildContext context) {
store.dispatch(UpdateContext(context));
if (MediaQuery.of(context).size.width <= 1200) {
mainSpace = MediaQuery.of(context).size.width;
sideSpace = 0;
} else {
mainSpace = 1200;
sideSpace = (MediaQuery.of(context).size.width - 1200) / 2;
}
Widget view = Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Form(
key: _formKey,
child: Container(
padding: EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
child: TextFormField(
controller: usernameController,
enabled: usernameEnable,
keyboardType: widget.isMobile ? TextInputType.phone : TextInputType.emailAddress,
decoration: new InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black12,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.blue,
),
),
labelText: widget.isMobile ? S.of(context).mobile_number : S.of(context).email,
),
style: TextStyle(
fontSize: 18.0
),
autofocus: true,
validator: (String value) {
if (value.trim().isEmpty) {
if (widget.isMobile) {
return S
.of(context)
.mobile_is_required;
} else {
return S
.of(context)
.email_is_required;
}
}
if (widget.isMobile && value.trim() == store.state.user.mobile) {
return S.of(context).the_mobile_number_is_same_as_current;
}
if (!widget.isMobile && value.trim() == store.state.user.email) {
return S.of(context).the_email_is_same_as_current;
}
return null;
},
onChanged: (string) {
if (string.isEmpty) {
if (mounted) {
setState(() {
canRegister = false;
});
}
} else if (string.isNotEmpty && codeController.text.trim().isNotEmpty) {
if (mounted) {
setState(() {
canRegister = true;
});
}
}
if (string.isNotEmpty && !enableGetCode) {
if (mounted) {
setState(() {
enableGetCode = true;
});
}
} else if (string.isEmpty && enableGetCode) {
if (mounted) {
setState(() {
enableGetCode = false;
});
}
}
},
),
),
Container(
child: TextFormField(
controller: codeController,
keyboardType: TextInputType.number,
decoration: new InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black12,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.blue,
),
),
labelText: S.of(context).verification_code,
suffixIcon: MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
child: Container(
margin: EdgeInsets.only(top: 6.0),
child: Container(
padding: EdgeInsets.only(left: 10.0, right: 10.0, top: 10.0, bottom: 10.0),
decoration: BoxDecoration(
shape: BoxShape.rectangle,
border: new Border.all(
color: enableGetCode ? Colors.black87 : Colors.black26,
width: 1.0,
),
borderRadius: BorderRadius.all(Radius.circular(10.0)),
),
child: Text(
getCodeText,
style: TextStyle(
color: enableGetCode ? Colors.black87 : Colors.black26,
fontSize: 14.0
),
),
),
),
onTap: enableGetCode ? getCodeTapped : null,
),
),
),
style: TextStyle(
fontSize: 18.0
),
validator: (String value) {
if (value.trim().isEmpty) {
return S.of(context).verification_code_is_required;
}
return null;
},
onChanged: (string) {
if (usernameController.text.trim().isNotEmpty && string.isNotEmpty) {
if (mounted) {
setState(() {
canRegister = true;
});
}
} else {
if (mounted) {
setState(() {
canRegister = false;
});
}
}
},
),
),
],
),
),
),
Container(
margin: EdgeInsets.only(top: 0.0, right: 16.0),
child: Align(
alignment: Alignment.centerRight,
child: RaisedButton(
color: Theme.of(context).primaryColor,
child: Text(
S.of(context).submit_to_change,
style: TextStyle(
color: Colors.white,
),
),
onPressed: canRegister ? register : null,
),
),
),
],
);
Widget v = SingleChildScrollView(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: sideSpace,
),
Container(
width: mainSpace,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: mainSpace / 2,
margin: EdgeInsets.only(top: 16.0, bottom: 16.0),
padding: EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
child: Text(
widget.isMobile ? S.of(context).change_mobile : S.of(context).change_email,
style: TextStyle(
fontSize: 24.0,
color: Colors.black
),
),
),
Container(
padding: EdgeInsets.only(top: 8.0, bottom: 16.0),
child: Text(
widget.isMobile ? S.of(context).change_mobile_desc : S.of(context).change_email_desc,
style: TextStyle(
color: Colors.black38,
),
),
),
],
),
),
Container(
width: mainSpace / 2,
margin: EdgeInsets.only(top: 16.0, bottom: 16.0),
padding: EdgeInsets.all(10.0),
decoration: BoxDecoration(
border: Border(
left: BorderSide(
width: 1.0,
color: Colors.black12,
),
),
),
child: view,
),
],
),
),
Container(
width: sideSpace,
),
],
),
);
return v;
}
@override
void initState() {
super.initState();
setState(() {
enableGetCode = false;
canRegister = false;
usernameEnable = true;
});
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
getCodeText = S.of(context).get_code;
}
void register() {
final FormState form = _formKey.currentState;
if (form.validate()) {
HttpUtil.httpPost('v1/users', (response) {
Utils.showMessageDialog(context, Exception(S.of(context).update_success), title: S.of(context).success, onOk: () {
Navigator.of(context).pop();
Routes.router.navigateTo(context, '/me', replace: true, clearStack: true);
});
},
queryParameters: {
'action': 'change-mobile-email',
},
isFormData: true,
body: {
'id': store.state.user.id,
'mobile': usernameController.text.trim(),
'code': codeController.text.trim(),
},
).catchError((error) {
Utils.showMessageDialog(context, error);
});
}
}
void getCodeTapped() {
if (usernameController.text.isNotEmpty &&
((widget.isMobile && usernameController.text.trim() != store.state.user.mobile) ||
(!widget.isMobile && usernameController.text.trim() != store.state.user.email))) {
HttpUtil.httpPost('v1/users', (response) {
Fluttertoast.showToast(
msg: S.of(context).verification_code_sent,
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
backgroundColor: Colors.black54,
textColor: Colors.white
);
countDownListener = CountDown(Duration(seconds: 90)).stream.listen(null);
startCountDown();
if (mounted) {
setState(() {
usernameEnable = false;
});
}
},
queryParameters: {
'action': 'change_mobile_email_send_code'
},
body: {
'id': store.state.user.id,
'mobile': usernameController.text,
},
isFormData: true,
).catchError((error) {
if (mounted) {
setState(() {
canRegister = false;
});
}
Utils.showMessageDialog(context, error);
});
} else {
String errorMsg = '';
if (widget.isMobile && usernameController.text.trim().isEmpty) {
errorMsg = S.of(context).mobile_is_required;
} else if (!widget.isMobile && usernameController.text.trim().isEmpty) {
errorMsg = S.of(context).email_is_required;
} else if (widget.isMobile && usernameController.text.trim() == store.state.user.mobile) {
errorMsg = S.of(context).the_mobile_number_is_same_as_current;
} else if (!widget.isMobile && usernameController.text.trim() == store.state.user.email) {
errorMsg = S.of(context).the_email_is_same_as_current;
}
Fluttertoast.showToast(
msg: errorMsg,
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
backgroundColor: Colors.red,
textColor: Colors.white
);
}
}
void startCountDown() {
countDownListener.onData((Duration d) {
if (mounted) {
setState(() {
enableGetCode = false;
getCodeText = S.of(context).get_code_token(d.inSeconds);
});
}
});
countDownListener.onDone(() {
if (mounted) {
setState(() {
enableGetCode = true;
getCodeText = S.of(context).get_code_again;
});
}
countDownListener = CountDown(Duration(seconds: 90)).stream.listen(null);
});
}
}

View File

@@ -0,0 +1,348 @@
import 'package:flutter/material.dart';
import '../../generated/l10n.dart';
import '../../store/store.dart';
import '../../utils/http_util.dart';
import '../../utils/utils.dart';
import '../../widgets/general/breadcrumbs.dart';
class DesktopChangePassword extends StatefulWidget {
const DesktopChangePassword({Key key}) : super(key: key);
@override
State<StatefulWidget> createState() {
return DesktopChangePasswordState();
}
}
class DesktopChangePasswordState extends State<DesktopChangePassword> {
final GlobalKey<FormState> _formKey = GlobalKey();
final oldPasswordController = TextEditingController();
final passwordController = TextEditingController();
final passwordAgainController = TextEditingController();
bool passwordVisible;
bool passwordAgainVisible;
bool canReset;
double sideSpace = 0;
double mainSpace = 1200;
@override
void initState() {
super.initState();
canReset = false;
passwordVisible = true;
passwordAgainVisible = true;
}
@override
Widget build(BuildContext context) {
if (MediaQuery.of(context).size.width <= 1200) {
mainSpace = MediaQuery.of(context).size.width;
sideSpace = 0;
} else {
mainSpace = 1200;
sideSpace = (MediaQuery.of(context).size.width - 1200) / 2;
}
Widget view = SingleChildScrollView(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: sideSpace,
),
Container(
width: mainSpace,
padding: EdgeInsets.only(top: 16.0, bottom: 20.0),
child: Container(
width: mainSpace,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
child: Container(
padding: EdgeInsets.only(top: 16.0, left: 16.0, right: 16.0, bottom: 16.0),
child: Text(
S.of(context).change_password,
style: TextStyle(
fontSize: 24.0,
color: Colors.black
),
),
),
),
Container(
padding: EdgeInsets.only(left: 20.0, right: 20.0),
child: Text(
S.of(context).change_password_desc,
style: TextStyle(
color: Colors.black38,
),
),
),
],
),
),
Expanded(
child: Column(
children: [
Container(
padding: EdgeInsets.only(top: 0.0, left: 16.0, right: 16.0, bottom: 0.0),
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
child: TextFormField(
controller: oldPasswordController,
decoration: new InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black12,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.blue,
),
),
labelText: S.of(context).old_password,
suffixIcon: IconButton(
icon: Icon(
passwordVisible ? Icons.visibility_off : Icons.visibility,
color: Theme.of(context).primaryColorDark,
),
onPressed: () {
setState(() {
passwordVisible = !passwordVisible;
});
},
),
),
style: TextStyle(
fontSize: 18.0
),
validator: (String value) {
if (value.trim().isEmpty) {
return S.of(context).current_password_is_required;
}
return null;
},
obscureText: passwordVisible,
onChanged: (string) {
if (string.trim().isNotEmpty && passwordController.text.trim().isNotEmpty && passwordAgainController.text.trim().isNotEmpty) {
if (mounted) {
setState(() {
canReset = true;
});
}
} else {
if (mounted) {
setState(() {
canReset = false;
});
}
}
},
),
),
Container(
child: TextFormField(
controller: passwordController,
decoration: new InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black12,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.blue,
),
),
labelText: S.of(context).password,
suffixIcon: IconButton(
icon: Icon(
passwordVisible ? Icons.visibility_off : Icons.visibility,
color: Theme.of(context).primaryColorDark,
),
onPressed: () {
setState(() {
passwordVisible = !passwordVisible;
});
},
),
),
style: TextStyle(
fontSize: 18.0
),
validator: (String value) {
if (value.trim().isEmpty) {
return S.of(context).password_is_required;
}
return null;
},
obscureText: passwordVisible,
onChanged: (string) {
if (string.trim().isNotEmpty && oldPasswordController.text.trim().isNotEmpty && passwordAgainController.text.trim().isNotEmpty) {
if (mounted) {
setState(() {
canReset = true;
});
}
} else {
if (mounted) {
setState(() {
canReset = false;
});
}
}
},
),
),
Container(
child: TextFormField(
controller: passwordAgainController,
decoration: new InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black12,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.blue,
),
),
labelText: S.of(context).password_again,
suffixIcon: IconButton(
icon: Icon(
passwordAgainVisible ? Icons.visibility_off : Icons.visibility,
color: Theme.of(context).primaryColorDark,
),
onPressed: () {
setState(() {
passwordAgainVisible = !passwordAgainVisible;
});
},
),
),
style: TextStyle(
fontSize: 18.0
),
validator: (String value) {
if (value.trim().isEmpty) {
return S.of(context).password_is_required;
}
if (value.trim() != passwordController.text.trim()) {
return S.of(context).password_is_not_match_password_again;
}
return null;
},
obscureText: passwordAgainVisible,
onChanged: (string) {
if (passwordController.text.trim().isNotEmpty && string.trim().isNotEmpty && oldPasswordController.text.trim().isNotEmpty) {
if (mounted) {
setState(() {
canReset = true;
});
}
} else {
if (mounted) {
setState(() {
canReset = false;
});
}
}
},
),
),
],
),
),
),
Container(
padding: EdgeInsets.only(right: 16.0, top: 16.0),
child: Align(
alignment: Alignment.centerRight,
child: RaisedButton(
color: Theme.of(context).primaryColor,
child: Text(
S.of(context).submit,
style: TextStyle(
color: Colors.white,
),
),
onPressed: canReset ? resetPassword : null,
),
),
),
],
),
),
],
),
),
),
Container(
width: sideSpace,
),
],
),
);
return view;
}
void resetPassword() {
final FormState form = _formKey.currentState;
if (form.validate()) {
HttpUtil.httpPost('v1/users', (response) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(S.of(context).success),
content: Text(S.of(context).password_has_been_changed),
actions: <Widget>[
FlatButton(
child: Text(S.of(context).ok),
onPressed: () {
Navigator.of(context).pop();
Navigator.of(context).pop();
},
),
],
);
},
);
},
queryParameters: {
'action': 'change_password',
},
isFormData: true,
body: {
'id': store.state.user.id,
'old_password': oldPasswordController.text.trim(),
'password': passwordController.text.trim(),
}
).catchError((error) {
Utils.showMessageDialog(context, error);
});
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,357 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_wisetronic/widgets/general/text_link.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:universal_io/io.dart';
import '../../constants.dart';
import '../../generated/l10n.dart';
import '../../models/business.dart';
import '../../widgets/general/bottom_nav.dart';
import '../../widgets/general/breadcrumbs.dart';
import '../../widgets/general/navigationbar.dart';
import '../../utils/util_web.dart' if (dart.library.io) '../../utils/util_io.dart';
class DesktopContactUs extends StatefulWidget {
final Business business;
const DesktopContactUs(this.business, {Key key}) : super(key: key);
@override
State<StatefulWidget> createState() {
return DesktopContactUsState();
}
}
class DesktopContactUsState extends State<DesktopContactUs> {
double sideSpace = 0;
double mainSpace = 1200;
String mapUrl = 'https://goo.gl/maps/M365MF5AW35n9ij67';
Completer<GoogleMapController> _controller = Completer();
LatLng _lastMapPosition;
final Set<Marker> _markers = {};
final Set<Polyline> _polyLine = {};
void _onMapCreated(GoogleMapController controller) {
_controller.complete(controller);
}
void _onCameraMove(CameraPosition position) {
_lastMapPosition = position.target;
}
@override
Widget build(BuildContext context) {
if (MediaQuery.of(context).size.width <= 1200) {
mainSpace = MediaQuery.of(context).size.width;
sideSpace = 0;
} else {
mainSpace = 1200;
sideSpace = (MediaQuery.of(context).size.width - 1200) / 2;
}
Column col = Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [],
);
col.children.add(
Container(
padding: EdgeInsets.only(top: 20.0, left: 16, right: 16, bottom: 20),
child: Center(
child: Text(
S.of(context).contact_us,
style: TextStyle(
fontSize: 36,
),
),
),
)
);
col.children.add(
Container(
padding: EdgeInsets.only(left: 16.0, right: 16.0, top: 0, bottom: 20),
child: Row(
children: [
Container(
padding: EdgeInsets.only(right: 20),
child: Util.showImage(
'${widget.business.picUrl}',
width: 100,
height: 100,
),
),
Container(
child: Text(
'${widget.business.name}',
style: TextStyle(
fontSize: 20,
),
),
),
],
),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 1.0,
color: Colors.black38,
),
),
),
),
);
col.children.add(
Container(
padding: EdgeInsets.only(left: 8.0, right: 8.0, top: 8, bottom: 8),
child: Container(
padding: EdgeInsets.only(left: 8, right: 8, top: 8, bottom: 8),
child: Row(
children: [
Container(
padding: EdgeInsets.only(right: 8.0),
child: Icon(Icons.mail_outline, size: 18, color: Colors.black38,),
),
Text(
S.of(context).by_email,
style: TextStyle(
color: Colors.black38,
),
),
],
),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 1.0,
color: Colors.black12,
),
),
),
),
)
);
col.children.add(
Container(
padding: EdgeInsets.only(left: 16, right: 16, top: 4, bottom: 4),
child: TextLink(
'support@wisetronic.com',
'support@wisetronic.com',
isEmail: true,
),
),
);
col.children.add(
Container(
padding: EdgeInsets.only(left: 8.0, right: 8.0, top: 8, bottom: 8),
child: Container(
padding: EdgeInsets.only(left: 8, right: 8, top: 8, bottom: 8),
child: Row(
children: [
Container(
padding: EdgeInsets.only(right: 8.0),
child: Icon(Icons.phone, size: 18, color: Colors.black38,),
),
Text(
S.of(context).by_phone,
style: TextStyle(
color: Colors.black38,
),
),
],
),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 1.0,
color: Colors.black12,
),
),
),
),
)
);
col.children.add(
Container(
padding: EdgeInsets.only(left: 16, right: 16, top: 4, bottom: 4),
child: TextLink(
'905-604-8861',
'905-604-8861',
isPhone: true,
),
)
);
col.children.add(
Container(
padding: EdgeInsets.only(left: 16, right: 16, top: 4, bottom: 4),
child: TextLink(
'905-604-6681',
'905-604-6681',
isPhone: true,
),
)
);
col.children.add(
Container(
padding: EdgeInsets.only(left: 16, right: 16, top: 4, bottom: 4),
child: Row(
children: [
Container(
child: Text(
S.of(context).toll_free,
),
),
Container(
child: TextLink(
'1-855-278-8026',
'1-855-278-8026',
isPhone: true,
),
),
],
),
)
);
col.children.add(
Container(
padding: EdgeInsets.only(left: 8.0, right: 8.0, top: 8, bottom: 8),
child: Container(
padding: EdgeInsets.only(left: 8, right: 8, top: 8, bottom: 8),
child: Row(
children: [
Container(
padding: EdgeInsets.only(right: 8.0),
child: Icon(Icons.location_on, size: 18, color: Colors.black38,),
),
Text(
S.of(context).address,
style: TextStyle(
color: Colors.black38,
),
),
],
),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 1.0,
color: Colors.black12,
),
),
),
),
)
);
col.children.add(
Container(
padding: EdgeInsets.only(left: 16, right: 16, top: 4, bottom: 4),
child: Text(
'${widget.business.address.addressLine1}',
),
)
);
if (widget.business.address.addressLine2 != null && widget.business.address.addressLine2.isNotEmpty) {
col.children.add(
Container(
padding: EdgeInsets.only(left: 16, right: 16, top: 4, bottom: 4),
child: Text(
'${widget.business.address.addressLine2}',
),
)
);
}
col.children.add(
Container(
padding: EdgeInsets.only(left: 16, right: 16, top: 4, bottom: 4),
child: Text(
'${widget.business.address.city}, ${widget.business.address.state}',
),
)
);
col.children.add(
Container(
padding: EdgeInsets.only(left: 16, right: 16, top: 4, bottom: 4),
child: Text(
'${widget.business.address.country}, ${widget.business.address.zip}',
),
)
);
if (Platform.isAndroid || Platform.isIOS) {
_markers.clear();
_markers.add(Marker(
markerId: MarkerId('shop_position'),
position: LatLng(double.parse(widget.business.address.lat),
double.parse(widget.business.address.lng)),
infoWindow: InfoWindow(
title: S
.of(context)
.store,
snippet: '',
),
));
col.children.add(
Container(
height: 200,
padding: EdgeInsets.only(left: 16, right: 16, top: 4, bottom: 20),
child: GoogleMap(
onMapCreated: _onMapCreated,
initialCameraPosition: CameraPosition(
target: LatLng(
double.parse(widget.business.address.lat),
double.parse(widget.business.address.lng)),
zoom: 14.0,
),
onCameraMove: _onCameraMove,
markers: _markers,
gestureRecognizers: <Factory<OneSequenceGestureRecognizer>>[
new Factory<OneSequenceGestureRecognizer>(() => new EagerGestureRecognizer(),)
].toSet(),
),
),
);
} else {
col.children.add(
Container(
padding: EdgeInsets.only(left: 16, right: 16, top: 4, bottom: 20),
child: TextLink(
S.of(context).view_on_google_map,
mapUrl,
isLink: true,
),
)
);
}
return Scaffold(
appBar: NavigationBar(
title: S.of(context).blog,
back: true,
breadCrumbs: [
BreadCrumb(S.of(context).contact_us, null),
],
breadCrumbHeight: Constants.BREADCRUMB_HEIGHT,
),
body: SingleChildScrollView(
child: Row(
children: [
Container(
width: sideSpace,
),
Expanded(child: col,),
Container(
width: sideSpace,
),
],
),
),
bottomNavigationBar: BottomNav(),
);
}
}

View File

@@ -0,0 +1,330 @@
import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import '../../constants.dart';
import '../../generated/l10n.dart';
import '../../models/coupon.dart';
import '../../routes.dart';
import '../../store/actions.dart';
import '../../store/store.dart';
import '../../utils/http_util.dart';
import '../../utils/util_web.dart' if (dart.library.io) '../../utils/util_io.dart';
import '../../utils/utils.dart';
import '../../widgets/general/breadcrumbs.dart';
import '../../widgets/general/navigationbar.dart';
class DesktopCoupons extends StatefulWidget {
final int contactId;
final Key key;
const DesktopCoupons(this.contactId, {this.key});
@override
State<StatefulWidget> createState() {
return DesktopCouponsState();
}
}
class DesktopCouponsState extends State<DesktopCoupons> {
List<Coupon> coupons;
double sideSpace = 0;
double mainSpace = 1200;
@override
Widget build(BuildContext context) {
store.dispatch(UpdateContext(context));
if (coupons == null ) {
return new Scaffold(
body: Center(
child: SpinKitWave(
color: Colors.lightBlueAccent,
size: 40.0,
),
),
);
}
if (MediaQuery.of(context).size.width <= 1200) {
mainSpace = MediaQuery.of(context).size.width;
sideSpace = 0;
} else {
mainSpace = 1200;
sideSpace = (MediaQuery.of(context).size.width - 1200) / 2;
}
Widget w = ListView.builder(
itemCount: coupons.length > 0 ? coupons.length : 1,
itemBuilder: (BuildContext context, int position) {
if (coupons.length > 0) {
Coupon coupon = coupons[position];
return Container(
color: Colors.black12,
child: couponWidget(coupon),
);
} else {
return Center(
child: Container(
padding: EdgeInsets.all(20.0),
child: Text(
S.of(context).no_coupon_available,
),
),
);
}
}
);
return Scaffold(
appBar: NavigationBar(
title: S.of(context).blog,
back: true,
breadCrumbs: [
BreadCrumb(S.of(context).coupons, null),
],
breadCrumbHeight: Constants.BREADCRUMB_HEIGHT,
),
body: Row(
children: [
Container(
width: sideSpace,
),
Expanded(child: w,),
Container(
width: sideSpace,
),
],
),
);
}
Widget couponWidget(Coupon coupon) {
Column column = Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[],
);
Row row = Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
padding: EdgeInsets.only(right: 5.0),
child: coupon.store != null ?
Util.showImage('${coupon.store.picUrl}',
fit: BoxFit.fill,
width: 40.0,
) :
Image.asset(
'assets/images/ic_launcher.png',
width: 40.0,
height: 40.0,
fit: BoxFit.fill,
),
),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
coupon.store != null ? coupon.store.name : S.of(context).general_coupon,
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
Text(
coupon.description,
style: TextStyle(
fontSize: 12.0,
color: Colors.black26,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
),
)
],
),
),
),
],
);
Column valueColumn = Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
child: !coupon.isPercentage ?
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
child: Text(
'\$',
style: TextStyle(
fontSize: 15.0,
color: Colors.redAccent,
),
),
),
Container(
child: Text(
'${Utils.smartRound(coupon.valueAmount, 2)}',
style: TextStyle(
fontSize: 28.0,
color: Colors.redAccent,
),
),
),
],
) :
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
child: Text(
'${Utils.smartRound(coupon.valueAmount, 2)}',
style: TextStyle(
fontSize: 28.0,
color: Colors.redAccent,
),
),
),
Container(
child: Text(
S.of(context).percent_discount,
style: TextStyle(
fontSize: 15.0,
color: Colors.redAccent,
),
),
),
],
),
),
Container(
child: Text(
coupon.minAmount > 0 ?
S.of(context).available_for_order_over_token(coupon.minAmount) :
S.of(context).no_restriction,
style: TextStyle(
fontSize: 10.0,
color: Colors.black26,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
],
);
row.children.add(valueColumn);
column.children.add(row);
column.children.add(Container(
width: double.infinity,
margin: EdgeInsets.only(top: 10.0, bottom: 0.0),
padding: EdgeInsets.only(left: 10.0, right: 10.0, top: 10.0, bottom: 0.0),
decoration: BoxDecoration(
border: Border(
top: BorderSide(
width: 0.5,
color: Colors.black12
)
)
),
child: SizedBox.shrink(),
));
column.children.add(
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
child: Text(
coupon.expirationDate == null || coupon.expirationDate.length == 0 ?
S.of(context).no_expiration :
S.of(context).expiration_date_token(coupon.expirationDate),
style: TextStyle(
color: Colors.black26,
fontSize: 15.0,
),
),
),
Container(
child: RaisedButton(
color: Theme.of(context).primaryColor,
child: Text(
S.of(context).redeem_coupon,
style: TextStyle(
color: Colors.white,
),
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0),
),
onPressed: () {
if (coupon.store != null) {
Routes.router.navigateTo(context, '/shop/${coupon.store.id}/na/na/na');
} else {
Routes.router.navigateTo(context, '/businesses');
}
},
),
),
],
),
);
return Container(
margin: EdgeInsets.only(top: 10.0, left: 10.0, right: 10.0, bottom: 5.0),
padding: EdgeInsets.only(top: 20.0, bottom: 20.0, left: 10.0, right: 10.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10.0),
topRight: Radius.circular(10.0),
bottomLeft: Radius.circular(10.0),
bottomRight: Radius.circular(10.0),
),
),
child: column,
);
}
@override
void initState() {
super.initState();
coupons = null;
HttpUtil.httpGet(
'v1/coupons'
).then((data) {
if (mounted) {
setState(() {
coupons =
(data as List).map((e) => Coupon.fromJson(e)).toList();
});
}
}).catchError((error) {
Utils.showMessageDialog(context, error);
});
}
}

View File

@@ -1,5 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_wisetronic/generated/l10n.dart';
import 'package:flutter_wisetronic/widgets/general/breadcrumbs.dart';
import '../../widgets/general/download_item.dart';
import '../../store/store.dart';
import '../../utils/util_web.dart' if (dart.library.io) '../../utils/util_io.dart';
@@ -34,8 +36,10 @@ class DesktopDownloadAppsState extends State<DesktopDownloadApps> {
}
Column col = Column(
children: [
Util.showImage(
'https:${widget.data['download-image']['image']}'
Container(
child: Util.showImage(
'https:${widget.data['download-image']['image']}'
),
),
],
mainAxisAlignment: MainAxisAlignment.start,
@@ -50,20 +54,33 @@ class DesktopDownloadAppsState extends State<DesktopDownloadApps> {
wrap.children.add(apps[i]);
}
col.children.add(wrap);
return Row(
children: [
Container(
width: sideSpace,
),
Container(
width: mainSpace,
child: col,
),
Container(
width: sideSpace,
),
],
Widget view = SingleChildScrollView(
child: Row(
children: [
Container(
width: sideSpace,
),
Container(
width: mainSpace,
child: col,
),
Container(
width: sideSpace,
),
],
),
);
// return Column(
// mainAxisAlignment: MainAxisAlignment.start,
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// BreadCrumbs(true, breadCrumbs: [
// BreadCrumb(S.of(context).downloads, null)
// ],),
// Expanded(child: view),
// ],
// );
return view;
}
List<Widget> _getApps() {

View File

@@ -0,0 +1,589 @@
import 'package:email_validator/email_validator.dart';
import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:flutter_wisetronic/widgets/general/breadcrumbs.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:gender_selection/gender_selection.dart';
import '../../constants.dart';
import '../../events/eventbus.dart';
import '../../events/events.dart';
import '../../generated/l10n.dart';
import '../../models/address.dart';
import '../../routes.dart';
import '../../store/actions.dart';
import '../../store/store.dart';
import '../../utils/http_util.dart';
import '../../utils/utils.dart';
import '../../widgets/general/navigationbar.dart';
class DesktopEditAddress extends StatefulWidget {
final Key key;
final Address address;
final int businessId;
const DesktopEditAddress(this.address, {this.key, int businessId}) :
businessId = businessId ?? Constants.BUSINESS_ID;
@override
State<StatefulWidget> createState() {
return DesktopEditAddressState();
}
}
class DesktopEditAddressState extends State<DesktopEditAddress> {
final GlobalKey<FormState> _formKey = new GlobalKey();
final contactNameController = TextEditingController();
final phoneController = TextEditingController();
final streetLine1Controller = TextEditingController();
final streetLine2Controller = TextEditingController();
final cityController = TextEditingController();
final postalCodeController = TextEditingController();
final emailController = TextEditingController();
final faxController = TextEditingController();
String country;
Gender _selectedGender;
String _selectedProvince;
bool showLoading;
double sideSpace = 0;
double mainSpace = 1200;
@override
Widget build(BuildContext context) {
store.dispatch(UpdateContext(context));
if (MediaQuery.of(context).size.width <= 1200) {
mainSpace = MediaQuery.of(context).size.width;
sideSpace = 0;
} else {
mainSpace = 1200;
sideSpace = (MediaQuery.of(context).size.width - 1200) / 2;
}
if (showLoading) {
return new Scaffold(
body: Center(
child: SpinKitWave(
color: Colors.lightBlueAccent,
size: 40.0,
),
),
);
}
Widget form = SingleChildScrollView(
padding: EdgeInsets.only(
left: 0.0, right: 0.0, top: 0.0, bottom: 0.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
padding: EdgeInsets.only(
left: 16.0, right: 16.0, top: 16.0, bottom: 0.0),
child: TextFormField(
controller: contactNameController,
decoration: InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black12,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.blue,
)
),
labelText: S
.of(context)
.contact_name,
),
validator: (String value) {
if (value
.trim()
.isEmpty) {
return S
.of(context)
.contact_name_is_required;
}
return null;
},
),
),
GenderSelection(
maleText: S
.of(context)
.mr,
femaleText: S
.of(context)
.ms,
selectedGenderIconBackgroundColor: Colors.indigo,
checkIconAlignment: Alignment.bottomRight,
selectedGenderCheckIcon: Icons.check,
onChanged: (Gender gender) {
_selectedGender = gender;
},
equallyAligned: true,
animationDuration: Duration(milliseconds: 400),
isCircular: true,
isSelectedGenderIconCircular: true,
opacityOfGradient: 0.6,
padding: const EdgeInsets.all(3.0),
size: 50,
selectedGender: _selectedGender,
),
Container(
padding: EdgeInsets.only(
left: 16.0, right: 16.0, top: 16.0, bottom: 0.0),
child: TextFormField(
keyboardType: TextInputType.phone,
controller: phoneController,
decoration: InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black12,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.blue,
)
),
labelText: S
.of(context)
.mobile_phone_number,
),
validator: (String value) {
if (value
.trim()
.isEmpty) {
return S
.of(context)
.mobile_phone_number_is_required;
}
return null;
},
),
),
Container(
padding: EdgeInsets.only(
left: 16.0, right: 16.0, top: 16.0, bottom: 0.0),
child: TextFormField(
controller: streetLine1Controller,
decoration: InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black12,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.blue,
)
),
labelText: S
.of(context)
.street_line_1,
),
validator: (String value) {
if (value
.trim()
.isEmpty) {
return S
.of(context)
.street_line_1_is_required;
}
return null;
},
),
),
Container(
padding: EdgeInsets.only(
left: 16.0, right: 16.0, top: 16.0, bottom: 0.0),
child: TextFormField(
controller: streetLine2Controller,
decoration: InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black12,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.blue,
)
),
labelText: S
.of(context)
.street_line_2,
),
),
),
Container(
padding: EdgeInsets.only(
left: 16.0, right: 16.0, top: 16.0, bottom: 0.0),
child: TextFormField(
controller: cityController,
decoration: InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black12,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.blue,
)
),
labelText: S
.of(context)
.city,
),
validator: (String value) {
if (value
.trim()
.isEmpty) {
return S
.of(context)
.city_is_required;
}
return null;
},
),
),
Container(
padding: EdgeInsets.only(
left: 16.0, right: 16.0, top: 16.0, bottom: 0.0),
child: DropdownButton<String>(
value: _selectedProvince,
items: <String>[
'Ontario',
'Quebec',
'British Columbia',
'Alberta',
'Manitoba',
'Saskatchewan',
'Nova Scotia',
'New Brunswich',
'Newfoundland and Labrador',
'Prince Edward Island',
'Northwest Territories',
'Nunavut',
'Yukon',
].map((value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
onChanged: (newValue) {
if (mounted) {
setState(() {
_selectedProvince = newValue;
});
}
},
hint: Text(S
.of(context)
.province),
isExpanded: true,
),
),
Container(
padding: EdgeInsets.only(
left: 16.0, right: 16.0, top: 16.0, bottom: 0.0),
child: TextFormField(
controller: postalCodeController,
decoration: InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black12,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.blue,
)
),
labelText: S
.of(context)
.postal_code,
),
validator: (String value) {
if (value
.trim()
.isEmpty) {
return S
.of(context)
.postal_code_is_required;
}
return null;
},
),
),
Container(
padding: EdgeInsets.only(
left: 16.0, right: 16.0, top: 32.0, bottom: 0.0),
child: Text(
S
.of(context)
.optional_information,
style: TextStyle(
fontSize: 12.0,
color: Colors.grey,
),
),
),
Container(
padding: EdgeInsets.only(
left: 16.0, right: 16.0, top: 0.0, bottom: 0.0),
child: TextFormField(
controller: emailController,
decoration: InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black12,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.blue,
)
),
labelText: S
.of(context)
.email,
),
validator: (String value) {
if (value.isNotEmpty && !EmailValidator.validate(value)) {
return S
.of(context)
.email_is_not_valid;
}
return null;
},
),
),
Container(
padding: EdgeInsets.only(
left: 16.0, right: 16.0, top: 16.0, bottom: 16.0),
child: TextFormField(
controller: faxController,
keyboardType: TextInputType.phone,
decoration: InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black12,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.blue,
)
),
labelText: S
.of(context)
.fax,
),
),
),
],
),
);
return Scaffold(
appBar: NavigationBar(
title: S.of(context).edit_address,
back: true,
breadCrumbs: [
BreadCrumb(S.of(context).edit_address, null),
],
breadCrumbHeight: Constants.BREADCRUMB_HEIGHT,
),
body: Form(
key: _formKey,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: sideSpace,
),
Container(
width: mainSpace,
child: form,
),
Container(
width: sideSpace,
),
],
),
),
bottomNavigationBar: Container(
padding: EdgeInsets.all(8.0),
color: Theme
.of(context)
.buttonColor,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
FlatButton(
child: Text(
S
.of(context)
.delete,
),
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(S
.of(context)
.warning),
content: Text(S
.of(context)
.are_you_sure_to_delete_the_address),
actions: <Widget>[
FlatButton(
child: Text(S
.of(context)
.cancel),
onPressed: () {
Navigator.of(context).pop();
},
),
FlatButton(
child: Text(S
.of(context)
.yes_i_am_sure),
onPressed: () {
Navigator.of(context).pop();
_deleteAddress();
},
)
],
);
}
);
},
),
FlatButton(
child: Text(
S
.of(context)
.save
),
onPressed: () {
_saveEditAddress();
},
),
],
),
),
);
}
@override
void initState() {
super.initState();
setState(() {
showLoading = false;
_selectedProvince = widget.address.state;
cityController.text = widget.address.city;
postalCodeController.text = widget.address.zip;
streetLine1Controller.text = widget.address.addressLine1;
streetLine2Controller.text = widget.address.addressLine2;
_selectedGender = widget.address.gender == 1 ? Gender.Male : Gender.Female;
country = widget.address.country;
contactNameController.text = widget.address.contactName;
phoneController.text = widget.address.phone;
emailController.text = widget.address.email;
faxController.text = widget.address.fax;
});
}
void _saveEditAddress() {
final FormState form = _formKey.currentState;
if (form.validate()) {
if (mounted) {
setState(() {
showLoading = true;
});
}
HttpUtil.httpPatch('v1/addresses/${widget.address.id}', (response) {
if (mounted) {
setState(() {
showLoading = false;
});
}
eventBus.fire(OnAddressesUpdated());
if (widget.businessId > 0) {
Routes.router.navigateTo(context, '/checkout/${widget.businessId}', replace: true);
} else {
Navigator.of(context).pop();
}
},
body: {
'id': widget.address.id,
'name': contactNameController.text.trim(),
'address_line1': streetLine1Controller.text.trim(),
'address_line2': streetLine2Controller.text.trim(),
'city': cityController.text.trim(),
'state': _selectedProvince,
'zip': postalCodeController.text.trim(),
'phone': phoneController.text.trim(),
'gender': _selectedGender == Gender.Male ? true : false,
'email': emailController.text,
'fax': faxController.text,
'country': country,
}
).catchError((error) {
if (mounted) {
setState(() {
showLoading = false;
});
}
Utils.showMessageDialog(context, error);
});
}
}
void _deleteAddress() {
if (mounted) {
setState(() {
showLoading = true;
});
}
HttpUtil.httpDelete('v1/addresses/${widget.address.id}', (response) {
if (mounted) {
setState(() {
showLoading = false;
});
}
Fluttertoast.showToast(
msg: S.of(context).the_address_has_been_deleted,
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
backgroundColor: Colors.black54,
textColor: Colors.white
);
eventBus.fire(OnAddressesUpdated());
Navigator.of(context).pop();
}).catchError((error) {
if (mounted) {
setState(() {
showLoading = false;
});
}
Utils.showMessageDialog(context, error);
});
}
}

View File

@@ -0,0 +1,380 @@
import 'package:countdown/countdown.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_wisetronic/widgets/general/breadcrumbs.dart';
import 'package:fluttertoast/fluttertoast.dart';
import '../../generated/l10n.dart';
import '../../routes.dart';
import '../../store/actions.dart';
import '../../store/store.dart';
import '../../utils/http_util.dart';
import '../../utils/utils.dart';
class DesktopForgotPassword extends StatefulWidget {
const DesktopForgotPassword({Key key}) : super(key: key);
@override
State<StatefulWidget> createState() {
return DesktopForgotPasswordState();
}
}
class DesktopForgotPasswordState extends State<DesktopForgotPassword> {
final GlobalKey<FormState> _formKey = GlobalKey();
final usernameController = TextEditingController();
bool usernameEnable = true;
final codeController = TextEditingController();
bool enableGetCode;
String getCodeText;
bool canRegister;
var countDownListener;
double sideSpace = 0;
double mainSpace = 1200;
@override
Widget build(BuildContext context) {
store.dispatch(UpdateContext(context));
if (MediaQuery.of(context).size.width <= 1200) {
mainSpace = MediaQuery.of(context).size.width;
sideSpace = 0;
} else {
mainSpace = 1200;
sideSpace = (MediaQuery.of(context).size.width - 1200) / 2;
}
Widget view = Column(
children: <Widget>[
Form(
key: _formKey,
child: Container(
padding: EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
margin: EdgeInsets.only(top: 0.0),
child: TextFormField(
controller: usernameController,
enabled: usernameEnable,
keyboardType: TextInputType.emailAddress,
decoration: new InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black12,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.blue,
),
),
labelText: S.of(context).mobile_or_email,
),
style: TextStyle(
fontSize: 18.0
),
autofocus: true,
validator: (String value) {
if (value.trim().isEmpty) {
return S.of(context).mobile_or_email_is_required;
}
return null;
},
onChanged: (string) {
if (string.isEmpty) {
if (mounted) {
setState(() {
canRegister = false;
});
}
} else if (string.isNotEmpty && codeController.text.trim().isNotEmpty) {
if (mounted) {
setState(() {
canRegister = true;
});
}
}
if (string.isNotEmpty && !enableGetCode) {
if (mounted) {
setState(() {
enableGetCode = true;
});
}
} else if (string.isEmpty && enableGetCode) {
if (mounted) {
setState(() {
enableGetCode = false;
});
}
}
},
),
),
Container(
child: TextFormField(
controller: codeController,
keyboardType: TextInputType.number,
decoration: new InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black12,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.blue,
),
),
labelText: S.of(context).verification_code,
suffixIcon: MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
child: Container(
margin: EdgeInsets.only(top: 6.0),
child: Container(
padding: EdgeInsets.only(left: 10.0, right: 10.0, top: 10.0, bottom: 10.0),
decoration: BoxDecoration(
shape: BoxShape.rectangle,
border: new Border.all(
color: enableGetCode ? Colors.black87 : Colors.black26,
width: 1.0,
),
borderRadius: BorderRadius.all(Radius.circular(10.0)),
),
child: Text(
getCodeText,
style: TextStyle(
color: enableGetCode ? Colors.black87 : Colors.black26,
fontSize: 12.0
),
),
),
),
onTap: enableGetCode ? getCodeTapped : null,
),
),
),
style: TextStyle(
fontSize: 18.0
),
validator: (String value) {
if (value.trim().isEmpty) {
return S.of(context).verification_code_is_required;
}
return null;
},
onChanged: (string) {
if (usernameController.text.trim().isNotEmpty && string.isNotEmpty) {
if (mounted) {
setState(() {
canRegister = true;
});
}
} else {
if (mounted) {
setState(() {
canRegister = false;
});
}
}
},
),
),
],
),
),
),
Container(
margin: EdgeInsets.only(top: 0.0, right: 16.0),
child: Align(
alignment: Alignment.centerRight,
child: RaisedButton(
color: Theme.of(context).primaryColor,
child: Text(
S.of(context).verify,
style: TextStyle(
color: Colors.white,
),
),
onPressed: canRegister ? register : null,
),
),
),
],
);
return SingleChildScrollView(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: sideSpace,
),
Container(
width: mainSpace,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: mainSpace / 2,
margin: EdgeInsets.only(top: 16.0, bottom: 16.0),
padding: EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
child: Text(
S.of(context).forgot_password,
style: TextStyle(
fontSize: 24.0,
color: Colors.black
),
),
),
Container(
padding: EdgeInsets.only(top: 8.0, bottom: 16.0),
child: Text(
S.of(context).forgot_password_description,
style: TextStyle(
color: Colors.black38,
),
),
),
],
),
),
Container(
width: mainSpace / 2,
margin: EdgeInsets.only(top: 16.0, bottom: 16.0),
padding: EdgeInsets.all(10.0),
decoration: BoxDecoration(
border: Border(
left: BorderSide(
width: 1.0,
color: Colors.black12,
),
),
),
child: view,
),
],
),
),
Container(
width: sideSpace,
),
],
),
);
}
@override
void initState() {
super.initState();
setState(() {
enableGetCode = false;
canRegister = false;
usernameEnable = true;
});
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
getCodeText = S.of(context).get_code;
}
void register() {
final FormState form = _formKey.currentState;
if (form.validate()) {
HttpUtil.httpPost('v1/users', (response) {
Routes.router.navigateTo(context, '/reset-password/${usernameController.text.trim()}/${codeController.text.trim()}', replace: true);
},
queryParameters: {
'action': 'forgot_password_verify_code'
},
isFormData: true,
body: {
'mobile': usernameController.text.trim(),
'code': codeController.text.trim(),
},
).catchError((error) {
Utils.showMessageDialog(context, error);
});
}
}
void getCodeTapped() {
if (usernameController.text.trim().isNotEmpty) {
HttpUtil.httpPost('v1/users', (response) {
Fluttertoast.showToast(
msg: S.of(context).verification_code_sent,
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
backgroundColor: Colors.black54,
textColor: Colors.white
);
if (mounted) {
setState(() {
usernameEnable = false;
});
}
countDownListener = CountDown(Duration(seconds: 90)).stream.listen(null);
startCountDown();
},
queryParameters: {'action': 'forgot_password'},
body: {
'mobile': usernameController.text.trim(),
},
isFormData: true,
).catchError((error) {
if (mounted) {
setState(() {
canRegister = false;
});
}
Utils.showMessageDialog(context, error);
});
} else {
Fluttertoast.showToast(
msg: S.of(context).enter_mobile_or_email,
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
backgroundColor: Colors.red,
textColor: Colors.white
);
}
}
void startCountDown() {
countDownListener.onData((Duration d) {
if (mounted) {
setState(() {
enableGetCode = false;
getCodeText = S.of(context).get_code_token(d.inSeconds);
});
}
});
countDownListener.onDone(() {
if (mounted) {
setState(() {
enableGetCode = true;
getCodeText = S.of(context).get_code_again;
});
}
countDownListener = CountDown(Duration(seconds: 90)).stream.listen(null);
});
}
}

View File

@@ -0,0 +1,156 @@
import 'package:flutter/material.dart';
import 'package:youtube_player_iframe/youtube_player_iframe.dart';
import '../../generated/l10n.dart';
import '../../utils/util_web.dart' if (dart.library.io) '../../utils/util_io.dart';
import '../../widgets/general/breadcrumbs.dart';
class DesktopiGoShowLearnMore extends StatefulWidget {
final Map<String, dynamic> data;
const DesktopiGoShowLearnMore(this.data, {Key key}) : super(key: key);
@override
State<StatefulWidget> createState() {
return DesktopiGoShowLearnMoreState();
}
}
class DesktopiGoShowLearnMoreState extends State<DesktopiGoShowLearnMore> {
double sideSpace = 0;
double mainSpace = 1200;
@override
Widget build(BuildContext context) {
if (MediaQuery.of(context).size.width <= 1200) {
mainSpace = MediaQuery.of(context).size.width;
sideSpace = 0;
} else {
mainSpace = 1200;
sideSpace = (MediaQuery.of(context).size.width - 1200) / 2;
}
Column col = Column(
children: [
Util.showImage(
'https:${widget.data['image-top']['image']}'
),
],
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
);
List<Widget> w = _getContent();
Wrap wrap = Wrap(
children: [],
);
for (int i = 0; i < w.length; i++) {
wrap.children.add(w[i]);
}
col.children.add(wrap);
Widget view = SingleChildScrollView(
child: Row(
children: [
Container(
width: sideSpace,
),
Container(
width: mainSpace,
child: col,
),
Container(
width: sideSpace,
),
],
),
);
return view;
}
@override
void initState() {
super.initState();
}
List<Widget> _getContent() {
List<Widget> widgets = [];
for (int i = 0; i < (widget.data['sections'] as List).length; i++) {
Column col = Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [],
);
if ((widget.data['sections'] as List)[i]['image'] != null) {
col.children.add(
Container(
padding: EdgeInsets.only(
top: 10.0, left: 8.0, right: 8.0, bottom: 8.0),
child: ClipRRect(
borderRadius: BorderRadius.circular(20.0),
child: Util.showImage(
'https:${(widget
.data['sections'] as List)[i]['image']['image']}',
fit: BoxFit.contain,
),
),
),
);
} else if (((widget.data['sections'] as List)[i]['youtube_videos'] as List).length > 0) {
YoutubePlayerController _controller = YoutubePlayerController(
initialVideoId: '${((widget.data['sections'] as List)[i]['youtube_videos'] as List)[0] as String}',
params: YoutubePlayerParams(
playlist: ((widget.data['sections'] as List)[i]['youtube_videos'] as List).length > 1 ?
((widget.data['sections'] as List)[i]['youtube_videos'] as List).map((e) {
return '$e';
}).toList() : [], // Defining custom playlist
startAt: Duration(seconds: 0),
showControls: true,
showFullscreenButton: false,
),
);
col.children.add(
Container(
padding: EdgeInsets.only(
top: 10.0, left: 8.0, right: 8.0, bottom: 8.0,
),
child: ClipRRect(
borderRadius: BorderRadius.circular(20.0),
child: YoutubePlayerIFrame(
controller: _controller,
aspectRatio: 1.7778,
),
),
),
);
}
col.children.add(Container(
margin: EdgeInsets.only(top: 8.0, left: 8.0, right: 8.0, bottom: 4.0),
child: Text(
'${(widget.data['sections'] as List)[i]['title']}',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16.0,
color: Colors.black87,
),
),
));
col.children.add(Container(
margin: EdgeInsets.only(top: 4.0, left: 8.0, right: 8.0, bottom: 20.0),
padding: EdgeInsets.only(bottom: 10.0),
child: Text(
'${(widget.data['sections'] as List)[i]['description']}',
style: TextStyle(
fontSize: 14.0,
color: Colors.black38,
),
),
));
widgets.add(Container(
width: mainSpace / 2,
child: col,
));
}
return widgets;
}
}

View File

@@ -46,7 +46,7 @@ class DesktopIndexMainContent1State extends State<DesktopIndexMainContent1> {
),
),
decoration: BoxDecoration(
color: Colors.lightGreen,
color: Color(0xFF4FB0C6),
borderRadius: BorderRadius.circular(10),
),
),

View File

@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_wisetronic/generated/l10n.dart';
import '../../generated/l10n.dart';
import '../../widgets/general/text_link.dart';
import '../../utils/util_web.dart' if (dart.library.io) '../../utils/util_io.dart';
class DesktopIndexMainContent2 extends StatefulWidget {
@@ -79,7 +80,7 @@ class DesktopIndexMainContent2State extends State<DesktopIndexMainContent2> {
Container(
padding: EdgeInsets.symmetric(horizontal: 10.0),
child: Util.showImage(
'http:${widget.content['minipos_image']['image']}',
'https:${widget.content['minipos_image']['image']}',
fit: BoxFit.fitWidth
),
),
@@ -126,7 +127,7 @@ class DesktopIndexMainContent2State extends State<DesktopIndexMainContent2> {
Container(
padding: EdgeInsets.symmetric(horizontal: 10.0),
child: Util.showImage(
'http:${widget.content['igoshow_image']['image']}',
'https:${widget.content['igoshow_image']['image']}',
fit: BoxFit.fitWidth
),
),
@@ -175,16 +176,12 @@ class DesktopIndexMainContent2State extends State<DesktopIndexMainContent2> {
));
}
col.children.add(
GestureDetector(
child: Container(
padding: EdgeInsets.symmetric(vertical: 10.0, horizontal: 0.0),
child: Text(
S.of(context).learn_more,
style: TextStyle(
color: Colors.blue,
fontWeight: FontWeight.bold,
),
),
Container(
alignment: Alignment.centerRight,
child: TextLink(
S.of(context).learn_more,
'/minipos-learn-more',
fontWeight: FontWeight.bold,
),
),
);
@@ -225,16 +222,12 @@ class DesktopIndexMainContent2State extends State<DesktopIndexMainContent2> {
));
}
col.children.add(
GestureDetector(
child: Container(
padding: EdgeInsets.symmetric(vertical: 10.0, horizontal: 0.0),
child: Text(
S.of(context).learn_more,
style: TextStyle(
color: Colors.blue,
fontWeight: FontWeight.bold,
),
),
Container(
alignment: Alignment.centerRight,
child: TextLink(
S.of(context).learn_more,
'/igoshow-learn-more',
fontWeight: FontWeight.bold,
),
),
);

View File

@@ -34,148 +34,325 @@ class DesktopIndexMainContent3State extends State<DesktopIndexMainContent3> {
return Container(
width: MediaQuery.of(context).size.width,
margin: EdgeInsets.only(top: 20.0),
padding: EdgeInsets.symmetric(vertical: 20.0, horizontal: 20.0),
padding: EdgeInsets.symmetric(vertical: 20.0, horizontal: 0.0),
decoration: BoxDecoration(
color: Color(0xff262626),
),
child: Column(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
child: Text(
S.of(context).information,
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.bold,
color: Colors.white70,
),
),
width: sideSpace,
),
Container(
child: Row(
children: [
TextLink(S.of(context).service_policy, '/service_policy', paddingVertical: 5.0, paddingHorizontal: 10.0,),
TextLink(S.of(context).return_policy, '/return_policy', paddingVertical: 5.0, paddingHorizontal: 10.0,),
TextLink(S.of(context).privacy_policy, '/privacy_policy', paddingVertical: 5.0, paddingHorizontal: 10.0,),
TextLink(S.of(context).license_agreement, '/license_agreement', paddingVertical: 5.0, paddingHorizontal: 10.0,)
],
),
width: mainSpace,
child: _getBody2(),
),
Container(
margin: EdgeInsets.only(top: 12.0),
child: Text(
S.of(context).support,
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.bold,
color: Colors.white70,
),
),
),
Container(
child: Row(
children: [
TextLink(S.of(context).wiki, '/wiki', paddingVertical: 5.0, paddingHorizontal: 10.0,),
TextLink(S.of(context).support_ticket, '/support_ticket', paddingVertical: 5.0, paddingHorizontal: 10.0,),
TextLink(S.of(context).contact_us, '/contact_us', paddingVertical: 5.0, paddingHorizontal: 10.0,),
TextLink(S.of(context).about_us, '/about_us', paddingVertical: 5.0, paddingHorizontal: 10.0,),
TextLink(S.of(context).renew_license, '/renew_license', paddingVertical: 5.0, paddingHorizontal: 10.0,)
],
),
),
Container(
margin: EdgeInsets.only(top: 12.0),
child: Text(
S.of(context).developer_of,
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.bold,
color: Colors.white70,
),
),
),
Container(
child: Row(
children: [
Container(
padding: EdgeInsets.all(10.0),
child: Icon(
IconData(
Constants.FONT_GOOGLE,
fontFamily: 'wisetronic',
fontPackage: null
),
color: Colors.white24,
size: 48.0,
),
),
Container(
padding: EdgeInsets.all(10.0),
child: Icon(
IconData(
Constants.FONT_ALEXA,
fontFamily: 'wisetronic',
fontPackage: null
),
color: Colors.white24,
size: 48.0,
),
),
Container(
padding: EdgeInsets.all(10.0),
child: Icon(
IconData(
Constants.FONT_APPLE,
fontFamily: 'wisetronic',
fontPackage: null
),
color: Colors.white24,
size: 48.0,
),
),
Container(
padding: EdgeInsets.all(10.0),
child: Icon(
IconData(
Constants.FONT_EBAY,
fontFamily: 'wisetronic',
fontPackage: null
),
color: Colors.white24,
size: 48.0,
),
),
Container(
padding: EdgeInsets.all(10.0),
child: Icon(
IconData(
Constants.FONT_QUICKBOOKS,
fontFamily: 'wisetronic',
fontPackage: null
),
color: Colors.white24,
size: 48.0,
),
),
Container(
padding: EdgeInsets.all(10.0),
child: Icon(
IconData(
Constants.FONT_SHOPIFY,
fontFamily: 'wisetronic',
fontPackage: null
),
color: Colors.white24,
size: 48.0,
),
),
],
),
width: sideSpace,
),
],
),
);
}
Column _getBody() {
Column col;
col = Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
child: Text(
S.of(context).information,
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.bold,
color: Colors.white70,
),
),
),
Container(
child: Row(
children: [
TextLink(S.of(context).service_policy, '/service-policy', paddingVertical: 5.0, paddingHorizontal: 10.0,),
TextLink(S.of(context).return_policy, '/return-policy', paddingVertical: 5.0, paddingHorizontal: 10.0,),
TextLink(S.of(context).privacy_policy, '/privacy-policy', paddingVertical: 5.0, paddingHorizontal: 10.0,),
TextLink(S.of(context).license_agreement, '/end-user-license-agreement', paddingVertical: 5.0, paddingHorizontal: 10.0,)
],
),
),
Container(
margin: EdgeInsets.only(top: 12.0),
child: Text(
S.of(context).support,
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.bold,
color: Colors.white70,
),
),
),
Container(
child: Row(
children: [
TextLink(S.of(context).wiki, '/wiki', paddingVertical: 5.0, paddingHorizontal: 10.0,),
TextLink(S.of(context).support_ticket, '/my-support/${Constants.BUSINESS_ID}', paddingVertical: 5.0, paddingHorizontal: 10.0,),
TextLink(S.of(context).contact_us, '/contact-us', paddingVertical: 5.0, paddingHorizontal: 10.0,),
TextLink(S.of(context).about_us, '/about-us', paddingVertical: 5.0, paddingHorizontal: 10.0,),
TextLink(S.of(context).renew_license, '/renew-license', paddingVertical: 5.0, paddingHorizontal: 10.0,)
],
),
),
Container(
margin: EdgeInsets.only(top: 12.0),
child: Text(
S.of(context).developer_of,
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.bold,
color: Colors.white70,
),
),
),
Container(
child: Row(
children: [
Container(
padding: EdgeInsets.all(10.0),
child: Icon(
IconData(
Constants.FONT_GOOGLE,
fontFamily: 'wisetronic',
fontPackage: null
),
color: Colors.white24,
size: 48.0,
),
),
Container(
padding: EdgeInsets.all(10.0),
child: Icon(
IconData(
Constants.FONT_ALEXA,
fontFamily: 'wisetronic',
fontPackage: null
),
color: Colors.white24,
size: 48.0,
),
),
Container(
padding: EdgeInsets.all(10.0),
child: Icon(
IconData(
Constants.FONT_APPLE,
fontFamily: 'wisetronic',
fontPackage: null
),
color: Colors.white24,
size: 48.0,
),
),
Container(
padding: EdgeInsets.all(10.0),
child: Icon(
IconData(
Constants.FONT_EBAY,
fontFamily: 'wisetronic',
fontPackage: null
),
color: Colors.white24,
size: 48.0,
),
),
Container(
padding: EdgeInsets.all(10.0),
child: Icon(
IconData(
Constants.FONT_QUICKBOOKS,
fontFamily: 'wisetronic',
fontPackage: null
),
color: Colors.white24,
size: 48.0,
),
),
Container(
padding: EdgeInsets.all(10.0),
child: Icon(
IconData(
Constants.FONT_SHOPIFY,
fontFamily: 'wisetronic',
fontPackage: null
),
color: Colors.white24,
size: 48.0,
),
),
],
),
),
],
);
return col;
}
Row _getBody2() {
Row row;
row = Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: mainSpace / 10 * 3,
padding: EdgeInsets.only(left: 16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
child: Text(
S.of(context).information,
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.bold,
color: Colors.white70,
),
),
),
TextLink(S.of(context).service_policy, '/service-policy', paddingVertical: 5.0, paddingHorizontal: 0.0,),
TextLink(S.of(context).return_policy, '/return-policy', paddingVertical: 5.0, paddingHorizontal: 0.0,),
TextLink(S.of(context).privacy_policy, '/privacy-policy', paddingVertical: 5.0, paddingHorizontal: 0.0,),
TextLink(S.of(context).license_agreement, '/end-user-license-agreement', paddingVertical: 5.0, paddingHorizontal: 0.0,)
],
),
),
Container(
width: mainSpace / 10 * 3,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: EdgeInsets.only(top: 0.0),
child: Text(
S.of(context).support,
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.bold,
color: Colors.white70,
),
),
),
TextLink(S.of(context).wiki, '/wiki', paddingVertical: 5.0, paddingHorizontal: 0.0,),
TextLink(S.of(context).support_ticket, '/my-support/${Constants.BUSINESS_ID}', paddingVertical: 5.0, paddingHorizontal: 0.0,),
TextLink(S.of(context).contact_us, '/contact-us', paddingVertical: 5.0, paddingHorizontal: 0.0,),
TextLink(S.of(context).about_us, '/about-us', paddingVertical: 5.0, paddingHorizontal: 0.0,),
TextLink(S.of(context).renew_license, '/renew-license', paddingVertical: 5.0, paddingHorizontal: 0.0,)
],
),
),
Container(
width: mainSpace / 10 * 4,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: EdgeInsets.only(top: 0.0),
child: Text(
S.of(context).developer_of,
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.bold,
color: Colors.white70,
),
),
),
Wrap(
children: [
Container(
padding: EdgeInsets.all(10.0),
child: Icon(
IconData(
Constants.FONT_GOOGLE,
fontFamily: 'wisetronic',
fontPackage: null
),
color: Colors.white24,
size: 48.0,
),
),
Container(
padding: EdgeInsets.all(10.0),
child: Icon(
IconData(
Constants.FONT_ALEXA,
fontFamily: 'wisetronic',
fontPackage: null
),
color: Colors.white24,
size: 48.0,
),
),
Container(
padding: EdgeInsets.all(10.0),
child: Icon(
IconData(
Constants.FONT_APPLE,
fontFamily: 'wisetronic',
fontPackage: null
),
color: Colors.white24,
size: 48.0,
),
),
Container(
padding: EdgeInsets.all(10.0),
child: Icon(
IconData(
Constants.FONT_EBAY,
fontFamily: 'wisetronic',
fontPackage: null
),
color: Colors.white24,
size: 48.0,
),
),
Container(
padding: EdgeInsets.all(10.0),
child: Icon(
IconData(
Constants.FONT_QUICKBOOKS,
fontFamily: 'wisetronic',
fontPackage: null
),
color: Colors.white24,
size: 48.0,
),
),
Container(
padding: EdgeInsets.all(10.0),
child: Icon(
IconData(
Constants.FONT_SHOPIFY,
fontFamily: 'wisetronic',
fontPackage: null
),
color: Colors.white24,
size: 48.0,
),
),
],
),
],
),
),
],
);
return row;
}
}

View File

@@ -0,0 +1,336 @@
import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import '../../constants.dart';
import '../../generated/l10n.dart';
import '../../models/user.dart';
import '../../routes.dart';
import '../../store/actions.dart';
import '../../store/store.dart';
import '../../utils/http_util.dart';
import '../../utils/utils.dart';
class DesktopLogin extends StatefulWidget {
final Key key;
const DesktopLogin({this.key}) : super(key: key);
@override
State<StatefulWidget> createState() {
return DesktopLoginState();
}
}
class DesktopLoginState extends State<DesktopLogin> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final usernameController = TextEditingController();
final passwordController = TextEditingController();
bool passwordVisible;
bool onSubmitting;
double sideSpace = 0;
double mainSpace = 1200;
@override
void initState() {
super.initState();
passwordVisible = true;
onSubmitting = false;
}
@override
Widget build(BuildContext context) {
store.dispatch(UpdateContext(context));
if (MediaQuery.of(context).size.width <= 1200) {
mainSpace = MediaQuery.of(context).size.width;
sideSpace = 0;
} else {
mainSpace = 1200;
sideSpace = (MediaQuery.of(context).size.width - 1200) / 2;
}
if (onSubmitting) {
return Container(
padding: EdgeInsets.all(50.0),
child: Center(
child: SpinKitThreeBounce(
color: Colors.lightBlueAccent,
size: 40.0,
),
),
);
}
Widget view = SingleChildScrollView(
child: Row(
children: [
Container(
width: sideSpace,
),
Container(
width: mainSpace,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: mainSpace / 2,
margin: EdgeInsets.only(top: 16.0, bottom: 16.0),
padding: EdgeInsets.all(10.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding:
EdgeInsets.only(left: 16.0, top: 16.0, right: 16.0),
child: Text(
S.of(context).please_login,
style: TextStyle(fontSize: 24.0, color: Colors.black),
),
),
Container(
padding: EdgeInsets.only(
top: 12.0, left: 16.0, right: 16.0, bottom: 12.0),
child: Text(
S.of(context).login_instruction,
style: TextStyle(
color: Colors.black54,
fontSize: 14.0,
),
),
),
],
),
),
Container(
width: mainSpace / 2,
margin: EdgeInsets.only(top: 16.0, bottom: 16.0),
padding: EdgeInsets.all(10.0),
decoration: BoxDecoration(
border: Border(
left: BorderSide(
width: 1.0,
color: Colors.black12,
),
),
),
child: Column(
children: <Widget>[
Form(
key: _formKey,
child: Column(
children: <Widget>[
Container(
padding: EdgeInsets.only(
top: 16.0,
left: 16.0,
right: 16.0,
bottom: 0.0),
child: TextFormField(
controller: usernameController,
keyboardType: TextInputType.emailAddress,
decoration: new InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black12,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.blue,
),
),
labelText:
S.of(context).mobile_email_username,
),
style: TextStyle(fontSize: 18.0),
autofocus: true,
validator: (String value) {
if (value.trim().isEmpty) {
return S.of(context).this_field_is_required;
}
return null;
},
),
),
Container(
padding: EdgeInsets.only(
top: 0.0,
left: 16.0,
right: 16.0,
bottom: 5.0),
child: TextFormField(
controller: passwordController,
decoration: new InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black12,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.blue,
),
),
labelText: S.of(context).password,
suffixIcon: IconButton(
icon: Icon(
passwordVisible
? Icons.visibility_off
: Icons.visibility,
color: Theme.of(context).primaryColorDark,
),
onPressed: () {
setState(() {
passwordVisible = !passwordVisible;
});
},
),
),
style: TextStyle(fontSize: 18.0),
obscureText: passwordVisible,
validator: (String value) {
if (value.trim().isEmpty) {
return S.of(context).password_is_required;
}
return null;
},
),
),
],
),
),
Container(
padding: EdgeInsets.symmetric(
vertical: 12.0, horizontal: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
FlatButton(
child: Text(
S.of(context).new_user_question,
style: TextStyle(
fontSize: 14.0,
),
),
onPressed: () {
newUser();
},
),
Expanded(
child: Text(''),
),
Container(
child: FlatButton(
child: Text(
S.of(context).forgot_password_question,
style: TextStyle(
fontSize: 14.0,
),
),
onPressed: () {
forgotPassword();
},
),
),
],
),
),
Align(
alignment: Alignment.centerRight,
child: Container(
padding: EdgeInsets.only(
top: 20.0, left: 16.0, right: 16.0, bottom: 20.0),
child: RaisedButton(
color: Theme.of(context).primaryColor,
child: Text(
S.of(context).login,
style: TextStyle(
color: Colors.white,
),
),
onPressed: () {
signIn();
},
),
),
),
],
),
),
],
),
),
Container(
width: sideSpace,
),
],
),
);
return view;
}
void newUser() {
Routes.router.navigateTo(context, '/new-user');
}
void forgotPassword() {
Routes.router.navigateTo(context, '/forgot-password');
}
void signIn() {
final FormState form = _formKey.currentState;
if (form.validate()) {
if (mounted) {
setState(() {
onSubmitting = true;
});
}
HttpUtil.httpPost(
'v1/oauth-wisetronic/access_token',
(response) {
User user = User.fromJson(response.data['user']);
store.dispatch(UpdateCurrentUser(user));
Utils.getBox().then((box) {
box.put(Constants.KEY_USER_ID, response.data['user_id']);
box.put(Constants.KEY_ACCESS_TOKEN, response.data['access_token']);
if (store.state.redirectRoute != null) {
Routes.router.navigateTo(context, store.state.redirectRoute,
replace: true);
store.dispatch(UpdateRedirectRoute(null));
} else {
Routes.router
.navigateTo(context, '/me', replace: true, clearStack: false);
}
}).catchError((error) {
Utils.showMessageDialog(context, error);
});
},
queryParameters: {
'client_id': '${Utils.getPlatformName()}',
'grant_type': 'password'
},
body: {
'username': usernameController.text.trim(),
'password': passwordController.text.trim(),
'fcm_token': store.state.fcmToken != null ? store.state.fcmToken : ''
},
isFormData: true,
businessId: Constants.BUSINESS_ID,
).catchError((error) {
if (mounted) {
setState(() {
onSubmitting = false;
});
}
Utils.showMessageDialog(context, error);
});
}
}
}

View File

@@ -0,0 +1,501 @@
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:flutter_wisetronic/widgets/general/breadcrumbs.dart';
import 'package:fluttertoast/fluttertoast.dart';
import '../../constants.dart';
import '../../events/eventbus.dart';
import '../../events/events.dart';
import '../../generated/l10n.dart';
import '../../models/user.dart';
import '../../routes.dart';
import '../../store/actions.dart';
import '../../store/store.dart';
import '../../utils/util_web.dart'
if (dart.library.io) '../../utils/util_io.dart';
import '../../utils/utils.dart';
MediaQueryData mediaQuery;
double statusBarHeight;
double screenHeight;
class DesktopMe extends StatefulWidget {
final Key key;
const DesktopMe({this.key}) : super(key: key);
@override
State<StatefulWidget> createState() {
return DesktopMeState();
}
}
class DesktopMeState extends State<DesktopMe> {
int userId;
String accessToken;
bool isLoading;
User _user;
double sideSpace = 0;
double mainSpace = 1200;
double division = 3;
@override
Widget build(BuildContext context) {
store.dispatch(UpdateContext(context));
if (MediaQuery.of(context).size.width <= 1200) {
mainSpace = MediaQuery.of(context).size.width;
sideSpace = 0;
} else {
mainSpace = 1200;
sideSpace = (MediaQuery.of(context).size.width - 1200) / 2;
}
if (isLoading) {
return new Scaffold(
body: Center(
child: SpinKitWave(
color: Colors.lightBlueAccent,
size: 40.0,
),
),
);
}
mediaQuery ??= MediaQuery.of(context);
screenHeight ??= mediaQuery.size.height;
statusBarHeight ??= mediaQuery.padding.top;
Widget userInfo = GestureDetector(
child: Container(
padding:
EdgeInsets.only(left: 16.0, right: 16.0, top: 12.0, bottom: 5.0),
color: Colors.transparent,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
margin: EdgeInsets.only(right: 5.0),
child: _user != null && _user.avatarUrl.isNotEmpty
? Util.showImage(
'https:${_user.avatarUrl}',
width: 60,
height: 60,
fit: BoxFit.fill,
errorWidget: (context, url, error) => Icon(
Icons.account_circle,
size: 60.0,
color: Colors.blue,
),
)
: Icon(
Icons.person_outline,
size: 60.0,
color: Colors.black38,
),
),
Container(
child: Text(
_user != null ? _user.nickname : S.of(context).please_login,
style: TextStyle(
color: Colors.black38,
fontSize: 18.0,
),
),
),
Visibility(
visible: _user != null,
child: Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
child: Icon(
Icons.phone_iphone,
color: Colors.blue,
size: 20.0,
),
),
Container(
child: Text(
_user != null
? Utils.safePhoneNumber(_user.mobile)
: '',
style: TextStyle(
color: Colors.black38,
fontSize: 14.0,
),
),
)
],
),
),
),
],
),
),
onTap: () {
if (_user != null) {
Routes.router.navigateTo(context, '/user-profile');
} else {
Routes.router.navigateTo(context, '/login');
}
},
);
Widget widget = SingleChildScrollView(
child: Column(
children: [
Row(
children: [
Container(
width: sideSpace,
),
Container(
width: mainSpace,
child: Column(
children: [
Row(
children: [
Expanded(
child: MouseRegion(
cursor: SystemMouseCursors.click,
child: userInfo,
),
),
Expanded(
child: Container(
padding: EdgeInsets.only(
left: 16.0,
right: 16.0,
top: 16.0,
bottom: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
child: Text(
_user != null
? '${_user.wallet.toStringAsFixed(2)}'
: '0.00',
style: TextStyle(
fontSize: 24.0,
color: Colors.redAccent,
),
),
),
Container(
child: Text(
S.of(context).wallet,
style: TextStyle(
fontSize: 12.0,
color: Colors.grey,
),
),
)
],
),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Colors.black12,
width: 6.0,
),
right: BorderSide(
color: Colors.black12,
width: 1.0,
),
),
),
),
),
Expanded(
child: GestureDetector(
child: Container(
padding: EdgeInsets.only(
left: 16.0,
right: 16.0,
top: 16.0,
bottom: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
child: Text(
_user != null ? '${_user.coupon}' : '0',
style: TextStyle(
fontSize: 24.0,
color: Colors.orangeAccent,
),
),
),
Container(
child: Text(
S.of(context).red_coupon,
style: TextStyle(
fontSize: 12.0,
color: Colors.grey,
),
),
)
],
),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Colors.black12,
width: 6.0,
),
right: BorderSide(
color: Colors.black12,
width: 1.0,
),
),
),
),
onTap: () {
if (_user != null) {
Routes.router.navigateTo(
context, '/coupons/${_user.id}');
} else {
_pleaseLoginToast();
}
},
),
),
Expanded(
child: Container(
padding: EdgeInsets.only(
left: 16.0,
right: 16.0,
top: 16.0,
bottom: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
child: Text(
_user != null ? '${_user.points}' : '0',
style: TextStyle(
fontSize: 24.0,
color: Colors.lightGreen,
),
),
),
Container(
child: Text(
S.of(context).point,
style: TextStyle(
fontSize: 12.0,
color: Colors.grey,
),
),
)
],
),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Colors.black12,
width: 6.0,
),
),
),
),
),
],
),
Wrap(
children: [
getLinkItem(
mainSpace / division,
200.0,
Icon(
Icons.lock,
size: 60.0,
color: Colors.lightBlueAccent,
),
Text(S.of(context).change_password),
() {
if (_user != null) {
Routes.router.navigateTo(
context,
'/change-password',
);
} else {
_pleaseLoginToast();
}
},
),
getLinkItem(
mainSpace / division,
200.0,
Icon(
Icons.sticky_note_2_outlined,
size: 60.0,
color: Colors.lightBlueAccent,
),
Text(S.of(context).my_orders),
() {
if (_user != null) {
Routes.router.navigateTo(context, '/orders');
} else {
_pleaseLoginToast();
}
},
),
getLinkItem(
mainSpace / division,
200.0,
Icon(
Icons.location_on,
size: 60.0,
color: Colors.lightBlueAccent,
),
Text(S.of(context).my_addresses),
() {
if (_user != null) {
Routes.router
.navigateTo(context, '/my-addresses/-1');
} else {
_pleaseLoginToast();
}
},
),
getLinkItem(
mainSpace / division,
200.0,
Icon(
Icons.credit_card,
size: 60.0,
color: Colors.lightBlueAccent,
),
Text(S.of(context).my_cards),
() {
if (_user != null) {
Routes.router.navigateTo(context, '/my-cards');
} else {
_pleaseLoginToast();
}
},
),
getLinkItem(
mainSpace / division,
200.0,
Icon(
Icons.support_agent,
size: 60.0,
color: Colors.lightBlueAccent,
),
Text(S.of(context).my_support),
() {
if (_user != null) {
Routes.router.navigateTo(context,
'/my-support/${Constants.BUSINESS_ID}');
} else {
_pleaseLoginToast();
}
},
),
],
),
],
),
),
Container(
width: sideSpace,
),
],
),
],
),
);
return widget;
}
@override
void initState() {
super.initState();
setState(() {
isLoading = true;
_user = null;
});
eventBus.on<OnCurrentUserUpdated>().listen((event) {
if (mounted) {
setState(() {
isLoading = false;
_user = store.state.user;
});
}
});
eventBus.on<OnGetCurrentUserFailed>().listen((event) {
if (mounted) {
setState(() {
isLoading = false;
_user = null;
});
}
});
Utils.getCurrentUser();
}
void _toLogin() {
Routes.router
.navigateTo(context, '/login', replace: true, clearStack: true);
}
_pleaseLoginToast() {
Fluttertoast.showToast(
msg: S.of(context).please_login,
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
backgroundColor: Colors.black54,
textColor: Colors.white);
}
Widget getLinkItem(
double width, double height, Icon icon, Text label, Function tap) {
Widget widget = MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
child: Container(
width: width,
height: height,
padding: EdgeInsets.symmetric(vertical: 8.0, horizontal: 8.0),
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
child: icon,
),
Container(
child: label,
),
],
),
decoration: BoxDecoration(
shape: BoxShape.rectangle,
border: new Border.all(
color: Colors.black12,
width: 1.0,
),
borderRadius: BorderRadius.all(Radius.circular(10.0)),
),
),
),
onTap: tap,
),
);
return widget;
}
}

View File

@@ -0,0 +1,124 @@
import 'package:flutter/material.dart';
import '../../generated/l10n.dart';
import '../../utils/util_web.dart' if (dart.library.io) '../../utils/util_io.dart';
import '../../widgets/general/breadcrumbs.dart';
class DesktopMiniPosLearnMore extends StatefulWidget {
final Map<String, dynamic> data;
const DesktopMiniPosLearnMore(this.data, {Key key}) : super(key: key);
@override
State<StatefulWidget> createState() {
return DesktopMiniPosLearnMoreState();
}
}
class DesktopMiniPosLearnMoreState extends State<DesktopMiniPosLearnMore> {
double sideSpace = 0;
double mainSpace = 1200;
@override
Widget build(BuildContext context) {
if (MediaQuery.of(context).size.width <= 1200) {
mainSpace = MediaQuery.of(context).size.width;
sideSpace = 0;
} else {
mainSpace = 1200;
sideSpace = (MediaQuery.of(context).size.width - 1200) / 2;
}
Column col = Column(
children: [
Util.showImage(
'https:${widget.data['image-top']['image']}'
),
],
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
);
List<Widget> w = _getContent();
Wrap wrap = Wrap(
children: [],
);
for (int i = 0; i < w.length; i++) {
wrap.children.add(w[i]);
}
col.children.add(wrap);
Widget view = SingleChildScrollView(
child: Row(
children: [
Container(
width: sideSpace,
),
Container(
width: mainSpace,
child: col,
),
Container(
width: sideSpace,
),
],
),
);
return view;
}
@override
void initState() {
super.initState();
}
List<Widget> _getContent() {
List<Widget> widgets = [];
for (int i = 0; i < (widget.data['sections'] as List).length; i++) {
Column col = Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [],
);
col.children.add(
Container(
padding: EdgeInsets.only(top: 10.0, left: 8.0, right: 8.0, bottom: 8.0),
child: ClipRRect(
borderRadius: BorderRadius.circular(20.0),
child: Util.showImage(
'https:${(widget.data['sections'] as List)[i]['image']['image']}',
fit: BoxFit.contain,
),
),
),
);
col.children.add(Container(
margin: EdgeInsets.only(top: 8.0, left: 8.0, right: 8.0, bottom: 4.0),
child: Text(
'${(widget.data['sections'] as List)[i]['title']}',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16.0,
color: Colors.black87,
),
),
));
col.children.add(Container(
margin: EdgeInsets.only(top: 4.0, left: 8.0, right: 8.0, bottom: 20.0),
padding: EdgeInsets.only(bottom: 10.0),
child: Text(
'${(widget.data['sections'] as List)[i]['description']}',
style: TextStyle(
fontSize: 14.0,
color: Colors.black38,
),
),
));
widgets.add(Container(
width: mainSpace / 2,
child: col,
));
}
return widgets;
}
}

View File

@@ -0,0 +1,269 @@
import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import '../../constants.dart';
import '../../events/eventbus.dart';
import '../../events/events.dart';
import '../../generated/l10n.dart';
import '../../models/address.dart';
import '../../pages/edit_address.dart';
import '../../routes.dart';
import '../../store/actions.dart';
import '../../store/store.dart';
import '../../utils/http_util.dart';
import '../../utils/utils.dart';
import '../../widgets/general/bottom_nav.dart';
import '../../widgets/general/breadcrumbs.dart';
import '../../widgets/general/navigationbar.dart';
class DesktopMyAddresses extends StatefulWidget {
final int businessId;
const DesktopMyAddresses({Key key, this.businessId}) : super(key: key);
@override
State<StatefulWidget> createState() {
return DesktopMyAddressesState();
}
}
class DesktopMyAddressesState extends State<DesktopMyAddresses> {
List<Address> addresses;
double sideSpace = 0;
double mainSpace = 1200;
double division = 2;
@override
Widget build(BuildContext context) {
store.dispatch(UpdateContext(context));
if (MediaQuery.of(context).size.width <= 1200) {
mainSpace = MediaQuery.of(context).size.width;
sideSpace = 0;
} else {
mainSpace = 1200;
sideSpace = (MediaQuery.of(context).size.width - 1200) / 2;
}
if (addresses == null) {
return new Scaffold(
body: Center(
child: SpinKitWave(
color: Colors.lightBlueAccent,
size: 40.0,
),
),
);
}
BuildContext mainContext = context;
return Scaffold(
appBar: NavigationBar(
title: S.of(context).my_addresses,
back: true,
breadCrumbs: [
BreadCrumb(S.of(context).my_addresses, null),
BreadCrumb(S.of(context).add_new_address,
'/search-place/${widget.businessId}',
),
],
breadCrumbHeight: Constants.BREADCRUMB_HEIGHT,
),
body: SingleChildScrollView(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: sideSpace,
),
Container(
width: mainSpace,
padding: EdgeInsets.only(
top: 12.0,
bottom: 16.0,
left: 8.0,
right: 8.0,
),
child: addresses == null ? Text('') :
(addresses.length > 0 ?
Wrap(
children: addresses.map((a) => _getAddress(a)).toList(),
) :
Center(
child: Text(S.of(context).no_address_yet),
)),
),
Container(
width: sideSpace,
),
],
),
),
bottomNavigationBar: BottomNav(),
);
}
Widget _getAddress(Address address) {
return Container(
width: (mainSpace - 16.0) / division,
padding: EdgeInsets.symmetric(vertical: 8.0, horizontal: 8.0),
child: Container(
padding: EdgeInsets.only(top: 20.0, bottom: 20.0, left: 16.0, right: 16.0,),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Colors.black12,
width: 0.6,
),
top: BorderSide(
color: Colors.black12,
width: 0.6,
),
left: BorderSide(
color: Colors.black12,
width: 0.6,
),
right: BorderSide(
color: Colors.black12,
width: 0.6,
),
),
borderRadius: BorderRadius.all(Radius.circular(10.0)),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
child: Text(
address.addressLine1,
style: TextStyle(
fontSize: 19.0,
),
overflow: TextOverflow.ellipsis,
),
),
Container(
child: Text(
address.addressLine2,
style: TextStyle(
fontSize: 13.0,
color: Colors.grey,
),
),
),
Container(
margin: EdgeInsets.only(top: 10.0),
child: Text(
'${address.contactName} ${Utils.safePhoneNumber(address.phone)}',
style: TextStyle(
fontSize: 15.0,
color: Colors.black87,
),
),
)
],
),
),
Container(
child: widget.businessId == 0 ?
IconButton(
icon: Icon(
Icons.edit,
color: Colors.grey,
),
onPressed: () {
Navigator.push(context, MaterialPageRoute(
builder: (BuildContext context) => new EditAddress(address, businessId: 0,),
));
},
) :
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
margin: EdgeInsets.only(right: 5.0),
child: IconButton(
icon: Icon(
Icons.edit,
color: Colors.grey,
),
onPressed: () {
Navigator.push(context, MaterialPageRoute(
builder: (BuildContext context) => new EditAddress(address, businessId: widget.businessId,),
));
},
),
),
Container(
child: widget.businessId > 0 ? IconButton(
icon: Icon(
Icons.check,
color: Colors.grey,
),
onPressed: () {
HttpUtil.httpPut('v1/select-address/${address.id}', (response) {
Routes.router.navigateTo(context, '/checkout/${widget.businessId}', replace: true);
}).catchError((error) {
Utils.showMessageDialog(context, error, onOk: () {
Navigator.of(context).pop();
Navigator.of(context).pop();
});
});
},
) : SizedBox.shrink(),
)
],
),
),
],
),
),
);
}
@override
void initState() {
super.initState();
loadAddresses();
eventBus.on<OnAddressesUpdated>().listen((event) {
if (mounted) {
setState(() {
addresses = null;
});
}
loadAddresses();
});
}
void loadAddresses() {
HttpUtil.httpGet(
'v1/addresses',
).then((value) {
if (mounted) {
setState(() {
addresses = (value as List).map((e) => Address.fromJson(e)).toList();
});
}
}).catchError((error) {
Utils.showMessageDialog(context, error, onOk: () {
Navigator.of(context).pop();
Navigator.of(context).pop();
});
});
}
}

View File

@@ -0,0 +1,208 @@
import 'package:flutter/material.dart';
import '../../constants.dart';
import '../../events/eventbus.dart';
import '../../events/events.dart';
import '../../generated/l10n.dart';
import '../../models/stripe_payment_method.dart';
import '../../models/user.dart';
import '../../store/actions.dart';
import '../../store/store.dart';
import '../../utils/http_util.dart';
import '../../utils/utils.dart';
import '../../widgets/general/bottom_nav.dart';
import '../../widgets/general/breadcrumbs.dart';
import '../../widgets/general/navigationbar.dart';
class DesktopMyCards extends StatefulWidget {
final Key key;
const DesktopMyCards({this.key});
@override
State<StatefulWidget> createState() {
return MyCardsState();
}
}
class MyCardsState extends State<DesktopMyCards> {
User _user;
bool isSubmitting;
double sideSpace = 0;
double mainSpace = 1200;
@override
Widget build(BuildContext context) {
store.dispatch(UpdateContext(context));
if (MediaQuery.of(context).size.width <= 1200) {
mainSpace = MediaQuery.of(context).size.width;
sideSpace = 0;
} else {
mainSpace = 1200;
sideSpace = (MediaQuery.of(context).size.width - 1200) / 2;
}
return Scaffold(
appBar: NavigationBar(
title: S.of(context).blog,
back: true,
breadCrumbs: [
BreadCrumb(S.of(context).my_cards, null),
],
breadCrumbHeight: Constants.BREADCRUMB_HEIGHT,
),
body: Row(
children: [
Container(
width: sideSpace,
),
Expanded(
child: ListView.builder(
itemCount: _user.stripePaymentMethods.length,
itemBuilder: (BuildContext context, int position) {
StripePaymentMethod paymentMethod = _user.stripePaymentMethods[position];
return cardWidget(paymentMethod);
}
),
),
Container(
width: sideSpace,
),
],
),
bottomNavigationBar: BottomNav(),
);
}
Widget cardWidget(StripePaymentMethod paymentMethod) {
return Container(
padding: EdgeInsets.only(
top: 16.0, left: 16.0, right: 16.0, bottom: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
paymentMethod.cardBrand == 'visa' ?
Image.asset(
'assets/images/visa.png',
width: 50.0,
height: 50.0,
fit: BoxFit.fill,
) : (paymentMethod.cardBrand == 'mastercard' ?
Image.asset(
'assets/images/master.png',
width: 50.0,
height: 50.0,
fit: BoxFit.fill,
) : Icon(
Icons.credit_card, size: 50.0, color: Colors.black38,)),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
margin: EdgeInsets.only(left: 20.0, right: 10.0),
child: Text(
'**** ${paymentMethod.cardLast4}',
style: TextStyle(
fontSize: 20.0,
),
),
),
Container(
margin: EdgeInsets.only(left: 20.0, right: 10.0),
child: Text(
S.of(context).expire_token(paymentMethod.cardExpMonth, paymentMethod.cardExpYear),
style: TextStyle(
fontSize: 14.0,
color: Colors.black26,
),
),
),
],
),
),
IconButton(
icon: Icon(Icons.clear, color: Colors.black26,),
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(S.of(context).warning),
content: Text(
S.of(context).are_you_sure_to_remove_the_card,
),
actions: <Widget>[
FlatButton(
child: Text(S.of(context).cancel),
color: Theme.of(context).primaryColor,
onPressed: () {
Navigator.of(context).pop();
},
),
FlatButton(
child: Text(S.of(context).yes_i_am_sure),
onPressed: () {
Navigator.of(context).pop();
_removeCard(paymentMethod);
},
)
],
);
}
);
},
),
],
),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 0.5,
color: Colors.black12,
),
),
),
);
}
_removeCard(StripePaymentMethod paymentMethod) {
HttpUtil.httpDelete('v1/stripe-card/${paymentMethod.id}', (response) {
_user = User.fromJson(response.data);
store.dispatch(UpdateCurrentUser(_user));
eventBus.fire(OnCurrentUserUpdated());
if (mounted) {
Navigator.of(context).pop();
setState(() {
isSubmitting = false;
});
}
}).catchError((error) {
if (isSubmitting) {
Navigator.of(context).pop();
isSubmitting = false;
}
Utils.showMessageDialog(context, error, onOk: () {
Navigator.of(context).pop();
Navigator.of(context).pop();
});
});
isSubmitting = true;
Utils.showSubmitDialog(context);
}
@override
void initState() {
super.initState();
setState(() {
isSubmitting = false;
_user = store.state.user;
});
}
}

View File

@@ -0,0 +1,343 @@
import 'package:flutter/material.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import '../../constants.dart';
import '../../events/eventbus.dart';
import '../../events/events.dart';
import '../../generated/l10n.dart';
import '../../models/ticket.dart';
import '../../routes.dart';
import '../../store/actions.dart';
import '../../store/store.dart';
import '../../utils/http_util.dart';
import '../../utils/utils.dart';
import '../../widgets/general/bottom_nav.dart';
import '../../widgets/general/breadcrumbs.dart';
import '../../widgets/general/double_back_to_close_app_wrapper.dart';
import '../../widgets/general/navigationbar.dart';
class DesktopMySupport extends StatefulWidget {
final int businessId;
const DesktopMySupport({Key key, this.businessId}) : super(key: key);
@override
State<StatefulWidget> createState() {
return DesktopMySupportState();
}
}
class DesktopMySupportState extends State<DesktopMySupport> {
List<Ticket> tickets;
double division = 3;
int _page = 1;
int _pageCount = 1;
bool _isLoading = false;
bool _loadingFinish = false;
RefreshController _refreshController = RefreshController(initialRefresh: true);
void _onRefresh() {
_page = 1;
if (tickets != null) {
tickets.clear();
} else {
tickets = [];
}
_refreshController.resetNoData();
loadTicketes(true);
}
void _onLoadMore() {
// if failed,use loadFailed(),if no data return,use LoadNodata()
if (_pageCount > _page) {
_page += 1;
loadTicketes(false);
} else {
_refreshController.loadNoData();
}
}
double sideSpace = 0;
double mainSpace = 1200;
@override
Widget build(BuildContext context) {
store.dispatch(UpdateContext(context));
if (MediaQuery.of(context).size.width <= 1200) {
mainSpace = MediaQuery.of(context).size.width;
sideSpace = 0;
} else {
mainSpace = 1200;
sideSpace = (MediaQuery.of(context).size.width - 1200) / 2;
}
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
if (store.state.user == null) {
Utils.requireLogin(context, returnUrl: '/my-support/${widget.businessId}');
return;
}
});
BuildContext mainContext = context;
return Scaffold(
appBar: NavigationBar(
title: S.of(context).my_support,
back: true,
breadCrumbs: [
BreadCrumb(S.of(context).my_support, null),
BreadCrumb(S.of(context).add_new_ticket,
'/new-ticket/${widget.businessId}',
icon: Icons.add,
),
],
breadCrumbHeight: Constants.BREADCRUMB_HEIGHT,
),
body: DoubleBackToCloseAppWrapper(
child: SmartRefresher(
enablePullDown: true,
enablePullUp: true,
header: WaterDropHeader(),
footer: CustomFooter(
builder: (BuildContext context, LoadStatus mode){
Widget footer;
if(mode == LoadStatus.idle) {
footer = Text(S.of(context).pull_up_to_load_more);
} else if (mode == LoadStatus.loading) {
footer = CircularProgressIndicator();
} else if (mode == LoadStatus.failed) {
footer = Text(S.of(context).load_failed_retry);
} else if (mode == LoadStatus.canLoading) {
footer = Text(S.of(context).release_to_load_more);
} else if (mode == LoadStatus.noMore) {
footer = Text(S.of(context).no_more_record);
} else {
footer = Text('...');
}
return Container(
height: 55.0,
child: Center(child: footer,),
);
},
),
controller: _refreshController,
onRefresh: _onRefresh,
onLoading: _onLoadMore,
child: _buildBody(),
),
),
bottomNavigationBar: BottomNav(),
);
}
Widget _buildBody() {
if (tickets == null) {
return SizedBox.shrink();
}
Row row = Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: sideSpace,
),
Container(
width: mainSpace,
padding: EdgeInsets.only(
top: 12.0,
bottom: 16.0,
left: 8.0,
right: 8.0,
),
child: tickets == null ? Text('') :
(tickets.length > 0 ?
Wrap(
children: tickets.map((a) => _getTicket(a)).toList(),
) :
Center(
child: Text(S.of(context).no_ticket_yet),
)),
),
Container(
width: sideSpace,
),
],
);
return SingleChildScrollView(
child: row,
);
}
@override
void dispose() {
_refreshController?.dispose();
super.dispose();
}
Widget _getTicket(Ticket ticket) {
return Container(
width: (mainSpace - 16.0) / division,
padding: EdgeInsets.symmetric(vertical: 8.0, horizontal: 8.0),
child: Container(
padding: EdgeInsets.only(top: 20.0, bottom: 20.0, left: 16.0, right: 16.0,),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Colors.black12,
width: 0.6,
),
top: BorderSide(
color: Colors.black12,
width: 0.6,
),
left: BorderSide(
color: Colors.black12,
width: 0.6,
),
right: BorderSide(
color: Colors.black12,
width: 0.6,
),
),
borderRadius: BorderRadius.all(Radius.circular(10.0)),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
child: Text(
ticket.issue.msg,
style: TextStyle(
fontSize: 19.0,
),
overflow: TextOverflow.ellipsis,
),
),
Container(
child: Text(
Utils.utcDatetimeStringToLocalDatetimeString(context, ticket.createdAt, withTime: true),
style: TextStyle(
fontSize: 13.0,
color: Colors.grey,
),
),
),
Container(
margin: EdgeInsets.only(top: 10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ticket.isClosed ?
Container(
padding: EdgeInsets.only(right: 10.0),
child: Icon(Icons.lock, color: Colors.green, size: 16.0,),
) :
SizedBox.shrink(),
Expanded(
child: Text(
S.of(context).followups_token(ticket.followUps.length),
style: TextStyle(
fontSize: 15.0,
color: Colors.black87,
),
overflow: TextOverflow.ellipsis,
),
),
],
),
)
],
),
),
Container(
child: IconButton(
icon: Icon(
Icons.remove_red_eye,
color: Colors.grey,
),
onPressed: () {
Routes.router.navigateTo(context, '/view-ticket/${ticket.id}');
},
),
),
],
),
),
);
}
@override
void initState() {
super.initState();
eventBus.on<OnTicketsUpdated>().listen((event) {
if (mounted) {
setState(() {
tickets = null;
});
}
_refreshController.requestRefresh();
});
}
void loadTicketes(bool isRefresh) {
HttpUtil.httpGet(
'v1/mysupport',
businessId: widget.businessId,
queryParameters: {
'page': _page.toString(),
'size': Constants.TICKET_PER_PAGE_DESKTOP.toString(),
}
).then((value) {
if (mounted) {
if (isRefresh) {
_refreshController.refreshCompleted();
} else {
_refreshController.loadComplete();
}
if (int.parse(value['_meta']['currentPage'].toString()) >= int.parse(value['_meta']['pageCount'].toString())) {
_loadingFinish = true;
}
if (_loadingFinish) {
_refreshController.loadNoData();
}
_page = int.parse(value['_meta']['currentPage'].toString());
_pageCount = int.parse(value['_meta']['pageCount'].toString());
setState(() {
if (tickets == null) {
tickets = [];
}
tickets.addAll((value['tickets'] as List).map((e) => Ticket.fromJson(e)).toList());
});
}
}).catchError((error) {
if (mounted) {
if (isRefresh) {
_refreshController.refreshFailed();
} else {
_refreshController.loadFailed();
}
_isLoading = false;
Utils.showMessageDialog(context, error, onOk: () {
Navigator.of(context).pop();
Navigator.of(context).pop();
});
}
});
}
}

View File

@@ -1,12 +1,29 @@
import 'package:flutter/material.dart';
import 'package:flutter_wisetronic/generated/l10n.dart';
import 'package:flutter_wisetronic/widgets/general/navigationbar_logo.dart';
import 'package:flutter_wisetronic/widgets/general/text_link.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_wisetronic/widgets/desktop/desktop_appbar_menu.dart';
import '../../widgets/general/breadcrumbs.dart';
import '../../constants.dart';
import '../../dialog/logout_dialog.dart';
import '../../events/eventbus.dart';
import '../../events/events.dart';
import '../../generated/l10n.dart';
import '../../models/user.dart';
import '../../routes.dart';
import '../../store/store.dart';
import '../../utils/utils.dart';
import '../../widgets/general/navigationbar_logo.dart';
import '../../widgets/general/text_link.dart';
class DesktopNavigationBar extends StatefulWidget {
const DesktopNavigationBar({Key key}) : super(key: key);
final bool hasBack;
final List<BreadCrumb> breadCrumbs;
final Widget shoppingCart;
const DesktopNavigationBar({Key key, this.hasBack, this.breadCrumbs,
this.shoppingCart}) : super(key: key);
@override
State<StatefulWidget> createState() {
@@ -16,92 +33,201 @@ class DesktopNavigationBar extends StatefulWidget {
}
class DesktopNavigationBarState extends State<DesktopNavigationBar> {
User _user;
@override
Widget build(BuildContext context) {
String currentRoute = ModalRoute.of(context).settings.name;
return Stack(
children: [
Container(
color: Colors.blue,
height: 80.0,
),
Container(
height: 80.0,
padding: EdgeInsets.only(left: 10.0, right: 10.0, top: 5.0, bottom: 5.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
NavigationBarLogo(),
Expanded(
child: Container(
alignment: Alignment.centerRight,
padding: EdgeInsets.only(left: 8.0, right: 16.0),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(width: 20.0,),
TextLink(
S.of(context).home,
'/',
color: Colors.white,
selected: currentRoute == '/',
clearStack: true,
),
SizedBox(width: 15.0,),
TextLink(
S.of(context).download,
'/download',
color: Colors.white,
selected: currentRoute == '/download',
),
SizedBox(width: 15.0,),
TextLink(
S.of(context).tutorials,
'/tutorials',
color: Colors.white,
selected: currentRoute == '/tutorials',
),
SizedBox(width: 15.0,),
TextLink(
S.of(context).support,
'/support',
color: Colors.white,
selected: currentRoute == '/support',
),
SizedBox(width: 15.0,),
TextLink(
S.of(context).shop,
'/shop',
color: Colors.white,
selected: currentRoute == '/shop',
),
SizedBox(width: 15.0,),
TextLink(
S.of(context).blog,
'/blog',
color: Colors.white,
selected: currentRoute == '/blog',
),
SizedBox(width: 15.0,),
TextLink(
S.of(context).login,
'/login',
color: Colors.white,
selected: currentRoute == '/login',
),
],
),
),
),
// String currentRoute = ModalRoute.of(context).settings.name;
// Stack stack = Stack(
// children: [
// Container(
// color: Colors.blue,
// height: 80.0,
// ),
// Container(
// height: 80.0,
// padding: EdgeInsets.only(left: 10.0, right: 10.0, top: 5.0, bottom: 5.0),
// child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// NavigationBarLogo(),
// Expanded(
// child: Container(
// alignment: Alignment.centerRight,
// padding: EdgeInsets.only(left: 8.0, right: 16.0),
// child: SingleChildScrollView(
// scrollDirection: Axis.horizontal,
// child: Row(
// mainAxisSize: MainAxisSize.min,
// children: [
// SizedBox(width: 20.0,),
// TextLink(
// S.of(context).home,
// '/',
// color: Colors.white,
// selected: currentRoute == '/',
// clearStack: true,
// ),
// SizedBox(width: 15.0,),
// TextLink(
// S.of(context).download,
// '/download',
// color: Colors.white,
// selected: currentRoute == '/download',
// ),
// SizedBox(width: 15.0,),
// TextLink(
// S.of(context).tutorials,
// '/tutorials',
// color: Colors.white,
// selected: currentRoute == '/tutorials',
// ),
// SizedBox(width: 15.0,),
// TextLink(
// S.of(context).support,
// '/my-support/${Constants.BUSINESS_ID}',
// color: Colors.white,
// selected: currentRoute == '/my-support/${Constants.BUSINESS_ID}',
// ),
// SizedBox(width: 15.0,),
// TextLink(
// S.of(context).shop,
// '/shop',
// color: Colors.white,
// selected: currentRoute == '/shop',
// ),
// SizedBox(width: 15.0,),
// TextLink(
// S.of(context).blog,
// '/blog/${Constants.BUSINESS_ID}',
// color: Colors.white,
// selected: currentRoute == '/blog/${Constants.BUSINESS_ID}',
// ),
// SizedBox(width: 15.0,),
// _user != null ?
// (currentRoute == '/me') ?
// MouseRegion(
// cursor: SystemMouseCursors.click,
// child: GestureDetector(
// child: Row(
// mainAxisAlignment: MainAxisAlignment.start,
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Container(
// padding: EdgeInsets.only(right: 4.0),
// child: Text(
// S.of(context).logout,
// style: TextStyle(
// color: Colors.white,
// ),
// ),
// ),
// Icon(
// Icons.logout,
// color: Colors.white,
// size: 16.0,
// )
// ],
// ),
// onTap: () {
// showDialog(
// context: context,
// builder: (BuildContext context) {
// return logoutDialog(context);
// }
// );
// },
// ),
// ) : IconButton(
// icon: Icon(
// Icons.account_circle,
// color: Colors.white,
// ),
// onPressed: () {
// Routes.router.navigateTo(context, '/me');
// },
// ) :
// TextLink(
// S.of(context).login,
// '/login',
// color: Colors.white,
// selected: currentRoute == '/login',
// ),
// ],
// ),
// ),
// ),
// ),
// ],
// ),
// ),
// ],
// );
Widget sc = SizedBox.shrink();
if (widget.shoppingCart != null) {
sc = widget.shoppingCart;
}
Widget breadCrumbBar = SizedBox.shrink();
if (widget.breadCrumbs != null && widget.breadCrumbs.length > 0) {
breadCrumbBar = Container(
width: MediaQuery.of(context).size.width,
height: 38.0,
color: Color(0xFF4FB0C6),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: BreadCrumbs(
widget.hasBack ?? false,
breadCrumbs: widget.breadCrumbs,
),
],
),
),
sc,
Container(
width: 1.0,
),
],
),
);
}
// stack.children.add(
// Positioned(
// top: 90.0,
// child: breadCrumbBar,
// ),
// );
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
DesktopAppBarMenu(),
breadCrumbBar,
],
);
}
@override
void initState() {
super.initState();
eventBus.on<OnCurrentUserUpdated>().listen((event) {
if (mounted) {
setState(() {
_user = store.state.user;
});
}
});
eventBus.on<OnGetCurrentUserFailed>().listen((event) {
if (mounted) {
setState(() {
_user = null;
});
}
});
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
Utils.getCurrentUser();
});
}
}

View File

@@ -0,0 +1,424 @@
import 'package:email_validator/email_validator.dart';
import 'package:flutter/material.dart';
import 'package:flutter_wisetronic/widgets/general/breadcrumbs.dart';
import 'package:gender_selection/gender_selection.dart';
import '../../constants.dart';
import '../../generated/l10n.dart';
import '../../models/located_address.dart';
import '../../routes.dart';
import '../../store/actions.dart';
import '../../store/store.dart';
import '../../utils/http_util.dart';
import '../../widgets/general/navigationbar.dart';
class DesktopNewAddress extends StatefulWidget {
final Key key;
final LocatedAddress locatedAddress;
final int businessId;
const DesktopNewAddress({this.key, this.locatedAddress, this.businessId}) : super(key: key);
@override
State<StatefulWidget> createState() {
return DesktopNewAddressState();
}
}
class DesktopNewAddressState extends State<DesktopNewAddress> {
final GlobalKey<FormState> _formKey = new GlobalKey();
final contactNameController = TextEditingController();
final phoneController = TextEditingController();
final streetLine1Controller = TextEditingController();
final streetLine2Controller = TextEditingController();
final cityController = TextEditingController();
final postalCodeController = TextEditingController();
final emailController = TextEditingController();
final faxController = TextEditingController();
String country = 'CA';
Gender _selectedGender;
String _selectedProvince;
double sideSpace = 0;
double mainSpace = 1200;
List<String> provinces = <String>[
'Ontario',
'Quebec',
'British Columbia',
'Alberta',
'Manitoba',
'Saskatchewan',
'Nova Scotia',
'New Brunswich',
'Newfoundland and Labrador',
'Prince Edward Island',
'Northwest Territories',
'Nunavut',
'Yukon',
];
@override
Widget build(BuildContext context) {
store.dispatch(UpdateContext(context));
if (MediaQuery.of(context).size.width <= 1200) {
mainSpace = MediaQuery.of(context).size.width;
sideSpace = 0;
} else {
mainSpace = 1200;
sideSpace = (MediaQuery.of(context).size.width - 1200) / 2;
}
Widget form = SingleChildScrollView(
padding: EdgeInsets.only(left: 0.0, right: 0.0, top: 0.0, bottom: 0.0),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Container(
padding: EdgeInsets.only(left: 16.0, right: 16.0, top: 16.0, bottom: 0.0),
child: TextFormField(
controller: contactNameController,
decoration: InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black12,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.blue,
)
),
labelText: S.of(context).contact_name,
),
validator: (String value) {
if (value.trim().isEmpty) {
return S.of(context).contact_name_is_required;
}
return null;
},
),
),
GenderSelection(
maleText: S.of(context).mr,
femaleText: S.of(context).ms,
selectedGenderIconBackgroundColor: Colors.indigo,
checkIconAlignment: Alignment.bottomRight,
selectedGenderCheckIcon: Icons.check,
onChanged: (Gender gender) {
_selectedGender = gender;
},
equallyAligned: true,
animationDuration: Duration(milliseconds: 400),
isCircular: true,
isSelectedGenderIconCircular: true,
opacityOfGradient: 0.6,
padding: const EdgeInsets.all(3.0),
size: 50,
selectedGender: _selectedGender,
),
Container(
padding: EdgeInsets.only(left: 16.0, right: 16.0, top: 16.0, bottom: 0.0),
child: TextFormField(
keyboardType: TextInputType.phone,
controller: phoneController,
decoration: InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black12,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.blue,
)
),
labelText: S.of(context).mobile_phone_number,
),
validator: (String value) {
if (value.trim().isEmpty) {
return S.of(context).mobile_phone_number_is_required;
}
return null;
},
),
),
Container(
padding: EdgeInsets.only(left: 16.0, right: 16.0, top: 16.0, bottom: 0.0),
child: TextFormField(
controller: streetLine1Controller,
decoration: InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black12,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.blue,
)
),
labelText: S.of(context).street_line_1,
),
validator: (String value) {
if (value.trim().isEmpty) {
return S.of(context).street_line_1_is_required;
}
return null;
},
),
),
Container(
padding: EdgeInsets.only(left: 16.0, right: 16.0, top: 16.0, bottom: 0.0),
child: TextFormField(
controller: streetLine2Controller,
decoration: InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black12,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.blue,
)
),
labelText: S.of(context).street_line_2,
),
),
),
Container(
padding: EdgeInsets.only(left: 16.0, right: 16.0, top: 16.0, bottom: 0.0),
child: TextFormField(
controller: cityController,
decoration: InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black12,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.blue,
)
),
labelText: S.of(context).city,
),
validator: (String value) {
if (value.trim().isEmpty) {
return S.of(context).city_is_required;
}
return null;
},
),
),
Container(
padding: EdgeInsets.only(left: 16.0, right: 16.0, top: 16.0, bottom: 0.0),
child: DropdownButton<String>(
value: _selectedProvince,
items: provinces.map((value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
onChanged: (newValue) {
if (mounted) {
setState(() {
_selectedProvince = newValue;
});
}
},
hint: Text(S.of(context).province),
isExpanded: true,
),
),
Container(
padding: EdgeInsets.only(left: 16.0, right: 16.0, top: 16.0, bottom: 0.0),
child: TextFormField(
controller: postalCodeController,
decoration: InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black12,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.blue,
)
),
labelText: S.of(context).postal_code,
),
validator: (String value) {
if (value.trim().isEmpty) {
return S.of(context).postal_code_is_required;
}
return null;
},
),
),
Container(
padding: EdgeInsets.only(left: 16.0, right: 16.0, top: 32.0, bottom: 0.0),
child: Text(
S.of(context).optional_information,
style: TextStyle(
fontSize: 12.0,
color: Colors.grey,
),
),
),
Container(
padding: EdgeInsets.only(left: 16.0, right: 16.0, top: 0.0, bottom: 0.0),
child: TextFormField(
controller: emailController,
decoration: InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black12,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.blue,
)
),
labelText: S.of(context).email,
),
validator: (String value) {
if (value.isNotEmpty && !EmailValidator.validate(value)) {
return S.of(context).email_is_not_valid;
}
return null;
},
),
),
Container(
padding: EdgeInsets.only(left: 16.0, right: 16.0, top: 16.0, bottom: 16.0),
child: TextFormField(
controller: faxController,
keyboardType: TextInputType.phone,
decoration: InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black12,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.blue,
)
),
labelText: S.of(context).fax,
),
),
),
],
),
);
return Scaffold(
appBar: NavigationBar(
title: S.of(context).new_address,
back: true,
breadCrumbs: [
BreadCrumb(S.of(context).new_address, null),
],
breadCrumbHeight: Constants.BREADCRUMB_HEIGHT,
),
body: Form(
key: _formKey,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: sideSpace,
),
Container(
width: mainSpace,
child: form,
),
Container(
width: sideSpace,
),
],
),
),
bottomNavigationBar: Container(
padding: EdgeInsets.all(8.0),
color: Theme.of(context).buttonColor,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
SizedBox(),
FlatButton(
child: Text(
S.of(context).save
),
onPressed: () {
_saveNewAddress();
},
),
],
),
),
);
}
@override
void initState() {
super.initState();
setState(() {
if (widget.locatedAddress != null) {
if (provinces.contains(widget.locatedAddress.province)) {
_selectedProvince = widget.locatedAddress.province;
}
cityController.text = widget.locatedAddress.city;
postalCodeController.text = widget.locatedAddress.postalCode;
streetLine1Controller.text = (widget.locatedAddress.streetNumber != null
&& widget.locatedAddress.streetNumber.isNotEmpty
? widget.locatedAddress.streetNumber + ' ' : '')
+ widget.locatedAddress.streetName;
} else {
_selectedProvince = 'Ontario';
}
streetLine2Controller.text = '';
_selectedGender = Gender.Male;
});
}
void _saveNewAddress() {
final FormState form = _formKey.currentState;
if (form.validate()) {
HttpUtil.httpPost('v1/addresses', (response) {
if (widget.businessId > 0) {
Routes.router.navigateTo(context, '/checkout/${widget.businessId}', replace: true);
} else {
Routes.router.navigateTo(context, '/my-addresses/${widget.businessId}', replace: true);
}
},
body: {
'name': contactNameController.text.trim(),
'address_line1': streetLine1Controller.text.trim(),
'address_line2': streetLine2Controller.text.trim(),
'city': cityController.text.trim(),
'state': _selectedProvince,
'zip': postalCodeController.text.trim(),
'phone': phoneController.text.trim(),
'gender': _selectedGender == Gender.Male ? 1 : 0,
'email': emailController.text,
'fax': faxController.text,
'country': country,
}
);
}
}
}

View File

@@ -0,0 +1,406 @@
import 'package:badges/badges.dart';
import 'package:flutter/material.dart';
import 'package:flutter_wisetronic/widgets/general/breadcrumbs.dart';
import 'package:flutter_wisetronic/widgets/general/navigationbar.dart';
import 'package:percent_indicator/linear_percent_indicator.dart';
import 'package:smooth_star_rating/smooth_star_rating.dart';
import '../../constants.dart';
import '../../events/eventbus.dart';
import '../../events/events.dart';
import '../../generated/l10n.dart';
import '../../models/comment.dart';
import '../../models/product_image.dart';
import '../../routes.dart';
import '../../store/actions.dart';
import '../../store/store.dart';
import '../../utils/http_util.dart';
import '../../utils/util_web.dart' if (dart.library.io) '../../utils/util_io.dart';
import '../../utils/utils.dart';
class DesktopNewComment extends StatefulWidget {
final Key key;
final int orderId;
const DesktopNewComment(this.orderId, {this.key});
@override
State<StatefulWidget> createState() {
return DesktopNewCommentState();
}
}
class DesktopNewCommentState extends State<DesktopNewComment> {
Comment comment;
bool _showProgress;
double _progress;
double rating;
bool isSubmitting = false;
final TextEditingController commentController = new TextEditingController();
double sideSpace = 0;
double mainSpace = 1200;
@override
Widget build(BuildContext context) {
store.dispatch(UpdateContext(context));
if (MediaQuery.of(context).size.width <= 1200) {
mainSpace = MediaQuery.of(context).size.width;
sideSpace = 0;
} else {
mainSpace = 1200;
sideSpace = (MediaQuery.of(context).size.width - 1200) / 2;
}
return Scaffold(
appBar: NavigationBar(
title: S.of(context).new_comment,
back: true,
breadCrumbs: [
BreadCrumb(S.of(context).pay_now, null),
],
breadCrumbHeight: Constants.BREADCRUMB_HEIGHT,
),
body: _buildBody(context),
);
}
@override
void initState() {
super.initState();
_showProgress = false;
_progress = 0.0;
setState(() {
rating = 5.0;
});
eventBus.on<OnUploadCommentImageProgressEvent>().listen((event) {
if (mounted) {
setState(() {
_showProgress = event.showProgress;
_progress = event.progress;
});
}
});
eventBus.on<OnCommentUpdatedEvent>().listen((event) {
if (mounted) {
setState(() {
comment = event.comment;
});
}
});
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
}
Widget _buildBody(BuildContext context) {
ListView listView = ListView.builder(
itemCount: 4,
itemBuilder: (BuildContext context, int position) {
switch(position) {
case 0:
return Container(
padding: EdgeInsets.only(left: 20.0, right: 20.0, top: 16.0, bottom: 16.0),
child: SmoothStarRating(
allowHalfRating: false,
onRated: (v) {
setState(() {
rating = v;
});
},
starCount: 5,
rating: rating,
size: 40.0,
filledIconData: Icons.star,
color: Colors.green,
borderColor: Colors.green,
spacing: 0.0,
),
);
break;
case 1:
return Container(
padding: EdgeInsets.only(top: 0.0, bottom: 10.0, left: 16.0, right: 16.0),
child: TextField(
controller: commentController,
keyboardType: TextInputType.multiline,
maxLines: 10,
maxLength: 500,
decoration: new InputDecoration(
border: new OutlineInputBorder(
borderRadius: const BorderRadius.all(
const Radius.circular(12.0),
),
),
filled: true,
hintStyle: new TextStyle(color: Colors.grey[800]),
hintText: S.of(context).input_your_comment,
fillColor: Colors.white70,
),
),
);
break;
case 2:
return Container(
padding: EdgeInsets.only(left: 16.0, right: 16.0, top: 16.0, bottom: 16.0),
child: commentImages(context),
);
break;
case 3:
return Container(
padding: EdgeInsets.only(left: 16.0, right: 16.0, top: 16.0, bottom: 16.0),
child: Align(
alignment: Alignment.centerRight,
child: SizedBox(
width: 150.0,
height: 36.0,
child: RaisedButton(
child: Text(
S.of(context).submit,
style: TextStyle(
color: Colors.white,
),
),
color: Theme.of(context).primaryColor,
onPressed: () {
_submit();
},
),
),
),
);
}
return null;
}
);
return Row(
children: [
Container(width: sideSpace,),
Expanded(child: listView,),
Container(width: sideSpace,),
],
);
}
Widget commentImages(BuildContext mainContext) {
Row row = Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[],
);
if (comment != null && comment.images.length > 0) {
for (ProductImage image in comment.images) {
row.children.add(
Container(
padding: EdgeInsets.only(left: 10.0),
child: Badge(
position: BadgePosition.topEnd(top: -10.0, end: -6.0),
badgeContent: Container(
child: Text(
'-',
style: TextStyle(
fontSize: 14.0,
color: Colors.white,
),
),
),
child: GestureDetector(
child: Container(
child: Util.showImage('https:${image.image}',
width: 60.0,
height: 60.0,
fit: BoxFit.fill,
errorWidget: (mainContext, url, error) => Icon(
Icons.image,
size: 60.0,
color: Colors.grey,
),
),
),
onTap: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(S.of(context).warning),
content: Text(S.of(context).are_you_sure_to_remove_the_picture),
actions: <Widget>[
FlatButton(
child: Text(S.of(context).cancel),
onPressed: () {
Navigator.of(context).pop();
},
color: Theme.of(context).primaryColor,
),
FlatButton(
child: Text(S.of(context).yes_i_am_sure),
onPressed: () {
Navigator.of(context).pop();
_deleteImage(image.id);
},
),
],
);
}
);
},
),
),
),
);
}
}
row.children.add(
GestureDetector(
child: Container(
margin: EdgeInsets.only(left: 10,),
child: Icon(
Icons.add,
size: 60.0,
color: comment == null || comment.images.length < 3 ? Colors.lightBlue : Colors.black12,
),
decoration: BoxDecoration(
color: Colors.white70,
border: Border(
top: BorderSide(
width: 0.5,
color: Colors.black12,
),
bottom: BorderSide(
width: 0.5,
color: Colors.black12,
),
left: BorderSide(
width: 0.5,
color: Colors.black12,
),
right: BorderSide(
width: 0.5,
color: Colors.black12,
),
),
),
),
onTap: () {
if (comment == null || comment.images.length < 3) {
showDialog(
context: mainContext,
barrierDismissible: true,
builder: (BuildContext context) {
return Util().getPicture(mainContext, store.state.user,
commentId: comment != null ? comment.id : 0,
orderId: widget.orderId);
}
);
}
},
),
);
return Stack(
children: <Widget>[
Positioned(
top: 0.0,
left: 0.0,
right: 0.0,
bottom: -100.0,
child: Visibility(
visible: _showProgress,
child: LinearPercentIndicator(
lineHeight: 10.0,
percent: _progress,
backgroundColor: Colors.transparent,
progressColor: Colors.blue,
linearStrokeCap: LinearStrokeCap.butt,
),
),
),
Positioned(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
padding: EdgeInsets.only(bottom: 16.0),
child: Text(
S.of(context).add_pictures,
style: TextStyle(
fontSize: 17.0,
color: Colors.black38,
),
),
),
row,
],
),
),
],
);
}
void _deleteImage(int imageId) {
HttpUtil.httpDelete('v1/comment-image/${imageId}', (response) {
if (mounted) {
setState(() {
comment = Comment.fromJson(response.data);
});
}
},
queryParameters: {
'comment_id': comment != null ? comment.id : 0,
},
).catchError((error) {
Utils.showMessageDialog(context, error);
});
}
void _submit() {
if (commentController.text.isEmpty) {
Utils.showMessageDialog(context, Exception(S.of(context).comment_empty));
} else {
HttpUtil.httpPost('v1/add-comment', (response) {
if (isSubmitting) {
isSubmitting = false;
Navigator.of(context).pop();
}
Utils.showMessageDialog(context,
Exception(S.of(context).thank_you_for_your_comment),
title: S.of(context).success,
onOk: () {
Routes.router.navigateTo(context, '/orders', replace: true, clearStack: true);
}
);
},
isFormData: true,
body: {
'order_id': widget.orderId,
'comment_id': comment != null ? comment.id : 0,
'content': commentController.text,
'rating': rating.round(),
},
).catchError((error) {
if (isSubmitting) {
isSubmitting = false;
Navigator.of(context).pop();
}
Utils.showMessageDialog(context, error);
});
isSubmitting = true;
Utils.showSubmitDialog(context);
}
}
}

View File

@@ -0,0 +1,459 @@
import 'package:badges/badges.dart';
import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:flutter_wisetronic/events/eventbus.dart';
import 'package:flutter_wisetronic/events/events.dart';
import 'package:percent_indicator/circular_percent_indicator.dart';
import '../../constants.dart';
import '../../generated/l10n.dart';
import '../../models/user.dart';
import '../../routes.dart';
import '../../store/actions.dart';
import '../../store/store.dart';
import '../../utils/http_util.dart';
import '../../utils/utils.dart';
import '../../widgets/general/breadcrumbs.dart';
import '../../utils/util_web.dart' if (dart.library.io) '../../utils/util_io.dart';
class DesktopNewTicket extends StatefulWidget {
final Key key;
final int businessId;
const DesktopNewTicket({this.key, int businessId}) :
businessId = businessId ?? Constants.BUSINESS_ID;
@override
State<StatefulWidget> createState() {
return DesktopNewTicketState();
}
}
class DesktopNewTicketState extends State<DesktopNewTicket> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final issueMsgController = TextEditingController();
bool onSubmitting = false;
double submitProgress = 0.0;
final List<Map<String, dynamic>> galleryImagesFlag = [
{'show': false, 'progress': 0.0, 'path': ''},
{'show': false, 'progress': 0.0, 'path': ''},
{'show': false, 'progress': 0.0, 'path': ''},
];
double sideSpace = 0;
double mainSpace = 1200;
@override
void initState() {
super.initState();
onSubmitting = false;
}
@override
Widget build(BuildContext context) {
store.dispatch(UpdateContext(context));
BuildContext mainContext = context;
if (MediaQuery.of(context).size.width <= 1200) {
mainSpace = MediaQuery.of(context).size.width;
sideSpace = 0;
} else {
mainSpace = 1200;
sideSpace = (MediaQuery.of(context).size.width - 1200) / 2;
}
Row row = Row(
children: [
Container(
width: sideSpace,
),
Container(
width: mainSpace,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: mainSpace / 2,
margin: EdgeInsets.only(top: 16.0, bottom: 16.0),
padding: EdgeInsets.all(10.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: EdgeInsets.only(left: 16.0, top: 16.0, right: 16.0),
child: Text(
S.of(context).add_new_ticket,
style: TextStyle(
fontSize: 24.0,
color: Colors.black
),
),
),
Container(
padding: EdgeInsets.only(top: 12.0, left: 16.0, right: 16.0, bottom: 12.0),
child: Text(
S.of(context).add_new_ticket_desc,
style: TextStyle(
color: Colors.black54,
fontSize: 14.0,
),
),
),
],
),
),
Container(
width: mainSpace / 2,
margin: EdgeInsets.only(top: 16.0, bottom: 16.0),
padding: EdgeInsets.all(10.0),
decoration: BoxDecoration(
border: Border(
left: BorderSide(
width: 1.0,
color: Colors.black12,
),
),
),
child: Column(
children: <Widget>[
Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
padding: EdgeInsets.only(top: 16.0, left: 16.0, right: 16.0, bottom: 16.0),
child: TextFormField(
controller: issueMsgController,
keyboardType: TextInputType.multiline,
textCapitalization: TextCapitalization.sentences,
decoration: new InputDecoration(
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
width: 0.5,
color: Colors.black12,
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
width: 1.0,
color: Colors.blue,
),
),
labelText: S.of(context).your_question_issue,
),
style: TextStyle(
fontSize: 18.0
),
autofocus: true,
validator: (String value) {
if (value.trim().isEmpty) {
return S.of(context).this_field_is_required;
}
return null;
},
maxLines: 5,
maxLength: 1000,
),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 1.0,
color: Colors.black12,
),
),
),
),
Container(
padding: EdgeInsets.only(top: 16.0, left: 16.0, right: 16.0, bottom: 4.0),
child: Text(
S.of(context).attach_pictures,
style: TextStyle(
fontSize: 17.0,
color: Colors.black87,
fontWeight: FontWeight.bold,
),
),
),
Container(
padding: EdgeInsets.only(top: 4.0, left: 16.0, right: 16.0, bottom: 8.0),
child: Text(
S.of(context).attach_pictures_desc,
style: TextStyle(
fontSize: 14.0,
color: Colors.black38,
),
),
),
Container(
height: 120.0,
padding: EdgeInsets.only(top: 8.0, left: 16.0, right: 16.0, bottom: 16.0),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: galleryImages(mainContext),
),
),
],
),
),
Align(
alignment: Alignment.centerRight,
child: Container(
padding: EdgeInsets.only(top: 20.0, left: 16.0, right: 16.0, bottom: 20.0),
child: RaisedButton(
color: Theme.of(context).primaryColor,
child: Text(
S.of(context).login,
style: TextStyle(
color: Colors.white,
),
),
onPressed: () {
_submit();
},
),
),
),
],
),
),
],
),
),
Container(
width: sideSpace,
),
],
);
Widget view = SingleChildScrollView(
child: Stack(
children: [
Visibility(
visible: onSubmitting,
child: Container(
height: MediaQuery.of(context).size.height - 100.0,
color: Colors.grey.withOpacity(0.6),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
CircularProgressIndicator(
strokeWidth: 10,
backgroundColor: Colors.green,
valueColor: new AlwaysStoppedAnimation<Color>(Colors.red),
value: submitProgress,
),
Container(
margin: EdgeInsets.only(top: 16.0),
child: Text(
S.of(context).submitting_please_wait,
style: TextStyle(
color: Colors.white,
),
),
),
],
),
),
),
),
Visibility(
visible: !onSubmitting,
child: row,
),
],
),
);
return view;
}
Widget galleryImages(BuildContext mainContext) {
Row row = Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[],
);
for (int i = 0; i < 3; i++) {
Map<String, dynamic> image = galleryImagesFlag[i];
Widget imageWidget;
imageWidget = GestureDetector(
child: Stack(
alignment: Alignment.center,
children: [
Container(
margin: EdgeInsets.only(right: 20.0),
child: Util.showImage(
'${image['path']}',
width: 80.0,
height: 80.0,
fit: BoxFit.fill
),
decoration: BoxDecoration(
border: Border(
top: BorderSide(
width: 0.5,
color: Colors.black12,
),
bottom: BorderSide(
width: 0.5,
color: Colors.black12,
),
left: BorderSide(
width: 0.5,
color: Colors.black12,
),
right: BorderSide(
width: 0.5,
color: Colors.black12,
),
),
),
),
Visibility(
visible: galleryImagesFlag[i]['show'],
child: Container(
padding: EdgeInsets.only(right: 20.0),
child: CircularPercentIndicator(
radius: 60.0,
lineWidth: 10.0,
percent: galleryImagesFlag[i]['progress'],
center: new Text(
'${(galleryImagesFlag[i]['progress'] * 100.0).toStringAsFixed(1)}%',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 15.0,
color: Colors.white,
),
),
circularStrokeCap: CircularStrokeCap.round,
progressColor: Colors.orange,
),
),
),
],
),
onTap: () {
showDialog(
context: mainContext,
builder: (BuildContext context) {
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus) {
currentFocus.unfocus();
}
return Util().getPicture2(mainContext, i, (int imageId, String path){
setState(() {
galleryImagesFlag[imageId]['path'] = path;
});
});
return null;
}
);
},
);
row.children.add(Badge(
position: BadgePosition.topEnd(top: -10.0, end: 10.0),
badgeContent: Container(
child: GestureDetector(
child: Icon(
Icons.clear,
color: Colors.white,
size: 14.0,
),
onTap: () {
_deleteImage(mainContext, i);
},
),
),
showBadge: image['path'].isNotEmpty ? true : false,
child: imageWidget,
));
}
return row;
}
void _deleteImage(BuildContext mainContext, int imageId) {
showDialog(context: mainContext,
builder: (BuildContext context) {
return AlertDialog(
title: Text(S.of(context).warning),
content: Text(S.of(context).are_you_sure_to_remove_the_picture),
actions: [
FlatButton(
child: Text(S.of(context).cancel),
onPressed: () {
Navigator.of(context).pop();
},
),
RaisedButton(
child: Text(S.of(context).yes_i_am_sure),
color: Theme.of(context).primaryColor,
onPressed: () {
Navigator.of(context).pop();
setState(() {
galleryImagesFlag[imageId]['path'] = '';
});
},
),
],
);
},
);
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
eventBus.on<OnSubmitProgressEvent>().listen((event) {
if (mounted) {
setState(() {
onSubmitting = event.showProgress;
submitProgress = event.progress;
});
}
});
}
void _submit() {
final FormState form = _formKey.currentState;
if (form.validate()) {
setState(() {
onSubmitting = true;
});
Util().createTicket(context, issueMsgController.text.trim(),
galleryImagesFlag, (response){
showDialog(context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: Text(S.of(context).success),
content: Text(S.of(context).ticket_created_success),
actions: <Widget>[
FlatButton(
child: Text(S.of(context).ok),
onPressed: () {
Routes.router.navigateTo(context, '/my-support/${widget.businessId}', replace: true);
},
),
],
);
});
}, (error) {
Utils.showMessageDialog(context, error, onOk: () {
Routes.router.pop(context);
Routes.router.pop(context);
});
});
}
}
}

View File

@@ -0,0 +1,379 @@
import 'package:countdown/countdown.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_wisetronic/widgets/general/breadcrumbs.dart';
import 'package:fluttertoast/fluttertoast.dart';
import '../../constants.dart';
import '../../generated/l10n.dart';
import '../../routes.dart';
import '../../utils/http_util.dart';
import '../../utils/utils.dart';
class DesktopNewUser extends StatefulWidget {
const DesktopNewUser({Key key}) : super(key: key);
@override
State<StatefulWidget> createState() {
return DesktopNewUserState();
}
}
class DesktopNewUserState extends State<DesktopNewUser> {
final GlobalKey<FormState> _formKey = GlobalKey();
final usernameController = TextEditingController();
bool usernameEnable = true;
final codeController = TextEditingController();
bool enableGetCode;
String getCodeText;
bool canRegister;
var countDownListener;
double sideSpace = 0;
double mainSpace = 1200;
@override
Widget build(BuildContext context) {
if (MediaQuery.of(context).size.width <= 1200) {
mainSpace = MediaQuery.of(context).size.width;
sideSpace = 0;
} else {
mainSpace = 1200;
sideSpace = (MediaQuery.of(context).size.width - 1200) / 2;
}
Widget view = Column(
children: <Widget>[
Form(
key: _formKey,
child: Container(
padding: EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
child: TextFormField(
controller: usernameController,
enabled: usernameEnable,
keyboardType: TextInputType.emailAddress,
decoration: new InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black12,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.blue,
),
),
labelText: S.of(context).mobile_or_email,
),
style: TextStyle(
fontSize: 18.0
),
autofocus: true,
validator: (String value) {
if (value.trim().isEmpty) {
return S.of(context).mobile_or_email_is_required;
}
return null;
},
onChanged: (string) {
if (string.isEmpty) {
if (mounted) {
setState(() {
canRegister = false;
});
}
} else if (string.isNotEmpty && codeController.text.trim().isNotEmpty) {
if (mounted) {
setState(() {
canRegister = true;
});
}
}
if (string.isNotEmpty && !enableGetCode) {
if (mounted) {
setState(() {
enableGetCode = true;
});
}
} else if (string.isEmpty && enableGetCode) {
if (mounted) {
setState(() {
enableGetCode = false;
});
}
}
},
),
),
Container(
child: TextFormField(
controller: codeController,
keyboardType: TextInputType.number,
decoration: new InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black12,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.blue,
),
),
labelText: S.of(context).verification_code,
suffixIcon: MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
child: Container(
margin: EdgeInsets.only(top: 6.0),
child: Container(
padding: EdgeInsets.only(left: 10.0, right: 10.0, top: 10.0, bottom: 10.0),
decoration: BoxDecoration(
shape: BoxShape.rectangle,
border: new Border.all(
color: enableGetCode ? Colors.black87 : Colors.black26,
width: 1.0,
),
borderRadius: BorderRadius.all(Radius.circular(10.0)),
),
child: Text(
getCodeText,
style: TextStyle(
color: enableGetCode ? Colors.black87 : Colors.black26,
fontSize: 14.0
),
),
),
),
onTap: enableGetCode ? getCodeTapped : null,
),
),
),
style: TextStyle(
fontSize: 18.0
),
validator: (String value) {
if (value.trim().isEmpty) {
return S.of(context).verification_code_is_required;
}
return null;
},
onChanged: (string) {
if (usernameController.text.trim().isNotEmpty && string.isNotEmpty) {
if (mounted) {
setState(() {
canRegister = true;
});
}
} else {
if (mounted) {
setState(() {
canRegister = false;
});
}
}
},
),
),
],
),
),
),
Container(
margin: EdgeInsets.only(top: 0.0, right: 16.0),
child: Align(
alignment: Alignment.centerRight,
child: RaisedButton(
color: Theme.of(context).primaryColor,
child: Text(
S.of(context).register,
style: TextStyle(
color: Colors.white,
),
),
onPressed: canRegister ? register : null,
),
),
),
],
);
return SingleChildScrollView(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: sideSpace,
),
Container(
width: mainSpace,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: mainSpace / 2,
margin: EdgeInsets.only(top: 16.0, bottom: 16.0),
padding: EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
child: Text(
S.of(context).user_registration,
style: TextStyle(
fontSize: 24.0,
color: Colors.black
),
),
),
Container(
padding: EdgeInsets.only(top: 8.0, bottom: 16.0),
child: Text(
S.of(context).user_registration_desc,
style: TextStyle(
color: Colors.black38,
),
),
),
],
),
),
Container(
width: mainSpace / 2,
margin: EdgeInsets.only(top: 16.0, bottom: 16.0),
padding: EdgeInsets.all(10.0),
decoration: BoxDecoration(
border: Border(
left: BorderSide(
width: 1.0,
color: Colors.black12,
),
),
),
child: view,
),
],
),
),
Container(
width: sideSpace,
),
],
),
);
}
@override
void initState() {
super.initState();
setState(() {
enableGetCode = false;
canRegister = false;
usernameEnable = true;
});
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
getCodeText = S.of(context).get_code;
}
void register() {
final FormState form = _formKey.currentState;
if (form.validate()) {
HttpUtil.httpPost('v1/users', (response) {
Routes.router.navigateTo(context, '/set-password/${usernameController.text.trim()}/${codeController.text.trim()}', replace: true);
},
queryParameters: {
'action': 'verify_code'
},
isFormData: true,
body: {
'mobile': usernameController.text.trim(),
'code': codeController.text.trim(),
'store_id': Constants.BUSINESS_ID
},
).catchError((error) {
Utils.showMessageDialog(context, error);
});
}
}
void getCodeTapped() {
if (usernameController.text.isNotEmpty) {
HttpUtil.httpPost('v1/users', (response) {
Fluttertoast.showToast(
msg: S.of(context).verification_code_sent,
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
backgroundColor: Colors.black54,
textColor: Colors.white
);
countDownListener = CountDown(Duration(seconds: 90)).stream.listen(null);
startCountDown();
if (mounted) {
setState(() {
usernameEnable = false;
});
}
},
queryParameters: {'action': 'send_code'},
body: {
'mobile': usernameController.text,
},
isFormData: true,
).catchError((error) {
if (mounted) {
setState(() {
canRegister = false;
});
}
Utils.showMessageDialog(context, error);
});
} else {
Fluttertoast.showToast(
msg: S.of(context).enter_mobile_or_email,
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
backgroundColor: Colors.red,
textColor: Colors.white
);
}
}
void startCountDown() {
countDownListener.onData((Duration d) {
if (mounted) {
setState(() {
enableGetCode = false;
getCodeText = S.of(context).get_code_token(d.inSeconds);
});
}
});
countDownListener.onDone(() {
if (mounted) {
setState(() {
enableGetCode = true;
getCodeText = S.of(context).get_code_again;
});
}
countDownListener = CountDown(Duration(seconds: 90)).stream.listen(null);
});
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,467 @@
import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import '../../constants.dart';
import '../../events/eventbus.dart';
import '../../events/events.dart';
import '../../generated/l10n.dart';
import '../../models/order.dart';
import '../../pages/order_detail.dart';
import '../../routes.dart';
import '../../store/actions.dart';
import '../../store/store.dart';
import '../../utils/double_back_to_close_app.dart';
import '../../utils/http_util.dart';
import '../../utils/util_web.dart' if (dart.library.io) '../../utils/util_io.dart';
import '../../utils/utils.dart';
import '../../widgets/general/bottom_nav.dart';
import '../../widgets/general/breadcrumbs.dart';
import '../../widgets/general/navigationbar.dart';
class DesktopOrders extends StatefulWidget {
final Key key;
const DesktopOrders({this.key});
@override
State<StatefulWidget> createState() {
return DesktopOrdersState();
}
}
class DesktopOrdersState extends State<DesktopOrders> with SingleTickerProviderStateMixin {
GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
List<Order> orders;
bool _isLoading = false;
int _page = 1;
int _pageCount = 1;
double sideSpace = 0;
double mainSpace = 1200;
ScrollController scrollController = ScrollController();
@override
Widget build(BuildContext context) {
store.dispatch(UpdateContext(context));
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
if (store.state.user == null) {
store.dispatch(UpdateRedirectRoute('/orders'));
Routes.router.navigateTo(context, '/login', replace: true);
return;
}
});
if (orders == null) {
return new Scaffold(
body: Center(
child: SpinKitWave(
color: Colors.lightBlueAccent,
size: 40.0,
),
),
);
}
if (MediaQuery.of(context).size.width <= 1200) {
mainSpace = MediaQuery.of(context).size.width;
sideSpace = 0;
} else {
mainSpace = 1200;
sideSpace = (MediaQuery.of(context).size.width - 1200) / 2;
}
Widget body = NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification scrollInfo) {
if (!_isLoading && scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent) {
if (_pageCount > _page) {
setState(() {
_page += 1;
_isLoading = true;
});
_loadData();
return true;
}
}
return false;
},
child: _buildBody(),
);
return Scaffold(
key: _scaffoldKey,
appBar: NavigationBar(
title: S.of(context).blog,
back: true,
breadCrumbs: [
BreadCrumb(S.of(context).my_orders, null),
],
breadCrumbHeight: Constants.BREADCRUMB_HEIGHT,
),
body: DoubleBackToCloseApp(
snackBar: SnackBar(
content: Text(S.of(context).tap_back_again_to_exit),
),
child: Row(
children: [
Container(
width: sideSpace,
),
Expanded(
child: body,
),
Container(
width: sideSpace,
),
],
),
),
bottomNavigationBar: BottomNav(),
);
}
Widget _buildBody() {
return ListView.builder(
controller: scrollController,
itemCount: orders != null && orders.length > 0 ? orders.length + 1 : 1,
itemBuilder: (BuildContext context, int position) {
Order order;
if (orders != null && orders.length > 0 && position < orders.length) {
order = orders[position];
}
if (order == null && orders.length <= 0) {
return Container(
padding: EdgeInsets.all(16.0),
child: Center(
child: Text(
S.of(context).you_have_no_orders_yet,
),
),
);
} else if (position >= orders.length) {
if (_pageCount > _page) {
return Container(
padding: EdgeInsets.all(16.0),
child: Center(
child: SpinKitThreeBounce(
color: Colors.lightBlueAccent,
size: 40.0,
),
),
);
} else {
return Container(
padding: EdgeInsets.all(16.0),
child: Center(
child: Center(
child: Text(
S.of(context).no_more_record,
),
),
),
);
}
}
Row row = Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[],
);
row.children.add(Expanded(
child: Container(
child: Text(
order.cartInfo.productList[0].name,
style: TextStyle(
fontSize: 14.0,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
));
if (order.cartInfo.productList.length > 1) {
row.children.add(Container(
child: Text(
S.of(context).and_more_item_token(Utils.getProductLineInOrder(order.cartInfo)),
style: TextStyle(
fontSize: 12.0,
color: Colors.black26,
),
),
));
}
row.children.add(Container(
width: 80.0,
alignment: Alignment.centerRight,
child: Text(
'\$${order.totalPrice.toStringAsFixed(2)}',
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.bold,
),
),
));
Row row3 = Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
RaisedButton(
child: Text(
S.of(context).detail,
),
onPressed: () {
Navigator.push(context, MaterialPageRoute(
builder: (BuildContext context) => OrderDetail(order.id, fromOrders: true,),
));
},
),
],
);
if (order.status == Constants.STATUS_COMPLETE && !order.hasComment) {
row3.children.add(
Padding(
padding: EdgeInsets.only(left: 10.0),
child: RaisedButton(
child: Text(
S.of(context).comment,
),
onPressed: () {
Routes.router.navigateTo(context, '/new-comment/${order.id}');
},
),
),
);
}
Row row2 = Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[],
);
if (order.paymentStatus != Constants.PAYMENT_STATUS_PAID) {
row2.children.add(row3);
if (order.paymentStatus != Constants.PAYMENT_STATUS_PAID
&& order.status != Constants.STATUS_CANCELLED
&& order.status != Constants.STATUS_COMPLETE) {
row2.children.add(RaisedButton(
child: Text(
S.of(context)
.pay_now,
style: TextStyle(
color: Colors.white,
),
),
color: Colors.redAccent,
onPressed: () {
Routes.router.navigateTo(context, '/paynow/${order.id}');
},
));
} else {
row2.children.add(RaisedButton(
child: Text(
S.of(context).order_again,
style: TextStyle(
color: Colors.white,
),
),
color: Theme.of(context).primaryColor,
onPressed: () {
Utils.orderAgain(context, order.cartInfo);
},
));
}
} else {
row2.children.add(row3);
row2.children.add(RaisedButton(
child: Text(
S.of(context).order_again,
style: TextStyle(
color: Colors.white,
),
),
color: Theme.of(context).primaryColor,
onPressed: () {
Utils.orderAgain(context, order.cartInfo);
},
));
}
return Container(
padding: EdgeInsets.only(left: 16.0, right: 16.0, top: 16.0, bottom: 16.0),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 10.0,
color: Colors.black26,
),
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Container(
child: Util.showImage('${order.cartInfo.businessInfo.picUrl}',
width: 32.0,
height: 32.0,
fit: BoxFit.fill,
),
),
Expanded(
child: GestureDetector(
child: Container(
margin: EdgeInsets.only(left: 5.0, right: 5.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
child: Text(
'${order.cartInfo.businessInfo.name}',
style: TextStyle(
fontSize: 20.0,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
Container(
child: Text(
'#${order.orderNum} ${Utils.timestampToString(context, order.createdAt, withTime: true)}',
style: TextStyle(
fontSize: 12.0,
color: Colors.black26,
),
),
),
],
),
),
onTap: () {
Navigator.push(context, MaterialPageRoute(
builder: (BuildContext context) => OrderDetail(order.id, fromOrders: true,),
));
},
),
),
Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
child: Text(
'${Utils.getOrderStatus(context, order.status)}',
style: TextStyle(
fontSize: 15.0,
fontWeight: FontWeight.bold,
),
),
),
Container(
child: SizedBox.shrink(),
),
],
),
),
],
),
Container(
padding: EdgeInsets.only(top: 10.0, bottom: 10.0),
width: double.infinity,
child: SizedBox(),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 0.5,
color: Colors.black12,
),
),
),
),
GestureDetector(
child: Container(
margin: EdgeInsets.only(top: 10.0, bottom: 20.0),
child: row,
),
onTap: () {
Navigator.push(context, MaterialPageRoute(
builder: (BuildContext context) => OrderDetail(order.id, fromOrders: true,),
));
},
),
Container(
child: row2,
),
],
),
);
},
);
}
@override
void initState() {
super.initState();
_page = 1;
_pageCount = 1;
eventBus.on<OnOrderUpdated>().listen((event) {
if (mounted) {
setState(() {
orders = null;
});
}
});
_loadData();
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
}
_loadData() {
HttpUtil.httpGet('v1/orders',
queryParameters: {
'expand': 'cart_info',
'page': _page.toString(),
'size': Constants.ORDERS_PER_PAGE.toString(),
},
).then((data) {
// Utils.jsonPrettyPrint(data);
_page = int.parse(data['_meta']['currentPage'].toString());
_pageCount = int.parse(data['_meta']['pageCount'].toString());
if (mounted) {
setState(() {
_isLoading = false;
if (orders == null) {
orders = [];
}
orders.addAll((data['items'] as List).map((e) => Order.fromJson(e)).toList());
});
}
}).catchError((error) {
if(mounted) {
setState(() {
_isLoading = false;
});
}
Utils.showMessageDialog(context, error);
});
}
}

View File

@@ -0,0 +1,396 @@
import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:flutter_wisetronic/widgets/general/breadcrumbs.dart';
import 'package:flutter_wisetronic/widgets/general/navigationbar.dart';
import '../../constants.dart';
import '../../events/eventbus.dart';
import '../../events/events.dart';
import '../../generated/l10n.dart';
import '../../models/order.dart';
import '../../models/payment_platform.dart';
import '../../models/stripe_payment_method.dart';
import '../../models/user.dart';
import '../../routes.dart';
import '../../store/actions.dart';
import '../../store/store.dart';
import '../../utils/http_util.dart';
import '../../utils/util_web.dart' if (dart.library.io) '../../utils/util_io.dart';
import '../../utils/utils.dart';
import '../../widgets/general/payment_verification_code_dialog.dart';
class DesktopPayNow extends StatefulWidget {
final Key key;
final int orderId;
const DesktopPayNow(this.orderId, {this.key});
@override
State<StatefulWidget> createState() {
return DesktopPayNowState();
}
}
class DesktopPayNowState extends State<DesktopPayNow> {
Order order;
List<PaymentPlatform> paymentPlatforms;
User _user;
bool nativePay;
double sideSpace = 0;
double mainSpace = 1200;
@override
Widget build(BuildContext context) {
store.dispatch(UpdateContext(context));
if (order == null ) {
return new Scaffold(
body: Center(
child: SpinKitWave(
color: Colors.lightBlueAccent,
size: 40.0,
),
),
);
}
if (MediaQuery.of(context).size.width <= 1200) {
mainSpace = MediaQuery.of(context).size.width;
sideSpace = 0;
} else {
mainSpace = 1200;
sideSpace = (MediaQuery.of(context).size.width - 1200) / 2;
}
BuildContext mainContext = context;
ListView listView = ListView.builder(
itemCount: nativePay ? paymentPlatforms.length + 3 : paymentPlatforms.length + 2,
itemBuilder: (BuildContext context, int position) {
if (position == 0) {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: EdgeInsets.only(top: 32.0, right: 16.0, left: 16.0, bottom: 32.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
S.of(context).payment_amount,
style: TextStyle(
fontSize: 15.0,
color: Colors.black26,
),
),
Text(
'\$${order.totalPrice.toStringAsFixed(2)}',
style: TextStyle(
fontSize: 24.0,
fontWeight: FontWeight.bold,
),
),
],
),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 10.0,
color: Colors.black26,
)
)
),
),
store.state.deviceId != null && store.state.deviceId.isNotEmpty || store.state.tableNumber != null && store.state.tableNumber.isNotEmpty ?
GestureDetector(
child: Container(
padding: EdgeInsets.only(top: 20.0, bottom: 20.0, left: 16.0, right: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Icon(
Icons.local_cafe,
size: 50.0,
),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
margin: EdgeInsets.only(left: 20.0, right: 10.0),
child: Text(
S.of(context).pay_later,
style: TextStyle(
fontSize: 20.0,
),
),
),
Container(
margin: EdgeInsets.only(left: 20.0, right: 10.0),
child: Text(
S.of(context).pay_after_meal,
style: TextStyle(
fontSize: 14.0,
color: Colors.black26,
),
),
),
],
),
),
Icon(
Icons.arrow_forward_ios,
color: Colors.black26,
),
],
),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 10.0,
color: Colors.black26,
)
)
),
),
onTap: () {
Routes.router.navigateTo(context, '/orders', replace: true, clearStack: true);
},
) :
SizedBox.shrink()
],
);
}
PaymentPlatform paymentPlatform;
if (position == 1) {
if (_user.stripePaymentMethods.length > 0) {
paymentPlatform = paymentPlatforms[position - 1];
Column column = Column(
children: <Widget>[],
);
column.children.add(
Container(
padding: EdgeInsets.all(16.0),
width: double.infinity,
child: Text(
S.of(context).pay_with_existing_cards,
textAlign: TextAlign.left,
),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 1,
color: Colors.black26,
),
),
),
),
);
for (StripePaymentMethod stripePaymentMethod in _user.stripePaymentMethods) {
column.children.add(
GestureDetector(
child: Container(
padding: EdgeInsets.only(
top: 16.0, left: 16.0, right: 16.0, bottom: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
stripePaymentMethod.cardBrand == 'visa' ?
Image.asset(
'assets/images/visa.png',
width: 50.0,
height: 50.0,
fit: BoxFit.fill,
) : (stripePaymentMethod.cardBrand == 'mastercard' ?
Image.asset(
'assets/images/master.png',
width: 50.0,
height: 50.0,
fit: BoxFit.fill,
) : Icon(
Icons.credit_card, size: 50.0, color: Colors.black38,)),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
margin: EdgeInsets.only(left: 20.0, right: 10.0),
child: Text(
'**** ${stripePaymentMethod.cardLast4}',
style: TextStyle(
fontSize: 20.0,
),
),
),
Container(
margin: EdgeInsets.only(left: 20.0, right: 10.0),
child: Text(
S.of(context).expire_token(stripePaymentMethod.cardExpMonth, stripePaymentMethod.cardExpYear),
style: TextStyle(
fontSize: 14.0,
color: Colors.black26,
),
),
),
],
),
),
Icon(
Icons.arrow_forward_ios,
color: Colors.black26,
),
],
),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 0.5,
color: Colors.black12,
),
),
),
),
onTap: () {
showDialog(
context: mainContext,
builder: (BuildContext context) {
return PaymentVerificationCodeDialog(_user, () {
Util.goPayment(context, order, paymentPlatform,
stripePaymentMethod: stripePaymentMethod);
}, () {
});
},
);
},
),
);
}
column.children.add(
Container(
width: double.infinity,
child: SizedBox.shrink(),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 10,
color: Colors.black26,
),
),
),
)
);
return column;
} else {
return SizedBox.shrink();
}
}
if (position == 2 && nativePay) {
paymentPlatform = paymentPlatforms[position - 2];
return Util().getNativePay(mainContext, order, paymentPlatform);
}
paymentPlatform = nativePay ? paymentPlatforms[position - 3] : paymentPlatforms[position - 2];
return GestureDetector(
child: Container(
padding: EdgeInsets.only(top: 16.0, left: 16.0, right: 16.0, bottom: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Container(
padding: EdgeInsets.only(right: 10.0),
child: Util.showImage(paymentPlatform.icon,
errorWidget: (context, url, error) => Icon(Icons.broken_image, size: 50.0, color: Colors.transparent,),
fit: BoxFit.cover,
width: 50.0,
height: 50.0,
),
),
Expanded(
child: Text(
S.of(context).pay_with_token(PaymentPlatform.getPaymentPlatformName(context, paymentPlatform.code)),
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.bold,
),
),
),
Icon(
Icons.arrow_forward_ios,
color: Colors.black26,
),
],
),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 10,
color: Colors.black26,
),
),
),
),
onTap: () {
Util.goPayment(context, order, paymentPlatform);
},
);
}
);
return Scaffold(
appBar: NavigationBar(
title: S.of(context).blog,
back: true,
breadCrumbs: [
BreadCrumb(S.of(context).pay_now, null),
],
breadCrumbHeight: Constants.BREADCRUMB_HEIGHT,
),
body: Row(
children: [
Container(width: sideSpace,),
Expanded(child: listView,),
Container(width: sideSpace,),
],
),
);
}
@override
void initState() {
super.initState();
HttpUtil.httpGet(
'v1/${widget.orderId}/paymentplatforms',
).then((data) {
paymentPlatforms = (data['payment_platforms'] as List).map((e) =>
PaymentPlatform.fromJson(e)).toList();
PaymentPlatform pp = paymentPlatforms[0];
if (Constants.ENABLE_NATIVE_PAY && pp.publishableKey != null
&& pp.publishableKey.isNotEmpty && pp.merchantId != null
&& pp.merchantId.isNotEmpty) {
nativePay = true;
} else {
nativePay = false;
}
_user = User.fromJson(data['contact']);
store.dispatch(UpdateCurrentUser(_user));
eventBus.fire(OnCurrentUserUpdated());
setState(() {
order = Order.fromJson(data['order']);
});
}).catchError((error) {
Utils.showMessageDialog(context, error, onOk: () {
Routes.router.navigateTo(context, "/orders", replace: true);
});
});
}
}
// 198.54.117.10

View File

@@ -0,0 +1,88 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:flutter_wisetronic/widgets/general/bottom_nav.dart';
import '../../constants.dart';
import '../../generated/l10n.dart';
import '../../models/blog.dart';
import '../../widgets/general/breadcrumbs.dart';
import '../../widgets/general/navigationbar.dart';
class DesktopPlainPage extends StatefulWidget {
final Blog blog;
const DesktopPlainPage(this.blog, {Key key}) : super(key: key);
@override
State<StatefulWidget> createState() {
return DesktopPlainPageState();
}
}
class DesktopPlainPageState extends State<DesktopPlainPage> {
double sideSpace = 0;
double mainSpace = 1200;
@override
Widget build(BuildContext context) {
if (MediaQuery.of(context).size.width <= 1200) {
mainSpace = MediaQuery.of(context).size.width;
sideSpace = 0;
} else {
mainSpace = 1200;
sideSpace = (MediaQuery.of(context).size.width - 1200) / 2;
}
Column col = Column(
children: [],
);
col.children.add(Container(
padding: EdgeInsets.only(top: 20, left: 16.0, right: 16.0, bottom: 16.0),
child: Center(
child: Text(
widget.blog.title,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
),
));
col.children.add(Container(
padding: EdgeInsets.only(left: 16.0, right: 16.0, bottom: 20.0),
child: MarkdownBody(
shrinkWrap: true,
data: widget.blog.body,
),
));
return Scaffold(
appBar: NavigationBar(
title: S.of(context).blog,
back: true,
breadCrumbs: [
BreadCrumb(widget.blog.title, null),
],
breadCrumbHeight: Constants.BREADCRUMB_HEIGHT,
),
body: SingleChildScrollView(
child: Row(
children: [
Container(
width: sideSpace,
),
Expanded(child: col,),
Container(
width: sideSpace,
),
],
),
),
bottomNavigationBar: BottomNav(),
);
}
}

View File

@@ -0,0 +1,479 @@
import 'package:flutter/material.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import '../../constants.dart';
import '../../events/eventbus.dart';
import '../../events/events.dart';
import '../../generated/l10n.dart';
import '../../models/Subproduct.dart';
import '../../models/business.dart';
import '../../models/product.dart';
import '../../models/product_detail.dart';
import '../../store/actions.dart';
import '../../store/store.dart';
import '../../utils/http_util.dart';
import '../../utils/util_web.dart'
if (dart.library.io) '../../utils/util_io.dart';
import '../../utils/utils.dart';
import '../../widgets/general/add_remove_button.dart';
import '../../widgets/general/bottom_nav.dart';
import '../../widgets/general/breadcrumbs.dart';
import '../../widgets/general/carousel.dart';
import '../../widgets/general/navigationbar.dart';
import '../../widgets/general/show_price.dart';
import 'desktop_shopping_cart_widget.dart';
class DesktopProductDetailPage extends StatefulWidget {
final Business business;
final Product product;
const DesktopProductDetailPage(
{@required this.business, @required this.product, Key key})
: super(key: key);
@override
State<StatefulWidget> createState() {
return DesktopProductDetailPageState();
}
}
class DesktopProductDetailPageState extends State<DesktopProductDetailPage>
with SingleTickerProviderStateMixin {
TabController _tabController;
final double _tabBarHeight = 50;
ProductDetail productDetail;
bool refresh;
double sideSpace = 0;
double mainSpace = 1200;
@override
Widget build(BuildContext context) {
store.dispatch(UpdateContext(context));
if (productDetail == null) {
return new Scaffold(
body: Center(
child: SpinKitWave(
color: Colors.lightBlueAccent,
size: 40.0,
),
),
);
}
refresh = false;
if (MediaQuery.of(context).size.width <= 1200) {
mainSpace = MediaQuery.of(context).size.width;
sideSpace = 0;
} else {
mainSpace = 1200;
sideSpace = (MediaQuery.of(context).size.width - 1200) / 2;
}
return Scaffold(
appBar: NavigationBar(
title: S.of(context).shop,
back: true,
breadCrumbs: [
BreadCrumb(productDetail.name, null),
],
breadCrumbHeight: Constants.BREADCRUMB_HEIGHT,
// shoppingCart: DesktopShoppingCartWidget(business: widget.business,),
),
body: SingleChildScrollView(
child: Container(
child: Row(
children: [
Container(
width: sideSpace,
),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
margin: EdgeInsets.only(top: 30.0, left: 16.0, right: 16.0, bottom: 30.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
getImage(mainSpace * 0.50),
Container(
margin: EdgeInsets.only(top: 10),
child: Text(
'SKU: ${productDetail.sku}',
overflow: TextOverflow.ellipsis,
maxLines: 2,
style: TextStyle(
color: Colors.black45,
fontSize: 12.0,
),
),
),
Container(
child: Text(
productDetail.name,
overflow: TextOverflow.ellipsis,
maxLines: 2,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20.0,
),
),
),
Container(
padding: EdgeInsets.only(top: 10, bottom: 20),
child: Text(
'${productDetail.description}',
overflow: TextOverflow.ellipsis,
maxLines: 3,
style: TextStyle(
color: Colors.black54,
fontSize: 15,
),
),
),
],
),
),
Container(
width: mainSpace * 0.5,
padding: EdgeInsets.only(top: 16.0, left: 16.0, right: 16.0, bottom: 16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: EdgeInsets.only(bottom: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ShowPrice(
productDetail.price,
currencySign: '\$',
fontWeight: FontWeight.bold,
smallFontSize: 24,
largeFontSize: 40,
regularPrice: productDetail.regularPrice,
),
AddRemoveButton(
product: widget.product,
business: widget.business,
addToBasket: true,
),
],
),
),
TabBar(
labelColor: new Color(0xFF3190E8),
unselectedLabelColor: new Color(0xFF666666),
indicatorColor: new Color(0xFF3190E8),
indicatorSize: TabBarIndicatorSize.label,
labelStyle: new TextStyle(
fontSize: 16.0,
),
controller: _tabController,
tabs: <Widget>[
Tab(
text: S.of(context).detail,
),
Tab(
text: S.of(context).specification,
),
],
),
Container(
height: mainSpace / 2,
child: TabBarView(
controller: _tabController,
children: <Widget>[
SingleChildScrollView(
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
(productDetail.subproducts.length > 0) ?
subProducts(productDetail.subproducts) :
SizedBox.shrink(),
Container(
padding: EdgeInsets.only(
top: 10.0, left: 10.0, right: 10.0, bottom: 16.0),
child: Text(
productDetail.description,
style: TextStyle(
fontSize: 14.0, color: Colors.black54),
),
),
Container(
padding: EdgeInsets.only(left: 10.0, right: 10.0),
child: (productDetail.description2 != null &&
!productDetail.description2.isEmpty)
? Text(
'${productDetail.description2}',
style: TextStyle(
fontSize: 14.0, color: Colors.black54),
)
: SizedBox.shrink(),
),
productDetail.detailDescription != null
? Container(
padding:
EdgeInsets.only(left: 10.0, right: 10.0),
child: MarkdownBody(
data: '${productDetail.detailDescription}',
shrinkWrap: true,
),
)
: SizedBox.shrink(),
],
),
),
),
Container(
padding: EdgeInsets.only(
top: 10.0, left: 10.0, right: 10.0, bottom: 10.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
child: Text(
S.of(context).weight_token(productDetail.weight),
style: TextStyle(
fontSize: 12.0,
color: Colors.black54,
),
),
),
Container(
child: Text(
S.of(context).dimentions_token(
productDetail.dimentionsLength,
productDetail.dimentionsWidth,
productDetail.dimentionsHeight),
style: TextStyle(
fontSize: 12.0,
color: Colors.black54,
),
),
)
],
),
)
],
),
),
],
),
),
],
),
),
],
),
),
Container(
width: sideSpace,
),
],
),
),
),
bottomNavigationBar: BottomNav(),
);
}
Widget getImage(double width) {
if (productDetail.images.length <= 0) {
return Util.showImage(
productDetail.image,
width: width,
height: width,
);
} else {
return Carousel(
height: width,
pages: _getProductPictures(context),
autoPlay: true,
duration: Duration(seconds: 10,),
);
}
}
Widget subProducts(List<Subproduct> subproducts) {
Column col = Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: EdgeInsets.only(left: 12, top: 10, bottom: 10),
child: Text(
S.of(context).includes,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
)
],
);
for (int i = 0; i < subproducts.length; i++) {
col.children.add(subProduct(subproducts[i]));
}
return col;
}
Widget subProduct(Subproduct subproduct) {
Row row = Row(
children: [],
);
row.children.add(
Container(
padding: EdgeInsets.symmetric(horizontal: 12.0, vertical: 5.0),
child: Util.showImage(
'https:${subproduct.product.image}',
width: 48,
height: 48,
fit: BoxFit.contain,
),
),
);
row.children.add(
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Expanded(
child: Container(
padding: EdgeInsets.only(left: 12, top: 5),
child: Text(
subproduct.product.name,
style: TextStyle(
fontSize: 15.0,
),
),
),
),
Container(
width: 80,
padding: EdgeInsets.only(left: 12, top: 5, right: 12),
child: Text(
'${subproduct.product.price.toStringAsFixed(2)}',
style: TextStyle(
fontSize: 13,
decoration: TextDecoration.lineThrough,
),
),
alignment: Alignment.centerRight,
),
Container(
width: 60,
padding: EdgeInsets.only(left: 12, top: 5, right: 12),
child: Text(
'x${subproduct.quantity.toStringAsFixed(0)}',
style: TextStyle(
fontSize: 13,
),
),
alignment: Alignment.centerRight,
),
],
),
Container(
padding: EdgeInsets.only(left: 12, top: 12, right: 12),
child: Text(
'${subproduct.product.description}',
style: TextStyle(
fontSize: 12,
color: Colors.black45,
),
),
),
],
),
),
);
return Container(
padding: EdgeInsets.only(left: 0, right: 0, top: 0, bottom: 0),
child: row,
);
}
@override
void initState() {
setState(() {
refresh = false;
});
super.initState();
_tabController = TabController(vsync: this, length: 2);
HttpUtil.httpGet(
'v1/product-detail/${widget.product.id}',
businessId: widget.business.id,
).then((value) {
print(value);
productDetail = ProductDetail.fromJson(value);
setState(() {
refresh = true;
});
}).catchError((error) {
Utils.showMessageDialog(context, error, onOk: () {
Navigator.of(context).pop();
Navigator.of(context).pop();
});
});
eventBus.on<OnCartInfoUpdated>().listen((event) {
if (mounted) {
setState(() {
refresh = true;
});
}
});
}
List<Widget> _getProductPictures(BuildContext context) {
var pages = <Widget>[];
List<String> images = [];
images.add(productDetail.image);
for (var i = 0; i < productDetail.images.length; i++) {
// print('>>https:' + productDetail.images[i].image);
images.add(productDetail.images[i].image);
}
for (var i = 0; i < images.length; i++) {
pages.add(new GestureDetector(
child: new Container(
child: Util.showImage(
'https:' + images[i],
),
),
onTap: () {},
));
}
return pages;
}
@override
void dispose() {
_tabController?.dispose();
super.dispose();
}
}

View File

@@ -0,0 +1,230 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_wisetronic/widgets/general/show_price.dart';
import '../../generated/l10n.dart';
import '../../models/business.dart';
import '../../models/product.dart';
import '../../pages/product_detail_page.dart';
import '../../utils/util_web.dart' if (dart.library.io) '../../utils/util_io.dart';
import '../../widgets/general/add_remove_button.dart';
import '../../widgets/general/style.dart';
import 'desktop_product_detail_page.dart';
class DesktopProductItem extends StatefulWidget {
final Product product;
final Business business;
final bool horizontal;
DesktopProductItem({this.product, this.business, Key key, this.horizontal = false})
: super(key: key);
@override
State<StatefulWidget> createState() {
return DesktopProductItemState();
}
}
class DesktopProductItemState extends State<DesktopProductItem> {
int _qty = 0;
@override
Widget build(BuildContext context) {
return new Container(
width: 160.0,
height: widget.horizontal ? 160 : 268.0,
padding: new EdgeInsets.symmetric(horizontal: 10.0, vertical: 15.0).copyWith(bottom: 5.0),
decoration: new BoxDecoration(
color: Style.backgroundColor,
border: new Border(
bottom: new BorderSide(
color: new Color(0xFFEBEBEB),
)
),
),
child: new SizedBox.expand(
child: Container(
padding: EdgeInsets.only(left: 10.0, right: 10.0, top: 10.0, bottom: 10.0),
child: widget.horizontal ? getHorizontal() : getVertial(),
),
),
);
}
Widget getHorizontal() {
return Row(
children: [
Container(
margin: new EdgeInsets.all(10.0),
width: 110.0,
height: 110.0,
child: GestureDetector(
child: Util.showImage('${widget.product.imagePath}',
fit: BoxFit.fill,
),
onTap: (){
_showProductDetail();
},
),
),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
child: GestureDetector(
child: Text(
'${widget.product.name}',
// overflow: kIsWeb ? null : TextOverflow.ellipsis,
overflow: TextOverflow.ellipsis,
maxLines: 2,
softWrap: true,
style: new TextStyle(
fontSize: 15.0,
),
),
onTap: (){
_showProductDetail();
},
),
),
Text(
widget.product.description,
// overflow: kIsWeb ? null : TextOverflow.ellipsis,
overflow: TextOverflow.ellipsis,
maxLines: 2,
style: new TextStyle(
fontSize: 12.0,
color: new Color(0xFF999999),
),
),
new Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Container(
child: widget.business.showMonthlySold ?
Text(
S.of(context).sold_per_month_token(widget.product.monthSales.toStringAsFixed(0)),
style: new TextStyle(
fontSize: 9.0
),
) : SizedBox.shrink(),
),
ShowPrice(
widget.product.price,
regularPrice: widget.product.regularPrice,
currencySign: '\$',
fontWeight: FontWeight.bold,
),
],
),
new AddRemoveButton(product: widget.product, business: widget.business, addOnly: false,),
],
),
],
),
),
],
);
}
Widget getVertial() {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
new Container(
margin: new EdgeInsets.all(10.0),
width: 110.0,
height: 110.0,
child: GestureDetector(
child: Util.showImage('${widget.product.imagePath}',
fit: BoxFit.fill,
),
onTap: (){
_showProductDetail();
},
),
),
new Expanded(
child: new Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
child: GestureDetector(
child: Text(
'${widget.product.name}',
// overflow: kIsWeb ? null : TextOverflow.ellipsis,
overflow: TextOverflow.ellipsis,
maxLines: 2,
softWrap: true,
style: new TextStyle(
fontSize: 15.0,
),
),
onTap: (){
_showProductDetail();
},
),
),
Container(
child: Text(
widget.product.description,
// overflow: kIsWeb ? null : TextOverflow.ellipsis,
overflow: TextOverflow.ellipsis,
maxLines: 2,
style: new TextStyle(
fontSize: 12.0,
color: new Color(0xFF999999),
),
),
),
new Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Container(
child: widget.business.showMonthlySold ?
Text(
S.of(context).sold_per_month_token(widget.product.monthSales.toStringAsFixed(0)),
style: new TextStyle(
fontSize: 9.0
),
) : SizedBox.shrink(),
),
ShowPrice(
widget.product.price,
regularPrice: widget.product.regularPrice,
currencySign: '\$',
fontWeight: FontWeight.bold,
),
],
),
new AddRemoveButton(product: widget.product, business: widget.business, addOnly: false,),
],
),
],
),
),
],
);
}
void _showProductDetail() {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProductDetailPage(
product: widget.product,
business: widget.business,
)),
);
}
}

View File

@@ -0,0 +1,209 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import '../../constants.dart';
import '../../generated/l10n.dart';
import '../../routes.dart';
import '../../utils/http_util.dart';
import '../../utils/utils.dart';
import '../../widgets/general/bottom_nav.dart';
import '../../widgets/general/breadcrumbs.dart';
import '../../widgets/general/navigationbar.dart';
class DesktopRenewLicense extends StatefulWidget {
final int businessId;
const DesktopRenewLicense(this.businessId, {Key key}) : super(key: key);
@override
State<StatefulWidget> createState() {
return DesktopRenewLicenseState();
}
}
class DesktopRenewLicenseState extends State<DesktopRenewLicense> {
double sideSpace = 0;
double mainSpace = 1200;
TextEditingController groupNumberController = TextEditingController();
bool canSubmit = false;
@override
Widget build(BuildContext context) {
if (MediaQuery.of(context).size.width <= 1200) {
mainSpace = MediaQuery.of(context).size.width;
sideSpace = 0;
} else {
mainSpace = 1200;
sideSpace = (MediaQuery.of(context).size.width - 1200) / 2;
}
Row row = Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [],
);
row.children.add(
Expanded(
child: Container(
padding: EdgeInsets.only(left: 16.0, right: 16.0, top: 20, bottom: 20),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: EdgeInsets.only(bottom: 4.0),
child: Text(
S.of(context).renew_license,
style: TextStyle(
fontSize: 28,
),
),
),
Container(
padding: EdgeInsets.only(top: 4, bottom: 4),
child: Text(
S.of(context).group_number_can_be_found,
style: TextStyle(
color: Colors.black45,
),
),
),
Container(
padding: EdgeInsets.only(top: 4),
child: Image.asset(
'assets/images/group_number.png',
width: 400,
),
),
],
),
decoration: BoxDecoration(
border: Border(
right: BorderSide(
width: 1,
color: Colors.black12,
),
),
),
),
),
);
row.children.add(
Expanded(
child: Container(
padding: EdgeInsets.only(left: 16.0, right: 16.0, top: 20, bottom: 20),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: EdgeInsets.only(bottom: 4,),
child: Text(
S.of(context).group_number,
style: TextStyle(
fontSize: 28,
),
),
),
Container(
padding: EdgeInsets.only(top: 4,),
child: TextFormField(
controller: groupNumberController,
decoration: new InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black12,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.blue,
),
),
labelText: S.of(context).group_number,
),
style: TextStyle(
fontSize: 18.0
),
autofocus: true,
validator: (String value) {
if (value.trim().isEmpty) {
return S.of(context).please_enter_group_number;
}
return null;
},
onChanged: (string) {
if (string.isNotEmpty) {
canSubmit = true;
} else {
canSubmit = false;
}
setState(() {});
},
),
),
Container(
padding: EdgeInsets.only(top: 20),
child: ElevatedButton(
child: Text(
S.of(context).submit,
),
onPressed: canSubmit ? () {
_submit();
} : null,
),
),
],
),
),
),
);
return Scaffold(
appBar: NavigationBar(
title: S.of(context).blog,
back: true,
breadCrumbs: [
BreadCrumb(S.of(context).renew_license, null),
],
breadCrumbHeight: Constants.BREADCRUMB_HEIGHT,
),
body: SingleChildScrollView(
child: Row(
children: [
Container(
width: sideSpace,
),
Expanded(child: row,),
Container(
width: sideSpace,
),
],
),
),
bottomNavigationBar: BottomNav(),
);
}
void _submit() {
HttpUtil.httpPost('v1/get-license-renewal',
(response) {
Routes.router.navigateTo(context, '/renew-minioffice/${response.data['id']}', replace: true);
},
body: {
'group_name': groupNumberController.text.trim(),
},
isFormData: true,
).onError((error, stackTrace) {
Utils.showMessageDialog(context, error);
});
}
}

View File

@@ -0,0 +1,179 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import '../../constants.dart';
import '../../generated/l10n.dart';
import '../../routes.dart';
import '../../store/store.dart';
import '../../utils/http_util.dart';
import '../../utils/utils.dart';
import '../../widgets/general/bottom_nav.dart';
import '../../widgets/general/breadcrumbs.dart';
import '../../widgets/general/navigationbar.dart';
class DesktopRenewMiniOffice extends StatefulWidget {
final Map<String, dynamic> data;
const DesktopRenewMiniOffice(this.data, {Key key}) : super(key: key);
@override
State<StatefulWidget> createState() {
return DesktopRenewMiniOfficeState();
}
}
class DesktopRenewMiniOfficeState extends State<DesktopRenewMiniOffice> {
double sideSpace = 0;
double mainSpace = 1200;
@override
Widget build(BuildContext context) {
if (MediaQuery.of(context).size.width <= 1200) {
mainSpace = MediaQuery.of(context).size.width;
sideSpace = 0;
} else {
mainSpace = 1200;
sideSpace = (MediaQuery.of(context).size.width - 1200) / 2;
}
Row row = Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [],
);
Column col1 = Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: EdgeInsets.only(bottom: 16),
child: Text(
S.of(context).group_license_renewal,
style: TextStyle(
fontSize: 28,
),
),
),
],
);
col1.children.add(
Utils.buildLine(S.of(context).group_number, widget.data['group']['name'], valueSize: 18),
);
col1.children.add(
Utils.buildLine(S.of(context).expiration_date,
Utils.utcDatetimeStringToLocalDatetimeString(context, widget.data['expiration_date']),
valueSize: 18
),
);
col1.children.add(
Utils.buildLine(S.of(context).after_renewed,
Utils.utcDatetimeStringToLocalDatetimeString(context, widget.data['renewed_expiration_date']),
valueSize: 18
),
);
col1.children.add(
Utils.buildLine(S.of(context).renewal_fee, '\$${widget.data['renewal_fee']}', valueSize: 18),
);
col1.children.add(
Utils.buildLine(S.of(context).tax, '\$${widget.data['tax']}', valueSize: 18),
);
col1.children.add(
Utils.buildLine(S.of(context).total, '\$${widget.data['renewal_total']}', valueSize: 38),
);
Column col2 = Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: EdgeInsets.only(left: 16, right: 16),
child: ElevatedButton(
child: Container(
padding: EdgeInsets.only(left: 16, right: 16, top: 8, bottom: 8),
child: Text(
S.of(context).pay_amount_token(widget.data['renewal_total']),
style: TextStyle(
fontSize: 20,
),
),
),
onPressed: () {
_submit();
},
),
),
],
);
row.children.add(
Expanded(
child: Container(
padding: EdgeInsets.only(top: 20, left: 16, right: 16, bottom: 20),
child: col1,
decoration: BoxDecoration(
border: Border(
right: BorderSide(
width: 1,
color: Colors.black12,
),
),
),
),
)
);
row.children.add(
Expanded(
child: Container(
padding: EdgeInsets.only(top: 20, left: 16, right: 16, bottom: 20),
child: col2,
),
)
);
return Scaffold(
appBar: NavigationBar(
title: S.of(context).blog,
back: true,
breadCrumbs: [
BreadCrumb(S.of(context).renew_license, null),
],
breadCrumbHeight: Constants.BREADCRUMB_HEIGHT,
),
body: SingleChildScrollView(
child: Row(
children: [
Container(
width: sideSpace,
),
Expanded(child: row,),
Container(
width: sideSpace,
),
],
),
),
bottomNavigationBar: BottomNav(),
);
}
void _submit() {
if (store.state.user == null) {
Utils.requireLogin(context, returnUrl: '/renew-minioffice/${widget.data['group']['id']}');
return;
}
HttpUtil.httpPost('v1/create-minioffice-renewal-invoice',
(response) {
Routes.router.navigateTo(context, '/paynow/${response.data['order_id']}', replace: true);
},
body: widget.data,
).onError((error, stackTrace) {
Utils.showMessageDialog(context, error);
});
}
}

View File

@@ -0,0 +1,304 @@
import 'package:flutter/material.dart';
import '../../generated/l10n.dart';
import '../../routes.dart';
import '../../store/actions.dart';
import '../../store/store.dart';
import '../../utils/http_util.dart';
import '../../utils/utils.dart';
class DesktopResetPassword extends StatefulWidget {
final String mobile;
final String code;
const DesktopResetPassword(this.mobile, {this.code, Key key}) :
super(key: key);
@override
State<StatefulWidget> createState() =>
DesktopResetPasswordState();
}
class DesktopResetPasswordState extends State<DesktopResetPassword> {
final GlobalKey<FormState> _formKey = GlobalKey();
final passwordController = TextEditingController();
final passwordAgainController = TextEditingController();
bool passwordVisible;
bool passwordAgainVisible;
bool canReset;
double sideSpace = 0;
double mainSpace = 1200;
@override
void initState() {
super.initState();
canReset = false;
passwordVisible = true;
passwordAgainVisible = true;
}
@override
Widget build(BuildContext context) {
store.dispatch(UpdateContext(context));
if (MediaQuery.of(context).size.width <= 1200) {
mainSpace = MediaQuery.of(context).size.width;
sideSpace = 0;
} else {
mainSpace = 1200;
sideSpace = (MediaQuery.of(context).size.width - 1200) / 2;
}
Widget form = Container(
padding: EdgeInsets.only(top: 0.0, left: 16.0, right: 16.0, bottom: 0.0),
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
child: TextFormField(
controller: passwordController,
decoration: new InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black12,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.blue,
),
),
labelText: S.of(context).password,
suffixIcon: IconButton(
icon: Icon(
passwordVisible ? Icons.visibility_off : Icons.visibility,
color: Theme.of(context).primaryColorDark,
),
onPressed: () {
setState(() {
passwordVisible = !passwordVisible;
});
},
),
),
style: TextStyle(
fontSize: 18.0
),
validator: (String value) {
if (value.trim().isEmpty) {
return S.of(context).password_is_required;
}
return null;
},
obscureText: passwordVisible,
onChanged: (string) {
if (string.trim().isNotEmpty && passwordAgainController.text.trim().isNotEmpty) {
if (mounted) {
setState(() {
canReset = true;
});
}
} else {
if (mounted) {
setState(() {
canReset = false;
});
}
}
},
),
),
Container(
child: TextFormField(
controller: passwordAgainController,
decoration: new InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black12,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.blue,
),
),
labelText: S.of(context).password_again,
suffixIcon: IconButton(
icon: Icon(
passwordAgainVisible ? Icons.visibility_off : Icons.visibility,
color: Theme.of(context).primaryColorDark,
),
onPressed: () {
setState(() {
passwordAgainVisible = !passwordAgainVisible;
});
},
),
),
style: TextStyle(
fontSize: 18.0
),
validator: (String value) {
if (value.trim().isEmpty) {
return S.of(context).password_is_required;
}
if (value.trim() != passwordController.text.trim()) {
return S.of(context).password_is_not_match_password_again;
}
return null;
},
obscureText: passwordAgainVisible,
onChanged: (string) {
if (passwordController.text.trim().isNotEmpty && string.trim().isNotEmpty) {
if (mounted) {
setState(() {
canReset = true;
});
}
} else {
if (mounted) {
setState(() {
canReset = false;
});
}
}
},
),
),
Container(
padding: EdgeInsets.only(right: 0.0, top: 16.0),
child: Align(
alignment: Alignment.centerRight,
child: RaisedButton(
color: Theme.of(context).primaryColor,
child: Text(
S.of(context).submit,
style: TextStyle(
color: Colors.white,
),
),
onPressed: canReset ? resetPassword : null,
),
),
),
],
),
),
);
return SingleChildScrollView(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: sideSpace,
),
Container(
width: mainSpace,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: mainSpace / 2,
margin: EdgeInsets.only(top: 16.0, bottom: 16.0),
padding: EdgeInsets.all(10.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: EdgeInsets.only(left: 16.0, top: 0.0, right: 16.0),
child: Text(
S.of(context).reset_password,
style: TextStyle(
fontSize: 24.0,
color: Colors.black
),
),
),
Container(
padding: EdgeInsets.only(top: 12.0, left: 16.0, right: 16.0, bottom: 12.0),
child: Text(
S.of(context).reset_password_desc,
style: TextStyle(
color: Colors.black54,
fontSize: 14.0,
),
),
),
],
),
),
Container(
width: mainSpace / 2,
margin: EdgeInsets.only(top: 16.0, bottom: 16.0),
padding: EdgeInsets.all(10.0),
decoration: BoxDecoration(
border: Border(
left: BorderSide(
width: 1.0,
color: Colors.black12,
),
),
),
child: form,
),
],
),
),
Container(
width: sideSpace,
),
],
),
);
}
void resetPassword() {
final FormState form = _formKey.currentState;
if (form.validate()) {
HttpUtil.httpPost('v1/users', (response) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(S.of(context).success),
content: Text(S.of(context).reset_password_success),
actions: <Widget>[
FlatButton(
child: Text(S.of(context).ok),
onPressed: () {
Routes.router.navigateTo(context, '/login', replace: true, clearStack: false);
},
),
],
);
},
);
},
queryParameters: {
'action': 'reset_password',
},
isFormData: true,
body: {
'mobile': widget.mobile,
'code': widget.code,
'password': passwordController.text.trim()
}
).catchError((error) {
Utils.showMessageDialog(context, error);
});
}
}
}

View File

@@ -0,0 +1,153 @@
import 'package:dio/dio.dart';
import 'package:flappy_search_bar/flappy_search_bar.dart';
import 'package:flutter/material.dart';
import 'package:flutter_wisetronic/widgets/general/breadcrumbs.dart';
import 'package:flutter_wisetronic/widgets/general/navigationbar.dart';
import '../../constants.dart';
import '../../events/eventbus.dart';
import '../../events/events.dart';
import '../../generated/l10n.dart';
import '../../models/located_address.dart';
import '../../pages/new_address.dart';
import '../../store/actions.dart';
import '../../store/store.dart';
import '../../utils/http_util.dart';
import '../../utils/utils.dart';
class DesktopSearchPlace extends StatefulWidget {
final Key key;
final int businessId;
const DesktopSearchPlace(this.businessId, {this.key}) : super(key: key);
@override
State<StatefulWidget> createState() {
return DesktopSearchPlaceState();
}
}
class DesktopSearchPlaceState extends State<DesktopSearchPlace> {
double sideSpace = 0;
double mainSpace = 1200;
@override
Widget build(BuildContext context) {
store.dispatch(UpdateContext(context));
if (MediaQuery.of(context).size.width <= 1200) {
mainSpace = MediaQuery.of(context).size.width;
sideSpace = 0;
} else {
mainSpace = 1200;
sideSpace = (MediaQuery.of(context).size.width - 1200) / 2;
}
return Scaffold(
appBar: NavigationBar(
title: S.of(context).search_place,
back: true,
breadCrumbs: [
BreadCrumb(S.of(context).search_place, null),
],
breadCrumbHeight: Constants.BREADCRUMB_HEIGHT,
),
body: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: sideSpace,
),
Container(
padding: EdgeInsets.only(top: 16.0, right: 16.0, bottom: 16.0, left: 16.0),
width: mainSpace,
child: SearchBar(
minimumChars: 6,
hintText: S.of(context).enter_delivery_address,
cancellationWidget: Text(
S.of(context).cancel,
),
onSearch: search,
onItemFound: (LocatedAddress locatedAddress, int index) {
return ListTile(
title: Text(locatedAddress.streetName),
subtitle: Text(locatedAddress.formattedAddress),
onTap: () {
if (widget.businessId == 0) {
_selectPlace(locatedAddress);
} else {
_selectPlaceAsNew(locatedAddress);
}
},
dense: true,
);
},
onError: (error) {
return Center(
child: Text("$error"),
);
},
emptyWidget: Center(
child: widget.businessId > 0 ?
GestureDetector(
child: Text(
S.of(context).empty_address_change_keyword,
),
onTap: () {
_selectPlaceAsNew(null);
},
) :
Text(
S.of(context).empty_result_change_keyword
),
),
),
),
Container(
width: sideSpace,
),
],
),
);
}
@override
void initState() {
super.initState();
}
Future<List<LocatedAddress>> search(String keyword) async {
var result = await HttpUtil.httpGet('v1/search-places',
queryParameters: {
'keyword': keyword,
},
returnError: true,
);
if (result is DioError) {
if (result.response != null) {
throw RuntimeError(result.response.data['message']);
} else {
throw RuntimeError(result.message);
}
} else if (result != null && result is List) {
return result.map((e) => LocatedAddress.fromJson(e)).toList();
}
return [];
}
void _selectPlace(LocatedAddress locatedAddress) {
store.dispatch(UpdateLocatedAddress(locatedAddress));
eventBus.fire(OnUpdateLocatedAddressSuccess());
Navigator.of(context).pop();
}
void _selectPlaceAsNew(LocatedAddress locatedAddress) {
print('located address: ${locatedAddress.toString()}');
Navigator.push(context, MaterialPageRoute(
builder: (BuildContext context) => NewAddress(locatedAddress: locatedAddress, businessId: widget.businessId,),
));
}
}

View File

@@ -0,0 +1,304 @@
import 'package:flutter/material.dart';
import '../../generated/l10n.dart';
import '../../routes.dart';
import '../../store/actions.dart';
import '../../store/store.dart';
import '../../utils/http_util.dart';
import '../../utils/utils.dart';
class DesktopSetPassword extends StatefulWidget {
final String mobile;
final String code;
const DesktopSetPassword(this.mobile, {this.code, Key key}) :
super(key: key);
@override
State<StatefulWidget> createState() =>
DesktopSetPasswordState();
}
class DesktopSetPasswordState extends State<DesktopSetPassword> {
final GlobalKey<FormState> _formKey = GlobalKey();
final passwordController = TextEditingController();
final passwordAgainController = TextEditingController();
bool passwordVisible;
bool passwordAgainVisible;
bool canReset;
double sideSpace = 0;
double mainSpace = 1200;
@override
void initState() {
super.initState();
canReset = false;
passwordVisible = true;
passwordAgainVisible = true;
}
@override
Widget build(BuildContext context) {
store.dispatch(UpdateContext(context));
if (MediaQuery.of(context).size.width <= 1200) {
mainSpace = MediaQuery.of(context).size.width;
sideSpace = 0;
} else {
mainSpace = 1200;
sideSpace = (MediaQuery.of(context).size.width - 1200) / 2;
}
Widget form = Container(
padding: EdgeInsets.only(top: 0.0, left: 16.0, right: 16.0, bottom: 0.0),
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
child: TextFormField(
controller: passwordController,
decoration: new InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black12,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.blue,
),
),
labelText: S.of(context).password,
suffixIcon: IconButton(
icon: Icon(
passwordVisible ? Icons.visibility_off : Icons.visibility,
color: Theme.of(context).primaryColorDark,
),
onPressed: () {
setState(() {
passwordVisible = !passwordVisible;
});
},
),
),
style: TextStyle(
fontSize: 18.0
),
validator: (String value) {
if (value.trim().isEmpty) {
return S.of(context).password_is_required;
}
return null;
},
obscureText: passwordVisible,
onChanged: (string) {
if (string.trim().isNotEmpty && passwordAgainController.text.trim().isNotEmpty) {
if (mounted) {
setState(() {
canReset = true;
});
}
} else {
if (mounted) {
setState(() {
canReset = false;
});
}
}
},
),
),
Container(
child: TextFormField(
controller: passwordAgainController,
decoration: new InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black12,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.blue,
),
),
labelText: S.of(context).password_again,
suffixIcon: IconButton(
icon: Icon(
passwordAgainVisible ? Icons.visibility_off : Icons.visibility,
color: Theme.of(context).primaryColorDark,
),
onPressed: () {
setState(() {
passwordAgainVisible = !passwordAgainVisible;
});
},
),
),
style: TextStyle(
fontSize: 18.0
),
validator: (String value) {
if (value.trim().isEmpty) {
return S.of(context).password_is_required;
}
if (value.trim() != passwordController.text.trim()) {
return S.of(context).password_is_not_match_password_again;
}
return null;
},
obscureText: passwordAgainVisible,
onChanged: (string) {
if (passwordController.text.trim().isNotEmpty && string.trim().isNotEmpty) {
if (mounted) {
setState(() {
canReset = true;
});
}
} else {
if (mounted) {
setState(() {
canReset = false;
});
}
}
},
),
),
Container(
padding: EdgeInsets.only(right: 16.0, top: 16.0),
child: Align(
alignment: Alignment.centerRight,
child: RaisedButton(
color: Theme.of(context).primaryColor,
child: Text(
S.of(context).submit,
style: TextStyle(
color: Colors.white,
),
),
onPressed: canReset ? resetPassword : null,
),
),
),
],
),
),
);
return SingleChildScrollView(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: sideSpace,
),
Container(
width: mainSpace,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: mainSpace / 2,
margin: EdgeInsets.only(top: 16.0, bottom: 16.0),
padding: EdgeInsets.all(10.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: EdgeInsets.only(left: 16.0, top: 0.0, right: 16.0),
child: Text(
S.of(context).set_password,
style: TextStyle(
fontSize: 24.0,
color: Colors.black
),
),
),
Container(
padding: EdgeInsets.only(top: 12.0, left: 16.0, right: 16.0, bottom: 12.0),
child: Text(
S.of(context).set_password_desc,
style: TextStyle(
color: Colors.black54,
fontSize: 14.0,
),
),
),
],
),
),
Container(
width: mainSpace / 2,
margin: EdgeInsets.only(top: 16.0, bottom: 16.0),
padding: EdgeInsets.all(10.0),
decoration: BoxDecoration(
border: Border(
left: BorderSide(
width: 1.0,
color: Colors.black12,
),
),
),
child: form,
),
],
),
),
Container(
width: sideSpace,
),
],
),
);
}
void resetPassword() {
final FormState form = _formKey.currentState;
if (form.validate()) {
HttpUtil.httpPost('v1/users', (response) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(S.of(context).success),
content: Text(S.of(context).reset_password_success),
actions: <Widget>[
FlatButton(
child: Text(S.of(context).ok),
onPressed: () {
Routes.router.navigateTo(context, '/login', replace: true, clearStack: false);
},
),
],
);
},
);
},
queryParameters: {
'action': 'reset_password',
},
isFormData: true,
body: {
'mobile': widget.mobile,
'code': widget.code,
'password': passwordController.text.trim()
}
).catchError((error) {
Utils.showMessageDialog(context, error);
});
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,67 @@
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import '../../models/business.dart';
import '../../models/cart_info.dart';
import '../../store/store.dart';
import '../../utils/utils.dart';
class DesktopShoppingCartWidget extends StatefulWidget {
final Business business;
final Function onTap;
DesktopShoppingCartWidget({@required this.business, this.onTap});
@override
State<StatefulWidget> createState() => DesktopShoppingCartWidgetState();
}
class DesktopShoppingCartWidgetState extends State<DesktopShoppingCartWidget> {
CartInfo cartInfo;
double totalPrice = 0.0;
@override
Widget build(BuildContext context) {
totalPrice = 0.0;
cartInfo = Utils.getCartInfoByBusiness(store.state.cartInfos, widget.business);
if (cartInfo != null && cartInfo.businessInfo.id == widget.business.id) {
totalPrice = cartInfo.getTotalPrice();
}
Row row = Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
padding: EdgeInsets.only(left: 16.0, right: 4.0, top: 8.0),
child: Icon(
Icons.shopping_basket_outlined,
size: 24,
color: Colors.blue,
),
),
Container(
padding: EdgeInsets.only(left: 4.0, right: 16.0, top: 8.0),
child: Text(
'\$${totalPrice.toStringAsFixed(2)}',
style: TextStyle(
color: totalPrice > 0 ? Colors.red : Colors.black45,
fontWeight: totalPrice > 0 ? FontWeight.bold : FontWeight.normal,
),
),
),
],
);
return MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
child: Container(
child: row,
width: 160.0,
),
onTap: widget.onTap,
),
);
}
}

View File

@@ -0,0 +1,173 @@
import 'package:dio/dio.dart';
import 'package:flappy_search_bar/flappy_search_bar.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_wisetronic/pages/product_detail_page.dart';
import 'package:flutter_wisetronic/widgets/desktop/desktop_product_item.dart';
import '../../events/eventbus.dart';
import '../../events/events.dart';
import '../../generated/l10n.dart';
import '../../models/business.dart';
import '../../models/product.dart';
import '../../store/actions.dart';
import '../../store/store.dart';
import '../../utils/http_util.dart';
import '../../utils/util_web.dart' if (dart.library.io) '../../utils/util_io.dart';
import '../../utils/utils.dart';
import '../../widgets/general/add_remove_button.dart';
class DesktopStoreProductSearch extends StatefulWidget {
final Key key;
final Business business;
const DesktopStoreProductSearch(this.business, {this.key});
@override
State<StatefulWidget> createState() {
return DesktopStoreProductSearchState();
}
}
class DesktopStoreProductSearchState extends State<DesktopStoreProductSearch> {
double sideSpace = 0;
double mainSpace = 1200;
double rate = 1;
int page = 1;
int numPerPage = 10;
int lastResultSize = 0;
double lastBottomPosition = 0;
String lastKeyword = '';
SearchBarController _controller = SearchBarController<Product>();
@override
Widget build(BuildContext context) {
store.dispatch(UpdateContext(context));
if (MediaQuery.of(context).size.width <= 1200) {
mainSpace = MediaQuery.of(context).size.width;
sideSpace = 0;
} else {
mainSpace = 1200;
sideSpace = (MediaQuery.of(context).size.width - 1200) / 2;
}
rate = mainSpace / 1200;
return SafeArea(
child: Container(
child: Row(
children: [
Container(
width: sideSpace,
),
Container(
width: mainSpace,
padding: EdgeInsets.all(16.0),
child: NotificationListener<ScrollNotification>(
child: SearchBar(
searchBarController: _controller,
minimumChars: 2,
hintText: S.of(context).enter_product_keyword,
cancellationWidget: Text(
S.of(context).cancel,
),
onSearch: search,
onItemFound: (Product searchProduct, int index) {
return DesktopProductItem(
product: searchProduct,
business: widget.business,
horizontal: true,
);
},
onError: (error) {
return Center(
child: Text("$error"),
);
},
emptyWidget: Center(
child: Text(
S.of(context).empty_result_change_keyword,
),
),
),
onNotification: (notification) {
if (notification.metrics.atEdge) {
if (page != 0 && lastResultSize >= numPerPage && notification.metrics.pixels != 0) {
if (notification.metrics.pixels > lastBottomPosition) {
lastBottomPosition = notification.metrics.pixels;
page += 1;
_controller.replayLastSearch();
}
}
}
return true;
},
),
),
Container(
width: sideSpace,
),
],
),
),
);
}
Future<List<Product>> search(String keyword) async {
if (lastKeyword != keyword) {
page = 1;
}
lastKeyword = keyword;
var result = await HttpUtil.httpGet('v1/search-store-product2',
queryParameters: {
'store_id': widget.business.id,
'keyword': keyword,
'page': page,
'num_per_page': numPerPage,
},
returnError: true,
);
if (result is DioError) {
if (result.response != null) {
throw RuntimeError(result.response.data);
} else {
throw RuntimeError(result.message);
}
} else if (result != null && result is List) {
lastBottomPosition = 0;
var r = result.map((e) => Product.fromJson(e)).toList();
lastResultSize = r.length;
if (lastResultSize < numPerPage) {
page = 1;
}
return r;
}
return [];
}
@override
void initState() {
super.initState();
eventBus.on<OnCartInfoUpdated>().listen((event) {
if (mounted) {
setState(() {});
}
});
}
void _showProductDetail(Product p) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProductDetailPage(
product: p,
business: widget.business,
)),
);
}
}

View File

@@ -0,0 +1,250 @@
import 'package:flutter/material.dart';
import 'package:stripe_sdk/stripe_sdk.dart';
import 'package:stripe_sdk/stripe_sdk_ui.dart';
import '../../constants.dart';
import '../../events/eventbus.dart';
import '../../events/events.dart';
import '../../generated/l10n.dart';
import '../../models/order.dart';
import '../../models/payment_platform.dart';
import '../../models/stripe_payment_method.dart';
import '../../routes.dart';
import '../../store/actions.dart';
import '../../store/store.dart';
import '../../utils/utils.dart';
import '../../widgets/general/breadcrumbs.dart';
import '../../widgets/general/navigationbar.dart';
class DesktopStripePayWeb extends StatefulWidget {
final Key key;
final Order order;
final PaymentPlatform paymentPlatform;
final StripePaymentMethod stripePaymentMethod;
const DesktopStripePayWeb(this.order, this.paymentPlatform, {this.key, this.stripePaymentMethod});
@override
State<StatefulWidget> createState() {
return DesktopStripePayWebState();
}
}
class DesktopStripePayWebState extends State<DesktopStripePayWeb> {
GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey();
final formKey = GlobalKey<FormState>();
final card = StripeCard();
CardForm form;
bool isSubmitting;
double sideSpace = 0;
double mainSpace = 1200;
@override
Widget build(BuildContext context) {
store.dispatch(UpdateContext(context));
if (MediaQuery.of(context).size.width <= 1200) {
mainSpace = MediaQuery.of(context).size.width;
sideSpace = 0;
} else {
mainSpace = 1200;
sideSpace = (MediaQuery.of(context).size.width - 1200) / 2;
}
Widget body = Center(
child: Icon(
Icons.credit_card,
size: 40.0,
color: Colors.black26,
),
);
if (widget.stripePaymentMethod == null) {
form = CardForm(card: card, formKey: formKey,);
body = ListView(
children: <Widget>[
form,
Container(
padding: EdgeInsets.only(top: 20.0, bottom: 20.0, right: 16.0),
alignment: Alignment.centerRight,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Theme.of(context).primaryColor,
),
child: Text(
S.of(context).submit,
style: TextStyle(
color: Colors.white,
),
),
onPressed: () {
if (formKey.currentState.validate()) {
formKey.currentState.save();
_paymentRequestWithCard(context);
} else {
ScaffoldMessenger.of(context).showSnackBar(
messageSnackBar(
context, S.of(context).this_credit_card_is_invalid
)
);
}
},
),
),
],
);
}
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
if (widget.stripePaymentMethod != null) {
_paymentWithPaymentMethod(context);
}
});
return Scaffold(
key: _scaffoldKey,
appBar: NavigationBar(
title: S.of(context).blog,
back: true,
breadCrumbs: [
BreadCrumb(S.of(context).add_credit_card, null),
],
breadCrumbHeight: Constants.BREADCRUMB_HEIGHT,
),
body: Row(
children: [
Container(width: sideSpace,),
Expanded(child: body,),
Container(width: sideSpace,),
],
),
);
}
@override
void initState() {
super.initState();
isSubmitting = false;
StripeApi.init(widget.paymentPlatform.publishableKey);
}
SnackBar messageSnackBar(BuildContext context, String message) {
Column column = Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[Text(
message,
style: TextStyle(
color: Colors.white,
),
)],
);
return SnackBar(
content: Container(
height: 45.0,
child: column,
),
action: SnackBarAction(
label: S.of(context).ok,
onPressed: () {
ScaffoldMessenger.of(context).hideCurrentSnackBar();
},
),
);
}
_paymentWithPaymentMethod(BuildContext context) async {
Utils.stripePaymentIntent(widget.order, widget.stripePaymentMethod.customerId,
widget.stripePaymentMethod.paymentMethodId,
widget.stripePaymentMethod.paymentMethodType, (response) async {
if (response.data['status'] == Constants.STRIPE_STATUS_REQUIRES_CONFIRMATION) {
await StripeApi.instance.confirmPaymentIntent(
response.data[Constants.STRIPE_CLIENT_SECRET],
data: {
'payment_method': response.data['payment_method'],
},
).then((result2) {
if (result2['status'] == Constants.STRIPE_STATUS_SUCCEDED) {
Utils.stripeChargedSuccess(widget.order,
widget.stripePaymentMethod.paymentMethodId,
result2['id'], (response) {
if (isSubmitting) {
Navigator.of(context).pop();
}
eventBus.fire(OnOrderUpdated());
Routes.router.navigateTo(context, '/orderdetail/${widget
.order.id}', replace: true);
},
(showErrorDialog)
);
} else {
showErrorDialog(Exception('Unknown error'));
}
}).catchError(showErrorDialog);
}
}, (showErrorDialog));
isSubmitting = true;
Utils.showSubmitDialog(context);
}
_paymentRequestWithCard(BuildContext context) async {
isSubmitting = true;
Utils.showSubmitDialog(context);
await StripeApi.instance.createPaymentMethodFromCard(card)
.then((result) {
Utils.stripePaymentIntent(widget.order, null, result['id'], result['type'], (response) async {
if (response.data['status'] == Constants.STRIPE_STATUS_REQUIRES_CONFIRMATION) {
await StripeApi.instance.confirmPaymentIntent(
response.data[Constants.STRIPE_CLIENT_SECRET],
data: {
'payment_method': response.data['payment_method'],
}
).then((result2) {
if (result2['status'] == Constants.STRIPE_STATUS_SUCCEDED) {
Utils.stripeChargedSuccess(widget.order,
result['id'], // payment method id
result2['id'], (response) { // payment intent id
if (isSubmitting) {
Navigator.of(context).pop();
}
eventBus.fire(OnOrderUpdated());
Routes.router.navigateTo(context, '/orderdetail/${widget
.order.id}', replace: true);
},
(showErrorDialog)
);
} else {
showErrorDialog(Exception('Unknown error'));
}
}).catchError(showErrorDialog);
}
}, (showErrorDialog),
cardBrand: result['card']['brand'],
cardCountry: result['card']['country'],
cardExpMonth: result['card']['exp_month'],
cardExpYear: result['card']['exp_year'],
cardFunding: result['card']['funding'],
cardLast4: result['card']['last4'],
);
}).catchError(showErrorDialog);
}
void showErrorDialog(dynamic error) {
if (isSubmitting) {
Navigator.of(context).pop();
isSubmitting = false;
}
Utils.showMessageDialog(context, error, onOk: () {
Navigator.of(context).pop();
Navigator.of(context).pop();
});
}
}

View File

@@ -0,0 +1,127 @@
import '../../constants.dart';
import '../../utils/iframe_web.dart' if (dart.library.io) '../../utils/fake_iframe_web.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
class DesktopTutorials extends StatefulWidget {
const DesktopTutorials({Key key}) : super(key: key);
@override
State<StatefulWidget> createState() {
return DesktopTutorialsState();
}
}
class DesktopTutorialsState extends State<DesktopTutorials> {
double sideSpace = 0;
double mainSpace = 1200;
InAppWebViewController webView;
String url = "";
double progress = 0;
bool isLoadStop = false;
@override
Widget build(BuildContext context) {
if (MediaQuery.of(context).size.width <= 1200) {
mainSpace = MediaQuery.of(context).size.width;
sideSpace = 0;
} else {
mainSpace = 1200;
sideSpace = (MediaQuery.of(context).size.width - 1200) / 2;
}
if (kIsWeb) {
return Row(
children: [
Container(
width: sideSpace,
),
Expanded(
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
Expanded(
child: IFrameWeb(
width: double.maxFinite.toString(),
height: double.maxFinite.toString(),
src: Constants.TUTORIAL_URL,
),
),
],
),
),
Container(
width: sideSpace,
),
],
);
} else {
print('progress: $progress');
return Stack(
children: [
Row(
children: [
Container(
width: sideSpace,
),
Expanded(
child: InAppWebView(
initialUrlRequest: URLRequest(url: Uri.parse(Constants.TUTORIAL_URL)),
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
),
),
onWebViewCreated: (InAppWebViewController controller) {
webView = controller;
},
onLoadStart: (controller, url) {
if (mounted) {
setState(() {
this.url = url.toString();
this.isLoadStop = false;
});
}
},
onLoadStop: (controller, url) async {
if (mounted) {
setState(() {
this.url = url.toString();
this.isLoadStop = true;
});
}
},
onProgressChanged: (InAppWebViewController controller, int progress) {
if (mounted) {
setState(() {
if (this.progress < 1.0) {
this.isLoadStop = false;
} else {
this.isLoadStop = true;
}
this.progress = progress / 100;
});
}
},
),
),
Container(
width: sideSpace,
),
],
),
isLoadStop ? Container() :
Center(
child: CircularProgressIndicator(),
),
],
);
}
}
}

View File

@@ -0,0 +1,480 @@
import 'package:flutter/material.dart';
import 'package:percent_indicator/linear_percent_indicator.dart';
import '../../constants.dart';
import '../../events/eventbus.dart';
import '../../events/events.dart';
import '../../generated/l10n.dart';
import '../../models/user.dart';
import '../../routes.dart';
import '../../store/actions.dart';
import '../../store/store.dart';
import '../../utils/http_util.dart';
import '../../utils/util_web.dart' if (dart.library.io) '../../utils/util_io.dart';
import '../../utils/utils.dart';
import '../../widgets/general/breadcrumbs.dart';
class DesktopUserProfile extends StatefulWidget {
final Key key;
const DesktopUserProfile({this.key})
: super(key: key);
@override
State<StatefulWidget> createState() {
return DesktopUserProfileState();
}
}
class DesktopUserProfileState extends State<DesktopUserProfile> {
User _user;
bool _showProgress;
double _progress;
double sideSpace = 0;
double mainSpace = 1200;
@override
Widget build(BuildContext context) {
store.dispatch(UpdateContext(context));
if (MediaQuery.of(context).size.width <= 1200) {
mainSpace = MediaQuery.of(context).size.width;
sideSpace = 0;
} else {
mainSpace = 1200;
sideSpace = (MediaQuery.of(context).size.width - 1200) / 2;
}
Widget view = SingleChildScrollView(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: sideSpace,
),
Container(
width: mainSpace,
padding: EdgeInsets.only(left: 16.0, right: 16.0, bottom: 20.0, top: 20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
padding: EdgeInsets.only(left: 16.0, right: 16.0, top: 10.0, bottom: 10.0),
child: Text(
S.of(context).basic_info,
style: TextStyle(
fontSize: 12.0,
color: Colors.grey
),
),
),
Stack(
children: <Widget>[
Positioned(
top: 0.0,
left: 0.0,
right: 0.0,
bottom: -85.0,
child: Visibility(
visible: _showProgress,
child: LinearPercentIndicator(
lineHeight: 10.0,
percent: _progress,
backgroundColor: Colors.transparent,
progressColor: Colors.blue,
linearStrokeCap: LinearStrokeCap.butt,
),
),
),
Positioned(
child: GestureDetector(
child: Container(
padding: EdgeInsets.only(left: 16.0, right: 16.0, top: 16.0, bottom: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
child: Text(
S.of(context).avatar,
style: TextStyle(
),
),
),
Container(
child: Util.showImage('https:${_user.avatarUrl}',
width: 60,
height: 60,
fit: BoxFit.fill,
errorWidget: (context, url, error) => Icon(
Icons.account_circle,
size: 60.0,
color: Colors.grey,
),
),
),
],
),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Colors.black12,
width: 1.0,
),
),
),
),
onTap: () {
_getAvatar(context);
},
),
),
],
),
GestureDetector(
child: Container(
padding: EdgeInsets.only(left: 16.0, right: 16.0, top: 16.0, bottom: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
child: Text(
S.of(context).nick_name,
style: TextStyle(
),
),
),
Container(
child: Text(
_user.nickname,
style: TextStyle(
color: Colors.grey
),
),
),
],
),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Colors.black12,
width: 1.0,
),
),
),
),
onTap: () {
_changeNickname(context);
},
),
GestureDetector(
child: Container(
padding: EdgeInsets.only(left: 16.0, right: 16.0, top: 16.0, bottom: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
child: Text(
S.of(context).my_addresses,
style: TextStyle(
),
),
),
Container(
child: Icon(
Icons.arrow_forward_ios,
size: 14.0,
color: Colors.grey,
),
),
],
),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Colors.black12,
width: 1.0,
),
),
),
),
onTap: () {
Routes.router.navigateTo(context, '/my-addresses/-1');
},
),
Container(
padding: EdgeInsets.only(left: 16.0, right: 16.0, top: 36.0, bottom: 10.0),
child: Text(
S.of(context).account_binding,
style: TextStyle(
fontSize: 12.0,
color: Colors.grey,
),
),
),
GestureDetector(
child: Container(
padding: EdgeInsets.only(left: 16.0, right: 16.0, top: 10.0, bottom: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
child: Icon(
Icons.phone_iphone,
size: 16.0,
),
),
Container(
margin: EdgeInsets.only(left: 5.0),
child: Text(
S.of(context).mobile_number,
style: TextStyle(
),
),
)
],
),
),
Container(
child: Text(
_user.mobile != null && _user.mobile.isNotEmpty ? Utils.safePhoneNumber(_user.mobile) : S.of(context).not_binding,
style: TextStyle(
color: Colors.grey,
),
),
),
],
),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Colors.black12,
width: 1.0,
),
),
),
),
onTap: () {
Routes.router.navigateTo(context, '/change-mobile-email/1');
},
),
GestureDetector(
child: Container(
padding: EdgeInsets.only(left: 16.0, right: 16.0, top: 16.0, bottom: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
child: Icon(
Icons.mail_outline,
size: 16.0,
),
),
Container(
margin: EdgeInsets.only(left: 5.0),
child: Text(
S.of(context).email,
style: TextStyle(
),
),
)
],
),
),
Container(
child: Text(
_user.email != null && _user.email.isNotEmpty ? Utils.safePhoneNumber(_user.email) : S.of(context).not_binding,
style: TextStyle(
color: Colors.grey,
),
),
),
],
),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Colors.black12,
width: 1.0,
),
),
),
),
onTap: () {
Routes.router.navigateTo(context, '/change-mobile-email/0');
},
),
],
),
),
Container(
width: sideSpace,
)
],
),
);
return view;
}
@override
void initState() {
super.initState();
if (store.state.user == null) {
Routes.router.navigateTo(context, '/login', replace: true);
} else {
print(store.state.user.toString());
setState(() {
_user = store.state.user;
_showProgress = false;
_progress = 0.0;
});
}
eventBus.on<OnCurrentUserUpdated>().listen((event) {
if (mounted) {
setState(() {
_user = store.state.user;
});
}
});
eventBus.on<OnProgressEvent>().listen((event) {
if (mounted) {
setState(() {
_showProgress = event.showProgress;
_progress = event.progress;
});
}
});
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
}
void _getAvatar(BuildContext mainContext) {
showDialog(
context: mainContext,
barrierDismissible: true,
builder: (BuildContext context) {
return Util().getPicture(mainContext, _user);
}
);
}
void _updateCurrentUser() {
store.dispatch(new UpdateCurrentUser(_user));
eventBus.fire(new OnCurrentUserUpdated());
}
void _changeNickname(BuildContext context) {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final nicknameController = TextEditingController();
nicknameController.text = _user.nickname;
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(
S.of(context).change_nickname,
),
content: Container(
child: Form(
key: _formKey,
child: TextFormField(
controller: nicknameController,
decoration: new InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black12,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.blue,
),
),
labelText: S.of(context).enter_new_nickname,
),
style: TextStyle(
fontSize: 18.0
),
autofocus: true,
validator: (String value) {
if (value.trim().isEmpty) {
return S.of(context).nickname_is_required;
}
return null;
},
),
),
),
actions: <Widget>[
FlatButton(
child: Text(
S.of(context).cancel
),
onPressed: () {
Navigator.of(context).pop();
},
),
FlatButton(
child: Text(
S.of(context).submit_to_change,
),
onPressed: () {
final FormState form = _formKey.currentState;
if (form.validate()) {
Utils.getBox().then((box) {
int userId = box.get(Constants.KEY_USER_ID, defaultValue: 0);
HttpUtil.httpPut('v1/users/$userId', (response) {
Navigator.of(context).pop();
if (mounted) {
setState(() {
_user = User.fromJson(response.data);
});
_updateCurrentUser();
}
},
body: {
'nickname': nicknameController.text
},
);
}).catchError((error){
Navigator.of(context).pop();
Routes.router.navigateTo(context, '/login');
});
}
},
)
],
);
}
);
}
}

View File

@@ -0,0 +1,194 @@
import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:photo_view/photo_view.dart';
import '../../constants.dart';
import '../../generated/l10n.dart';
import '../../models/blog.dart';
import '../../routes.dart';
import '../../store/actions.dart';
import '../../store/store.dart';
import '../../utils/http_util.dart';
import '../../utils/utils.dart';
import '../../widgets/general/breadcrumbs.dart';
class DesktopViewBlog extends StatefulWidget {
final Key key;
final int bid;
const DesktopViewBlog(this.bid, {this.key}) : super(key: key);
@override
State<StatefulWidget> createState() {
return DesktopViewBlogState();
}
}
class DesktopViewBlogState extends State<DesktopViewBlog> {
Blog blog;
double sideSpace = 0;
double mainSpace = 1200;
@override
void initState() {
super.initState();
_loadBlog();
}
void _loadBlog() {
HttpUtil.httpGet(
'v1/blog/${widget.bid}',
businessId: Constants.BUSINESS_ID,
).then((value) {
if (mounted) {
setState(() {
blog = Blog.fromJson(value);
print('blog: $blog');
});
}
}).catchError((error) {
Utils.showMessageDialog(context, error, onOk: () {
Routes.router.pop(context);
Routes.router.pop(context);
});
});
}
@override
Widget build(BuildContext context) {
store.dispatch(UpdateContext(context));
if (MediaQuery.of(context).size.width <= 1200) {
mainSpace = MediaQuery.of(context).size.width;
sideSpace = 0;
} else {
mainSpace = 1200;
sideSpace = (MediaQuery.of(context).size.width - 1200) / 2;
}
if (blog == null) {
return Container(
padding: EdgeInsets.all(50.0),
child: Center(
child: SpinKitThreeBounce(
color: Colors.lightBlueAccent,
size: 40.0,
),
),
);
}
BuildContext mainContext = context;
Column blogCol = Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
padding: EdgeInsets.only(left: 16.0, top: 16.0, right: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Expanded(
child: Text(
'${blog.title}',
style: TextStyle(
fontSize: 24.0,
color: Colors.black
),
),
),
Text(
Utils.utcDatetimeStringToLocalDatetimeString(context, blog.createdAt),
style: TextStyle(
fontSize: 14.0,
color: Colors.black38,
),
)
],
),
),
Container(
width: double.maxFinite,
padding: EdgeInsets.only(top: 8.0, left: 16.0, right: 16.0, bottom: 24.0),
child: Container(
child: Text(
'${blog.body}',
style: TextStyle(
color: Colors.black87,
fontSize: 17.0,
),
),
),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 0.5,
color: Colors.black12,
),
),
),
),
],
);
Widget view = SingleChildScrollView(
child: Row(
children: [
Container(
width: sideSpace,
),
Container(
width: mainSpace,
child: IntrinsicHeight(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: mainSpace / 2,
margin: EdgeInsets.only(top: 16.0, bottom: 16.0),
padding: EdgeInsets.all(10.0),
child: blogCol,
decoration: BoxDecoration(
border: Border(
right: BorderSide(
width: 1.0,
color: Colors.black12,
),
),
),
),
Container(
width: mainSpace / 2,
margin: EdgeInsets.only(top: 16.0, bottom: 16.0),
padding: EdgeInsets.all(10.0),
child: (blog.imageUrl == null) ?
SizedBox.shrink()
: Container(
width: mainSpace / 2 - 100.0,
height: mainSpace / 2 - 100.0,
child: PhotoView(
imageProvider: NetworkImage(
'https:${blog.imageUrl}',
),
),
),
),
],
),
),
),
Container(
width: sideSpace,
),
],
),
);
return view;
}
}

View File

@@ -0,0 +1,708 @@
import 'package:badges/badges.dart';
import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:percent_indicator/percent_indicator.dart';
import '../../constants.dart';
import '../../dialog/image_viewer.dart';
import '../../events/eventbus.dart';
import '../../events/events.dart';
import '../../generated/l10n.dart';
import '../../models/ticket.dart';
import '../../routes.dart';
import '../../store/actions.dart';
import '../../store/store.dart';
import '../../utils/http_util.dart';
import '../../utils/util_web.dart' if (dart.library.io) '../../utils/util_io.dart';
import '../../utils/utils.dart';
import '../../widgets/general/text_link.dart';
class DesktopViewTicket extends StatefulWidget {
final Key key;
final int ticketId;
const DesktopViewTicket(this.ticketId, {this.key}) : super(key: key);
@override
State<StatefulWidget> createState() {
return DesktopViewTicketState();
}
}
class DesktopViewTicketState extends State<DesktopViewTicket> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
Ticket ticket;
final issueMsgController = TextEditingController();
bool onSubmitting = false;
double submitProgress = 0.0;
final List<Map<String, dynamic>> galleryImagesFlag = [
{'show': false, 'progress': 0.0, 'path': ''},
{'show': false, 'progress': 0.0, 'path': ''},
{'show': false, 'progress': 0.0, 'path': ''},
];
double sideSpace = 0;
double mainSpace = 1200;
@override
void initState() {
super.initState();
onSubmitting = false;
_loadTicket();
}
void _loadTicket() {
HttpUtil.httpGet(
'v1/get-ticket/${widget.ticketId}',
businessId: Constants.BUSINESS_ID,
).then((value) {
if (mounted) {
setState(() {
ticket = Ticket.fromJson(value);
print('ticket: $ticket');
});
}
}).catchError((error) {
Utils.showMessageDialog(context, error, onOk: () {
Routes.router.pop(context);
Routes.router.pop(context);
});
});
}
@override
Widget build(BuildContext context) {
store.dispatch(UpdateContext(context));
if (MediaQuery.of(context).size.width <= 1200) {
mainSpace = MediaQuery.of(context).size.width;
sideSpace = 0;
} else {
mainSpace = 1200;
sideSpace = (MediaQuery.of(context).size.width - 1200) / 2;
}
if (ticket == null) {
return Container(
padding: EdgeInsets.all(50.0),
child: Center(
child: SpinKitThreeBounce(
color: Colors.lightBlueAccent,
size: 40.0,
),
),
);
}
BuildContext mainContext = context;
Widget f = Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
padding: EdgeInsets.only(top: 16.0, left: 16.0, right: 16.0, bottom: 16.0),
child: Text(
S.of(context).your_reply,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20.0,
),
),
),
Container(
padding: EdgeInsets.only(top: 16.0, left: 16.0, right: 16.0, bottom: 16.0),
child: TextFormField(
controller: issueMsgController,
keyboardType: TextInputType.multiline,
textCapitalization: TextCapitalization.sentences,
decoration: new InputDecoration(
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
width: 0.5,
color: Colors.black12,
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
width: 1.0,
color: Colors.blue,
),
),
labelText: S.of(context).your_reply,
),
style: TextStyle(
fontSize: 18.0
),
autofocus: false,
validator: (String value) {
if (value.trim().isEmpty) {
return S.of(context).this_field_is_required;
}
return null;
},
maxLines: 5,
maxLength: 1000,
),
),
Container(
padding: EdgeInsets.only(top: 16.0, left: 16.0, right: 16.0, bottom: 4.0),
child: Text(
S.of(context).attach_pictures,
style: TextStyle(
fontSize: 17.0,
color: Colors.black87,
fontWeight: FontWeight.bold,
),
),
),
Container(
padding: EdgeInsets.only(top: 4.0, left: 16.0, right: 16.0, bottom: 8.0),
child: Text(
S.of(context).attach_pictures_desc,
style: TextStyle(
fontSize: 14.0,
color: Colors.black38,
),
),
),
Container(
padding: EdgeInsets.only(top: 8.0, left: 16.0, right: 16.0, bottom: 16.0),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Container(
height: 100.0,
child: galleryImages(mainContext),
),
),
),
],
),
);
Widget s = Align(
alignment: Alignment.centerRight,
child: Container(
padding: EdgeInsets.only(top: 20.0, left: 16.0, right: 16.0, bottom: 20.0),
child: RaisedButton(
color: Theme.of(context).primaryColor,
child: Text(
S.of(context).reply,
style: TextStyle(
color: Colors.white,
),
),
onPressed: () {
_submit();
},
),
),
);
Column ticketCol = Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
padding: EdgeInsets.only(left: 16.0, top: 16.0, right: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
S.of(context).ticket_number_token(ticket.id),
style: TextStyle(
fontSize: 24.0,
color: Colors.black
),
),
Text(
Utils.utcDatetimeStringToLocalDatetimeString(context, ticket.createdAt),
style: TextStyle(
fontSize: 14.0,
color: Colors.black38,
),
)
],
),
),
Container(
width: double.maxFinite,
padding: EdgeInsets.only(top: 8.0, left: 16.0, right: 16.0, bottom: 24.0),
child: Text(
'${ticket.issue.msg}',
style: TextStyle(
color: Colors.black54,
fontSize: 14.0,
),
),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 0.5,
color: Colors.black12,
),
),
),
),
Container(
width: double.maxFinite,
padding: EdgeInsets.only(top: 16.0, bottom: 16.0, left: 16.0, right: 16.0),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: showGalleryImages(mainContext, ticket.issue.files),
),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 0.5,
color: Colors.black45,
),
),
),
),
Container(
width: double.maxFinite,
height: 10.0,
padding: EdgeInsets.only(bottom: 10.0),
decoration: BoxDecoration(
border: Border(
top: BorderSide(
width: 8,
color: Colors.black26,
),
bottom: BorderSide(
width: 0.5,
color: Colors.white70,
),
),
),
),
],
);
if (ticket.followUps.length > 0) {
ticketCol.children.add(
Container(
width: double.maxFinite,
padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 16.0),
child: Text(
S.of(context).follow_ups,
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
),
),
),
);
for (int i = 0; i < ticket.followUps.length; i++) {
FollowUp followUp = ticket.followUps[i];
ticketCol.children.add(
Container(
width: double.maxFinite,
padding: EdgeInsets.symmetric(horizontal: 8.0, vertical: 8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${followUp.poster}',
style: TextStyle(
fontSize: 14.0,
fontWeight: FontWeight.bold,
color: Colors.black38,
),
),
Text(
Utils.utcDatetimeStringToLocalDatetimeString(
context,
followUp.createdAt
),
style: TextStyle(
fontSize: 14.0,
color: Colors.black38,
),
),
],
),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 0.5,
color: Colors.black12,
),
),
),
),
Container(
width: double.maxFinite,
padding: EdgeInsets.symmetric(horizontal: 8.0, vertical: 8.0),
child: Text(
'${followUp.msg}',
style: TextStyle(
color: Colors.black87,
),
),
),
Container(
padding: EdgeInsets.only(top: 8.0, left: 8.0, right: 8.0, bottom: 20.0),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: showGalleryImages(
mainContext, followUp.files,
),
),
),
],
),
),
);
}
}
Widget view = SingleChildScrollView(
child: Row(
children: [
Container(
width: sideSpace,
),
Container(
width: mainSpace,
child: IntrinsicHeight(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: mainSpace / 2,
margin: EdgeInsets.only(top: 16.0, bottom: 16.0),
padding: EdgeInsets.all(10.0),
child: ticketCol,
decoration: BoxDecoration(
border: Border(
right: BorderSide(
width: 1.0,
color: Colors.black12,
),
),
),
),
Container(
width: mainSpace / 2,
margin: EdgeInsets.only(top: 16.0, bottom: 16.0),
padding: EdgeInsets.all(10.0),
child: (ticket.isClosed) ?
Column(
children: [
Container(
padding: EdgeInsets.symmetric(horizontal: 20.0, vertical: 20.0),
child: Text(
S.of(context).the_ticket_is_closed_desc,
style: TextStyle(
fontSize: 15.0,
color: Colors.black87,
),
),
),
Container(
padding: EdgeInsets.only(left: 20.0, right: 20.0, top: 0.0, bottom: 30.0),
child: TextLink(
S.of(context).new_ticket,
'/new-ticket/${ticket.store.id}',
),
)
],
)
: Column(
children: <Widget>[
f,
s,
],
),
),
],
),
),
),
Container(
width: sideSpace,
),
],
),
);
return view;
}
Widget showGalleryImages(BuildContext mainContext, List<TicketFile> files) {
if (files.length <= 0) {
return SizedBox.shrink();
}
Row row = Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[],
);
for (int i = 0; i < files.length; i++) {
TicketFile image = files[i];
Widget imageWidget;
imageWidget = GestureDetector(
child: Container(
margin: EdgeInsets.only(right: 20.0),
child: Util.showImage(
'https:${image.url}',
width: 80.0,
height: 80.0,
fit: BoxFit.fill
),
decoration: BoxDecoration(
border: Border(
top: BorderSide(
width: 0.5,
color: Colors.black12,
),
bottom: BorderSide(
width: 0.5,
color: Colors.black12,
),
left: BorderSide(
width: 0.5,
color: Colors.black12,
),
right: BorderSide(
width: 0.5,
color: Colors.black12,
),
),
),
),
onTap: () {
showDialog(
context: mainContext,
builder: (BuildContext context) {
return AlertDialog(
title: Text('${image.fileName}'),
content: ImageViewer(
NetworkImage(
'https:${image.url}',
),
),
actions: [
TextButton(
onPressed: () {
Routes.router.pop(context);
},
child: Text(S.of(context).close)
),
],
);
},
);
},
);
row.children.add(imageWidget);
}
return Container(
height: 100.0,
child: row,
);
}
Widget galleryImages(BuildContext mainContext) {
Row row = Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[],
);
for (int i = 0; i < 3; i++) {
Map<String, dynamic> image = galleryImagesFlag[i];
Widget imageWidget;
imageWidget = GestureDetector(
child: Stack(
alignment: Alignment.center,
children: [
Container(
margin: EdgeInsets.only(right: 20.0),
child: Util.showImage(
'${image['path']}',
width: 80.0,
height: 80.0,
fit: BoxFit.fill
),
decoration: BoxDecoration(
border: Border(
top: BorderSide(
width: 0.5,
color: Colors.black12,
),
bottom: BorderSide(
width: 0.5,
color: Colors.black12,
),
left: BorderSide(
width: 0.5,
color: Colors.black12,
),
right: BorderSide(
width: 0.5,
color: Colors.black12,
),
),
),
),
Visibility(
visible: galleryImagesFlag[i]['show'],
child: Container(
padding: EdgeInsets.only(right: 20.0),
child: CircularPercentIndicator(
radius: 60.0,
lineWidth: 10.0,
percent: galleryImagesFlag[i]['progress'],
center: new Text(
'${(galleryImagesFlag[i]['progress'] * 100.0).toStringAsFixed(1)}%',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 15.0,
color: Colors.white,
),
),
circularStrokeCap: CircularStrokeCap.round,
progressColor: Colors.orange,
),
),
),
],
),
onTap: () {
showDialog(
context: mainContext,
builder: (BuildContext context) {
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus) {
currentFocus.unfocus();
}
return Util().getPicture2(mainContext, i, (int imageId, String path){
setState(() {
galleryImagesFlag[imageId]['path'] = path;
});
});
return null;
}
);
},
);
row.children.add(Badge(
position: BadgePosition.topEnd(top: -10.0, end: 10.0),
badgeContent: Container(
child: GestureDetector(
child: Icon(
Icons.clear,
color: Colors.white,
size: 14.0,
),
onTap: () {
_deleteImage(mainContext, i);
},
),
),
showBadge: image['path'].isNotEmpty ? true : false,
child: imageWidget,
));
}
return row;
}
void _deleteImage(BuildContext mainContext, int imageId) {
showDialog(context: mainContext,
builder: (BuildContext context) {
return AlertDialog(
title: Text(S.of(context).warning),
content: Text(S.of(context).are_you_sure_to_remove_the_picture),
actions: [
FlatButton(
child: Text(S.of(context).cancel),
onPressed: () {
Navigator.of(context).pop();
},
),
RaisedButton(
child: Text(S.of(context).yes_i_am_sure),
color: Theme.of(context).primaryColor,
onPressed: () {
Navigator.of(context).pop();
setState(() {
galleryImagesFlag[imageId]['path'] = '';
});
},
),
],
);
},
);
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
eventBus.on<OnSubmitProgressEvent>().listen((event) {
if (mounted) {
setState(() {
onSubmitting = event.showProgress;
submitProgress = event.progress;
});
}
});
}
void _submit() {
final FormState form = _formKey.currentState;
if (form.validate()) {
setState(() {
onSubmitting = true;
});
Util().createTicket(context,
issueMsgController.text.trim(),
galleryImagesFlag,
(response){
showDialog(context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: Text(S.of(context).success),
content: Text(S.of(context).ticket_created_success),
actions: <Widget>[
FlatButton(
child: Text(S.of(context).ok),
onPressed: () {
Routes.router.navigateTo(context,
'/my-support/${ticket.store.id}',
replace: true,
);
},
),
],
);
});
}, (error) {
Utils.showMessageDialog(context, error, onOk: () {
Routes.router.pop(context);
Routes.router.pop(context);
});
}, id: widget.ticketId);
}
}
}