feat: add swipe action to refresh report screen

This commit is contained in:
Patryk Hegenberg 2025-04-21 20:19:22 +02:00
parent ebc4cdf754
commit 055b402c81

View file

@ -1,7 +1,6 @@
import 'dart:developer';
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
@ -16,14 +15,12 @@ DateTime unixSecondsToDateTime(int ts) =>
DateTime.fromMillisecondsSinceEpoch(ts * 1000, isUtc: true).toLocal();
extension TimeEntryFormatting on TimeEntry {
/// Konvertiert den startTime (Unix Timestamp) in ein lokales DateTime Objekt.
DateTime get startDateTime =>
DateTime.fromMillisecondsSinceEpoch(
startTime.toInt() * 1000,
isUtc: true,
).toLocal();
/// Konvertiert den optionalen endTime (Unix Timestamp) in ein lokales DateTime Objekt.
DateTime? get endDateTime =>
endTime == null
? null
@ -32,7 +29,6 @@ extension TimeEntryFormatting on TimeEntry {
isUtc: true,
).toLocal();
/// Formatiert die Dauer (durationSecs) als HH:MM:SS String.
String get durationFormatted {
final durationInSeconds = durationSecs?.toInt();
if (durationInSeconds == null || durationInSeconds < 0) return '--:--:--';
@ -47,7 +43,6 @@ extension TimeEntryFormatting on TimeEntry {
}
extension ReportDataFormatting on ReportData {
/// Formatiert die Gesamtdauer als HH:MM:SS String.
String get totalDurationFormatted {
final durationInSeconds = totalDurationSecs.toInt();
if (durationInSeconds < 0) return '--:--:--';
@ -99,8 +94,9 @@ class _ReportScreenState extends State<ReportScreen> {
final Map<DateTime, Map<String, Duration>> dailyTagDurations = {};
for (final entry in entries) {
if (entry.durationSecs == null || entry.durationSecs!.toInt() <= 0)
if (entry.durationSecs == null || entry.durationSecs!.toInt() <= 0) {
continue;
}
final entryDay = DateTime(
entry.startDateTime.year,
@ -393,14 +389,24 @@ class _ReportScreenState extends State<ReportScreen> {
children: [
_buildFilterControls(tagOptions),
Expanded(
child:
_isLoadingReport
? Center(child: PlatformCircularProgressIndicator())
: _reportData == null
? const Center(
child: Text('Keine Reportdaten gefunden oder Fehler.'),
)
: _buildReportView(),
child: RefreshIndicator.adaptive(
onRefresh: _generateReport,
child:
_isLoadingReport
? Center(child: PlatformCircularProgressIndicator())
: _reportData == null
? ListView(
children: [
SizedBox(
height: MediaQuery.of(context).size.height * 0.5,
child: Text(
'Keine Reportdaten gefunden oder Fehler.',
),
),
],
)
: _buildReportView(),
),
),
],
),
@ -487,8 +493,6 @@ class _ReportScreenState extends State<ReportScreen> {
),
),
),
// SizedBox(height: 250, child: _buildPieChart(_reportData!)),
// const Divider(),
SizedBox(
height: 250,
child: _buildBarChart(_reportData!, startDate, endDate),
@ -512,103 +516,6 @@ class _ReportScreenState extends State<ReportScreen> {
);
}
Widget _buildPieChart(ReportData data) {
Map<String, double> durationPerTag = {};
for (var entry in data.entries) {
final tagName = entry.tagName ?? 'Ohne Tag';
final duration = (entry.durationSecs?.toInt() ?? 0).toDouble();
durationPerTag[tagName] = (durationPerTag[tagName] ?? 0) + duration;
}
if (durationPerTag.isEmpty) {
return const Center(child: Text("Keine Daten für Chart"));
}
List<Color> colors = Colors.primaries.take(durationPerTag.length).toList();
if (durationPerTag.length > Colors.primaries.length) {
colors.addAll(
Colors.accents.take(durationPerTag.length - Colors.primaries.length),
);
}
int colorIndex = 0;
List<PieChartSectionData> sections =
durationPerTag.entries.map((entry) {
final isTouched = false;
final fontSize = isTouched ? 18.0 : 14.0;
final radius = isTouched ? 60.0 : 50.0;
final color = colors[colorIndex % colors.length];
colorIndex++;
final hours = (entry.value / 3600).toStringAsFixed(1);
return PieChartSectionData(
color: color,
value: entry.value,
title: '${entry.key}\n${hours}h',
radius: radius,
titleStyle: TextStyle(
fontSize: fontSize,
fontWeight: FontWeight.bold,
color: Colors.white,
shadows: [
Shadow(color: Colors.black.withOpacity(0.7), blurRadius: 2),
],
),
titlePositionPercentageOffset: 0.6,
);
}).toList();
return Padding(
padding: const EdgeInsets.all(8.0),
child: PieChart(
PieChartData(
sections: sections,
centerSpaceRadius: 40,
sectionsSpace: 2,
),
duration: const Duration(milliseconds: 150),
curve: Curves.linear,
),
);
}
// Widget _buildDataTable(List<TimeEntry> entries) {
// final DateFormat timeFormatter = DateFormat('HH:mm:ss');
// final DateFormat dateFormatter = DateFormat('dd.MM.yy');
// return DataTable(
// columnSpacing: 10,
// columns: [
// DataColumn(label: PlatformText('Tag')),
// DataColumn(label: PlatformText('Start')),
// DataColumn(label: PlatformText('Ende')),
// DataColumn(label: PlatformText('Dauer'), numeric: true),
// ],
// rows:
// entries.map((entry) {
// return DataRow(
// cells: [
// DataCell(PlatformText(entry.tagName ?? '-')),
// DataCell(
// PlatformText(
// '${dateFormatter.format(unixSecondsToDateTime(entry.startTime))}\n${timeFormatter.format(unixSecondsToDateTime(entry.startTime))}',
// ),
// ),
// DataCell(
// entry.endTime != null
// ? PlatformText(
// '${dateFormatter.format(unixSecondsToDateTime(entry.endTime!))}\n${timeFormatter.format(unixSecondsToDateTime(entry.endTime!))}',
// )
// : PlatformText('-'),
// ),
// DataCell(Text(entry.durationFormatted)),
// ],
// );
// }).toList(),
// );
// }
void _confirmAndDeleteEntry(TimeEntry entry, BuildContext context) {
showPlatformDialog(
context: context,
@ -855,10 +762,7 @@ class _ReportScreenState extends State<ReportScreen> {
actions: <Widget>[
PlatformDialogAction(
child: PlatformText('OK'),
onPressed:
() => Navigator.pop(
dialogContext,
), // Pop using dialog's context
onPressed: () => Navigator.pop(dialogContext),
),
],
),