Skip to content

Commit 449e182

Browse files
authored
add way to edit team email (#125)
* add way to edit team email * fix issues or whatever * improve team email changing
1 parent cb92c94 commit 449e182

3 files changed

Lines changed: 268 additions & 11 deletions

File tree

lib/pages/settings.dart

Lines changed: 251 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import 'package:scouting_dashboard_app/reusable/emphasized_container.dart';
1111
import 'package:scouting_dashboard_app/reusable/friendly_error_view.dart';
1212
import 'package:scouting_dashboard_app/reusable/inset_picker.dart';
1313
import 'package:scouting_dashboard_app/reusable/lovat_api/delete_account.dart';
14+
import 'package:scouting_dashboard_app/reusable/lovat_api/edit_team_email.dart';
1415
import 'package:scouting_dashboard_app/reusable/lovat_api/get_analysts.dart';
1516
import 'package:scouting_dashboard_app/reusable/lovat_api/get_csv_export.dart';
1617
import 'package:scouting_dashboard_app/reusable/lovat_api/get_team_code.dart';
@@ -102,6 +103,8 @@ class _SettingsPageState extends State<SettingsPage> {
102103
label: const Text("Export CSV"),
103104
),
104105
],
106+
if (cachedUserProfile?.role == UserRole.scoutingLead)
107+
const EmailBox(),
105108
const AnalystsBox(),
106109
const SizedBox(height: 40),
107110
const ResetAppButton(),
@@ -190,7 +193,7 @@ class _TeamSourceSelectorState extends State<TeamSourceSelector> {
190193
bool thisTeamLoaded = false;
191194

192195
bool get isLoading => mode == null || !thisTeamLoaded;
193-
String? errorMesssage;
196+
String? errorMessage;
194197

195198
Future<void> load() async {
196199
try {
@@ -202,7 +205,7 @@ class _TeamSourceSelectorState extends State<TeamSourceSelector> {
202205
});
203206
} catch (e) {
204207
setState(() {
205-
errorMesssage = "Failed to load source teams";
208+
errorMessage = "Failed to load source teams";
206209
});
207210
}
208211

@@ -216,7 +219,7 @@ class _TeamSourceSelectorState extends State<TeamSourceSelector> {
216219
});
217220
} catch (e) {
218221
setState(() {
219-
errorMesssage = "Failed to load profile";
222+
errorMessage = "Failed to load profile";
220223
});
221224
}
222225
}
@@ -229,7 +232,7 @@ class _TeamSourceSelectorState extends State<TeamSourceSelector> {
229232

230233
@override
231234
Widget build(BuildContext context) {
232-
if (isLoading && errorMesssage == null) {
235+
if (isLoading && errorMessage == null) {
233236
return const SkeletonAvatar(
234237
style: SkeletonAvatarStyle(
235238
width: 200,
@@ -239,15 +242,15 @@ class _TeamSourceSelectorState extends State<TeamSourceSelector> {
239242
);
240243
}
241244

242-
if (isLoading && errorMesssage != null) {
245+
if (isLoading && errorMessage != null) {
243246
return Container(
244247
decoration: BoxDecoration(
245248
color: Theme.of(context).colorScheme.surfaceContainerHighest,
246249
borderRadius: BorderRadius.circular(50),
247250
),
248251
height: 48,
249252
child: Center(
250-
child: MediumErrorMessage(message: errorMesssage),
253+
child: MediumErrorMessage(message: errorMessage),
251254
),
252255
);
253256
}
@@ -293,7 +296,7 @@ class _TeamSourceSelectorState extends State<TeamSourceSelector> {
293296
await load();
294297
} catch (e) {
295298
setState(() {
296-
errorMesssage = "Failed to save source team settings";
299+
errorMessage = "Failed to save source team settings";
297300
});
298301
}
299302
})();
@@ -323,7 +326,7 @@ class _TeamSourceSelectorState extends State<TeamSourceSelector> {
323326
);
324327
} catch (e) {
325328
setState(() {
326-
errorMesssage = "Failed to save source team settings";
329+
errorMessage = "Failed to save source team settings";
327330
});
328331
}
329332
},
@@ -332,10 +335,10 @@ class _TeamSourceSelectorState extends State<TeamSourceSelector> {
332335
}
333336
},
334337
),
335-
if (errorMesssage != null)
338+
if (errorMessage != null)
336339
Padding(
337340
padding: const EdgeInsets.only(top: 10),
338-
child: MediumErrorMessage(message: errorMesssage),
341+
child: MediumErrorMessage(message: errorMessage),
339342
),
340343
],
341344
);
@@ -888,6 +891,136 @@ class _AnalystsBoxState extends State<AnalystsBox> {
888891
}
889892
}
890893

894+
class EmailBox extends StatefulWidget {
895+
const EmailBox({super.key});
896+
897+
@override
898+
State<EmailBox> createState() => _EmailBoxState();
899+
}
900+
901+
class _EmailBoxState extends State<EmailBox> {
902+
String? email;
903+
bool loaded = false;
904+
String? errorMessage;
905+
906+
Future<void> load() async {
907+
try {
908+
final email = await lovatAPI.getTeamEmail();
909+
910+
setState(() {
911+
this.email = email;
912+
});
913+
} catch (e) {
914+
setState(() {
915+
errorMessage = "Failed to load email";
916+
});
917+
} finally {
918+
setState(() {
919+
loaded = true;
920+
});
921+
}
922+
}
923+
924+
@override
925+
void initState() {
926+
super.initState();
927+
load();
928+
}
929+
930+
@override
931+
Widget build(BuildContext context) {
932+
if (!loaded && errorMessage == null) {
933+
return Column(
934+
crossAxisAlignment: CrossAxisAlignment.stretch,
935+
children: [
936+
const SizedBox(height: 28),
937+
Text(
938+
"Team email",
939+
style: Theme.of(context).textTheme.labelLarge,
940+
),
941+
const SizedBox(height: 7),
942+
const SkeletonAvatar(
943+
style: SkeletonAvatarStyle(
944+
width: 200,
945+
height: 60,
946+
borderRadius: BorderRadius.all(Radius.circular(4)),
947+
),
948+
),
949+
],
950+
);
951+
}
952+
953+
if (!loaded && errorMessage != null) {
954+
return Container(
955+
decoration: BoxDecoration(
956+
color: Theme.of(context).colorScheme.surfaceContainerHighest,
957+
borderRadius: BorderRadius.circular(4),
958+
),
959+
height: 60,
960+
child: Center(
961+
child: MediumErrorMessage(message: errorMessage),
962+
),
963+
);
964+
}
965+
966+
if (loaded && email == null) {
967+
return Container();
968+
}
969+
970+
return Column(
971+
crossAxisAlignment: CrossAxisAlignment.stretch,
972+
children: [
973+
const SizedBox(height: 28),
974+
Text(
975+
"Team email",
976+
style: Theme.of(context).textTheme.labelLarge,
977+
),
978+
const SizedBox(height: 7),
979+
Container(
980+
height: 60,
981+
decoration: BoxDecoration(
982+
color: Theme.of(context).colorScheme.surfaceContainerHighest,
983+
borderRadius: BorderRadius.circular(4),
984+
),
985+
child: Padding(
986+
padding: const EdgeInsets.all(12),
987+
child: Row(
988+
mainAxisAlignment: MainAxisAlignment.spaceBetween,
989+
children: [
990+
Flexible(
991+
child: Text(
992+
email!,
993+
style: TextStyle(
994+
color: Theme.of(context).colorScheme.onSurfaceVariant,
995+
),
996+
maxLines: 1,
997+
overflow: TextOverflow.ellipsis,
998+
),
999+
),
1000+
TextButton(
1001+
onPressed: () {
1002+
showDialog(
1003+
context: context,
1004+
barrierDismissible: false,
1005+
builder: (context) => ChangeEmailDialog(onAdd: load),
1006+
);
1007+
},
1008+
child: const Text("Change"),
1009+
),
1010+
],
1011+
),
1012+
),
1013+
),
1014+
if (errorMessage != null)
1015+
Padding(
1016+
padding: const EdgeInsets.only(top: 10),
1017+
child: MediumErrorMessage(message: errorMessage),
1018+
),
1019+
],
1020+
);
1021+
}
1022+
}
1023+
8911024
class AnalystPromotionPage extends StatefulWidget {
8921025
const AnalystPromotionPage({super.key, this.onSubmit});
8931026

@@ -1375,3 +1508,111 @@ class _DataExportPageState extends State<DataExportPage> {
13751508
);
13761509
}
13771510
}
1511+
1512+
class ChangeEmailDialog extends StatefulWidget {
1513+
const ChangeEmailDialog({
1514+
super.key,
1515+
this.onAdd,
1516+
});
1517+
1518+
final Function()? onAdd;
1519+
1520+
@override
1521+
State<ChangeEmailDialog> createState() => _ChangeEmailDialogState();
1522+
}
1523+
1524+
class _ChangeEmailDialogState extends State<ChangeEmailDialog> {
1525+
String email = '';
1526+
bool submitting = false;
1527+
bool submitted = false;
1528+
String? error;
1529+
1530+
@override
1531+
Widget build(BuildContext context) {
1532+
if (submitted) {
1533+
return AlertDialog(
1534+
title: const Text("Check the team's inbox"),
1535+
content: const Text(
1536+
"We've sent an email to your team with a link to verify that it belongs to your team."),
1537+
actions: [
1538+
TextButton(
1539+
child: const Text("Close"),
1540+
onPressed: () => Navigator.of(context).pop())
1541+
],
1542+
);
1543+
} else {
1544+
return AlertDialog(
1545+
title: const Text("Change email"),
1546+
content: Column(
1547+
mainAxisSize: MainAxisSize.min,
1548+
children: [
1549+
TextField(
1550+
autofocus: true,
1551+
decoration: InputDecoration(
1552+
labelText: "New Email",
1553+
filled: true,
1554+
errorText: error,
1555+
),
1556+
textCapitalization: TextCapitalization.none,
1557+
keyboardType: TextInputType.emailAddress,
1558+
onChanged: (value) {
1559+
setState(() {
1560+
email = value;
1561+
});
1562+
},
1563+
),
1564+
],
1565+
),
1566+
actions: [
1567+
TextButton(
1568+
onPressed: submitting
1569+
? null
1570+
: () {
1571+
Navigator.of(context).pop();
1572+
},
1573+
child: const Text("Cancel"),
1574+
),
1575+
FilledButton(
1576+
onPressed: submitting || email.isEmpty
1577+
? null
1578+
: () async {
1579+
setState(() {
1580+
submitting = true;
1581+
error = null;
1582+
});
1583+
1584+
try {
1585+
await lovatAPI.editTeamEmail(email);
1586+
widget.onAdd?.call();
1587+
setState(() {
1588+
submitting = false;
1589+
submitted = true;
1590+
});
1591+
} on LovatAPIException catch (e) {
1592+
setState(() {
1593+
error = e.message;
1594+
submitting = false;
1595+
});
1596+
} catch (_) {
1597+
setState(() {
1598+
error = "Failed to update email";
1599+
submitting = false;
1600+
});
1601+
}
1602+
},
1603+
child: submitting
1604+
? const SizedBox(
1605+
height: 16,
1606+
width: 16,
1607+
child: CircularProgressIndicator(
1608+
valueColor: AlwaysStoppedAnimation(Colors.white),
1609+
strokeWidth: 2,
1610+
),
1611+
)
1612+
: const Text("Update"),
1613+
),
1614+
],
1615+
);
1616+
}
1617+
}
1618+
}

lib/pages/team_lookup/tabs/team_lookup_notes.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,6 @@ class RobotBrokeBox extends StatelessWidget {
112112
.toColor();
113113

114114
const double iconSize = 24;
115-
const double horizontalSpacing = 7;
116115

117116
final String description =
118117
"View ${breakDescriptions.length} ${breakDescriptions.length > 1 ? "reports" : "report"}";

lib/reusable/lovat_api/edit_team_email.dart

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,21 @@ extension EditTeamEmail on LovatAPI {
2323
}
2424
}
2525
}
26+
27+
Future<String> getTeamEmail() async {
28+
final response = await lovatAPI.get('/v1/manager/settings/teamemail');
29+
30+
if (response?.statusCode != 200) {
31+
debugPrint(response?.body ?? '');
32+
try {
33+
throw LovatAPIException(jsonDecode(response!.body)['displayError']);
34+
} on LovatAPIException {
35+
rethrow;
36+
} catch (_) {
37+
throw Exception('Failed to get team email');
38+
}
39+
} else {
40+
return response!.body;
41+
}
42+
}
2643
}

0 commit comments

Comments
 (0)