Fix: Resolve critical linting issues and bugs (concurrency, syntax, dead code)
This commit is contained in:
@@ -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,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user