feat: legacy mobile apps created

This commit is contained in:
Achintha Isuru
2025-12-02 23:51:04 -05:00
parent 850441ca64
commit 8e7753b324
1519 changed files with 0 additions and 16 deletions

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);
});
}
}