Skip to content

refactor(web): adopt <DataState> in routine panels#1714

Merged
Skords-01 merged 1 commit into
mainfrom
devin/1777912997-routine-datastate-adoption
May 4, 2026
Merged

refactor(web): adopt <DataState> in routine panels#1714
Skords-01 merged 1 commit into
mainfrom
devin/1777912997-routine-datastate-adoption

Conversation

@Skords-01
Copy link
Copy Markdown
Owner

@Skords-01 Skords-01 commented May 4, 2026

Summary

Initiative 0011 PR 2.7 — adopt the unified <DataState> wrapper inside apps/web/src/modules/routine/RoutineTimeline.tsx for the routine calendar panel.

The habit-pending skeleton (4× SkeletonHabitRow) used to live in an inline ternary:

{isHabitPending && mainTab === "calendar" ? (
  <div className="px-4 pt-2 space-y-2 motion-safe:animate-pulse">
    {[0, 1, 2, 3].map((i) => (
      <SkeletonHabitRow ... />
    ))}
  </div>
) : (
  <RoutineCalendarPanel hidden={mainTab !== "calendar"} />
)}

It now flows through <DataState query={calendarQuery} skeleton={calendarLoadingSkeleton}> — same shape as PR 2.4 (#1703), PR 2.5 (#1709), PR 2.6 (#1713). Visible behaviour is preserved across all four (mainTab, isHabitPending) combinations:

mainTab isHabitPending Before After
calendar true SkeletonHabitRow shimmer SkeletonHabitRow shimmer (via DataState)
calendar false <RoutineCalendarPanel hidden={false}> <RoutineCalendarPanel hidden={false}>
stats true <RoutineCalendarPanel hidden={true}> <RoutineCalendarPanel hidden={true}> (panel stays mounted)
stats false <RoutineCalendarPanel hidden={true}> <RoutineCalendarPanel hidden={true}>

Mapping: calendarBusy = isHabitPending && mainTab === 'calendar', query.data = calendarBusy ? undefined : true. DataState only renders the skeleton slot when data === undefined, so the calendar panel stays mounted on the stats tab even while a habit mutation is pending, just like before. The stats branch and pull-to-refresh wrapper are untouched.

Scope rationale: RoutineTimeline.tsx is the only consumer in apps/web/src/modules/routine/** that imports @shared/components/ui/Skeleton. Closes the routine slot of initiative 0011 Phase 2 — <DataState> adoption catalogue.

Governing Skill

  • Primary skill: sergeant-web-ui
  • Secondary skill (if truly needed): sergeant-feature-delivery

Playbook

  • Primary playbook: n/a (mechanical adoption migration described inline in docs/initiatives/0011-foundation-adoption-and-process-discipline.md Phase 2)
  • Why this playbook: this is a behaviour-preserving wrapper migration, not a feature ship.
  • If no playbook matched, why: pattern stable across PR 2.4, 2.5, 2.6; 2.7 closes the routine slot.

Verification

pnpm --filter @sergeant/web typecheck
pnpm --filter @sergeant/web exec eslint src/modules/routine/RoutineTimeline.tsx
pnpm --filter @sergeant/web exec vitest run src/modules/routine

Results:

  • typecheck: exit 0 (tsc -p tsconfig.json --noEmit && tsc -p tsconfig.sw.json --noEmit).
  • eslint RoutineTimeline.tsx: no warnings, no errors.
  • vitest src/modules/routine: 9 files, 106 / 106 tests passed.

Additional checks:

  • Local smoke / manual validation completed
  • Surface-specific checks completed

Docs and Governance

  • I updated docs that changed with the behavior, contract, workflow, or rollout.
  • I checked whether AGENTS.md needed an update.
  • I checked whether a playbook or skill needed an update.
  • I checked whether governance docs or review docs needed an update.

Updated docs:

Risk and Rollout

  • User-visible risk: none. Pure presentation wrap; same skeleton DOM, same <RoutineCalendarPanel hidden={...} /> semantics. The truth-table above proves behaviour parity.
  • Rollout / deploy order: ship as-is on merge to main. No DB / config / env changes.
  • Backout plan: revert this PR; standalone refactor with no follow-up dependents.

Hard Rule #15

  • I read AGENTS.md before coding.
  • Internal docs I touched are in Ukrainian.
  • I did not use --no-verify.

Reviewer Notes

  • The data: true as const literal is a deliberate sentinel: routine has no RQ data flowing through this branch (calendar panel reads from local-first RoutineCalendarContext), so DataState's data !== undefined check is the only signal we need. <DataState>{() => ...} doesn't read the true value.
  • I purposely left <RoutineStatsPanel> outside the DataState wrap — it has no skeleton state in this file, only the hidden={mainTab !== "stats"} prop, and its internal mounting/unmounting policy is owned by the panel itself.
  • Pre-existing CI failures on main (markdown link checker, governance sync, server apiV1.test.ts, mobile vite build against @sergeant/db-schema/sqlite, Argos visual regression) are unrelated to this change — observed identically on PR 2.4 (refactor(web): adopt <DataState> in finyk Mono panels #1703), PR 2.5 (refactor(web): adopt <DataState> in fizruk Workouts journal #1709), PR 2.6 (refactor(web): adopt <DataState> in nutrition panels #1713).

Summary by cubic

Adopted the unified <DataState> wrapper in apps/web/src/modules/routine/RoutineTimeline.tsx for the calendar panel to standardize loading behavior. No visible changes; the skeleton and panel render exactly as before, and the calendar stays mounted when viewing stats.

  • Refactors
    • Replaced the inline ternary with <DataState> using calendarQuery and calendarLoadingSkeleton (4× SkeletonHabitRow shimmer).
    • Logic: calendarBusy = isHabitPending && mainTab === "calendar"; query.data = calendarBusy ? undefined : true.
    • Left RoutineStatsPanel as-is; RoutineCalendarPanel still uses hidden={mainTab !== "calendar"} and remains mounted.

Written for commit bdab9f9. Summary will update on new commits.

Summary by CodeRabbit

  • Bug Fixes
    • Enhanced loading state visualization in the routine calendar, improving visual feedback displayed to users while calendar data is being refreshed.

Wrap the routine calendar skeleton/panel branch in
`apps/web/src/modules/routine/RoutineTimeline.tsx` with the unified
<DataState> wrapper so the habit-pending loading swap flows through the
same loading contract as finyk (PR 2.4), fizruk (PR 2.5) and nutrition
(PR 2.6) — initiative 0011 PR 2.7.

Visible behaviour is preserved across all four (mainTab, isHabitPending)
combinations:

- calendar + pending → 4x SkeletonHabitRow shimmer (exactly as before).
- calendar + idle    → <RoutineCalendarPanel hidden={false} />.
- stats    + pending → <RoutineCalendarPanel hidden={true} /> (panel
  stays mounted, preserving its internal state).
- stats    + idle    → <RoutineCalendarPanel hidden={true} />.

Mapping: `calendarBusy = isHabitPending && mainTab === 'calendar'`,
`query.data = calendarBusy ? undefined : true` — DataState only
renders the skeleton slot when data is undefined, so the calendar panel
remains mounted on the stats tab even while a habit mutation is
pending, just like before. Stats branch is untouched.
@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented May 4, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
sergeant Ready Ready Preview, Comment May 4, 2026 4:45pm

Request Review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 4, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 4b5dfdc5-8901-4502-85ce-9333713b6ebe

📥 Commits

Reviewing files that changed from the base of the PR and between 3097207 and bdab9f9.

📒 Files selected for processing (1)
  • apps/web/src/modules/routine/RoutineTimeline.tsx

📝 Walkthrough

Walkthrough

RoutineTimeline refactors calendar loading-state rendering to use a DataState component wrapper. Instead of an inline conditional checking isHabitPending && mainTab === "calendar", a new calendarBusy flag drives a DataStateQueryLike<true> query object that centralizes skeleton and panel rendering logic.

Changes

Calendar Loading State Refactoring

Layer / File(s) Summary
Imports & Types
apps/web/src/modules/routine/RoutineTimeline.tsx
Add imports for DataState and type DataStateQueryLike from @shared/components/ui/DataState.
Loading State Setup
apps/web/src/modules/routine/RoutineTimeline.tsx
Create calendarBusy flag (derived from isHabitPending && mainTab === "calendar"), define calendarQuery as a DataStateQueryLike<true> object, and add calendarLoadingSkeleton JSX for the loading state.
UI Rendering
apps/web/src/modules/routine/RoutineTimeline.tsx
Replace inline conditional skeleton/panel rendering with DataState wrapper that uses calendarQuery and calendarLoadingSkeleton, preserving the visibility condition mainTab !== "calendar".

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

size/M

Poem

🐰 A skeleton dances in place,
'til DataState shows the real face,
No more inline mess,
Refactored to less—
State management finds its grace!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main change: adopting the DataState component pattern in routine panels, which aligns with the core refactoring work described in the PR objectives.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch devin/1777912997-routine-datastate-adoption

Review rate limit: 8/10 reviews remaining, refill in 9 minutes and 26 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 4, 2026

⏱️ CI Pipeline Duration Report

Based on the last 50 successful runs on the default branch.

Overall Pipeline

Metric Value
p50 6m 26s
p95 7m 55s
p99 9m 3s
Current run 6m 24s
vs p95 -19.2%

Trend (last 20 runs): ▃▃▁▂▃▃▃▂▃▃▂▂▄▃▃▆▅▄█▆

Per-Job Breakdown

Job p50 p95 p99 Current vs p95
Accessibility (axe-core) 2m 5s 2m 21s 2m 23s 0s -100.7%
Commit messages (commitlint) 0s 0s 0s 41s N/A
Critical-flow E2E (Playwright) 1m 36s 1m 44s 1m 44s 6m 7s +252.9%
Migration lint (AGENTS rule 0s 0s 0s 8s N/A
Pipeline duration (p95 trend) 26s 27s 27s
Secret scan (gitleaks) 8s 11s 11s 9s -18.2%
Smoke E2E (Playwright) 1m 26s 1m 40s 1m 40s
Test coverage (vitest) 2m 4s 2m 33s 2m 33s 2m 17s -10.5%
Workflow lint (actionlint) 7s 7s 7s 5s -28.6%
check 4m 12s 4m 54s 5m 6s 48s -83.7%
tsconfig strict guard (PR-1.A) 5s 14s 14s 6s -57.1%

@Skords-01 Skords-01 merged commit 7299bbb into main May 4, 2026
33 of 50 checks passed
@Skords-01 Skords-01 deleted the devin/1777912997-routine-datastate-adoption branch May 4, 2026 16:56
Skords-01 pushed a commit that referenced this pull request May 4, 2026
Update initiative 0011 to reflect Phase 2 progress through 2026-05-04:

- Status header bumped to include the four open <DataState> consumer
  PRs: 2.4 #1703 (finyk), 2.5 #1709 (fizruk), 2.6 #1713 (nutrition),
  2.7 #1714 (routine). Last-validated handle aligned with the actual
  initiative owner (@Skords-01).
- Phase 2 table column 'Файли (приклад)' renamed to 'Файли (фактичні
  споживачі)' and ETA column replaced with Status (PR-link). Rows for
  2.4–2.7 now point at the actual landed targets, not the initial
  guess names from the proposal draft (MonoTransactionsPanel,
  WorkoutHistoryPanel, NutritionMealsPanel, RoutineList, etc. — none
  of those components physically exist in the repo).
- New explanatory note after the table records why the file list
  diverged from the original draft (real Skeleton-based loading sites
  per module) and documents the per-module finding for 2.6 / 2.7
  (NutritionApp.tsx Menu plan branch; RoutineTimeline.tsx calendar
  branch — both modules have exactly one consumer of
  @shared/components/ui/Skeleton).

Supersedes #1710 (single doc PR for 2.4 + 2.5 only); this combined
update is the cleaner record because all four <DataState> consumer
adoption PRs landed on the same day.

Closes the doc-side of initiative 0011 PR 2.6 + 2.7.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants