Merge dev into feature branch
This commit is contained in:
@@ -40,27 +40,42 @@ class CertificatesRepositoryImpl implements CertificatesRepository {
|
||||
Future<StaffCertificate> uploadCertificate({
|
||||
required String certificateType,
|
||||
required String name,
|
||||
required String filePath,
|
||||
String? filePath,
|
||||
String? existingFileUri,
|
||||
DateTime? expiryDate,
|
||||
String? issuer,
|
||||
String? certificateNumber,
|
||||
}) async {
|
||||
// 1. Upload the file to cloud storage
|
||||
final FileUploadResponse uploadRes = await _uploadService.uploadFile(
|
||||
filePath: filePath,
|
||||
fileName:
|
||||
'staff_cert_${certificateType}_${DateTime.now().millisecondsSinceEpoch}.pdf',
|
||||
visibility: FileVisibility.private,
|
||||
);
|
||||
String fileUri;
|
||||
String? signedUrl;
|
||||
|
||||
// 2. Generate a signed URL
|
||||
final SignedUrlResponse signedUrlRes =
|
||||
await _signedUrlService.createSignedUrl(fileUri: uploadRes.fileUri);
|
||||
if (filePath != null) {
|
||||
// NEW FILE: Full upload pipeline
|
||||
// 1. Upload the file to cloud storage
|
||||
final FileUploadResponse uploadRes = await _uploadService.uploadFile(
|
||||
filePath: filePath,
|
||||
fileName:
|
||||
'staff_cert_${certificateType}_${DateTime.now().millisecondsSinceEpoch}.pdf',
|
||||
visibility: FileVisibility.private,
|
||||
);
|
||||
|
||||
// 3. Initiate verification
|
||||
// 2. Generate a signed URL
|
||||
final SignedUrlResponse signedUrlRes =
|
||||
await _signedUrlService.createSignedUrl(fileUri: uploadRes.fileUri);
|
||||
|
||||
fileUri = uploadRes.fileUri;
|
||||
signedUrl = signedUrlRes.signedUrl;
|
||||
} else if (existingFileUri != null) {
|
||||
// EXISTING FILE: Metadata-only update — skip upload steps
|
||||
fileUri = existingFileUri;
|
||||
} else {
|
||||
throw ArgumentError('Either filePath or existingFileUri must be provided');
|
||||
}
|
||||
|
||||
// 3. Create verification (works for both new and existing files)
|
||||
final VerificationResponse verificationRes =
|
||||
await _verificationService.createVerification(
|
||||
fileUri: uploadRes.fileUri,
|
||||
fileUri: fileUri,
|
||||
type: 'certification',
|
||||
subjectType: 'worker',
|
||||
subjectId: certificateType,
|
||||
@@ -71,21 +86,21 @@ class CertificatesRepositoryImpl implements CertificatesRepository {
|
||||
},
|
||||
);
|
||||
|
||||
// 4. Save certificate via V2 API
|
||||
// 4. Save/update certificate via V2 API (upserts on certificate_type)
|
||||
await _api.post(
|
||||
StaffEndpoints.certificates,
|
||||
data: <String, dynamic>{
|
||||
'certificateType': certificateType,
|
||||
'name': name,
|
||||
'fileUri': signedUrlRes.signedUrl,
|
||||
'expiresAt': expiryDate?.toIso8601String(),
|
||||
if (signedUrl != null) 'fileUri': signedUrl,
|
||||
'expiresAt': expiryDate?.toUtc().toIso8601String(),
|
||||
'issuer': issuer,
|
||||
'certificateNumber': certificateNumber,
|
||||
'verificationId': verificationRes.verificationId,
|
||||
},
|
||||
);
|
||||
|
||||
// 5. Return updated list
|
||||
// 5. Return updated certificate
|
||||
final List<StaffCertificate> certificates = await getCertificates();
|
||||
return certificates.firstWhere(
|
||||
(StaffCertificate c) => c.certificateType == certificateType,
|
||||
|
||||
@@ -9,10 +9,15 @@ abstract interface class CertificatesRepository {
|
||||
Future<List<StaffCertificate>> getCertificates();
|
||||
|
||||
/// Uploads a certificate file and saves the record.
|
||||
///
|
||||
/// When [filePath] is provided, a new file is uploaded to cloud storage.
|
||||
/// When only [existingFileUri] is provided, the existing stored file is
|
||||
/// reused and only metadata (e.g. expiry date) is updated.
|
||||
Future<StaffCertificate> uploadCertificate({
|
||||
required String certificateType,
|
||||
required String name,
|
||||
required String filePath,
|
||||
String? filePath,
|
||||
String? existingFileUri,
|
||||
DateTime? expiryDate,
|
||||
String? issuer,
|
||||
String? certificateNumber,
|
||||
|
||||
@@ -15,6 +15,7 @@ class UploadCertificateUseCase
|
||||
certificateType: params.certificateType,
|
||||
name: params.name,
|
||||
filePath: params.filePath,
|
||||
existingFileUri: params.existingFileUri,
|
||||
expiryDate: params.expiryDate,
|
||||
issuer: params.issuer,
|
||||
certificateNumber: params.certificateNumber,
|
||||
@@ -25,14 +26,21 @@ class UploadCertificateUseCase
|
||||
/// Parameters for [UploadCertificateUseCase].
|
||||
class UploadCertificateParams {
|
||||
/// Creates [UploadCertificateParams].
|
||||
///
|
||||
/// Either [filePath] (for a new file upload) or [existingFileUri] (for a
|
||||
/// metadata-only update using an already-stored file) must be provided.
|
||||
UploadCertificateParams({
|
||||
required this.certificateType,
|
||||
required this.name,
|
||||
required this.filePath,
|
||||
this.filePath,
|
||||
this.existingFileUri,
|
||||
this.expiryDate,
|
||||
this.issuer,
|
||||
this.certificateNumber,
|
||||
});
|
||||
}) : assert(
|
||||
filePath != null || existingFileUri != null,
|
||||
'Either filePath or existingFileUri must be provided',
|
||||
);
|
||||
|
||||
/// The type of certification (e.g. "FOOD_HYGIENE", "SIA_BADGE").
|
||||
final String certificateType;
|
||||
@@ -40,8 +48,12 @@ class UploadCertificateParams {
|
||||
/// The name of the certificate.
|
||||
final String name;
|
||||
|
||||
/// The local file path to upload.
|
||||
final String filePath;
|
||||
/// The local file path to upload, or null when reusing an existing file.
|
||||
final String? filePath;
|
||||
|
||||
/// The remote URI of an already-uploaded file, used for metadata-only
|
||||
/// updates (e.g. changing only the expiry date).
|
||||
final String? existingFileUri;
|
||||
|
||||
/// The expiry date of the certificate.
|
||||
final DateTime? expiryDate;
|
||||
|
||||
@@ -43,6 +43,12 @@ class _CertificateUploadPageState extends State<CertificateUploadPage> {
|
||||
super.initState();
|
||||
_cubit = Modular.get<CertificateUploadCubit>();
|
||||
|
||||
// Pre-populate file path with existing remote URI when editing so
|
||||
// the form is valid without re-picking a file.
|
||||
if (widget.certificate?.fileUri != null) {
|
||||
_cubit.setSelectedFilePath(widget.certificate!.fileUri);
|
||||
}
|
||||
|
||||
if (widget.certificate != null) {
|
||||
_selectedExpiryDate = widget.certificate!.expiresAt;
|
||||
_issuerController.text = widget.certificate!.issuer ?? '';
|
||||
@@ -148,9 +154,7 @@ class _CertificateUploadPageState extends State<CertificateUploadPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider<CertificateUploadCubit>.value(
|
||||
value: _cubit..setSelectedFilePath(
|
||||
widget.certificate?.fileUri,
|
||||
),
|
||||
value: _cubit,
|
||||
child: BlocConsumer<CertificateUploadCubit, CertificateUploadState>(
|
||||
listener: (BuildContext context, CertificateUploadState state) {
|
||||
if (state.status == CertificateUploadStatus.success) {
|
||||
@@ -182,7 +186,8 @@ class _CertificateUploadPageState extends State<CertificateUploadPage> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
PdfFileTypesBanner(
|
||||
message: t.staff_documents.upload.pdf_banner,
|
||||
title: t.staff_documents.upload.pdf_banner_title,
|
||||
description: t.staff_documents.upload.pdf_banner_description,
|
||||
),
|
||||
const SizedBox(height: UiConstants.space6),
|
||||
|
||||
@@ -222,18 +227,27 @@ class _CertificateUploadPageState extends State<CertificateUploadPage> {
|
||||
padding: const EdgeInsets.all(UiConstants.space5),
|
||||
child: CertificateUploadActions(
|
||||
isAttested: state.isAttested,
|
||||
isFormValid: state.selectedFilePath != null &&
|
||||
isFormValid: (state.selectedFilePath != null ||
|
||||
widget.certificate?.fileUri != null) &&
|
||||
state.isAttested &&
|
||||
_nameController.text.isNotEmpty,
|
||||
isUploading: state.status == CertificateUploadStatus.uploading,
|
||||
hasExistingCertificate: widget.certificate != null,
|
||||
onUploadPressed: () {
|
||||
final String? selectedPath = state.selectedFilePath;
|
||||
final bool isLocalFile = selectedPath != null &&
|
||||
!selectedPath.startsWith('http') &&
|
||||
!selectedPath.startsWith('gs://');
|
||||
|
||||
BlocProvider.of<CertificateUploadCubit>(context)
|
||||
.uploadCertificate(
|
||||
UploadCertificateParams(
|
||||
certificateType: _selectedType,
|
||||
name: _nameController.text,
|
||||
filePath: state.selectedFilePath!,
|
||||
filePath: isLocalFile ? selectedPath : null,
|
||||
existingFileUri: !isLocalFile
|
||||
? (selectedPath ?? widget.certificate?.fileUri)
|
||||
: null,
|
||||
expiryDate: _selectedExpiryDate,
|
||||
issuer: _issuerController.text,
|
||||
certificateNumber: _numberController.text,
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:core_localization/core_localization.dart';
|
||||
/// Widget for certificate metadata input fields (name, issuer, number).
|
||||
class CertificateMetadataFields extends StatelessWidget {
|
||||
const CertificateMetadataFields({
|
||||
super.key,
|
||||
required this.nameController,
|
||||
required this.issuerController,
|
||||
required this.numberController,
|
||||
@@ -32,9 +33,7 @@ class CertificateMetadataFields extends StatelessWidget {
|
||||
enabled: isNewCertificate,
|
||||
decoration: InputDecoration(
|
||||
hintText: t.staff_certificates.upload_modal.name_hint,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: UiConstants.radiusLg,
|
||||
),
|
||||
border: OutlineInputBorder(borderRadius: UiConstants.radiusLg),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: UiConstants.space4),
|
||||
@@ -50,27 +49,20 @@ class CertificateMetadataFields extends StatelessWidget {
|
||||
enabled: isNewCertificate,
|
||||
decoration: InputDecoration(
|
||||
hintText: t.staff_certificates.upload_modal.issuer_hint,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: UiConstants.radiusLg,
|
||||
),
|
||||
border: OutlineInputBorder(borderRadius: UiConstants.radiusLg),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: UiConstants.space4),
|
||||
|
||||
// Certificate Number Field
|
||||
Text(
|
||||
'Certificate Number',
|
||||
style: UiTypography.body2m.textPrimary,
|
||||
),
|
||||
Text('Certificate Number', style: UiTypography.body2m.textPrimary),
|
||||
const SizedBox(height: UiConstants.space2),
|
||||
TextField(
|
||||
controller: numberController,
|
||||
enabled: isNewCertificate,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Enter number if applicable',
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: UiConstants.radiusLg,
|
||||
),
|
||||
border: OutlineInputBorder(borderRadius: UiConstants.radiusLg),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -8,6 +8,7 @@ import '../../blocs/certificate_upload/certificate_upload_cubit.dart';
|
||||
/// Widget for attestation checkbox and action buttons in certificate upload form.
|
||||
class CertificateUploadActions extends StatelessWidget {
|
||||
const CertificateUploadActions({
|
||||
super.key,
|
||||
required this.isAttested,
|
||||
required this.isFormValid,
|
||||
required this.isUploading,
|
||||
@@ -34,10 +35,9 @@ class CertificateUploadActions extends StatelessWidget {
|
||||
children: <Widget>[
|
||||
Checkbox(
|
||||
value: isAttested,
|
||||
onChanged: (bool? val) =>
|
||||
BlocProvider.of<CertificateUploadCubit>(context).setAttested(
|
||||
val ?? false,
|
||||
),
|
||||
onChanged: (bool? val) => BlocProvider.of<CertificateUploadCubit>(
|
||||
context,
|
||||
).setAttested(val ?? false),
|
||||
activeColor: UiColors.primary,
|
||||
),
|
||||
Expanded(
|
||||
@@ -54,17 +54,11 @@ class CertificateUploadActions extends StatelessWidget {
|
||||
onPressed: isFormValid ? onUploadPressed : null,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: UiColors.primary,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: UiConstants.space4,
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: UiConstants.radiusLg,
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(vertical: UiConstants.space4),
|
||||
shape: RoundedRectangleBorder(borderRadius: UiConstants.radiusLg),
|
||||
),
|
||||
child: isUploading
|
||||
? const CircularProgressIndicator(
|
||||
color: Colors.white,
|
||||
)
|
||||
? const CircularProgressIndicator(color: Colors.white)
|
||||
: Text(
|
||||
t.staff_certificates.upload_modal.save,
|
||||
style: UiTypography.body1m.white,
|
||||
@@ -87,9 +81,7 @@ class CertificateUploadActions extends StatelessWidget {
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: UiConstants.radiusLg,
|
||||
side: const BorderSide(
|
||||
color: UiColors.destructive,
|
||||
),
|
||||
side: const BorderSide(color: UiColors.destructive),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:core_localization/core_localization.dart';
|
||||
/// Widget for selecting certificate expiry date.
|
||||
class ExpiryDateField extends StatelessWidget {
|
||||
const ExpiryDateField({
|
||||
super.key,
|
||||
required this.selectedDate,
|
||||
required this.onTap,
|
||||
});
|
||||
|
||||
@@ -3,12 +3,24 @@ import 'package:flutter/material.dart';
|
||||
|
||||
/// Banner displaying accepted file types and size limit for PDF upload.
|
||||
class PdfFileTypesBanner extends StatelessWidget {
|
||||
const PdfFileTypesBanner({super.key, required this.message});
|
||||
const PdfFileTypesBanner({
|
||||
super.key,
|
||||
required this.title,
|
||||
this.description,
|
||||
});
|
||||
|
||||
final String message;
|
||||
/// Short title for the banner.
|
||||
final String title;
|
||||
|
||||
/// Optional description with additional details.
|
||||
final String? description;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return UiNoticeBanner(title: message, icon: UiIcons.info);
|
||||
return UiNoticeBanner(
|
||||
title: title,
|
||||
description: description,
|
||||
icon: UiIcons.info,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,23 +12,23 @@ class CertificatesHeaderSkeleton extends StatelessWidget {
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(UiConstants.space5),
|
||||
decoration: const BoxDecoration(color: UiColors.primary),
|
||||
child: SafeArea(
|
||||
child: const SafeArea(
|
||||
bottom: false,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
const SizedBox(height: UiConstants.space4),
|
||||
const UiShimmerCircle(size: 64),
|
||||
const SizedBox(height: UiConstants.space3),
|
||||
SizedBox(height: UiConstants.space4),
|
||||
UiShimmerCircle(size: 64),
|
||||
SizedBox(height: UiConstants.space3),
|
||||
UiShimmerLine(
|
||||
width: 120,
|
||||
height: 14,
|
||||
),
|
||||
const SizedBox(height: UiConstants.space2),
|
||||
SizedBox(height: UiConstants.space2),
|
||||
UiShimmerLine(
|
||||
width: 80,
|
||||
height: 12,
|
||||
),
|
||||
const SizedBox(height: UiConstants.space6),
|
||||
SizedBox(height: UiConstants.space6),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -70,7 +70,8 @@ class DocumentUploadPage extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
PdfFileTypesBanner(
|
||||
message: t.staff_documents.upload.pdf_banner,
|
||||
title: t.staff_documents.upload.pdf_banner_title,
|
||||
description: t.staff_documents.upload.pdf_banner_description,
|
||||
),
|
||||
const SizedBox(height: UiConstants.space6),
|
||||
DocumentFileSelector(
|
||||
|
||||
@@ -3,12 +3,24 @@ import 'package:flutter/material.dart';
|
||||
|
||||
/// Banner displaying accepted file types and size limit for PDF upload.
|
||||
class PdfFileTypesBanner extends StatelessWidget {
|
||||
const PdfFileTypesBanner({required this.message, super.key});
|
||||
const PdfFileTypesBanner({
|
||||
super.key,
|
||||
required this.title,
|
||||
this.description,
|
||||
});
|
||||
|
||||
final String message;
|
||||
/// Short title for the banner.
|
||||
final String title;
|
||||
|
||||
/// Optional description with additional details.
|
||||
final String? description;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return UiNoticeBanner(title: message, icon: UiIcons.info);
|
||||
return UiNoticeBanner(
|
||||
title: title,
|
||||
description: description,
|
||||
icon: UiIcons.info,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,14 +121,14 @@ class _FormI9PageState extends State<FormI9Page> {
|
||||
|
||||
void _handleNext(BuildContext context, int currentStep) {
|
||||
if (currentStep < _steps.length - 1) {
|
||||
context.read<FormI9Cubit>().nextStep(_steps.length);
|
||||
ReadContext(context).read<FormI9Cubit>().nextStep(_steps.length);
|
||||
} else {
|
||||
context.read<FormI9Cubit>().submit();
|
||||
ReadContext(context).read<FormI9Cubit>().submit();
|
||||
}
|
||||
}
|
||||
|
||||
void _handleBack(BuildContext context) {
|
||||
context.read<FormI9Cubit>().previousStep();
|
||||
ReadContext(context).read<FormI9Cubit>().previousStep();
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -178,8 +178,9 @@ class _FormI9PageState extends State<FormI9Page> {
|
||||
}
|
||||
},
|
||||
builder: (BuildContext context, FormI9State state) {
|
||||
if (state.status == FormI9Status.success)
|
||||
if (state.status == FormI9Status.success) {
|
||||
return _buildSuccessView(i18n);
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: UiColors.background,
|
||||
@@ -458,7 +459,7 @@ class _FormI9PageState extends State<FormI9Page> {
|
||||
i18n.fields.first_name,
|
||||
value: state.firstName,
|
||||
onChanged: (String val) =>
|
||||
context.read<FormI9Cubit>().firstNameChanged(val),
|
||||
ReadContext(context).read<FormI9Cubit>().firstNameChanged(val),
|
||||
placeholder: i18n.fields.hints.first_name,
|
||||
),
|
||||
),
|
||||
@@ -468,7 +469,7 @@ class _FormI9PageState extends State<FormI9Page> {
|
||||
i18n.fields.last_name,
|
||||
value: state.lastName,
|
||||
onChanged: (String val) =>
|
||||
context.read<FormI9Cubit>().lastNameChanged(val),
|
||||
ReadContext(context).read<FormI9Cubit>().lastNameChanged(val),
|
||||
placeholder: i18n.fields.hints.last_name,
|
||||
),
|
||||
),
|
||||
@@ -482,7 +483,7 @@ class _FormI9PageState extends State<FormI9Page> {
|
||||
i18n.fields.middle_initial,
|
||||
value: state.middleInitial,
|
||||
onChanged: (String val) =>
|
||||
context.read<FormI9Cubit>().middleInitialChanged(val),
|
||||
ReadContext(context).read<FormI9Cubit>().middleInitialChanged(val),
|
||||
placeholder: i18n.fields.hints.middle_initial,
|
||||
),
|
||||
),
|
||||
@@ -493,7 +494,7 @@ class _FormI9PageState extends State<FormI9Page> {
|
||||
i18n.fields.other_last_names,
|
||||
value: state.otherLastNames,
|
||||
onChanged: (String val) =>
|
||||
context.read<FormI9Cubit>().otherLastNamesChanged(val),
|
||||
ReadContext(context).read<FormI9Cubit>().otherLastNamesChanged(val),
|
||||
placeholder: i18n.fields.maiden_name,
|
||||
),
|
||||
),
|
||||
@@ -504,7 +505,7 @@ class _FormI9PageState extends State<FormI9Page> {
|
||||
i18n.fields.dob,
|
||||
value: state.dob,
|
||||
onChanged: (String val) =>
|
||||
context.read<FormI9Cubit>().dobChanged(val),
|
||||
ReadContext(context).read<FormI9Cubit>().dobChanged(val),
|
||||
placeholder: i18n.fields.hints.dob,
|
||||
keyboardType: TextInputType.datetime,
|
||||
),
|
||||
@@ -517,7 +518,7 @@ class _FormI9PageState extends State<FormI9Page> {
|
||||
onChanged: (String val) {
|
||||
String text = val.replaceAll(RegExp(r'\D'), '');
|
||||
if (text.length > 9) text = text.substring(0, 9);
|
||||
context.read<FormI9Cubit>().ssnChanged(text);
|
||||
ReadContext(context).read<FormI9Cubit>().ssnChanged(text);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: UiConstants.space4),
|
||||
@@ -525,7 +526,7 @@ class _FormI9PageState extends State<FormI9Page> {
|
||||
i18n.fields.email,
|
||||
value: state.email,
|
||||
onChanged: (String val) =>
|
||||
context.read<FormI9Cubit>().emailChanged(val),
|
||||
ReadContext(context).read<FormI9Cubit>().emailChanged(val),
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
placeholder: i18n.fields.hints.email,
|
||||
),
|
||||
@@ -534,7 +535,7 @@ class _FormI9PageState extends State<FormI9Page> {
|
||||
i18n.fields.phone,
|
||||
value: state.phone,
|
||||
onChanged: (String val) =>
|
||||
context.read<FormI9Cubit>().phoneChanged(val),
|
||||
ReadContext(context).read<FormI9Cubit>().phoneChanged(val),
|
||||
keyboardType: TextInputType.phone,
|
||||
placeholder: i18n.fields.hints.phone,
|
||||
),
|
||||
@@ -553,7 +554,7 @@ class _FormI9PageState extends State<FormI9Page> {
|
||||
i18n.fields.address_long,
|
||||
value: state.address,
|
||||
onChanged: (String val) =>
|
||||
context.read<FormI9Cubit>().addressChanged(val),
|
||||
ReadContext(context).read<FormI9Cubit>().addressChanged(val),
|
||||
placeholder: i18n.fields.hints.address,
|
||||
),
|
||||
const SizedBox(height: UiConstants.space4),
|
||||
@@ -561,7 +562,7 @@ class _FormI9PageState extends State<FormI9Page> {
|
||||
i18n.fields.apt,
|
||||
value: state.aptNumber,
|
||||
onChanged: (String val) =>
|
||||
context.read<FormI9Cubit>().aptNumberChanged(val),
|
||||
ReadContext(context).read<FormI9Cubit>().aptNumberChanged(val),
|
||||
placeholder: i18n.fields.hints.apt,
|
||||
),
|
||||
const SizedBox(height: UiConstants.space4),
|
||||
@@ -573,7 +574,7 @@ class _FormI9PageState extends State<FormI9Page> {
|
||||
i18n.fields.city,
|
||||
value: state.city,
|
||||
onChanged: (String val) =>
|
||||
context.read<FormI9Cubit>().cityChanged(val),
|
||||
ReadContext(context).read<FormI9Cubit>().cityChanged(val),
|
||||
placeholder: i18n.fields.hints.city,
|
||||
),
|
||||
),
|
||||
@@ -592,7 +593,7 @@ class _FormI9PageState extends State<FormI9Page> {
|
||||
DropdownButtonFormField<String>(
|
||||
initialValue: state.state.isEmpty ? null : state.state,
|
||||
onChanged: (String? val) =>
|
||||
context.read<FormI9Cubit>().stateChanged(val ?? ''),
|
||||
ReadContext(context).read<FormI9Cubit>().stateChanged(val ?? ''),
|
||||
items: _usStates.map((String stateAbbr) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: stateAbbr,
|
||||
@@ -625,7 +626,7 @@ class _FormI9PageState extends State<FormI9Page> {
|
||||
i18n.fields.zip,
|
||||
value: state.zipCode,
|
||||
onChanged: (String val) =>
|
||||
context.read<FormI9Cubit>().zipCodeChanged(val),
|
||||
ReadContext(context).read<FormI9Cubit>().zipCodeChanged(val),
|
||||
placeholder: i18n.fields.hints.zip,
|
||||
keyboardType: TextInputType.number,
|
||||
),
|
||||
@@ -659,7 +660,7 @@ class _FormI9PageState extends State<FormI9Page> {
|
||||
i18n.fields.uscis_number_label,
|
||||
value: state.uscisNumber,
|
||||
onChanged: (String val) =>
|
||||
context.read<FormI9Cubit>().uscisNumberChanged(val),
|
||||
ReadContext(context).read<FormI9Cubit>().uscisNumberChanged(val),
|
||||
placeholder: i18n.fields.hints.uscis,
|
||||
),
|
||||
)
|
||||
@@ -717,7 +718,7 @@ class _FormI9PageState extends State<FormI9Page> {
|
||||
}) {
|
||||
final bool isSelected = state.citizenshipStatus == value;
|
||||
return GestureDetector(
|
||||
onTap: () => context.read<FormI9Cubit>().citizenshipStatusChanged(value),
|
||||
onTap: () => ReadContext(context).read<FormI9Cubit>().citizenshipStatusChanged(value),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(UiConstants.space4),
|
||||
decoration: BoxDecoration(
|
||||
@@ -802,7 +803,7 @@ class _FormI9PageState extends State<FormI9Page> {
|
||||
CheckboxListTile(
|
||||
value: state.preparerUsed,
|
||||
onChanged: (bool? val) {
|
||||
context.read<FormI9Cubit>().preparerUsedChanged(val ?? false);
|
||||
ReadContext(context).read<FormI9Cubit>().preparerUsedChanged(val ?? false);
|
||||
},
|
||||
contentPadding: EdgeInsets.zero,
|
||||
title: Text(
|
||||
@@ -836,7 +837,7 @@ class _FormI9PageState extends State<FormI9Page> {
|
||||
TextPosition(offset: state.signature.length),
|
||||
),
|
||||
onChanged: (String val) =>
|
||||
context.read<FormI9Cubit>().signatureChanged(val),
|
||||
ReadContext(context).read<FormI9Cubit>().signatureChanged(val),
|
||||
decoration: InputDecoration(
|
||||
hintText: i18n.fields.signature_hint,
|
||||
filled: true,
|
||||
|
||||
@@ -111,14 +111,14 @@ class _FormW4PageState extends State<FormW4Page> {
|
||||
|
||||
void _handleNext(BuildContext context, int currentStep) {
|
||||
if (currentStep < _steps.length - 1) {
|
||||
context.read<FormW4Cubit>().nextStep(_steps.length);
|
||||
ReadContext(context).read<FormW4Cubit>().nextStep(_steps.length);
|
||||
} else {
|
||||
context.read<FormW4Cubit>().submit();
|
||||
ReadContext(context).read<FormW4Cubit>().submit();
|
||||
}
|
||||
}
|
||||
|
||||
void _handleBack(BuildContext context) {
|
||||
context.read<FormW4Cubit>().previousStep();
|
||||
ReadContext(context).read<FormW4Cubit>().previousStep();
|
||||
}
|
||||
|
||||
int _totalCredits(FormW4State state) {
|
||||
@@ -180,8 +180,9 @@ class _FormW4PageState extends State<FormW4Page> {
|
||||
}
|
||||
},
|
||||
builder: (BuildContext context, FormW4State state) {
|
||||
if (state.status == FormW4Status.success)
|
||||
if (state.status == FormW4Status.success) {
|
||||
return _buildSuccessView(i18n);
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: UiColors.background,
|
||||
@@ -457,7 +458,7 @@ class _FormW4PageState extends State<FormW4Page> {
|
||||
i18n.fields.first_name,
|
||||
value: state.firstName,
|
||||
onChanged: (String val) =>
|
||||
context.read<FormW4Cubit>().firstNameChanged(val),
|
||||
ReadContext(context).read<FormW4Cubit>().firstNameChanged(val),
|
||||
placeholder: i18n.fields.placeholder_john,
|
||||
),
|
||||
),
|
||||
@@ -467,7 +468,7 @@ class _FormW4PageState extends State<FormW4Page> {
|
||||
i18n.fields.last_name,
|
||||
value: state.lastName,
|
||||
onChanged: (String val) =>
|
||||
context.read<FormW4Cubit>().lastNameChanged(val),
|
||||
ReadContext(context).read<FormW4Cubit>().lastNameChanged(val),
|
||||
placeholder: i18n.fields.placeholder_smith,
|
||||
),
|
||||
),
|
||||
@@ -482,7 +483,7 @@ class _FormW4PageState extends State<FormW4Page> {
|
||||
onChanged: (String val) {
|
||||
String text = val.replaceAll(RegExp(r'\D'), '');
|
||||
if (text.length > 9) text = text.substring(0, 9);
|
||||
context.read<FormW4Cubit>().ssnChanged(text);
|
||||
ReadContext(context).read<FormW4Cubit>().ssnChanged(text);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: UiConstants.space4),
|
||||
@@ -490,7 +491,7 @@ class _FormW4PageState extends State<FormW4Page> {
|
||||
i18n.fields.address,
|
||||
value: state.address,
|
||||
onChanged: (String val) =>
|
||||
context.read<FormW4Cubit>().addressChanged(val),
|
||||
ReadContext(context).read<FormW4Cubit>().addressChanged(val),
|
||||
placeholder: i18n.fields.placeholder_address,
|
||||
),
|
||||
const SizedBox(height: UiConstants.space4),
|
||||
@@ -498,7 +499,7 @@ class _FormW4PageState extends State<FormW4Page> {
|
||||
i18n.fields.city_state_zip,
|
||||
value: state.cityStateZip,
|
||||
onChanged: (String val) =>
|
||||
context.read<FormW4Cubit>().cityStateZipChanged(val),
|
||||
ReadContext(context).read<FormW4Cubit>().cityStateZipChanged(val),
|
||||
placeholder: i18n.fields.placeholder_csz,
|
||||
),
|
||||
],
|
||||
@@ -556,7 +557,7 @@ class _FormW4PageState extends State<FormW4Page> {
|
||||
) {
|
||||
final bool isSelected = state.filingStatus == value;
|
||||
return GestureDetector(
|
||||
onTap: () => context.read<FormW4Cubit>().filingStatusChanged(value),
|
||||
onTap: () => ReadContext(context).read<FormW4Cubit>().filingStatusChanged(value),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(UiConstants.space4),
|
||||
decoration: BoxDecoration(
|
||||
@@ -640,7 +641,7 @@ class _FormW4PageState extends State<FormW4Page> {
|
||||
),
|
||||
const SizedBox(height: UiConstants.space6),
|
||||
GestureDetector(
|
||||
onTap: () => context.read<FormW4Cubit>().multipleJobsChanged(
|
||||
onTap: () => ReadContext(context).read<FormW4Cubit>().multipleJobsChanged(
|
||||
!state.multipleJobs,
|
||||
),
|
||||
child: Container(
|
||||
@@ -751,7 +752,7 @@ class _FormW4PageState extends State<FormW4Page> {
|
||||
i18n.fields.children_each,
|
||||
(FormW4State s) => s.qualifyingChildren,
|
||||
(int val) =>
|
||||
context.read<FormW4Cubit>().qualifyingChildrenChanged(val),
|
||||
ReadContext(context).read<FormW4Cubit>().qualifyingChildrenChanged(val),
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 16),
|
||||
@@ -764,7 +765,7 @@ class _FormW4PageState extends State<FormW4Page> {
|
||||
i18n.fields.other_each,
|
||||
(FormW4State s) => s.otherDependents,
|
||||
(int val) =>
|
||||
context.read<FormW4Cubit>().otherDependentsChanged(val),
|
||||
ReadContext(context).read<FormW4Cubit>().otherDependentsChanged(val),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -880,7 +881,7 @@ class _FormW4PageState extends State<FormW4Page> {
|
||||
i18n.fields.other_income,
|
||||
value: state.otherIncome,
|
||||
onChanged: (String val) =>
|
||||
context.read<FormW4Cubit>().otherIncomeChanged(val),
|
||||
ReadContext(context).read<FormW4Cubit>().otherIncomeChanged(val),
|
||||
placeholder: i18n.fields.hints.zero,
|
||||
keyboardType: TextInputType.number,
|
||||
),
|
||||
@@ -896,7 +897,7 @@ class _FormW4PageState extends State<FormW4Page> {
|
||||
i18n.fields.deductions,
|
||||
value: state.deductions,
|
||||
onChanged: (String val) =>
|
||||
context.read<FormW4Cubit>().deductionsChanged(val),
|
||||
ReadContext(context).read<FormW4Cubit>().deductionsChanged(val),
|
||||
placeholder: i18n.fields.hints.zero,
|
||||
keyboardType: TextInputType.number,
|
||||
),
|
||||
@@ -912,7 +913,7 @@ class _FormW4PageState extends State<FormW4Page> {
|
||||
i18n.fields.extra_withholding,
|
||||
value: state.extraWithholding,
|
||||
onChanged: (String val) =>
|
||||
context.read<FormW4Cubit>().extraWithholdingChanged(val),
|
||||
ReadContext(context).read<FormW4Cubit>().extraWithholdingChanged(val),
|
||||
placeholder: i18n.fields.hints.zero,
|
||||
keyboardType: TextInputType.number,
|
||||
),
|
||||
@@ -995,7 +996,7 @@ class _FormW4PageState extends State<FormW4Page> {
|
||||
TextPosition(offset: state.signature.length),
|
||||
),
|
||||
onChanged: (String val) =>
|
||||
context.read<FormW4Cubit>().signatureChanged(val),
|
||||
ReadContext(context).read<FormW4Cubit>().signatureChanged(val),
|
||||
decoration: InputDecoration(
|
||||
hintText: i18n.fields.signature_hint,
|
||||
filled: true,
|
||||
|
||||
@@ -4,7 +4,7 @@ import 'package:krow_domain/krow_domain.dart';
|
||||
|
||||
/// Widget displaying the overall progress of tax form completion.
|
||||
class TaxFormsProgressOverview extends StatelessWidget {
|
||||
const TaxFormsProgressOverview({required this.forms});
|
||||
const TaxFormsProgressOverview({super.key, required this.forms});
|
||||
|
||||
final List<TaxForm> forms;
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import 'package:krow_domain/krow_domain.dart';
|
||||
|
||||
/// Widget displaying status badge for a tax form.
|
||||
class TaxFormStatusBadge extends StatelessWidget {
|
||||
const TaxFormStatusBadge({required this.status});
|
||||
const TaxFormStatusBadge({super.key, required this.status});
|
||||
|
||||
final TaxFormStatus status;
|
||||
|
||||
|
||||
@@ -3,15 +3,10 @@ import 'package:flutter/material.dart';
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
|
||||
class AccountCard extends StatelessWidget {
|
||||
const AccountCard({super.key, required this.account, required this.strings});
|
||||
final BankAccount account;
|
||||
final dynamic strings;
|
||||
|
||||
const AccountCard({
|
||||
super.key,
|
||||
required this.account,
|
||||
required this.strings,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final bool isPrimary = account.isPrimary;
|
||||
|
||||
@@ -1,45 +1,27 @@
|
||||
// ignore_for_file: always_specify_types, depend_on_referenced_packages, dead_code, dead_null_aware_expression, unused_local_variable, unused_import, sort_constructors_first, prefer_final_fields, prefer_const_constructors, deprecated_member_use, implicit_call_tearoffs, implementation_imports
|
||||
import 'package:core_localization/core_localization.dart';
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_modular/flutter_modular.dart';
|
||||
import 'package:core_localization/core_localization.dart';
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:krow_core/core.dart';
|
||||
import '../blocs/time_card_bloc.dart';
|
||||
import '../widgets/month_selector.dart';
|
||||
import '../widgets/shift_history_list.dart';
|
||||
import '../widgets/time_card_skeleton/time_card_skeleton.dart';
|
||||
import '../widgets/time_card_summary.dart';
|
||||
import 'package:staff_time_card/src/presentation/blocs/time_card_bloc.dart';
|
||||
import 'package:staff_time_card/src/presentation/widgets/month_selector.dart';
|
||||
import 'package:staff_time_card/src/presentation/widgets/shift_history_list.dart';
|
||||
import 'package:staff_time_card/src/presentation/widgets/time_card_skeleton/time_card_skeleton.dart';
|
||||
import 'package:staff_time_card/src/presentation/widgets/time_card_summary.dart';
|
||||
|
||||
/// The main page for displaying the staff time card.
|
||||
class TimeCardPage extends StatefulWidget {
|
||||
class TimeCardPage extends StatelessWidget {
|
||||
/// Creates a [TimeCardPage].
|
||||
const TimeCardPage({super.key});
|
||||
|
||||
@override
|
||||
State<TimeCardPage> createState() => _TimeCardPageState();
|
||||
}
|
||||
|
||||
class _TimeCardPageState extends State<TimeCardPage> {
|
||||
late final TimeCardBloc _bloc;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_bloc = Modular.get<TimeCardBloc>();
|
||||
_bloc.add(LoadTimeCards(DateTime.now()));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final Translations t = Translations.of(context);
|
||||
return BlocProvider.value(
|
||||
value: _bloc,
|
||||
child: Scaffold(
|
||||
appBar: UiAppBar(
|
||||
title: t.staff_time_card.title,
|
||||
showBackButton: true,
|
||||
),
|
||||
body: BlocConsumer<TimeCardBloc, TimeCardState>(
|
||||
return Scaffold(
|
||||
appBar: UiAppBar(title: t.staff_time_card.title, showBackButton: true),
|
||||
body: BlocProvider<TimeCardBloc>.value(
|
||||
value: Modular.get<TimeCardBloc>()..add(LoadTimeCards(DateTime.now())),
|
||||
child: BlocConsumer<TimeCardBloc, TimeCardState>(
|
||||
listener: (BuildContext context, TimeCardState state) {
|
||||
if (state is TimeCardError) {
|
||||
UiSnackbar.show(
|
||||
@@ -75,22 +57,24 @@ class _TimeCardPageState extends State<TimeCardPage> {
|
||||
children: <Widget>[
|
||||
MonthSelector(
|
||||
selectedDate: state.selectedMonth,
|
||||
onPreviousMonth: () => _bloc.add(
|
||||
ChangeMonth(
|
||||
DateTime(
|
||||
state.selectedMonth.year,
|
||||
state.selectedMonth.month - 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
onNextMonth: () => _bloc.add(
|
||||
ChangeMonth(
|
||||
DateTime(
|
||||
state.selectedMonth.year,
|
||||
state.selectedMonth.month + 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
onPreviousMonth: () =>
|
||||
ReadContext(context).read<TimeCardBloc>().add(
|
||||
ChangeMonth(
|
||||
DateTime(
|
||||
state.selectedMonth.year,
|
||||
state.selectedMonth.month - 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
onNextMonth: () =>
|
||||
ReadContext(context).read<TimeCardBloc>().add(
|
||||
ChangeMonth(
|
||||
DateTime(
|
||||
state.selectedMonth.year,
|
||||
state.selectedMonth.month + 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: UiConstants.space6),
|
||||
TimeCardSummary(
|
||||
|
||||
@@ -17,9 +17,7 @@ class ShiftHistoryList extends StatelessWidget {
|
||||
children: <Widget>[
|
||||
Text(
|
||||
t.staff_time_card.shift_history,
|
||||
style: UiTypography.title2b.copyWith(
|
||||
color: UiColors.textPrimary,
|
||||
),
|
||||
style: UiTypography.title2b,
|
||||
),
|
||||
const SizedBox(height: UiConstants.space3),
|
||||
if (timesheets.isEmpty)
|
||||
|
||||
@@ -6,7 +6,6 @@ import 'package:krow_domain/krow_domain.dart';
|
||||
|
||||
/// A card widget displaying details of a single shift/timecard.
|
||||
class TimesheetCard extends StatelessWidget {
|
||||
|
||||
const TimesheetCard({super.key, required this.timesheet});
|
||||
final TimeCardEntry timesheet;
|
||||
|
||||
@@ -25,9 +24,10 @@ class TimesheetCard extends StatelessWidget {
|
||||
decoration: BoxDecoration(
|
||||
color: UiColors.bgPopup,
|
||||
borderRadius: UiConstants.radiusLg,
|
||||
border: Border.all(color: UiColors.border),
|
||||
border: Border.all(color: UiColors.border, width: 0.5),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
@@ -60,20 +60,22 @@ class TimesheetCard extends StatelessWidget {
|
||||
if (timesheet.clockInAt != null && timesheet.clockOutAt != null)
|
||||
_IconText(
|
||||
icon: UiIcons.clock,
|
||||
text: '${DateFormat('h:mm a').format(timesheet.clockInAt!)} - ${DateFormat('h:mm a').format(timesheet.clockOutAt!)}',
|
||||
text:
|
||||
'${DateFormat('h:mm a').format(timesheet.clockInAt!)} - ${DateFormat('h:mm a').format(timesheet.clockOutAt!)}',
|
||||
),
|
||||
if (timesheet.location != null)
|
||||
_IconText(icon: UiIcons.mapPin, text: timesheet.location!),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: UiConstants.space3),
|
||||
const SizedBox(height: UiConstants.space5),
|
||||
Container(
|
||||
padding: const EdgeInsets.only(top: UiConstants.space3),
|
||||
padding: const EdgeInsets.only(top: UiConstants.space4),
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(top: BorderSide(color: UiColors.border)),
|
||||
border: Border(top: BorderSide(color: UiColors.border, width: 0.5)),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
'${totalHours.toStringAsFixed(1)} ${t.staff_time_card.hours} @ \$${hourlyRate.toStringAsFixed(2)}${t.staff_time_card.per_hr}',
|
||||
@@ -81,7 +83,7 @@ class TimesheetCard extends StatelessWidget {
|
||||
),
|
||||
Text(
|
||||
'\$${totalPay.toStringAsFixed(2)}',
|
||||
style: UiTypography.title2b.primary,
|
||||
style: UiTypography.title1b,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -93,7 +95,6 @@ class TimesheetCard extends StatelessWidget {
|
||||
}
|
||||
|
||||
class _IconText extends StatelessWidget {
|
||||
|
||||
const _IconText({required this.icon, required this.text});
|
||||
final IconData icon;
|
||||
final String text;
|
||||
@@ -105,10 +106,7 @@ class _IconText extends StatelessWidget {
|
||||
children: <Widget>[
|
||||
Icon(icon, size: 14, color: UiColors.iconSecondary),
|
||||
const SizedBox(width: UiConstants.space1),
|
||||
Text(
|
||||
text,
|
||||
style: UiTypography.body2r.textSecondary,
|
||||
),
|
||||
Text(text, style: UiTypography.body2r.textSecondary),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ class StaffTimeCardModule extends Module {
|
||||
);
|
||||
|
||||
// UseCases
|
||||
i.add<GetTimeCardsUseCase>(GetTimeCardsUseCase.new);
|
||||
i.addLazySingleton<GetTimeCardsUseCase>(GetTimeCardsUseCase.new);
|
||||
|
||||
// Blocs
|
||||
i.add<TimeCardBloc>(TimeCardBloc.new);
|
||||
|
||||
@@ -11,7 +11,7 @@ class EmergencyContactAddButton extends StatelessWidget {
|
||||
return Center(
|
||||
child: TextButton.icon(
|
||||
onPressed: () =>
|
||||
context.read<EmergencyContactBloc>().add(EmergencyContactAdded()),
|
||||
ReadContext(context).read<EmergencyContactBloc>().add(EmergencyContactAdded()),
|
||||
icon: const Icon(UiIcons.add, size: 20.0),
|
||||
label: Text(
|
||||
'Add Another Contact',
|
||||
|
||||
@@ -44,7 +44,7 @@ class EmergencyContactFormItem extends StatelessWidget {
|
||||
initialValue: contact.fullName,
|
||||
hint: 'Contact name',
|
||||
icon: UiIcons.user,
|
||||
onChanged: (val) => context.read<EmergencyContactBloc>().add(
|
||||
onChanged: (val) => ReadContext(context).read<EmergencyContactBloc>().add(
|
||||
EmergencyContactUpdated(index, contact.copyWith(fullName: val)),
|
||||
),
|
||||
),
|
||||
@@ -54,7 +54,7 @@ class EmergencyContactFormItem extends StatelessWidget {
|
||||
initialValue: contact.phone,
|
||||
hint: '+1 (555) 000-0000',
|
||||
icon: UiIcons.phone,
|
||||
onChanged: (val) => context.read<EmergencyContactBloc>().add(
|
||||
onChanged: (val) => ReadContext(context).read<EmergencyContactBloc>().add(
|
||||
EmergencyContactUpdated(index, contact.copyWith(phone: val)),
|
||||
),
|
||||
),
|
||||
@@ -66,7 +66,7 @@ class EmergencyContactFormItem extends StatelessWidget {
|
||||
items: _kRelationshipTypes,
|
||||
onChanged: (val) {
|
||||
if (val != null) {
|
||||
context.read<EmergencyContactBloc>().add(
|
||||
ReadContext(context).read<EmergencyContactBloc>().add(
|
||||
EmergencyContactUpdated(
|
||||
index,
|
||||
contact.copyWith(relationshipType: val),
|
||||
@@ -144,7 +144,7 @@ class EmergencyContactFormItem extends StatelessWidget {
|
||||
color: UiColors.textError,
|
||||
size: 20.0,
|
||||
),
|
||||
onPressed: () => context.read<EmergencyContactBloc>().add(
|
||||
onPressed: () => ReadContext(context).read<EmergencyContactBloc>().add(
|
||||
EmergencyContactRemoved(index),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -39,11 +39,11 @@ class TappableRow extends StatelessWidget {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
spacing: UiConstants.space3,
|
||||
children: [
|
||||
children: <Widget>[
|
||||
FieldLabel(text: i18n.locations_label),
|
||||
GestureDetector(
|
||||
onTap: enabled ? onTap : null,
|
||||
child: Container(
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
|
||||
@@ -37,9 +37,9 @@ class _FaqsWidgetState extends State<FaqsWidget> {
|
||||
|
||||
void _onSearchChanged(String value) {
|
||||
if (value.isEmpty) {
|
||||
context.read<FaqsBloc>().add(const FetchFaqsEvent());
|
||||
ReadContext(context).read<FaqsBloc>().add(const FetchFaqsEvent());
|
||||
} else {
|
||||
context.read<FaqsBloc>().add(SearchFaqsEvent(query: value));
|
||||
ReadContext(context).read<FaqsBloc>().add(SearchFaqsEvent(query: value));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ class PrivacySectionWidget extends StatelessWidget {
|
||||
type: UiSnackbarType.success,
|
||||
);
|
||||
// Clear the flag after showing the snackbar
|
||||
context.read<PrivacySecurityBloc>().add(
|
||||
ReadContext(context).read<PrivacySecurityBloc>().add(
|
||||
const ClearProfileVisibilityUpdatedEvent(),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user