update staff list, add pin editor and privilege editor

This commit is contained in:
tzw
2024-02-14 16:58:45 +06:30
parent 106ddded29
commit 13e6e232d5
20 changed files with 770 additions and 113 deletions

View File

@@ -106,4 +106,29 @@ class StaffModel extends BaseModel {
}
return users;
}
Future<User?> getUser(String userID) async {
if (userID == "") return null;
String path = "/$user_collection";
try {
var snap = await FirebaseFirestore.instance
.collection(path)
.doc(userID)
.get(const GetOptions(source: Source.server));
if (snap.data() == null) return null;
Map<String, dynamic>? data = snap.data() as Map<String, dynamic>;
if (data['delete_time'] == 0) {
User user = User.fromMap(data, snap.id);
return user;
} else {
return null;
}
} catch (e) {
log.warning("Error!! $e");
}
return null;
}
}

View File

@@ -72,7 +72,7 @@ class _StaffEditorState extends State<StaffEditor> {
}),
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: Icon(p.iconData, size: 50, color: Colors.black38),
child: Icon(p.iconData, size: 30, color: Colors.black38),
),
Expanded(
child: Column(
@@ -80,10 +80,10 @@ class _StaffEditorState extends State<StaffEditor> {
children: <Widget>[
new Text(
p.name ?? "",
style: TextStyle(fontSize: 15.0, color: primaryColor),
style: TextStyle(fontSize: 15.0, color: Colors.black),
),
Text(p.desc ?? "",
style: TextStyle(fontSize: 13, color: Colors.grey[600]))
style: TextStyle(fontSize: 13, color: labelColor))
],
),
),
@@ -155,15 +155,22 @@ class _StaffEditorState extends State<StaffEditor> {
],
);
final updateButton = fcsButton(
context,
getLocalString(context, 'staff.update'),
callack: _save,
final updateButton = Padding(
padding: const EdgeInsets.symmetric(horizontal: 30),
child: fcsButton(
context,
getLocalString(context, 'staff.update'),
callack: _save,
),
);
final addButton = fcsButton(
context,
getLocalString(context, 'staff.add'),
callack: _add,
final addButton = Padding(
padding: const EdgeInsets.symmetric(horizontal: 30),
child: fcsButton(
context,
getLocalString(context, 'staff.add'),
callack: _add,
),
);
return LocalProgress(
@@ -196,9 +203,7 @@ class _StaffEditorState extends State<StaffEditor> {
Container(
child: isNew ? addButton : updateButton,
),
SizedBox(
height: 10,
)
SizedBox(height: 20)
],
),
),

View File

@@ -0,0 +1,203 @@
import 'package:fcs/domain/entities/user.dart';
import 'package:fcs/domain/vo/privilege.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/pages/main/util.dart';
import 'package:fcs/pages/staff/model/staff_model.dart';
import 'package:fcs/pages/widgets/display_text.dart';
import 'package:fcs/pages/widgets/fcs_id_icon.dart';
import 'package:fcs/pages/widgets/local_app_bar.dart';
import 'package:fcs/pages/widgets/local_text.dart';
import 'package:fcs/pages/widgets/progress.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_vector_icons/flutter_vector_icons.dart';
import 'package:provider/provider.dart';
import 'staff_pin_editor.dart';
import 'staff_privilege_editor.dart';
class StaffInfo extends StatefulWidget {
final User staff;
const StaffInfo({required this.staff});
@override
_StaffInfoState createState() => _StaffInfoState();
}
class _StaffInfoState extends State<StaffInfo> {
bool _isLoading = false;
late User _staff;
List<Privilege> _privileges = [];
@override
void initState() {
_staff = widget.staff;
_init();
super.initState();
}
_init() {
List<Privilege> list =
Provider.of<StaffModel>(context, listen: false).privileges;
list.forEach((p) => _staff.privileges.contains(p.id)
? p.isChecked = true
: p.isChecked = false);
_privileges = list.where((e) => e.isChecked ?? false).toList();
if (mounted) {
setState(() {});
}
}
List<Widget> showprivilegeList(BuildContext context) {
return _privileges.map((p) {
return new ListTile(
title: new Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: Icon(p.iconData, size: 30, color: Colors.black38),
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Text(
p.name ?? "",
style: TextStyle(fontSize: 15.0, color: Colors.black),
),
Text(p.desc ?? "",
style: TextStyle(fontSize: 13, color: labelColor))
],
),
),
],
));
}).toList();
}
@override
Widget build(BuildContext context) {
final fcsIdBox = DisplayText(
text: _staff.fcsID,
labelTextKey: "customer.fcs.id",
icon: FcsIDIcon(),
);
final namebox = DisplayText(
text: _staff.name,
labelTextKey: "customer.name",
iconData: Icons.person,
);
final phoneNumberBox = Row(
children: <Widget>[
Expanded(
child: DisplayText(
text: _staff.phoneNumber,
labelTextKey: "customer.phone",
iconData: Icons.phone,
)),
IconButton(
icon: Icon(Icons.open_in_new, color: primaryColor),
onPressed: () => call(context, _staff.phoneNumber!)),
],
);
final pinBox = Row(
children: <Widget>[
Expanded(
child: DisplayText(
text: _staff.enablePinLogin ? "Enabled" : "Disabled",
labelTextKey: "staff.pin_login",
iconData: MaterialCommunityIcons.lock)),
SizedBox(
width: 65,
height: 35,
child: OutlinedButton(
style: OutlinedButton.styleFrom(
padding: EdgeInsets.all(5),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0)),
side: BorderSide(color: primaryColor),
),
onPressed: () async {
bool? updated = await Navigator.of(context).push(
CupertinoPageRoute(
builder: (context) => StaffPinEditor(staff: _staff)));
if (updated ?? false) {
User? _u =
await context.read<StaffModel>().getUser(widget.staff.id!);
if (_u == null) return;
_staff = _u;
_init();
}
},
child: LocalText(context, 'staff.edit_pin',
color: primaryColor, fontSize: 13),
),
)
],
);
final privilegeTitleBox = Row(
children: [
Expanded(
child: LocalText(context, 'staff.privilege.title',
color: Colors.black54, fontSize: 15)),
SizedBox(
width: 100,
height: 35,
child: OutlinedButton(
style: OutlinedButton.styleFrom(
padding: EdgeInsets.all(5),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0)),
side: BorderSide(color: primaryColor),
),
onPressed: () async {
bool? updated = await Navigator.of(context).push(
CupertinoPageRoute(
builder: (context) =>
StaffPrivilegeEditor(staff: _staff)));
if (updated ?? false) {
User? _u =
await context.read<StaffModel>().getUser(widget.staff.id!);
if (_u == null) return;
_staff = _u;
_init();
}
},
child: LocalText(context, 'staff.edit_privileges',
color: primaryColor, fontSize: 13),
),
)
],
);
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: LocalAppBar(
labelKey: "staff.form.title",
backgroundColor: Colors.white,
labelColor: primaryColor,
arrowColor: primaryColor),
body: Padding(
padding: const EdgeInsets.only(left: 12.0, right: 12),
child: ListView(
children: <Widget>[
fcsIdBox,
phoneNumberBox,
namebox,
pinBox,
const SizedBox(height: 10),
privilegeTitleBox,
Column(children: showprivilegeList(context)),
SizedBox(height: 30)
],
),
),
));
}
}

View File

@@ -12,6 +12,7 @@ import 'package:provider/provider.dart';
import '../../pagination/paginator_listview.dart';
import 'staff_editor.dart';
import 'staff_info.dart';
class StaffList extends StatefulWidget {
@override
@@ -57,23 +58,19 @@ class _StaffListState extends State<StaffList> {
return InkWell(
onTap: () {
Navigator.of(context).push(
CupertinoPageRoute(builder: (context) => StaffEditor(staff: user)));
CupertinoPageRoute(builder: (context) => StaffInfo(staff: user)));
},
child: Row(
children: <Widget>[
Expanded(
child: new Padding(
padding: const EdgeInsets.symmetric(vertical: 10.0),
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: new Row(
children: <Widget>[
new Padding(
padding: new EdgeInsets.symmetric(
horizontal: 32.0 - dotSize / 2),
child: Icon(
MaterialCommunityIcons.account_hard_hat,
color: primaryColor,
size: 40,
),
padding: new EdgeInsets.symmetric(horizontal: 15),
child: Icon(MaterialCommunityIcons.account_hard_hat,
color: primaryColor, size: 30),
),
new Expanded(
child: new Column(
@@ -84,7 +81,15 @@ class _StaffListState extends State<StaffList> {
style: new TextStyle(fontSize: 15.0),
),
Padding(
padding: const EdgeInsets.only(top: 8.0),
padding: const EdgeInsets.only(top: 2),
child: new Text(
user.fcsID ?? "",
style: new TextStyle(
fontSize: 15.0, color: Colors.grey),
),
),
Padding(
padding: const EdgeInsets.only(top: 2),
child: new Text(
user.phoneNumber ?? "",
style: new TextStyle(

View File

@@ -0,0 +1,192 @@
import 'package:fcs/domain/entities/user.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/pages/widgets/local_app_bar.dart';
import 'package:fcs/pages/widgets/progress.dart';
import 'package:flutter/material.dart';
import 'package:pin_input_text_field/pin_input_text_field.dart';
import '../main/util.dart';
import '../widgets/local_text.dart';
class StaffPinEditor extends StatefulWidget {
final User staff;
const StaffPinEditor({required this.staff});
@override
_StaffPinEditorState createState() => _StaffPinEditorState();
}
class _StaffPinEditorState extends State<StaffPinEditor> {
bool _isLoading = false;
late User _staff;
bool _enablePinLogin = false;
String _newPin = "";
String _confirmPin = "";
@override
void initState() {
_staff = widget.staff;
_enablePinLogin = _staff.enablePinLogin;
_newPin = _staff.pinDigit ?? "";
_confirmPin = _staff.confirmPinDigit ?? "";
super.initState();
}
@override
Widget build(BuildContext context) {
final enablePinBox = Row(
children: [
Checkbox(
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
visualDensity: const VisualDensity(
horizontal: VisualDensity.minimumDensity,
vertical: VisualDensity.minimumDensity),
activeColor: primaryColor,
side: BorderSide(color: Colors.black38, width: 2),
value: _enablePinLogin,
onChanged: (value) {
setState(() {
_enablePinLogin = value ?? false;
});
},
),
const SizedBox(width: 15),
LocalText(context, 'staff.enable_pin',
fontSize: 15, color: Colors.black)
],
);
final newPinBox = Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocalText(context, 'staff.new_pin',
color: Colors.black54, fontSize: 15),
const SizedBox(height: 8),
PinInputTextField(
cursor: Cursor(
color: primaryColor, enabled: true, width: 2, height: 30),
pinLength: 6,
decoration: BoxLooseDecoration(
strokeColorBuilder:
PinListenColorBuilder(primaryColor, Colors.grey.shade400)),
textInputAction: TextInputAction.done,
autoFocus: true,
onChanged: _newPinChange),
],
);
final confirmPinBox = Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocalText(context, 'staff.confirm_pin',
color: Colors.black54, fontSize: 15),
const SizedBox(height: 8),
PinInputTextField(
pinLength: 6,
cursor: Cursor(
color: primaryColor, enabled: true, width: 2, height: 30),
decoration: BoxLooseDecoration(
strokeColorBuilder:
PinListenColorBuilder(primaryColor, Colors.grey.shade400)),
textInputAction: TextInputAction.done,
autoFocus: false,
onChanged: _confirmPinChange),
],
);
final saveButton = Padding(
padding: const EdgeInsets.symmetric(horizontal: 30),
child: fcsButton(
context,
getLocalString(context, 'btn.save'),
callack: _save,
),
);
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: LocalAppBar(
labelKey: "staff.pin.title",
backgroundColor: Colors.white,
labelColor: primaryColor,
arrowColor: primaryColor),
body: Padding(
padding: const EdgeInsets.only(left: 15.0, right: 15),
child: ListView(
children: <Widget>[
Column(
children: [
Text(_staff.name ?? "",
style: TextStyle(color: Colors.black, fontSize: 18)),
Text(_staff.fcsID ?? "",
style: TextStyle(color: Colors.black, fontSize: 15)),
],
),
const SizedBox(height: 20),
enablePinBox,
const SizedBox(height: 30),
newPinBox,
SizedBox(height: 30),
confirmPinBox,
SizedBox(height: 30),
saveButton,
SizedBox(height: 30)
],
),
),
));
}
_newPinChange(pin) {
setState(() {
this._newPin = pin;
});
}
_confirmPinChange(pin) {
setState(() {
this._confirmPin = pin;
});
}
_save() async {
if (_newPin == "") {
showMsgDialog(context, "Error", "Invalid new PIN");
return;
}
if (_newPin.length < 6) {
showMsgDialog(context, "Error", "New PIN must be 6 digits");
return;
}
if (_confirmPin == "") {
showMsgDialog(context, "Error", "Invalid confirm PIN");
return;
}
if (_confirmPin.length < 6) {
showMsgDialog(context, "Error", "Confirm PIN must be 6 digits");
return;
}
if (_confirmPin != _newPin) {
showMsgDialog(context, "Error", "Those pins didnt match. Try again.");
return;
}
setState(() {
_isLoading = true;
});
try {
Navigator.pop(context, true);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
}

View File

@@ -0,0 +1,149 @@
import 'package:fcs/domain/entities/user.dart';
import 'package:fcs/domain/vo/privilege.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/pages/staff/model/staff_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:provider/provider.dart';
import '../main/util.dart';
import '../widgets/local_text.dart';
class StaffPrivilegeEditor extends StatefulWidget {
final User staff;
const StaffPrivilegeEditor({required this.staff});
@override
_StaffPrivilegeEditorState createState() => _StaffPrivilegeEditorState();
}
class _StaffPrivilegeEditorState extends State<StaffPrivilegeEditor> {
bool _isLoading = false;
late User _staff;
List<Privilege> _privileges = [];
@override
void initState() {
_staff = widget.staff;
_privileges = Provider.of<StaffModel>(context, listen: false).privileges;
_privileges.forEach((p) => _staff.privileges.contains(p.id)
? p.isChecked = true
: p.isChecked = false);
super.initState();
}
List<Widget> showprivilegeList(BuildContext context) {
return _privileges.map((p) {
return new ListTile(
title: InkWell(
onTap: () {
setState(() {
p.isChecked = p.isChecked == null ? true : !p.isChecked!;
});
},
child: new Row(
children: <Widget>[
new Checkbox(
value: p.isChecked == null ? false : p.isChecked,
activeColor: primaryColor,
side: BorderSide(color: Colors.black38, width: 2),
onChanged: (bool? value) {
if (value != null)
setState(() {
p.isChecked = value;
});
}),
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: Icon(p.iconData, size: 30, color: Colors.black38),
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Text(
p.name ?? "",
style: TextStyle(fontSize: 15.0, color: Colors.black),
),
Text(p.desc ?? "",
style: TextStyle(fontSize: 13, color: labelColor))
],
),
),
],
),
));
}).toList();
}
@override
Widget build(BuildContext context) {
final saveButton = Padding(
padding: const EdgeInsets.symmetric(horizontal: 30),
child: fcsButton(
context,
getLocalString(context, 'btn.save'),
callack: _save,
),
);
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: LocalAppBar(
labelKey: "staff.privilege.title",
backgroundColor: Colors.white,
labelColor: primaryColor,
arrowColor: primaryColor),
body: Padding(
padding: const EdgeInsets.only(left: 12.0, right: 12),
child: ListView(
children: <Widget>[
Column(
children: [
Text(_staff.name ?? "",
style: TextStyle(color: Colors.black, fontSize: 18)),
Text(_staff.fcsID ?? "",
style: TextStyle(color: Colors.black, fontSize: 15)),
],
),
const SizedBox(height: 10),
LocalText(context, 'staff.privilege.title',
color: Colors.black54, fontSize: 15),
Column(
children: showprivilegeList(context),
),
saveButton,
SizedBox(height: 20)
],
),
),
));
}
List<String> privilegesIDs() {
return this
._privileges
.where((p) => p.isChecked!)
.map((p) => p.id)
.toList();
}
_save() async {
setState(() {
_isLoading = true;
});
StaffModel staffModel = Provider.of<StaffModel>(context, listen: false);
try {
await staffModel.updatePrivileges(widget.staff.id!, privilegesIDs());
Navigator.pop(context, true);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
}