Merge branch 'master' of tzw/fcs into master

This commit is contained in:
2024-03-05 22:01:53 +06:30
committed by Gogs
18 changed files with 249 additions and 81 deletions

View File

@@ -13,6 +13,7 @@
"btn.ok": "Ok", "btn.ok": "Ok",
"btn.continue":"Continue", "btn.continue":"Continue",
"btn.previous":"Previous", "btn.previous":"Previous",
"btn.delete":"Continue to account deletion",
"feet":"Feet", "feet":"Feet",
"inch":"Inch", "inch":"Inch",
"back.button_confirm":"Are you sure you want to continue without submitting changes?", "back.button_confirm":"Are you sure you want to continue without submitting changes?",
@@ -170,6 +171,8 @@
"profile.edit.currency.title":"Preferred Currency", "profile.edit.currency.title":"Preferred Currency",
"profile.name": "Name", "profile.name": "Name",
"profile.phone": "Phone", "profile.phone": "Phone",
"profile.delete.title": "Delete Account",
"delete.confirm.label":"Are you sure to delete your account?",
"profile.language": "Languages", "profile.language": "Languages",
"profile.logout": "logout", "profile.logout": "logout",
"profile.currency":"Preferred currency", "profile.currency":"Preferred currency",

View File

@@ -12,6 +12,7 @@
"btn.ok": "အိုကေ", "btn.ok": "အိုကေ",
"btn.continue":"ဆက်ရန်", "btn.continue":"ဆက်ရန်",
"btn.previous":"နောက်သို့", "btn.previous":"နောက်သို့",
"btn.delete":"အကောင့်ဖျက်ခြင်းကို ဆက်လက်လုပ်ဆောင်မည်",
"feet":"ပေ", "feet":"ပေ",
"inch":"လက်မ", "inch":"လက်မ",
"back.button_confirm":"Are you sure you want to continue without submitting changes?", "back.button_confirm":"Are you sure you want to continue without submitting changes?",
@@ -171,6 +172,8 @@
"profile.edit.currency.title":"နှစ်သက်သော ငွေအမျိုးအစား", "profile.edit.currency.title":"နှစ်သက်သော ငွေအမျိုးအစား",
"profile.name":"နာမည်", "profile.name":"နာမည်",
"profile.phone": "ဖုန်းနံပါတ်", "profile.phone": "ဖုန်းနံပါတ်",
"profile.delete.title": "အကောင့်ဖျက်ခြင်း",
"delete.confirm.label":"သင့်အကောင့်ကို ဖျက်ရန် သေချာပြီလား?",
"profile.language": "ဘာသာစကားများ", "profile.language": "ဘာသာစကားများ",
"profile.logout": "အကောင့်ထွက်ရန်", "profile.logout": "အကောင့်ထွက်ရန်",
"profile.currency":"နှစ်သက်သော ငွေအမျိုးအစား", "profile.currency":"နှစ်သက်သော ငွေအမျိုးအစား",

View File

@@ -202,6 +202,10 @@ class AuthFb {
payload: {"preferred_currency": currency}, token: await getToken()); payload: {"preferred_currency": currency}, token: await getToken());
} }
Future<void> deleteAccount() async {
return await requestAPI("/accounts", "DELETE", token: await getToken());
}
Stream<Setting> settings() async* { Stream<Setting> settings() async* {
Stream<DocumentSnapshot> snapshot = FirebaseFirestore.instance Stream<DocumentSnapshot> snapshot = FirebaseFirestore.instance
.collection(config_collection) .collection(config_collection)

View File

@@ -69,4 +69,9 @@ class AuthServiceImp implements AuthService {
Future<void> signoutStart() { Future<void> signoutStart() {
return authFb.signoutStart(); return authFb.signoutStart();
} }
@override
Future<void> deleteAccount() {
return authFb.deleteAccount();
}
} }

View File

@@ -14,4 +14,5 @@ abstract class AuthService {
Future<bool> hasInvite(); Future<bool> hasInvite();
Stream<User?> getUserStream(); Stream<User?> getUserStream();
Stream<Setting> getSetting(); Stream<Setting> getSetting();
Future<void> deleteAccount();
} }

View File

@@ -23,6 +23,8 @@ class Setting {
final String? termsMm; final String? termsMm;
String? about; String? about;
String? courierWebsite; String? courierWebsite;
String? deactivateTextEn;
String? deactivateTextMm;
List<String> shipmentTypes; List<String> shipmentTypes;
@@ -40,7 +42,9 @@ class Setting {
this.termsMm, this.termsMm,
this.about, this.about,
this.shipmentTypes = const [], this.shipmentTypes = const [],
this.courierWebsite}); this.courierWebsite,
this.deactivateTextEn,
this.deactivateTextMm});
factory Setting.fromMap(Map<String, dynamic> map) { factory Setting.fromMap(Map<String, dynamic> map) {
return Setting( return Setting(
@@ -58,6 +62,8 @@ class Setting {
termsMm: map['terms_mm_markdown'], termsMm: map['terms_mm_markdown'],
shipmentTypes: List.from(map['shipment_types']), shipmentTypes: List.from(map['shipment_types']),
courierWebsite: map['courier_website'], courierWebsite: map['courier_website'],
deactivateTextEn: map['deactivate_text_en']??"",
deactivateTextMm: map['deactivate_text_mm']??""
); );
} }

View File

@@ -108,7 +108,7 @@ class _CartonFilterState extends State<CartonFilter> {
Future<void> _loadMoreConsignee() async { Future<void> _loadMoreConsignee() async {
if (_isLoadMoreConsignee) return; if (_isLoadMoreConsignee) return;
var model = context.read<ConsigneeSelectionModel>(); var model = context.read<ConsigneeSelectionModel>();
if (model.reachEnd || model.ended) return; if (model.ended) return;
setState(() { setState(() {
_isLoadMoreConsignee = true; _isLoadMoreConsignee = true;
}); });
@@ -141,7 +141,7 @@ class _CartonFilterState extends State<CartonFilter> {
Future<void> _loadMoreSender() async { Future<void> _loadMoreSender() async {
if (_isLoadMoreSender) return; if (_isLoadMoreSender) return;
var model = context.read<SenderSelectionModel>(); var model = context.read<SenderSelectionModel>();
if (model.reachEnd || model.ended) return; if (model.ended) return;
setState(() { setState(() {
_isLoadMoreSender = true; _isLoadMoreSender = true;
}); });

View File

@@ -1,23 +1,28 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert';
import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:fcs/config.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import '../../../domain/constants.dart'; import '../../../domain/constants.dart';
import '../../../domain/entities/user.dart'; import '../../../domain/entities/user.dart';
import '../../../helpers/api_helper.dart';
import '../../../helpers/firebase_helper.dart';
import '../../main/model/base_model.dart'; import '../../main/model/base_model.dart';
class ConsigneeSelectionModel extends BaseModel { class ConsigneeSelectionModel extends BaseModel {
final log = Logger("ConsigneeSearchModel"); final log = Logger("ConsigneeSearchModel");
// for search // for search
String query = ""; String query = "";
int offset = 0;
bool reachEnd = false;
List<User> _consignees = []; List<User> _consignees = [];
List<User> get getConsginees { List<User> get getConsginees {
var users = new List<User>.from(_consignees); var users = new List<User>.from(_consignees);
return users..insert(0, User(id: all, name: "All")); if (query == "") {
return users..insert(0, User(id: all, name: "All"));
}
return users;
} }
bool isLoading = false; bool isLoading = false;
@@ -30,8 +35,6 @@ class ConsigneeSelectionModel extends BaseModel {
search(String term, {bool imm = false}) async { search(String term, {bool imm = false}) async {
query = term; query = term;
_consignees.clear(); _consignees.clear();
offset = 0;
reachEnd = false;
t?.cancel(); t?.cancel();
t = Timer(Duration(milliseconds: imm ? 0 : 800), () async { t = Timer(Duration(milliseconds: imm ? 0 : 800), () async {
await loadMoreSearch(term: term); await loadMoreSearch(term: term);
@@ -43,35 +46,26 @@ class ConsigneeSelectionModel extends BaseModel {
await _refresh(); await _refresh();
return; return;
} }
// int rowPerPage = 21; var bytes = utf8.encode(term);
// List<Carton> list = []; var base64Str = base64.encode(bytes);
// SearchPara searchPara = SearchPara(filters: [], term: term); HtmlEscape htmlEscape = const HtmlEscape();
// isLoading = true; String escapeSender = htmlEscape.convert(base64Str);
// var path = int rowPerPage = 20;
// "/search/$cartons_collection/${searchPara.escapeTerm}/$rowPerPage/$offset/${searchPara.escapeFilters}"; List<User> list = [];
// var result = await requestAPI(path, "GET", var result = await requestAPI(
// token: await getToken(), url: Config.instance.searchURL); "/api/fts/$user_collection/$escapeSender/$rowPerPage", "GET",
url: Config.instance.reportURL, token: await getToken());
// if (result != null) { if (result != null) {
// for (var row in result) { for (var row in result) {
// var item = ArtistExt.fromMapForSearch(row); var item = User.fromJson(row);
// list.add(item); list.add(item);
// } }
// } }
// for (var p in list) { _consignees = List.from(list);
// selectedArtistList.contains(p)
// ? p.isSelected = true
// : p.isSelected = false;
// }
// artists.addAll(list);
// offset += rowPerPage;
// if (list.length < rowPerPage) {
// reachEnd = true;
// }
notifyListeners(); notifyListeners();
} }

View File

@@ -1,23 +1,28 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert';
import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import '../../../config.dart';
import '../../../domain/constants.dart'; import '../../../domain/constants.dart';
import '../../../domain/entities/user.dart'; import '../../../domain/entities/user.dart';
import '../../../helpers/api_helper.dart';
import '../../../helpers/firebase_helper.dart';
import '../../main/model/base_model.dart'; import '../../main/model/base_model.dart';
class SenderSelectionModel extends BaseModel { class SenderSelectionModel extends BaseModel {
final log = Logger("SenderSelectionModel"); final log = Logger("SenderSelectionModel");
// for search // for search
String query = ""; String query = "";
int offset = 0;
bool reachEnd = false;
List<User> _senders = []; List<User> _senders = [];
List<User> get getSenders { List<User> get getSenders {
var users = new List<User>.from(_senders); var users = new List<User>.from(_senders);
return users..insert(0, User(id: all, name: "All")); if (query == "") {
return users..insert(0, User(id: all, name: "All"));
}
return users;
} }
bool isLoading = false; bool isLoading = false;
@@ -30,8 +35,6 @@ class SenderSelectionModel extends BaseModel {
search(String term, {bool imm = false}) async { search(String term, {bool imm = false}) async {
query = term; query = term;
_senders.clear(); _senders.clear();
offset = 0;
reachEnd = false;
t?.cancel(); t?.cancel();
t = Timer(Duration(milliseconds: imm ? 0 : 800), () async { t = Timer(Duration(milliseconds: imm ? 0 : 800), () async {
await loadMoreSearch(term: term); await loadMoreSearch(term: term);
@@ -44,31 +47,28 @@ class SenderSelectionModel extends BaseModel {
return; return;
} }
// var bytes = utf8.encode(term); var bytes = utf8.encode(term);
// var base64Str = base64.encode(bytes); var base64Str = base64.encode(bytes);
// HtmlEscape htmlEscape = const HtmlEscape(); HtmlEscape htmlEscape = const HtmlEscape();
// String escapeSender = htmlEscape.convert(base64Str); String escapeSender = htmlEscape.convert(base64Str);
// int rowPerPage = 20; int rowPerPage = 20;
// List<User> list = []; List<User> list = [];
// var result = await requestAPI( var result = await requestAPI(
// "/api/fts/$user_collection/$escapeSender/$rowPerPage", "GET", "/api/fts/$user_collection/$escapeSender/$rowPerPage", "GET",
// url: Config.instance.reportURL, token: await getToken()); url: Config.instance.reportURL, token: await getToken());
// if (result != null) { if (result != null) {
// for (var row in result) { for (var row in result) {
// var item = User.fromJson(row); var item = User.fromJson(row);
// list.add(item); list.add(item);
// } }
// } }
// _senders.addAll(list); _senders = List.from(list);
// offset += rowPerPage;
// if (list.length < rowPerPage) { notifyListeners();
// reachEnd = true;
// }
// notifyListeners();
} }
addDefaultSenders() async { addDefaultSenders() async {

View File

@@ -1,8 +1,10 @@
import 'package:fcs/domain/entities/carton.dart'; import 'package:fcs/domain/entities/carton.dart';
import 'package:fcs/helpers/theme.dart'; import 'package:fcs/helpers/theme.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_vector_icons/flutter_vector_icons.dart'; import 'package:flutter_vector_icons/flutter_vector_icons.dart';
import '../carton/print_qr_code_page.dart';
import 'carton_search.dart'; import 'carton_search.dart';
class CartonListRow extends StatelessWidget { class CartonListRow extends StatelessWidget {
@@ -69,7 +71,14 @@ class CartonListRow extends StatelessWidget {
), ),
const SizedBox(width: 15), const SizedBox(width: 15),
IconButton( IconButton(
onPressed: () {}, onPressed: () {
Navigator.push(
context,
CupertinoPageRoute(
builder: (context) =>
PrintQrCodePage(carton: carton)),
);
},
icon: Icon(AntDesign.qrcode, icon: Icon(AntDesign.qrcode,
color: Colors.black)) color: Colors.black))
], ],

View File

@@ -81,6 +81,7 @@ class PartSearchDelegate extends SearchDelegate<Carton> {
child: Text( child: Text(
"No result found", "No result found",
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle(color: Colors.black),
), ),
), ),
); );

View File

@@ -62,6 +62,7 @@ class _CartonSizeEditorState extends State<CartonSizeEditor> {
final lengthBox = LengthPicker( final lengthBox = LengthPicker(
controller: _lengthController, controller: _lengthController,
lableKey: "box.length", lableKey: "box.length",
displayFeet: true,
); );
final widthBox = LengthPicker( final widthBox = LengthPicker(
controller: _widthController, controller: _widthController,

View File

@@ -192,4 +192,8 @@ class MainModel extends ChangeNotifier {
await Services.instance.authService.updatePreferredCurrency(currency); await Services.instance.authService.updatePreferredCurrency(currency);
notifyListeners(); notifyListeners();
} }
Future<void> deleteAccount() async {
return await Services.instance.authService.deleteAccount();
}
} }

View File

@@ -80,6 +80,7 @@ class PackageSearchDelegate extends SearchDelegate<Package> {
child: Text( child: Text(
"No result found", "No result found",
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle(color: Colors.black),
), ),
), ),
); );

View File

@@ -81,6 +81,7 @@ class PackageSearchDelegate extends SearchDelegate<Pickup> {
child: Text( child: Text(
"No result found", "No result found",
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle(color: Colors.black),
), ),
), ),
); );

View File

@@ -0,0 +1,105 @@
import 'package:fcs/domain/entities/setting.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/pages/main/model/main_model.dart';
import 'package:fcs/pages/widgets/local_app_bar.dart';
import 'package:fcs/pages/widgets/progress.dart';
import 'package:flutter/material.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:provider/provider.dart';
import '../main/model/language_model.dart';
import '../main/util.dart';
import '../widgets/local_text.dart';
class AccountDelectionPage extends StatefulWidget {
final Function onlogout;
const AccountDelectionPage({Key? key, required this.onlogout})
: super(key: key);
@override
_AccountDelectionPageState createState() => _AccountDelectionPageState();
}
class _AccountDelectionPageState extends State<AccountDelectionPage> {
bool _isLoading = false;
@override
Widget build(BuildContext context) {
bool isEng = Provider.of<LanguageModel>(context).isEng;
Setting? setting = Provider.of<MainModel>(context).setting;
String? text = isEng
? (setting!.deactivateTextEn ?? "")
: (setting!.deactivateTextMm ?? "");
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: LocalAppBar(
labelKey: 'profile.delete.title',
backgroundColor: Colors.white,
labelColor: primaryColor,
arrowColor: primaryColor,
),
body: SafeArea(
child: ScrollConfiguration(
behavior: const ScrollBehavior().copyWith(overscroll: false),
child: ListView(
padding: const EdgeInsets.all(10),
children: [
Markdown(
shrinkWrap: true,
softLineBreak: true,
physics: const BouncingScrollPhysics(),
data: (text).replaceAll("\\n", '\n'),
styleSheet: MarkdownStyleSheet.fromTheme(ThemeData(
textTheme: TextTheme(
bodyMedium: TextStyle(
fontSize: isEng ? 15 : 14,
color: Colors.black87))))),
const SizedBox(
height: 50,
),
Container(
padding: const EdgeInsets.all(20),
child: TextButton(
style: TextButton.styleFrom(
foregroundColor: buttonColor,
backgroundColor: primaryColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30.0),
)),
onPressed: () {
showConfirmDialog(context, 'delete.confirm.label',
() {
_deactivate();
});
},
child: LocalText(context, "btn.delete",
fontSize: 15, color: buttonColor))),
],
),
),
),
),
);
}
_deactivate() async {
try {
setState(() {
_isLoading = true;
});
// await context.read<MainModel>().deleteAccount();
// await widget.onlogout();
Navigator.pop(context, true);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
if (mounted) {
setState(() {
_isLoading = false;
});
}
}
}
}

View File

@@ -24,6 +24,8 @@ import 'package:flutter_vector_icons/flutter_vector_icons.dart';
import '../../helpers/theme.dart'; import '../../helpers/theme.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'account_delection_page.dart';
typedef void ProfileCallback(); typedef void ProfileCallback();
class Profile extends StatefulWidget { class Profile extends StatefulWidget {
@@ -82,6 +84,10 @@ class _ProfileState extends State<Profile> {
labelTextKey: "profile.currency", labelTextKey: "profile.currency",
iconData: FontAwesome5Regular.money_bill_alt, iconData: FontAwesome5Regular.money_bill_alt,
); );
final deleteAccountBox = DisplayText(
labelTextKey: "profile.delete.title",
iconData: MaterialCommunityIcons.delete,
);
final phonenumberbox = DisplayText( final phonenumberbox = DisplayText(
text: mainModel.user!.phone, text: mainModel.user!.phone,
@@ -124,8 +130,11 @@ class _ProfileState extends State<Profile> {
); );
final logoutbutton = fcsButton( final logoutbutton = fcsButton(
context, getLocalString(context, "profile.logout"), context, getLocalString(context, "profile.logout"), callack: () {
callack: _logout, iconData: Icons.exit_to_app); showConfirmDialog(context, "profile.logout.confirm", () async {
await _logout();
});
}, iconData: Icons.exit_to_app);
return LocalProgress( return LocalProgress(
inAsyncCall: _isLoading, inAsyncCall: _isLoading,
@@ -181,6 +190,19 @@ class _ProfileState extends State<Profile> {
iconData: Icons.language, iconData: Icons.language,
isEng: languageModel.isEng, isEng: languageModel.isEng,
), ),
Row(
children: <Widget>[
Expanded(
child: deleteAccountBox,
),
Padding(
padding: const EdgeInsets.only(right: 0),
child: IconButton(
icon: Icon(Icons.edit, color: Colors.grey),
onPressed: _editDelete),
)
],
),
getPrivilegeBox(context), getPrivilegeBox(context),
SizedBox(height: 15), SizedBox(height: 15),
logoutbutton, logoutbutton,
@@ -344,6 +366,15 @@ class _ProfileState extends State<Profile> {
); );
} }
_editDelete() {
Navigator.of(context).push<void>(CupertinoPageRoute(
builder: (context) => AccountDelectionPage(
onlogout: () {
_logout();
},
)));
}
_editName() { _editName() {
Navigator.of(context) Navigator.of(context)
.push<void>(CupertinoPageRoute(builder: (context) => ProfileEdit())); .push<void>(CupertinoPageRoute(builder: (context) => ProfileEdit()));
@@ -354,25 +385,23 @@ class _ProfileState extends State<Profile> {
CupertinoPageRoute(builder: (context) => ProfileCurrencyEdit())); CupertinoPageRoute(builder: (context) => ProfileCurrencyEdit()));
} }
_logout() { _logout() async {
showConfirmDialog(context, "profile.logout.confirm", () async { setState(() {
setState(() { _isLoading = true;
_isLoading = true;
});
try {
await context.read<MainModel>().signout();
} catch (e) {
} finally {
Future.delayed(Duration(seconds: 1), () {
if (mounted) {
setState(() {
_isLoading = false;
});
}
});
Navigator.of(context).pushNamedAndRemoveUntil(
"/welcome", ModalRoute.withName('/welcome'));
}
}); });
try {
await context.read<MainModel>().signout();
} catch (e) {
} finally {
Future.delayed(Duration(seconds: 1), () {
if (mounted) {
setState(() {
_isLoading = false;
});
}
});
Navigator.of(context)
.pushNamedAndRemoveUntil("/welcome", ModalRoute.withName('/welcome'));
}
} }
} }

View File

@@ -79,6 +79,7 @@ class UserSearchDelegate extends SearchDelegate<User> {
child: Text( child: Text(
"No result found", "No result found",
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle(color:Colors.black),
), ),
), ),
); );