From 078f828aadb811e86a319edc15b96de1976e55d0 Mon Sep 17 00:00:00 2001 From: Achintha Isuru Date: Tue, 27 Jan 2026 16:25:47 -0500 Subject: [PATCH] feat: introduce TaxForm entity and adapter, refactor tax forms repository and use cases for improved data handling --- .../packages/domain/lib/krow_domain.dart | 2 + .../adapters/profile/tax_form_adapter.dart | 86 ++++++++++ .../lib/src/entities/profile/tax_form.dart | 45 ++++++ .../tax_forms_repository_impl.dart | 148 ++++++------------ .../repositories/tax_forms_repository.dart | 4 +- .../usecases/get_tax_forms_usecase.dart | 4 +- .../usecases/submit_tax_form_usecase.dart | 2 +- .../presentation/blocs/i9/form_i9_cubit.dart | 3 +- .../blocs/tax_forms/tax_forms_cubit.dart | 4 +- .../blocs/tax_forms/tax_forms_state.dart | 8 +- .../presentation/blocs/w4/form_w4_cubit.dart | 3 +- .../presentation/pages/tax_forms_page.dart | 110 +++++++------ 12 files changed, 253 insertions(+), 166 deletions(-) create mode 100644 apps/mobile/packages/domain/lib/src/adapters/profile/tax_form_adapter.dart create mode 100644 apps/mobile/packages/domain/lib/src/entities/profile/tax_form.dart diff --git a/apps/mobile/packages/domain/lib/krow_domain.dart b/apps/mobile/packages/domain/lib/krow_domain.dart index 710b8989..fc4d87f9 100644 --- a/apps/mobile/packages/domain/lib/krow_domain.dart +++ b/apps/mobile/packages/domain/lib/krow_domain.dart @@ -54,6 +54,7 @@ export 'src/entities/profile/staff_document.dart'; export 'src/entities/profile/attire_item.dart'; export 'src/entities/profile/relationship_type.dart'; export 'src/entities/profile/industry.dart'; +export 'src/entities/profile/tax_form.dart'; // Ratings & Penalties export 'src/entities/ratings/staff_rating.dart'; @@ -85,3 +86,4 @@ export 'src/adapters/profile/emergency_contact_adapter.dart'; export 'src/adapters/profile/experience_adapter.dart'; export 'src/entities/profile/experience_skill.dart'; export 'src/adapters/profile/bank_account_adapter.dart'; +export 'src/adapters/profile/tax_form_adapter.dart'; diff --git a/apps/mobile/packages/domain/lib/src/adapters/profile/tax_form_adapter.dart b/apps/mobile/packages/domain/lib/src/adapters/profile/tax_form_adapter.dart new file mode 100644 index 00000000..41f85479 --- /dev/null +++ b/apps/mobile/packages/domain/lib/src/adapters/profile/tax_form_adapter.dart @@ -0,0 +1,86 @@ +import '../../entities/profile/tax_form.dart'; + +/// Adapter for [TaxForm] to map data layer values to domain entity. +class TaxFormAdapter { + /// Maps primitive values to [TaxForm]. + static TaxForm fromPrimitives({ + required String id, + required String type, + required String title, + String? subtitle, + String? description, + required String status, + String? staffId, + dynamic formData, + DateTime? createdAt, + DateTime? updatedAt, + }) { + return TaxForm( + id: id, + type: _stringToType(type), + title: title, + subtitle: subtitle, + description: description, + status: _stringToStatus(status), + staffId: staffId, + formData: formData is Map ? Map.from(formData) : null, + createdAt: createdAt, + updatedAt: updatedAt, + ); + } + + static TaxFormType _stringToType(String? value) { + if (value == null) return TaxFormType.i9; + try { + return TaxFormType.values.firstWhere( + (TaxFormType e) => e.name.toLowerCase() == value.toLowerCase(), + orElse: () => TaxFormType.i9, + ); + } catch (_) { + return TaxFormType.i9; + } + } + + static TaxFormStatus _stringToStatus(String? value) { + if (value == null) return TaxFormStatus.notStarted; + try { + final String normalizedValue = value.replaceAll('_', '').toLowerCase(); + // map DRAFT to inProgress + if (normalizedValue == 'draft') return TaxFormStatus.inProgress; + + return TaxFormStatus.values.firstWhere( + (TaxFormStatus e) { + // Handle differences like not_started vs notStarted if any, + // but standardizing to lowercase is a good start. + // The enum names are camelCase in Dart, but might be SNAKE_CASE from backend. + final String normalizedEnum = e.name.toLowerCase(); + return normalizedValue == normalizedEnum; + }, + orElse: () => TaxFormStatus.notStarted, + ); + } catch (_) { + return TaxFormStatus.notStarted; + } + } + + /// Converts domain [TaxFormType] to string for backend. + static String typeToString(TaxFormType type) { + return type.name.toUpperCase(); + } + + /// Converts domain [TaxFormStatus] to string for backend. + static String statusToString(TaxFormStatus status) { + switch (status) { + case TaxFormStatus.notStarted: + return 'NOT_STARTED'; + case TaxFormStatus.inProgress: + return 'DRAFT'; + case TaxFormStatus.submitted: + return 'SUBMITTED'; + case TaxFormStatus.approved: + return 'APPROVED'; + case TaxFormStatus.rejected: + return 'REJECTED'; + } + } +} diff --git a/apps/mobile/packages/domain/lib/src/entities/profile/tax_form.dart b/apps/mobile/packages/domain/lib/src/entities/profile/tax_form.dart new file mode 100644 index 00000000..096380c5 --- /dev/null +++ b/apps/mobile/packages/domain/lib/src/entities/profile/tax_form.dart @@ -0,0 +1,45 @@ +import 'package:equatable/equatable.dart'; + +enum TaxFormType { i9, w4 } + +enum TaxFormStatus { notStarted, inProgress, submitted, approved, rejected } + +class TaxForm extends Equatable { + final String id; + final TaxFormType type; + final String title; + final String? subtitle; + final String? description; + final TaxFormStatus status; + final String? staffId; + final Map? formData; + final DateTime? createdAt; + final DateTime? updatedAt; + + const TaxForm({ + required this.id, + required this.type, + required this.title, + this.subtitle, + this.description, + this.status = TaxFormStatus.notStarted, + this.staffId, + this.formData, + this.createdAt, + this.updatedAt, + }); + + @override + List get props => [ + id, + type, + title, + subtitle, + description, + status, + staffId, + formData, + createdAt, + updatedAt, + ]; +} diff --git a/apps/mobile/packages/features/staff/profile_sections/compliance/tax_forms/lib/src/data/repositories/tax_forms_repository_impl.dart b/apps/mobile/packages/features/staff/profile_sections/compliance/tax_forms/lib/src/data/repositories/tax_forms_repository_impl.dart index 89eecc7a..5f8368a4 100644 --- a/apps/mobile/packages/features/staff/profile_sections/compliance/tax_forms/lib/src/data/repositories/tax_forms_repository_impl.dart +++ b/apps/mobile/packages/features/staff/profile_sections/compliance/tax_forms/lib/src/data/repositories/tax_forms_repository_impl.dart @@ -1,10 +1,10 @@ import 'dart:async'; -import 'package:firebase_auth/firebase_auth.dart' hide User; +import 'package:firebase_auth/firebase_auth.dart' as auth; import 'package:firebase_data_connect/firebase_data_connect.dart'; import 'package:krow_data_connect/krow_data_connect.dart' as dc; +import 'package:krow_domain/krow_domain.dart'; -import '../../domain/entities/tax_form_entity.dart'; import '../../domain/repositories/tax_forms_repository.dart'; class TaxFormsRepositoryImpl implements TaxFormsRepository { @@ -13,42 +13,34 @@ class TaxFormsRepositoryImpl implements TaxFormsRepository { required this.dataConnect, }); - final FirebaseAuth firebaseAuth; + final auth.FirebaseAuth firebaseAuth; final dc.ExampleConnector dataConnect; - String? _staffId; - - Future _getStaffId() async { - if (_staffId != null) return _staffId!; - - final user = firebaseAuth.currentUser; + /// Helper to get the logged-in staff ID. + String _getStaffId() { + final auth.User? user = firebaseAuth.currentUser; if (user == null) { - throw Exception('User not logged in'); + throw Exception('User not authenticated'); } - - final result = - await dataConnect.getStaffByUserId(userId: user.uid).execute(); - final staffs = result.data.staffs; - if (staffs.isEmpty) { - // This might happen if the user is logged in but staff profile isn't created yet or not synced. - // For now, fail hard. Code can be improved to handle this case properly. - throw Exception('No staff profile found for user'); + + final String? staffId = dc.StaffSessionStore.instance.session?.staff?.id; + if (staffId == null || staffId.isEmpty) { + throw Exception('Staff profile is missing or session not initialized.'); } - - _staffId = staffs.first.id; - return _staffId!; + return staffId; } @override - Future> getTaxForms() async { - final staffId = await _getStaffId(); - final result = + Future> getTaxForms() async { + final String staffId = _getStaffId(); + final QueryResult + result = await dataConnect.getTaxFormsBystaffId(staffId: staffId).execute(); - final forms = result.data.taxForms.map((e) => _mapToEntity(e)).toList(); + final List forms = result.data.taxForms.map((dc.GetTaxFormsBystaffIdTaxForms e) => _mapToEntity(e)).toList(); // Check if required forms exist, create if not. - final typesPresent = forms.map((f) => f.type).toSet(); + final Set typesPresent = forms.map((TaxForm f) => f.type).toSet(); bool createdNew = false; if (!typesPresent.contains(TaxFormType.i9)) { @@ -61,9 +53,10 @@ class TaxFormsRepositoryImpl implements TaxFormsRepository { } if (createdNew) { - final result2 = + final QueryResult + result2 = await dataConnect.getTaxFormsBystaffId(staffId: staffId).execute(); - return result2.data.taxForms.map((e) => _mapToEntity(e)).toList(); + return result2.data.taxForms.map((dc.GetTaxFormsBystaffIdTaxForms e) => _mapToEntity(e)).toList(); } return forms; @@ -87,7 +80,7 @@ class TaxFormsRepositoryImpl implements TaxFormsRepository { await dataConnect .createTaxForm( staffId: staffId, - formType: _mapTypeToGenerated(type), + formType: dc.TaxFormType.values.byName(TaxFormAdapter.typeToString(type)), title: title, ) .subtitle(subtitle) @@ -98,13 +91,14 @@ class TaxFormsRepositoryImpl implements TaxFormsRepository { @override Future submitForm(TaxFormType type, Map data) async { - final staffId = await _getStaffId(); - final result = + final String staffId = _getStaffId(); + final QueryResult + result = await dataConnect.getTaxFormsBystaffId(staffId: staffId).execute(); - final targetTypeString = _mapTypeToGenerated(type).name; + final String targetTypeString = TaxFormAdapter.typeToString(type); - final form = result.data.taxForms.firstWhere( - (e) => e.formType.stringValue == targetTypeString, + final dc.GetTaxFormsBystaffIdTaxForms form = result.data.taxForms.firstWhere( + (dc.GetTaxFormsBystaffIdTaxForms e) => e.formType.stringValue == targetTypeString, orElse: () => throw Exception('Form not found for submission'), ); @@ -120,13 +114,14 @@ class TaxFormsRepositoryImpl implements TaxFormsRepository { @override Future updateFormStatus(TaxFormType type, TaxFormStatus status) async { - final staffId = await _getStaffId(); - final result = + final String staffId = _getStaffId(); + final QueryResult + result = await dataConnect.getTaxFormsBystaffId(staffId: staffId).execute(); - final targetTypeString = _mapTypeToGenerated(type).name; + final String targetTypeString = TaxFormAdapter.typeToString(type); - final form = result.data.taxForms.firstWhere( - (e) => e.formType.stringValue == targetTypeString, + final dc.GetTaxFormsBystaffIdTaxForms form = result.data.taxForms.firstWhere( + (dc.GetTaxFormsBystaffIdTaxForms e) => e.formType.stringValue == targetTypeString, orElse: () => throw Exception('Form not found for update'), ); @@ -134,73 +129,22 @@ class TaxFormsRepositoryImpl implements TaxFormsRepository { .updateTaxForm( id: form.id, ) - .status(_mapStatusToGenerated(status)) + .status(dc.TaxFormStatus.values.byName(TaxFormAdapter.statusToString(status))) .execute(); } - TaxFormEntity _mapToEntity(dc.GetTaxFormsBystaffIdTaxForms form) { - return TaxFormEntity( - type: _mapTypeFromString(form.formType.stringValue), + TaxForm _mapToEntity(dc.GetTaxFormsBystaffIdTaxForms form) { + return TaxFormAdapter.fromPrimitives( + id: form.id, + type: form.formType.stringValue, title: form.title, - subtitle: form.subtitle ?? '', - description: form.description ?? '', - status: _mapStatusFromString(form.status.stringValue), - lastUpdated: form.updatedAt?.toDateTime(), + subtitle: form.subtitle, + description: form.description, + status: form.status.stringValue, + staffId: form.staffId, + formData: form.formData, // Adapter expects dynamic + updatedAt: form.updatedAt?.toDateTime(), ); } - - TaxFormType _mapTypeFromString(String value) { - switch (value) { - case 'I9': - return TaxFormType.i9; - case 'W4': - return TaxFormType.w4; - default: - // Handle unexpected types gracefully if needed, or throw. - // Assuming database integrity for now. - if (value == 'I-9') return TaxFormType.i9; // Fallback just in case - throw Exception('Unknown TaxFormType string: $value'); - } - } - - TaxFormStatus _mapStatusFromString(String value) { - switch (value) { - case 'NOT_STARTED': - return TaxFormStatus.notStarted; - case 'DRAFT': - return TaxFormStatus.inProgress; - case 'SUBMITTED': - return TaxFormStatus.submitted; - case 'APPROVED': - return TaxFormStatus.approved; - case 'REJECTED': - return TaxFormStatus.rejected; - default: - return TaxFormStatus.notStarted; // Default fallback - } - } - - dc.TaxFormType _mapTypeToGenerated(TaxFormType type) { - switch (type) { - case TaxFormType.i9: - return dc.TaxFormType.I9; - case TaxFormType.w4: - return dc.TaxFormType.W4; - } - } - - dc.TaxFormStatus _mapStatusToGenerated(TaxFormStatus status) { - switch (status) { - case TaxFormStatus.notStarted: - return dc.TaxFormStatus.NOT_STARTED; - case TaxFormStatus.inProgress: - return dc.TaxFormStatus.DRAFT; - case TaxFormStatus.submitted: - return dc.TaxFormStatus.SUBMITTED; - case TaxFormStatus.approved: - return dc.TaxFormStatus.APPROVED; - case TaxFormStatus.rejected: - return dc.TaxFormStatus.REJECTED; - } - } } + diff --git a/apps/mobile/packages/features/staff/profile_sections/compliance/tax_forms/lib/src/domain/repositories/tax_forms_repository.dart b/apps/mobile/packages/features/staff/profile_sections/compliance/tax_forms/lib/src/domain/repositories/tax_forms_repository.dart index 4ae464b2..de7095f5 100644 --- a/apps/mobile/packages/features/staff/profile_sections/compliance/tax_forms/lib/src/domain/repositories/tax_forms_repository.dart +++ b/apps/mobile/packages/features/staff/profile_sections/compliance/tax_forms/lib/src/domain/repositories/tax_forms_repository.dart @@ -1,7 +1,7 @@ -import '../entities/tax_form_entity.dart'; +import 'package:krow_domain/krow_domain.dart'; abstract class TaxFormsRepository { - Future> getTaxForms(); + Future> getTaxForms(); Future submitForm(TaxFormType type, Map data); Future updateFormStatus(TaxFormType type, TaxFormStatus status); } diff --git a/apps/mobile/packages/features/staff/profile_sections/compliance/tax_forms/lib/src/domain/usecases/get_tax_forms_usecase.dart b/apps/mobile/packages/features/staff/profile_sections/compliance/tax_forms/lib/src/domain/usecases/get_tax_forms_usecase.dart index f8f95b15..2e203594 100644 --- a/apps/mobile/packages/features/staff/profile_sections/compliance/tax_forms/lib/src/domain/usecases/get_tax_forms_usecase.dart +++ b/apps/mobile/packages/features/staff/profile_sections/compliance/tax_forms/lib/src/domain/usecases/get_tax_forms_usecase.dart @@ -1,4 +1,4 @@ -import '../entities/tax_form_entity.dart'; +import 'package:krow_domain/krow_domain.dart'; import '../repositories/tax_forms_repository.dart'; class GetTaxFormsUseCase { @@ -6,7 +6,7 @@ class GetTaxFormsUseCase { GetTaxFormsUseCase(this._repository); - Future> call() async { + Future> call() async { return _repository.getTaxForms(); } } diff --git a/apps/mobile/packages/features/staff/profile_sections/compliance/tax_forms/lib/src/domain/usecases/submit_tax_form_usecase.dart b/apps/mobile/packages/features/staff/profile_sections/compliance/tax_forms/lib/src/domain/usecases/submit_tax_form_usecase.dart index 431a99bc..c6a7f143 100644 --- a/apps/mobile/packages/features/staff/profile_sections/compliance/tax_forms/lib/src/domain/usecases/submit_tax_form_usecase.dart +++ b/apps/mobile/packages/features/staff/profile_sections/compliance/tax_forms/lib/src/domain/usecases/submit_tax_form_usecase.dart @@ -1,4 +1,4 @@ -import '../entities/tax_form_entity.dart'; +import 'package:krow_domain/krow_domain.dart'; import '../repositories/tax_forms_repository.dart'; class SubmitTaxFormUseCase { diff --git a/apps/mobile/packages/features/staff/profile_sections/compliance/tax_forms/lib/src/presentation/blocs/i9/form_i9_cubit.dart b/apps/mobile/packages/features/staff/profile_sections/compliance/tax_forms/lib/src/presentation/blocs/i9/form_i9_cubit.dart index ac6f132e..d4f1972a 100644 --- a/apps/mobile/packages/features/staff/profile_sections/compliance/tax_forms/lib/src/presentation/blocs/i9/form_i9_cubit.dart +++ b/apps/mobile/packages/features/staff/profile_sections/compliance/tax_forms/lib/src/presentation/blocs/i9/form_i9_cubit.dart @@ -1,5 +1,6 @@ import 'package:flutter_bloc/flutter_bloc.dart'; -import '../../../domain/entities/tax_form_entity.dart'; +import 'package:krow_domain/krow_domain.dart'; + import '../../../domain/usecases/submit_tax_form_usecase.dart'; import 'form_i9_state.dart'; diff --git a/apps/mobile/packages/features/staff/profile_sections/compliance/tax_forms/lib/src/presentation/blocs/tax_forms/tax_forms_cubit.dart b/apps/mobile/packages/features/staff/profile_sections/compliance/tax_forms/lib/src/presentation/blocs/tax_forms/tax_forms_cubit.dart index de06dd28..4af9a967 100644 --- a/apps/mobile/packages/features/staff/profile_sections/compliance/tax_forms/lib/src/presentation/blocs/tax_forms/tax_forms_cubit.dart +++ b/apps/mobile/packages/features/staff/profile_sections/compliance/tax_forms/lib/src/presentation/blocs/tax_forms/tax_forms_cubit.dart @@ -1,5 +1,5 @@ import 'package:flutter_bloc/flutter_bloc.dart'; -import '../../../domain/entities/tax_form_entity.dart'; +import 'package:krow_domain/krow_domain.dart'; import '../../../domain/usecases/get_tax_forms_usecase.dart'; import 'tax_forms_state.dart'; @@ -11,7 +11,7 @@ class TaxFormsCubit extends Cubit { Future loadTaxForms() async { emit(state.copyWith(status: TaxFormsStatus.loading)); try { - final List forms = await _getTaxFormsUseCase(); + final List forms = await _getTaxFormsUseCase(); emit(state.copyWith( status: TaxFormsStatus.success, forms: forms, diff --git a/apps/mobile/packages/features/staff/profile_sections/compliance/tax_forms/lib/src/presentation/blocs/tax_forms/tax_forms_state.dart b/apps/mobile/packages/features/staff/profile_sections/compliance/tax_forms/lib/src/presentation/blocs/tax_forms/tax_forms_state.dart index 8cef372e..a117fda3 100644 --- a/apps/mobile/packages/features/staff/profile_sections/compliance/tax_forms/lib/src/presentation/blocs/tax_forms/tax_forms_state.dart +++ b/apps/mobile/packages/features/staff/profile_sections/compliance/tax_forms/lib/src/presentation/blocs/tax_forms/tax_forms_state.dart @@ -1,22 +1,22 @@ import 'package:equatable/equatable.dart'; -import '../../../domain/entities/tax_form_entity.dart'; +import 'package:krow_domain/krow_domain.dart'; enum TaxFormsStatus { initial, loading, success, failure } class TaxFormsState extends Equatable { final TaxFormsStatus status; - final List forms; + final List forms; final String? errorMessage; const TaxFormsState({ this.status = TaxFormsStatus.initial, - this.forms = const [], + this.forms = const [], this.errorMessage, }); TaxFormsState copyWith({ TaxFormsStatus? status, - List? forms, + List? forms, String? errorMessage, }) { return TaxFormsState( diff --git a/apps/mobile/packages/features/staff/profile_sections/compliance/tax_forms/lib/src/presentation/blocs/w4/form_w4_cubit.dart b/apps/mobile/packages/features/staff/profile_sections/compliance/tax_forms/lib/src/presentation/blocs/w4/form_w4_cubit.dart index 47088736..536a51fd 100644 --- a/apps/mobile/packages/features/staff/profile_sections/compliance/tax_forms/lib/src/presentation/blocs/w4/form_w4_cubit.dart +++ b/apps/mobile/packages/features/staff/profile_sections/compliance/tax_forms/lib/src/presentation/blocs/w4/form_w4_cubit.dart @@ -1,5 +1,6 @@ import 'package:flutter_bloc/flutter_bloc.dart'; -import '../../../domain/entities/tax_form_entity.dart'; +import 'package:krow_domain/krow_domain.dart'; + import '../../../domain/usecases/submit_tax_form_usecase.dart'; import 'form_w4_state.dart'; diff --git a/apps/mobile/packages/features/staff/profile_sections/compliance/tax_forms/lib/src/presentation/pages/tax_forms_page.dart b/apps/mobile/packages/features/staff/profile_sections/compliance/tax_forms/lib/src/presentation/pages/tax_forms_page.dart index c4acec93..c0abdb8b 100644 --- a/apps/mobile/packages/features/staff/profile_sections/compliance/tax_forms/lib/src/presentation/pages/tax_forms_page.dart +++ b/apps/mobile/packages/features/staff/profile_sections/compliance/tax_forms/lib/src/presentation/pages/tax_forms_page.dart @@ -2,7 +2,7 @@ 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 '../../domain/entities/tax_form_entity.dart'; +import 'package:krow_domain/krow_domain.dart'; import '../blocs/tax_forms/tax_forms_cubit.dart'; import '../blocs/tax_forms/tax_forms_state.dart'; @@ -11,13 +11,7 @@ class TaxFormsPage extends StatelessWidget { @override Widget build(BuildContext context) { - final TaxFormsCubit cubit = Modular.get(); - - if (cubit.state.status == TaxFormsStatus.initial) { - cubit.loadTaxForms(); - } return Scaffold( - backgroundColor: UiColors.background, appBar: AppBar( backgroundColor: UiColors.primary, elevation: 0, @@ -27,9 +21,7 @@ class TaxFormsPage extends StatelessWidget { ), title: Text( 'Tax Documents', - style: UiTypography.headline3m.copyWith( - color: UiColors.bgPopup, - ), + style: UiTypography.headline3m.copyWith(color: UiColors.bgPopup), ), bottom: PreferredSize( preferredSize: const Size.fromHeight(24), @@ -54,40 +46,53 @@ class TaxFormsPage extends StatelessWidget { ), ), ), - body: BlocBuilder( - bloc: Modular.get(), - builder: (BuildContext context, TaxFormsState state) { - if (state.status == TaxFormsStatus.loading) { - return const Center(child: CircularProgressIndicator()); + body: BlocProvider( + create: (BuildContext context) { + final TaxFormsCubit cubit = Modular.get(); + if (cubit.state.status == TaxFormsStatus.initial) { + cubit.loadTaxForms(); } - - if (state.status == TaxFormsStatus.failure) { - return Center(child: Text(state.errorMessage ?? 'Error loading forms')); - } - - return SingleChildScrollView( - padding: const EdgeInsets.symmetric( - horizontal: UiConstants.space5, - vertical: UiConstants.space6, - ), - child: Column( - children: [ - _buildProgressOverview(state.forms), - const SizedBox(height: UiConstants.space6), - ...state.forms.map(_buildFormCard), - const SizedBox(height: UiConstants.space6), - _buildInfoCard(), - ], - ), - ); + return cubit; }, + child: BlocBuilder( + builder: (BuildContext context, TaxFormsState state) { + if (state.status == TaxFormsStatus.loading) { + return const Center(child: CircularProgressIndicator()); + } + + if (state.status == TaxFormsStatus.failure) { + return Center( + child: Text(state.errorMessage ?? 'Error loading forms'), + ); + } + + return SingleChildScrollView( + padding: const EdgeInsets.symmetric( + horizontal: UiConstants.space5, + vertical: UiConstants.space6, + ), + child: Column( + spacing: UiConstants.space6, + children: [ + _buildProgressOverview(state.forms), + ...state.forms.map(_buildFormCard), + _buildInfoCard(), + ], + ), + ); + }, + ), ), ); } - Widget _buildProgressOverview(List forms) { + Widget _buildProgressOverview(List forms) { final int completedCount = forms - .where((TaxFormEntity f) => f.status == TaxFormStatus.submitted || f.status == TaxFormStatus.approved) + .where( + (TaxForm f) => + f.status == TaxFormStatus.submitted || + f.status == TaxFormStatus.approved, + ) .length; final int totalCount = forms.length; final double progress = totalCount > 0 ? completedCount / totalCount : 0.0; @@ -112,8 +117,9 @@ class TaxFormsPage extends StatelessWidget { ), Text( '$completedCount/$totalCount', - style: - UiTypography.body2m.copyWith(color: UiColors.textSecondary), + style: UiTypography.body2m.copyWith( + color: UiColors.textSecondary, + ), ), ], ), @@ -124,9 +130,7 @@ class TaxFormsPage extends StatelessWidget { value: progress, minHeight: 8, backgroundColor: UiColors.background, - valueColor: const AlwaysStoppedAnimation( - UiColors.primary, - ), + valueColor: const AlwaysStoppedAnimation(UiColors.primary), ), ), ], @@ -134,10 +138,10 @@ class TaxFormsPage extends StatelessWidget { ); } - Widget _buildFormCard(TaxFormEntity form) { + Widget _buildFormCard(TaxForm form) { // Helper to get icon based on type (could be in entity or a mapper) final String icon = form.type == TaxFormType.i9 ? '🛂' : '📋'; - + return GestureDetector( onTap: () { if (form.type == TaxFormType.i9) { @@ -164,9 +168,7 @@ class TaxFormsPage extends StatelessWidget { color: UiColors.primary.withOpacity(0.1), borderRadius: UiConstants.radiusLg, ), - child: Center( - child: Text(icon, style: UiTypography.headline1m), - ), + child: Center(child: Text(icon, style: UiTypography.headline1m)), ), const SizedBox(width: UiConstants.space4), Expanded( @@ -187,7 +189,7 @@ class TaxFormsPage extends StatelessWidget { ), const SizedBox(height: UiConstants.space1), Text( - form.subtitle, + form.subtitle ?? '', style: UiTypography.body2m.copyWith( fontWeight: FontWeight.w500, color: UiColors.textSecondary, @@ -195,7 +197,7 @@ class TaxFormsPage extends StatelessWidget { ), const SizedBox(height: UiConstants.space1), Text( - form.description, + form.description ?? '', style: UiTypography.body3r.copyWith( color: UiColors.textSecondary, ), @@ -231,7 +233,11 @@ class TaxFormsPage extends StatelessWidget { child: Row( mainAxisSize: MainAxisSize.min, children: [ - const Icon(UiIcons.success, size: 12, color: UiColors.textSuccess), + const Icon( + UiIcons.success, + size: 12, + color: UiColors.textSuccess, + ), const SizedBox(width: UiConstants.space1), Text( 'Completed', @@ -311,7 +317,9 @@ class TaxFormsPage extends StatelessWidget { const SizedBox(height: UiConstants.space1), Text( 'I-9 and W-4 forms are required by federal law to verify your employment eligibility and set up correct tax withholding.', - style: UiTypography.body3r.copyWith(color: UiColors.textSecondary), + style: UiTypography.body3r.copyWith( + color: UiColors.textSecondary, + ), ), ], ),