feat: introduce TaxForm entity and adapter, refactor tax forms repository and use cases for improved data handling
This commit is contained in:
@@ -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';
|
||||
|
||||
@@ -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<String, dynamic>.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';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<String, dynamic>? 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<Object?> get props => [
|
||||
id,
|
||||
type,
|
||||
title,
|
||||
subtitle,
|
||||
description,
|
||||
status,
|
||||
staffId,
|
||||
formData,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
];
|
||||
}
|
||||
@@ -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<String> _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<List<TaxFormEntity>> getTaxForms() async {
|
||||
final staffId = await _getStaffId();
|
||||
final result =
|
||||
Future<List<TaxForm>> getTaxForms() async {
|
||||
final String staffId = _getStaffId();
|
||||
final QueryResult<dc.GetTaxFormsBystaffIdData, dc.GetTaxFormsBystaffIdVariables>
|
||||
result =
|
||||
await dataConnect.getTaxFormsBystaffId(staffId: staffId).execute();
|
||||
|
||||
final forms = result.data.taxForms.map((e) => _mapToEntity(e)).toList();
|
||||
final List<TaxForm> 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<TaxFormType> 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<dc.GetTaxFormsBystaffIdData, dc.GetTaxFormsBystaffIdVariables>
|
||||
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<void> submitForm(TaxFormType type, Map<String, dynamic> data) async {
|
||||
final staffId = await _getStaffId();
|
||||
final result =
|
||||
final String staffId = _getStaffId();
|
||||
final QueryResult<dc.GetTaxFormsBystaffIdData, dc.GetTaxFormsBystaffIdVariables>
|
||||
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<void> updateFormStatus(TaxFormType type, TaxFormStatus status) async {
|
||||
final staffId = await _getStaffId();
|
||||
final result =
|
||||
final String staffId = _getStaffId();
|
||||
final QueryResult<dc.GetTaxFormsBystaffIdData, dc.GetTaxFormsBystaffIdVariables>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import '../entities/tax_form_entity.dart';
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
|
||||
abstract class TaxFormsRepository {
|
||||
Future<List<TaxFormEntity>> getTaxForms();
|
||||
Future<List<TaxForm>> getTaxForms();
|
||||
Future<void> submitForm(TaxFormType type, Map<String, dynamic> data);
|
||||
Future<void> updateFormStatus(TaxFormType type, TaxFormStatus status);
|
||||
}
|
||||
|
||||
@@ -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<List<TaxFormEntity>> call() async {
|
||||
Future<List<TaxForm>> call() async {
|
||||
return _repository.getTaxForms();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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<TaxFormsState> {
|
||||
Future<void> loadTaxForms() async {
|
||||
emit(state.copyWith(status: TaxFormsStatus.loading));
|
||||
try {
|
||||
final List<TaxFormEntity> forms = await _getTaxFormsUseCase();
|
||||
final List<TaxForm> forms = await _getTaxFormsUseCase();
|
||||
emit(state.copyWith(
|
||||
status: TaxFormsStatus.success,
|
||||
forms: forms,
|
||||
|
||||
@@ -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<TaxFormEntity> forms;
|
||||
final List<TaxForm> forms;
|
||||
final String? errorMessage;
|
||||
|
||||
const TaxFormsState({
|
||||
this.status = TaxFormsStatus.initial,
|
||||
this.forms = const <TaxFormEntity>[],
|
||||
this.forms = const <TaxForm>[],
|
||||
this.errorMessage,
|
||||
});
|
||||
|
||||
TaxFormsState copyWith({
|
||||
TaxFormsStatus? status,
|
||||
List<TaxFormEntity>? forms,
|
||||
List<TaxForm>? forms,
|
||||
String? errorMessage,
|
||||
}) {
|
||||
return TaxFormsState(
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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<TaxFormsCubit>();
|
||||
|
||||
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,15 +46,24 @@ class TaxFormsPage extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
body: BlocBuilder<TaxFormsCubit, TaxFormsState>(
|
||||
bloc: Modular.get<TaxFormsCubit>(),
|
||||
body: BlocProvider<TaxFormsCubit>(
|
||||
create: (BuildContext context) {
|
||||
final TaxFormsCubit cubit = Modular.get<TaxFormsCubit>();
|
||||
if (cubit.state.status == TaxFormsStatus.initial) {
|
||||
cubit.loadTaxForms();
|
||||
}
|
||||
return cubit;
|
||||
},
|
||||
child: BlocBuilder<TaxFormsCubit, TaxFormsState>(
|
||||
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 Center(
|
||||
child: Text(state.errorMessage ?? 'Error loading forms'),
|
||||
);
|
||||
}
|
||||
|
||||
return SingleChildScrollView(
|
||||
@@ -71,23 +72,27 @@ class TaxFormsPage extends StatelessWidget {
|
||||
vertical: UiConstants.space6,
|
||||
),
|
||||
child: Column(
|
||||
spacing: UiConstants.space6,
|
||||
children: <Widget>[
|
||||
_buildProgressOverview(state.forms),
|
||||
const SizedBox(height: UiConstants.space6),
|
||||
...state.forms.map(_buildFormCard),
|
||||
const SizedBox(height: UiConstants.space6),
|
||||
_buildInfoCard(),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildProgressOverview(List<TaxFormEntity> forms) {
|
||||
Widget _buildProgressOverview(List<TaxForm> 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<Color>(
|
||||
UiColors.primary,
|
||||
),
|
||||
valueColor: const AlwaysStoppedAnimation<Color>(UiColors.primary),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -134,7 +138,7 @@ 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 ? '🛂' : '📋';
|
||||
|
||||
@@ -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: <Widget>[
|
||||
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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user