You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
cloudsolutions-atoms/lib/modules/cleaner_module/pages/one_time_task_view.dart

438 lines
15 KiB
Dart

import 'package:flutter/material.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/modules/cleaner_module/models/one_time_task_data.dart';
import 'package:test_sa/modules/cleaner_module/provider/cleaner_provider.dart';
import 'package:test_sa/modules/cleaner_module/widgets/cleaner_checklist_widget.dart';
import 'package:test_sa/modules/cleaner_module/widgets/cleaner_preview_dialog.dart';
import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/new_views/common_widgets/app_filled_button.dart';
import 'package:test_sa/new_views/common_widgets/app_lazy_loading.dart';
import 'package:test_sa/new_views/common_widgets/default_app_bar.dart';
import 'package:test_sa/new_views/pages/land_page/land_page.dart';
import 'package:test_sa/views/widgets/loaders/app_loading.dart';
import 'package:test_sa/views/widgets/loaders/no_data_found.dart';
import 'package:test_sa/views/widgets/dialogs/confirm_dialog.dart';
class OneTimeTaskView extends StatefulWidget {
static const String routeName = "/one_time_task";
final int taskId;
const OneTimeTaskView({Key? key, required this.taskId}) : super(key: key);
@override
State<OneTimeTaskView> createState() => _OneTimeTaskViewState();
}
class _OneTimeTaskViewState extends State<OneTimeTaskView> {
OneTimeTaskData? _taskData;
bool _isLoading = false;
DateTime? _timerStart;
@override
void initState() {
super.initState();
_loadTaskData();
}
Future<void> _loadTaskData() async {
setState(() {
_isLoading = true;
});
try {
final result = await CleanerProvider().getOneTimeTaskById(id: widget.taskId);
if (result['success'] && result['data'] != null) {
setState(() {
_taskData = OneTimeTaskData.fromJson(result['data']);
_isLoading = false;
// Start timer automatically if task is in progress
if (_taskData?.statusValue == 1 && _taskData?.startTime != null) {
_timerStart = DateTime.parse(_taskData!.startTime!);
}
});
} else {
setState(() {
_isLoading = false;
});
if (mounted) {
_showErrorDialog(context, result['message'] ?? 'Failed to load task');
}
}
} catch (e) {
setState(() {
_isLoading = false;
});
if (mounted) {
_showErrorDialog(context, 'Failed to load task data');
}
}
}
bool get isClosed => _taskData?.statusValue == 2;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: DefaultAppBar(
title: 'One Time Task',
onWillPopScope: isClosed
? null
: () {
_showExitDialog();
},
),
body: _isLoading
? const ALoading()
: _taskData != null
? SafeArea(
child: Column(
children: [
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildTaskInfoCard(context),
16.height,
CleanerChecklistWidget(
isClosed: isClosed,
checklistItems: _convertInstructionsToChecklist(),
onChecklistChanged: (value) {
setState(() {
_updateInstructions(value);
});
},
capturedImageBase64: _taskData!.imageRoom,
onImageCaptured: (imageBase64) {
setState(() {
_taskData!.imageRoom = imageBase64;
});
},
),
],
),
),
),
if (!isClosed) ...[
FooterActionButton.footerContainer(
context: context,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
AppFilledButton(
label: context.translation.save,
buttonColor: context.isDark ? AppColor.neutral20 : AppColor.white60,
textColor: context.isDark ? Colors.white : AppColor.black10,
onPressed: () => Navigator.pop(context),
).expanded,
12.width,
AppFilledButton(
label: context.translation.complete,
buttonColor: AppColor.primary10,
onPressed: _onComplete,
).expanded,
],
),
),
]
],
),
)
: NoDataFound(message: context.translation.noDataFound).center,
).handlePopScope(
cxt: context,
showPopUp: !isClosed,
onSave: () {
Navigator.pop(context);
},
);
}
Widget _buildTaskInfoCard(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (_taskData?.statusName != null)
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: _getStatusColor().withOpacity(0.1),
borderRadius: BorderRadius.circular(4),
),
child: Text(
_taskData!.statusName!,
style: AppTextStyles.bodyText.copyWith(
color: _getStatusColor(),
fontWeight: FontWeight.w600,
),
),
),
if (_taskData?.createdDate != null)
Text(
_formatDate(_taskData!.createdDate!),
style: AppTextStyles.tinyFont.copyWith(
color: context.isDark ? AppColor.neutral30 : AppColor.neutral50,
),
),
],
),
12.height,
if (_taskData?.oneTimeSetting?.taskTypeName != null)
Text(
_taskData!.oneTimeSetting!.taskTypeName!,
style: AppTextStyles.heading4.copyWith(
color: context.isDark ? AppColor.neutral30 : AppColor.neutral50,
),
),
8.height,
if (_taskData?.oneTimeSetting?.instructionDescriptionName != null)
Text(
_taskData!.oneTimeSetting!.instructionDescriptionName!,
style: AppTextStyles.bodyText.copyWith(
color: context.isDark ? AppColor.neutral10 : AppColor.neutral120,
),
),
8.height,
if (_taskData?.siteName != null)
_buildInfoRow('Site', _taskData!.siteName!),
if (_taskData?.buildingName != null)
_buildInfoRow('Building', _taskData!.buildingName!),
if (_taskData?.floorName != null)
_buildInfoRow('Floor', _taskData!.floorName!),
if (_taskData?.departmentName != null)
_buildInfoRow('Department', _taskData!.departmentName!),
if (_taskData?.roomName != null)
_buildInfoRow('Room', _taskData!.roomName!),
if (_taskData?.locationDescription != null)
_buildInfoRow('Location', _taskData!.locationDescription!),
// Timer display
if (_timerStart != null && !isClosed) ...[
12.height,
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: AppColor.primary10.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: AppColor.primary10),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Working Time:',
style: AppTextStyles.bodyText.copyWith(
fontWeight: FontWeight.w600,
color: context.isDark ? AppColor.neutral10 : AppColor.neutral120,
),
),
Text(
_formatDuration(DateTime.now().difference(_timerStart!)),
style: AppTextStyles.heading4.copyWith(
color: AppColor.primary10,
fontWeight: FontWeight.bold,
),
),
],
),
),
],
],
).paddingOnly(start: 16, end: 16, top: 16, bottom: 16),
],
).toShadowContainer(context, padding: 0);
}
Widget _buildInfoRow(String label, String value) {
return Padding(
padding: const EdgeInsets.only(bottom: 4),
child: Text(
'$label: $value',
style: AppTextStyles.bodyText.copyWith(
color: context.isDark ? AppColor.neutral10 : AppColor.neutral120,
),
),
);
}
List<ChecklistItem> _convertInstructionsToChecklist() {
return _taskData?.instructions?.map((instruction) {
return ChecklistItem(
id: instruction.id ?? 0,
title: instruction.instructionText ?? '',
isChecked: instruction.isDone ?? false,
isMandatory: instruction.isRequired ?? false,
comment: instruction.comment ?? '',
);
}).toList() ?? [];
}
void _updateInstructions(List<ChecklistItem> checklist) {
_taskData?.instructions = checklist.map((item) {
return Instruction(
id: item.id,
instructionTextId: _taskData?.instructions?.firstWhere((i) => i.id == item.id).instructionTextId,
instructionText: item.title,
isDone: item.isChecked,
isRequired: item.isMandatory,
comment: item.comment,
);
}).toList();
}
void _onComplete() {
// Validate mandatory items
final uncheckedMandatory = _taskData?.instructions?.where((item) =>
(item.isRequired ?? false) && !(item.isDone ?? false)
).toList() ?? [];
if (uncheckedMandatory.isNotEmpty) {
_showErrorDialog(context, "Please complete all mandatory tasks (marked with *)");
return;
}
// Show preview dialog
_showPreviewDialog();
}
void _showPreviewDialog() {
// Calculate working hours
double totalWorkingHours = 0;
if (_timerStart != null) {
final duration = DateTime.now().difference(_timerStart!);
totalWorkingHours = duration.inSeconds / 3600.0;
}
CleanerPreviewDialog.show(
context,
model: _convertToRecurrentWoData(),
totalWorkingHours: totalWorkingHours,
onConfirm: _performComplete,
);
}
// Convert OneTimeTaskData to RecurrentWoData for preview dialog compatibility
dynamic _convertToRecurrentWoData() {
return {
'siteName': _taskData?.siteName,
'buildingName': _taskData?.buildingName,
'floorName': _taskData?.floorName,
'departmentName': _taskData?.departmentName,
'roomName': _taskData?.roomName,
'locationDescription': _taskData?.locationDescription,
'taskNo': 'OT-${_taskData?.id}',
'title': _taskData?.oneTimeSetting?.taskTypeName,
};
}
Future<void> _performComplete() async {
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => const AppLazyLoading(),
);
try {
final instructionsPayload = _taskData?.instructions?.map((instruction) {
return {
'id': instruction.id,
'instructionTextId': instruction.instructionTextId,
'instructionText': instruction.instructionText,
'isRequired': instruction.isRequired,
'isDone': instruction.isDone,
'comment': instruction.comment,
};
}).toList() ?? [];
final result = await CleanerProvider().completeOneTimeTask(
id: _taskData!.id!,
instructions: instructionsPayload,
imageRoom: _taskData?.imageRoom,
);
if (mounted) Navigator.pop(context); // Close loading dialog
if (result['success']) {
if (mounted) {
// Navigate to dashboard
Navigator.of(context).pushNamedAndRemoveUntil(
LandPage.routeName,
(routes) => false,
);
}
} else {
if (mounted) {
_showErrorDialog(context, result['message'] ?? 'Failed to complete task');
}
}
} catch (e) {
if (mounted) Navigator.pop(context);
if (mounted) {
_showErrorDialog(context, 'Failed to complete task. Please try again.');
}
}
}
void _showExitDialog() {
showDialog(
context: context,
builder: (context) => ConfirmDialog(
message: 'Do you want to exit without completing?',
title: 'Confirm',
onTap: () {
Navigator.pop(context); // Close dialog
Navigator.pop(context); // Exit screen
},
),
);
}
void _showErrorDialog(BuildContext context, String message) {
showDialog(
context: context,
builder: (context) => ConfirmDialog(
message: message,
title: 'Error',
onTap: () => Navigator.pop(context),
),
);
}
Color _getStatusColor() {
if (_taskData?.statusValue == 2) {
return Colors.green;
}
return AppColor.primary10;
}
String _formatDate(String dateStr) {
try {
final date = DateTime.parse(dateStr);
return '${date.day}/${date.month}/${date.year}';
} catch (e) {
return dateStr;
}
}
String _formatDuration(Duration duration) {
final hours = duration.inHours;
final minutes = duration.inMinutes.remainder(60);
final seconds = duration.inSeconds.remainder(60);
return '${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}';
}
}