diff --git a/.envExample b/.envExample index 42a180c..d3f4517 100644 --- a/.envExample +++ b/.envExample @@ -3,4 +3,5 @@ APPWRITE_PROJECT_NAME= APPWRITE_PUBLIC_ENDPOINT= PTV_GEOLINK_API_KEY= APPWRITE_DATABASE_ID= -APPWRITE_COLLECTION_ID= \ No newline at end of file +APPWRITE_COLLECTION_ID= +TANKSTOPS_BASE_URL=https://api.e-control.at/sprit/1.0/search/gas-stations/by-address?latitude=&longitude=&fuelType=DIE&includeClosed=false \ No newline at end of file diff --git a/lib/data/repository/appwrite_repository.dart b/lib/data/repository/appwrite_repository.dart index de24e15..f285f45 100644 --- a/lib/data/repository/appwrite_repository.dart +++ b/lib/data/repository/appwrite_repository.dart @@ -112,4 +112,22 @@ class AppwriteRepository { ); return response; } + + Future updateTankStop(String documentId, Map map) async { + final response = await _databases.updateDocument( + databaseId: dotenv.get('APPWRITE_DATABASE_ID'), + collectionId: dotenv.get('APPWRITE_COLLECTION_ID'), + documentId: documentId, + data: map, + ); + return response; + } + + Future deleteTankStop(String documentId) async { + return await _databases.deleteDocument( + databaseId: dotenv.get('APPWRITE_DATABASE_ID'), + collectionId: dotenv.get('APPWRITE_COLLECTION_ID'), + documentId: documentId, + ); + } } diff --git a/lib/pages/login/login_controller.dart b/lib/pages/login/login_controller.dart index ed53346..2fd2fcb 100644 --- a/lib/pages/login/login_controller.dart +++ b/lib/pages/login/login_controller.dart @@ -227,6 +227,6 @@ class LoginController extends GetxController { } void goToTankPage() { - Get.offAndToNamed(TankPage.namedRoute); + Get.offAndToNamed(TankPage.namedRoute, arguments: {'from': 'Input'}); } } diff --git a/lib/pages/tank/tank_controller.dart b/lib/pages/tank/tank_controller.dart index 139a9b6..ea7bf36 100644 --- a/lib/pages/tank/tank_controller.dart +++ b/lib/pages/tank/tank_controller.dart @@ -40,6 +40,9 @@ class TankController extends GetxController { final FocusNode firstFocusNode = FocusNode(); // Deklariere den FocusNode + final isEditMode = false.obs; + String documentId = ''; + // Methode für das ausgewählte Datum Future selectDateTime(BuildContext context) async { // 1. Datum auswählen @@ -150,8 +153,38 @@ class TankController extends GetxController { @override void onInit() { super.onInit(); - // Rufe den Standort direkt beim Initialisieren des Controllers ab, falls gewünscht. - fetchCurrentLocation(); + handleInputOrUpdate(); + } + + void handleInputOrUpdate() { + var mapFromArguments = Get.arguments as Map?; + if (mapFromArguments != null && mapFromArguments['from'] != null) { + isEditMode.value = mapFromArguments['from'] == 'Input' ? true : false; + } + //bei true ein insert bei false ein update + if (isEditMode.value) { + fetchCurrentLocation(); + } else { + // Im Editiermodus, keine Standortabfrage durchführen + print('Im Editiermodus, keine Standortabfrage durchführen'); + // Den Fokus auf das erste Eingabefeld setzen + FocusScope.of(Get.context!).requestFocus(firstFocusNode); + print('TankController is in Edit Mode'); + if (mapFromArguments != null && mapFromArguments['data'] != null) { + var dataMap = mapFromArguments['data'] as Map; + print('Data Map for Edit Mode: $dataMap'); + // Setze die documentId für spätere Updates + documentId = dataMap['\$id'] ?? ''; + // Setze die Werte in die TextEditingController + dateController.text = dataMap['date'] ?? ''; + kilometerStandEdittingController.text = dataMap['odometer'] ?? ''; + mengeController.text = dataMap['liters'] ?? ''; + pricePerLiterController.text = dataMap['pricePerLiter'] ?? ''; + ortController.text = dataMap['location'] ?? ''; + // Berechne den Gesamtpreis + updateSumPrice(); + } + } } @override @@ -213,33 +246,64 @@ class TankController extends GetxController { print('Formular ist gültig'); formKeyTank.currentState!.save(); try { - //creatiung a tank stop - await _authRepository.createTankStop({ - 'userId': _dataBox.read('userId'), - 'date': f.format(DateTime.parse(dateController.text)), - 'odometer': kilometerStandEdittingController.text, - 'liters': mengeController.text, - 'pricePerLiter': pricePerLiterController.text, - 'location': ortController.text, - }).then((models.Document document) { - print('Tankstopp erfolgreich gespeichert: ${document.data}'); - messageToUser = 'Tankstopp erfolgreich gespeichert!'; - isErrorBySaving = false; - }) - // Handle specific Appwrite exceptions - .catchError((error) { - isErrorBySaving = true; - if (error is AppwriteException) { - // Handle specific Appwrite exceptions - messageToUser = 'Appwrite Fehler: ${error.message}'; - print('Appwrite Fehler: ${error.message}'); - } else { - // Handle other types of errors - messageToUser = 'Allgemeiner Fehler: $error'; - print('Allgemeiner Fehler: $error'); - } - print('Fehler bei der speicherung: $error'); - }); + //false update a tank stop + if (isEditMode.value == false) { + await _authRepository.updateTankStop(documentId, { + 'userId': _dataBox.read('userId'), + 'date': f.format(DateTime.parse(dateController.text)), + 'odometer': kilometerStandEdittingController.text, + 'liters': mengeController.text, + 'pricePerLiter': pricePerLiterController.text, + 'location': ortController.text, + }).then((models.Document document) { + print('Tankstopp erfolgreich aktualisiert: ${document.data}'); + messageToUser = 'Tankstopp erfolgreich aktualisiert!'; + isErrorBySaving = false; + }) + // Handle specific Appwrite exceptions + .catchError((error) { + isErrorBySaving = true; + if (error is AppwriteException) { + // Handle specific Appwrite exceptions + messageToUser = 'Appwrite Fehler: ${error.message}'; + print('Appwrite Fehler: ${error.message}'); + } else { + // Handle other types of errors + messageToUser = 'Allgemeiner Fehler: $error'; + print('Allgemeiner Fehler: $error'); + } + print('Fehler bei der speicherung: $error'); + }); + } else { + //true creating a tank stop + await _authRepository.createTankStop({ + 'userId': _dataBox.read('userId'), + 'date': f.format(DateTime.parse(dateController.text)), + 'odometer': kilometerStandEdittingController.text, + 'liters': mengeController.text, + 'pricePerLiter': pricePerLiterController.text, + 'location': ortController.text, + }).then((models.Document document) { + print('Tankstopp erfolgreich gespeichert: ${document.data}'); + messageToUser = 'Tankstopp erfolgreich gespeichert!'; + isErrorBySaving = false; + }) + // Handle specific Appwrite exceptions + .catchError((error) { + isErrorBySaving = true; + if (error is AppwriteException) { + // Handle specific Appwrite exceptions + messageToUser = 'Appwrite Fehler: ${error.message}'; + print('Appwrite Fehler: ${error.message}'); + } else { + // Handle other types of errors + messageToUser = 'Allgemeiner Fehler: $error'; + print('Allgemeiner Fehler: $error'); + } + print('Fehler bei der speicherung: $error'); + }); + // bei true ein insert + } } // Handle any other exceptions that might occur catch (e) { @@ -259,6 +323,7 @@ class TankController extends GetxController { snackPosition: SnackPosition.BOTTOM, duration: const Duration(seconds: 4), ); + goToTankStopsView(); } } diff --git a/lib/pages/tank/tank_view.dart b/lib/pages/tank/tank_view.dart index e5cff72..629e83d 100644 --- a/lib/pages/tank/tank_view.dart +++ b/lib/pages/tank/tank_view.dart @@ -8,6 +8,7 @@ class TankPage extends GetView { @override Widget build(BuildContext context) { + var tankCtrl = controller; return SafeArea( child: Scaffold( appBar: AppBar( @@ -126,23 +127,30 @@ class TankPage extends GetView { SizedBox( width: 350, height: 50, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: Colors.blue.withValues( - alpha: 0.9, - ), // Button-Farbe - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular( - 10, - ), // Abgerundete Ecken + child: Obx( + () => ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blue.withValues( + alpha: 0.9, + ), // Button-Farbe + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + 10, + ), // Abgerundete Ecken + ), ), - ), - onPressed: () { - controller.saveTankstopp(); - }, - child: Text( - 'Tankstop erfassen', - style: TextStyle(color: Colors.white, fontSize: 20), + onPressed: () { + controller.saveTankstopp(); + }, + child: tankCtrl.isEditMode.value == true ? Text( + 'Tankstop erfassen', + style: + TextStyle(color: Colors.white, fontSize: 20), + ): Text( + 'Tankstop updaten', + style: TextStyle( + color: Colors.white, fontSize: 20), + ), ), ), ), diff --git a/lib/pages/tanklist/tanklist_controller.dart b/lib/pages/tanklist/tanklist_controller.dart index 4ba86ae..838e66b 100644 --- a/lib/pages/tanklist/tanklist_controller.dart +++ b/lib/pages/tanklist/tanklist_controller.dart @@ -36,7 +36,7 @@ class TanklistController extends GetxController { @override void onClose() {} - void getTankList() async { + Future getTankList() async { isloadingList(true); bool isErrorByLoading = false; String message = ''; @@ -45,8 +45,14 @@ class TanklistController extends GetxController { .listTankStops(szRxUserId.value) .then((tankListData) { if (tankListData.documents.isEmpty) { - isErrorByLoading = true; + isErrorByLoading = false; message = 'Leere Liste keine Daten vorhanden'; + isloadingList(false); + rxTankListAlles.clear(); + rxTankListActualYear.clear(); + szRxSummeYearLiter('0.0'); + szRxSummePrice('0.0'); + update(); return; } rxTankListAlles.clear(); @@ -64,6 +70,7 @@ class TanklistController extends GetxController { rxTankListActualYear.value = rxTankListAlles .where((tank) => tank.date.startsWith(szRxYear.value)) .toList(); + // rxTankListActualYear.value = rxTankListAlles; //Summe Liter aktuelles Jahr********* szRxSummeYearLiter( rxTankListActualYear.fold(0.0, (previousValue, element) { @@ -72,10 +79,11 @@ class TanklistController extends GetxController { //Summe Preis aktuelles Jahr********* szRxSummePrice( rxTankListActualYear.fold(0.0, (previousValue, element) { - return previousValue + (double.tryParse(element.szSummePreis!) ?? 0.0); + return previousValue + + (double.tryParse(element.szSummePreis!) ?? 0.0); }).toStringAsFixed(2)); - message = 'Liste wurde erfolgreich geladen'; + isErrorByLoading = false; }).catchError((error) { isErrorByLoading = true; if (error is AppwriteException) { @@ -94,21 +102,20 @@ class TanklistController extends GetxController { } } String title = isErrorByLoading ? 'Fehler' : 'Erfolg'; - Get.snackbar( - title, - message, - backgroundColor: isErrorByLoading ? Colors.red : Colors.green, - snackPosition: SnackPosition.BOTTOM, - duration: const Duration(seconds: 4), - ); + print('$title: $message'); update(); } - void goToUpdatePage() { + void goToUpdatePage(AppWriteTankModel item) { + var map = item.toMap(); + print('Go to Update Page with data: $map'); // Go to Update Page + Get.offAndToNamed(TankPage.namedRoute, + arguments: {'from': 'Update', 'data': map}); } + void goToInputPage() { - Get.offAndToNamed(TankPage.namedRoute); + Get.offAndToNamed(TankPage.namedRoute, arguments: {'from': 'Input'}); } void logoutSessionAndGoToLoginPage() async { @@ -127,4 +134,30 @@ class TanklistController extends GetxController { void goToChartView() { Get.offAndToNamed(GraphPage.namedRoute); } + + void deleteTankStop(String documentId) async { + bool isErrorByLoading = false; + String message = ''; + try { + await _authRepository.deleteTankStop(documentId).then((response) async { + await getTankList(); + message = 'Tankstopp wurde erfolgreich gelöscht'; + }).catchError((error) { + isErrorByLoading = true; + if (error is AppwriteException) { + message = error.message!; + } else { + message = 'Uuups da ist was schief gelaufen'; + } + }); + } catch (e) { + isErrorByLoading = true; + message = 'Fehler beim Löschen des Tankstopps'; + print('Error deleting tank stop: $e'); + } + String title = isErrorByLoading ? 'Fehler' : 'Erfolg'; + print('$title: $message'); + } + + void goToGasView() {} } diff --git a/lib/pages/tanklist/tanklist_view.dart b/lib/pages/tanklist/tanklist_view.dart index 99b2130..bedffbb 100644 --- a/lib/pages/tanklist/tanklist_view.dart +++ b/lib/pages/tanklist/tanklist_view.dart @@ -22,6 +22,13 @@ class TanklistPage extends GetView { actions: [ IconButton( icon: Icon(Icons.add_chart, color: Colors.grey.shade300), + onPressed: () async { + // Handle go to Chart View + tankListCtrl.goToGasView(); + }, + ), + IconButton( + icon: Icon(Icons.local_gas_station_sharp, color: Colors.grey.shade300), onPressed: () async { // Handle go to Chart View tankListCtrl.goToChartView(); @@ -57,20 +64,30 @@ class TanklistPage extends GetView { SizedBox( child: Column( children: [ - Text(tankListCtrl.szRxYear.value, style: TextStyle(fontSize: 25, color: Colors.orange)), + Text(tankListCtrl.szRxYear.value, + style: TextStyle( + fontSize: 25, color: Colors.orange)), Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, + mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Column( children: [ - Text('Jahresverbrauch', style: TextStyle(fontSize: 14)), - Text( tankListCtrl.szRxSummeYearLiter.value, style: TextStyle(fontSize: 20, color: Colors.orange)), + Text('Jahresverbrauch', + style: TextStyle(fontSize: 14)), + Text(tankListCtrl.szRxSummeYearLiter.value, + style: TextStyle( + fontSize: 20, + color: Colors.orange)), ], ), Column( children: [ - Text('Jahressumme', style: TextStyle(fontSize: 14)), - Text(tankListCtrl.szRxSummePrice.value, style: TextStyle(fontSize: 20, color: Colors.orange)), + Text('Jahressumme', + style: TextStyle(fontSize: 14)), + Text(tankListCtrl.szRxSummePrice.value, + style: TextStyle( + fontSize: 20, + color: Colors.orange)), ], ), ], @@ -78,9 +95,12 @@ class TanklistPage extends GetView { ], ), ), - Divider(color: Colors.grey.shade200, height: 0.0,), + Divider( + color: Colors.grey.shade200, + height: 0.0, + ), Expanded( - child: ListView.builder( + child: tankListCtrl.rxTankListActualYear.isNotEmpty ? ListView.builder( padding: EdgeInsets.only(top: 8, bottom: 8), physics: const BouncingScrollPhysics(), itemBuilder: ((BuildContext context, int index) { @@ -105,14 +125,27 @@ class TanklistPage extends GetView { ), ], ), - child: MyListTileItem(listItem: item), + child: MyListTileItem( + listItem: item, + onPressedEdit: () { + tankListCtrl.goToUpdatePage(item); + }, + onPressedDelete: () { + tankListCtrl.deleteTankStop(item.documentId); + }, + ), ), SizedBox(height: 15), ], ); }), itemCount: tankListCtrl.rxTankListActualYear.length, - ), + ):Expanded(child: Center( + child: Text('Keine Einträge für das aktuelle Jahr vorhanden', + style: TextStyle( + fontSize: 18, color: Colors.grey.shade600)), + ), + ) ), ], ), diff --git a/lib/pages/tanklist/widgets/my_list_tile_item.dart b/lib/pages/tanklist/widgets/my_list_tile_item.dart index a9b0e62..52b6b17 100644 --- a/lib/pages/tanklist/widgets/my_list_tile_item.dart +++ b/lib/pages/tanklist/widgets/my_list_tile_item.dart @@ -2,11 +2,13 @@ import 'package:flutter/material.dart'; import '../../../data/models/tank_model.dart'; - class MyListTileItem extends StatelessWidget { - const MyListTileItem({super.key, required this.listItem}); + const MyListTileItem( + {super.key, required this.listItem, required this.onPressedEdit, required this.onPressedDelete}); final AppWriteTankModel listItem; + final void Function()? onPressedEdit; + final void Function()? onPressedDelete; @override Widget build(BuildContext context) { @@ -72,7 +74,8 @@ class MyListTileItem extends StatelessWidget { children: [ Icon(Icons.price_change), SizedBox(width: 10), - Text('${listItem.szSummePreis} €', style: TextStyle(color: textColor, fontSize: 25)), + Text('${listItem.szSummePreis} €', + style: TextStyle(color: textColor, fontSize: 25)), ], ), Divider(thickness: 1, color: Colors.black), @@ -89,6 +92,38 @@ class MyListTileItem extends StatelessWidget { ), ], ), + Divider(thickness: 1, color: Colors.black), + Wrap( + spacing: 10, + children: [ + ElevatedButton.icon( + onPressed: onPressedEdit, + label: Text( + 'Edit', + style: TextStyle(color: Colors.grey.shade200), + ), + icon: const Icon(Icons.edit), + style: ElevatedButton.styleFrom( + backgroundColor: const Color.fromARGB(255, 3, 71, 128), + foregroundColor: Colors.grey.shade200, + shadowColor: Colors.white, + ), + ), + ElevatedButton.icon( + onPressed: onPressedDelete, + label: Text( + 'Delete', + style: TextStyle(color: Colors.grey.shade200), + ), + icon: const Icon(Icons.delete), + style: ElevatedButton.styleFrom( + backgroundColor: const Color.fromARGB(255, 128, 3, 45), + foregroundColor: Colors.grey.shade200, + shadowColor: Colors.white, + ), + ), + ], + ), ], ), );