Fix: Resolve critical linting issues and bugs (concurrency, syntax, dead code)

This commit is contained in:
2026-02-10 19:12:01 +05:30
parent 5e7bf0d5c0
commit 7570ffa3b9
46 changed files with 4057 additions and 1299 deletions

View File

@@ -1,4 +1,5 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:krow_core/core.dart';
import 'package:krow_domain/krow_domain.dart';
import '../../domain/arguments/save_attire_arguments.dart';
@@ -8,7 +9,8 @@ import '../../domain/usecases/save_attire_usecase.dart';
import '../../domain/usecases/upload_attire_photo_usecase.dart';
import 'attire_state.dart';
class AttireCubit extends Cubit<AttireState> {
class AttireCubit extends Cubit<AttireState>
with BlocErrorHandler<AttireState> {
final GetAttireOptionsUseCase _getAttireOptionsUseCase;
final SaveAttireUseCase _saveAttireUseCase;
final UploadAttirePhotoUseCase _uploadAttirePhotoUseCase;
@@ -23,30 +25,41 @@ class AttireCubit extends Cubit<AttireState> {
Future<void> loadOptions() async {
emit(state.copyWith(status: AttireStatus.loading));
try {
final List<AttireItem> options = await _getAttireOptionsUseCase();
// Auto-select mandatory items initially as per prototype
final List<String> mandatoryIds = options
.where((AttireItem e) => e.isMandatory)
.map((AttireItem e) => e.id)
.toList();
final List<String> initialSelection = List<String>.from(state.selectedIds);
for (final String id in mandatoryIds) {
if (!initialSelection.contains(id)) {
initialSelection.add(id);
}
}
await handleError(
emit: emit,
action: () async {
final List<AttireItem> options = await _getAttireOptionsUseCase();
emit(state.copyWith(
status: AttireStatus.success,
options: options,
selectedIds: initialSelection,
));
} catch (e) {
emit(state.copyWith(status: AttireStatus.failure, errorMessage: e.toString()));
}
// Auto-select mandatory items initially as per prototype
final List<String> mandatoryIds =
options
.where((AttireItem e) => e.isMandatory)
.map((AttireItem e) => e.id)
.toList();
final List<String> initialSelection = List<String>.from(
state.selectedIds,
);
for (final String id in mandatoryIds) {
if (!initialSelection.contains(id)) {
initialSelection.add(id);
}
}
emit(
state.copyWith(
status: AttireStatus.success,
options: options,
selectedIds: initialSelection,
),
);
},
onError:
(String errorKey) => state.copyWith(
status: AttireStatus.failure,
errorMessage: errorKey,
),
);
}
void toggleSelection(String id) {
@@ -67,51 +80,81 @@ class AttireCubit extends Cubit<AttireState> {
}
Future<void> uploadPhoto(String itemId) async {
final Map<String, bool> currentUploading = Map<String, bool>.from(state.uploadingStatus);
final Map<String, bool> currentUploading = Map<String, bool>.from(
state.uploadingStatus,
);
currentUploading[itemId] = true;
emit(state.copyWith(uploadingStatus: currentUploading));
try {
final String url = await _uploadAttirePhotoUseCase(
UploadAttirePhotoArguments(itemId: itemId),
);
final Map<String, String> currentPhotos = Map<String, String>.from(state.photoUrls);
currentPhotos[itemId] = url;
// Auto-select item on upload success if not selected
final List<String> currentSelection = List<String>.from(state.selectedIds);
if (!currentSelection.contains(itemId)) {
currentSelection.add(itemId);
}
await handleError(
emit: emit,
action: () async {
final String url = await _uploadAttirePhotoUseCase(
UploadAttirePhotoArguments(itemId: itemId),
);
currentUploading[itemId] = false;
emit(state.copyWith(
uploadingStatus: currentUploading,
photoUrls: currentPhotos,
selectedIds: currentSelection,
));
} catch (e) {
currentUploading[itemId] = false;
emit(state.copyWith(
uploadingStatus: currentUploading,
final Map<String, String> currentPhotos = Map<String, String>.from(
state.photoUrls,
);
currentPhotos[itemId] = url;
// Auto-select item on upload success if not selected
final List<String> currentSelection = List<String>.from(
state.selectedIds,
);
if (!currentSelection.contains(itemId)) {
currentSelection.add(itemId);
}
final Map<String, bool> updatedUploading = Map<String, bool>.from(
state.uploadingStatus,
);
updatedUploading[itemId] = false;
emit(
state.copyWith(
uploadingStatus: updatedUploading,
photoUrls: currentPhotos,
selectedIds: currentSelection,
),
);
},
onError: (String errorKey) {
final Map<String, bool> updatedUploading = Map<String, bool>.from(
state.uploadingStatus,
);
updatedUploading[itemId] = false;
// Could handle error specifically via snackbar event
));
}
// For now, attaching the error message but keeping state generally usable
return state.copyWith(
uploadingStatus: updatedUploading,
errorMessage: errorKey,
);
},
);
}
Future<void> save() async {
if (!state.canSave) return;
emit(state.copyWith(status: AttireStatus.saving));
try {
await _saveAttireUseCase(SaveAttireArguments(
selectedItemIds: state.selectedIds,
photoUrls: state.photoUrls,
));
emit(state.copyWith(status: AttireStatus.saved));
} catch (e) {
emit(state.copyWith(status: AttireStatus.failure, errorMessage: e.toString()));
}
await handleError(
emit: emit,
action: () async {
await _saveAttireUseCase(
SaveAttireArguments(
selectedItemIds: state.selectedIds,
photoUrls: state.photoUrls,
),
);
emit(state.copyWith(status: AttireStatus.saved));
},
onError:
(String errorKey) => state.copyWith(
status: AttireStatus.failure,
errorMessage: errorKey,
),
);
}
}

View File

@@ -1,4 +1,5 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:krow_core/core.dart';
import 'package:krow_domain/krow_domain.dart';
import '../../domain/arguments/get_emergency_contacts_arguments.dart';
import '../../domain/arguments/save_emergency_contacts_arguments.dart';
@@ -12,7 +13,8 @@ export 'emergency_contact_state.dart';
// BLoC
class EmergencyContactBloc
extends Bloc<EmergencyContactEvent, EmergencyContactState> {
extends Bloc<EmergencyContactEvent, EmergencyContactState>
with BlocErrorHandler<EmergencyContactState> {
final GetEmergencyContactsUseCase getEmergencyContacts;
final SaveEmergencyContactsUseCase saveEmergencyContacts;
@@ -28,29 +30,30 @@ class EmergencyContactBloc
add(EmergencyContactsLoaded());
}
Future<void> _onLoaded(
EmergencyContactsLoaded event,
Emitter<EmergencyContactState> emit,
) async {
emit(state.copyWith(status: EmergencyContactStatus.loading));
try {
final contacts = await getEmergencyContacts(
const GetEmergencyContactsArguments(),
);
emit(state.copyWith(
status: EmergencyContactStatus.loaded,
contacts: contacts.isNotEmpty
? contacts
: [EmergencyContact.empty()],
));
} catch (e) {
emit(state.copyWith(
await handleError(
emit: emit,
action: () async {
final contacts = await getEmergencyContacts(
const GetEmergencyContactsArguments(),
);
emit(
state.copyWith(
status: EmergencyContactStatus.loaded,
contacts: contacts.isNotEmpty ? contacts : [EmergencyContact.empty()],
),
);
},
onError: (String errorKey) => state.copyWith(
status: EmergencyContactStatus.failure,
errorMessage: e.toString(),
));
}
errorMessage: errorKey,
),
);
}
void _onAdded(
@@ -85,18 +88,19 @@ class EmergencyContactBloc
Emitter<EmergencyContactState> emit,
) async {
emit(state.copyWith(status: EmergencyContactStatus.saving));
try {
await saveEmergencyContacts(
SaveEmergencyContactsArguments(
contacts: state.contacts,
),
);
emit(state.copyWith(status: EmergencyContactStatus.saved));
} catch (e) {
emit(state.copyWith(
await handleError(
emit: emit,
action: () async {
await saveEmergencyContacts(
SaveEmergencyContactsArguments(contacts: state.contacts),
);
emit(state.copyWith(status: EmergencyContactStatus.saved));
},
onError: (String errorKey) => state.copyWith(
status: EmergencyContactStatus.failure,
errorMessage: e.toString(),
));
}
errorMessage: errorKey,
),
);
}
}

View File

@@ -1,5 +1,6 @@
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:krow_core/core.dart';
import 'package:krow_domain/krow_domain.dart';
import '../../domain/arguments/save_experience_arguments.dart';
import '../../domain/usecases/get_staff_industries_usecase.dart';
@@ -92,8 +93,8 @@ class ExperienceState extends Equatable {
}
// BLoC
class ExperienceBloc extends Bloc<ExperienceEvent, ExperienceState> {
class ExperienceBloc extends Bloc<ExperienceEvent, ExperienceState>
with BlocErrorHandler<ExperienceState> {
final GetStaffIndustriesUseCase getIndustries;
final GetStaffSkillsUseCase getSkills;
final SaveExperienceUseCase saveExperience;
@@ -102,10 +103,12 @@ class ExperienceBloc extends Bloc<ExperienceEvent, ExperienceState> {
required this.getIndustries,
required this.getSkills,
required this.saveExperience,
}) : super(const ExperienceState(
availableIndustries: Industry.values,
availableSkills: ExperienceSkill.values,
)) {
}) : super(
const ExperienceState(
availableIndustries: Industry.values,
availableSkills: ExperienceSkill.values,
),
) {
on<ExperienceLoaded>(_onLoaded);
on<ExperienceIndustryToggled>(_onIndustryToggled);
on<ExperienceSkillToggled>(_onSkillToggled);
@@ -120,26 +123,28 @@ class ExperienceBloc extends Bloc<ExperienceEvent, ExperienceState> {
Emitter<ExperienceState> emit,
) async {
emit(state.copyWith(status: ExperienceStatus.loading));
try {
final results = await Future.wait([
getIndustries(),
getSkills(),
]);
await handleError(
emit: emit,
action: () async {
final results = await Future.wait([getIndustries(), getSkills()]);
emit(state.copyWith(
status: ExperienceStatus.initial,
selectedIndustries: results[0]
.map((e) => Industry.fromString(e))
.whereType<Industry>()
.toList(),
selectedSkills: results[1],
));
} catch (e) {
emit(state.copyWith(
emit(
state.copyWith(
status: ExperienceStatus.initial,
selectedIndustries:
results[0]
.map((e) => Industry.fromString(e))
.whereType<Industry>()
.toList(),
selectedSkills: results[1],
),
);
},
onError: (String errorKey) => state.copyWith(
status: ExperienceStatus.failure,
errorMessage: e.toString(),
));
}
errorMessage: errorKey,
),
);
}
void _onIndustryToggled(
@@ -183,19 +188,22 @@ class ExperienceBloc extends Bloc<ExperienceEvent, ExperienceState> {
Emitter<ExperienceState> emit,
) async {
emit(state.copyWith(status: ExperienceStatus.loading));
try {
await saveExperience(
SaveExperienceArguments(
industries: state.selectedIndustries.map((e) => e.value).toList(),
skills: state.selectedSkills,
),
);
emit(state.copyWith(status: ExperienceStatus.success));
} catch (e) {
emit(state.copyWith(
await handleError(
emit: emit,
action: () async {
await saveExperience(
SaveExperienceArguments(
industries: state.selectedIndustries.map((e) => e.value).toList(),
skills: state.selectedSkills,
),
);
emit(state.copyWith(status: ExperienceStatus.success));
},
onError: (String errorKey) => state.copyWith(
status: ExperienceStatus.failure,
errorMessage: e.toString(),
));
}
errorMessage: errorKey,
),
);
}
}

View File

@@ -1,5 +1,6 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_modular/flutter_modular.dart';
import 'package:krow_core/core.dart';
import 'package:krow_domain/krow_domain.dart';
import '../../domain/usecases/get_personal_info_usecase.dart';
@@ -13,17 +14,17 @@ import 'personal_info_state.dart';
/// during onboarding or profile editing. It delegates business logic to
/// use cases following Clean Architecture principles.
class PersonalInfoBloc extends Bloc<PersonalInfoEvent, PersonalInfoState>
with BlocErrorHandler<PersonalInfoState>
implements Disposable {
/// Creates a [PersonalInfoBloc].
///
/// Requires the use cases to load and update the profile.
PersonalInfoBloc({
required GetPersonalInfoUseCase getPersonalInfoUseCase,
required UpdatePersonalInfoUseCase updatePersonalInfoUseCase,
}) : _getPersonalInfoUseCase = getPersonalInfoUseCase,
_updatePersonalInfoUseCase = updatePersonalInfoUseCase,
super(const PersonalInfoState.initial()) {
}) : _getPersonalInfoUseCase = getPersonalInfoUseCase,
_updatePersonalInfoUseCase = updatePersonalInfoUseCase,
super(const PersonalInfoState.initial()) {
on<PersonalInfoLoadRequested>(_onLoadRequested);
on<PersonalInfoFieldChanged>(_onFieldChanged);
on<PersonalInfoAddressSelected>(_onAddressSelected);
@@ -40,32 +41,37 @@ class PersonalInfoBloc extends Bloc<PersonalInfoEvent, PersonalInfoState>
Emitter<PersonalInfoState> emit,
) async {
emit(state.copyWith(status: PersonalInfoStatus.loading));
try {
final Staff staff = await _getPersonalInfoUseCase();
// Initialize form values from staff entity
// Note: Staff entity currently stores address as a string, but we want to map it to 'preferredLocations'
final Map<String, dynamic> initialValues = <String, dynamic>{
'name': staff.name,
'email': staff.email,
'phone': staff.phone,
'preferredLocations': staff.address != null
? <String?>[staff.address]
: <dynamic>[], // TODO: Map correctly when Staff entity supports list
'avatar': staff.avatar,
};
await handleError(
emit: emit,
action: () async {
final Staff staff = await _getPersonalInfoUseCase();
emit(state.copyWith(
status: PersonalInfoStatus.loaded,
staff: staff,
formValues: initialValues,
));
} catch (e) {
emit(state.copyWith(
// Initialize form values from staff entity
// Note: Staff entity currently stores address as a string, but we want to map it to 'preferredLocations'
final Map<String, dynamic> initialValues = <String, dynamic>{
'name': staff.name,
'email': staff.email,
'phone': staff.phone,
'preferredLocations':
staff.address != null
? <String?>[staff.address]
: <dynamic>[], // TODO: Map correctly when Staff entity supports list
'avatar': staff.avatar,
};
emit(
state.copyWith(
status: PersonalInfoStatus.loaded,
staff: staff,
formValues: initialValues,
),
);
},
onError: (String errorKey) => state.copyWith(
status: PersonalInfoStatus.error,
errorMessage: e.toString(),
));
}
errorMessage: errorKey,
),
);
}
/// Handles updating a field value in the current staff profile.
@@ -86,43 +92,48 @@ class PersonalInfoBloc extends Bloc<PersonalInfoEvent, PersonalInfoState>
if (state.staff == null) return;
emit(state.copyWith(status: PersonalInfoStatus.saving));
try {
final Staff updatedStaff = await _updatePersonalInfoUseCase(
UpdatePersonalInfoParams(
staffId: state.staff!.id,
data: state.formValues,
),
);
// Update local state with the returned staff and keep form values in sync
final Map<String, dynamic> newValues = <String, dynamic>{
'name': updatedStaff.name,
'email': updatedStaff.email,
'phone': updatedStaff.phone,
'preferredLocations': updatedStaff.address != null
? <String?>[updatedStaff.address]
: <dynamic>[],
'avatar': updatedStaff.avatar,
};
await handleError(
emit: emit,
action: () async {
final Staff updatedStaff = await _updatePersonalInfoUseCase(
UpdatePersonalInfoParams(
staffId: state.staff!.id,
data: state.formValues,
),
);
emit(state.copyWith(
status: PersonalInfoStatus.saved,
staff: updatedStaff,
formValues: newValues,
));
} catch (e) {
emit(state.copyWith(
// Update local state with the returned staff and keep form values in sync
final Map<String, dynamic> newValues = <String, dynamic>{
'name': updatedStaff.name,
'email': updatedStaff.email,
'phone': updatedStaff.phone,
'preferredLocations':
updatedStaff.address != null
? <String?>[updatedStaff.address]
: <dynamic>[],
'avatar': updatedStaff.avatar,
};
emit(
state.copyWith(
status: PersonalInfoStatus.saved,
staff: updatedStaff,
formValues: newValues,
),
);
},
onError: (String errorKey) => state.copyWith(
status: PersonalInfoStatus.error,
errorMessage: e.toString(),
));
}
errorMessage: errorKey,
),
);
}
void _onAddressSelected(
PersonalInfoAddressSelected event,
Emitter<PersonalInfoState> emit,
) {
// TODO: Implement Google Places logic if needed
// TODO: Implement Google Places logic if needed
}
/// With _onPhotoUploadRequested and _onSaveRequested removed or renamed,
@@ -133,3 +144,4 @@ class PersonalInfoBloc extends Bloc<PersonalInfoEvent, PersonalInfoState>
close();
}
}