Skip to content

Commit c022288

Browse files
authored
feat(absence_page): absence page design and functionment (#1247)
1 parent e4d92d8 commit c022288

File tree

11 files changed

+229
-192
lines changed

11 files changed

+229
-192
lines changed

yaki_mobile/assets/translations/en.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,5 +131,12 @@
131131
"feedback": "Help center ?",
132132
"feedbackMessage": "Do you have a question ? send us a mail at : yaki@xpeho.fr",
133133

134-
"filter": "Filter"
134+
"filter": "Filter",
135+
136+
"absenceTitle1": "Declare an",
137+
"absenceTitle2": "absence",
138+
"absenceDatePickerInitialLabel": "Choose a date",
139+
"absenceStart": "Start",
140+
"absenceEnd": "End",
141+
"absenceError": "* Please select a start date and an end date, \n The start date must be before or the same as the end date"
135142
}

yaki_mobile/assets/translations/fr.json

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,13 +126,19 @@
126126
"somethingWentWrong": "Certaines informations se sont perdues en chemin",
127127
"retry": "réessayer",
128128

129-
130129
"upload": "Envoyer",
131130
"takePicture": "prendre une photo",
132131
"imgGallery": "image à partir de ma gallerie",
133132

134133
"feedback": "Besoin d'aide ?",
135134
"feedbackMessage": "N'hésitez pas à nous contacter à l'adresse suivante : yaki@xpeho.fr",
136135

137-
"filter": "Filtrer"
136+
"filter": "Filtrer",
137+
138+
"absenceTitle1": "Déclarer une",
139+
"absenceTitle2": "absence",
140+
"absenceDatePickerInitialLabel": "Choisir une date",
141+
"absenceStart": "Début",
142+
"absenceEnd": "Fin",
143+
"absenceError": "* Veuillez renseigner une date de début et de fin, \n Votre date de fin doit être antérieur ou identique à votre date de début"
138144
}

yaki_mobile/lib/app_router.dart

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import 'package:go_router/go_router.dart';
44
import 'package:yaki/data/sources/local/shared_preference.dart';
55
import 'package:yaki/presentation/displaydata/declaration_enum.dart';
66
import 'package:yaki/presentation/displaydata/declaration_summary_enum.dart';
7+
import 'package:yaki/presentation/features/absence/absence.dart';
78
import 'package:yaki/presentation/features/authentication/authentication.dart';
89
import 'package:yaki/presentation/features/declaration/declaration_page.dart';
910
import 'package:yaki/presentation/features/splash_screen/splash_screen.dart';
@@ -104,6 +105,19 @@ final goRouterProvider = Provider<GoRouter>(
104105
}
105106
},
106107
),
108+
GoRoute(
109+
path: 'declaration/long-absence',
110+
builder: (context, state) => const PopScope(
111+
canPop: false,
112+
child: Absence(),
113+
),
114+
redirect: (BuildContext context, GoRouterState state) async {
115+
if (await isTokenSavedAndValid(ref)) {
116+
return '/declaration/long-absence';
117+
}
118+
return '/authentication';
119+
},
120+
),
107121
GoRoute(
108122
path: 'declaration/half-day-start',
109123
builder: (context, state) => const PopScope(
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
import 'package:easy_localization/easy_localization.dart';
2+
import 'package:flutter/material.dart';
3+
import 'package:flutter_riverpod/flutter_riverpod.dart';
4+
import 'package:go_router/go_router.dart';
5+
import 'package:yaki/presentation/displaydata/declaration_status_enum.dart';
6+
import 'package:yaki/presentation/displaydata/declaration_summary_enum.dart';
7+
import 'package:yaki/presentation/features/absence/view/absence_header.dart';
8+
import 'package:yaki/presentation/features/shared/app_bar_go_back.dart';
9+
import 'package:yaki/presentation/state/providers/declaration_provider.dart';
10+
import 'package:yaki/presentation/state/providers/team_provider.dart';
11+
import 'package:yaki_ui/yaki_ui.dart';
12+
13+
class Absence extends ConsumerStatefulWidget {
14+
const Absence({super.key});
15+
16+
@override
17+
ConsumerState<Absence> createState() => _AbsenceState();
18+
}
19+
20+
class _AbsenceState extends ConsumerState<Absence> {
21+
void returnToTeamSelection(BuildContext context, WidgetRef ref) {
22+
context.go("/team-selection");
23+
ref.read(teamProvider.notifier).clearTeamList();
24+
}
25+
26+
bool _isButtonActive = false;
27+
DateTime? _selectedDateStart;
28+
DateTime? _selectedDateEnd;
29+
30+
/// This method is called every time the user selects a date.
31+
/// It checks if the selected dates are valid and if so, it enables the button.
32+
///
33+
/// - If any of the dates is null, the button is disabled. (meaning not date has been selected)
34+
/// - If the start date is after the end date, the button is disabled.
35+
/// - If the start date is the same as the end date, the button is enabled.
36+
/// - If the start date is before the end date, the button is enabled.
37+
void _isDateValid() {
38+
if (_selectedDateStart == null || _selectedDateEnd == null) {
39+
_isButtonActive = false;
40+
return;
41+
}
42+
bool areDatesValid = _selectedDateStart!.isBefore(_selectedDateEnd!) ||
43+
_selectedDateStart == _selectedDateEnd;
44+
45+
_isButtonActive = areDatesValid;
46+
}
47+
48+
void onValidationPress() {
49+
final fetchedTeamList = ref.read(teamProvider).fetchedTeamList;
50+
//get first team with valid teamId
51+
final int selectedTeamId = fetchedTeamList
52+
.firstWhere(
53+
(team) => team.teamId > 0,
54+
orElse: () => fetchedTeamList.first,
55+
)
56+
.teamId;
57+
58+
ref.watch(declarationProvider.notifier).createVacationDeclaration(
59+
dateStart: _selectedDateStart!,
60+
dateEnd: _selectedDateEnd!,
61+
status: StatusEnum.absence.name,
62+
teamId: selectedTeamId,
63+
);
64+
65+
context.go('/summary/${DeclarationSummaryPaths.absence.text}');
66+
}
67+
68+
@override
69+
Widget build(BuildContext context) {
70+
return Scaffold(
71+
appBar: AppBarGoBack(
72+
onGobackIconPressed: returnToTeamSelection,
73+
),
74+
body: SingleChildScrollView(
75+
child: SafeArea(
76+
child: Padding(
77+
padding: const EdgeInsets.symmetric(horizontal: 16.0),
78+
child: Column(
79+
crossAxisAlignment: CrossAxisAlignment.start,
80+
children: [
81+
const SizedBox(height: 32),
82+
const AbsenceHeader(),
83+
const SizedBox(height: 32),
84+
DatePickerCard(
85+
title: tr('absenceStart'),
86+
initialButtonLabel: tr('absenceDatePickerInitialLabel'),
87+
earliestSelectableDate: DateTime.now(),
88+
onDateSelection: (selectedDate) {
89+
setState(() {
90+
_selectedDateStart = selectedDate;
91+
});
92+
_isDateValid();
93+
},
94+
toggleLabels: [
95+
tr('morning'),
96+
tr('afternoon'),
97+
],
98+
isTopRadius: true,
99+
),
100+
const Divider(
101+
color: kBackgroundColor,
102+
thickness: 1.0,
103+
height: 1,
104+
),
105+
DatePickerCard(
106+
title: tr('absenceEnd'),
107+
initialButtonLabel: tr('absenceDatePickerInitialLabel'),
108+
earliestSelectableDate: DateTime.now(),
109+
onDateSelection: (selectedDate) {
110+
setState(() {
111+
_selectedDateEnd = selectedDate;
112+
});
113+
_isDateValid();
114+
},
115+
toggleLabels: [
116+
tr('morning'),
117+
tr('afternoon'),
118+
],
119+
isTopRadius: false,
120+
),
121+
const SizedBox(height: 70),
122+
Button(
123+
text: tr('registrationSnackValidation').toUpperCase(),
124+
buttonHeight: 58,
125+
onPressed: _isButtonActive ? onValidationPress : null,
126+
),
127+
if (!_isButtonActive)
128+
Padding(
129+
padding:
130+
const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
131+
child: Text(
132+
tr('absenceError'),
133+
style: const TextStyle(color: Colors.grey),
134+
),
135+
),
136+
const SizedBox(height: 16),
137+
],
138+
),
139+
),
140+
),
141+
),
142+
);
143+
}
144+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import 'package:easy_localization/easy_localization.dart';
2+
import 'package:flutter/material.dart';
3+
import 'package:yaki/presentation/styles/text_style.dart';
4+
5+
class AbsenceHeader extends StatelessWidget {
6+
const AbsenceHeader({super.key});
7+
8+
@override
9+
Widget build(BuildContext context) {
10+
return Padding(
11+
padding: const EdgeInsets.only(left: 12.0, bottom: 35),
12+
child: Column(
13+
crossAxisAlignment: CrossAxisAlignment.start,
14+
children: [
15+
Text(
16+
tr('absenceTitle1'),
17+
style: textStylePageTitle(),
18+
),
19+
Text(
20+
tr('absenceTitle2'),
21+
style: textStylePageTitle(),
22+
),
23+
],
24+
),
25+
);
26+
}
27+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:flutter_riverpod/flutter_riverpod.dart';
3+
4+
class AppBarGoBack extends ConsumerWidget implements PreferredSizeWidget {
5+
final void Function(BuildContext, WidgetRef) onGobackIconPressed;
6+
7+
const AppBarGoBack({super.key, required this.onGobackIconPressed});
8+
9+
@override
10+
Widget build(BuildContext context, WidgetRef ref) {
11+
return AppBar(
12+
elevation: 0,
13+
backgroundColor: Colors.transparent,
14+
leading: IconButton(
15+
onPressed: () => onGobackIconPressed(context, ref),
16+
icon: const Icon(Icons.arrow_back),
17+
),
18+
);
19+
}
20+
21+
@override
22+
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
23+
}

yaki_mobile/lib/presentation/features/team_selection/team_selection.dart

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
import 'package:easy_localization/easy_localization.dart';
22
import 'package:go_router/go_router.dart';
33
import 'package:yaki/presentation/displaydata/declaration_enum.dart';
4-
import 'package:yaki/presentation/displaydata/declaration_summary_enum.dart';
54
import 'package:yaki/presentation/features/shared/app_bar_date.dart';
65
import 'package:yaki/presentation/features/team_selection/view/team_selection_header.dart';
76
import 'package:yaki/presentation/features/team_selection/view/team_selection_list.dart';
87
import 'package:yaki/presentation/features/shared/sized_circle_avatar.dart';
98
import 'package:yaki/presentation/state/providers/avatar_provider.dart';
109
import 'package:yaki/presentation/state/providers/team_provider.dart';
1110
import 'package:yaki/presentation/state/providers/user_provider.dart';
12-
import 'package:yaki/presentation/ui/vacation/vacation_selection_dialog.dart';
1311
import 'package:yaki_ui/yaki_ui.dart';
1412
import 'package:flutter/material.dart';
1513
import 'package:flutter_riverpod/flutter_riverpod.dart';
@@ -78,28 +76,12 @@ void onValidationTap({
7876
final teamList = ref.read(teamProvider).selectedTeamList;
7977
final selectCount = teamList.length;
8078

81-
final fetchedTeamList = ref.read(teamProvider).fetchedTeamList;
82-
8379
if (selectCount == 1) {
8480
final bool isAbsenceSelected =
8581
teamList.any((team) => team.teamName == "Absence");
8682

8783
if (isAbsenceSelected) {
88-
// get first team with valid teamId
89-
final int selectedTeamId = fetchedTeamList
90-
.firstWhere(
91-
(team) => team.teamId > 0,
92-
orElse: () => fetchedTeamList.first,
93-
)
94-
.teamId;
95-
96-
VacationSelectionDialog(
97-
teamId: selectedTeamId,
98-
ref: ref,
99-
context: context,
100-
goToPage: () =>
101-
context.go('/summary/${DeclarationSummaryPaths.absence.text}'),
102-
).show();
84+
context.go("/declaration/long-absence");
10385
} else {
10486
context.go("/declaration/${DeclarationPaths.fullDay.text}");
10587
}

yaki_mobile/lib/presentation/ui/vacation/vacation_selection_dialog.dart

Lines changed: 0 additions & 32 deletions
This file was deleted.

0 commit comments

Comments
 (0)