feat: Refactor code structure and optimize performance across multiple modules

This commit is contained in:
Achintha Isuru
2025-11-17 23:29:28 -05:00
parent 831570f2e0
commit a64cbd9edf
1508 changed files with 105319 additions and 0 deletions

View File

@@ -0,0 +1,27 @@
const String allWorkingAreas = r'''
query allWorkingAreas {
working_areas {
id
address
}
}
''';
const String staffWorkingAreas = r'''
query staffAreas {
me {
id
working_areas {
id
address
}
}
}
''';
const syncWorkingAreas = r'''
mutation sync($areas: [ID!]!) {
sync_staff_working_areas(areas: $areas) {
}
}
''';

View File

@@ -0,0 +1,9 @@
import 'package:krow/core/data/models/staff/workin_area.dart';
abstract class WorkingAreaRepository {
Stream<List<WorkingArea>> getAllWorkingAreas();
Stream<List<WorkingArea>> getStaffWorkingAreas();
Future<void> saveWorkingArea(List<String> ids);
}

View File

@@ -0,0 +1,57 @@
import 'package:graphql_flutter/graphql_flutter.dart';
import 'package:injectable/injectable.dart';
import 'package:krow/core/application/clients/api/api_client.dart';
import 'package:krow/core/data/models/staff/workin_area.dart';
import 'package:krow/features/profile/working_area/data/gql.dart';
@injectable
class WorkingAreasApiProvider {
final ApiClient _apiClient;
WorkingAreasApiProvider(this._apiClient);
Stream<List<WorkingArea>> getAllWorkingAreas() async* {
await for (var result
in _apiClient.queryWithCache(schema: allWorkingAreas)) {
if (result == null || result.data == null) continue;
if (result.hasException) {
throw Exception(result.exception.toString());
}
if (result.data?['working_areas'] == null) continue;
yield result.data!['working_areas'].map<WorkingArea>((e) {
return WorkingArea.fromJson(e);
}).toList();
}
}
Stream<List<WorkingArea>> getStaffWorkingAreas() async* {
await for (var result
in _apiClient.queryWithCache(schema: staffWorkingAreas)) {
if (result == null || result.data == null) {
continue;
}
if (result.hasException) {
if (result.exception is OperationException) continue;
throw Exception(result.exception.toString());
}
if (result.data?['me']?['working_areas'] == null) continue;
yield result.data!['me']['working_areas'].map<WorkingArea>((e) {
return WorkingArea.fromJson(e);
}).toList();
}
}
Future<void> putWorkingAreas(List<String> workingAreas) async {
final Map<String, dynamic> variables = {
'areas': workingAreas,
};
var result =
await _apiClient.mutate(schema: syncWorkingAreas, body: variables);
if (result.hasException) throw Exception(result.exception.toString());
}
}

View File

@@ -0,0 +1,64 @@
import 'package:flutter/foundation.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:krow/core/application/di/injectable.dart';
import 'package:krow/core/data/models/staff/workin_area.dart';
import 'package:krow/core/data/enums/state_status.dart';
import 'package:krow/features/profile/working_area/data/working_area_repository.dart';
import 'package:rxdart/rxdart.dart';
part 'working_area_event.dart';
part 'working_area_state.dart';
class WorkingAreaBloc extends Bloc<WorkingAreaEvent, WorkingAreaState> {
WorkingAreaBloc() : super(const WorkingAreaState()) {
on<InitializeWorkingAreaEvent>(_onInitialize);
on<CheckAreaEvent>(_onCheckArea);
on<SubmitWorkingAreaEvent>(_onSubmit);
}
void _onInitialize(
InitializeWorkingAreaEvent event, Emitter<WorkingAreaState> emit) async {
emit(state.copyWith(status: StateStatus.loading));
var staffWorkingAreasStream =
getIt<WorkingAreaRepository>().getStaffWorkingAreas();
var allWorkingAreasStream =
getIt<WorkingAreaRepository>().getAllWorkingAreas();
await for (var areas in CombineLatestStream.combine2(
allWorkingAreasStream,
staffWorkingAreasStream,
(List<WorkingArea> allAreas, List<WorkingArea> staffAreas) {
return allAreas.map((area) {
var selected = staffAreas.any((assignedArea) => assignedArea.id == area.id);
return AreaItemState(id: area.id, name: area.address, selected: selected);
}).toList();
},
)) {
emit(state.copyWith(status: StateStatus.idle, areas: areas));
}
}
void _onCheckArea(CheckAreaEvent event, Emitter<WorkingAreaState> emit) {
var areas = state.areas.map((e) {
if (e.id == event.id) {
return e.copyWith(selected: event.selected);
}
return e;
}).toList();
emit(state.copyWith(areas: areas));
}
void _onSubmit(
SubmitWorkingAreaEvent event, Emitter<WorkingAreaState> emit) async {
emit(state.copyWith(status: StateStatus.loading));
var ids = state.areas
.where((element) => element.selected)
.map((e) => e.id)
.toList();
await getIt<WorkingAreaRepository>().saveWorkingArea(ids);
emit(state.copyWith(status: StateStatus.success));
}
}

View File

@@ -0,0 +1,19 @@
part of 'working_area_bloc.dart';
@immutable
sealed class WorkingAreaEvent {}
class InitializeWorkingAreaEvent extends WorkingAreaEvent {
InitializeWorkingAreaEvent();
}
class CheckAreaEvent extends WorkingAreaEvent {
final String id;
final bool selected;
CheckAreaEvent({required this.id, required this.selected});
}
class SubmitWorkingAreaEvent extends WorkingAreaEvent {
SubmitWorkingAreaEvent();
}

View File

@@ -0,0 +1,40 @@
part of 'working_area_bloc.dart';
@immutable
class WorkingAreaState {
final StateStatus status;
final List<AreaItemState> areas;
const WorkingAreaState({
this.areas = const [],
this.status = StateStatus.idle,
});
WorkingAreaState copyWith({
List<AreaItemState>? areas,
StateStatus? status,
}) {
return WorkingAreaState(
areas: areas ?? this.areas,
status: status ?? this.status,
);
}
}
class AreaItemState {
final String id;
final String name;
final bool selected;
AreaItemState({required this.id, required this.name, this.selected = false});
AreaItemState copyWith({
bool? selected,
}) {
return AreaItemState(
id: id,
name: name,
selected: selected ?? this.selected,
);
}
}

View File

@@ -0,0 +1,27 @@
import 'package:injectable/injectable.dart';
import 'package:krow/core/data/models/staff/workin_area.dart';
import 'package:krow/features/profile/working_area/data/working_area_repository.dart';
import 'package:krow/features/profile/working_area/data/working_areas_api_provider.dart';
@Singleton(as: WorkingAreaRepository)
class WorkingAreaRepositoryImpl implements WorkingAreaRepository {
final WorkingAreasApiProvider _apiProvider;
WorkingAreaRepositoryImpl({required WorkingAreasApiProvider apiProvider})
: _apiProvider = apiProvider;
@override
Stream<List<WorkingArea>> getAllWorkingAreas() {
return _apiProvider.getAllWorkingAreas();
}
@override
Stream<List<WorkingArea>> getStaffWorkingAreas() {
return _apiProvider.getStaffWorkingAreas();
}
@override
Future<void> saveWorkingArea(List<String> ids) {
return _apiProvider.putWorkingAreas(ids);
}
}

View File

@@ -0,0 +1,106 @@
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:gap/gap.dart';
import 'package:krow/core/application/routing/routes.gr.dart';
import 'package:krow/core/data/enums/state_status.dart';
import 'package:krow/core/presentation/styles/kw_text_styles.dart';
import 'package:krow/core/presentation/styles/theme.dart';
import 'package:krow/core/presentation/widgets/scroll_layout_helper.dart';
import 'package:krow/core/presentation/widgets/ui_kit/check_box.dart';
import 'package:krow/core/presentation/widgets/ui_kit/check_box_card.dart';
import 'package:krow/core/presentation/widgets/ui_kit/kw_app_bar.dart';
import 'package:krow/core/presentation/widgets/ui_kit/kw_button.dart';
import 'package:krow/core/presentation/widgets/ui_kit/kw_loading_overlay.dart';
import 'package:krow/features/profile/working_area/domain/bloc/working_area_bloc.dart';
@RoutePage()
class WorkingAreaScreen extends StatelessWidget implements AutoRouteWrapper {
final bool isInEditMode;
const WorkingAreaScreen({super.key, this.isInEditMode = true});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: KwAppBar(
showNotification: isInEditMode,
titleText: 'working_area'.tr(),
),
body: BlocConsumer<WorkingAreaBloc, WorkingAreaState>(
listenWhen: (previous, current) => previous.status != current.status,
listener: (context, state) {
if (state.status == StateStatus.success) {
if (isInEditMode) {
Navigator.pop(context);
} else {
context.router.push(
RoleRoute(isInEditMode: false),
);
}
}
},
builder: (context, state) {
return KwLoadingOverlay(
shouldShowLoading: state.status == StateStatus.loading,
child: ScrollLayoutHelper(
padding: isInEditMode? const EdgeInsets.symmetric(horizontal: 16):const EdgeInsets.all(16),
upperWidget: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
if (isInEditMode)
Text(
'select_nearby'.tr(),
style: AppTextStyles.bodySmallReg
.copyWith(color: AppColors.blackGray),
),
if (!isInEditMode) ...[
const Gap(20),
Text(
'where_u_can_work'.tr(),
style: AppTextStyles.headingH1,
),
Text(
'define_the_zone'.tr(),
style: AppTextStyles.bodyMediumReg
.copyWith(color: AppColors.blackGray),
),
],
const Gap(24),
...state.areas.map((item) => CheckBoxCard(
checkBoxStyle: CheckBoxStyle.black,
padding: const EdgeInsets.only(bottom: 8.0),
isChecked: item.selected,
title: item.name,
onTap: () {
context.read<WorkingAreaBloc>().add(CheckAreaEvent(
id: item.id, selected: !item.selected));
},
)),
const Gap(24),
],
),
lowerWidget: KwButton.primary(
disabled: state.areas.every((element) => !element.selected),
label: isInEditMode ? 'save_changes'.tr() : 'save_and_continue'.tr(),
onPressed: () {
context
.read<WorkingAreaBloc>()
.add(SubmitWorkingAreaEvent());
}),
),
);
},
),
);
}
@override
Widget wrappedRoute(BuildContext context) {
return BlocProvider(
create: (context) => WorkingAreaBloc()..add(InitializeWorkingAreaEvent()),
child: this,
);
}
}