Files
stadtbote-app/lib/content_views/task_list_page.dart
T

694 lines
21 KiB
Dart

import 'dart:convert';
import 'package:dart_mq/dart_mq.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:intl/intl.dart';
import 'package:isar/isar.dart';
import 'package:stadtbote/content_views/utils/content_view_widget.dart';
import 'package:stadtbote/messages/pop_content_view_message.dart';
import 'package:stadtbote/model/job.dart';
import 'package:stadtbote/model/job_tour.dart';
import 'package:stadtbote/model/pictures.dart';
import 'package:stadtbote/popups/tasks_popups/arrived_popup.dart';
import 'package:stadtbote/popups/tasks_popups/commision_number_popup.dart';
import 'package:stadtbote/popups/tasks_popups/pickup_delivery_status_popup.dart';
import 'package:stadtbote/popups/tasks_popups/receipt_giver_popup.dart';
import 'package:stadtbote/popups/tasks_popups/scan_barcodes_popup.dart';
import 'package:stadtbote/popups/tasks_popups/signature_popup.dart';
import 'package:stadtbote/popups/tasks_popups/station_notice_popup.dart';
import 'package:stadtbote/popups/tasks_popups/take_photos_popup.dart';
import 'package:stadtbote/protocoll_messages/accept_job_message.dart';
import 'package:stadtbote/protocoll_messages/fin_task_message.dart';
import 'package:stadtbote/protocoll_messages/fin_tour_message.dart';
import 'package:stadtbote/protocoll_messages/pic_message.dart';
import 'package:stadtbote/tools/constants.dart';
import 'package:stadtbote/tools/messages/publish_payload_message.dart';
import '../main.dart';
import '../model/task_info.dart';
import '../tools/util.dart';
class TaskListPage extends ContentViewWidget {
const TaskListPage({super.key, required this.job, required this.tour, this.readOnly = false});
final Job job;
final JobTour tour;
final bool readOnly;
@override
State<TaskListPage> createState() => _TaskListPageState();
@override
Future<Widget> getHeaderWidget(BuildContext context) async {
await Future.delayed(const Duration(microseconds: 0));
return _HeaderWidget();
}
}
class _TaskListPageState extends State<TaskListPage> with ProducerMixin {
late bool _initCompleted = false;
late List<TaskInfo> _taskList;
late String _commisionNumber;
@override
void initState() {
Future.delayed(const Duration(seconds: 0), () async {
_taskList = await _fetchTaskList();
_commisionNumber = widget.tour.com ?? "";
_initCompleted = true;
setState(() {});
});
super.initState();
}
@override
Widget build(BuildContext context) {
if (!_initCompleted) {
return Util.getWaitWidget(context);
}
var steps = _initSteps();
return Padding(
padding: const EdgeInsets.all(8.0),
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
padding: const EdgeInsets.all(0),
child: AbsorbPointer(
absorbing: widget.readOnly,
child: Column(
children: [
Visibility(
visible: _commisionNumber.isNotEmpty,
child: Row(
children: [
Text('Komm-#: $_commisionNumber', style: const TextStyle(fontSize: 20), textAlign: TextAlign.left),
],
),
),
const SizedBox(height: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: steps,
),
],
),
),
),
);
}
List<Widget> _initSteps() {
List<Widget> steps = [];
for (int i=0; i<_taskList.length; i++) {
var taskInfo = _taskList[i];
var textColor = isTaskActive(i) ? Colors.black : const Color(0x33000000);
var optional = taskInfo.task.opt != null && taskInfo.task.opt == true;
steps.add(
ListTile(
minVerticalPadding: 0,
title: Container(
padding: const EdgeInsets.all(0),
height: 60,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Stack(
children: [
Positioned(
left: 16.5,
top: i == 0 ? 40 : 40,
child: Container(
height: i == widget.tour.tasks!.length - 1 ? 0 : 20,
width: 6,
color: isTaskActive(i) ? Constants.colorPrimary : Constants.colorPrimaryOpaque,
),
),
Column(
children: [
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: isTaskActive(i) ? Constants.colorPrimary : Constants.colorPrimaryOpaque,
),
child: const Icon(Icons.check, size: 30.0, color: Colors.white),
),
],
),
],
),
const SizedBox(width: 20),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
taskInfo.task.topic + (optional ? "" : " *"),
textAlign: TextAlign.left,
style: TextStyle(
color: textColor,
fontSize: 15.0,
),
),
const SizedBox(height: 5),
SizedBox(
width: MediaQuery.of(context).size.width * 0.7,
child: Text(
taskInfo.additionalText ?? '',
textAlign: TextAlign.left,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: textColor,
fontSize: 12.0,
),
),
),
],
),
],
),
),
onTap: () {
if (isTaskActive(i)) {
_processTask(taskInfo);
}
},
),
);
}
return steps;
}
Future<List<TaskInfo>> _fetchTaskList() async {
List<TaskInfo> taskList = [];
for(var task in widget.tour.tasks!) {
var taskInfo = await MyApp.isarInstance.taskInfos.filter().sIdEqualTo(task.sId).findFirst();
if (taskInfo == null) {
taskInfo = TaskInfo();
taskInfo.id = await TaskInfo.getNextId();
taskInfo.sId = task.sId;
taskInfo.answer = null;
taskInfo.additionalText = null;
taskInfo.completed = false;
}
taskInfo.task = task;
taskList.add(taskInfo);
}
return taskList;
}
void _processTask(TaskInfo taskInfo) {
switch(taskInfo.task.type) {
case 2:
_processSignature(taskInfo);
break;
case 3:
_processTaskArrivedOrFinished(taskInfo);
break;
case 4:
_processTakePhotos(taskInfo);
break;
case 5:
_processScanBarcodes(taskInfo);
break;
case 7:
_processReceiptGiver(taskInfo);
break;
case 8:
_processStationNotice(taskInfo);
break;
case 9:
_processCommissionNumber(taskInfo);
break;
case 11:
_processPickupDeliveryStatusTask(taskInfo);
break;
}
}
_processTaskArrivedOrFinished(TaskInfo taskInfo) {
Util.logInfo('Task ${taskInfo.task.sId} called');
showTaskArrivedPopup(context, taskInfo, () {
DateFormat dateFormat = DateFormat("dd.MM.yyyy HH:mm");
var answer = _createAnswerHeader(taskInfo);
taskInfo.answer = jsonEncode(answer.toJson());
taskInfo.additionalText = dateFormat.format(DateTime.now());
taskInfo.completed = true;
_sendArrivedMessage(taskInfo);
setState(() {});
_finishTaskProcessing(taskInfo);
});
}
_processCommissionNumber(TaskInfo taskInfo) {
Util.logInfo('Task ${taskInfo.task.sId} called');
showComissionNumberPopup(context, taskInfo, (comissionNumber) {
var answer = _createAnswerHeader(taskInfo);
answer.val = comissionNumber;
taskInfo.answer = jsonEncode(answer.toJson());
taskInfo.additionalText = comissionNumber;
taskInfo.completed = true;
_commisionNumber = comissionNumber;
setState(() {});
_finishTaskProcessing(taskInfo);
});
}
_processScanBarcodes(TaskInfo taskInfo) {
Util.logInfo('Task ${taskInfo.task.sId} called');
showScanBarcodesPopup(context, taskInfo, (List<String> barcodes) {
var answer = _createAnswerHeader(taskInfo);
answer.codes = [];
for(var barcode in barcodes) {
answer.codes!.add(barcode);
}
taskInfo.answer = jsonEncode(answer.toJson());
taskInfo.additionalText = "Barcodes gescannt";
taskInfo.completed = true;
setState(() {});
_finishTaskProcessing(taskInfo);
});
}
_processTakePhotos(TaskInfo taskInfo) {
Util.logInfo('Task ${taskInfo.task.sId} called');
showTakePhotosPopup(context, taskInfo, (List<Uint8List>? photos) async {
if (photos != null) {
await MyApp.isarInstance.writeTxn(() async {
await MyApp.isarInstance.pictures.filter().tourIdEqualTo(widget.tour.id).deleteAll();
for (int i = 0; i < photos.length; i++) {
var pictureData = photos[i];
var picture = Picture();
picture.number = i;
picture.tourId = widget.tour.id;
picture.data = base64Encode(pictureData);
await MyApp.isarInstance.pictures.put(picture);
}
});
taskInfo.additionalText = "Fotos aufgenommen";
taskInfo.completed = true;
setState(() {});
_finishTaskProcessing(taskInfo);
}
});
}
void _processStationNotice(TaskInfo taskInfo) {
Util.logInfo('Task ${taskInfo.task.sId} called');
showStationNoticePopup(context, taskInfo, (notice) {
var answer = _createAnswerHeader(taskInfo);
answer.val = notice;
taskInfo.answer = jsonEncode(answer.toJson());
taskInfo.additionalText = notice;
taskInfo.completed = true;
_finishTaskProcessing(taskInfo);
setState(() {});
});
}
void _processReceiptGiver(TaskInfo taskInfo) {
Util.logInfo('Task ${taskInfo.task.sId} called');
showReceiptGiverPopup(context, taskInfo, (receiptGiver, selectedTourEntry) {
var answer = _createAnswerHeader(taskInfo);
answer.code = selectedTourEntry.code;
answer.val = receiptGiver;
taskInfo.answer = jsonEncode(answer.toJson());
taskInfo.additionalText = receiptGiver;
taskInfo.completed = true;
setState(() {});
_finishTaskProcessing(taskInfo);
});
}
void _processPickupDeliveryStatusTask(TaskInfo taskInfo) {
Util.logInfo('Task ${taskInfo.task.sId} called');
Future.delayed(const Duration(seconds: 1), () async {
showPickupDeliveryStatusPopup(context, taskInfo, (tourEntry, remark) {
var answer = _createAnswerHeader(taskInfo);
answer.code = tourEntry!.code;
answer.val = remark;
taskInfo.answer = jsonEncode(answer.toJson());
taskInfo.additionalText = tourEntry.text;
taskInfo.completed = true;
_finishTaskProcessing(taskInfo);
setState(() {});
});
});
}
void _processSignature(TaskInfo taskInfo) {
Util.logInfo('Task ${taskInfo.task.sId} called');
MyApp.suppressAcceptDialog = true;
SystemChrome.setPreferredOrientations([DeviceOrientation.landscapeLeft]);
Future.delayed(const Duration(seconds: 1), () async {
showSignaturePopup(context, taskInfo, (String? signature) async {
MyApp.suppressAcceptDialog = false;
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
if (signature != null) {
var answer = _createAnswerHeader(taskInfo);
answer.sign = signature;
taskInfo.answer = jsonEncode(answer.toJson());
taskInfo.additionalText = "Unterschrift erfasst";
taskInfo.completed = true;
_sendFinTourMessage();
_finishTaskProcessing(taskInfo);
// TODO MESSAGING
sendMessage(
routingKey: Constants.messageTopicPopContentView,
payload: PopContentViewMessage(count: 2),
);
//await MessageService().publish<PopContentViewMessage>(Constants.messageTopicPopContentView, arguments: PopContentViewMessage(count: 2));
}
});
});
}
_finishTaskProcessing(TaskInfo taskInfo) async {
Util.logInfo('Task ${taskInfo.task.sId} finished');
await MyApp.isarInstance.writeTxn(() async {
await MyApp.isarInstance.taskInfos.put(taskInfo);
});
}
bool isTaskActive(int index) {
if (!widget.readOnly) {
var currentTaskInfo = _taskList[index];
if (currentTaskInfo.completed && !currentTaskInfo.task.reenter!) return false;
for (var taskInfo in _taskList)
{
if (taskInfo == currentTaskInfo) break;
if (!taskInfo.task.opt! && !taskInfo.completed) return false;
}
if (currentTaskInfo.task.reenter!) return true;
return true;
}
return false;
}
_sendArrivedMessage(TaskInfo taskInfo) async {
var message = FinTask();
message.trId = widget.tour.id;
message.task = Task();
message.task.sId = taskInfo.task.sId;
message.task.time = DateTime.now().toIso8601String();
var position = MyApp.currentPosition;
if (position != null) {
message.task.pos = FinTaskPos();
message.task.pos!.lat = position.latitude;
message.task.pos!.long = position.longitude;
} else {
message.task.pos = null;
}
// TODO MESSAGING
sendMessage(
routingKey: Constants.messageTopicPublishPayload,
payload: PublishPayloadMessage(topic: "fin_task", message: message.toJson(), acknowledgeRequired: true),
);
//await MessageService().publish<PublishPayloadMessage>(Constants.messageTopicPublishPayload, arguments: PublishPayloadMessage(topic: "fin_task", message: message.toJson(), qos: MqttQos.exactlyOnce));
}
FinTourTask _createAnswerHeader(TaskInfo taskInfo) {
var position = MyApp.currentPosition;
var finTourTask = FinTourTask();
finTourTask.sId = taskInfo.task.sId;
finTourTask.time = DateTime.now().toIso8601String();
if (position != null) {
finTourTask.pos = FinTaskPos();
finTourTask.pos!.lat = position.latitude;
finTourTask.pos!.long = position.longitude;
finTourTask.pos!.acc = position.accuracy.toInt();
} else {
finTourTask.pos = null;
}
return finTourTask;
}
_sendFinTourMessage() async {
var finTourMessage = FinTour();
finTourMessage.trId = widget.tour.id;
finTourMessage.com = _commisionNumber;
var position = MyApp.currentPosition;
if (position != null) {
finTourMessage.track = Track();
finTourMessage.track!.time = DateTime.now().toIso8601String();
finTourMessage.track!.pos = FinTaskPos();
finTourMessage.track!.pos!.lat = position.latitude;
finTourMessage.track!.pos!.long = position.longitude;
}
finTourMessage.tasks = [];
for(var taskInfo in _taskList) {
if (taskInfo.answer != null) {
var finTourTask = FinTourTask.fromJson(jsonDecode(taskInfo.answer!));
finTourMessage.tasks.add(finTourTask);
}
}
// TODO MESSAGING
sendMessage(
routingKey: Constants.messageTopicPublishPayload,
payload: PublishPayloadMessage(topic: "fin_tour", message: finTourMessage.toJson(), acknowledgeRequired: true),
);
//await MessageService().publish<PublishPayloadMessage>(Constants.messageTopicPublishPayload, arguments: PublishPayloadMessage(topic: "fin_tour", message: finTourMessage.toJson(), qos: MqttQos.exactlyOnce));
widget.tour.state = Constants.stateFinishedNotConfirmed;
await MyApp.isarInstance.writeTxn(() async {
await MyApp.isarInstance.jobs.put(widget.job);
});
_sendImages();
await _checkJobFinished();
}
_sendImages() {
Future.delayed(const Duration(seconds: 1), () async {
var pictures = await MyApp.isarInstance.pictures.where().filter().tourIdEqualTo(widget.tour.id).findAll();
if (pictures.isNotEmpty) {
for (var picture in pictures) {
var message = PicData();
message.trId = picture.tourId;
message.num = picture.number;
message.time = DateTime.now().millisecondsSinceEpoch;
message.data = picture.data;
// TODO MESSAGING
sendMessage(
routingKey: Constants.messageTopicPublishPayload,
payload: PublishPayloadMessage(topic: "pic", message: message.toJson(), acknowledgeRequired: true),
);
//await MessageService().publish<PublishPayloadMessage>(Constants.messageTopicPublishPayload, arguments: PublishPayloadMessage(topic: "pic", message: message.toJson(), qos: MqttQos.exactlyOnce));
await MyApp.isarInstance.writeTxn(() async {
await MyApp.isarInstance.pictures.delete(picture.id);
});
}
}
});
}
Future<void> _checkJobFinished() async {
var jobFinished = true;
for (var tour in widget.job.tours) {
if (tour.state == 0) {
jobFinished = false;
break;
}
}
if (jobFinished) {
widget.job.state = 2;
await MyApp.isarInstance.writeTxn(() async {
await MyApp.isarInstance.jobs.put(widget.job);
});
}
}
}
class _HeaderWidget extends StatefulWidget {
@override
State<_HeaderWidget> createState() => _HeaderWidgetState();
}
class _HeaderWidgetState extends State<_HeaderWidget> with ProducerMixin {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
var cellWidth = (MediaQuery.of(context).size.width - 60 - 60 - 25) / 2;
return Padding(
padding: const EdgeInsets.all(8),
child: Stack(
alignment: Alignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Visibility(
visible: true,
child: Container(
width: 60,
height: 60,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
),
child: FittedBox(
child: GestureDetector(
onTap: () async {
// TODO MESSAGING
sendMessage(
routingKey: Constants.messageTopicPopContentView,
payload: PopContentViewMessage(count: 1),
);
//await MessageService().publish<PopContentViewMessage>(Constants.messageTopicPopContentView, arguments: PopContentViewMessage(count: 1));
},
child: const CircleAvatar(
radius: 10,
backgroundColor: Constants.colorPrimary,
child: Padding(padding: EdgeInsets.all(1.0), child: Icon(Icons.arrow_back, size: 20.0, color: Colors.white)),
),
),
),
),
),
Visibility(
visible: false,
child: Container(
width: 60,
height: 60,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Colors.red,
),
child: FittedBox(
child: GestureDetector(
onTap: () async {
// TODO MESSAGING
sendMessage(
routingKey: Constants.messageTopicPopContentView,
payload: PopContentViewMessage(count: 1),
);
//await MessageService().publish<PopContentViewMessage>(Constants.messageTopicPopContentView, arguments: PopContentViewMessage(count: 1));
},
child: const CircleAvatar(
backgroundColor: Constants.colorPrimary,
radius: 10,
child: Padding(padding: EdgeInsets.all(1.0), child: Icon(Icons.info_outline, size: 20.0, color: Colors.white))),
),
),
),
),
],
),
SizedBox(
height: 100,
width: cellWidth * 2,
child: const Center(
child: Text(
textAlign: TextAlign.center,
"Liste der Aufgaben",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.white),
),
),
),
],
),
);
}
}