finish save data

This commit is contained in:
2025-04-04 23:47:32 +02:00
parent cccadf4dab
commit 2673d00708
21 changed files with 764 additions and 119 deletions

View File

@@ -0,0 +1,228 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
import 'package:intl/intl.dart';
class HomeController extends GetxController {
final isloadingData = false.obs;
final box = GetStorage();
final listKantine = <dynamic>[].obs;
final newBelegName = '?'.obs;
final newBelegDate = '?'.obs;
final newBelegBetrag = '?'.obs;
@override
void onInit() async {
super.onInit();
await _loadFromNotionKantine();
// Initialize any necessary data or services here
}
@override
void onClose() {}
Future<void> _loadFromNotionKantine() async {
isloadingData(true);
var postUrl = box.read('BASEURL');
var baeraToken = box.read('TOKEN'); // baeraToken aus GetStorage lesen
var headers = Map<String, String>.from(box.read('HEADERS') ?? {});
headers['Authorization'] = 'Bearer $baeraToken';
var httpClient = GetHttpClient(timeout: const Duration(seconds: 20));
var response = await httpClient.post(postUrl, headers: headers);
if (response.isOk) {
// Parse the response data and update your UI accordingly
var data = response.body;
var allJsonDataDynamicList = (data['results']);
for (var item in allJsonDataDynamicList) {
Map<String, dynamic> properties =
item['properties'] as Map<String, dynamic>;
properties['id'] = item['id'];
listKantine.add(properties);
}
listKantine.removeWhere((element) => element['Gelöscht']['number'] == 1);
listKantine.sort((a, b) {
var dateA = DateTime.parse(a['Verkaufsdatum']['date']['start']);
var dateB = DateTime.parse(b['Verkaufsdatum']['date']['start']);
return dateB.compareTo(dateA); // Sort in descending order
});
} else {
// Handle error
print('Error: ${response.statusCode}');
}
isloadingData(false);
httpClient.close();
update();
}
Future<void> addBeleg() async {
var dateFormat = DateFormat('yyyy-MM-dd');
DateTime selectedDate = DateTime.now(); // Standardwert für das Datum
await Get.defaultDialog(
title: 'Beleg hinzufügen',
content: Column(
children: [
TextField(
decoration: InputDecoration(
labelText: 'Beleg Name',
border: OutlineInputBorder(),
),
onChanged: (value) {
newBelegName(value); // Beleg Name speichern
},
),
const SizedBox(height: 10),
TextButton(
onPressed: () async {
DateTime? pickedDate = await showDatePicker(
context: Get.context!,
initialDate: selectedDate,
firstDate: DateTime(2025),
lastDate: DateTime(2100),
);
if (pickedDate != null) {
selectedDate = pickedDate;
newBelegDate.value = dateFormat.format(
selectedDate,
); // Datum speichern
}
},
child: Obx(
() => Text(
'Datum auswählen: ${newBelegDate.value == '?' ? 'Kein Datum' : dateFormat.format(selectedDate)}',
style: TextStyle(
color: newBelegDate.value == '?' ? Colors.red : Colors.black,
fontSize: 20,
),
textAlign: TextAlign.center,
),
),
),
TextField(
decoration: InputDecoration(
labelText: 'Beleg Betrag',
border: OutlineInputBorder(),
),
onChanged: (value) {
newBelegBetrag(value); // Beleg Betrag speichern
},
),
],
),
textConfirm: 'Hinzufügen',
textCancel: 'Abbrechen',
confirmTextColor: Colors.white,
cancelTextColor: Colors.red,
buttonColor: Colors.blue,
onCancel: () {
// Handle cancel action
Get.back();
},
onConfirm: () {
// Handle adding the receipt
print('Beleg Name: ${newBelegName.value}');
print('Beleg Datum: ${newBelegDate.value}');
print('Beleg Betrag: ${newBelegBetrag.value}');
Get.back();
},
);
update();
_saveData();
}
Map<String, dynamic> currentBodyItem(
String currentDate,
String preis,
String belegName,
String dbId,
) {
return {
'parent': {'database_id': dbId},
'properties': {
'Verkaufsdatum': {
'date': {'start': currentDate},
},
'Gelöscht': {'number': 0},
'Betrag': {'number': double.parse(preis)},
'BelegName': {
'id': 'title',
'type': 'title',
'title': [
{
'type': 'text',
'text': {'content': belegName},
},
],
},
},
};
}
Future<void> _saveData() async {
isloadingData(true);
var dbId = 'd036c0bc06304126aedaa8c5ad92b406';
var baeraToken = box.read('TOKEN');
var saveUrl = 'https://api.notion.com/v1/pages/';
var headers = Map<String, String>.from(box.read('HEADERS') ?? {});
headers['Authorization'] = 'Bearer $baeraToken';
var kantinData = currentBodyItem(
newBelegDate.value,
newBelegBetrag.value,
newBelegName.value,
dbId,
);
var httpClient = GetHttpClient(timeout: const Duration(seconds: 20));
await httpClient
.post(saveUrl, headers: headers, body: kantinData)
.then((response) {
if (response.isOk) {
print('Beleg erfolgreich hinzugefügt');
_loadFromNotionKantine();
} else {
print('Fehler beim Hinzufügen des Belegs: ${response.statusCode}');
}
})
.catchError((error) {
print('Fehler: $error');
})
.whenComplete(() {
httpClient.close();
});
newBelegName.value = '?';
newBelegDate.value = '?';
newBelegBetrag.value = '?';
isloadingData(false);
update();
}
void deleteBeleg(stringItem, index) {
isloadingData(true);
print(stringItem);
var baeraToken = box.read('TOKEN');
var delUrl = 'https://api.notion.com/v1/pages/$stringItem/';
var headers = Map<String, String>.from(box.read('HEADERS') ?? {});
headers['Authorization'] = 'Bearer $baeraToken';
var httpClient = GetHttpClient(timeout: const Duration(seconds: 20));
var updateMap = {
'properties': {
'Gelöscht': {'number': 1},
},
};
httpClient
.patch(delUrl, headers: headers, body: updateMap)
.then((response) {
if (response.isOk) {
print('Beleg erfolgreich gelöscht');
} else {
print('Fehler beim Löschen des Belegs: ${response.statusCode}');
}
})
.catchError((error) {
print('Fehler: $error');
})
.whenComplete(() {
httpClient.close();
});
listKantine.removeAt(index);
isloadingData(false);
update();
}
}

View File

@@ -1,6 +1,16 @@
import 'package:flutter/material.dart';
import 'dart:io';
void main() {
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
import '../../utils/http_overrides.dart';
import '../../pages/home/home_view.dart';
import '../../utils/sample_routes.dart';
void main() async {
await GetStorage.init();
HttpOverrides.global = MyHttpOverrides();
runApp(const MyApp());
}
@@ -10,113 +20,32 @@ class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
final boxSettings = GetStorage();
// Eine Map definieren
bool isNoSettings = boxSettings.hasData('BASEURL');
if (isNoSettings == false) {
_preSetDataInLocalStorage(boxSettings);
}
return GetMaterialApp(
debugShowCheckedModeBanner: false, // Debug-Banner deaktivieren
title: 'Kantine Ausgaben',
theme: ThemeData(
// This is the theme of your application.
//
// TRY THIS: Try running your application with "flutter run". You'll see
// the application has a purple toolbar. Then, without quitting the app,
// try changing the seedColor in the colorScheme below to Colors.green
// and then invoke "hot reload" (save your changes or press the "hot
// reload" button in a Flutter-supported IDE, or press "r" if you used
// the command line to start the app).
//
// Notice that the counter didn't reset back to zero; the application
// state is not lost during the reload. To reset the state, use hot
// restart instead.
//
// This works for code too, not just values: Most code changes can be
// tested with just a hot reload.
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
colorScheme: ColorScheme.fromSeed(seedColor: Colors.grey.shade600),
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
initialRoute: HomePage.namedRoute,
getPages: SampleRouts.samplePages,
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}
@override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// TRY THIS: Try changing the color here to a specific color (to
// Colors.amber, perhaps?) and trigger a hot reload to see the AppBar
// change color while the other colors stay the same.
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also a layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
//
// TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint"
// action in the IDE, or press "p" in the console), to see the
// wireframe for each widget.
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('You have pushed the button this many times:'),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
void _preSetDataInLocalStorage(GetStorage boxSettings) {
boxSettings.write('BASEURL', 'https://api.notion.com/v1/databases/d036c0bc06304126aedaa8c5ad92b406/query');
boxSettings.write('TOKEN', 'secret_05Q0wxvdUrJuNC1s1CuEXxO2nxdJFxiPAbWfaDKGWKo');
Map<String, String> headers = {
'Notion-Version': '2021-08-16',
'Content-Type': 'application/json',
};
boxSettings.write('HEADERS', headers);
}
}

View File

View File

@@ -0,0 +1,9 @@
import 'package:get/get.dart';
import '../../controller/home_controller.dart';
class HomeBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut<HomeController>(() => HomeController());
}
}

View File

@@ -0,0 +1,104 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../controller/home_controller.dart';
class HomePage extends GetView<HomeController> {
static const String namedRoute = '/home';
const HomePage({super.key});
@override
Widget build(BuildContext context) {
var homeCtrl = controller;
return SafeArea(
child: Scaffold(
appBar: AppBar(
toolbarHeight: 90,
backgroundColor: Colors.grey.shade800,
centerTitle: true,
title: Text(
'Kantine Ausgaben Liste',
style: TextStyle(color: Colors.grey.shade200, fontSize: 35.0),
),
),
body: Obx(
() =>
homeCtrl.isloadingData.value
? Center(child: CircularProgressIndicator())
: Container(
decoration: BoxDecoration(color: Colors.grey.shade600),
child: ListView.separated(
itemCount: homeCtrl.listKantine.length,
separatorBuilder:
(context, index) =>
Divider(color: Colors.grey.shade700),
padding: EdgeInsets.all(10),
physics: BouncingScrollPhysics(),
shrinkWrap: true,
scrollDirection: Axis.vertical,
primary: false,
itemBuilder: (context, index) {
var properties = homeCtrl.listKantine[index];
var dateStart = properties['Verkaufsdatum']['date']['start'];
var betrag = properties['Betrag']['number'].toString();
var belegName = properties['BelegName']['title'][0]['plain_text'];
return Container(
decoration: BoxDecoration(
color: Colors.grey.shade800,
borderRadius: BorderRadius.circular(10),
),
child: Dismissible(
key: Key(properties['id']),
background: Container(
color: Colors.red,
child: Icon(Icons.delete),
),
direction: DismissDirection.endToStart,
onDismissed: (direction) {
homeCtrl.deleteBeleg(properties['id'], index);
},
child: ListTile(
title: Text(
dateStart,
style: TextStyle(
color: Colors.grey.shade200,
fontSize: 20.0,
),
),
subtitle: Text(
belegName,
style: TextStyle(
color: Colors.grey.shade400,
fontSize: 25.0,
),
),
tileColor: Colors.grey.shade800,
textColor: Colors.grey.shade200,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
contentPadding: EdgeInsets.all(10),
leading: Icon(Icons.money),
trailing: Text(
'$betrag',
style: TextStyle(
color: Colors.green.shade200,
fontSize: 30.0,
),
),
),
),
);
},
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
homeCtrl.addBeleg(); // Add your action here
},
backgroundColor: Colors.grey.shade400,
child: Icon(Icons.add),
),
),
);
}
}

View File

@@ -0,0 +1,10 @@
import 'dart:io';
class MyHttpOverrides extends HttpOverrides {
@override
HttpClient createHttpClient(SecurityContext? context) {
return super.createHttpClient(context)
..badCertificateCallback =
(X509Certificate cert, String host, int port) => true;
}
}

View File

@@ -0,0 +1,13 @@
import 'package:get/get.dart';
import '../controller/home_controller.dart';
class SampleBindings extends Bindings {
@override
void dependencies() {
Get.lazyPut<HomeController>(() => HomeController());
}
}

View File

@@ -0,0 +1,16 @@
import 'package:get/get.dart';
import './sample_bindings.dart';
import '../../pages/home/home_view.dart';
class SampleRouts {
static final sampleBindings = SampleBindings();
static final samplePages = [
GetPage(
name: HomePage.namedRoute,
binding: sampleBindings,
page: () => const HomePage(),
transition: Transition.leftToRight,
),
];
}