Skip to content

Commit 3eb502a

Browse files
committed
prevents negative time
1 parent 5854b03 commit 3eb502a

2 files changed

Lines changed: 132 additions & 92 deletions

File tree

android/app/src/main/kotlin/com/beforbike/app/database/SeedData.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,9 @@ object SeedData {
4343
val powers = listOf(165f, 172f, 158f, 185f, 175f)
4444
val cadences = listOf(88f, 92f, 85f, 95f, 89f)
4545

46-
// Ensure ride exists first
47-
if (!dbHelper.ensureRideExists(SAMPLE_RIDE_ID, "Sample Ride")) {
46+
// Ensure ride exists first (pass a proper timestamp string, not a label)
47+
val startTimeStr = java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", java.util.Locale.getDefault()).format(java.util.Date(baseTime))
48+
if (!dbHelper.ensureRideExists(SAMPLE_RIDE_ID, startTimeStr)) {
4849
android.util.Log.e("BeForBike", "Failed to ensure ride exists")
4950
return
5051
}

lib/presentation/statistics/screens/statistics_screen.dart

Lines changed: 129 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -38,44 +38,56 @@ class StatisticsScreen extends HookConsumerWidget {
3838
const SizedBox(height: 20),
3939
if (selectedActivity != null) ...[
4040
// Time and Duration Cards (Top Priority)
41-
Card(
41+
Card(
4242
elevation: 4,
4343
child: Padding(
4444
padding: const EdgeInsets.all(16.0),
4545
child: Row(
4646
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
4747
children: [
48-
// Start Time Column
49-
Column(
50-
mainAxisAlignment: MainAxisAlignment.center,
51-
children: [
52-
Icon(
53-
Icons.access_time,
54-
size: 24,
55-
color: ColorUtils.main,
56-
),
57-
const SizedBox(height: 4),
58-
Text(
59-
'Start Time',
60-
style: TextStyle(
61-
fontSize: 12,
62-
fontWeight: FontWeight.bold,
63-
color: Theme.of(context).brightness == Brightness.dark ? Colors.white : Colors.black87,
64-
),
65-
textAlign: TextAlign.center,
66-
),
67-
const SizedBox(height: 4),
68-
Text(
69-
DateFormat('HH:mm:ss\ndd/MM/yyyy').format(selectedActivity!.startDatetime),
70-
style: const TextStyle(
71-
fontSize: 10,
72-
color: Colors.grey,
73-
height: 1.2,
74-
),
75-
textAlign: TextAlign.center,
76-
),
77-
],
78-
),
48+
// Before showing, ensure start <= end. If not, swap them.
49+
// This avoids negative durations while preserving chronological order.
50+
Builder(builder: (ctx) {
51+
DateTime start = selectedActivity!.startDatetime;
52+
DateTime end = selectedActivity!.endDatetime;
53+
if (end.isBefore(start)) {
54+
final tmp = start;
55+
start = end;
56+
end = tmp;
57+
}
58+
59+
// Start Time Column
60+
return Column(
61+
mainAxisAlignment: MainAxisAlignment.center,
62+
children: [
63+
Icon(
64+
Icons.access_time,
65+
size: 24,
66+
color: ColorUtils.main,
67+
),
68+
const SizedBox(height: 4),
69+
Text(
70+
'Start Time',
71+
style: TextStyle(
72+
fontSize: 12,
73+
fontWeight: FontWeight.bold,
74+
color: Theme.of(context).brightness == Brightness.dark ? Colors.white : Colors.black87,
75+
),
76+
textAlign: TextAlign.center,
77+
),
78+
const SizedBox(height: 4),
79+
Text(
80+
DateFormat('HH:mm:ss\ndd/MM/yyyy').format(start),
81+
style: const TextStyle(
82+
fontSize: 10,
83+
color: Colors.grey,
84+
height: 1.2,
85+
),
86+
textAlign: TextAlign.center,
87+
),
88+
],
89+
);
90+
}),
7991
// Vertical separator
8092
Container(
8193
height: 60,
@@ -84,37 +96,47 @@ class StatisticsScreen extends HookConsumerWidget {
8496
? Colors.white.withValues(alpha: 0.3)
8597
: Colors.grey.withValues(alpha: 0.3),
8698
),
87-
// End Time Column
88-
Column(
89-
mainAxisAlignment: MainAxisAlignment.center,
90-
children: [
91-
Icon(
92-
Icons.access_time_filled,
93-
size: 24,
94-
color: ColorUtils.main,
95-
),
96-
const SizedBox(height: 4),
97-
Text(
98-
'End Time',
99-
style: TextStyle(
100-
fontSize: 12,
101-
fontWeight: FontWeight.bold,
102-
color: Theme.of(context).brightness == Brightness.dark ? Colors.white : Colors.black87,
99+
// End Time Column (uses same swapped start/end from Builder above)
100+
Builder(builder: (ctx) {
101+
DateTime start = selectedActivity!.startDatetime;
102+
DateTime end = selectedActivity!.endDatetime;
103+
if (end.isBefore(start)) {
104+
final tmp = start;
105+
start = end;
106+
end = tmp;
107+
}
108+
109+
return Column(
110+
mainAxisAlignment: MainAxisAlignment.center,
111+
children: [
112+
Icon(
113+
Icons.access_time_filled,
114+
size: 24,
115+
color: ColorUtils.main,
103116
),
104-
textAlign: TextAlign.center,
105-
),
106-
const SizedBox(height: 4),
107-
Text(
108-
DateFormat('HH:mm:ss\ndd/MM/yyyy').format(selectedActivity!.endDatetime),
109-
style: const TextStyle(
110-
fontSize: 10,
111-
color: Colors.grey,
112-
height: 1.2,
117+
const SizedBox(height: 4),
118+
Text(
119+
'End Time',
120+
style: TextStyle(
121+
fontSize: 12,
122+
fontWeight: FontWeight.bold,
123+
color: Theme.of(context).brightness == Brightness.dark ? Colors.white : Colors.black87,
124+
),
125+
textAlign: TextAlign.center,
113126
),
114-
textAlign: TextAlign.center,
115-
),
116-
],
117-
),
127+
const SizedBox(height: 4),
128+
Text(
129+
DateFormat('HH:mm:ss\ndd/MM/yyyy').format(end),
130+
style: const TextStyle(
131+
fontSize: 10,
132+
color: Colors.grey,
133+
height: 1.2,
134+
),
135+
textAlign: TextAlign.center,
136+
),
137+
],
138+
);
139+
}),
118140
// Vertical separator
119141
Container(
120142
height: 60,
@@ -123,37 +145,49 @@ class StatisticsScreen extends HookConsumerWidget {
123145
? Colors.white.withValues(alpha: 0.3)
124146
: Colors.grey.withValues(alpha: 0.3),
125147
),
126-
// Duration Column
127-
Column(
128-
mainAxisAlignment: MainAxisAlignment.center,
129-
children: [
130-
Icon(
131-
Icons.timer,
132-
size: 24,
133-
color: ColorUtils.main,
134-
),
135-
const SizedBox(height: 4),
136-
Text(
137-
'Duration',
138-
style: TextStyle(
139-
fontSize: 12,
140-
fontWeight: FontWeight.bold,
141-
color: Theme.of(context).brightness == Brightness.dark ? Colors.white : Colors.black87,
148+
// Duration Column (calculates from ordered start/end)
149+
Builder(builder: (ctx) {
150+
DateTime start = selectedActivity!.startDatetime;
151+
DateTime end = selectedActivity!.endDatetime;
152+
if (end.isBefore(start)) {
153+
final tmp = start;
154+
start = end;
155+
end = tmp;
156+
}
157+
158+
final duration = end.difference(start);
159+
160+
return Column(
161+
mainAxisAlignment: MainAxisAlignment.center,
162+
children: [
163+
Icon(
164+
Icons.timer,
165+
size: 24,
166+
color: ColorUtils.main,
142167
),
143-
textAlign: TextAlign.center,
144-
),
145-
const SizedBox(height: 4),
146-
Text(
147-
_formatDuration(selectedActivity!.endDatetime.difference(selectedActivity!.startDatetime)),
148-
style: const TextStyle(
149-
fontSize: 10,
150-
color: Colors.grey,
151-
height: 1.2,
168+
const SizedBox(height: 4),
169+
Text(
170+
'Duration',
171+
style: TextStyle(
172+
fontSize: 12,
173+
fontWeight: FontWeight.bold,
174+
color: Theme.of(context).brightness == Brightness.dark ? Colors.white : Colors.black87,
175+
),
176+
textAlign: TextAlign.center,
152177
),
153-
textAlign: TextAlign.center,
154-
),
155-
],
156-
),
178+
const SizedBox(height: 4),
179+
Text(
180+
_formatDuration(duration),
181+
style: const TextStyle(
182+
fontSize: 10,
183+
color: Colors.grey,
184+
height: 1.2,
185+
),
186+
textAlign: TextAlign.center,
187+
),
188+
],
189+
);
190+
}),
157191
],
158192
),
159193
),
@@ -515,6 +549,11 @@ class StatisticsScreen extends HookConsumerWidget {
515549

516550
/// Formats a Duration into a readable string (HH:MM:SS).
517551
String _formatDuration(Duration duration) {
552+
// If negative, use absolute duration (handles cases where end < start)
553+
if (duration.isNegative) {
554+
duration = Duration(microseconds: duration.inMicroseconds.abs());
555+
}
556+
518557
String twoDigits(int n) => n.toString().padLeft(2, '0');
519558
final hours = twoDigits(duration.inHours);
520559
final minutes = twoDigits(duration.inMinutes.remainder(60));

0 commit comments

Comments
 (0)