From d864b9c72a3e4339ed98f837590f145dcdedd3e1 Mon Sep 17 00:00:00 2001 From: Sven Carstensen Date: Thu, 2 Jul 2026 10:15:01 +0200 Subject: [PATCH] Aufgaben-JSON testweise von Web-URL laden, Ankunft-Task als Button, RadioGroup-Umstellung - TEMP-Loader: Aufgaben beim Oeffnen der Task-Seite von https://appcreation.de/download/task.json laden (Fallback: Tour-Daten) - Task-Typ 1 (Ankunft melden) als Bestaetigungs-Button statt Checkbox - Statusauswahl (Typ 11) auf RadioGroup umgestellt (Deprecation behoben) Co-Authored-By: Claude Fable 5 --- lib/content_views/task_list_page.dart | 103 +++++++++++++++++++++++--- lib/tools/message_header.dart | 1 - 2 files changed, 92 insertions(+), 12 deletions(-) diff --git a/lib/content_views/task_list_page.dart b/lib/content_views/task_list_page.dart index 67b6d17..8f42a4b 100644 --- a/lib/content_views/task_list_page.dart +++ b/lib/content_views/task_list_page.dart @@ -2,6 +2,8 @@ import 'dart:convert'; import 'package:dart_mq/dart_mq.dart'; import 'package:flutter/material.dart'; +// TEMP (nur zum Testen, s. _tempTaskJsonUrl): wieder entfernen. +import 'package:http/http.dart' as http; import 'package:flutter/services.dart'; import 'package:image/image.dart' as image_lib; import 'package:intl/intl.dart'; @@ -25,6 +27,44 @@ import '../model/tour_task.dart'; import '../tools/app_localizations.dart'; import '../tools/util.dart'; +// ============================================================================= +// TEMPORÄR — NUR ZUM TESTEN, WIEDER ENTFERNEN +// +// Lädt das Aufgaben-JSON beim Öffnen der Seite von einer Webseite statt die +// Aufgaben der übergebenen Tour zu verwenden. Die URL muss entweder eine +// JSON-Liste von Aufgaben liefern oder ein Objekt mit einer "tasks"-Liste. +// +// Zum Entfernen: diesen Block, den http-Import oben und die mit +// "TEMP" markierte Zeile in _fetchTaskList() löschen. Alternativ die URL auf +// null setzen, dann greift wieder das normale Verhalten (auch bei Ladefehler). +// ============================================================================= +// ignore: unnecessary_nullable_for_final_variable_declarations +const String? _tempTaskJsonUrl = "https://appcreation.de/download/task.json"; + +Future?> _tempLoadTasksFromWeb() async { + final url = _tempTaskJsonUrl; + if (url == null) { + return null; + } + + try { + final response = await http.get(Uri.parse(url)); + if (response.statusCode != 200) { + debugPrint("TEMP: Aufgaben-JSON laden fehlgeschlagen, HTTP ${response.statusCode}"); + return null; + } + + final decoded = jsonDecode(utf8.decode(response.bodyBytes)); + final list = decoded is List ? decoded : (decoded as Map)['tasks'] as List; + + return list.map((e) => TourTask.fromJson(e as Map)).toList(); + } catch (e) { + debugPrint("TEMP: Aufgaben-JSON laden fehlgeschlagen: $e"); + return null; + } +} +// ========================================================== ENDE TEMPORÄR ==== + /// Seiten-Wizard zur Bearbeitung der Aufgaben einer Tour. Pro Aufgabe wird /// genau eine Seite angezeigt; mit „Zurück"/„Weiter" navigiert der Bearbeiter /// zwischen den Aufgaben, die letzte Seite schließt die Tour ab. @@ -98,7 +138,10 @@ class _TaskListPageState extends State with ProducerMixin { Future> _fetchTaskList() async { List taskList = []; - for (var task in widget.tour.tasks!) { + // TEMP (nur zum Testen): Aufgaben von Webseite statt aus der Tour laden. + final tasks = await _tempLoadTasksFromWeb() ?? widget.tour.tasks!; + + for (var task in tasks) { final box = MyApp.objectBoxStore.box(); final query = box.query(TaskInfo_.sId.equals(task.sId)).build(); var taskInfo = query.findFirst(); @@ -274,7 +317,7 @@ class _TaskListPageState extends State with ProducerMixin { switch (type) { case 1: - return _buildConfirmBody(draft, 'confirm_arrival'.tr); + return _buildConfirmButtonBody(draft, 'confirm_arrival'.tr); case 2: return _buildSignatureBody(taskInfo, draft); case 3: @@ -300,7 +343,39 @@ class _TaskListPageState extends State with ProducerMixin { } } - /// Bestätigungs-Aufgabe (Ankunft melden = 1, Checkbox = 6). + /// Bestätigung per Button (Ankunft melden = 1). Antippen bestätigt bzw. + /// nimmt die Bestätigung wieder zurück; der Zustand wird über ein + /// Häkchen-Symbol im Button angezeigt. + Widget _buildConfirmButtonBody(_TaskDraft draft, String label) { + final confirmed = draft.confirmed; + return SizedBox( + height: 52, + child: MaterialButton( + color: Constants.colorPrimary, + elevation: 3, + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(15))), + onPressed: () => setState(() => draft.confirmed = !confirmed), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (confirmed) ...[ + const Icon(Icons.check_circle, color: Colors.white), + const SizedBox(width: 8), + ], + Flexible( + child: Text( + label, + overflow: TextOverflow.ellipsis, + style: const TextStyle(fontSize: 16, color: Colors.white), + ), + ), + ], + ), + ), + ); + } + + /// Bestätigungs-Aufgabe als Checkbox (Typ 6). Widget _buildConfirmBody(_TaskDraft draft, String label) { return CheckboxListTile( value: draft.confirmed, @@ -339,14 +414,20 @@ class _TaskListPageState extends State with ProducerMixin { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - ...options.map((o) => RadioListTile( - value: o.code, - groupValue: draft.selectedCode, - contentPadding: EdgeInsets.zero, - activeColor: Constants.colorPrimary, - title: Text(o.label), - onChanged: (value) => setState(() => draft.selectedCode = value), - )), + RadioGroup( + groupValue: draft.selectedCode, + onChanged: (value) => setState(() => draft.selectedCode = value), + child: Column( + children: options + .map((o) => RadioListTile( + value: o.code, + contentPadding: EdgeInsets.zero, + activeColor: Constants.colorPrimary, + title: Text(o.label), + )) + .toList(), + ), + ), if (requiresRemark) ...[ const SizedBox(height: 8), Util.getDefaultTextField( diff --git a/lib/tools/message_header.dart b/lib/tools/message_header.dart index 55e8f9e..3cf11ba 100644 --- a/lib/tools/message_header.dart +++ b/lib/tools/message_header.dart @@ -16,4 +16,3 @@ class MessageHeader { Map toJson() => _$MessageHeaderToJson(this); } -