diff --git a/recipients_app/.vscode/settings.json b/recipients_app/.vscode/settings.json index 4cd8c4fe3..650587894 100644 --- a/recipients_app/.vscode/settings.json +++ b/recipients_app/.vscode/settings.json @@ -1,3 +1,9 @@ { - "java.configuration.updateBuildConfiguration": "automatic" + "java.configuration.updateBuildConfiguration": "automatic", + "dart.lineLength": 120, + "[dart]": { + "editor.rulers": [ + 120 + ], +} } diff --git a/recipients_app/README.md b/recipients_app/README.md index 6115886d2..8f1483d8e 100644 --- a/recipients_app/README.md +++ b/recipients_app/README.md @@ -12,9 +12,10 @@ Open `recipients_app` project folder in your development environment of choice. Building flavor should work seamlessly for Android Studio and VS Code with predefined build configs. -We have two build flavors: +We have three build flavors: - `dev` -> Connecting with Firebase Emulators (Firestore and Auth) +- `stage` -> Connecting with staging online firebase project - `prod` -> Connecting with production online firebase project and need real Firebase configuration json / plist file @@ -31,7 +32,15 @@ setup. ## Rebuilding JSON Serialization -flutter pub run build_runner watch --delete-conflicting-outputs +``` +dart run build_runner watch --delete-conflicting-outputs +``` + +or + +``` +dart run build_runner build --delete-conflicting-outputs +``` # Testing diff --git a/recipients_app/analysis_options.yaml b/recipients_app/analysis_options.yaml index cc2c6038b..cc603d418 100644 --- a/recipients_app/analysis_options.yaml +++ b/recipients_app/analysis_options.yaml @@ -31,6 +31,7 @@ linter: prefer_double_quotes: true prefer_const_constructors: true prefer_const_constructors_in_immutables: true + lines_longer_than_80_chars: false dart_code_metrics: rules: diff --git a/recipients_app/lib/data/models/payment/social_income_payment.g.dart b/recipients_app/lib/data/models/payment/social_income_payment.g.dart index 92864bae7..d625e0b37 100644 --- a/recipients_app/lib/data/models/payment/social_income_payment.g.dart +++ b/recipients_app/lib/data/models/payment/social_income_payment.g.dart @@ -9,7 +9,7 @@ part of 'social_income_payment.dart'; SocialIncomePayment _$SocialIncomePaymentFromJson(Map json) => SocialIncomePayment( id: json['id'] as String? ?? '', - amount: json['amount'] as int?, + amount: (json['amount'] as num?)?.toInt(), paymentAt: _$JsonConverterFromJson( json['payment_at'], const TimestampConverter().fromJson), currency: json['currency'] as String?, diff --git a/recipients_app/lib/data/models/phone.g.dart b/recipients_app/lib/data/models/phone.g.dart index 035247bac..7cbe33b1a 100644 --- a/recipients_app/lib/data/models/phone.g.dart +++ b/recipients_app/lib/data/models/phone.g.dart @@ -7,7 +7,7 @@ part of 'phone.dart'; // ************************************************************************** Phone _$PhoneFromJson(Map json) => Phone( - json['phone'] as int? ?? 0, + (json['phone'] as num?)?.toInt() ?? 0, ); Map _$PhoneToJson(Phone instance) => { diff --git a/recipients_app/lib/data/models/recipient.dart b/recipients_app/lib/data/models/recipient.dart index c0cc1b8a8..96bbe3641 100644 --- a/recipients_app/lib/data/models/recipient.dart +++ b/recipients_app/lib/data/models/recipient.dart @@ -75,6 +75,9 @@ class Recipient extends Equatable { @JsonKey(name: "last_updated_by") final String? updatedBy; + @JsonKey(name: "successor") + final String? successorName; + // this should be got from `/recipients//payments` collection @JsonKey(includeFromJson: false, includeToJson: false) final List? payments; @@ -101,6 +104,7 @@ class Recipient extends Equatable { this.organizationRef, this.payments = const [], this.updatedBy, + this.successorName, }); @override @@ -127,6 +131,7 @@ class Recipient extends Equatable { organizationRef, payments, updatedBy, + successorName, ]; } @@ -152,6 +157,7 @@ class Recipient extends Equatable { DocumentReference? organizationRef, List? payments, String? updatedBy, + String? successorName, }) { return Recipient( userId: userId ?? this.userId, @@ -176,6 +182,7 @@ class Recipient extends Equatable { organizationRef: organizationRef ?? this.organizationRef, payments: payments ?? this.payments, updatedBy: updatedBy ?? this.updatedBy, + successorName: successorName ?? this.successorName, ); } diff --git a/recipients_app/lib/data/models/recipient.g.dart b/recipients_app/lib/data/models/recipient.g.dart index fd0408a38..ce63b10a4 100644 --- a/recipients_app/lib/data/models/recipient.g.dart +++ b/recipients_app/lib/data/models/recipient.g.dart @@ -38,6 +38,7 @@ Recipient _$RecipientFromJson(Map json) => Recipient( json['organisation'], const DocumentReferenceConverter().fromJson), updatedBy: json['last_updated_by'] as String?, + successorName: json['successor'] as String?, ); Map _$RecipientToJson(Recipient instance) => { @@ -64,6 +65,7 @@ Map _$RecipientToJson(Recipient instance) => { 'next_survey': _$JsonConverterToJson( instance.nextSurvey, const TimestampConverter().toJson), 'last_updated_by': instance.updatedBy, + 'successor': instance.successorName, }; Value? _$JsonConverterFromJson( diff --git a/recipients_app/lib/l10n/app_en.arb b/recipients_app/lib/l10n/app_en.arb index 497626e8f..e5da358be 100644 --- a/recipients_app/lib/l10n/app_en.arb +++ b/recipients_app/lib/l10n/app_en.arb @@ -1,4 +1,5 @@ { + "@@locale": "en", "profileUpdateSuccess": "Profile updated successfully", "profileUpdateError": "Failed to update profile. Please try again or contact our support", "profile": "Profile", @@ -30,6 +31,9 @@ "contactNumber": "Contact Number", "contactNumberError": "Please enter your contact phone number", "contactNumberError2": "Please enter a valid phone number. Only numbers are allowed", + "inCaseOfDeathTitle": "⁠In Case of Death", + "inCaseOfDeathDescription": "Who should be the recipient of your Social Income payments if you pass away during the program?", + "successorName": "Successor Name", "support": "Support", "supportInfo": "In case you have any questions or problems, please contact us.", "getInTouch": "Get in touch with us", @@ -60,7 +64,6 @@ "call": "Call", "supportTeam": "Support Team", "phone": "Phone", - "email": "Email", "close": "Close", "whatsappError": "WhatsApp not installed", "paymentsConfirmedCount": "payments received", @@ -160,7 +163,7 @@ "placeholders": { "done": { "type": "int" - }, + }, "all": { "type": "int" } @@ -250,7 +253,7 @@ "secondFactorAlreadyEnrolled": "Second factor already enrolled", "secondFactorLimitExceeded": "Maximum second factor count exceeded", "tenantIdMismatch": "Tenant ID mismatch", - "timeoutError": "Timeout", + "timeoutError": "Timeout", "tokenExpired": "User token expired", "tooManyAttemptsTryLater": "Too many requests, try again later", "unauthorizedDomain": "Unauthorized domain for resource", diff --git a/recipients_app/lib/l10n/app_kri.arb b/recipients_app/lib/l10n/app_kri.arb index 7254df255..bedc1eb70 100644 --- a/recipients_app/lib/l10n/app_kri.arb +++ b/recipients_app/lib/l10n/app_kri.arb @@ -31,6 +31,9 @@ "contactNumber": "Nɔmba we fɔ kɔl yu", "contactNumberError": "Duya put yu fon nɔmba ya", "contactNumberError2": "Put di fon nɔmba prɔpa wan ɛn tayp di nɔmba dɛm nɔmɔ.", + "inCaseOfDeathTitle": "⁠If yu day", + "inCaseOfDeathDescription": "Udat na di pɔsin we wi fɔ gi yu Social Income pemɛnt if yu day we di program nɔ dɔn yet? Yu wɛf, pikin, mama, papa, brɔda ɔ sista", + "successorName": "Wetin na in nem?", "support": "Yu want ɛp", "supportInfo": "If yu get ɛni kwɛstyɔn ɔ di fesin prɔblɛm, duya rich awt to wi.", "getInTouch": "Mek wi no aw yu fil", @@ -61,7 +64,6 @@ "call": "Kɔl", "supportTeam": "Sɔpɔt Tim", "phone": "Fon", - "email": "Imel", "close": "Lɔk am", "whatsappError": "Wasap nɔ go insay yu fon yet", "paymentsConfirmedCount": "Pemɛnt dɛn we yu dɔn gɛt", @@ -162,7 +164,7 @@ "placeholders": { "done": { "type": "int" - }, + }, "all": { "type": "int" } diff --git a/recipients_app/lib/view/pages/account_page.dart b/recipients_app/lib/view/pages/account_page.dart index 227d977cd..a4c65b96e 100644 --- a/recipients_app/lib/view/pages/account_page.dart +++ b/recipients_app/lib/view/pages/account_page.dart @@ -38,6 +38,7 @@ class AccountPageState extends State { late final TextEditingController _callingNameController; late final TextEditingController _paymentNumberController; late final TextEditingController _contactNumberController; + late final TextEditingController _successorNameController; late final TextEditingController _emailController; PackageInfo _packageInfo = PackageInfo( @@ -74,6 +75,9 @@ class AccountPageState extends State { _contactNumberController = TextEditingController( text: widget.recipient.communicationMobilePhone?.phoneNumber.toString() ?? "", ); + _successorNameController = TextEditingController( + text: widget.recipient.successorName ?? "", + ); _initAppVersionInfo(); @@ -301,7 +305,9 @@ class AccountPageState extends State { final emailRegex = RegExp( r"^[\w-]+(\.[\w-]+)*@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*(\.[a-zA-Z]{2,})$", ); - if (!emailRegex.hasMatch(value)) return localizations.errorEmailInvalid; + if (!emailRegex.hasMatch(value)) { + return localizations.errorEmailInvalid; + } return null; }, @@ -397,6 +403,7 @@ class AccountPageState extends State { } }, ), + const SizedBox(height: 24), // TODO add later /*const SizedBox(height: 8), DropdownButtonFormField( @@ -416,9 +423,30 @@ class AccountPageState extends State { : null, ), */ + /// SUCCESSOR IN THE CASE OF DEATH + Text( + localizations.inCaseOfDeathTitle, + style: Theme.of(context).textTheme.bodyLarge, + ), + const SizedBox(height: 16), + Text( + localizations.inCaseOfDeathDescription, + style: Theme.of(context).textTheme.bodySmall, + ), + const SizedBox(height: 16), + InputText( + hintText: localizations.successorName, + controller: _successorNameController, + keyboardType: TextInputType.name, + onSubmitted: (value) { + context.read().updateRecipient( + recipient.copyWith(successorName: value), + ); + }, + ), + /// RECOMMENDING ORGA - if (widget.organization != null) - OrganizationInfo(organization: widget.organization!), + if (widget.organization != null) OrganizationInfo(organization: widget.organization!), const SizedBox(height: 24), Text( localizations.support,