feat: add staff tax forms module with I-9 and W-4 forms
- Created staff_tax_forms_module.dart to manage tax forms feature. - Implemented routes for TaxFormsPage, FormI9Page, and FormW4Page. - Added mock repository for tax forms. - Established necessary dependencies in pubspec.yaml. - Exported module in staff_tax_forms.dart.
This commit is contained in:
@@ -38,7 +38,7 @@ extension ProfileNavigator on IModularNavigator {
|
||||
|
||||
/// Navigates to the tax forms page.
|
||||
void pushTaxForms() {
|
||||
pushNamed('/tax-forms');
|
||||
pushNamed('../tax-forms/');
|
||||
}
|
||||
|
||||
/// Navigates to Krow University.
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import 'package:staff_tax_forms/src/domain/entities/tax_form_entity.dart';
|
||||
import 'package:staff_tax_forms/src/domain/repositories/tax_forms_repository.dart';
|
||||
|
||||
class TaxFormsRepositoryMock implements TaxFormsRepository {
|
||||
final List<TaxFormEntity> _forms = [
|
||||
const TaxFormEntity(
|
||||
type: TaxFormType.i9,
|
||||
title: 'Form I-9',
|
||||
subtitle: 'Employment Eligibility Verification',
|
||||
description: 'Required to verify your identity and work authorization',
|
||||
status: TaxFormStatus.submitted,
|
||||
),
|
||||
const TaxFormEntity(
|
||||
type: TaxFormType.w4,
|
||||
title: 'Form W-4',
|
||||
subtitle: 'Employee\'s Withholding Certificate',
|
||||
description: 'Set up your federal tax withholding',
|
||||
status: TaxFormStatus.notStarted,
|
||||
),
|
||||
];
|
||||
|
||||
@override
|
||||
Future<List<TaxFormEntity>> getTaxForms() async {
|
||||
await Future.delayed(const Duration(milliseconds: 800)); // Simulating network
|
||||
return _forms;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> submitForm(TaxFormType type, Map<String, dynamic> data) async {
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
final index = _forms.indexWhere((f) => f.type == type);
|
||||
if (index != -1) {
|
||||
_forms[index] = TaxFormEntity(
|
||||
type: _forms[index].type,
|
||||
title: _forms[index].title,
|
||||
subtitle: _forms[index].subtitle,
|
||||
description: _forms[index].description,
|
||||
status: TaxFormStatus.submitted,
|
||||
lastUpdated: DateTime.now(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updateFormStatus(TaxFormType type, TaxFormStatus status) async {
|
||||
final index = _forms.indexWhere((f) => f.type == type);
|
||||
if (index != -1) {
|
||||
_forms[index] = TaxFormEntity(
|
||||
type: _forms[index].type,
|
||||
title: _forms[index].title,
|
||||
subtitle: _forms[index].subtitle,
|
||||
description: _forms[index].description,
|
||||
status: status,
|
||||
lastUpdated: DateTime.now(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
enum TaxFormType { i9, w4 }
|
||||
|
||||
enum TaxFormStatus { notStarted, inProgress, submitted, approved, rejected }
|
||||
|
||||
class TaxFormEntity extends Equatable {
|
||||
final TaxFormType type;
|
||||
final String title;
|
||||
final String subtitle;
|
||||
final String description;
|
||||
final TaxFormStatus status;
|
||||
final DateTime? lastUpdated;
|
||||
|
||||
const TaxFormEntity({
|
||||
required this.type,
|
||||
required this.title,
|
||||
required this.subtitle,
|
||||
required this.description,
|
||||
this.status = TaxFormStatus.notStarted,
|
||||
this.lastUpdated,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [type, title, subtitle, description, status, lastUpdated];
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import '../entities/tax_form_entity.dart';
|
||||
|
||||
abstract class TaxFormsRepository {
|
||||
Future<List<TaxFormEntity>> getTaxForms();
|
||||
Future<void> submitForm(TaxFormType type, Map<String, dynamic> data);
|
||||
Future<void> updateFormStatus(TaxFormType type, TaxFormStatus status);
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../../../domain/entities/tax_form_entity.dart';
|
||||
import '../../../domain/repositories/tax_forms_repository.dart';
|
||||
import 'form_i9_state.dart';
|
||||
|
||||
class FormI9Cubit extends Cubit<FormI9State> {
|
||||
final TaxFormsRepository _repository;
|
||||
|
||||
FormI9Cubit(this._repository) : super(const FormI9State());
|
||||
|
||||
void nextStep(int totalSteps) {
|
||||
if (state.currentStep < totalSteps - 1) {
|
||||
emit(state.copyWith(currentStep: state.currentStep + 1));
|
||||
}
|
||||
}
|
||||
|
||||
void previousStep() {
|
||||
if (state.currentStep > 0) {
|
||||
emit(state.copyWith(currentStep: state.currentStep - 1));
|
||||
}
|
||||
}
|
||||
|
||||
void updateCitizenship({required bool isUsCitizen}) {
|
||||
emit(state.copyWith(
|
||||
isUsCitizen: isUsCitizen,
|
||||
isLawfulResident: false,
|
||||
));
|
||||
}
|
||||
|
||||
void updateLawfulResident({required bool isLawfulResident}) {
|
||||
emit(state.copyWith(
|
||||
isLawfulResident: isLawfulResident,
|
||||
isUsCitizen: false,
|
||||
));
|
||||
}
|
||||
|
||||
void updateNonCitizen() {
|
||||
emit(state.copyWith(
|
||||
isUsCitizen: false,
|
||||
isLawfulResident: false,
|
||||
));
|
||||
}
|
||||
|
||||
void ssnChanged(String value) {
|
||||
emit(state.copyWith(ssn: value));
|
||||
}
|
||||
|
||||
void alienNumberChanged(String value) {
|
||||
emit(state.copyWith(alienNumber: value));
|
||||
}
|
||||
|
||||
Future<void> submit() async {
|
||||
emit(state.copyWith(status: FormI9Status.submitting));
|
||||
try {
|
||||
await _repository.submitForm(
|
||||
TaxFormType.i9,
|
||||
{
|
||||
'isUsCitizen': state.isUsCitizen,
|
||||
'isLawfulResident': state.isLawfulResident,
|
||||
'ssn': state.ssn,
|
||||
'alienNumber': state.alienNumber,
|
||||
},
|
||||
);
|
||||
emit(state.copyWith(status: FormI9Status.success));
|
||||
} catch (e) {
|
||||
emit(state.copyWith(
|
||||
status: FormI9Status.failure,
|
||||
errorMessage: e.toString(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
enum FormI9Status { initial, submitting, success, failure }
|
||||
|
||||
class FormI9State extends Equatable {
|
||||
final int currentStep;
|
||||
final bool isUsCitizen;
|
||||
final bool isLawfulResident;
|
||||
final String ssn;
|
||||
final String alienNumber;
|
||||
final FormI9Status status;
|
||||
final String? errorMessage;
|
||||
|
||||
const FormI9State({
|
||||
this.currentStep = 0,
|
||||
this.isUsCitizen = false,
|
||||
this.isLawfulResident = false,
|
||||
this.ssn = '',
|
||||
this.alienNumber = '',
|
||||
this.status = FormI9Status.initial,
|
||||
this.errorMessage,
|
||||
});
|
||||
|
||||
FormI9State copyWith({
|
||||
int? currentStep,
|
||||
bool? isUsCitizen,
|
||||
bool? isLawfulResident,
|
||||
String? ssn,
|
||||
String? alienNumber,
|
||||
FormI9Status? status,
|
||||
String? errorMessage,
|
||||
}) {
|
||||
return FormI9State(
|
||||
currentStep: currentStep ?? this.currentStep,
|
||||
isUsCitizen: isUsCitizen ?? this.isUsCitizen,
|
||||
isLawfulResident: isLawfulResident ?? this.isLawfulResident,
|
||||
ssn: ssn ?? this.ssn,
|
||||
alienNumber: alienNumber ?? this.alienNumber,
|
||||
status: status ?? this.status,
|
||||
errorMessage: errorMessage ?? this.errorMessage,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
currentStep,
|
||||
isUsCitizen,
|
||||
isLawfulResident,
|
||||
ssn,
|
||||
alienNumber,
|
||||
status,
|
||||
errorMessage,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../../../domain/entities/tax_form_entity.dart';
|
||||
import '../../../domain/repositories/tax_forms_repository.dart';
|
||||
import 'tax_forms_state.dart';
|
||||
|
||||
class TaxFormsCubit extends Cubit<TaxFormsState> {
|
||||
final TaxFormsRepository _repository;
|
||||
|
||||
TaxFormsCubit(this._repository) : super(const TaxFormsState());
|
||||
|
||||
Future<void> loadTaxForms() async {
|
||||
emit(state.copyWith(status: TaxFormsStatus.loading));
|
||||
try {
|
||||
final List<TaxFormEntity> forms = await _repository.getTaxForms();
|
||||
emit(state.copyWith(
|
||||
status: TaxFormsStatus.success,
|
||||
forms: forms,
|
||||
));
|
||||
} catch (e) {
|
||||
emit(state.copyWith(
|
||||
status: TaxFormsStatus.failure,
|
||||
errorMessage: e.toString(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import '../../../domain/entities/tax_form_entity.dart';
|
||||
|
||||
enum TaxFormsStatus { initial, loading, success, failure }
|
||||
|
||||
class TaxFormsState extends Equatable {
|
||||
final TaxFormsStatus status;
|
||||
final List<TaxFormEntity> forms;
|
||||
final String? errorMessage;
|
||||
|
||||
const TaxFormsState({
|
||||
this.status = TaxFormsStatus.initial,
|
||||
this.forms = const <TaxFormEntity>[],
|
||||
this.errorMessage,
|
||||
});
|
||||
|
||||
TaxFormsState copyWith({
|
||||
TaxFormsStatus? status,
|
||||
List<TaxFormEntity>? forms,
|
||||
String? errorMessage,
|
||||
}) {
|
||||
return TaxFormsState(
|
||||
status: status ?? this.status,
|
||||
forms: forms ?? this.forms,
|
||||
errorMessage: errorMessage ?? this.errorMessage,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[status, forms, errorMessage];
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../../../domain/entities/tax_form_entity.dart';
|
||||
import '../../../domain/repositories/tax_forms_repository.dart';
|
||||
import 'form_w4_state.dart';
|
||||
|
||||
class FormW4Cubit extends Cubit<FormW4State> {
|
||||
final TaxFormsRepository _repository;
|
||||
|
||||
FormW4Cubit(this._repository) : super(const FormW4State());
|
||||
|
||||
void nextStep(int totalSteps) {
|
||||
if (state.currentStep < totalSteps - 1) {
|
||||
emit(state.copyWith(currentStep: state.currentStep + 1));
|
||||
}
|
||||
}
|
||||
|
||||
void previousStep() {
|
||||
if (state.currentStep > 0) {
|
||||
emit(state.copyWith(currentStep: state.currentStep - 1));
|
||||
}
|
||||
}
|
||||
|
||||
void filingStatusChanged(String status) {
|
||||
emit(state.copyWith(filingStatus: status));
|
||||
}
|
||||
|
||||
void multipleJobsChanged(bool value) {
|
||||
emit(state.copyWith(multipleJobs: value));
|
||||
}
|
||||
|
||||
void dependentsAmountChanged(String value) {
|
||||
emit(state.copyWith(dependentsAmount: value));
|
||||
}
|
||||
|
||||
void otherIncomeChanged(String value) {
|
||||
emit(state.copyWith(otherIncome: value));
|
||||
}
|
||||
|
||||
void deductionsChanged(String value) {
|
||||
emit(state.copyWith(deductions: value));
|
||||
}
|
||||
|
||||
void extraWithholdingChanged(String value) {
|
||||
emit(state.copyWith(extraWithholding: value));
|
||||
}
|
||||
|
||||
Future<void> submit() async {
|
||||
emit(state.copyWith(status: FormW4Status.submitting));
|
||||
try {
|
||||
await _repository.submitForm(
|
||||
TaxFormType.w4,
|
||||
{
|
||||
'filingStatus': state.filingStatus,
|
||||
'multipleJobs': state.multipleJobs,
|
||||
'dependentsAmount': state.dependentsAmount,
|
||||
'otherIncome': state.otherIncome,
|
||||
'deductions': state.deductions,
|
||||
'extraWithholding': state.extraWithholding,
|
||||
},
|
||||
);
|
||||
emit(state.copyWith(status: FormW4Status.success));
|
||||
} catch (e) {
|
||||
emit(state.copyWith(
|
||||
status: FormW4Status.failure,
|
||||
errorMessage: e.toString(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
enum FormW4Status { initial, submitting, success, failure }
|
||||
|
||||
class FormW4State extends Equatable {
|
||||
final int currentStep;
|
||||
final String filingStatus;
|
||||
final bool multipleJobs;
|
||||
final String dependentsAmount;
|
||||
final String otherIncome;
|
||||
final String deductions;
|
||||
final String extraWithholding;
|
||||
final FormW4Status status;
|
||||
final String? errorMessage;
|
||||
|
||||
const FormW4State({
|
||||
this.currentStep = 0,
|
||||
this.filingStatus = 'Single',
|
||||
this.multipleJobs = false,
|
||||
this.dependentsAmount = '',
|
||||
this.otherIncome = '',
|
||||
this.deductions = '',
|
||||
this.extraWithholding = '',
|
||||
this.status = FormW4Status.initial,
|
||||
this.errorMessage,
|
||||
});
|
||||
|
||||
FormW4State copyWith({
|
||||
int? currentStep,
|
||||
String? filingStatus,
|
||||
bool? multipleJobs,
|
||||
String? dependentsAmount,
|
||||
String? otherIncome,
|
||||
String? deductions,
|
||||
String? extraWithholding,
|
||||
FormW4Status? status,
|
||||
String? errorMessage,
|
||||
}) {
|
||||
return FormW4State(
|
||||
currentStep: currentStep ?? this.currentStep,
|
||||
filingStatus: filingStatus ?? this.filingStatus,
|
||||
multipleJobs: multipleJobs ?? this.multipleJobs,
|
||||
dependentsAmount: dependentsAmount ?? this.dependentsAmount,
|
||||
otherIncome: otherIncome ?? this.otherIncome,
|
||||
deductions: deductions ?? this.deductions,
|
||||
extraWithholding: extraWithholding ?? this.extraWithholding,
|
||||
status: status ?? this.status,
|
||||
errorMessage: errorMessage ?? this.errorMessage,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
currentStep,
|
||||
filingStatus,
|
||||
multipleJobs,
|
||||
dependentsAmount,
|
||||
otherIncome,
|
||||
deductions,
|
||||
extraWithholding,
|
||||
status,
|
||||
errorMessage,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,853 @@
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_modular/flutter_modular.dart';
|
||||
|
||||
class FormI9Page extends StatefulWidget {
|
||||
const FormI9Page({super.key});
|
||||
|
||||
@override
|
||||
State<FormI9Page> createState() => _FormI9PageState();
|
||||
}
|
||||
|
||||
class _FormI9PageState extends State<FormI9Page> {
|
||||
int _currentStep = 0;
|
||||
bool _isSubmitting = false;
|
||||
bool _isSuccess = false;
|
||||
|
||||
final Map<String, dynamic> _formData = <String, dynamic>{
|
||||
'firstName': '',
|
||||
'lastName': '',
|
||||
'middleInitial': '',
|
||||
'otherLastNames': '',
|
||||
'address': '',
|
||||
'aptNumber': '',
|
||||
'city': '',
|
||||
'state': '',
|
||||
'zipCode': '',
|
||||
'dob': '',
|
||||
'ssn': '',
|
||||
'email': '',
|
||||
'phone': '',
|
||||
'citizenshipStatus': '',
|
||||
'uscisNumber': '',
|
||||
'admissionNumber': '',
|
||||
'passportNumber': '',
|
||||
'countryIssuance': '',
|
||||
};
|
||||
|
||||
String _signature = '';
|
||||
bool _preparerUsed = false;
|
||||
|
||||
final List<String> _usStates = <String>[
|
||||
'AL', 'AK', 'AZ', 'AR', 'CA', 'CO', 'CT', 'DE', 'FL', 'GA',
|
||||
'HI', 'ID', 'IL', 'IN', 'IA', 'KS', 'KY', 'LA', 'ME', 'MD',
|
||||
'MA', 'MI', 'MN', 'MS', 'MO', 'MT', 'NE', 'NV', 'NH', 'NJ',
|
||||
'NM', 'NY', 'NC', 'ND', 'OH', 'OK', 'OR', 'PA', 'RI', 'SC',
|
||||
'SD', 'TN', 'TX', 'UT', 'VT', 'VA', 'WA', 'WV', 'WI', 'WY'
|
||||
];
|
||||
|
||||
final List<Map<String, String>> _steps = <Map<String, String>>[
|
||||
<String, String>{'title': 'Personal Information', 'subtitle': 'Name and contact details'},
|
||||
<String, String>{'title': 'Address', 'subtitle': 'Your current address'},
|
||||
<String, String>{'title': 'Citizenship Status', 'subtitle': 'Work authorization verification'},
|
||||
<String, String>{'title': 'Review & Sign', 'subtitle': 'Confirm your information'},
|
||||
];
|
||||
|
||||
void _updateField(String key, dynamic value) {
|
||||
setState(() {
|
||||
_formData[key] = value;
|
||||
});
|
||||
}
|
||||
|
||||
bool _canProceed() {
|
||||
switch (_currentStep) {
|
||||
case 0:
|
||||
return (_formData['firstName'] as String).trim().isNotEmpty &&
|
||||
(_formData['lastName'] as String).trim().isNotEmpty &&
|
||||
(_formData['dob'] as String).isNotEmpty &&
|
||||
(_formData['ssn'] as String).replaceAll(RegExp(r'\D'), '').length >= 9;
|
||||
case 1:
|
||||
return (_formData['address'] as String).trim().isNotEmpty &&
|
||||
(_formData['city'] as String).trim().isNotEmpty &&
|
||||
(_formData['state'] as String).isNotEmpty &&
|
||||
(_formData['zipCode'] as String).isNotEmpty;
|
||||
case 2:
|
||||
return (_formData['citizenshipStatus'] as String).isNotEmpty;
|
||||
case 3:
|
||||
return _signature.trim().isNotEmpty;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void _handleNext() {
|
||||
if (_currentStep < _steps.length - 1) {
|
||||
setState(() => _currentStep++);
|
||||
} else {
|
||||
_submitForm();
|
||||
}
|
||||
}
|
||||
|
||||
void _handleBack() {
|
||||
if (_currentStep > 0) {
|
||||
setState(() => _currentStep--);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _submitForm() async {
|
||||
setState(() => _isSubmitting = true);
|
||||
// Mock API call
|
||||
await Future<void>.delayed(const Duration(seconds: 2));
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isSubmitting = false;
|
||||
_isSuccess = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (_isSuccess) return _buildSuccessView();
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: UiColors.background,
|
||||
body: Column(
|
||||
children: <Widget>[
|
||||
_buildHeader(),
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 24),
|
||||
child: _buildCurrentStep(),
|
||||
),
|
||||
),
|
||||
_buildFooter(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSuccessView() {
|
||||
return Scaffold(
|
||||
backgroundColor: UiColors.background,
|
||||
body: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(24.0),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(32),
|
||||
decoration: BoxDecoration(
|
||||
color: UiColors.bgPopup,
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
border: Border.all(color: UiColors.border),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
width: 64,
|
||||
height: 64,
|
||||
decoration: const BoxDecoration(
|
||||
color: Color(0xFFDCFCE7),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(
|
||||
UiIcons.success,
|
||||
color: Color(0xFF16A34A),
|
||||
size: 32,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Form I-9 Submitted!',
|
||||
style: UiTypography.headline4m.copyWith(
|
||||
color: UiColors.textPrimary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'Your employment eligibility verification has been submitted.',
|
||||
textAlign: TextAlign.center,
|
||||
style: UiTypography.body2r.copyWith(color: UiColors.textSecondary),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton(
|
||||
onPressed: () => Modular.to.pop(),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: UiColors.primary,
|
||||
foregroundColor: UiColors.bgPopup,
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
elevation: 0,
|
||||
),
|
||||
child: const Text('Back to Documents'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHeader() {
|
||||
return Container(
|
||||
color: UiColors.primary,
|
||||
padding: const EdgeInsets.only(top: 60, bottom: 24, left: 20, right: 20),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Row(
|
||||
children: <Widget>[
|
||||
GestureDetector(
|
||||
onTap: () => Modular.to.pop(),
|
||||
child: const Icon(
|
||||
UiIcons.arrowLeft,
|
||||
color: UiColors.bgPopup,
|
||||
size: 24,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
'Form I-9',
|
||||
style: UiTypography.headline4m.copyWith(
|
||||
color: UiColors.bgPopup,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'Employment Eligibility Verification',
|
||||
style: UiTypography.body3r.copyWith(color: UiColors.bgPopup.withOpacity(0.7)),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Row(
|
||||
children: _steps.asMap().entries.map((MapEntry<int, Map<String, String>> entry) {
|
||||
final int idx = entry.key;
|
||||
final bool isLast = idx == _steps.length - 1;
|
||||
return Expanded(
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: 4,
|
||||
decoration: BoxDecoration(
|
||||
color: idx <= _currentStep
|
||||
? UiColors.bgPopup
|
||||
: UiColors.bgPopup.withOpacity(0.3),
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (!isLast) const SizedBox(width: 4),
|
||||
],
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
'Step ${_currentStep + 1} of ${_steps.length}',
|
||||
style: UiTypography.body3r.copyWith(color: UiColors.bgPopup.withOpacity(0.7)),
|
||||
),
|
||||
Expanded(
|
||||
child: Text(
|
||||
_steps[_currentStep]['title']!,
|
||||
textAlign: TextAlign.end,
|
||||
style: UiTypography.body3m.copyWith(
|
||||
color: UiColors.bgPopup,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCurrentStep() {
|
||||
switch (_currentStep) {
|
||||
case 0:
|
||||
return _buildStep1();
|
||||
case 1:
|
||||
return _buildStep2();
|
||||
case 2:
|
||||
return _buildStep3();
|
||||
case 3:
|
||||
return _buildStep4();
|
||||
default:
|
||||
return Container();
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildTextField(
|
||||
String label,
|
||||
String key, {
|
||||
TextInputType? keyboardType,
|
||||
String? placeholder,
|
||||
Function(String)? onChanged,
|
||||
}) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
label,
|
||||
style: UiTypography.body3m.copyWith(
|
||||
color: UiColors.textSecondary,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
TextField(
|
||||
controller: TextEditingController(text: _formData[key].toString())
|
||||
..selection = TextSelection.fromPosition(
|
||||
TextPosition(offset: (_formData[key].toString()).length),
|
||||
),
|
||||
onChanged: onChanged ?? (String val) => _updateField(key, val),
|
||||
keyboardType: keyboardType,
|
||||
style: UiTypography.body2r.copyWith(color: UiColors.textPrimary),
|
||||
decoration: InputDecoration(
|
||||
hintText: placeholder,
|
||||
hintStyle: TextStyle(color: Colors.grey[400]),
|
||||
filled: true,
|
||||
fillColor: UiColors.bgPopup,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 16,
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: const BorderSide(color: UiColors.border),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: const BorderSide(color: UiColors.border),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: const BorderSide(color: UiColors.primary),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStep1() {
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: _buildTextField(
|
||||
'First Name *',
|
||||
'firstName',
|
||||
placeholder: 'John',
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: _buildTextField(
|
||||
'Last Name *',
|
||||
'lastName',
|
||||
placeholder: 'Smith',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: _buildTextField(
|
||||
'Middle Initial',
|
||||
'middleInitial',
|
||||
placeholder: 'A',
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: _buildTextField(
|
||||
'Other Last Names',
|
||||
'otherLastNames',
|
||||
placeholder: 'Maiden name (if any)',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_buildTextField(
|
||||
'Date of Birth *',
|
||||
'dob',
|
||||
placeholder: 'MM/DD/YYYY',
|
||||
keyboardType: TextInputType.datetime,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_buildTextField(
|
||||
'Social Security Number *',
|
||||
'ssn',
|
||||
placeholder: 'XXX-XX-XXXX',
|
||||
keyboardType: TextInputType.number,
|
||||
onChanged: (String val) {
|
||||
String text = val.replaceAll(RegExp(r'\D'), '');
|
||||
if (text.length > 9) text = text.substring(0, 9);
|
||||
_updateField('ssn', text);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_buildTextField(
|
||||
'Email Address',
|
||||
'email',
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
placeholder: 'john.smith@example.com',
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_buildTextField(
|
||||
'Phone Number',
|
||||
'phone',
|
||||
keyboardType: TextInputType.phone,
|
||||
placeholder: '(555) 555-5555',
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStep2() {
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
_buildTextField(
|
||||
'Address (Street Number and Name) *',
|
||||
'address',
|
||||
placeholder: '123 Main Street',
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_buildTextField(
|
||||
'Apt. Number',
|
||||
'aptNumber',
|
||||
placeholder: '4B',
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: _buildTextField(
|
||||
'City or Town *',
|
||||
'city',
|
||||
placeholder: 'San Francisco',
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
'State *',
|
||||
style: UiTypography.body3m.copyWith(
|
||||
color: UiColors.textSecondary,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
DropdownButtonFormField<String>(
|
||||
value: _formData['state'].toString().isEmpty ? null : _formData['state'].toString(),
|
||||
onChanged: (String? val) => _updateField('state', val),
|
||||
items: _usStates.map((String state) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: state,
|
||||
child: Text(state),
|
||||
);
|
||||
}).toList(),
|
||||
decoration: InputDecoration(
|
||||
filled: true,
|
||||
fillColor: UiColors.bgPopup,
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: const BorderSide(color: UiColors.border),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: const BorderSide(color: UiColors.border),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_buildTextField(
|
||||
'ZIP Code *',
|
||||
'zipCode',
|
||||
placeholder: '94103',
|
||||
keyboardType: TextInputType.number,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStep3() {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
'I attest, under penalty of perjury, that I am (check one of the following boxes):',
|
||||
style: UiTypography.body2m.copyWith(color: UiColors.textPrimary),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
_buildRadioOption(
|
||||
'citizen',
|
||||
'1. A citizen of the United States',
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_buildRadioOption(
|
||||
'noncitizen_national',
|
||||
'2. A noncitizen national of the United States',
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_buildRadioOption(
|
||||
'permanent_resident',
|
||||
'3. A lawful permanent resident',
|
||||
child: _formData['citizenshipStatus'] == 'permanent_resident'
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(top: 12),
|
||||
child: _buildTextField(
|
||||
'USCIS Number',
|
||||
'uscisNumber',
|
||||
placeholder: 'A-123456789',
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_buildRadioOption(
|
||||
'alien_authorized',
|
||||
'4. An alien authorized to work',
|
||||
child: _formData['citizenshipStatus'] == 'alien_authorized'
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(top: 12),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
_buildTextField(
|
||||
'USCIS/Admission Number',
|
||||
'admissionNumber',
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_buildTextField(
|
||||
'Foreign Passport Number',
|
||||
'passportNumber',
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_buildTextField(
|
||||
'Country of Issuance',
|
||||
'countryIssuance',
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildRadioOption(String value, String label, {Widget? child}) {
|
||||
final bool isSelected = _formData['citizenshipStatus'] == value;
|
||||
return GestureDetector(
|
||||
onTap: () => _updateField('citizenshipStatus', value),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: UiColors.bgPopup,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(
|
||||
color: isSelected ? UiColors.primary : UiColors.border,
|
||||
width: isSelected ? 2 : 1,
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Container(
|
||||
width: 20,
|
||||
height: 20,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: isSelected ? UiColors.primary : Colors.grey,
|
||||
width: isSelected ? 6 : 2,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Text(
|
||||
label,
|
||||
style: UiTypography.body2m.copyWith(
|
||||
color: UiColors.textPrimary,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (child != null) child,
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStep4() {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: UiColors.bgPopup,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: UiColors.border),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
'Summary',
|
||||
style: UiTypography.headline4m.copyWith(fontSize: 14),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_buildSummaryRow('Name', '${_formData['firstName']} ${_formData['lastName']}'),
|
||||
_buildSummaryRow('Address', '${_formData['address']}, ${_formData['city']}'),
|
||||
_buildSummaryRow('SSN', '***-**-${(_formData['ssn'] as String).length >= 4 ? (_formData['ssn'] as String).substring((_formData['ssn'] as String).length - 4) : '****'}'),
|
||||
_buildSummaryRow('Citizenship', _getReadableCitizenship(_formData['citizenshipStatus'] as String)),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
CheckboxListTile(
|
||||
value: _preparerUsed,
|
||||
onChanged: (bool? val) {
|
||||
setState(() {
|
||||
_preparerUsed = val ?? false;
|
||||
});
|
||||
},
|
||||
contentPadding: EdgeInsets.zero,
|
||||
title: Text(
|
||||
'I used a preparer or translator',
|
||||
style: UiTypography.body2r.copyWith(color: UiColors.textPrimary),
|
||||
),
|
||||
controlAffinity: ListTileControlAffinity.leading,
|
||||
activeColor: UiColors.primary,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.amber[50],
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: const Text(
|
||||
'I am aware that federal law provides for imprisonment and/or fines for false statements or use of false documents in connection with the completion of this form.',
|
||||
style: TextStyle(fontSize: 12, color: Color(0xFFB45309)),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Text(
|
||||
'Signature (type your full name) *',
|
||||
style: UiTypography.body3m.copyWith(
|
||||
color: UiColors.textSecondary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
TextField(
|
||||
onChanged: (String val) => setState(() => _signature = val),
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Type your full name',
|
||||
filled: true,
|
||||
fillColor: UiColors.bgPopup,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 16,
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: const BorderSide(color: UiColors.border),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: const BorderSide(color: UiColors.border),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: const BorderSide(color: UiColors.primary),
|
||||
),
|
||||
),
|
||||
style: const TextStyle(fontFamily: 'Cursive', fontSize: 18),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Date',
|
||||
style: UiTypography.body3m.copyWith(
|
||||
color: UiColors.textSecondary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF3F4F6),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: UiColors.border),
|
||||
),
|
||||
child: Text(
|
||||
DateTime.now().toString().split(' ')[0],
|
||||
style: const TextStyle(color: UiColors.textPrimary),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSummaryRow(String label, String value) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
label,
|
||||
style: UiTypography.body2r.copyWith(color: UiColors.textSecondary),
|
||||
),
|
||||
Expanded(
|
||||
child: Text(
|
||||
value,
|
||||
textAlign: TextAlign.end,
|
||||
style: UiTypography.body2m.copyWith(
|
||||
color: UiColors.textPrimary,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _getReadableCitizenship(String status) {
|
||||
switch (status) {
|
||||
case 'citizen':
|
||||
return 'US Citizen';
|
||||
case 'noncitizen_national':
|
||||
return 'Noncitizen National';
|
||||
case 'permanent_resident':
|
||||
return 'Permanent Resident';
|
||||
case 'alien_authorized':
|
||||
return 'Alien Authorized to Work';
|
||||
default:
|
||||
return 'Unknown';
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildFooter() {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: const BoxDecoration(
|
||||
color: UiColors.bgPopup,
|
||||
border: Border(top: BorderSide(color: UiColors.border)),
|
||||
),
|
||||
child: SafeArea(
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
if (_currentStep > 0)
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(right: 12),
|
||||
child: OutlinedButton(
|
||||
onPressed: _handleBack,
|
||||
style: OutlinedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
side: const BorderSide(color: UiColors.border),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
const Icon(UiIcons.arrowLeft, size: 16, color: UiColors.textPrimary),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'Back',
|
||||
style: UiTypography.body2r.copyWith(color: UiColors.textPrimary),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: ElevatedButton(
|
||||
onPressed: (_canProceed() && !_isSubmitting)
|
||||
? _handleNext
|
||||
: null,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: UiColors.primary,
|
||||
disabledBackgroundColor: Colors.grey[300],
|
||||
foregroundColor: UiColors.bgPopup,
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
elevation: 0,
|
||||
),
|
||||
child: _isSubmitting
|
||||
? const SizedBox(
|
||||
width: 20,
|
||||
height: 20,
|
||||
child: CircularProgressIndicator(
|
||||
color: UiColors.bgPopup,
|
||||
strokeWidth: 2,
|
||||
),
|
||||
)
|
||||
: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
_currentStep == _steps.length - 1
|
||||
? 'Sign & Submit'
|
||||
: 'Continue',
|
||||
),
|
||||
if (_currentStep < _steps.length - 1) ...<Widget>[
|
||||
const SizedBox(width: 8),
|
||||
const Icon(UiIcons.arrowRight, size: 16, color: UiColors.bgPopup),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,323 @@
|
||||
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 '../blocs/tax_forms/tax_forms_cubit.dart';
|
||||
import '../blocs/tax_forms/tax_forms_state.dart';
|
||||
|
||||
class TaxFormsPage extends StatelessWidget {
|
||||
const TaxFormsPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final cubit = Modular.get<TaxFormsCubit>();
|
||||
|
||||
if (cubit.state.status == TaxFormsStatus.initial) {
|
||||
cubit.loadTaxForms();
|
||||
}
|
||||
return Scaffold(
|
||||
backgroundColor: UiColors.background,
|
||||
appBar: AppBar(
|
||||
backgroundColor: UiColors.primary,
|
||||
elevation: 0,
|
||||
leading: IconButton(
|
||||
icon: const Icon(UiIcons.arrowLeft, color: UiColors.bgPopup),
|
||||
onPressed: () => Modular.to.pop(),
|
||||
),
|
||||
title: Text(
|
||||
'Tax Documents',
|
||||
style: UiTypography.headline3m.copyWith(
|
||||
color: UiColors.bgPopup,
|
||||
),
|
||||
),
|
||||
bottom: PreferredSize(
|
||||
preferredSize: const Size.fromHeight(24),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: UiConstants.space5,
|
||||
right: UiConstants.space5,
|
||||
bottom: UiConstants.space5,
|
||||
),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Complete required forms to start working',
|
||||
style: UiTypography.body3r.copyWith(
|
||||
color: UiColors.bgPopup.withOpacity(0.8),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
body: BlocBuilder<TaxFormsCubit, TaxFormsState>(
|
||||
bloc: Modular.get<TaxFormsCubit>(),
|
||||
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(
|
||||
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) {
|
||||
final int completedCount = forms
|
||||
.where((TaxFormEntity f) => f.status == TaxFormStatus.submitted || f.status == TaxFormStatus.approved)
|
||||
.length;
|
||||
final int totalCount = forms.length;
|
||||
final double progress = totalCount > 0 ? completedCount / totalCount : 0.0;
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(UiConstants.space4),
|
||||
decoration: BoxDecoration(
|
||||
color: UiColors.bgPopup,
|
||||
borderRadius: UiConstants.radiusLg,
|
||||
border: Border.all(color: UiColors.border),
|
||||
),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
'Document Progress',
|
||||
style: UiTypography.body2m.copyWith(
|
||||
color: UiColors.textPrimary,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'$completedCount/$totalCount',
|
||||
style:
|
||||
UiTypography.body2m.copyWith(color: UiColors.textSecondary),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: UiConstants.space3),
|
||||
ClipRRect(
|
||||
borderRadius: UiConstants.radiusSm,
|
||||
child: LinearProgressIndicator(
|
||||
value: progress,
|
||||
minHeight: 8,
|
||||
backgroundColor: UiColors.background,
|
||||
valueColor: const AlwaysStoppedAnimation<Color>(
|
||||
UiColors.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildFormCard(TaxFormEntity 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) {
|
||||
Modular.to.pushNamed('i9');
|
||||
} else if (form.type == TaxFormType.w4) {
|
||||
Modular.to.pushNamed('w4');
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(bottom: UiConstants.space4),
|
||||
padding: const EdgeInsets.all(UiConstants.space4),
|
||||
decoration: BoxDecoration(
|
||||
color: UiColors.bgPopup,
|
||||
borderRadius: UiConstants.radiusLg,
|
||||
border: Border.all(color: UiColors.border),
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
width: 48,
|
||||
height: 48,
|
||||
decoration: BoxDecoration(
|
||||
color: UiColors.primary.withOpacity(0.1),
|
||||
borderRadius: UiConstants.radiusLg,
|
||||
),
|
||||
child: Center(
|
||||
child: Text(icon, style: UiTypography.headline1m),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: UiConstants.space4),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
form.title,
|
||||
style: UiTypography.headline4m.copyWith(
|
||||
color: UiColors.textPrimary,
|
||||
),
|
||||
),
|
||||
_buildStatusBadge(form.status),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: UiConstants.space1),
|
||||
Text(
|
||||
form.subtitle,
|
||||
style: UiTypography.body2m.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
color: UiColors.textSecondary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: UiConstants.space1),
|
||||
Text(
|
||||
form.description,
|
||||
style: UiTypography.body3r.copyWith(
|
||||
color: UiColors.textSecondary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: UiConstants.space2),
|
||||
const Icon(
|
||||
UiIcons.chevronRight,
|
||||
color: UiColors.textSecondary,
|
||||
size: 20,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStatusBadge(TaxFormStatus status) {
|
||||
switch (status) {
|
||||
case TaxFormStatus.submitted:
|
||||
case TaxFormStatus.approved:
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: UiConstants.space2,
|
||||
vertical: UiConstants.space1,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: UiColors.tagSuccess,
|
||||
borderRadius: UiConstants.radiusLg,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
const Icon(UiIcons.success, size: 12, color: UiColors.textSuccess),
|
||||
const SizedBox(width: UiConstants.space1),
|
||||
Text(
|
||||
'Completed',
|
||||
style: UiTypography.footnote2b.copyWith(
|
||||
color: UiColors.textSuccess,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
case TaxFormStatus.inProgress:
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: UiConstants.space2,
|
||||
vertical: UiConstants.space1,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: UiColors.tagPending,
|
||||
borderRadius: UiConstants.radiusLg,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
const Icon(UiIcons.clock, size: 12, color: UiColors.textWarning),
|
||||
const SizedBox(width: UiConstants.space1),
|
||||
Text(
|
||||
'In Progress',
|
||||
style: UiTypography.footnote2b.copyWith(
|
||||
color: UiColors.textWarning,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
default:
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: UiConstants.space2,
|
||||
vertical: UiConstants.space1,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: UiColors.tagValue,
|
||||
borderRadius: UiConstants.radiusLg,
|
||||
),
|
||||
child: Text(
|
||||
'Not Started',
|
||||
style: UiTypography.footnote2b.copyWith(
|
||||
color: UiColors.textSecondary,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildInfoCard() {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(UiConstants.space4),
|
||||
decoration: BoxDecoration(
|
||||
color: UiColors.bgSecondary,
|
||||
borderRadius: UiConstants.radiusLg,
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
const Icon(UiIcons.file, color: UiColors.primary, size: 20),
|
||||
const SizedBox(width: UiConstants.space3),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
'Why are these needed?',
|
||||
style: UiTypography.headline4m.copyWith(
|
||||
color: UiColors.textPrimary,
|
||||
),
|
||||
),
|
||||
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),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import 'package:flutter_modular/flutter_modular.dart';
|
||||
import 'data/repositories/tax_forms_repository_mock.dart';
|
||||
import 'domain/repositories/tax_forms_repository.dart';
|
||||
import 'presentation/blocs/i9/form_i9_cubit.dart';
|
||||
import 'presentation/blocs/tax_forms/tax_forms_cubit.dart';
|
||||
import 'presentation/blocs/w4/form_w4_cubit.dart';
|
||||
import 'presentation/pages/form_i9_page.dart';
|
||||
import 'presentation/pages/form_w4_page.dart';
|
||||
import 'presentation/pages/tax_forms_page.dart';
|
||||
|
||||
class StaffTaxFormsModule extends Module {
|
||||
@override
|
||||
void binds(Injector i) {
|
||||
i.addLazySingleton<TaxFormsRepository>(TaxFormsRepositoryMock.new);
|
||||
i.addLazySingleton(TaxFormsCubit.new);
|
||||
i.addLazySingleton(FormI9Cubit.new);
|
||||
i.addLazySingleton(FormW4Cubit.new);
|
||||
}
|
||||
|
||||
@override
|
||||
void routes(RouteManager r) {
|
||||
r.child('/', child: (_) => const TaxFormsPage());
|
||||
r.child('/i9', child: (_) => const FormI9Page());
|
||||
r.child('/w4', child: (_) => const FormW4Page());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
library staff_tax_forms;
|
||||
|
||||
export 'src/staff_tax_forms_module.dart';
|
||||
@@ -0,0 +1,778 @@
|
||||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
_flutterfire_internals:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: _flutterfire_internals
|
||||
sha256: cd83f7d6bd4e4c0b0b4fef802e8796784032e1cc23d7b0e982cf5d05d9bbe182
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.66"
|
||||
archive:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: archive
|
||||
sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.6.1"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: args
|
||||
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.7.0"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.13.0"
|
||||
auto_injector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: auto_injector
|
||||
sha256: "1fc2624898e92485122eb2b1698dd42511d7ff6574f84a3a8606fc4549a1e8f8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
bloc:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: bloc
|
||||
sha256: "106842ad6569f0b60297619e9e0b1885c2fb9bf84812935490e6c5275777804e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.1.4"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: boolean_selector
|
||||
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: clock
|
||||
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
code_assets:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: code_assets
|
||||
sha256: "83ccdaa064c980b5596c35dd64a8d3ecc68620174ab9b90b6343b753aa721687"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.19.1"
|
||||
core_localization:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../../../../../core_localization"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.7"
|
||||
csv:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: csv
|
||||
sha256: c6aa2679b2a18cb57652920f674488d89712efaf4d3fdf2e537215b35fc19d6c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.0"
|
||||
design_system:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../../../../../design_system"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
equatable:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: equatable
|
||||
sha256: "3e0141505477fd8ad55d6eb4e7776d3fe8430be8e497ccb1521370c3f21a3e2b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.8"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fake_async
|
||||
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.3"
|
||||
ffi:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
sha256: d07d37192dbf97461359c1518788f203b0c9102cfd2c35a716b823741219542c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.5"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file
|
||||
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.1"
|
||||
firebase_app_check:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_app_check
|
||||
sha256: "45f0d279ea7ae4eac1867a4c85aa225761e3ac0ccf646386a860b2bc16581f76"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.4.1+4"
|
||||
firebase_app_check_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_app_check_platform_interface
|
||||
sha256: e32b4e6adeaac207a6f7afe0906d97c0811de42fb200d9b6317a09155de65e2b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.1+4"
|
||||
firebase_app_check_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_app_check_web
|
||||
sha256: "2cbc8a18a34813a7e31d7b30f989973087421cd5d0e397b4dd88a90289aa2bed"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.2+2"
|
||||
firebase_auth:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: firebase_auth
|
||||
sha256: b20d1540460814c5984474c1e9dd833bdbcff6ecd8d6ad86cc9da8cfd581c172
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.4"
|
||||
firebase_auth_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_auth_platform_interface
|
||||
sha256: fd0225320b6bbc92460c86352d16b60aea15f9ef88292774cca97b0522ea9f72
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.1.6"
|
||||
firebase_auth_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_auth_web
|
||||
sha256: be7dccb263b89fbda2a564de9d8193118196e8481ffb937222a025cdfdf82c40
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.2"
|
||||
firebase_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_core
|
||||
sha256: "923085c881663ef685269b013e241b428e1fb03cdd0ebde265d9b40ff18abf80"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.4.0"
|
||||
firebase_core_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_core_platform_interface
|
||||
sha256: cccb4f572325dc14904c02fcc7db6323ad62ba02536833dddb5c02cac7341c64
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.2"
|
||||
firebase_core_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_core_web
|
||||
sha256: "83e7356c704131ca4d8d8dd57e360d8acecbca38b1a3705c7ae46cc34c708084"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.4.0"
|
||||
firebase_data_connect:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: firebase_data_connect
|
||||
sha256: "01d0f8e33c520a6e6f59cf5ac6ff281d1927f7837f094fa8eb5fdb0b1b328ad8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.2+2"
|
||||
fixnum:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fixnum
|
||||
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_bloc:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_bloc
|
||||
sha256: b594505eac31a0518bdcb4b5b79573b8d9117b193cc80cc12e17d639b10aa27a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.1.6"
|
||||
flutter_localizations:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_modular:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_modular
|
||||
sha256: "33a63d9fe61429d12b3dfa04795ed890f17d179d3d38e988ba7969651fcd5586"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.4.1"
|
||||
flutter_test:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_web_plugins:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
font_awesome_flutter:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: font_awesome_flutter
|
||||
sha256: b9011df3a1fa02993630b8fb83526368cf2206a711259830325bab2f1d2a4eb0
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.12.0"
|
||||
glob:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: glob
|
||||
sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.3"
|
||||
google_fonts:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: google_fonts
|
||||
sha256: "6996212014b996eaa17074e02b1b925b212f5e053832d9048970dc27255a8fb3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.1.0"
|
||||
google_identity_services_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: google_identity_services_web
|
||||
sha256: "5d187c46dc59e02646e10fe82665fc3884a9b71bc1c90c2b8b749316d33ee454"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.3+1"
|
||||
googleapis_auth:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: googleapis_auth
|
||||
sha256: befd71383a955535060acde8792e7efc11d2fccd03dd1d3ec434e85b68775938
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.6.0"
|
||||
grpc:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: grpc
|
||||
sha256: e93ee3bce45c134bf44e9728119102358c7cd69de7832d9a874e2e74eb8cab40
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.4"
|
||||
hooks:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: hooks
|
||||
sha256: "5d309c86e7ce34cd8e37aa71cb30cb652d3829b900ab145e4d9da564b31d59f7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
http:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http
|
||||
sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.6.0"
|
||||
http2:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http2
|
||||
sha256: "382d3aefc5bd6dc68c6b892d7664f29b5beb3251611ae946a98d35158a82bbfa"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.1"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_parser
|
||||
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.2"
|
||||
intl:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: intl
|
||||
sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.20.2"
|
||||
krow_core:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../../../../../core"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
krow_data_connect:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../../../../../data_connect"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
krow_domain:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../../../../../domain"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "11.0.2"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.10"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_testing
|
||||
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: logging
|
||||
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
lucide_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: lucide_icons
|
||||
sha256: ad24d0fd65707e48add30bebada7d90bff2a1bba0a72d6e9b19d44246b0e83c4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.257.0"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.17"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.11.1"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.17.0"
|
||||
modular_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: modular_core
|
||||
sha256: "1db0420a0dfb8a2c6dca846e7cbaa4ffeb778e247916dbcb27fb25aa566e5436"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.4.1"
|
||||
native_toolchain_c:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: native_toolchain_c
|
||||
sha256: "89e83885ba09da5fdf2cdacc8002a712ca238c28b7f717910b34bcd27b0d03ac"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.17.4"
|
||||
nested:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: nested
|
||||
sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
objective_c:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: objective_c
|
||||
sha256: "7fd0c4d8ac8980011753b9bdaed2bf15111365924cdeeeaeb596214ea2b03537"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "9.2.4"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
path_provider:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider
|
||||
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.5"
|
||||
path_provider_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_android
|
||||
sha256: f2c65e21139ce2c3dad46922be8272bb5963516045659e71bb16e151c93b580e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.22"
|
||||
path_provider_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_foundation
|
||||
sha256: "2a376b7d6392d80cd3705782d2caa734ca4727776db0b6ec36ef3f1855197699"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.6.0"
|
||||
path_provider_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_linux
|
||||
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
path_provider_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_platform_interface
|
||||
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
path_provider_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_windows
|
||||
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: platform
|
||||
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.6"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: plugin_platform_interface
|
||||
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.8"
|
||||
protobuf:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: protobuf
|
||||
sha256: "68645b24e0716782e58948f8467fd42a880f255096a821f9e7d0ec625b00c84d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.0"
|
||||
provider:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: provider
|
||||
sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.5+1"
|
||||
pub_semver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pub_semver
|
||||
sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
result_dart:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: result_dart
|
||||
sha256: "0666b21fbdf697b3bdd9986348a380aa204b3ebe7c146d8e4cdaa7ce735e6054"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
shared_preferences:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences
|
||||
sha256: "2939ae520c9024cb197fc20dee269cd8cdbf564c8b5746374ec6cacdc5169e64"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.4"
|
||||
shared_preferences_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_android
|
||||
sha256: "83af5c682796c0f7719c2bbf74792d113e40ae97981b8f266fa84574573556bc"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.18"
|
||||
shared_preferences_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_foundation
|
||||
sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.6"
|
||||
shared_preferences_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_linux
|
||||
sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
shared_preferences_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_platform_interface
|
||||
sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
shared_preferences_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_web
|
||||
sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.3"
|
||||
shared_preferences_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_windows
|
||||
sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
slang:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: slang
|
||||
sha256: "13e3b6f07adc51ab751e7889647774d294cbce7a3382f81d9e5029acfe9c37b2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.12.0"
|
||||
slang_flutter:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: slang_flutter
|
||||
sha256: "0a4545cca5404d6b7487cf61cf1fe56c52daeb08de56a7574ee8381fbad035a0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.12.0"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.1"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.12.1"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.1"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.2"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.7"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
uuid:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: uuid
|
||||
sha256: a11b666489b1954e01d992f3d601b1804a33937b5a8fe677bd26b8a9f96f96e8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.5.2"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_math
|
||||
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "15.0.2"
|
||||
watcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: watcher
|
||||
sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xdg_directories
|
||||
sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: yaml
|
||||
sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.3"
|
||||
sdks:
|
||||
dart: ">=3.10.7 <4.0.0"
|
||||
flutter: ">=3.38.4"
|
||||
@@ -0,0 +1,31 @@
|
||||
name: staff_tax_forms
|
||||
description: Staff Tax Forms feature.
|
||||
version: 0.0.1
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
sdk: '>=3.10.0 <4.0.0'
|
||||
flutter: ">=3.0.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
flutter_bloc: ^8.1.0
|
||||
bloc: ^8.1.0
|
||||
flutter_modular: ^6.3.0
|
||||
equatable: ^2.0.5
|
||||
lucide_icons: ^0.257.0
|
||||
firebase_auth: ^6.1.4
|
||||
firebase_data_connect: ^0.2.2+2
|
||||
|
||||
# Architecture Packages
|
||||
design_system:
|
||||
path: ../../../../../design_system
|
||||
krow_core:
|
||||
path: ../../../../../core
|
||||
core_localization:
|
||||
path: ../../../../../core_localization
|
||||
krow_domain:
|
||||
path: ../../../../../domain
|
||||
krow_data_connect:
|
||||
path: ../../../../../data_connect
|
||||
@@ -6,6 +6,7 @@ import 'package:staff_profile_info/staff_profile_info.dart';
|
||||
import 'package:staff_emergency_contact/staff_emergency_contact.dart';
|
||||
import 'package:staff_profile_experience/staff_profile_experience.dart';
|
||||
import 'package:staff_bank_account/staff_bank_account.dart';
|
||||
import 'package:staff_tax_forms/staff_tax_forms.dart';
|
||||
|
||||
import 'package:staff_main/src/presentation/blocs/staff_main_cubit.dart';
|
||||
import 'package:staff_main/src/presentation/constants/staff_main_routes.dart';
|
||||
@@ -53,5 +54,6 @@ class StaffMainModule extends Module {
|
||||
r.module('/emergency-contact', module: StaffEmergencyContactModule());
|
||||
r.module('/experience', module: StaffProfileExperienceModule());
|
||||
r.module('/bank-account', module: StaffBankAccountModule());
|
||||
r.module('/tax-forms', module: StaffTaxFormsModule());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,8 @@ dependencies:
|
||||
path: ../profile_sections/onboarding/experience
|
||||
staff_bank_account:
|
||||
path: ../profile_sections/finances/staff_bank_account
|
||||
staff_tax_forms:
|
||||
path: ../profile_sections/compliance/tax_forms
|
||||
# staff_shifts:
|
||||
# path: ../shifts
|
||||
# staff_payments:
|
||||
|
||||
@@ -1071,6 +1071,13 @@ packages:
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
staff_tax_forms:
|
||||
dependency: transitive
|
||||
description:
|
||||
path: "packages/features/staff/profile_sections/compliance/tax_forms"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
Reference in New Issue
Block a user