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,212 @@
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/presentation/widgets/ui_kit/kw_app_bar.dart';
import 'package:krow/core/presentation/widgets/ui_kit/kw_loading_overlay.dart';
import 'package:krow/features/shifts/data/models/staff_shift.dart';
import 'package:krow/features/shifts/domain/blocs/shift_deteils_bloc/shift_details_bloc.dart';
import 'package:krow/features/shifts/domain/shift_entity.dart';
import 'package:krow/features/shifts/presentation/dialogs/geocoding_dialogs.dart';
import 'package:krow/features/shifts/presentation/widgets/shift_buttons_widget.dart';
import 'package:krow/features/shifts/presentation/widgets/shift_info_time_row/shift_info_clock_time_widget.dart';
import 'package:krow/features/shifts/presentation/widgets/shift_info_time_row/shift_info_planing_duration_widget.dart';
import 'package:krow/features/shifts/presentation/widgets/shift_info_time_row/shift_info_start_widget.dart';
import 'package:krow/features/shifts/presentation/widgets/shift_info_time_row/shift_info_total_duration_widget.dart';
import 'package:krow/features/shifts/presentation/widgets/shift_item_widget.dart';
import 'package:krow/features/shifts/presentation/widgets/shift_key_responsibilities_widget.dart';
import 'package:krow/features/shifts/presentation/widgets/shift_location_widget.dart';
import 'package:krow/features/shifts/presentation/widgets/shift_manage_widget.dart';
import 'package:krow/features/shifts/presentation/widgets/shift_payment_step_card_widget.dart';
import 'package:krow/features/shifts/presentation/widgets/shift_rating_widget.dart';
import 'package:krow/features/shifts/presentation/widgets/shift_timer_widgets/shift_timer_card_widget.dart';
@RoutePage()
class ShiftDetailsScreen extends StatefulWidget implements AutoRouteWrapper {
final ShiftEntity shift;
const ShiftDetailsScreen({super.key, required this.shift});
@override
State<ShiftDetailsScreen> createState() => _ShiftDetailsScreenState();
@override
Widget wrappedRoute(BuildContext context) {
return BlocProvider<ShiftDetailsBloc>(
create: (context) =>
ShiftDetailsBloc()..add(ShiftDetailsInitialEvent(shift: shift)),
child: this,
);
}
}
class _ShiftDetailsScreenState extends State<ShiftDetailsScreen> with WidgetsBindingObserver{
final OverlayPortalController _controller = OverlayPortalController();
var expanded = false;
String _getTitle(EventShiftRoleStaffStatus status) {
switch (status) {
//do not use enum name. Its need for future localization;
case EventShiftRoleStaffStatus.assigned:
return 'assigned';
case EventShiftRoleStaffStatus.confirmed:
return 'confirmed';
case EventShiftRoleStaffStatus.ongoing:
return 'active';
case EventShiftRoleStaffStatus.completed:
return 'completed';
case EventShiftRoleStaffStatus.declineByStaff:
return 'declined';
case EventShiftRoleStaffStatus.canceledByStaff:
case EventShiftRoleStaffStatus.canceledByBusiness:
case EventShiftRoleStaffStatus.canceledByAdmin:
case EventShiftRoleStaffStatus.requestedReplace:
return 'canceled';
}
}
bool _showButtons(EventShiftRoleStaffStatus status) =>
status == EventShiftRoleStaffStatus.assigned || status == EventShiftRoleStaffStatus.confirmed;
bool showTimer(EventShiftRoleStaffStatus status) =>
status == EventShiftRoleStaffStatus.confirmed ||
status == EventShiftRoleStaffStatus.ongoing;
void _listenHandler(BuildContext context, ShiftDetailsState state) {
if (state.needPop) {
context.router.maybePop();
return;
}
if (state.isLoading) {
_controller.show();
} else {
_controller.hide();
}
if(state.errorMessage!=null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(state.errorMessage!),
),
);
}
if (state.proximityState == GeofencingProximityState.none) return;
GeocodingDialogs.showGeocodingErrorDialog(state, context);
}
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
expanded = widget.shift.status != EventShiftRoleStaffStatus.completed;
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
if (state == AppLifecycleState.resumed) {
BlocProvider.of<ShiftDetailsBloc>(context).add(const ShiftCheckGeocodingEvent());
}
}
@override
Widget build(BuildContext context) {
return BlocConsumer<ShiftDetailsBloc, ShiftDetailsState>(
buildWhen: (previous, current) =>
previous.shiftViewModel != current.shiftViewModel,
listenWhen: (previous, current) =>
previous.isLoading != current.isLoading ||
previous.proximityState != current.proximityState || current.errorMessage!=null,
listener: _listenHandler,
builder: (context, state) {
var viewModel = state.shiftViewModel;
var status = viewModel.status;
return KwLoadingOverlay(
controller: _controller,
child: Scaffold(
appBar: KwAppBar(
titleText: '${_getTitle(status).tr()} ${'Shift'.tr()}',
showNotification: true,
),
body: Stack(
children: [
ListView(
padding: const EdgeInsets.only(top: 16),
children: [
ShiftItemWidget(
bottomPadding: 0,
viewModel,
isDetailsMode: true,
),
if (showTimer(viewModel.status))
const ShiftTimerCardWidget(),
_buildShiftTimeInfo(viewModel),
if (status == EventShiftRoleStaffStatus.completed)
ShiftPaymentStepCardWidget(viewModel: viewModel),
if (status == EventShiftRoleStaffStatus.completed &&
viewModel.rating != null)
ShiftRatingWidget(viewModel: viewModel),
if (status != EventShiftRoleStaffStatus.ongoing)
ShiftLocationWidget(viewModel: viewModel),
ShiftManageWidget(managers: viewModel.managers),
if (viewModel.additionalInfo != null)
ShiftKeyResponsibilitiesWidget(
text: viewModel.additionalInfo!,
expandable:
status == EventShiftRoleStaffStatus.completed,
isExpanded: expanded,
onTap: () {
setState(() {
expanded = !expanded;
});
},
),
SizedBox(height: MediaQuery.sizeOf(context).height / 3),
],
),
if (_showButtons(viewModel.status))
Positioned(
bottom: 0,
left: 0,
right: 0,
child: ShiftButtonsWidget(
viewModel.status,
),
),
],
),
),
);
},
);
}
Widget _buildShiftTimeInfo(ShiftEntity shiftViewModel) {
return Container(
margin: const EdgeInsets.only(left: 16, right: 16, top: 8),
child: Row(
children: [
shiftViewModel.status == EventShiftRoleStaffStatus.completed
? ShiftInfoClockTimeWidget(viewModel: shiftViewModel)
: ShiftInfoStartWidget(viewModel: shiftViewModel),
const Gap(8),
shiftViewModel.status == EventShiftRoleStaffStatus.completed
? ShiftInfoTotalDurationWidget(viewModel: shiftViewModel)
: ShiftInfoPlaningDurationWidget(viewModel: shiftViewModel)
],
),
);
}
}

View File

@@ -0,0 +1,204 @@
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/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/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_tabs.dart';
import 'package:krow/features/shifts/domain/blocs/shifts_list_bloc/shifts_bloc.dart';
import 'package:krow/features/shifts/domain/blocs/shifts_list_bloc/shifts_event.dart';
import 'package:krow/features/shifts/domain/blocs/shifts_list_bloc/shifts_state.dart';
import 'package:krow/features/shifts/domain/services/shift_completer_service.dart';
import 'package:krow/features/shifts/domain/shift_entity.dart';
import 'package:krow/features/shifts/presentation/widgets/shift_item_widget.dart';
@RoutePage()
class ShiftsListMainScreen extends StatefulWidget {
const ShiftsListMainScreen({super.key});
@override
State<ShiftsListMainScreen> createState() => _ShiftsListMainScreenState();
}
class _ShiftsListMainScreenState extends State<ShiftsListMainScreen> {
late AppLifecycleListener _appLifecycleListener;
var dialogOpened = false;
final List<String> tabs = [
'assigned',
'confirmed',
'active',
'completed',
'canceled'
];
late ScrollController _scrollController;
@override
void didChangeDependencies() {
super.didChangeDependencies();
WidgetsBinding.instance.addPostFrameCallback((_) {
BlocProvider.of<ShiftsBloc>(context).add(
LoadTabShiftEvent(
status: BlocProvider.of<ShiftsBloc>(context).state.tabIndex),
);
BlocProvider.of<ShiftsBloc>(context).add(
const ReloadMissingBreakShift(),
);
});
}
@override
void initState() {
super.initState();
_appLifecycleListener = AppLifecycleListener(onStateChange: (state) {
if (state == AppLifecycleState.resumed) {
BlocProvider.of<ShiftsBloc>(context).add(
const ReloadMissingBreakShift(),
);
}
});
_scrollController = ScrollController();
_scrollController.addListener(_onScroll);
}
@override
void dispose() {
_appLifecycleListener.dispose();
_scrollController.removeListener(_onScroll);
_scrollController.dispose();
super.dispose();
}
void _onScroll() {
if (_scrollController.position.atEdge) {
if (_scrollController.position.pixels != 0) {
BlocProvider.of<ShiftsBloc>(context).add(
LoadMoreShiftEvent(
status: BlocProvider.of<ShiftsBloc>(context).state.tabIndex),
);
}
}
}
@override
Widget build(BuildContext context) {
return BlocConsumer<ShiftsBloc, ShiftsState>(
listenWhen: (oldState, state) {
return state.missedShifts.isNotEmpty &&
(oldState.missedShifts.isEmpty ||
oldState.missedShifts.first.id != state.missedShifts.first.id);
},
listener: (context, state) async {
if (state.missedShifts.isNotEmpty) {
if(dialogOpened) return;
dialogOpened = true;
await ShiftCompleterService().startCompleteProcess(
context,
canSkip: false,
state.missedShifts.first,
onComplete: () {},
);
dialogOpened = false;
BlocProvider.of<ShiftsBloc>(context).add(
const ReloadMissingBreakShift(),
);
}
},
builder: (context, state) {
List<ShiftEntity> items = state.tabs[state.tabIndex]!.items;
return Scaffold(
appBar: KwAppBar(
titleText: 'your_shifts'.tr(),
showNotification: true,
centerTitle: false,
),
body: ScrollLayoutHelper(
padding: const EdgeInsets.symmetric(vertical: 16),
onRefresh: () async {
BlocProvider.of<ShiftsBloc>(context)
.add(const ReloadMissingBreakShift());
BlocProvider.of<ShiftsBloc>(context)
.add(LoadTabShiftEvent(status: state.tabIndex));
},
controller: _scrollController,
upperWidget: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
KwTabBar(
key: const Key('shifts_tab_bar'),
tabs: tabs.map((e) => e.tr()).toList(),
onTap: (index) {
BlocProvider.of<ShiftsBloc>(context)
.add(ShiftsTabChangedEvent(tabIndex: index));
}),
const Gap(16),
if (state.tabs[state.tabIndex]!.isLoading &&
state.tabs[state.tabIndex]!.items.isEmpty)
..._buildListLoading(),
if (!state.tabs[state.tabIndex]!.isLoading && items.isEmpty)
..._emptyListWidget(),
RefreshIndicator(
onRefresh: () async {
BlocProvider.of<ShiftsBloc>(context)
.add(LoadTabShiftEvent(status: state.tabIndex));
},
child: ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: items.length,
itemBuilder: (context, index) {
return ShiftItemWidget(
items[index],
bottomPadding: 12,
onPressed: () {
context.pushRoute(
ShiftDetailsRoute(shift: items[index]),
);
},
);
}),
),
],
),
lowerWidget: const SizedBox.shrink(),
),
);
},
);
}
List<Widget> _buildListLoading() {
return [
const Gap(116),
const Center(child: CircularProgressIndicator()),
];
}
List<Widget> _emptyListWidget() {
return [
const Gap(100),
Container(
height: 64,
width: 64,
decoration: BoxDecoration(
color: AppColors.grayWhite,
borderRadius: BorderRadius.circular(32),
),
child: Center(child: Assets.images.icons.xCircle.svg()),
),
const Gap(24),
Text(
'you_currently_have_no_shifts'.tr(),
textAlign: TextAlign.center,
style: AppTextStyles.headingH2,
),
];
}
}