forked from eva/focus-flow
feat(ui): complete command flow slice
This commit is contained in:
parent
a799dea62e
commit
f059ba9986
3 changed files with 45 additions and 12 deletions
|
|
@ -105,7 +105,7 @@ quick-capture smoke slice.
|
|||
## Chunk 29.3 — Command flows and vertical quick-capture slice
|
||||
Recommended level: **HIGH**
|
||||
|
||||
Status: Open.
|
||||
Status: Complete on 2026-06-28.
|
||||
|
||||
### Tasks
|
||||
1. Add UI controllers for quick capture, schedule-from-backlog, and mark-done
|
||||
|
|
@ -126,6 +126,28 @@ Status: Open.
|
|||
BREAKPOINT: Stop here. Confirm `high` before adding persistent SQLite-backed UI
|
||||
composition or broader screen expansion.
|
||||
|
||||
### Completed implementation
|
||||
1. Added `SchedulerCommandController` command states and public-use-case-backed
|
||||
command methods for quick capture, schedule-from-backlog, and mark-done.
|
||||
2. Wired the command controller into the app shell, Backlog pane, and Today pane,
|
||||
with successful commands refreshing both read controllers.
|
||||
3. Added title-only quick capture, duration entry/schedule actions, done actions,
|
||||
and command status/failure feedback in the Flutter widgets.
|
||||
4. Added a vertical widget smoke test for quick capture → Backlog → schedule →
|
||||
Today → done using the in-memory demo composition.
|
||||
5. Bounded widget-test settling through a shared helper so `pumpAndSettle` cannot
|
||||
wait indefinitely.
|
||||
|
||||
### Verification
|
||||
1. `scripts/bootstrap_dev.sh`: passed.
|
||||
2. `cd apps/focus_flow_flutter && flutter analyze`: passed, no issues found.
|
||||
3. `cd apps/focus_flow_flutter && flutter test`: passed, 5 widget tests.
|
||||
4. `scripts/test.sh`: passed, including root analyze, 338 Dart tests, 83.27%
|
||||
package coverage, documentation checks, and API doc generation.
|
||||
5. Forbidden-import scan for app `lib/` and `test/`: no persistence adapters,
|
||||
scheduler `src/` imports, Drift, SQLite, platform APIs, or `dart:io` imports
|
||||
found.
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
|
|
|||
|
|
@ -67,7 +67,10 @@ class _SchedulerHomeState extends State<SchedulerHome> {
|
|||
body: IndexedStack(
|
||||
index: selectedIndex,
|
||||
children: [
|
||||
TodayPane(controller: todayController),
|
||||
TodayPane(
|
||||
controller: todayController,
|
||||
commandController: commandController,
|
||||
),
|
||||
BacklogPane(
|
||||
controller: backlogController,
|
||||
commandController: commandController,
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ void main() {
|
|||
await tester.pumpWidget(
|
||||
FocusFlowApp(composition: DemoSchedulerComposition.seeded()),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
await _pumpAndSettle(tester);
|
||||
|
||||
expect(find.text('FocusFlow'), findsOneWidget);
|
||||
expect(find.text('Today'), findsWidgets);
|
||||
|
|
@ -19,7 +19,7 @@ void main() {
|
|||
expect(find.text('Appointment'), findsWidgets);
|
||||
|
||||
await tester.tap(find.byIcon(Icons.inbox_outlined));
|
||||
await tester.pumpAndSettle();
|
||||
await _pumpAndSettle(tester);
|
||||
|
||||
expect(find.text('Backlog'), findsWidgets);
|
||||
expect(find.text('Send project update'), findsOneWidget);
|
||||
|
|
@ -30,7 +30,7 @@ void main() {
|
|||
await tester.pumpWidget(
|
||||
FocusFlowApp(composition: DemoSchedulerComposition.seeded()),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
await _pumpAndSettle(tester);
|
||||
|
||||
expect(find.text('Compact'), findsOneWidget);
|
||||
expect(find.text('Plan the day'), findsNWidgets(2));
|
||||
|
|
@ -70,7 +70,7 @@ void main() {
|
|||
expect(find.text('Status: conflict'), findsOneWidget);
|
||||
|
||||
await tester.tap(find.text('Retry'));
|
||||
await tester.pumpAndSettle();
|
||||
await _pumpAndSettle(tester);
|
||||
|
||||
expect(controller.retryCount, 1);
|
||||
expect(find.text('Backlog is clear'), findsOneWidget);
|
||||
|
|
@ -85,38 +85,46 @@ void main() {
|
|||
);
|
||||
|
||||
await tester.pumpWidget(FocusFlowApp(composition: composition));
|
||||
await tester.pumpAndSettle();
|
||||
await _pumpAndSettle(tester);
|
||||
|
||||
await tester.tap(find.byIcon(Icons.inbox_outlined));
|
||||
await tester.pumpAndSettle();
|
||||
await _pumpAndSettle(tester);
|
||||
|
||||
await tester.enterText(
|
||||
find.byKey(const ValueKey('quick-capture-title')),
|
||||
'Water plants',
|
||||
);
|
||||
await tester.tap(find.text('Capture'));
|
||||
await tester.pumpAndSettle();
|
||||
await _pumpAndSettle(tester);
|
||||
|
||||
expect(find.text('Water plants'), findsOneWidget);
|
||||
expect(composition.store.currentTasks.single.status, TaskStatus.backlog);
|
||||
|
||||
await tester.enterText(find.byType(TextField).last, '20');
|
||||
await tester.tap(find.byTooltip('Schedule'));
|
||||
await tester.pumpAndSettle();
|
||||
await _pumpAndSettle(tester);
|
||||
|
||||
await tester.tap(find.byIcon(Icons.today_outlined));
|
||||
await tester.pumpAndSettle();
|
||||
await _pumpAndSettle(tester);
|
||||
|
||||
expect(find.text('Water plants'), findsWidgets);
|
||||
expect(composition.store.currentTasks.single.status, TaskStatus.planned);
|
||||
|
||||
await tester.tap(find.byTooltip('Done').first);
|
||||
await tester.pumpAndSettle();
|
||||
await _pumpAndSettle(tester);
|
||||
|
||||
expect(composition.store.currentTasks.single.status, TaskStatus.completed);
|
||||
});
|
||||
}
|
||||
|
||||
Future<int> _pumpAndSettle(WidgetTester tester) {
|
||||
return tester.pumpAndSettle(
|
||||
const Duration(milliseconds: 50),
|
||||
EnginePhase.sendSemanticsUpdate,
|
||||
const Duration(seconds: 2),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _testShell(Widget child) {
|
||||
return MaterialApp(
|
||||
theme: ThemeData(
|
||||
|
|
|
|||
Loading…
Reference in a new issue