Skip to content

Commit 435a7c6

Browse files
committed
Cleanup
1 parent 44af2ec commit 435a7c6

6 files changed

Lines changed: 135 additions & 7 deletions

File tree

lib/models/exercises/exercise_submission.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ sealed class ExerciseSubmissionApi with _$ExerciseSubmissionApi {
8181

8282
@JsonKey(name: 'license_author') required String author,
8383
@JsonKey(includeToJson: true, name: 'variation_group') String? variationGroup,
84+
85+
/// Exercise ID to connect to -- the server copies that exercise's variation group
8486
@JsonKey(includeToJson: true, name: 'variations_connect_to') int? variationConnectTo,
8587

8688
required List<ExerciseTranslationSubmissionApi> translations,

lib/models/exercises/exercise_submission.freezed.dart

Lines changed: 3 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/widgets/add_exercise/add_exercise_text_area.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,12 +180,12 @@ class _MarkdownEditorState extends State<MarkdownEditor> {
180180
IconButton(
181181
icon: const Icon(Icons.format_list_bulleted),
182182
tooltip: i18n.editorList,
183-
onPressed: () => _surroundSelection('\n- ', '\n- \n- '),
183+
onPressed: () => _surroundSelection('\n- ', ''),
184184
),
185185
IconButton(
186186
icon: const Icon(Icons.format_list_numbered),
187187
tooltip: i18n.editorList,
188-
onPressed: () => _surroundSelection('\n1. ', '\n1. \n1. '),
188+
onPressed: () => _surroundSelection('\n1. ', ''),
189189
),
190190
],
191191
);

lib/widgets/add_exercise/steps/step_2_variations.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,7 @@ class Step2Variations extends StatelessWidget {
5252
Consumer<AddExerciseProvider>(
5353
builder: (ctx, provider, __) => Switch(
5454
value: provider.variationGroup == key,
55-
onChanged: (state) =>
56-
provider.variationGroup = state ? key : null,
55+
onChanged: (state) => provider.variationGroup = state ? key : null,
5756
),
5857
),
5958
],

lib/widgets/measurements/forms.dart

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ class MeasurementEntryForm extends StatelessWidget {
176176
_dateController.text = dateFormat.format(_entryData['date']);
177177
}
178178
if (_timeController.text.isEmpty) {
179-
_timeController.text = TimeOfDay.fromDateTime(_entryData['date']).format(context);
179+
_timeController.text = timeFormat.format(_entryData['date']);
180180
}
181181

182182
final numberFormat = NumberFormat.decimalPattern(Localizations.localeOf(context).toString());
@@ -252,7 +252,16 @@ class MeasurementEntryForm extends StatelessWidget {
252252
);
253253

254254
if (pickedTime != null) {
255-
_timeController.text = pickedTime.format(context);
255+
// Use DateFormat.Hm to stay consistent with onSaved parsing
256+
final now = DateTime.now();
257+
final dt = DateTime(
258+
now.year,
259+
now.month,
260+
now.day,
261+
pickedTime.hour,
262+
pickedTime.minute,
263+
);
264+
_timeController.text = timeFormat.format(dt);
256265
}
257266
},
258267
onSaved: (newValue) {

test/measurements/measurement_entries_screen_test.dart

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ import 'package:flutter_test/flutter_test.dart';
2121
import 'package:mockito/mockito.dart';
2222
import 'package:provider/provider.dart';
2323
import 'package:wger/l10n/generated/app_localizations.dart';
24+
import 'package:wger/models/measurements/measurement_entry.dart';
2425
import 'package:wger/providers/measurement.dart';
2526
import 'package:wger/providers/nutrition.dart';
2627
import 'package:wger/screens/measurement_entries_screen.dart';
28+
import 'package:wger/widgets/measurements/forms.dart';
2729

2830
import '../../test_data/measurements.dart';
2931
import '../nutrition/nutritional_plan_form_test.mocks.dart';
@@ -99,4 +101,118 @@ void main() {
99101
expect(find.text('10.9.2022 08:00'), findsWidgets);
100102
expect(find.text('5.10.2022 07:30'), findsWidgets);
101103
});
104+
105+
group('MeasurementEntryForm time format consistency', () {
106+
Widget createFormScreen({
107+
String locale = 'en',
108+
MeasurementEntry? entry,
109+
}) {
110+
when(mockMeasurementProvider.categories).thenReturn(getMeasurementCategories());
111+
return ChangeNotifierProvider<MeasurementProvider>(
112+
create: (context) => mockMeasurementProvider,
113+
child: MaterialApp(
114+
locale: Locale(locale),
115+
localizationsDelegates: AppLocalizations.localizationsDelegates,
116+
supportedLocales: AppLocalizations.supportedLocales,
117+
home: Scaffold(
118+
body: SingleChildScrollView(
119+
child: MeasurementEntryForm(1, entry),
120+
),
121+
),
122+
),
123+
);
124+
}
125+
126+
testWidgets('Time field uses 24h format (HH:mm) for new entries - EN', (
127+
WidgetTester tester,
128+
) async {
129+
await tester.pumpWidget(createFormScreen(locale: 'en'));
130+
await tester.pumpAndSettle();
131+
132+
// The time field should contain a time in HH:mm format (24h),
133+
// not AM/PM format, to stay consistent with DateFormat.Hm parsing
134+
final timeField = find.byWidgetPredicate(
135+
(widget) => widget is TextFormField && widget.controller?.text != null,
136+
);
137+
expect(timeField, findsWidgets);
138+
139+
// Find the time controller text - it should match HH:mm pattern
140+
final timeFields = tester.widgetList<TextFormField>(timeField);
141+
final timeWidget = timeFields.where((w) {
142+
final text = w.controller?.text ?? '';
143+
// Time field contains a colon but no slash (date has slashes)
144+
return text.contains(':') && !text.contains('/') && !text.contains('.');
145+
});
146+
expect(timeWidget, isNotEmpty, reason: 'Should find a time field with HH:mm format');
147+
148+
final timeText = timeWidget.first.controller!.text;
149+
// Verify it does NOT contain AM/PM
150+
expect(
151+
timeText.contains('AM'),
152+
isFalse,
153+
reason: 'Time should be 24h format, not 12h with AM',
154+
);
155+
expect(
156+
timeText.contains('PM'),
157+
isFalse,
158+
reason: 'Time should be 24h format, not 12h with PM',
159+
);
160+
// Verify it matches HH:mm pattern
161+
expect(
162+
RegExp(r'^\d{1,2}:\d{2}$').hasMatch(timeText),
163+
isTrue,
164+
reason: 'Time "$timeText" should match HH:mm pattern',
165+
);
166+
});
167+
168+
testWidgets('Time field uses 24h format for existing entries with PM time', (
169+
WidgetTester tester,
170+
) async {
171+
// Use an entry with a PM time (18:30) to verify it's not shown as "6:30 PM"
172+
final pmEntry = MeasurementEntry(
173+
id: 10,
174+
category: 1,
175+
date: DateTime(2023, 6, 15, 18, 30),
176+
value: 25,
177+
notes: '',
178+
);
179+
await tester.pumpWidget(createFormScreen(locale: 'en', entry: pmEntry));
180+
await tester.pumpAndSettle();
181+
182+
// Should find "18:30" not "6:30 PM"
183+
final allTextFields = tester.widgetList<TextFormField>(find.byType(TextFormField));
184+
final timeTexts = allTextFields
185+
.map((w) => w.controller?.text ?? '')
186+
.where((t) => t.contains(':') && !t.contains('/') && !t.contains('.'));
187+
188+
expect(timeTexts, isNotEmpty);
189+
final timeText = timeTexts.first;
190+
expect(
191+
timeText,
192+
equals('18:30'),
193+
reason: 'PM time should be displayed as 18:30, not 6:30 PM',
194+
);
195+
});
196+
197+
testWidgets('Time field uses 24h format - DE locale', (WidgetTester tester) async {
198+
final entry = MeasurementEntry(
199+
id: 10,
200+
category: 1,
201+
date: DateTime(2023, 6, 15, 14, 45),
202+
value: 25,
203+
notes: '',
204+
);
205+
await tester.pumpWidget(createFormScreen(locale: 'de', entry: entry));
206+
await tester.pumpAndSettle();
207+
208+
final allTextFields = tester.widgetList<TextFormField>(find.byType(TextFormField));
209+
final timeTexts = allTextFields
210+
.map((w) => w.controller?.text ?? '')
211+
.where((t) => t.contains(':') && !t.contains('/') && !t.contains('.'));
212+
213+
expect(timeTexts, isNotEmpty);
214+
final timeText = timeTexts.first;
215+
expect(timeText, equals('14:45'));
216+
});
217+
});
102218
}

0 commit comments

Comments
 (0)