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,11 @@
const String attachStaffRolesMutation = r'''
mutation AttachStaffRoles($roles: [AttachStaffSkillInput!]!) {
attach_staff_roles(roles: $roles){}
}
''';
const String detachStaffRolesMutation = r'''
mutation DetachStaffRoles($ids: [ID!]!) {
detach_staff_roles(ids: $ids)
}
''';

View File

@@ -0,0 +1,65 @@
import 'package:injectable/injectable.dart';
import 'package:krow/core/application/clients/api/api_client.dart';
import 'package:krow/core/application/clients/api/gql.dart';
import 'package:krow/core/data/models/skill.dart';
import 'package:krow/core/data/models/staff_role.dart';
import 'package:krow/features/profile/role/data/gql.dart';
@injectable
class StaffRoleApiProvider {
final ApiClient _apiClient;
StaffRoleApiProvider(this._apiClient);
Future<List<StaffRole>> fetchStaffRoles() async {
var result = await _apiClient.query(schema: getStaffRolesQuery);
if (result.hasException) {
throw Exception(result.exception.toString());
}
return result.data!['staff_roles'].map<StaffRole>((e) {
return StaffRole.fromJson(e);
}).toList();
}
Future<List<Skill>> fetchSkills() {
return _apiClient.query(schema: getSkillsQuery).then((result) {
if (result.hasException) {
throw Exception(result.exception.toString());
}
return result.data!['skills'].map<Skill>((e) {
return Skill.fromJson(e);
}).toList();
});
}
Future<String> saveStaffRole(StaffRole role) {
return _apiClient.mutate(schema: attachStaffRolesMutation, body: {
'roles': [
{
'level': role.level.toString().split('.').last,
'experience': role.experience,
'skill_id': role.skill!.id,
}
],
}).then((result) {
if (result.hasException) {
throw Exception(result.exception.toString());
}
return '';
});
}
Future<void> deleteStaffRole(StaffRole role) {
return _apiClient.mutate(schema: detachStaffRolesMutation, body: {
'ids': [role.skill?.id],
}).then((result) {
if (result.hasException) {
throw Exception(result.exception.toString());
}
});
}
}

View File

@@ -0,0 +1,32 @@
import 'package:injectable/injectable.dart';
import 'package:krow/core/data/models/skill.dart';
import 'package:krow/core/data/models/staff_role.dart';
import 'package:krow/features/profile/role/data/staff_role_api.dart';
import 'package:krow/features/profile/role/domain/staff_role_repository.dart';
@Injectable(as: StaffRoleRepository)
class StaffRoleRepositoryImpl extends StaffRoleRepository {
final StaffRoleApiProvider _staffRoleApi;
StaffRoleRepositoryImpl(this._staffRoleApi);
@override
Future<List<StaffRole>> getStaffRole() async {
return _staffRoleApi.fetchStaffRoles();
}
@override
Future<List<Skill>> getSkills() {
return _staffRoleApi.fetchSkills();
}
@override
Future<String> saveStaffRole(StaffRole role) {
return _staffRoleApi.saveStaffRole(role);
}
@override
Future<void> deleteStaffRole(StaffRole role) {
return _staffRoleApi.deleteStaffRole(role);
}
}

View File

@@ -0,0 +1,99 @@
import 'dart:async';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:krow/core/application/di/injectable.dart';
import 'package:krow/core/data/enums/staff_skill_enums.dart';
import 'package:krow/core/data/models/skill.dart';
import 'package:krow/core/data/models/staff_role.dart';
import 'package:krow/features/profile/role/domain/bloc/role_event.dart';
import 'package:krow/features/profile/role/domain/bloc/role_state.dart';
import 'package:krow/features/profile/role/domain/staff_role_repository.dart';
class RoleBloc extends Bloc<RoleEvent, RoleState> {
List<Skill>? skills;
List<StaffRole> staffRoles = [];
RoleBloc() : super(const RoleState(staffRoles: [], isLoading: true)) {
on<RoleFetchEvent>(_onFetch);
on<RoleSelectEvent>(_onSelect);
on<RoleSpawnEvent>(_onSpawn);
on<RoleDeleteEvent>(_onDelete);
on<RoleUpdateEvent>(_onUpdate);
on<RoleSaveChangesEvent>(_onSaveChanges);
}
FutureOr<void> _onFetch(event, emit) async {
staffRoles = await getIt<StaffRoleRepository>().getStaffRole();
skills = await getIt<StaffRoleRepository>().getSkills();
var availableSkills = skills!
.where((e) => !staffRoles.any((r) => r.skill?.id == e.id))
.toList();
emit(RoleState(
staffRoles: staffRoles, skills: availableSkills, isLoading: false));
if (staffRoles.isEmpty) {
var staffRole = StaffRole(
id: DateTime.now().toIso8601String(),
level: StaffSkillLevel.beginner,
experience: 1);
emit(state.copyWith(editedRole: staffRole, staffRoles: [staffRole]));
}
}
FutureOr<void> _onSelect(RoleSelectEvent event, emit) async {
emit(state.copyWith(editedRole: event.editedRole));
}
FutureOr<void> _onSpawn(event, emit) async {
var newRole = StaffRole(
id: DateTime.now().toIso8601String(),
level: StaffSkillLevel.beginner,
experience: 1);
emit(state.copyWith(
editedRole: newRole, staffRoles: [...state.staffRoles, newRole]));
}
FutureOr<void> _onDelete(RoleDeleteEvent event, emit) async {
emit(state.copyWith(isLoading: true));
if (event.role.status != null) {
await getIt<StaffRoleRepository>().deleteStaffRole(event.role);
}
var roles = state.staffRoles.where((e) => e != event.role).toList();
var availableSkills =
skills!.where((e) => !roles.any((r) => r.skill?.id == e.id)).toList();
emit(state.copyWith(
editedRole:
event.role.id == state.editedRole?.id ? null : state.editedRole,
staffRoles: roles,
skills: availableSkills,
isLoading: false));
}
FutureOr<void> _onUpdate(RoleUpdateEvent event, emit) async {
var roles = state.staffRoles
.map((e) => e.id == event.role.id ? event.role : e)
.toList();
var availableSkills =
skills!.where((e) => !roles.any((r) => r.skill?.id == e.id)).toList();
emit(state.copyWith(
staffRoles: roles, editedRole: event.role, skills: availableSkills));
}
Future<void> _onSaveChanges(RoleSaveChangesEvent event, emit) async {
emit(state.copyWith(isLoading: true));
await getIt<StaffRoleRepository>().saveStaffRole(event.role);
List<StaffRole> roles = state.staffRoles
.map((e) => e.id == event.role.id
? event.role.copyWith(status: StaffSkillStatus.pending)
: e)
.toList();
emit(state.copyWith(
staffRoles: roles,
isLoading: false,
editedRole: StaffRole.empty(),
));
}
}

View File

@@ -0,0 +1,37 @@
import 'package:flutter/foundation.dart';
import 'package:krow/core/data/models/staff_role.dart';
@immutable
sealed class RoleEvent {}
class RoleFetchEvent extends RoleEvent {
RoleFetchEvent();
}
class RoleSelectEvent extends RoleEvent {
final StaffRole editedRole;
RoleSelectEvent(this.editedRole);
}
class RoleSpawnEvent extends RoleEvent {
RoleSpawnEvent();
}
class RoleDeleteEvent extends RoleEvent {
final StaffRole role;
RoleDeleteEvent(this.role);
}
class RoleUpdateEvent extends RoleEvent {
final StaffRole role;
RoleUpdateEvent(this.role);
}
class RoleSaveChangesEvent extends RoleEvent {
final StaffRole role;
RoleSaveChangesEvent(this.role);
}

View File

@@ -0,0 +1,31 @@
import 'package:flutter/foundation.dart';
import 'package:krow/core/data/models/skill.dart';
import 'package:krow/core/data/models/staff_role.dart';
@immutable
class RoleState {
final bool isLoading;
final List<StaffRole> staffRoles;
final List<Skill> skills;
final StaffRole? editedRole;
const RoleState(
{required this.staffRoles,
this.editedRole,
this.isLoading = false,
this.skills = const []});
copyWith({
List<StaffRole>? staffRoles,
StaffRole? editedRole,
bool? isLoading,
List<Skill>? skills,
}) {
return RoleState(
staffRoles: staffRoles ?? this.staffRoles,
editedRole: editedRole ?? this.editedRole,
isLoading: isLoading ?? this.isLoading,
skills: skills ?? this.skills,
);
}
}

View File

@@ -0,0 +1,12 @@
import 'package:krow/core/data/models/skill.dart';
import 'package:krow/core/data/models/staff_role.dart';
abstract class StaffRoleRepository {
Future<List<StaffRole>> getStaffRole();
Future<List<Skill>> getSkills();
Future<String> saveStaffRole(StaffRole role);
Future<void> deleteStaffRole(StaffRole role);
}

View File

@@ -0,0 +1,118 @@
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/presentation/styles/kw_box_decorations.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/kw_app_bar.dart';
import 'package:krow/core/presentation/widgets/ui_kit/kw_button.dart';
import 'package:krow/features/profile/role/domain/bloc/role_bloc.dart';
import 'package:krow/features/profile/role/domain/bloc/role_event.dart';
import 'package:krow/features/profile/role/domain/bloc/role_state.dart';
import 'package:krow/features/profile/role/presentation/widgets/role_card_widget.dart';
import 'package:krow/features/profile/role/presentation/widgets/role_edit_card_widget.dart';
import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart';
@RoutePage()
class RoleScreen extends StatelessWidget implements AutoRouteWrapper {
final bool isInEditMode;
const RoleScreen({
super.key,
this.isInEditMode = true,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: KwAppBar(
titleText: 'role'.tr(),
centerTitle: true,
),
body: BlocBuilder<RoleBloc, RoleState>(
builder: (context, state) {
return ModalProgressHUD(
inAsyncCall: state.isLoading,
child: ScrollLayoutHelper(
upperWidget: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Gap(36),
Text('select_u_role'.tr(),
style: AppTextStyles.headingH1),
const Gap(8),
Text(
'what_u_area'.tr(),
style: AppTextStyles.bodyMediumReg
.copyWith(color: AppColors.blackGray)),
const SizedBox(height: 12),
Column(
children: [
...state.staffRoles.indexed.toList().map(
(e) {
var (index, role) = e;
return Container(
decoration: KwBoxDecorations.primaryLight12,
padding: const EdgeInsets.only(
top: 12, left: 12, right: 12),
margin: const EdgeInsets.only(bottom: 12),
child: AnimatedSize(
alignment: Alignment.topCenter,
duration: const Duration(milliseconds: 200),
child: state.editedRole == role
? RoleEditCardWidget(
role: role,
index: index,
skills: state.skills,
mustShowWarningDialog: isInEditMode,
canDelete:
state.staffRoles.length > 1)
: RoleCard(
role: role,
index: index,
canDelete:
state.staffRoles.length > 1)));
},
),
const Gap(12),
if (!state.isLoading)
KwButton.outlinedPrimary(
label: 'add_role'.tr(),
onPressed: () {
context.read<RoleBloc>().add(RoleSpawnEvent());
},
),
],
)
],
),
lowerWidget: isInEditMode
? const SizedBox.shrink()
: Padding(
padding: const EdgeInsets.only(bottom: 20, top: 24),
child: KwButton.primary(
label: 'save_and_continue'.tr(), onPressed: () {
context.router.replace(const SplashRoute());
}),
),
),
);
},
),
);
}
@override
Widget wrappedRoute(BuildContext context) {
return BlocProvider(
create: (context) => RoleBloc()..add(RoleFetchEvent()),
child: this,
);
}
}

View File

@@ -0,0 +1,195 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_xlider/flutter_xlider.dart';
import 'package:gap/gap.dart';
import 'package:krow/core/presentation/styles/kw_text_styles.dart';
import 'package:krow/core/presentation/styles/theme.dart';
class ExpSliderWidget extends StatefulWidget {
final int initialValue;
final ValueChanged<int>? onChanged;
const ExpSliderWidget({
super.key,
this.initialValue = 1,
this.onChanged,
});
@override
State<ExpSliderWidget> createState() => _ExpSliderWidgetState();
}
class _ExpSliderWidgetState extends State<ExpSliderWidget> {
late double _value;
@override
void initState() {
super.initState();
_value = widget.initialValue.toDouble();
_value = _value.clamp(1, 20);
}
@override
void didUpdateWidget(covariant ExpSliderWidget oldWidget) {
_value = widget.initialValue.toDouble();
_value = _value.clamp(1, 20);
super.didUpdateWidget(oldWidget);
}
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.only(top: 12, bottom: 12),
margin: const EdgeInsets.only(bottom: 24),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: AppColors.grayStroke,
width: 1,
),
color: AppColors.graySecondaryFrame,
),
child: Column(
children: [
const Gap(24),
FlutterSlider(
min: 1,
max: 20,
handlerHeight: 26,
handlerWidth: 26,
tooltip: FlutterSliderTooltip(
alwaysShowTooltip: true,
custom: (value) {
return _SliderTooltip(
text: value.toStringAsFixed(0),
);
}),
handler: FlutterSliderHandler(
child: Container(
decoration: const BoxDecoration(
color: AppColors.grayWhite,
shape: BoxShape.circle,
),
child: Center(
child: Container(
height: 16,
width: 16,
decoration: const BoxDecoration(
color: AppColors.bgColorDark,
shape: BoxShape.circle,
)),
),
)),
trackBar: FlutterSliderTrackBar(
activeTrackBarHeight: 12,
inactiveTrackBarHeight: 12,
inactiveTrackBar: BoxDecoration(
borderRadius: BorderRadius.circular(6),
color: AppColors.grayTintStroke,
),
activeTrackBar: BoxDecoration(
borderRadius: BorderRadius.circular(6),
color: AppColors.bgColorDark,
),
),
values: [_value],
onDragging: (handlerIndex, lowerValue, upperValue) {
// setState(() {
// _value = lowerValue;
// });
widget.onChanged?.call(lowerValue.toInt());
},
),
Padding(
padding: const EdgeInsets.only(left: 12, right: 12),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'1 ${'year'.tr()}',
style: AppTextStyles.bodySmallMed
.copyWith(color: AppColors.blackCaptionGreen),
),
Text(
'20 ${'years'.tr()}',
style: AppTextStyles.bodySmallMed
.copyWith(color: AppColors.blackCaptionGreen),
),
],
),
)
],
),
);
}
}
class _SliderTooltip extends StatelessWidget {
final String text;
const _SliderTooltip({
required this.text,
});
@override
Widget build(BuildContext context) {
return Transform.translate(
offset: const Offset(0, -30),
child: Container(
alignment: Alignment.centerLeft,
width: 50,
height: 36,
child: CustomPaint(
painter: TooltipPainter(),
child: Center(
child: Padding(
padding: const EdgeInsets.only(bottom: 10),
child: Text(
'$text y',
textAlign: TextAlign.center,
style: AppTextStyles.bodySmallMed.copyWith(
color: AppColors.grayWhite,
),
),
),
),
),
),
);
}
}
class TooltipPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = const Color(0xFF001F2D);
final path = Path();
path.addRRect(
RRect.fromRectAndRadius(
const Rect.fromLTWH(0, 0, 48, 30),
const Radius.circular(15),
),
);
const double triangleWidth = 10;
const double triangleHeight = 6;
const double centerX = 24;
path.moveTo(centerX - triangleWidth / 2, 30);
path.quadraticBezierTo(
centerX,
40 + triangleHeight / 3,
centerX + triangleWidth / 2,
30,
);
path.close();
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return false;
}
}

View File

@@ -0,0 +1,132 @@
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/common/int_extensions.dart';
import 'package:krow/core/application/common/str_extensions.dart';
import 'package:krow/core/data/enums/staff_skill_enums.dart';
import 'package:krow/core/data/models/staff_role.dart';
import 'package:krow/core/presentation/gen/assets.gen.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/ui_kit/kw_button.dart';
import 'package:krow/features/profile/role/domain/bloc/role_bloc.dart';
import 'package:krow/features/profile/role/domain/bloc/role_event.dart';
class RoleCard extends StatelessWidget {
final StaffRole role;
final int index;
final bool canDelete;
const RoleCard({
super.key,
required this.role,
required this.index,
required this.canDelete,
});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${(index + 1).toOrdinal()} ${'role_details'.tr()}',
style: AppTextStyles.bodyMediumMed,
),
if (role.status != null) _buildStatus(),
],
),
const SizedBox(height: 24),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_buildRoleTextInfo(context, '${'role'.tr()}:', role.skill?.name),
_buildRoleTextInfo(
context, '${'experience'.tr()}:', '${role.experience} ${'years'.tr()}'),
_buildRoleTextInfo(context, '${'level'.tr()}:', role.level?.name),
],
),
const SizedBox(height: 24),
Row(
children: [
Expanded(
child: KwButton.outlinedPrimary(
label: 'edit_role'.tr(),
onPressed: () {
BlocProvider.of<RoleBloc>(context).add(
RoleSelectEvent(role),
);
},
leftIcon: Assets.images.icons.edit,
),
),
if (canDelete) ...[
const Gap(8),
KwButton.outlinedPrimary(
fit: KwButtonFit.circular,
onPressed: () {
BlocProvider.of<RoleBloc>(context).add(
RoleDeleteEvent(role),
);
},
leftIcon: Assets.images.icons.delete)
.copyWith(color: AppColors.statusError),
],
],
),
const Gap(12),
],
);
}
Container _buildStatus() {
Color color;
switch (role.status) {
case StaffSkillStatus.pending:
color = AppColors.primaryBlue;
break;
case StaffSkillStatus.verified:
color = AppColors.statusSuccess;
break;
case StaffSkillStatus.deactivated:
color = AppColors.statusError;
break;
default:
color = AppColors.bgColorDark;
}
return Container(
height: 24,
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(12),
),
padding: const EdgeInsets.only(left: 8, right: 8),
child: Center(
child: Text(
role.status?.name.capitalize() ?? '',
style: AppTextStyles.bodySmallMed.copyWith(color: Colors.white),
)),
);
}
Column _buildRoleTextInfo(BuildContext context, String title, String? value) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: AppTextStyles.captionReg.copyWith(
color: AppColors.blackGray,
),
),
const Gap(4),
Text(value ?? '', style: AppTextStyles.bodyMediumMed)
],
);
}
}

View File

@@ -0,0 +1,210 @@
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/common/int_extensions.dart';
import 'package:krow/core/application/common/validators/skill_exp_validator.dart';
import 'package:krow/core/data/enums/staff_skill_enums.dart';
import 'package:krow/core/data/models/skill.dart';
import 'package:krow/core/data/models/staff_role.dart';
import 'package:krow/core/presentation/gen/assets.gen.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/ui_kit/dialogs/kw_dialog.dart';
import 'package:krow/core/presentation/widgets/ui_kit/kw_button.dart';
import 'package:krow/core/presentation/widgets/ui_kit/kw_dropdown.dart';
import 'package:krow/core/presentation/widgets/ui_kit/kw_input.dart';
import 'package:krow/core/presentation/widgets/ui_kit/kw_option_selector.dart';
import 'package:krow/features/profile/role/domain/bloc/role_bloc.dart';
import 'package:krow/features/profile/role/domain/bloc/role_event.dart';
import 'package:krow/features/profile/role/presentation/widgets/exp_slider_widget.dart';
class RoleEditCardWidget extends StatefulWidget {
final StaffRole role;
final int index;
final List<Skill> skills;
final bool canDelete;
final bool mustShowWarningDialog;
const RoleEditCardWidget(
{super.key,
required this.role,
required this.index,
required this.skills,
required this.canDelete,
this.mustShowWarningDialog = false});
@override
State<RoleEditCardWidget> createState() => _RoleEditCardWidgetState();
}
class _RoleEditCardWidgetState extends State<RoleEditCardWidget> {
String? experienceError;
late TextEditingController textEditingController;
@override
void initState() {
textEditingController =
TextEditingController(text: widget.role.experience.toString());
textEditingController.addListener(() {
final value = textEditingController.text;
setState(() {
experienceError = SkillExpValidator.validate(value);
});
var exp = int.tryParse(value);
if (exp != null && experienceError == null) {
BlocProvider.of<RoleBloc>(context).add(RoleUpdateEvent(
widget.role.copyWith(experience: exp),
));
}
});
super.initState();
}
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_header(),
const Gap(16),
_skillDropDown(context),
const Gap(16),
KwOptionSelector(
selectedIndex: widget.role.level?.index ?? 0,
backgroundColor: AppColors.graySecondaryFrame,
onChanged: (index) {
BlocProvider.of<RoleBloc>(context).add(RoleUpdateEvent(
widget.role.copyWith(level: StaffSkillLevel.values[index]),
));
},
title: 'level'.tr(),
items: [
'Beginner'.tr(),
'Skilled'.tr(),
'Professional'.tr(),
]),
const Gap(16),
KwTextInput(
controller: textEditingController,
title: 'years_of_exp'.tr(),
helperText: experienceError,
showError: experienceError != null,
onChanged: (value) {}),
const Gap(12),
ExpSliderWidget(
initialValue: widget.role.experience ?? 1,
onChanged: (value) {
setState(() {
textEditingController.text = value.toString();
});
},
),
KwButton.primary(
disabled: widget.role.skill == null,
label: 'save_changes'.tr(),
onPressed: _onSaveChanges,
),
const Gap(12),
],
);
}
Widget _skillDropDown(BuildContext context) {
return IgnorePointer(
ignoring: widget.role.status != null,
child: KwDropdown<Skill>(
horizontalPadding: 28,
title: 'role'.tr(),
hintText: 'select_role'.tr(),
selectedItem: widget.role.skill == null
? null
: KwDropDownItem(
data: widget.role.skill!,
title: widget.role.skill?.name ?? ''),
items: widget.skills
.map((skill) => KwDropDownItem(data: skill, title: skill.name)),
onSelected: (skill) {
BlocProvider.of<RoleBloc>(context).add(RoleUpdateEvent(
widget.role.copyWith(skill: skill),
));
}),
);
}
Row _header() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${(widget.index + 1).toOrdinal()} ${'role_details'.tr()}:',
style: AppTextStyles.bodyMediumMed,
),
if (widget.canDelete)
GestureDetector(
onTap: () async {
BlocProvider.of<RoleBloc>(context)
.add(RoleDeleteEvent(widget.role));
},
child: Container(
color: Colors.transparent,
height: 24,
width: 24,
child: Center(
child: Assets.images.icons.delete.svg(
height: 16,
width: 16,
colorFilter: const ColorFilter.mode(
AppColors.statusError, BlendMode.srcIn),
),
),
),
),
],
);
}
void _onSaveChanges() {
if (widget.mustShowWarningDialog &&
widget.role.status != StaffSkillStatus.pending) {
_showWarningReviewDialog(context).then(
(value) {
if (value == true && context.mounted) {
BlocProvider.of<RoleBloc>(context)
.add(RoleSaveChangesEvent(widget.role));
}
},
);
} else {
BlocProvider.of<RoleBloc>(context).add(RoleSaveChangesEvent(widget.role));
}
}
Future<bool?> _showWarningReviewDialog(BuildContext context) {
return KwDialog.show<bool>(
context: context,
icon: Assets.images.icons.alertTriangle,
state: KwDialogState.neutral,
title: 'role_change'.tr(),
message: 'change_u_role'.tr(),
child: Text(
'wont_to_proceed'.tr(),
style: AppTextStyles.bodyMediumMed,
),
primaryButtonLabel: 'Continue'.tr(),
secondaryButtonLabel: 'cancel'.tr(),
onPrimaryButtonPressed: (BuildContext dialogContext) {
dialogContext.maybePop(true);
},
onSecondaryButtonPressed: (BuildContext dialogContext) {
dialogContext.maybePop(false);
});
}
}