Skip to content

Commit

Permalink
feature(recipients_app): Add overview list for surveys (#691)
Browse files Browse the repository at this point in the history
* Add new status and method for not filterred methods

* Add surveys overview page

* Add surveys overview card to dashboard

* Make whole card clickable

* Make whole dashboard scrollable

* Add survey name to dashboard survey card

* Make dateFormat to be based on date skeletons
  • Loading branch information
MDikkii authored Feb 11, 2024
1 parent 925d4c5 commit 2b22e20
Show file tree
Hide file tree
Showing 20 changed files with 506 additions and 67 deletions.
62 changes: 44 additions & 18 deletions recipients_app/lib/core/cubits/survey/survey_cubit.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import "package:app/data/models/models.dart";
import "package:app/data/repositories/crash_reporting_repository.dart";
import "package:app/data/repositories/survey_repository.dart";
import "package:cloud_firestore/cloud_firestore.dart";
import "package:collection/collection.dart";
import "package:equatable/equatable.dart";
import "package:flutter_bloc/flutter_bloc.dart";

Expand Down Expand Up @@ -28,29 +30,17 @@ class SurveyCubit extends Cubit<SurveyState> {

Future<void> getSurveys() async {
try {
final surveys =
await surveyRepository.fetchSurveys(recipientId: recipient.userId);

final mappedSurveys = surveys
.where((element) => _shouldShowSurveyCard(element))
.map(
(survey) => MappedSurvey(
survey: survey,
surveyUrl: _getSurveyUrl(
survey,
recipient.userId,
),
cardStatus: _getSurveyCardStatus(survey),
daysToOverdue: _getDaysToOverdue(survey),
daysAfterOverdue: _getDaysAfterOverdue(survey),
),
)
final mappedSurveys = await _getSurveys();

final dashboardSurveys = mappedSurveys
.where((element) => _shouldShowSurveyCard(element.survey))
.toList();

emit(
SurveyState(
status: SurveyStatus.updatedSuccess,
mappedSurveys: mappedSurveys,
dashboardMappedSurveys: dashboardSurveys,
),
);
} on Exception catch (ex, stackTrace) {
Expand All @@ -59,6 +49,30 @@ class SurveyCubit extends Cubit<SurveyState> {
}
}

Future<List<MappedSurvey>> _getSurveys() async {
final surveys =
await surveyRepository.fetchSurveys(recipientId: recipient.userId);

final mappedSurveys = surveys
.map(
(survey) => MappedSurvey(
name: _getReadableName(survey.id),
survey: survey,
surveyUrl: _getSurveyUrl(
survey,
recipient.userId,
),
cardStatus: _getSurveyCardStatus(survey),
daysToOverdue: _getDaysToOverdue(survey),
daysAfterOverdue: _getDaysAfterOverdue(survey),
),
)
.sortedBy((element) => element.survey.dueDateAt ?? Timestamp.now())
.toList();

return mappedSurveys;
}

String _getSurveyUrl(Survey survey, String recipientId) {
final params = {
"email": survey.accessEmail,
Expand Down Expand Up @@ -103,7 +117,11 @@ class SurveyCubit extends Cubit<SurveyState> {
dateDifferenceInDays < _kOverdueEndDay) {
return SurveyCardStatus.overdue;
} else {
return SurveyCardStatus.missed;
if ((_getDaysAfterOverdue(survey) ?? 0) > 0) {
return SurveyCardStatus.missed;
} else {
return SurveyCardStatus.upcoming;
}
}
} else if (survey.status == SurveyServerStatus.completed) {
return SurveyCardStatus.answered;
Expand All @@ -113,6 +131,14 @@ class SurveyCubit extends Cubit<SurveyState> {
}
}

String _getReadableName(String surveyId) {
return surveyId
.split("-")
.map((element) =>
"${element[0].toUpperCase()}${element.substring(1).toLowerCase()}")
.join(" ");
}

int? _getDaysToOverdue(Survey survey) {
final dueDateDaysDifference = _getSurveyDueDateAndNowDifferenceInDays(survey);
if (dueDateDaysDifference == null) {
Expand Down
4 changes: 3 additions & 1 deletion recipients_app/lib/core/cubits/survey/survey_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ enum SurveyStatus { initial, updatedSuccess, updatedFailure }
class SurveyState extends Equatable {
final SurveyStatus status;
final List<MappedSurvey> mappedSurveys;
final List<MappedSurvey> dashboardMappedSurveys;

const SurveyState({
this.status = SurveyStatus.initial,
this.mappedSurveys = const [],
this.dashboardMappedSurveys = const [],
});

@override
List<Object?> get props => [status, mappedSurveys];
List<Object?> get props => [status, mappedSurveys, dashboardMappedSurveys];
}
3 changes: 3 additions & 0 deletions recipients_app/lib/data/models/survey/mapped_survey.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import "package:app/data/models/survey/survey_card_status.dart";
import "package:equatable/equatable.dart";

class MappedSurvey extends Equatable {
final String name;
final Survey survey;
final String surveyUrl;
final SurveyCardStatus cardStatus;
final int? daysToOverdue;
final int? daysAfterOverdue;

const MappedSurvey({
required this.name,
required this.survey,
required this.surveyUrl,
required this.cardStatus,
Expand All @@ -19,6 +21,7 @@ class MappedSurvey extends Equatable {

@override
List<Object?> get props => [
name,
survey,
surveyUrl,
cardStatus,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ enum SurveyCardStatus {
overdue,
answered,
missed,
upcoming,
}
12 changes: 6 additions & 6 deletions recipients_app/lib/kri_intl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,12 @@ const kriLocaleDatePatterns = {
"H": "HH",
"Hm": "HH:mm",
"Hms": "HH:mm:ss",
"j": "HH",
"jm": "HH:mm",
"jms": "HH:mm:ss",
"jmv": "HH:mm v",
"jmz": "HH:mm z",
"jz": "HH z",
"j": "h a",
"jm": "h:mm a",
"jms": "h:mm:ss a",
"jmv": "h:mm a v",
"jmz": "h:mm a z",
"jz": "h a z",
"m": "m",
"ms": "mm:ss",
"s": "s",
Expand Down
20 changes: 20 additions & 0 deletions recipients_app/lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,26 @@
"continueText": "Continue",
"phoneNumber": "Phone number",
"appVersion": "App version:",
"surveysTitle": "Surveys",
"surveysEmpty": "No surveys.",
"surveyMissed": "Missed",
"surveyDue": "Due",
"surveyCompleted": "Completed",
"surveyUpcoming": "Upcoming",
"surveyInProgress": "In Progress",
"overview": "Overview",
"completedSurveysCount": "{done}/{all} completed",
"@completedSurveysCount": {
"placeholders": {
"done": {
"type": "int"
},
"all": {
"type": "int"
}
}
},
"mySurveysTitle": "My surveys",

"invalidPhoneNumberError": "Invalid phone number. Please check your phone number and try again.",
"invalidVerificationCodeError": "Invalid verification code. Please check provided SMS code and try again.",
Expand Down
21 changes: 21 additions & 0 deletions recipients_app/lib/l10n/app_kri.arb
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,27 @@
"phoneNumber": "Fon nɔmba",
"appVersion": "App version:",

"surveysTitle": "Surveys",
"surveysEmpty": "No surveys.",
"surveyMissed": "Missed",
"surveyDue": "Due",
"surveyCompleted": "Completed",
"surveyUpcoming": "Upcoming",
"surveyInProgress": "In Progress",
"overview": "Overview",
"completedSurveysCount": "{done}/{all} completed",
"@completedSurveysCount": {
"placeholders": {
"done": {
"type": "int"
},
"all": {
"type": "int"
}
}
},
"mySurveysTitle": "My surveys",

"invalidPhoneNumberError": "Fon nɔmba nɔ kɔrɛkt. Chɛk yu fon nɔmba ɛn tray bak ya.",
"invalidVerificationCodeError": "Di spɛshal kod we wi sɛn yu nɔ kɔrɛkt. Duya chɛk di SMS kod ɛn tray bak.",
"userDisabledError": "Wi dɔn lɔk yu akawnt. Rich awt to wi if yu want fɔ no mɔ.",
Expand Down
1 change: 1 addition & 0 deletions recipients_app/lib/ui/configs/app_theme.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ abstract class AppTheme {

static final ThemeData lightTheme = ThemeData(
fontFamily: "Unica77LL",
fontFamilyFallback: ["sans-serif"],
pageTransitionsTheme: const PageTransitionsTheme(
builders: {
TargetPlatform.android: SharedAxisPageTransitionsBuilder(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class SurveyStatusIconWithText extends StatelessWidget {
case SurveyCardStatus.overdue:
case SurveyCardStatus.firstReminder:
case SurveyCardStatus.newSurvey:
case SurveyCardStatus.upcoming:
// no impl for now.
return Container();
}
Expand Down
23 changes: 16 additions & 7 deletions recipients_app/lib/view/pages/account_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,12 @@ class AccountPageState extends State<AccountPage> {
_callingNameController = TextEditingController(
text: widget.recipient.callingName ?? "",
);
_birthDateController = TextEditingController(
text: getFormattedDate(widget.recipient.birthDate) ?? "",
);
_emailController = TextEditingController(
text: widget.recipient.email ?? "",
);

_birthDateController = TextEditingController(
text: "",
);
_paymentNumberController = TextEditingController(
text: widget.recipient.mobileMoneyPhone?.phoneNumber.toString() ?? "",
);
Expand All @@ -78,6 +77,12 @@ class AccountPageState extends State<AccountPage> {
);

_initAppVersionInfo();

WidgetsBinding.instance.addPostFrameCallback((_) {
final locale = Localizations.localeOf(context).toLanguageTag();
_birthDateController.text =
getFormattedDate(widget.recipient.birthDate, locale) ?? "";
});
}

Future<void> _initAppVersionInfo() async {
Expand All @@ -103,6 +108,7 @@ class AccountPageState extends State<AccountPage> {
@override
Widget build(BuildContext context) {
final localizations = AppLocalizations.of(context)!;
final locale = Localizations.localeOf(context).toLanguageTag();

final recipient =
context.watch<AuthCubit>().state.recipient ?? widget.recipient;
Expand Down Expand Up @@ -238,7 +244,7 @@ class AccountPageState extends State<AccountPage> {
),
);
_birthDateController.text =
getFormattedDate(timestamp) ?? "";
getFormattedDate(timestamp, locale) ?? "";
}
return;
}),
Expand Down Expand Up @@ -444,8 +450,11 @@ class AccountPageState extends State<AccountPage> {
);
}

String? getFormattedDate(Timestamp? timestamp) {
String? getFormattedDate(
Timestamp? timestamp,
String locale,
) {
if (timestamp == null) return null;
return DateFormat("dd.MM.yyyy").format(timestamp.toDate());
return DateFormat.yMd(locale).format(timestamp.toDate());
}
}
53 changes: 23 additions & 30 deletions recipients_app/lib/view/pages/dashboard_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import "package:app/core/cubits/survey/survey_cubit.dart";
import "package:app/data/repositories/repositories.dart";
import "package:app/ui/configs/configs.dart";
import "package:app/view/widgets/dashboard_item.dart";
import "package:app/view/widgets/empty_item.dart";
import "package:app/view/widgets/income/balance_card/balance_card_container.dart";
import "package:app/view/widgets/survey/survey_card_container.dart";
import "package:app/view/widgets/survey/surveys_overview_card.dart";
import "package:flutter/material.dart";
import "package:flutter_bloc/flutter_bloc.dart";
import "package:flutter_gen/gen_l10n/app_localizations.dart";

class DashboardPage extends StatelessWidget {
const DashboardPage({super.key});
Expand Down Expand Up @@ -51,7 +52,7 @@ class _DashboardView extends StatelessWidget {

@override
Widget build(BuildContext context) {
final localizations = AppLocalizations.of(context)!;
final surveys = context.watch<SurveyCubit>().state.mappedSurveys;

final List<DashboardItem> dashboardItems = context
.watch<DashboardCardManagerCubit>()
Expand All @@ -63,46 +64,38 @@ class _DashboardView extends StatelessWidget {
final List<DashboardItem> surveysItems = context
.watch<SurveyCubit>()
.state
.mappedSurveys
.dashboardMappedSurveys
.map<DashboardItem>(
(survey) => SurveyCardContainer(
mappedSurvey: survey,
),
)
.toList();

final items = dashboardItems + surveysItems;
final dynamicItemsCount = dashboardItems.length + surveysItems.length;

final List<DashboardItem> headerItems = [
const BalanceCardContainer(),
SurveysOverviewCard(mappedSurveys: surveys),
];

List<DashboardItem> items;

if (dynamicItemsCount > 0) {
items = headerItems + dashboardItems + surveysItems;
} else {
items = headerItems + [const EmptyItem()];
}

return BlocBuilder<PaymentsCubit, PaymentsState>(
builder: (context, state) {
return Padding(
padding: AppSpacings.h8,
child: Column(
children: [
const BalanceCardContainer(),
const SizedBox(height: 8),
if (items.isEmpty)
Expanded(
child: Padding(
padding: AppSpacings.a8,
child: Center(
child: Text(
localizations.dashboardUp2Date,
textAlign: TextAlign.center,
),
),
),
)
else
Expanded(
child: ListView.separated(
separatorBuilder: (context, index) =>
const SizedBox(height: 8),
itemCount: items.length,
itemBuilder: (context, index) => items[index],
),
),
],
child: ListView.separated(
separatorBuilder: (context, index) => const SizedBox(height: 4),
itemCount: items.length,
itemBuilder: (context, index) => items[index],
physics: const BouncingScrollPhysics(),
),
);
},
Expand Down
Loading

0 comments on commit 2b22e20

Please sign in to comment.