add edit and delete

This commit is contained in:
atseirjo 2025-08-26 09:12:36 +02:00
parent 085a7648f3
commit 12b385d7e0
8 changed files with 266 additions and 73 deletions

View File

@ -4,3 +4,4 @@ APPWRITE_PUBLIC_ENDPOINT=<YOUR_API_ENDPOINT/v1>
PTV_GEOLINK_API_KEY=<YOUR_GEOLINK_API_KEY> PTV_GEOLINK_API_KEY=<YOUR_GEOLINK_API_KEY>
APPWRITE_DATABASE_ID=<DatabaseID> APPWRITE_DATABASE_ID=<DatabaseID>
APPWRITE_COLLECTION_ID=<Collection_ID> APPWRITE_COLLECTION_ID=<Collection_ID>
TANKSTOPS_BASE_URL=https://api.e-control.at/sprit/1.0/search/gas-stations/by-address?latitude=<lat>&longitude=<lon>&fuelType=DIE<SUPSuperOrDIEDiesel>&includeClosed=false

View File

@ -112,4 +112,22 @@ class AppwriteRepository {
); );
return response; return response;
} }
Future<models.Document> updateTankStop(String documentId, Map<String, dynamic> 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<dynamic> deleteTankStop(String documentId) async {
return await _databases.deleteDocument(
databaseId: dotenv.get('APPWRITE_DATABASE_ID'),
collectionId: dotenv.get('APPWRITE_COLLECTION_ID'),
documentId: documentId,
);
}
} }

View File

@ -227,6 +227,6 @@ class LoginController extends GetxController {
} }
void goToTankPage() { void goToTankPage() {
Get.offAndToNamed(TankPage.namedRoute); Get.offAndToNamed(TankPage.namedRoute, arguments: {'from': 'Input'});
} }
} }

View File

@ -40,6 +40,9 @@ class TankController extends GetxController {
final FocusNode firstFocusNode = FocusNode(); // Deklariere den FocusNode final FocusNode firstFocusNode = FocusNode(); // Deklariere den FocusNode
final isEditMode = false.obs;
String documentId = '';
// Methode für das ausgewählte Datum // Methode für das ausgewählte Datum
Future<void> selectDateTime(BuildContext context) async { Future<void> selectDateTime(BuildContext context) async {
// 1. Datum auswählen // 1. Datum auswählen
@ -150,8 +153,38 @@ class TankController extends GetxController {
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
// Rufe den Standort direkt beim Initialisieren des Controllers ab, falls gewünscht. handleInputOrUpdate();
fetchCurrentLocation(); }
void handleInputOrUpdate() {
var mapFromArguments = Get.arguments as Map<String, dynamic>?;
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<String, dynamic>;
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 @override
@ -213,33 +246,64 @@ class TankController extends GetxController {
print('Formular ist gültig'); print('Formular ist gültig');
formKeyTank.currentState!.save(); formKeyTank.currentState!.save();
try { try {
//creatiung a tank stop //false update a tank stop
await _authRepository.createTankStop({ if (isEditMode.value == false) {
'userId': _dataBox.read('userId'), await _authRepository.updateTankStop(documentId, {
'date': f.format(DateTime.parse(dateController.text)), 'userId': _dataBox.read('userId'),
'odometer': kilometerStandEdittingController.text, 'date': f.format(DateTime.parse(dateController.text)),
'liters': mengeController.text, 'odometer': kilometerStandEdittingController.text,
'pricePerLiter': pricePerLiterController.text, 'liters': mengeController.text,
'location': ortController.text, 'pricePerLiter': pricePerLiterController.text,
}).then((models.Document document) { 'location': ortController.text,
print('Tankstopp erfolgreich gespeichert: ${document.data}'); }).then((models.Document document) {
messageToUser = 'Tankstopp erfolgreich gespeichert!'; print('Tankstopp erfolgreich aktualisiert: ${document.data}');
isErrorBySaving = false; messageToUser = 'Tankstopp erfolgreich aktualisiert!';
}) isErrorBySaving = false;
// Handle specific Appwrite exceptions })
.catchError((error) { // Handle specific Appwrite exceptions
isErrorBySaving = true; .catchError((error) {
if (error is AppwriteException) { isErrorBySaving = true;
// Handle specific Appwrite exceptions if (error is AppwriteException) {
messageToUser = 'Appwrite Fehler: ${error.message}'; // Handle specific Appwrite exceptions
print('Appwrite Fehler: ${error.message}'); messageToUser = 'Appwrite Fehler: ${error.message}';
} else { print('Appwrite Fehler: ${error.message}');
// Handle other types of errors } else {
messageToUser = 'Allgemeiner Fehler: $error'; // Handle other types of errors
print('Allgemeiner Fehler: $error'); messageToUser = 'Allgemeiner Fehler: $error';
} print('Allgemeiner Fehler: $error');
print('Fehler bei der speicherung: $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 // Handle any other exceptions that might occur
catch (e) { catch (e) {
@ -259,6 +323,7 @@ class TankController extends GetxController {
snackPosition: SnackPosition.BOTTOM, snackPosition: SnackPosition.BOTTOM,
duration: const Duration(seconds: 4), duration: const Duration(seconds: 4),
); );
goToTankStopsView();
} }
} }

View File

@ -8,6 +8,7 @@ class TankPage extends GetView<TankController> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var tankCtrl = controller;
return SafeArea( return SafeArea(
child: Scaffold( child: Scaffold(
appBar: AppBar( appBar: AppBar(
@ -126,23 +127,30 @@ class TankPage extends GetView<TankController> {
SizedBox( SizedBox(
width: 350, width: 350,
height: 50, height: 50,
child: ElevatedButton( child: Obx(
style: ElevatedButton.styleFrom( () => ElevatedButton(
backgroundColor: Colors.blue.withValues( style: ElevatedButton.styleFrom(
alpha: 0.9, backgroundColor: Colors.blue.withValues(
), // Button-Farbe alpha: 0.9,
shape: RoundedRectangleBorder( ), // Button-Farbe
borderRadius: BorderRadius.circular( shape: RoundedRectangleBorder(
10, borderRadius: BorderRadius.circular(
), // Abgerundete Ecken 10,
), // Abgerundete Ecken
),
), ),
), onPressed: () {
onPressed: () { controller.saveTankstopp();
controller.saveTankstopp(); },
}, child: tankCtrl.isEditMode.value == true ? Text(
child: Text( 'Tankstop erfassen',
'Tankstop erfassen', style:
style: TextStyle(color: Colors.white, fontSize: 20), TextStyle(color: Colors.white, fontSize: 20),
): Text(
'Tankstop updaten',
style: TextStyle(
color: Colors.white, fontSize: 20),
),
), ),
), ),
), ),

View File

@ -36,7 +36,7 @@ class TanklistController extends GetxController {
@override @override
void onClose() {} void onClose() {}
void getTankList() async { Future<void> getTankList() async {
isloadingList(true); isloadingList(true);
bool isErrorByLoading = false; bool isErrorByLoading = false;
String message = ''; String message = '';
@ -45,8 +45,14 @@ class TanklistController extends GetxController {
.listTankStops(szRxUserId.value) .listTankStops(szRxUserId.value)
.then((tankListData) { .then((tankListData) {
if (tankListData.documents.isEmpty) { if (tankListData.documents.isEmpty) {
isErrorByLoading = true; isErrorByLoading = false;
message = 'Leere Liste keine Daten vorhanden'; message = 'Leere Liste keine Daten vorhanden';
isloadingList(false);
rxTankListAlles.clear();
rxTankListActualYear.clear();
szRxSummeYearLiter('0.0');
szRxSummePrice('0.0');
update();
return; return;
} }
rxTankListAlles.clear(); rxTankListAlles.clear();
@ -64,6 +70,7 @@ class TanklistController extends GetxController {
rxTankListActualYear.value = rxTankListAlles rxTankListActualYear.value = rxTankListAlles
.where((tank) => tank.date.startsWith(szRxYear.value)) .where((tank) => tank.date.startsWith(szRxYear.value))
.toList(); .toList();
// rxTankListActualYear.value = rxTankListAlles;
//Summe Liter aktuelles Jahr********* //Summe Liter aktuelles Jahr*********
szRxSummeYearLiter( szRxSummeYearLiter(
rxTankListActualYear.fold<double>(0.0, (previousValue, element) { rxTankListActualYear.fold<double>(0.0, (previousValue, element) {
@ -72,10 +79,11 @@ class TanklistController extends GetxController {
//Summe Preis aktuelles Jahr********* //Summe Preis aktuelles Jahr*********
szRxSummePrice( szRxSummePrice(
rxTankListActualYear.fold<double>(0.0, (previousValue, element) { rxTankListActualYear.fold<double>(0.0, (previousValue, element) {
return previousValue + (double.tryParse(element.szSummePreis!) ?? 0.0); return previousValue +
(double.tryParse(element.szSummePreis!) ?? 0.0);
}).toStringAsFixed(2)); }).toStringAsFixed(2));
message = 'Liste wurde erfolgreich geladen'; message = 'Liste wurde erfolgreich geladen';
isErrorByLoading = false;
}).catchError((error) { }).catchError((error) {
isErrorByLoading = true; isErrorByLoading = true;
if (error is AppwriteException) { if (error is AppwriteException) {
@ -94,21 +102,20 @@ class TanklistController extends GetxController {
} }
} }
String title = isErrorByLoading ? 'Fehler' : 'Erfolg'; String title = isErrorByLoading ? 'Fehler' : 'Erfolg';
Get.snackbar( print('$title: $message');
title,
message,
backgroundColor: isErrorByLoading ? Colors.red : Colors.green,
snackPosition: SnackPosition.BOTTOM,
duration: const Duration(seconds: 4),
);
update(); update();
} }
void goToUpdatePage() { void goToUpdatePage(AppWriteTankModel item) {
var map = item.toMap();
print('Go to Update Page with data: $map');
// Go to Update Page // Go to Update Page
Get.offAndToNamed(TankPage.namedRoute,
arguments: {'from': 'Update', 'data': map});
} }
void goToInputPage() { void goToInputPage() {
Get.offAndToNamed(TankPage.namedRoute); Get.offAndToNamed(TankPage.namedRoute, arguments: {'from': 'Input'});
} }
void logoutSessionAndGoToLoginPage() async { void logoutSessionAndGoToLoginPage() async {
@ -127,4 +134,30 @@ class TanklistController extends GetxController {
void goToChartView() { void goToChartView() {
Get.offAndToNamed(GraphPage.namedRoute); 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() {}
} }

View File

@ -22,6 +22,13 @@ class TanklistPage extends GetView<TanklistController> {
actions: [ actions: [
IconButton( IconButton(
icon: Icon(Icons.add_chart, color: Colors.grey.shade300), 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 { onPressed: () async {
// Handle go to Chart View // Handle go to Chart View
tankListCtrl.goToChartView(); tankListCtrl.goToChartView();
@ -57,20 +64,30 @@ class TanklistPage extends GetView<TanklistController> {
SizedBox( SizedBox(
child: Column( child: Column(
children: [ children: [
Text(tankListCtrl.szRxYear.value, style: TextStyle(fontSize: 25, color: Colors.orange)), Text(tankListCtrl.szRxYear.value,
style: TextStyle(
fontSize: 25, color: Colors.orange)),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [ children: [
Column( Column(
children: [ children: [
Text('Jahresverbrauch', style: TextStyle(fontSize: 14)), Text('Jahresverbrauch',
Text( tankListCtrl.szRxSummeYearLiter.value, style: TextStyle(fontSize: 20, color: Colors.orange)), style: TextStyle(fontSize: 14)),
Text(tankListCtrl.szRxSummeYearLiter.value,
style: TextStyle(
fontSize: 20,
color: Colors.orange)),
], ],
), ),
Column( Column(
children: [ children: [
Text('Jahressumme', style: TextStyle(fontSize: 14)), Text('Jahressumme',
Text(tankListCtrl.szRxSummePrice.value, style: TextStyle(fontSize: 20, color: Colors.orange)), style: TextStyle(fontSize: 14)),
Text(tankListCtrl.szRxSummePrice.value,
style: TextStyle(
fontSize: 20,
color: Colors.orange)),
], ],
), ),
], ],
@ -78,9 +95,12 @@ class TanklistPage extends GetView<TanklistController> {
], ],
), ),
), ),
Divider(color: Colors.grey.shade200, height: 0.0,), Divider(
color: Colors.grey.shade200,
height: 0.0,
),
Expanded( Expanded(
child: ListView.builder( child: tankListCtrl.rxTankListActualYear.isNotEmpty ? ListView.builder(
padding: EdgeInsets.only(top: 8, bottom: 8), padding: EdgeInsets.only(top: 8, bottom: 8),
physics: const BouncingScrollPhysics(), physics: const BouncingScrollPhysics(),
itemBuilder: ((BuildContext context, int index) { itemBuilder: ((BuildContext context, int index) {
@ -105,14 +125,27 @@ class TanklistPage extends GetView<TanklistController> {
), ),
], ],
), ),
child: MyListTileItem(listItem: item), child: MyListTileItem(
listItem: item,
onPressedEdit: () {
tankListCtrl.goToUpdatePage(item);
},
onPressedDelete: () {
tankListCtrl.deleteTankStop(item.documentId);
},
),
), ),
SizedBox(height: 15), SizedBox(height: 15),
], ],
); );
}), }),
itemCount: tankListCtrl.rxTankListActualYear.length, 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)),
),
)
), ),
], ],
), ),

View File

@ -2,11 +2,13 @@ import 'package:flutter/material.dart';
import '../../../data/models/tank_model.dart'; import '../../../data/models/tank_model.dart';
class MyListTileItem extends StatelessWidget { 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 AppWriteTankModel listItem;
final void Function()? onPressedEdit;
final void Function()? onPressedDelete;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -72,7 +74,8 @@ class MyListTileItem extends StatelessWidget {
children: [ children: [
Icon(Icons.price_change), Icon(Icons.price_change),
SizedBox(width: 10), 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), 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,
),
),
],
),
], ],
), ),
); );