Conversation
…ker-history-mobile
+ revised layout
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds paginated history and period statistics to the mobile time-tracker (models, repo, cubit/state, UI widgets, localization, tests) and introduces server-side session action handlers, Zod schemas, and threshold utilities to enforce edit/pause/stop policies. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant UI as HistoryTab
participant Cubit as TimeTrackerCubit
participant Repo as TimeTrackerRepository
participant API as Backend
participant Prefs as SharedPreferences
User->>UI: open history view
UI->>Cubit: loadHistoryInitial(wsId,userId,firstDayOfWeek)
Cubit->>Prefs: read isHistoryStatsAccordionOpen
Cubit->>Repo: getHistorySessions(dateFrom,dateTo,cursor?)
Repo->>API: GET /sessions?dateFrom&dateTo&cursor
API-->>Repo: TimeTrackingSessionPage (sessions, nextCursor, hasMore)
Repo-->>Cubit: sessions, hasMore, nextCursor
Cubit->>Repo: getPeriodStats(dateFrom,dateTo)
Repo->>API: GET /stats?dateFrom&dateTo
API-->>Repo: TimeTrackingPeriodStats
Repo-->>Cubit: period stats
Cubit-->>UI: emit state (historySessions, historyPeriodStats, accordion state)
UI->>User: render controls, accordion, session list
sequenceDiagram
actor User
participant EditDialog as EditSessionDialog
participant Cubit as TimeTrackerCubit
participant Repo as TimeTrackerRepository
participant API as Backend
User->>EditDialog: open dialog
EditDialog->>EditDialog: compute sessionAge vs thresholdDays
alt exceeds threshold
EditDialog->>User: disable time fields, show approval UI
else within threshold
EditDialog->>User: allow full edits
end
User->>EditDialog: Save
EditDialog->>Cubit: editSession(sessionId, wsId, updates, userId, throwOnError)
Cubit->>Repo: editSession API call
Repo->>API: PATCH /sessions/[sessionId] { action: 'edit', ... }
API-->>Repo: updated session or approval-required response
Repo-->>Cubit: updated session
Cubit->>Cubit: refresh recent sessions and period stats
Cubit-->>EditDialog: success
EditDialog->>User: show success toast, close
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary of ChangesHello @VNOsST, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly enhances the time tracking feature by introducing a comprehensive history page. Users can now effortlessly browse their past sessions using infinite scrolling, navigate through different time periods like day, week, or month, and view aggregated statistics for each selected period. Additionally, the session editing experience has been refined to incorporate workspace-defined thresholds, ensuring that modifications to older sessions adhere to approval workflows, and providing immediate feedback on edit operations. Highlights
Changelog
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
🌐 i18n Check Report✅ All i18n checks passed!
🤖 Auto-generated by i18n Check workflow • Last updated: 2/24/2026, 8:24:53 AM
|
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
🔧 Biome Check ReportTotal Issues Found: 0🔧 Format Check Results ✅
🔍 Lint Check Results ✅
🎉 All Issues Resolved!Your code is now 100% clean! Great job! 🏆
🤖 Auto-generated by Biome Check workflow • Last updated: 2/24/2026, 8:24:24 AM
|
There was a problem hiding this comment.
Code Review
This pull request introduces a comprehensive time tracker history page for the mobile app, which is a great addition. The implementation includes infinite scrolling, period navigation, and statistics, all of which are well-structured within the BLoC pattern. The enhanced edit session flow, which now respects workspace thresholds, is a significant improvement for data integrity and user experience. I've identified a bug in the month navigation logic that can lead to incorrect date calculations and a minor performance improvement opportunity in data fetching. Overall, this is a solid contribution.
There was a problem hiding this comment.
4 issues found across 20 files
Confidence score: 3/5
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.tsmay still 404 for slug/aliaswsIdbecause session lookup doesn’t usenormalizedWsId, which is a concrete user-facing regression risk.- Mobile month navigation in
apps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dartcan jump months when the day doesn’t exist in the target month, which could confuse users moving across months. - Overall risk is moderate because there’s at least one user-impacting bug in the web API path and a navigation edge case on mobile.
- Pay close attention to
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts- session lookup should use the normalized workspace id.
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/mobile/lib/features/time_tracker/widgets/history_stats_accordion.dart">
<violation number="1" location="apps/mobile/lib/features/time_tracker/widgets/history_stats_accordion.dart:180">
P3: The new _formatSeconds duplicates the existing formatter in stats_cards.dart. Consider extracting a shared time-formatting helper to avoid divergence when formatting rules change.</violation>
</file>
<file name="apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts">
<violation number="1" location="apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts:214">
P2: The new normalization isn’t applied to the session lookup, so requests with a slug/alias `wsId` will pass membership checks but still 404 when fetching the session. Use `normalizedWsId` for the session query to keep workspace scoping consistent.</violation>
</file>
<file name="apps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart">
<violation number="1" location="apps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart:683">
P2: Month navigation can skip the intended month when the current day doesn’t exist in the target month (e.g., Jan 31 → Mar 2). Clamp the day to the last day of the target month before constructing the new DateTime.</violation>
<violation number="2" location="apps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart:751">
P2: The calls to `_repo.getSessions` and `_repo.getStats` are awaited sequentially despite being independent. This doubles the latency compared to running them in parallel with `Future.wait`, which was the pattern used elsewhere in this codebase before this refactor.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
Actionable comments posted: 15
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (6)
apps/mobile/lib/features/time_tracker/widgets/edit_session_dialog.dart (1)
288-295:⚠️ Potential issue | 🟡 MinorInconsistent
abs()usage produces misleading text for negative durations.When
_endTimeis before_startTime,d.inHoursis negative butd.inMinutes.abs() % 60is positive, so a -90 min duration displays as"-1h 30m"(implying -30 min). Although the Save button is disabled via_isValid, the duration text is still shown to the user.Consider either applying
abs()consistently with a leading minus sign, or displaying a placeholder like "Invalid" when_endTime ≤ _startTime.Proposed fix
String _formatDuration(Duration d) { + if (d.isNegative) return '--'; final h = d.inHours; final m = d.inMinutes.abs() % 60; final s = d.inSeconds.abs() % 60; if (h > 0) return '${h}h ${m}m'; if (m > 0) return '${m}m ${s}s'; return '${s}s'; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/mobile/lib/features/time_tracker/widgets/edit_session_dialog.dart` around lines 288 - 295, The duration formatter _formatDuration currently mixes negative hours with absolute minutes/seconds causing misleading output; update _formatDuration to detect negative durations (d.isNegative) and either return a clear placeholder like "Invalid" when used for cases where _endTime ≤ _startTime (use alongside existing validation with _isValid/_startTime/_endTime), or consistently format a negative duration by taking Duration.abs() and prefixing the whole string with '-' (e.g., '-1h 30m'); implement one of these approaches in _formatDuration so the UI no longer shows misleading mixed-sign times.apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts (4)
94-94: 🧹 Nitpick | 🔵 TrivialAvoid
anyforchainSummary.
chainSummaryis typed asany, which defeats type safety. Consider using theChainSummaryinterface already defined in this file, orChainSummary | null.♻️ Suggested fix
- let chainSummary: any = null; + let chainSummary: ChainSummary | null = null;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/src/app/api/v1/workspaces/`[wsId]/time-tracking/sessions/[sessionId]/route.ts at line 94, Replace the loose any typing on the variable chainSummary with the proper interface type: change its declaration to use the existing ChainSummary type (e.g., ChainSummary | null) so the variable is strongly typed; update its initialization and any assignments/usages in the same file (route.ts) to respect the new type (handle null cases or narrow to ChainSummary before accessing properties) and ensure imports/definitions of ChainSummary in this file are used.
249-250: 🧹 Nitpick | 🔵 TrivialMissing Zod validation for request body.
The PATCH handler destructures the request body without any runtime validation. Per coding guidelines, external inputs at API boundaries should be validated with Zod. The
actionfield drives significant branching logic, and each branch destructures additional fields (breakTypeId,title,startTime, etc.) without type or presence checks, which could lead to subtle bugs or exploitable edge cases.Consider defining a discriminated union schema with Zod (discriminated on
action) and validating early, returning 400 on failure.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/src/app/api/v1/workspaces/`[wsId]/time-tracking/sessions/[sessionId]/route.ts around lines 249 - 250, The PATCH handler currently reads request.json() and destructures action (and later fields like breakTypeId, title, startTime) without runtime validation; add a Zod schema (a discriminated union on action) that defines the allowed shapes for each action variant and validate the parsed body against it at the top of the handler, returning a 400 error when validation fails; then replace direct destructuring with the validated result so downstream branches (the code paths that reference breakTypeId, title, startTime, etc.) get properly typed/validated input.
253-259:⚠️ Potential issue | 🔴 CriticalBug: Session lookup uses raw
wsIdinstead ofnormalizedWsId.Line 257 queries
.eq('ws_id', wsId)with the raw route parameter, while every other query in this handler correctly usesnormalizedWsId. IfwsIdis a special identifier (e.g.,personalorinternal), this will never match a database row (which stores UUIDs), causing all PATCH operations to return 404.🐛 Fix: use normalizedWsId
const { data: session, error: sessionError } = await supabase .from('time_tracking_sessions') .select('*') .eq('id', sessionId) - .eq('ws_id', wsId) + .eq('ws_id', normalizedWsId) .eq('user_id', user.id) .single();Based on learnings: "Database
ws_idcolumns ALWAYS store UUIDs. Route parameters (wsId) may contain special identifiers likepersonalorinternal. NEVER use rawwsIddirectly in database queries."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/src/app/api/v1/workspaces/`[wsId]/time-tracking/sessions/[sessionId]/route.ts around lines 253 - 259, The session lookup uses the raw route param wsId instead of the normalized UUID, causing misses for special identifiers; update the Supabase query that builds session (the await supabase.from('time_tracking_sessions').select(...).eq('id', sessionId).eq('ws_id', wsId).eq('user_id', user.id).single()) to use normalizedWsId in the .eq('ws_id', ...) check (i.e., .eq('ws_id', normalizedWsId)), ensuring normalizedWsId is available in scope before the query.
207-831: 🧹 Nitpick | 🔵 TrivialPATCH handler is ~625 lines with deeply nested action branches — consider extracting.
This single handler manages
stop,pause,resume, andeditactions, each with substantial logic. The file overall is ~904 LOC, well above the 400 LOC guideline. Consider extracting each action into its own function (e.g.,handleStopAction,handlePauseAction, etc.) or splitting into separate route files if the framework supports it.As per coding guidelines: "Files >400 LOC and components >200 LOC should be refactored into smaller, focused units."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/src/app/api/v1/workspaces/`[wsId]/time-tracking/sessions/[sessionId]/route.ts around lines 207 - 831, The PATCH handler is too large and deeply nested; extract the action branches into focused helper functions to reduce file size and improve readability. Create separate functions like handleStopAction({ sbAdmin, supabase, session, sessionId, normalizedWsId, user, canBypass, requestBody }) , handlePauseAction({ sbAdmin, supabase, session, sessionId, normalizedWsId, user, canBypass, requestBody }) , handleResumeAction({ sbAdmin, session, sessionId, normalizedWsId, user }) , and handleEditAction({ sbAdmin, session, sessionId, normalizedWsId, user, canBypass, requestBody }) that encapsulate the DB queries, RPC calls, threshold checks (e.g., checkSessionThreshold), break handling, and response shaping; keep PATCH responsible only for auth, workspace normalization, permission checks, session lookup, and delegating to the appropriate handler which returns a NextResponse or throws for the outer try/catch to handle. Ensure each helper returns the same shape (NextResponse.json({ session, ... })) so callers can simply return its result.apps/mobile/lib/features/time_tracker/widgets/history_tab.dart (1)
197-215:⚠️ Potential issue | 🟡 Minor
DateTime.now()fallback for nullstartTimesilently misgroups sessions.Lines 202 and 209 use
DateTime.now()whensession.startTimeis null. This places such sessions in "today's" group regardless of their actual date, which can produce confusing UI. Consider filtering out sessions with null start times, or using a dedicated "Unknown date" group.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/mobile/lib/features/time_tracker/widgets/history_tab.dart` around lines 197 - 215, In _groupByDay, avoid using DateTime.now() for null TimeTrackingSession.startTime (which misgroups them); instead treat null startTime sessions as a separate "unknown" bucket: when iterating sessions, if session.startTime == null use a fixed key like 'unknown' and add to that group, and when mapping groups to _DayGroup produce a label 'Unknown date' for the 'unknown' key while continuing to format other keys with dateFmt.format on first.startTime!.toLocal(); ensure you reference _groupByDay, TimeTrackingSession.startTime, and _DayGroup when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart`:
- Around line 696-740: The localAnchor reconstruction in _historyPeriodRange
rebuilds anchor using DateTime(...) which is redundant for already-local
DateTimes and will incorrectly shift instants if anchor is UTC; replace the
manual field constructor with anchor.toLocal() when the goal is to normalize UTC
inputs, or simply stop reconstructing and use anchor directly (update uses of
localAnchor inside _historyPeriodRange and keep HistoryViewMode branches
unchanged).
- Around line 747-757: The helper _loadRecentAndSummary currently returns
state.stats when userId is null/empty which can yield stale UI data; change it
to always obtain up-to-date stats from the repository (call _repo.getStats(wsId,
userId) even when userId is null/empty) and return that fresh TimeTrackerStats
instead of falling back to state.stats, or if returning cached stats was
intentional add a brief inline comment in _loadRecentAndSummary explaining why
state.stats is used for the null/empty userId case (reference symbols:
_loadRecentAndSummary, _repo.getStats, TimeTrackerStats, state.stats).
- Around line 280-338: The catch block in loadHistoryInitial swallows repository
exceptions after emitting an error state; update loadHistoryInitial to rethrow
the caught Exception (or add a boolean throwOnError parameter like editSession
and rethrow only when true) so callers can react to failures; locate the
loadHistoryInitial method in TimeTrackerCubit, emit the same error state in the
on Exception catch (e) branch and then throw e (or conditionally throw when
throwOnError is true), keeping the existing state updates intact.
- Around line 678-694: The month branch in _moveHistoryAnchor produces invalid
month rollovers when current.day exceeds the target month's length; update the
HistoryViewMode.month case in _moveHistoryAnchor to compute the target
year/month, determine the target month's last day via DateTime(targetYear,
targetMonth + 1, 0).day, clamp the day to min(current.day, lastDay), then
construct the new DateTime using that clamped day while preserving current's
hour/minute/second/millisecond/microsecond so month navigation from dates like
Jan 31 correctly yields Feb 28/29 instead of rolling into March.
- Around line 300-316: The current parallel call uses Future.wait and then
unsafe casts of results[0] and results[1] to TimeTrackingSessionPage and
TimeTrackingPeriodStats; replace the Future.wait([...]) pattern around
_repo.getHistorySessions(...) and _repo.getPeriodStats(...) with Dart 3's record
`.wait` extension so you can await the tuple and destructure directly (e.g.,
final (page, periodStats) = await (...).wait) to preserve types and remove the
manual casts; update usages of page and periodStats accordingly and delete the
interim results variable and casts.
In `@apps/mobile/lib/features/time_tracker/cubit/time_tracker_state.dart`:
- Around line 85-100: historyAnchorDate is declared as DateTime? in copyWith but
lacks the sentinel/clear pattern, so it cannot be reset to null; update the
copyWith signature to accept either the sentinel pattern (change the parameter
type to Object? historyAnchorDate = _sentinel) or add a bool
clearHistoryAnchorDate flag and update copyWith logic to set historyAnchorDate
to null when the sentinel is passed or clearHistoryAnchorDate is true; modify
the copy logic branches that currently read historyAnchorDate to respect the
sentinel/clear flag and return null appropriately, and apply the same change for
the analogous nullable fields noted around lines 125-140 (use the same sentinel
or clear flag pattern) so all nullable fields can be explicitly cleared.
In `@apps/mobile/lib/features/time_tracker/widgets/edit_session_dialog.dart`:
- Around line 326-368: The async calls to showDatePicker and showTimePicker in
edit_session_dialog.dart can resume after the widget is disposed, so add a
mounted check after each await: immediately after the await showDatePicker(...)
and after await showTimePicker(...), insert if (!context.mounted) return; before
calling onChanged/constructing the new DateTime; this prevents onChanged from
being invoked on a disposed widget (affecting the buttons that use enabled,
value and onChanged).
- Around line 228-269: Replace uses of the deprecated State.mounted checks with
context.mounted after each await in the save handler: after the await
widget.onSave(...) and before showing toasts or calling navigator.pop verify if
(!context.mounted) return; and in the exception handler do the same; also avoid
exposing raw exception text in the UI by replacing error.toString() in the
Alert.destructive content with a generic localized message (e.g.,
context.l10n.commonSomethingWentWrong or a dedicated
context.l10n.timerSessionUpdateFailed) when calling shad.showToast from the
catch block for the onSave call; keep the rest of the logic around
_isSubmitting, widget.onSave, shad.showToast and navigator.pop unchanged.
In `@apps/mobile/lib/features/time_tracker/widgets/history_period_controls.dart`:
- Around line 30-31: Move the eager DateFormat.MMMd() computations into the
branch that needs them: the week view. Currently weekStartLabel and weekEndLabel
are computed unconditionally; instead compute them lazily inside the switch/case
handling viewMode == ViewMode.week (or the widget's week branch) using
period.start and period.end so they are only created when used. Update any
references to weekStartLabel/weekEndLabel to use the new local variables inside
that branch and remove the top-level declarations.
- Around line 127-134: The week-start is hardcoded to Monday in the
HistoryViewMode.week branch; change the logic to use a configurable or
locale-derived first day of week (e.g., a firstDayOfWeek int that you obtain
from MaterialLocalizations.firstDayOfWeekIndex or a passed-in config) and
compute the start using a safe modular offset: replace the subtraction of
(anchor.weekday - DateTime.monday) with a modular calculation using
firstDayOfWeek so the start Calculation (and resulting start and end values
returned by the HistoryViewMode.week case) respect the locale/configured first
day of week; update the code that calls this switch or the surrounding widget to
provide firstDayOfWeek if needed.
- Around line 143-189: _SegmentTab currently uses GestureDetector which provides
no accessibility semantics, focus traversal, or keyboard interaction; replace or
wrap the GestureDetector with an accessible interactive widget. Either wrap the
AnimatedContainer (or the whole child) in a Semantics(widget) with button: true
and a meaningful label (use the existing label prop, e.g., "Segment: $label")
and onTap/ onLongPress semantics, and also add FocusableActionDetector or
InkWell to provide focus and keyboard activation tied to the onTap callback;
ensure visual focus indicator and that onTap is invoked for keyboard
(Enter/Space) as well as touch.
In `@apps/mobile/lib/features/time_tracker/widgets/history_stats_accordion.dart`:
- Around line 180-185: _formatSeconds currently returns "0m" for durations under
60 seconds; update the function (named _formatSeconds) to display seconds for
sub-minute durations by checking if totalSeconds < 60 and returning
'${totalSeconds}s', otherwise keep the existing minutes/hours formatting (hours
via totalSeconds ~/ 3600, minutes via (totalSeconds % 3600) ~/ 60) so short
durations render as seconds instead of "0m".
In `@apps/mobile/lib/features/time_tracker/widgets/history_tab.dart`:
- Around line 75-191: The ListView currently builds all children eagerly (the
ListView with controller _scrollController and the children list that includes
HistoryPeriodControls, HistoryStatsAccordion, grouped.map(...) producing
SessionTile widgets, and optional load-more/footer widgets), which is
inefficient for paginated/large histories; convert this to a lazily-built scroll
view by replacing the ListView(children: [...]) with either a CustomScrollView
containing SliverList/SliverToBoxAdapter for the static controls and a
SliverList.builder for the variable grouped/session items, or implement a
ListView.builder that flattens your sections into an indexable list (compute a
flattened itemCount and in itemBuilder return the correct widget for headers
(entry.label), SessionTile (session with categoryColorById[session.categoryId]
and callbacks _showEditDialog/_deleteSession), loading indicators, and the "Load
More" button that calls cubit.loadHistoryMore); keep using the same
_scrollController and preserve existing widgets: HistoryPeriodControls,
HistoryStatsAccordion, SessionTile, and the load-more footer so behavior and
callbacks remain unchanged.
In `@apps/mobile/lib/features/time_tracker/widgets/stats_cards.dart`:
- Around line 104-110: The value Text in stats_cards.dart currently uses
theme.typography.small with only fontWeight changed; update it to use
theme.typography.p.copyWith(fontWeight: FontWeight.w700) for stronger hierarchy
and consistency with _OverviewCard, and ensure the accompanying label Text uses
theme.typography.textSmall (replace any theme.typography.small used for the
label) so the value and label match the established card-based stats pattern.
In `@apps/mobile/test/features/time_tracker/cubit/time_tracker_cubit_test.dart`:
- Around line 98-142: The test relies on the initial historyViewMode being week
which makes the call to setHistoryViewMode('ws-1','user-1',
HistoryViewMode.month) change state; add an explicit precondition assertion to
ensure the test actually exercises the change: before invoking
cubit.setHistoryViewMode assert that cubit.state.historyViewMode is not
HistoryViewMode.month (e.g., expect(cubit.state.historyViewMode,
isNot(HistoryViewMode.month))) so the test will fail loudly if defaults change
and you can still validate the load logic exercised by setHistoryViewMode.
---
Outside diff comments:
In `@apps/mobile/lib/features/time_tracker/widgets/edit_session_dialog.dart`:
- Around line 288-295: The duration formatter _formatDuration currently mixes
negative hours with absolute minutes/seconds causing misleading output; update
_formatDuration to detect negative durations (d.isNegative) and either return a
clear placeholder like "Invalid" when used for cases where _endTime ≤ _startTime
(use alongside existing validation with _isValid/_startTime/_endTime), or
consistently format a negative duration by taking Duration.abs() and prefixing
the whole string with '-' (e.g., '-1h 30m'); implement one of these approaches
in _formatDuration so the UI no longer shows misleading mixed-sign times.
In `@apps/mobile/lib/features/time_tracker/widgets/history_tab.dart`:
- Around line 197-215: In _groupByDay, avoid using DateTime.now() for null
TimeTrackingSession.startTime (which misgroups them); instead treat null
startTime sessions as a separate "unknown" bucket: when iterating sessions, if
session.startTime == null use a fixed key like 'unknown' and add to that group,
and when mapping groups to _DayGroup produce a label 'Unknown date' for the
'unknown' key while continuing to format other keys with dateFmt.format on
first.startTime!.toLocal(); ensure you reference _groupByDay,
TimeTrackingSession.startTime, and _DayGroup when making the change.
In
`@apps/web/src/app/api/v1/workspaces/`[wsId]/time-tracking/sessions/[sessionId]/route.ts:
- Line 94: Replace the loose any typing on the variable chainSummary with the
proper interface type: change its declaration to use the existing ChainSummary
type (e.g., ChainSummary | null) so the variable is strongly typed; update its
initialization and any assignments/usages in the same file (route.ts) to respect
the new type (handle null cases or narrow to ChainSummary before accessing
properties) and ensure imports/definitions of ChainSummary in this file are
used.
- Around line 249-250: The PATCH handler currently reads request.json() and
destructures action (and later fields like breakTypeId, title, startTime)
without runtime validation; add a Zod schema (a discriminated union on action)
that defines the allowed shapes for each action variant and validate the parsed
body against it at the top of the handler, returning a 400 error when validation
fails; then replace direct destructuring with the validated result so downstream
branches (the code paths that reference breakTypeId, title, startTime, etc.) get
properly typed/validated input.
- Around line 253-259: The session lookup uses the raw route param wsId instead
of the normalized UUID, causing misses for special identifiers; update the
Supabase query that builds session (the await
supabase.from('time_tracking_sessions').select(...).eq('id',
sessionId).eq('ws_id', wsId).eq('user_id', user.id).single()) to use
normalizedWsId in the .eq('ws_id', ...) check (i.e., .eq('ws_id',
normalizedWsId)), ensuring normalizedWsId is available in scope before the
query.
- Around line 207-831: The PATCH handler is too large and deeply nested; extract
the action branches into focused helper functions to reduce file size and
improve readability. Create separate functions like handleStopAction({ sbAdmin,
supabase, session, sessionId, normalizedWsId, user, canBypass, requestBody }) ,
handlePauseAction({ sbAdmin, supabase, session, sessionId, normalizedWsId, user,
canBypass, requestBody }) , handleResumeAction({ sbAdmin, session, sessionId,
normalizedWsId, user }) , and handleEditAction({ sbAdmin, session, sessionId,
normalizedWsId, user, canBypass, requestBody }) that encapsulate the DB queries,
RPC calls, threshold checks (e.g., checkSessionThreshold), break handling, and
response shaping; keep PATCH responsible only for auth, workspace normalization,
permission checks, session lookup, and delegating to the appropriate handler
which returns a NextResponse or throws for the outer try/catch to handle. Ensure
each helper returns the same shape (NextResponse.json({ session, ... })) so
callers can simply return its result.
ℹ️ Review info
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
⛔ Files ignored due to path filters (3)
apps/mobile/lib/l10n/gen/app_localizations.dartis excluded by!**/gen/**apps/mobile/lib/l10n/gen/app_localizations_en.dartis excluded by!**/gen/**apps/mobile/lib/l10n/gen/app_localizations_vi.dartis excluded by!**/gen/**
📒 Files selected for processing (17)
apps/mobile/lib/data/models/time_tracking/period_stats.dartapps/mobile/lib/data/models/time_tracking/session_page.dartapps/mobile/lib/data/repositories/time_tracker_repository.dartapps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dartapps/mobile/lib/features/time_tracker/cubit/time_tracker_state.dartapps/mobile/lib/features/time_tracker/view/time_tracker_page.dartapps/mobile/lib/features/time_tracker/widgets/edit_session_dialog.dartapps/mobile/lib/features/time_tracker/widgets/history_period_controls.dartapps/mobile/lib/features/time_tracker/widgets/history_stats_accordion.dartapps/mobile/lib/features/time_tracker/widgets/history_tab.dartapps/mobile/lib/features/time_tracker/widgets/stats_cards.dartapps/mobile/lib/features/time_tracker/widgets/timer_controls.dartapps/mobile/lib/l10n/arb/app_en.arbapps/mobile/lib/l10n/arb/app_vi.arbapps/mobile/test/features/time_tracker/cubit/time_tracker_cubit_test.dartapps/mobile/test/features/time_tracker/widgets/history_tab_test.dartapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
- GitHub Check: Build Android APK (Development)
- GitHub Check: build / build
- GitHub Check: Build iOS (Development, simulator)
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: Deploy-Preview
- GitHub Check: Playwright E2E
- GitHub Check: Run tests and collect coverage
- GitHub Check: Verify generated types
- GitHub Check: Type Check
- GitHub Check: Deploy-Preview
- GitHub Check: Analyze (javascript-typescript)
🧰 Additional context used
📓 Path-based instructions (19)
apps/mobile/lib/l10n/arb/*.arb
📄 CodeRabbit inference engine (CLAUDE.md)
Maintain ARB localization files in
lib/l10n/arb/with both English and Vietnamese translations
Files:
apps/mobile/lib/l10n/arb/app_en.arbapps/mobile/lib/l10n/arb/app_vi.arb
apps/mobile/lib/l10n/**/*.{arb,dart}
📄 CodeRabbit inference engine (GEMINI.md)
When updating
apps/mobile/lib/l10n/arb/*.arb, regenerate or updateapps/mobile/lib/l10n/gen/*so tracked outputs stay in sync.
Files:
apps/mobile/lib/l10n/arb/app_en.arbapps/mobile/lib/l10n/arb/app_vi.arb
apps/mobile/lib/**/*.dart
📄 CodeRabbit inference engine (CLAUDE.md)
apps/mobile/lib/**/*.dart: Useflutter_blocwith Cubits for feature state management in Flutter app
Usego_routerwith auth-aware redirects and ShellRoute for bottom tabs in Flutter app
Usesupabase_flutterwithflutter_secure_storagefor token persistence in Flutter app
Followvery_good_analysislinting rules in Flutter app
Useon Exception catch (e)instead of barecatchin Flutter code; avoid catchingErrorsubclasses
Capturecontextdependencies before firstawaitand guardBuildContextusage afterawaitwithif (!context.mounted) return;in Flutter
Never return from afinallyblock in Dart/Flutter code
UseFuture<void> Function()callbacks for mutation-driven UI actions in Flutter; await before closing dialogs
apps/mobile/lib/**/*.dart: When refactoring duplicated Flutter editable fields into shared widgets, preserve per-field validation and success messaging. Email fields should keep the@check and any email-specific success note (useTextInputType.emailAddressor an explicit parameter).
Useon Exception catch (e)(or specific exception types) instead of barecatch, avoid catchingErrorsubclasses likeTypeError, capturecontextdependencies before the firstawait, guardBuildContextusage afterawaitwithif (!context.mounted) return;, and do notreturninsidefinallyblocks.
The Flutter mobile app uses BLoC/Cubit state management,go_routernavigation, andsupabase_flutterfor auth. Build flavors:main_development.dart,main_staging.dart,main_production.dart.
For mutation-driven Flutter UI actions (approve/reject/update), useFuture<void> Function()callbacks (notVoidCallback), await them before closing dialogs/sheets, and surface failures in the UI (e.g.,SnackBar).
apps/mobile/lib/**/*.dart: Useon Exception catch (e)with specific exception types in Flutter; avoid untypedcatchblocks
Checkcontext.mountedbefore usingBuildContextafterawaitin Flutter to preven...
Files:
apps/mobile/lib/features/time_tracker/cubit/time_tracker_state.dartapps/mobile/lib/features/time_tracker/widgets/history_tab.dartapps/mobile/lib/features/time_tracker/widgets/timer_controls.dartapps/mobile/lib/data/models/time_tracking/period_stats.dartapps/mobile/lib/features/time_tracker/widgets/stats_cards.dartapps/mobile/lib/features/time_tracker/view/time_tracker_page.dartapps/mobile/lib/features/time_tracker/widgets/history_stats_accordion.dartapps/mobile/lib/data/models/time_tracking/session_page.dartapps/mobile/lib/data/repositories/time_tracker_repository.dartapps/mobile/lib/features/time_tracker/widgets/history_period_controls.dartapps/mobile/lib/features/time_tracker/widgets/edit_session_dialog.dartapps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart
apps/mobile/**/*.dart
📄 CodeRabbit inference engine (GEMINI.md)
Always run
bun check:mobileafter changes toapps/mobile/. This runsdart format --set-exit-if-changed lib test && flutter analyze && flutter test. All three checks MUST pass. If it reports a Dart format failure, rerun to confirm a clean pass.
apps/mobile/**/*.dart: Rundart format --set-exit-if-changed lib testinapps/mobile/to verify code formatting; fix withdart format lib test
Runbun check:mobile(dart format + flutter analyze + flutter test) after making changes toapps/mobile/
Files:
apps/mobile/lib/features/time_tracker/cubit/time_tracker_state.dartapps/mobile/lib/features/time_tracker/widgets/history_tab.dartapps/mobile/test/features/time_tracker/widgets/history_tab_test.dartapps/mobile/lib/features/time_tracker/widgets/timer_controls.dartapps/mobile/lib/data/models/time_tracking/period_stats.dartapps/mobile/lib/features/time_tracker/widgets/stats_cards.dartapps/mobile/lib/features/time_tracker/view/time_tracker_page.dartapps/mobile/lib/features/time_tracker/widgets/history_stats_accordion.dartapps/mobile/lib/data/models/time_tracking/session_page.dartapps/mobile/lib/data/repositories/time_tracker_repository.dartapps/mobile/lib/features/time_tracker/widgets/history_period_controls.dartapps/mobile/lib/features/time_tracker/widgets/edit_session_dialog.dartapps/mobile/test/features/time_tracker/cubit/time_tracker_cubit_test.dartapps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart
apps/mobile/test/**/*.dart
📄 CodeRabbit inference engine (CLAUDE.md)
Wrap Flutter widget tests rendering
shadcn_fluttercomponents withshad.ShadcnAppincludingshad.ShadcnLocalizations.delegateWrap Flutter widgets using
shadcn_flutterwithshad.ShadcnAppin tests to provide theme context
Files:
apps/mobile/test/features/time_tracker/widgets/history_tab_test.dartapps/mobile/test/features/time_tracker/cubit/time_tracker_cubit_test.dart
apps/mobile/**/test/**/*.dart
📄 CodeRabbit inference engine (GEMINI.md)
Wrap widgets that use
shadcn_flutterinshad.ShadcnAppwithshad.ShadcnLocalizations.delegatesoshad.Theme.of(context)resolves in tests.
Files:
apps/mobile/test/features/time_tracker/widgets/history_tab_test.dartapps/mobile/test/features/time_tracker/cubit/time_tracker_cubit_test.dart
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Use TypeScript with explicit return types for exported functions
Prefer interface for defining object shapes in TypeScript
Use TanStack Query (React Query) for ALL client-side data fetching - NEVER use useEffect for data fetching
Always includecache: 'no-store'in fetch() calls within TanStack Query queryFn to prevent dual-layer caching conflicts
Use Server Components by default; add'use client'only when necessary
Use@tuturuuu/ui/sonnerfor toast notifications instead of the deprecated@tuturuuu/ui/toast
Refactor files exceeding 400 LOC and components exceeding 200 LOC into smaller, focused units
Extract complex logic to separate utilities and custom hooks following single responsibility principle
Import database types frompackages/types/src/db.tsinstead of manually defining database types (only after user runs migrations viabun sb:pushand typegen viabun sb:typegen)
Always use stable array query keys with structure:[domain, subdomain?, paramsHash, version?]in TanStack Query
Use discriminated unions over enums for TypeScript type definitions
Narrowunknown/anyat boundaries; justify with comments if needed
Querypublic.user_private_detailstable for user email addresses instead ofpublic.userstable
Reference environment variables by name only in code - never echo values
Preferinterfaceovertypefor defining object shapes in TypeScript
Use camelCase for variable and function names in TypeScript/JavaScript
Use SCREAMING_SNAKE_CASE for constants
**/*.{ts,tsx}: Always prefer importing database types frompackages/types/src/db.ts. Never attempt to run migrations yourself - only after user runsbun sb:pushandbun sb:typegen.
Thepublic.userstable does NOT contain anpublic.user_private_details. When querying user email, always useuser_private_detailstable, notusers.
**/*.{ts,tsx}: Preferinterfacefor defining object shapes in TypeScript
N...
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
apps/*/src/app/api/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
apps/*/src/app/api/**/*.ts: Use Zod for runtime validation of external inputs at API boundaries
Validate inputs early in API routes and return 400/401/403 for bad requests
Never leak stack traces to client; wrap external service calls and surface sanitized error messages
Log detailed errors server-side only; never expose sensitive information to client
Define and export runtime if edge is required:export const runtime = 'edge'
Use@tuturuuu/supabaseclient for authentication in API routes
UsenormalizeWorkspaceId()helper to resolve workspace ID parameters in API routes
Enforce auth on AI endpoints by getting user viacreateClient()and returning 401 if absent
Check feature flags inworkspace_secretsbefore serving AI features
Use Vercel AI SDKstreamObject/generateObjectwith selected model for AI operations
SetmaxDurationfor long-running AI operations
Never log secrets or raw provider responses in AI endpoints
apps/*/src/app/api/**/*.ts: UsecreateClient(request)pattern in API routes to support both Bearer token auth (mobile) and cookie auth (web)
Useexport const runtime = 'edge'to explicitly target edge runtime execution in Next.js API routes when appropriate
Use Zod for runtime validation of external inputs (API bodies, env-derived config) at system boundaries
Resolve workspace ID parameters (wsId) to UUIDs before database operations; usenormalizeWorkspaceId()for special identifiers like 'personal' and 'internal'
Validate inputs early in API routes; return 4xx errors before processing to fail fast
Addexport const runtime = 'edge'for routes that should execute on the edge runtime
Use Vercel AI SDKgenerateObject/streamObjectwith Zod schema for deterministic AI output structure
UsecreateAdminClient()(sbAdmin) when bulk operations need to bypass trigger permission checks (e.g., user merge); always validate workspace membership first
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
apps/*/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
apps/*/src/**/*.{ts,tsx}: Extract functions used ≥2 times tosrc/lib/orsrc/utils/
Extract complex state logic to custom hooks insrc/hooks/
Extract pure computations as testable utilities
apps/*/src/**/*.{ts,tsx}: Never log secrets, API keys, tokens, or raw sensitive data to console; only reference environment variable names
Query thepublic.user_private_detailstable for user email addresses; thepublic.userstable does NOT contain an
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (GEMINI.md)
Always use
bun type-checkcommand for type checking. Do NOT usenpx tsgo,bunx tsgo, or other alternatives.
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
**/*.{ts,tsx,js,jsx,json,yaml,yml}
📄 CodeRabbit inference engine (GEMINI.md)
Use Biome for linting and formatting. Run
bun format-and-lint:fixto automatically fix issues.
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
**/{src,lib}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (GEMINI.md)
Files >400 LOC and components >200 LOC should be refactored into smaller, focused units. Apply best practices to ALL code, regardless of age. Follow single responsibility principle, extract utilities/hooks for complex logic, and leave code better than you found it.
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (GEMINI.md)
apps/web/**/*.{ts,tsx}: Never useuseEffectfor data fetching. MANDATORY: Use TanStack Query (useQuery,useMutation,useInfiniteQuery) for all client-side data fetching.
Never use rawfetch()without TanStack Query wrapper in client components. Always use TanStack Query hooks for client-side data fetching.
Anyfetch()inside aqueryFnMUST includecache: 'no-store'to prevent browser HTTP cache from serving stale responses after TanStack Query invalidation.
Use stable array query keys following pattern:[domain, subdomain?, paramsHash, version?]. SetstaleTime> 0 for rarely-changing data. Implement optimistic updates with rollback. Use narrow invalidations, avoid globalinvalidateQueries().
Follow the Tailwind Dynamic Color Policy. Never use hard-coded color classes liketext-blue-500. Instead, use thedynamic-*tokens, e.g.,text-dynamic-blue.
Usesonnerfor toasts:import { toast } from '@tuturuuu/ui/sonner';. Avoid the deprecated@tuturuuu/ui/toast. Never use native browser dialogs likealert()orconfirm().
Use the dialog system for modals:import { Dialog, ... } from '@tuturuuu/ui/dialog';. Never use native browser dialogs.
Databasews_idcolumns ALWAYS store UUIDs. Route parameters (wsId) may contain special identifiers likepersonalorinternal. NEVER use rawwsIddirectly in database queries. UsenormalizeWorkspaceId(wsId)helper for API routes or acceptworkspaceprop in components.
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
apps/web/src/app/**/*.{ts,tsx}
📄 CodeRabbit inference engine (GEMINI.md)
When adding new pages or routes, ALWAYS update the main navigation file (
apps/web/src/app/[locale]/(dashboard)/[wsId]/navigation.tsx). Add routes to both thealiasesarray andchildrennavigation items with proper icons, permission checks, and translation keys in bothen.jsonANDvi.json.
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
apps/*/src/app/api/**/*.{ts,tsx}
📄 CodeRabbit inference engine (GEMINI.md)
Create new API routes in
apps/<app>/src/app/api/.... Use Supabase client wrappers for authentication and Zod for input validation.
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
apps/web/src/app/api/**/*.{ts,tsx}
📄 CodeRabbit inference engine (GEMINI.md)
The mobile app connects to
apps/webAPI routes via Bearer token auth (not cookies). When updating web API routes for mobile features, maintain backward compatibility and usecreateClient(request)for Bearer token auth.
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
apps/web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (GEMINI.md)
Extract utilities to
src/lib/, hooks tosrc/hooks/, and sub-components as needed. Apply best practices to BOTH old and new code. Follow single responsibility principle, use meaningful names, and eliminate duplication.
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
**/*.ts: Avoid usinganytype in TypeScript; narrowunknownat boundaries with explicit type guards
Use discriminated unions over enums unless runtime enum is required
Always usebun type-checkfor TypeScript type checking; do NOT usenpx tsgo,bunx tsgo, or other alternatives
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
apps/mobile/lib/**/*{cubit,bloc}.dart
📄 CodeRabbit inference engine (GEMINI.md)
If a Flutter Cubit catches repository errors, rethrow after emitting error state so callers can handle failures locally.
Files:
apps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart
🧠 Learnings (22)
📚 Learning: 2026-02-23T06:49:12.270Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-23T06:49:12.270Z
Learning: Applies to apps/mobile/lib/l10n/arb/*.arb : Maintain ARB localization files in `lib/l10n/arb/` with both English and Vietnamese translations
Applied to files:
apps/mobile/lib/l10n/arb/app_en.arbapps/mobile/lib/l10n/arb/app_vi.arb
📚 Learning: 2026-02-23T06:49:53.243Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: GEMINI.md:0-0
Timestamp: 2026-02-23T06:49:53.243Z
Learning: Applies to apps/mobile/lib/l10n/**/*.{arb,dart} : When updating `apps/mobile/lib/l10n/arb/*.arb`, regenerate or update `apps/mobile/lib/l10n/gen/*` so tracked outputs stay in sync.
Applied to files:
apps/mobile/lib/l10n/arb/app_en.arbapps/mobile/lib/l10n/arb/app_vi.arb
📚 Learning: 2026-02-23T06:49:53.243Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: GEMINI.md:0-0
Timestamp: 2026-02-23T06:49:53.243Z
Learning: Applies to apps/mobile/lib/**/*.dart : The Flutter mobile app uses BLoC/Cubit state management, `go_router` navigation, and `supabase_flutter` for auth. Build flavors: `main_development.dart`, `main_staging.dart`, `main_production.dart`.
Applied to files:
apps/mobile/lib/features/time_tracker/widgets/history_tab.dartapps/mobile/test/features/time_tracker/widgets/history_tab_test.dartapps/mobile/lib/features/time_tracker/view/time_tracker_page.dartapps/mobile/lib/data/repositories/time_tracker_repository.dartapps/mobile/lib/features/time_tracker/widgets/edit_session_dialog.dartapps/mobile/test/features/time_tracker/cubit/time_tracker_cubit_test.dartapps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart
📚 Learning: 2026-02-23T06:49:53.243Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: GEMINI.md:0-0
Timestamp: 2026-02-23T06:49:53.243Z
Learning: Applies to apps/mobile/**/test/**/*.dart : Wrap widgets that use `shadcn_flutter` in `shad.ShadcnApp` with `shad.ShadcnLocalizations.delegate` so `shad.Theme.of(context)` resolves in tests.
Applied to files:
apps/mobile/test/features/time_tracker/widgets/history_tab_test.dartapps/mobile/lib/features/time_tracker/widgets/stats_cards.dartapps/mobile/lib/features/time_tracker/view/time_tracker_page.dartapps/mobile/lib/features/time_tracker/widgets/edit_session_dialog.dartapps/mobile/test/features/time_tracker/cubit/time_tracker_cubit_test.dart
📚 Learning: 2026-02-23T06:49:12.270Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-23T06:49:12.270Z
Learning: Applies to apps/mobile/lib/**/*.dart : Use `flutter_bloc` with Cubits for feature state management in Flutter app
Applied to files:
apps/mobile/test/features/time_tracker/widgets/history_tab_test.dartapps/mobile/test/features/time_tracker/cubit/time_tracker_cubit_test.dartapps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart
📚 Learning: 2026-02-23T06:49:12.271Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-23T06:49:12.271Z
Learning: Applies to apps/mobile/test/**/*.dart : Wrap Flutter widget tests rendering `shadcn_flutter` components with `shad.ShadcnApp` including `shad.ShadcnLocalizations.delegate`
Applied to files:
apps/mobile/test/features/time_tracker/widgets/history_tab_test.dartapps/mobile/lib/features/time_tracker/view/time_tracker_page.dartapps/mobile/lib/features/time_tracker/widgets/edit_session_dialog.dartapps/mobile/test/features/time_tracker/cubit/time_tracker_cubit_test.dart
📚 Learning: 2026-02-23T06:51:05.456Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-23T06:51:05.456Z
Learning: Applies to apps/mobile/test/**/*.dart : Wrap Flutter widgets using `shadcn_flutter` with `shad.ShadcnApp` in tests to provide theme context
Applied to files:
apps/mobile/test/features/time_tracker/widgets/history_tab_test.dartapps/mobile/lib/features/time_tracker/widgets/stats_cards.dartapps/mobile/lib/features/time_tracker/view/time_tracker_page.dartapps/mobile/lib/features/time_tracker/widgets/edit_session_dialog.dartapps/mobile/test/features/time_tracker/cubit/time_tracker_cubit_test.dart
📚 Learning: 2026-02-23T06:49:53.243Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: GEMINI.md:0-0
Timestamp: 2026-02-23T06:49:53.243Z
Learning: Applies to apps/mobile/lib/**/*.dart : When refactoring duplicated Flutter editable fields into shared widgets, preserve per-field validation and success messaging. Email fields should keep the `@` check and any email-specific success note (use `TextInputType.emailAddress` or an explicit parameter).
Applied to files:
apps/mobile/lib/features/time_tracker/widgets/edit_session_dialog.dart
📚 Learning: 2026-02-23T06:49:12.271Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-23T06:49:12.271Z
Learning: Applies to apps/mobile/lib/**/*.dart : Capture `context` dependencies before first `await` and guard `BuildContext` usage after `await` with `if (!context.mounted) return;` in Flutter
Applied to files:
apps/mobile/lib/features/time_tracker/widgets/edit_session_dialog.dartapps/mobile/test/features/time_tracker/cubit/time_tracker_cubit_test.dartapps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart
📚 Learning: 2026-02-23T06:49:12.270Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-23T06:49:12.270Z
Learning: Applies to apps/mobile/lib/**/*.dart : Use `supabase_flutter` with `flutter_secure_storage` for token persistence in Flutter app
Applied to files:
apps/mobile/test/features/time_tracker/cubit/time_tracker_cubit_test.dart
📚 Learning: 2026-02-23T06:49:53.243Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: GEMINI.md:0-0
Timestamp: 2026-02-23T06:49:53.243Z
Learning: Applies to apps/mobile/lib/**/*{cubit,bloc}.dart : If a Flutter Cubit catches repository errors, rethrow after emitting error state so callers can handle failures locally.
Applied to files:
apps/mobile/test/features/time_tracker/cubit/time_tracker_cubit_test.dartapps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart
📚 Learning: 2026-02-23T06:51:05.456Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-23T06:51:05.456Z
Learning: Applies to apps/*/src/app/api/**/*.ts : Resolve workspace ID parameters (`wsId`) to UUIDs before database operations; use `normalizeWorkspaceId()` for special identifiers like 'personal' and 'internal'
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📚 Learning: 2026-02-23T06:49:12.270Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-23T06:49:12.270Z
Learning: Applies to apps/*/src/app/api/**/*.ts : Use `normalizeWorkspaceId()` helper to resolve workspace ID parameters in API routes
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📚 Learning: 2026-02-23T06:49:53.243Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: GEMINI.md:0-0
Timestamp: 2026-02-23T06:49:53.243Z
Learning: Applies to apps/web/**/*.{ts,tsx} : Database `ws_id` columns ALWAYS store UUIDs. Route parameters (`wsId`) may contain special identifiers like `personal` or `internal`. NEVER use raw `wsId` directly in database queries. Use `normalizeWorkspaceId(wsId)` helper for API routes or accept `workspace` prop in components.
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📚 Learning: 2026-02-23T06:49:12.270Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-23T06:49:12.270Z
Learning: Applies to apps/**/src/components/**/*.{ts,tsx} : Use `workspace.id` directly from workspace object in client components instead of raw `wsId` parameter
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📚 Learning: 2026-02-23T06:49:53.243Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: GEMINI.md:0-0
Timestamp: 2026-02-23T06:49:53.243Z
Learning: Applies to packages/ui/src/**/*.{ts,tsx} : Extract portable UI components with no `@/` imports to `packages/ui`. Pass `workspace` prop to child components instead of raw `wsId`.
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📚 Learning: 2026-02-23T06:51:05.456Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-23T06:51:05.456Z
Learning: Applies to scripts/**/*.{js,ts} : Use workspace-relative paths in `apply_patch` (e.g., `apps/web/...`) instead of absolute Windows paths for better cross-platform compatibility
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📚 Learning: 2026-02-23T06:49:12.270Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-23T06:49:12.270Z
Learning: Applies to apps/web/src/app/[locale]/(dashboard)/[wsId]/navigation.tsx : Update main navigation in `apps/web/src/app/[locale]/(dashboard)/[wsId]/navigation.tsx` when adding new routes - add to both `aliases` array and `children` navigation items with proper icons and permissions
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📚 Learning: 2026-02-23T06:51:05.456Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-23T06:51:05.456Z
Learning: Applies to apps/web/src/app/[locale]/(dashboard)/[wsId]/navigation.tsx : Update main navigation file when adding new routes: add entries to both `aliases` array and `children` navigation items with proper icons and permission checks
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📚 Learning: 2026-02-23T06:51:05.456Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-23T06:51:05.456Z
Learning: Applies to apps/*/src/app/api/**/*.ts : Use `createAdminClient()` (sbAdmin) when bulk operations need to bypass trigger permission checks (e.g., user merge); always validate workspace membership first
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📚 Learning: 2026-02-23T06:49:12.270Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-23T06:49:12.270Z
Learning: Applies to apps/*/src/app/api/**/*.ts : Use `tuturuuu/supabase` client for authentication in API routes
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📚 Learning: 2026-02-23T06:49:53.243Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: GEMINI.md:0-0
Timestamp: 2026-02-23T06:49:53.243Z
Learning: Applies to apps/*/src/app/api/**/*.{ts,tsx} : Create new API routes in `apps/<app>/src/app/api/...`. Use Supabase client wrappers for authentication and Zod for input validation.
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #4341 +/- ##
==========================================
+ Coverage 49.10% 49.11% +0.01%
==========================================
Files 256 256
Lines 19633 19633
Branches 6713 6706 -7
==========================================
+ Hits 9641 9643 +2
+ Misses 8142 8141 -1
+ Partials 1850 1849 -1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
- Updated `getStats` method to accept nullable `userId` and conditionally include it in API requests. - Refactored `loadHistoryInitial` to support an optional `throwOnError` parameter. - Improved month navigation logic in `TimeTrackerCubit`. - Enhanced error handling in `EditSessionDialog` and `DateTimePicker` widgets. - Updated `HistoryPeriodControls` to dynamically adjust based on the first day of the week. - Refined `HistoryTab` layout using `CustomScrollView` for better performance and organization. - Adjusted styles in `StatCard` for consistency with typography updates. - Added validation for session duration formatting in `HistoryStatsAccordion`. - Introduced new API route and helper functions for session management in the web application.
There was a problem hiding this comment.
4 issues found across 11 files (changes from recent commits).
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/mobile/lib/features/time_tracker/widgets/history_tab.dart">
<violation number="1" location="apps/mobile/lib/features/time_tracker/widgets/history_tab.dart:237">
P3: Localize the "Unknown date" label instead of hard-coding it so it can be translated like the rest of the history UI.</violation>
</file>
<file name="apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/helpers.ts">
<violation number="1" location="apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/helpers.ts:363">
P2: The session "rollback" inside the `breakError` handler fires before the session has been modified (the `pause_session_for_break` RPC that actually updates the session comes after). The rollback is therefore an unnecessary DB write that may overwrite legitimate session state. It should be removed.</violation>
<violation number="2" location="apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/helpers.ts:621">
P1: `duration_seconds` is not recalculated when only `endTime` is provided. If a caller sends just an `endTime` update, the `end_time` column is written to the DB but `duration_seconds` stays at its old value, leaving the two fields inconsistent. Fall back to `session.start_time` when `startTime` is absent.</violation>
</file>
<file name="apps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart">
<violation number="1" location="apps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart:688">
P2: Month navigation can compute the wrong year when moving backwards across January because `~/` truncates toward zero for negative values. This will return December of the same year instead of the previous year.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
Actionable comments posted: 17
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts (1)
36-61:⚠️ Potential issue | 🟠 MajorGET handler uses raw
wsIdin database queries — normalize it like PATCH and DELETE.The PATCH (line 88) and DELETE (line 209) handlers both call
normalizeWorkspaceId(wsId, supabase)before querying, but GET uses the raw route parameter directly on lines 39 and 60. RoutewsIdmay contain special identifiers likepersonalorinternalthat are not valid UUIDs for DB columns.Proposed fix
try { const { wsId, sessionId } = await params; const supabase = await createClient(request); + let normalizedWsId: string; + try { + normalizedWsId = await normalizeWorkspaceId(wsId, supabase); + } catch { + return NextResponse.json( + { error: 'Workspace not found' }, + { status: 404 } + ); + } + const { data: { user }, error: authError, } = await supabase.auth.getUser(); if (authError || !user) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); } const { data: memberCheck } = await supabase .from('workspace_members') .select('id:user_id') - .eq('ws_id', wsId) + .eq('ws_id', normalizedWsId) .eq('user_id', user.id) .single(); // ... const { data, error } = await supabase .from('time_tracking_sessions') .select(/* ... */) .eq('id', sessionId) - .eq('ws_id', wsId) + .eq('ws_id', normalizedWsId) .eq('user_id', user.id) .single();Based on learnings: "Database
ws_idcolumns ALWAYS store UUIDs. Route parameters (wsId) may contain special identifiers likepersonalorinternal. NEVER use rawwsIddirectly in database queries. UsenormalizeWorkspaceId(wsId)helper for API routes."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/src/app/api/v1/workspaces/`[wsId]/time-tracking/sessions/[sessionId]/route.ts around lines 36 - 61, The GET handler is using the raw route wsId in DB queries; call the same helper used by PATCH/DELETE—normalizeWorkspaceId(wsId, supabase)—at the start of the GET flow, await its result into a variable (e.g., normalizedWsId), and use that normalizedWsId in both the workspace_members lookup (memberCheck) and the time_tracking_sessions query (instead of raw wsId); ensure you handle any error or null return from normalizeWorkspaceId before proceeding.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart`:
- Around line 726-735: The week range calculation in _historyPeriodRange (case
HistoryViewMode.week) hardcodes Monday via localAnchor.weekday -
DateTime.monday, causing mismatch with the locale-aware label in
HistoryPeriodControls; update the calculation to compute the start offset using
the actual first day of week (use the same firstDayOfWeek/locale value used by
HistoryPeriodControls or pass it into _historyPeriodRange) and subtract
(localAnchor.weekday - firstDayOfWeek) modulo 7 to get the correct start, then
compute end as start + 7 days - 1 microsecond as before so the API range matches
the UI label.
- Around line 681-706: The month-branch in _moveHistoryAnchor wrongly computes
year using integer division; replace the manual targetYear/targetMonth math by
leveraging Dart's DateTime normalization: construct a DateTime using
(current.year, current.month + delta, current.day, ...) to get the normalized
year/month, then clamp the day to the last day of that normalized month if
current.day exceeds it; ensure this logic is used only for HistoryViewMode.month
in the switch so hour/minute/second/millisecond/microsecond are preserved.
In `@apps/mobile/lib/features/time_tracker/widgets/edit_session_dialog.dart`:
- Around line 255-271: The exception handler currently uses an unbound `on
Exception` which swallows the error; change it to `on Exception catch (e, st)`
to bind the exception (and stacktrace) so you can log it before showing the
toast and resetting `_isSubmitting`; keep the `if (!context.mounted) return;`
check and call your logger (or `debugPrint`) with `e`/`st` inside the `catch`
block prior to invoking `shad.showToast` and `setState(() => _isSubmitting =
false);` so failures in the EditSessionDialog flow are observable.
- Around line 288-293: The current validation blocks Save when times are
inverted even if times are read-only; update the _isValid getter to only enforce
endTime > startTime when canEditTimes is true (e.g. return true if !canEditTimes
|| _endTime.isAfter(_startTime)) so metadata-only edits can be saved, and
replace the hardcoded 'Invalid' in _formatDuration with the localized string
from l10n (e.g. use context.l10n.invalid or the project's appropriate l10n key)
so localization is respected.
In `@apps/mobile/lib/features/time_tracker/widgets/history_period_controls.dart`:
- Around line 124-148: The week-start logic in the widget (_periodRange) uses a
locale-specific firstDayOfWeek but the cubit's _historyPeriodRange always uses
DateTime.monday, causing mismatched ranges; update the cubit by adding a
firstDayOfWeek parameter to TimeTrackerCubit._historyPeriodRange (and its
callers) and use the same calculation as the widget: compute localAnchor from
the anchor date, compute offset = (localAnchor.weekday - firstDayOfWeek + 7) %
7, set start = localAnchor.subtract(Duration(days: offset)) and end =
start.add(Duration(days: 6)) so the API queries use the identical week range the
UI displays.
In `@apps/mobile/lib/features/time_tracker/widgets/history_tab.dart`:
- Around line 43-52: _handleScroll can run after the State is disposed and
currently reads context inside the scroll callback; add a mounted guard at the
top of _handleScroll (e.g., return early if !mounted or !context.mounted) before
accessing context.read<WorkspaceCubit>() and context.read<TimeTrackerCubit>();
ensure you capture any ids (via _currentUserId()) only after the mount check and
then call loadHistoryMore(wsId, userId) so the listener will not use context
when the widget is unmounted.
- Around line 234-252: The hardcoded 'Unknown date' literal used when building
_DayGroup labels should be localized; update the _groupByDay implementation to
accept a L10n reference (or access context.l10n) and replace both occurrences of
'Unknown date' with the localized string (e.g., context.l10n.unknownDate),
ensuring _DayGroup construction uses the localized label; adjust the caller(s)
of _groupByDay to pass the l10n instance if you choose the parameter approach
and keep dateFmt and _DayGroup usage unchanged.
In `@apps/mobile/lib/features/time_tracker/widgets/stats_cards.dart`:
- Around line 21-25: The calculation of cardWidth in the LayoutBuilder (builder)
uses constraints.maxWidth directly which can be unbounded or smaller than
spacing, producing double.infinity or negative widths; fix it by first checking
constraints.hasBoundedWidth (or constraints.maxWidth.isFinite) and computing an
availableWidth = max(0.0, constraints.maxWidth - spacing) with a sensible
fallback when unbounded (e.g., a default total width), then derive cardWidth =
availableWidth / 2 and clamp it to a minimum card width (e.g., via math.max) to
ensure cardWidth is finite and non-negative before passing it to Wrap/SizedBox.
In
`@apps/web/src/app/api/v1/workspaces/`[wsId]/time-tracking/sessions/[sessionId]/helpers.ts:
- Around line 695-704: The current error handling in helpers.ts relies on
fragile string matching of error.message (checks for 'older than' and 'must be
submitted as requests') before returning a 400; change this to rely on
structured error metadata instead: remove the message.includes(...) checks and
only treat errors with error.code === 'P0001' (or a new custom error
property/hint you add in the database function) as client 400 responses,
returning NextResponse.json({ error: error.message }, { status: 400 });
otherwise rethrow the error; update the producer (the DB function that can throw
these conditions) to attach a stable error.code or hint if necessary so the
handler (the block that currently does the message.includes checks) can make a
robust decision.
- Around line 62-75: Replace the exported type alias SessionRecord with an
exported interface named SessionRecord that preserves the same property names
and types (id, ws_id, user_id, title, description, category_id, task_id,
start_time, end_time, duration_seconds, is_running, pending_approval) so code
consuming SessionRecord remains unchanged; update the declaration syntax from
"export type SessionRecord = { ... }" to "export interface SessionRecord { ...
}".
- Around line 1-6: This file is too large; split its responsibilities into
smaller modules: extract all Zod schemas and exported types into a new
schemas.ts (move lines containing schema definitions and type exports), move
getSessionChainRoot and checkSessionThreshold into threshold.ts (preserve their
implementations and imports), and create an actions/ folder with separate files
for each handler (stop.ts, pause.ts, resume.ts, edit.ts) exporting the
corresponding action functions; update imports in the original helpers.ts to
re-export or delegate to these new modules so existing callers (e.g., functions
referencing getSessionChainRoot, checkSessionThreshold, and the action handlers)
keep working.
- Around line 525-549: The validation currently checks startTime and endTime for
being in the future but doesn't ensure startTime < endTime, which allows
negative durations; after the existing future-time checks in helpers.ts, add a
check that when both startTime and endTime are provided (and parseable to Dates)
the computed start date is strictly before the end date, and if not return a
NextResponse.json error (400) with a clear message like "startTime must be
before endTime" so duration_seconds cannot be negative; reference startTime,
endTime, new Date(...), and NextResponse.json when locating where to insert this
validation.
- Around line 77-95: Change getSessionChainRoot and checkSessionThreshold to
accept an existing sbAdmin client instead of calling createAdminClient() inside
them: update their signatures (e.g., getSessionChainRoot(sbAdmin, sessionId) and
checkSessionThreshold(sbAdmin, ...)) remove the internal createAdminClient()
calls and use the passed sbAdmin for the RPCs, and update all callers
(handleStopAction, handlePauseAction) to pass the sbAdmin they already have into
these helper functions so a single admin client is reused per request.
- Around line 350-388: The current flow inserts into time_tracking_breaks before
calling sbAdmin.rpc('pause_session_for_break'), which leads to orphaned break
rows if the RPC fails and the current "revert" update (the block updating
time_tracking_sessions to is_running: true, end_time: null) is a no-op because
the session hasn't been paused yet; reorder and error-handle to fix this: call
sbAdmin.rpc('pause_session_for_break', { p_session_id: sessionId, p_end_time:
endTime, p_duration_seconds: durationSeconds, p_pending_approval:
pendingApproval || false }) first and only insert into time_tracking_breaks
(using finalBreakTypeId/finalBreakTypeName, break_start: endTime, created_by:
userId) after the RPC succeeds; alternatively, if you must keep the current
order, ensure you delete the break row (DELETE from time_tracking_breaks where
session_id = sessionId and break_start = endTime and created_by = userId) when
the RPC returns an error and remove the pointless revert update block that
assumes the session was already paused.
- Around line 570-629: When session.is_running is true the current code silently
skips startTime/endTime handling; change this to return a 400 response when a
client supplies startTime or endTime for a running session (unless a bypass like
canBypass applies). In helpers.ts locate the block that checks
session.is_running (and uses updateData) and before skipping, add a guard that
if (session.is_running && (startTime !== undefined || endTime !== undefined) &&
!canBypass) then return NextResponse.json({ error: 'Cannot edit start_time or
end_time for a running session' }, { status: 400 }). This ensures clients
receive explicit feedback instead of having edits discarded.
- Around line 618-628: The current block only recalculates
updateData.duration_seconds when both startTime and endTime are provided; change
it to recompute duration whenever at least one of startTime or endTime is being
updated by using the existing session's stored start/end as fallbacks: derive
start = startTime ? new Date(startTime) : new Date(existingSession.start_time)
and end = endTime ? new Date(endTime) : new Date(existingSession.end_time), then
if both start and end are present compute updateData.duration_seconds =
Math.floor((end.getTime() - start.getTime())/1000); ensure you reference
updateData, startTime, endTime, duration_seconds and the existing session values
(e.g., existingSession.start_time / existingSession.end_time) so partial edits
update duration correctly.
In
`@apps/web/src/app/api/v1/workspaces/`[wsId]/time-tracking/sessions/[sessionId]/route.ts:
- Around line 152-189: The switch over body.action (cases:
'stop','pause','resume','edit') can fall through if a new action is added;
update the switch in the route handler to be exhaustive by adding a default case
(or a post-switch throw/return) that returns a proper HTTP response or throws an
error when action is unrecognized; locate the switch that calls
handleStopAction, handlePauseAction, handleResumeAction, and handleEditAction
and add a fallback that logs the unknown action and returns a 400/500 response
(or throws) so the handler never silently returns undefined.
---
Outside diff comments:
In
`@apps/web/src/app/api/v1/workspaces/`[wsId]/time-tracking/sessions/[sessionId]/route.ts:
- Around line 36-61: The GET handler is using the raw route wsId in DB queries;
call the same helper used by PATCH/DELETE—normalizeWorkspaceId(wsId,
supabase)—at the start of the GET flow, await its result into a variable (e.g.,
normalizedWsId), and use that normalizedWsId in both the workspace_members
lookup (memberCheck) and the time_tracking_sessions query (instead of raw wsId);
ensure you handle any error or null return from normalizeWorkspaceId before
proceeding.
ℹ️ Review info
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (11)
apps/mobile/lib/data/repositories/time_tracker_repository.dartapps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dartapps/mobile/lib/features/time_tracker/cubit/time_tracker_state.dartapps/mobile/lib/features/time_tracker/widgets/edit_session_dialog.dartapps/mobile/lib/features/time_tracker/widgets/history_period_controls.dartapps/mobile/lib/features/time_tracker/widgets/history_stats_accordion.dartapps/mobile/lib/features/time_tracker/widgets/history_tab.dartapps/mobile/lib/features/time_tracker/widgets/stats_cards.dartapps/mobile/test/features/time_tracker/cubit/time_tracker_cubit_test.dartapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/helpers.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (13)
- GitHub Check: Comment on PR
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: Build Android APK (Development)
- GitHub Check: Build iOS (Development, simulator)
- GitHub Check: build / build
- GitHub Check: Run tests and collect coverage
- GitHub Check: Type Check
- GitHub Check: Verify generated types
- GitHub Check: cubic · AI code reviewer
- GitHub Check: Deploy-Preview
- GitHub Check: Deploy-Preview
- GitHub Check: Playwright E2E
- GitHub Check: Analyze (javascript-typescript)
🧰 Additional context used
📓 Path-based instructions (17)
apps/mobile/lib/**/*.dart
📄 CodeRabbit inference engine (CLAUDE.md)
apps/mobile/lib/**/*.dart: Useflutter_blocwith Cubits for feature state management in Flutter app
Usego_routerwith auth-aware redirects and ShellRoute for bottom tabs in Flutter app
Usesupabase_flutterwithflutter_secure_storagefor token persistence in Flutter app
Followvery_good_analysislinting rules in Flutter app
Useon Exception catch (e)instead of barecatchin Flutter code; avoid catchingErrorsubclasses
Capturecontextdependencies before firstawaitand guardBuildContextusage afterawaitwithif (!context.mounted) return;in Flutter
Never return from afinallyblock in Dart/Flutter code
UseFuture<void> Function()callbacks for mutation-driven UI actions in Flutter; await before closing dialogs
apps/mobile/lib/**/*.dart: When refactoring duplicated Flutter editable fields into shared widgets, preserve per-field validation and success messaging. Email fields should keep the@check and any email-specific success note (useTextInputType.emailAddressor an explicit parameter).
Useon Exception catch (e)(or specific exception types) instead of barecatch, avoid catchingErrorsubclasses likeTypeError, capturecontextdependencies before the firstawait, guardBuildContextusage afterawaitwithif (!context.mounted) return;, and do notreturninsidefinallyblocks.
The Flutter mobile app uses BLoC/Cubit state management,go_routernavigation, andsupabase_flutterfor auth. Build flavors:main_development.dart,main_staging.dart,main_production.dart.
For mutation-driven Flutter UI actions (approve/reject/update), useFuture<void> Function()callbacks (notVoidCallback), await them before closing dialogs/sheets, and surface failures in the UI (e.g.,SnackBar).
apps/mobile/lib/**/*.dart: Useon Exception catch (e)with specific exception types in Flutter; avoid untypedcatchblocks
Checkcontext.mountedbefore usingBuildContextafterawaitin Flutter to preven...
Files:
apps/mobile/lib/features/time_tracker/cubit/time_tracker_state.dartapps/mobile/lib/features/time_tracker/widgets/history_period_controls.dartapps/mobile/lib/features/time_tracker/widgets/history_stats_accordion.dartapps/mobile/lib/features/time_tracker/widgets/history_tab.dartapps/mobile/lib/features/time_tracker/widgets/stats_cards.dartapps/mobile/lib/data/repositories/time_tracker_repository.dartapps/mobile/lib/features/time_tracker/widgets/edit_session_dialog.dartapps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart
apps/mobile/**/*.dart
📄 CodeRabbit inference engine (GEMINI.md)
Always run
bun check:mobileafter changes toapps/mobile/. This runsdart format --set-exit-if-changed lib test && flutter analyze && flutter test. All three checks MUST pass. If it reports a Dart format failure, rerun to confirm a clean pass.
apps/mobile/**/*.dart: Rundart format --set-exit-if-changed lib testinapps/mobile/to verify code formatting; fix withdart format lib test
Runbun check:mobile(dart format + flutter analyze + flutter test) after making changes toapps/mobile/
Files:
apps/mobile/lib/features/time_tracker/cubit/time_tracker_state.dartapps/mobile/lib/features/time_tracker/widgets/history_period_controls.dartapps/mobile/test/features/time_tracker/cubit/time_tracker_cubit_test.dartapps/mobile/lib/features/time_tracker/widgets/history_stats_accordion.dartapps/mobile/lib/features/time_tracker/widgets/history_tab.dartapps/mobile/lib/features/time_tracker/widgets/stats_cards.dartapps/mobile/lib/data/repositories/time_tracker_repository.dartapps/mobile/lib/features/time_tracker/widgets/edit_session_dialog.dartapps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart
apps/mobile/test/**/*.dart
📄 CodeRabbit inference engine (CLAUDE.md)
Wrap Flutter widget tests rendering
shadcn_fluttercomponents withshad.ShadcnAppincludingshad.ShadcnLocalizations.delegateWrap Flutter widgets using
shadcn_flutterwithshad.ShadcnAppin tests to provide theme context
Files:
apps/mobile/test/features/time_tracker/cubit/time_tracker_cubit_test.dart
apps/mobile/**/test/**/*.dart
📄 CodeRabbit inference engine (GEMINI.md)
Wrap widgets that use
shadcn_flutterinshad.ShadcnAppwithshad.ShadcnLocalizations.delegatesoshad.Theme.of(context)resolves in tests.
Files:
apps/mobile/test/features/time_tracker/cubit/time_tracker_cubit_test.dart
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Use TypeScript with explicit return types for exported functions
Prefer interface for defining object shapes in TypeScript
Use TanStack Query (React Query) for ALL client-side data fetching - NEVER use useEffect for data fetching
Always includecache: 'no-store'in fetch() calls within TanStack Query queryFn to prevent dual-layer caching conflicts
Use Server Components by default; add'use client'only when necessary
Use@tuturuuu/ui/sonnerfor toast notifications instead of the deprecated@tuturuuu/ui/toast
Refactor files exceeding 400 LOC and components exceeding 200 LOC into smaller, focused units
Extract complex logic to separate utilities and custom hooks following single responsibility principle
Import database types frompackages/types/src/db.tsinstead of manually defining database types (only after user runs migrations viabun sb:pushand typegen viabun sb:typegen)
Always use stable array query keys with structure:[domain, subdomain?, paramsHash, version?]in TanStack Query
Use discriminated unions over enums for TypeScript type definitions
Narrowunknown/anyat boundaries; justify with comments if needed
Querypublic.user_private_detailstable for user email addresses instead ofpublic.userstable
Reference environment variables by name only in code - never echo values
Preferinterfaceovertypefor defining object shapes in TypeScript
Use camelCase for variable and function names in TypeScript/JavaScript
Use SCREAMING_SNAKE_CASE for constants
**/*.{ts,tsx}: Always prefer importing database types frompackages/types/src/db.ts. Never attempt to run migrations yourself - only after user runsbun sb:pushandbun sb:typegen.
Thepublic.userstable does NOT contain anpublic.user_private_details. When querying user email, always useuser_private_detailstable, notusers.
**/*.{ts,tsx}: Preferinterfacefor defining object shapes in TypeScript
N...
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/helpers.ts
apps/*/src/app/api/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
apps/*/src/app/api/**/*.ts: Use Zod for runtime validation of external inputs at API boundaries
Validate inputs early in API routes and return 400/401/403 for bad requests
Never leak stack traces to client; wrap external service calls and surface sanitized error messages
Log detailed errors server-side only; never expose sensitive information to client
Define and export runtime if edge is required:export const runtime = 'edge'
Use@tuturuuu/supabaseclient for authentication in API routes
UsenormalizeWorkspaceId()helper to resolve workspace ID parameters in API routes
Enforce auth on AI endpoints by getting user viacreateClient()and returning 401 if absent
Check feature flags inworkspace_secretsbefore serving AI features
Use Vercel AI SDKstreamObject/generateObjectwith selected model for AI operations
SetmaxDurationfor long-running AI operations
Never log secrets or raw provider responses in AI endpoints
apps/*/src/app/api/**/*.ts: UsecreateClient(request)pattern in API routes to support both Bearer token auth (mobile) and cookie auth (web)
Useexport const runtime = 'edge'to explicitly target edge runtime execution in Next.js API routes when appropriate
Use Zod for runtime validation of external inputs (API bodies, env-derived config) at system boundaries
Resolve workspace ID parameters (wsId) to UUIDs before database operations; usenormalizeWorkspaceId()for special identifiers like 'personal' and 'internal'
Validate inputs early in API routes; return 4xx errors before processing to fail fast
Addexport const runtime = 'edge'for routes that should execute on the edge runtime
Use Vercel AI SDKgenerateObject/streamObjectwith Zod schema for deterministic AI output structure
UsecreateAdminClient()(sbAdmin) when bulk operations need to bypass trigger permission checks (e.g., user merge); always validate workspace membership first
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/helpers.ts
apps/*/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
apps/*/src/**/*.{ts,tsx}: Extract functions used ≥2 times tosrc/lib/orsrc/utils/
Extract complex state logic to custom hooks insrc/hooks/
Extract pure computations as testable utilities
apps/*/src/**/*.{ts,tsx}: Never log secrets, API keys, tokens, or raw sensitive data to console; only reference environment variable names
Query thepublic.user_private_detailstable for user email addresses; thepublic.userstable does NOT contain an
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/helpers.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (GEMINI.md)
Always use
bun type-checkcommand for type checking. Do NOT usenpx tsgo,bunx tsgo, or other alternatives.
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/helpers.ts
**/*.{ts,tsx,js,jsx,json,yaml,yml}
📄 CodeRabbit inference engine (GEMINI.md)
Use Biome for linting and formatting. Run
bun format-and-lint:fixto automatically fix issues.
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/helpers.ts
**/{src,lib}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (GEMINI.md)
Files >400 LOC and components >200 LOC should be refactored into smaller, focused units. Apply best practices to ALL code, regardless of age. Follow single responsibility principle, extract utilities/hooks for complex logic, and leave code better than you found it.
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/helpers.ts
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (GEMINI.md)
apps/web/**/*.{ts,tsx}: Never useuseEffectfor data fetching. MANDATORY: Use TanStack Query (useQuery,useMutation,useInfiniteQuery) for all client-side data fetching.
Never use rawfetch()without TanStack Query wrapper in client components. Always use TanStack Query hooks for client-side data fetching.
Anyfetch()inside aqueryFnMUST includecache: 'no-store'to prevent browser HTTP cache from serving stale responses after TanStack Query invalidation.
Use stable array query keys following pattern:[domain, subdomain?, paramsHash, version?]. SetstaleTime> 0 for rarely-changing data. Implement optimistic updates with rollback. Use narrow invalidations, avoid globalinvalidateQueries().
Follow the Tailwind Dynamic Color Policy. Never use hard-coded color classes liketext-blue-500. Instead, use thedynamic-*tokens, e.g.,text-dynamic-blue.
Usesonnerfor toasts:import { toast } from '@tuturuuu/ui/sonner';. Avoid the deprecated@tuturuuu/ui/toast. Never use native browser dialogs likealert()orconfirm().
Use the dialog system for modals:import { Dialog, ... } from '@tuturuuu/ui/dialog';. Never use native browser dialogs.
Databasews_idcolumns ALWAYS store UUIDs. Route parameters (wsId) may contain special identifiers likepersonalorinternal. NEVER use rawwsIddirectly in database queries. UsenormalizeWorkspaceId(wsId)helper for API routes or acceptworkspaceprop in components.
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/helpers.ts
apps/web/src/app/**/*.{ts,tsx}
📄 CodeRabbit inference engine (GEMINI.md)
When adding new pages or routes, ALWAYS update the main navigation file (
apps/web/src/app/[locale]/(dashboard)/[wsId]/navigation.tsx). Add routes to both thealiasesarray andchildrennavigation items with proper icons, permission checks, and translation keys in bothen.jsonANDvi.json.
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/helpers.ts
apps/*/src/app/api/**/*.{ts,tsx}
📄 CodeRabbit inference engine (GEMINI.md)
Create new API routes in
apps/<app>/src/app/api/.... Use Supabase client wrappers for authentication and Zod for input validation.
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/helpers.ts
apps/web/src/app/api/**/*.{ts,tsx}
📄 CodeRabbit inference engine (GEMINI.md)
The mobile app connects to
apps/webAPI routes via Bearer token auth (not cookies). When updating web API routes for mobile features, maintain backward compatibility and usecreateClient(request)for Bearer token auth.
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/helpers.ts
apps/web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (GEMINI.md)
Extract utilities to
src/lib/, hooks tosrc/hooks/, and sub-components as needed. Apply best practices to BOTH old and new code. Follow single responsibility principle, use meaningful names, and eliminate duplication.
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/helpers.ts
**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
**/*.ts: Avoid usinganytype in TypeScript; narrowunknownat boundaries with explicit type guards
Use discriminated unions over enums unless runtime enum is required
Always usebun type-checkfor TypeScript type checking; do NOT usenpx tsgo,bunx tsgo, or other alternatives
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/helpers.ts
apps/mobile/lib/**/*{cubit,bloc}.dart
📄 CodeRabbit inference engine (GEMINI.md)
If a Flutter Cubit catches repository errors, rethrow after emitting error state so callers can handle failures locally.
Files:
apps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart
🧠 Learnings (28)
📚 Learning: 2026-02-23T06:51:05.456Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-23T06:51:05.456Z
Learning: Applies to **/*.tsx : Ensure interactive elements are keyboard-accessible with natural tab order; provide `aria-label` or text content for icon-only buttons
Applied to files:
apps/mobile/lib/features/time_tracker/widgets/history_period_controls.dart
📚 Learning: 2026-02-23T06:49:53.243Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: GEMINI.md:0-0
Timestamp: 2026-02-23T06:49:53.243Z
Learning: Applies to apps/mobile/**/test/**/*.dart : Wrap widgets that use `shadcn_flutter` in `shad.ShadcnApp` with `shad.ShadcnLocalizations.delegate` so `shad.Theme.of(context)` resolves in tests.
Applied to files:
apps/mobile/test/features/time_tracker/cubit/time_tracker_cubit_test.dartapps/mobile/lib/features/time_tracker/widgets/stats_cards.dartapps/mobile/lib/features/time_tracker/widgets/edit_session_dialog.dart
📚 Learning: 2026-02-23T06:49:53.243Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: GEMINI.md:0-0
Timestamp: 2026-02-23T06:49:53.243Z
Learning: Applies to apps/mobile/lib/**/*.dart : The Flutter mobile app uses BLoC/Cubit state management, `go_router` navigation, and `supabase_flutter` for auth. Build flavors: `main_development.dart`, `main_staging.dart`, `main_production.dart`.
Applied to files:
apps/mobile/test/features/time_tracker/cubit/time_tracker_cubit_test.dartapps/mobile/lib/features/time_tracker/widgets/history_tab.dartapps/mobile/lib/data/repositories/time_tracker_repository.dartapps/mobile/lib/features/time_tracker/widgets/edit_session_dialog.dartapps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart
📚 Learning: 2026-02-23T06:49:12.270Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-23T06:49:12.270Z
Learning: Applies to apps/mobile/lib/**/*.dart : Use `flutter_bloc` with Cubits for feature state management in Flutter app
Applied to files:
apps/mobile/test/features/time_tracker/cubit/time_tracker_cubit_test.dartapps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart
📚 Learning: 2026-02-23T06:51:05.456Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-23T06:51:05.456Z
Learning: Applies to apps/mobile/test/**/*.dart : Wrap Flutter widgets using `shadcn_flutter` with `shad.ShadcnApp` in tests to provide theme context
Applied to files:
apps/mobile/test/features/time_tracker/cubit/time_tracker_cubit_test.dartapps/mobile/lib/features/time_tracker/widgets/stats_cards.dartapps/mobile/lib/features/time_tracker/widgets/edit_session_dialog.dart
📚 Learning: 2026-02-23T06:49:12.271Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-23T06:49:12.271Z
Learning: Applies to apps/mobile/test/**/*.dart : Wrap Flutter widget tests rendering `shadcn_flutter` components with `shad.ShadcnApp` including `shad.ShadcnLocalizations.delegate`
Applied to files:
apps/mobile/test/features/time_tracker/cubit/time_tracker_cubit_test.dartapps/mobile/lib/features/time_tracker/widgets/edit_session_dialog.dart
📚 Learning: 2026-02-23T06:49:12.270Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-23T06:49:12.270Z
Learning: Applies to apps/mobile/lib/**/*.dart : Use `supabase_flutter` with `flutter_secure_storage` for token persistence in Flutter app
Applied to files:
apps/mobile/test/features/time_tracker/cubit/time_tracker_cubit_test.dartapps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart
📚 Learning: 2026-02-23T06:49:12.271Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-23T06:49:12.271Z
Learning: Applies to apps/mobile/lib/**/*.dart : Capture `context` dependencies before first `await` and guard `BuildContext` usage after `await` with `if (!context.mounted) return;` in Flutter
Applied to files:
apps/mobile/test/features/time_tracker/cubit/time_tracker_cubit_test.dartapps/mobile/lib/features/time_tracker/widgets/edit_session_dialog.dartapps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart
📚 Learning: 2026-02-23T06:49:53.243Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: GEMINI.md:0-0
Timestamp: 2026-02-23T06:49:53.243Z
Learning: Applies to apps/mobile/lib/**/*{cubit,bloc}.dart : If a Flutter Cubit catches repository errors, rethrow after emitting error state so callers can handle failures locally.
Applied to files:
apps/mobile/test/features/time_tracker/cubit/time_tracker_cubit_test.dartapps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart
📚 Learning: 2026-02-23T06:51:05.456Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-23T06:51:05.456Z
Learning: Applies to apps/mobile/**/*.dart : Run `dart format --set-exit-if-changed lib test` in `apps/mobile/` to verify code formatting; fix with `dart format lib test`
Applied to files:
apps/mobile/lib/features/time_tracker/widgets/history_stats_accordion.dart
📚 Learning: 2026-02-23T06:51:05.456Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-23T06:51:05.456Z
Learning: Applies to **/*.{tsx,css} : Avoid hard-coded palette utility classes (e.g., `text-blue-500`, `bg-purple-300/10`); use Tailwind dynamic color tokens instead (e.g., `text-dynamic-blue`, `bg-dynamic-purple/10`)
Applied to files:
apps/mobile/lib/features/time_tracker/widgets/stats_cards.dart
📚 Learning: 2026-02-23T06:49:53.243Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: GEMINI.md:0-0
Timestamp: 2026-02-23T06:49:53.243Z
Learning: Applies to apps/mobile/lib/**/*.dart : Use `on Exception catch (e)` (or specific exception types) instead of bare `catch`, avoid catching `Error` subclasses like `TypeError`, capture `context` dependencies before the first `await`, guard `BuildContext` usage after `await` with `if (!context.mounted) return;`, and do not `return` inside `finally` blocks.
Applied to files:
apps/mobile/lib/features/time_tracker/widgets/edit_session_dialog.dartapps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart
📚 Learning: 2026-02-23T06:51:05.456Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-23T06:51:05.456Z
Learning: Applies to apps/mobile/lib/**/*.dart : Check `context.mounted` before using `BuildContext` after `await` in Flutter to prevent errors
Applied to files:
apps/mobile/lib/features/time_tracker/widgets/edit_session_dialog.dart
📚 Learning: 2026-02-23T06:49:12.271Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-23T06:49:12.271Z
Learning: Applies to apps/mobile/lib/**/*.dart : Use `on Exception catch (e)` instead of bare `catch` in Flutter code; avoid catching `Error` subclasses
Applied to files:
apps/mobile/lib/features/time_tracker/widgets/edit_session_dialog.dartapps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart
📚 Learning: 2026-02-23T06:49:53.243Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: GEMINI.md:0-0
Timestamp: 2026-02-23T06:49:53.243Z
Learning: Applies to apps/mobile/lib/**/*.dart : For mutation-driven Flutter UI actions (approve/reject/update), use `Future<void> Function()` callbacks (not `VoidCallback`), await them before closing dialogs/sheets, and surface failures in the UI (e.g., `SnackBar`).
Applied to files:
apps/mobile/lib/features/time_tracker/widgets/edit_session_dialog.dartapps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart
📚 Learning: 2026-02-23T06:49:53.243Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: GEMINI.md:0-0
Timestamp: 2026-02-23T06:49:53.243Z
Learning: Applies to apps/mobile/lib/**/*.dart : When refactoring duplicated Flutter editable fields into shared widgets, preserve per-field validation and success messaging. Email fields should keep the `@` check and any email-specific success note (use `TextInputType.emailAddress` or an explicit parameter).
Applied to files:
apps/mobile/lib/features/time_tracker/widgets/edit_session_dialog.dart
📚 Learning: 2026-02-23T06:51:05.456Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-23T06:51:05.456Z
Learning: Applies to apps/mobile/lib/**/*.dart : Use `on Exception catch (e)` with specific exception types in Flutter; avoid untyped `catch` blocks
Applied to files:
apps/mobile/lib/features/time_tracker/widgets/edit_session_dialog.dart
📚 Learning: 2026-02-23T06:49:12.271Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-23T06:49:12.271Z
Learning: Applies to apps/mobile/lib/**/*.dart : Use `Future<void> Function()` callbacks for mutation-driven UI actions in Flutter; await before closing dialogs
Applied to files:
apps/mobile/lib/features/time_tracker/widgets/edit_session_dialog.dartapps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart
📚 Learning: 2026-02-23T06:51:05.456Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-23T06:51:05.456Z
Learning: Applies to apps/mobile/lib/**/*.dart : Capture dependencies from `context` (e.g., `context.read<T>()`) BEFORE the first `await` in Flutter async methods
Applied to files:
apps/mobile/lib/features/time_tracker/widgets/edit_session_dialog.dart
📚 Learning: 2026-02-23T06:51:05.456Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-23T06:51:05.456Z
Learning: Applies to apps/mobile/lib/**/*.dart : Use `Future<void> Function()` callbacks for widget actions depending on server mutations; await them and only close UI on success
Applied to files:
apps/mobile/lib/features/time_tracker/widgets/edit_session_dialog.dartapps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart
📚 Learning: 2026-02-23T06:49:12.270Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-23T06:49:12.270Z
Learning: Applies to apps/*/src/app/api/**/*.ts : Use `normalizeWorkspaceId()` helper to resolve workspace ID parameters in API routes
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📚 Learning: 2026-02-23T06:51:05.456Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-23T06:51:05.456Z
Learning: Applies to apps/*/src/app/api/**/*.ts : Resolve workspace ID parameters (`wsId`) to UUIDs before database operations; use `normalizeWorkspaceId()` for special identifiers like 'personal' and 'internal'
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📚 Learning: 2026-02-23T06:49:12.270Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-23T06:49:12.270Z
Learning: Applies to apps/**/src/components/**/*.{ts,tsx} : Use `workspace.id` directly from workspace object in client components instead of raw `wsId` parameter
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📚 Learning: 2026-02-23T06:49:53.243Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: GEMINI.md:0-0
Timestamp: 2026-02-23T06:49:53.243Z
Learning: Applies to apps/web/**/*.{ts,tsx} : Database `ws_id` columns ALWAYS store UUIDs. Route parameters (`wsId`) may contain special identifiers like `personal` or `internal`. NEVER use raw `wsId` directly in database queries. Use `normalizeWorkspaceId(wsId)` helper for API routes or accept `workspace` prop in components.
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📚 Learning: 2026-02-23T06:49:53.243Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: GEMINI.md:0-0
Timestamp: 2026-02-23T06:49:53.243Z
Learning: Applies to packages/ui/src/**/*.{ts,tsx} : Extract portable UI components with no `@/` imports to `packages/ui`. Pass `workspace` prop to child components instead of raw `wsId`.
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📚 Learning: 2026-02-23T06:51:05.456Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-23T06:51:05.456Z
Learning: Applies to apps/web/src/app/[locale]/(dashboard)/[wsId]/navigation.tsx : Update main navigation file when adding new routes: add entries to both `aliases` array and `children` navigation items with proper icons and permission checks
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📚 Learning: 2026-02-23T06:51:05.456Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-23T06:51:05.456Z
Learning: Applies to scripts/**/*.{js,ts} : Use workspace-relative paths in `apply_patch` (e.g., `apps/web/...`) instead of absolute Windows paths for better cross-platform compatibility
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📚 Learning: 2026-02-23T06:49:12.270Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-23T06:49:12.270Z
Learning: Applies to apps/*/src/app/api/**/*.ts : Use `tuturuuu/supabase` client for authentication in API routes
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
🔇 Additional comments (17)
apps/mobile/lib/features/time_tracker/widgets/stats_cards.dart (1)
104-116: Typography hierarchy update looks good.Value/label styling is clearer and consistent with other stat cards.
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts (1)
199-267: LGTM!DELETE handler correctly normalizes the workspace ID, verifies ownership before deletion, and uses the admin client for the actual delete.
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/helpers.ts (2)
173-281: LGTM onhandleStopAction.The threshold check, active-break closure, and branching between already-paused vs. running sessions are handled correctly with clear control flow.
428-504: LGTM onhandleResumeAction.Correctly closes the active break, creates a chained session with
parent_session_id, and returns break duration metadata.apps/mobile/lib/features/time_tracker/widgets/edit_session_dialog.dart (3)
243-258: Previouscontext.mountedanderror.toString()issues are resolved — LGTM.Both
if (!context.mounted) return;guards (Lines 243 and 256) and the localized error message (Line 264) correctly address the concerns raised in the prior review.
331-379:context.mountedguards after async pickers are in place — LGTM.Both
showDatePicker(Line 339) andshowTimePicker(Line 364) are correctly guarded withif (!context.mounted) return;before acting on their results.
66-70: No issues found. TheexceedsThresholdfunction properly handles nullableDateTimewith an explicit null guard that returnsfalse, allowing time editing whenstartTimeisnull.apps/mobile/lib/features/time_tracker/cubit/time_tracker_state.dart (1)
13-65: LGTM — new history state fields are well-structured.The sentinel pattern for
historyAnchorDateandhistoryNextCursor, theclear*flags forhistoryPeriodStatsandhistoryNextCursor, and the Equatablepropscoverage all look correct and consistent with the existing patterns in this state class.apps/mobile/lib/data/repositories/time_tracker_repository.dart (2)
248-268: LGTM —getHistorySessionsimplementation is clean.The cursor-based pagination with conditional query parameters follows the existing repository patterns well.
487-502: LGTM —getPeriodStatsendpoint integration looks good.apps/mobile/test/features/time_tracker/cubit/time_tracker_cubit_test.dart (1)
85-196: Good test coverage for the new history behavior.The precondition assertion at line 128 addresses the previously noted concern about test fragility. The pagination test using cursor-based mock differentiation is a clean approach.
apps/mobile/lib/features/time_tracker/widgets/history_period_controls.dart (1)
164-221: Good improvement:_SegmentTabnow has proper accessibility semantics.The use of
Semantics(button: true)withInkWellprovides keyboard focus, tap feedback, and semantic labeling.apps/mobile/lib/features/time_tracker/widgets/history_stats_accordion.dart (1)
1-77: Clean accordion implementation with proper state handling.The loading/null/data branching in
_Body, theAnimatedCrossFadefor expand/collapse, and the_formatSecondsfix for sub-minute durations all look good.apps/mobile/lib/features/time_tracker/widgets/history_tab.dart (1)
75-211: Good migration toCustomScrollViewwithSliverList.builderfor lazy rendering.The sliver-based layout addresses the previously noted performance concern about eager widget construction. The paginated loading, "load more" button, and end-of-list footer are well structured.
apps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart (3)
302-340: Good use of Dart 3 record.waitandthrowOnErrorpattern.The parallel fetch with type-safe destructuring and the conditional rethrow address both prior review comments. Clean implementation.
26-111:loadDatafires all futures first then awaits — good parallel execution.However, note that
_repo.getActiveBreak(line 67) is still sequential since it depends onrunningSession. This is unavoidable and correctly handled.
343-377:loadHistoryMorecorrectly guards against concurrent loads.The combined check for
historyHasMore,historyNextCursor,isHistoryLoadingMore, andisHistoryLoadingprevents redundant requests from scroll events.
…ation updates - Added support for customizable first day of the week in time tracking features. - Updated `loadData`, `goToPreviousPeriod`, `goToNextPeriod`, and related methods to accept `firstDayOfWeek` parameter. - Refactored `TimeTrackerPage` and `HistoryTab` to utilize the new first day of the week logic. - Improved localization for invalid duration and unknown date messages in the edit session dialog. - Introduced new API schemas and actions for session management, including edit and pause functionalities.
There was a problem hiding this comment.
4 issues found across 18 files (changes from recent commits).
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/resume.ts">
<violation number="1" location="apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/resume.ts:38">
P2: Swallowing the break update error means the API can resume a new session while the prior break stays open, leaving inconsistent time-tracking data. Consider failing the request when closing the break fails.</violation>
</file>
<file name="apps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart">
<violation number="1" location="apps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart:256">
P2: Defaulting firstDayOfWeek to Monday here overwrites the previously selected week start whenever this method is called without an explicit value, so locales using Sunday (or other starts) will silently revert to Monday. Use a nullable parameter and fall back to _historyFirstDayOfWeek instead of resetting it.</violation>
</file>
<file name="apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/edit.ts">
<violation number="1" location="apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/edit.ts:52">
P2: Validate start/end ordering when only one time field is updated, using the existing session times, or you can end up with start >= end and negative durations.</violation>
</file>
<file name="apps/mobile/lib/features/time_tracker/view/time_tracker_page.dart">
<violation number="1" location="apps/mobile/lib/features/time_tracker/view/time_tracker_page.dart:55">
P3: The same `_firstDayOfWeek` implementation is duplicated in three widgets. This is maintainability debt; consider extracting a shared helper (e.g., top-level function or extension) and reusing it to avoid divergent fixes.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (5)
apps/mobile/lib/l10n/arb/app_en.arb (1)
167-272:⚠️ Potential issue | 🟠 MajorRegenerate l10n outputs for ARB changes.
These new keys require updating
apps/mobile/lib/l10n/gen/*to keep generated localizations in sync; the generated files aren’t shown in this PR snippet. Please regenerate and include them.As per coding guidelines, "When updating
apps/mobile/lib/l10n/arb/*.arb, regenerate or updateapps/mobile/lib/l10n/gen/*so tracked outputs stay in sync."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/mobile/lib/l10n/arb/app_en.arb` around lines 167 - 272, The ARB additions in apps/mobile/lib/l10n/arb/app_en.arb were not propagated to the generated localization outputs in apps/mobile/lib/l10n/gen/*; regenerate the l10n outputs (e.g., run the project's localization generation command such as the Flutter gen-l10n or intl_utils generate task used by this repo) so that the new keys from app_en.arb appear in the generated files, then add the updated files under apps/mobile/lib/l10n/gen/ to this PR; ensure the generation covers the new placeholders like timerRequestActivityPageInfo and timerAllEditsRequireApproval.apps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart (1)
27-118:⚠️ Potential issue | 🟠 MajorRethrow repository errors after emitting error state.
loadDataandloadHistoryMorecatch and swallow exceptions, preventing callers from reacting to failures. Consider athrowOnErrorflag (likeloadHistoryInitial) or always rethrow after emitting the error state.As per coding guidelines, "If a Flutter Cubit catches repository errors, rethrow after emitting error state so callers can handle failures locally."💡 Example fix (conditional rethrow)
-Future<void> loadData( +Future<void> loadData( String wsId, String userId, { int? firstDayOfWeek, + bool throwOnError = false, }) async { ... - } on Exception catch (e) { + } on Exception catch (e) { emit( state.copyWith(status: TimeTrackerStatus.error, error: e.toString()), ); + if (throwOnError) rethrow; } }-Future<void> loadHistoryMore( +Future<void> loadHistoryMore( String wsId, String userId, { int? firstDayOfWeek, + bool throwOnError = false, }) async { ... - } on Exception catch (e) { - emit(state.copyWith(isHistoryLoadingMore: false, error: e.toString())); - } + } on Exception catch (e) { + emit(state.copyWith(isHistoryLoadingMore: false, error: e.toString())); + if (throwOnError) rethrow; + } }Also applies to: 400-444
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart` around lines 27 - 118, The catch block in loadData (and similarly in loadHistoryMore) swallows repository exceptions after emitting an error state; update these methods (loadData and loadHistoryMore) to rethrow the caught exception after calling emit (or add a throwOnError boolean parameter like loadHistoryInitial and conditionally rethrow) so callers can react to failures; ensure you reference the existing emit(state.copyWith(..., status: TimeTrackerStatus.error, error: e.toString())) call and then rethrow the Exception (or only rethrow when throwOnError is true) to preserve the current state emission behavior.apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts (3)
271-276: 🧹 Nitpick | 🔵 TrivialDefense-in-depth: scope the DELETE query with
ws_id.The ownership check at lines 259–265 already ensures the session belongs to the user and workspace, so this is safe in practice. However, adding
ws_idto the delete filter provides an extra guard against TOCTOU races or future refactors that might weaken the preceding check.Proposed fix
const { error } = await sbAdmin .from('time_tracking_sessions') .delete() - .eq('id', sessionId); + .eq('id', sessionId) + .eq('ws_id', normalizedWsId);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/src/app/api/v1/workspaces/`[wsId]/time-tracking/sessions/[sessionId]/route.ts around lines 271 - 276, The DELETE query currently removed a session by id only; add a defensive filter for workspace id to prevent TOCTOU or refactor regressions by adding an equality filter for ws_id on the same query. Locate the call chain created by createAdminClient() where sbAdmin.from('time_tracking_sessions').delete().eq('id', sessionId) is used and append an additional .eq('ws_id', wsId) to the delete query so the deletion is scoped to the current workspace.
24-63: 🧹 Nitpick | 🔵 TrivialConsider extracting the repeated auth+normalization boilerplate.
The pattern of
normalizeWorkspaceId→getUser→workspace_memberscheck is duplicated across all three handlers (~30 lines each). A shared helper likeauthenticateAndResolveWorkspace(request, wsId)returning{ user, normalizedWsId, supabase }or an error response would reduce duplication and ensure consistency. The file is within LOC limits, so this is optional.Also applies to: 97-131, 223-257
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/src/app/api/v1/workspaces/`[wsId]/time-tracking/sessions/[sessionId]/route.ts around lines 24 - 63, Extract the repeated auth+workspace normalization logic into a helper function (e.g., authenticateAndResolveWorkspace) that accepts (request, wsId) and returns a successful result object { supabase, user, normalizedWsId } or a NextResponse-like error; move the current sequence that calls createClient(request), normalizeWorkspaceId(wsId, supabase), supabase.auth.getUser(), and the workspace_members check into that helper, and update the three handlers to call authenticateAndResolveWorkspace(...) and early-return the error response if provided, otherwise use the returned supabase, user, and normalizedWsId for the rest of the handler logic.
219-287:⚠️ Potential issue | 🟠 MajorADD threshold/permission checks to DELETE handler to match PATCH enforcement.
The DELETE handler bypasses the
bypass_time_tracking_request_approvalpermission and threshold checks that PATCH enforces. When PATCH prevents editing sessions older than the workspace'smissed_entry_date_threshold, users can still delete those same sessions. If thresholds are meant to enforce an approval workflow for time record changes, deletion should be gated the same way.Consider adding threshold validation (via
checkSessionThreshold()) and permission checks to prevent deletion of sessions that exceed the threshold, unless the user hasbypass_time_tracking_request_approval.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/src/app/api/v1/workspaces/`[wsId]/time-tracking/sessions/[sessionId]/route.ts around lines 219 - 287, The DELETE handler currently removes a session without enforcing the same threshold/permission checks as PATCH; update the DELETE function to call the same threshold validator (e.g., checkSessionThreshold(sessionId, normalizedWsId, sbAdmin) or equivalent) after resolving normalizedWsId and before performing the admin delete, and also verify the user’s bypass permission (bypass_time_tracking_request_approval) on their workspace membership (same place you check memberCheck) so that if the session exceeds the workspace's missed_entry_date_threshold you return a 403 (or the same error/response PATCH uses) unless the member has the bypass permission; keep using the existing symbols (DELETE, normalizeWorkspaceId, createAdminClient, checkSessionThreshold, bypass_time_tracking_request_approval) and throw/return consistent errors when checks fail before calling sbAdmin.from('time_tracking_sessions').delete().
♻️ Duplicate comments (1)
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts (1)
167-209: Default case in switch — past concern addressed.The
defaultbranch now returns a 400 for unsupported actions, which prevents the handler from silently returningundefined. This addresses the previous review feedback.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/src/app/api/v1/workspaces/`[wsId]/time-tracking/sessions/[sessionId]/route.ts around lines 167 - 209, The switch on body.action can fall through and return undefined for unsupported actions; ensure the default branch returns a 400 JSON response using NextResponse.json({ error: `Unsupported action` }, { status: 400 }) in apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts (the switch that calls handleStopAction, handlePauseAction, handleResumeAction, handleEditAction) and confirm NextResponse is imported so callers never receive undefined.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/mobile/lib/features/time_tracker/view/time_tracker_page.dart`:
- Around line 55-69: The _firstDayOfWeek helper is duplicated across three
classes; extract it into a single shared private top-level function (or a small
extension) so all callers reuse the same implementation: move the weekdayByIndex
array and logic that uses MaterialLocalizations.of(context).firstDayOfWeekIndex
into one private function (e.g., _firstDayOfWeek(BuildContext)), replace the
three in-file copies with calls to that single function, and delete the
duplicate definitions; ensure all references (call sites) still import the file
or scope where the new shared helper lives.
In `@apps/mobile/lib/features/time_tracker/widgets/stats_cards.dart`:
- Around line 23-34: The cardWidth calculation can exceed the parent when
baseWidth < minCardWidth; update the computation in the LayoutBuilder so
cardWidth is clamped to not exceed baseWidth (e.g. compute cardWidth from
availableWidth/2 then apply math.min(baseWidth, ...) or clamp between
minCardWidth and baseWidth), referencing the existing variables cardWidth,
baseWidth, minCardWidth and availableWidth so the SizedBox(width: cardWidth)
never causes horizontal overflow.
In
`@apps/web/src/app/api/v1/workspaces/`[wsId]/time-tracking/sessions/[sessionId]/actions/pause.ts:
- Around line 87-104: The break insert can fail after pause_session_for_break
updated the session, leaving it paused with no break record; before returning
the 500 response you must roll back the session update: using sbAdmin and
sessionId, call the update on time_tracking_sessions to restore paused=false
(and reset paused_at, pause_expires_at and any status fields changed by
pause_session_for_break) and set updated_by to userId, log the rollback result,
then return the error response; alternatively wrap both session update and break
insert in a server-side transaction or RPC to make them atomic, but if keeping
the current flow ensure the explicit rollback happens on breakError.
In
`@apps/web/src/app/api/v1/workspaces/`[wsId]/time-tracking/sessions/[sessionId]/actions/resume.ts:
- Around line 30-40: The current resume flow updates the activeBreak row via
sbAdmin.from('time_tracking_breaks').update(...) and logs updateError but
continues to create a new running session, which may produce overlapping
periods; modify the resume handler so that if updateError is truthy (the update
that closes the break failed) you abort the resume path immediately (return an
error response or throw) instead of proceeding to create the session, or move
both operations into a single backend transaction/RPC so both succeed or fail
together; check the update result around activeBreak/ updateError/ resumeTime
and ensure session creation is only executed when the break-close succeeded.
In
`@apps/web/src/app/api/v1/workspaces/`[wsId]/time-tracking/sessions/[sessionId]/actions/stop.ts:
- Around line 61-71: The code currently logs but continues when closing an
active break fails (see activeBreak handling using
sbAdmin.from('time_tracking_breaks').update(...) which yields updateError),
leaving an open break while the session is stopped; change the stop flow to
abort on break-close failure by returning or throwing when updateError is
present (or use a single transactional/RPC endpoint to close the break and stop
the session atomically), ensuring the stop handler (the surrounding function
that calls this update and later stops the session) does not proceed if the
update to break_end (endTime) fails.
In
`@apps/web/src/app/api/v1/workspaces/`[wsId]/time-tracking/sessions/[sessionId]/route.ts:
- Around line 159-161: The error response uses Response.json(...) which is
inconsistent with the rest of the file; update the permission-check return to
use NextResponse.json(...) instead (i.e., replace Response.json with
NextResponse.json in the block that checks the permissions variable) so it
matches other error responses and existing imports (ensure NextResponse is
already referenced in this module).
In
`@apps/web/src/app/api/v1/workspaces/`[wsId]/time-tracking/sessions/[sessionId]/schemas.ts:
- Around line 36-44: The editActionSchema currently allows any string for
startTime and endTime; change those validators to z.string().datetime() (keeping
.optional() if still optional) so invalid datetime strings fail at the schema
boundary — update the editActionSchema's startTime and endTime entries to use
z.string().datetime().optional() (or z.string().datetime().nullable().optional()
if you need to allow explicit null) to enforce ISO/UTC/offset datetime formats
supported by Zod.
---
Outside diff comments:
In `@apps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart`:
- Around line 27-118: The catch block in loadData (and similarly in
loadHistoryMore) swallows repository exceptions after emitting an error state;
update these methods (loadData and loadHistoryMore) to rethrow the caught
exception after calling emit (or add a throwOnError boolean parameter like
loadHistoryInitial and conditionally rethrow) so callers can react to failures;
ensure you reference the existing emit(state.copyWith(..., status:
TimeTrackerStatus.error, error: e.toString())) call and then rethrow the
Exception (or only rethrow when throwOnError is true) to preserve the current
state emission behavior.
In `@apps/mobile/lib/l10n/arb/app_en.arb`:
- Around line 167-272: The ARB additions in apps/mobile/lib/l10n/arb/app_en.arb
were not propagated to the generated localization outputs in
apps/mobile/lib/l10n/gen/*; regenerate the l10n outputs (e.g., run the project's
localization generation command such as the Flutter gen-l10n or intl_utils
generate task used by this repo) so that the new keys from app_en.arb appear in
the generated files, then add the updated files under apps/mobile/lib/l10n/gen/
to this PR; ensure the generation covers the new placeholders like
timerRequestActivityPageInfo and timerAllEditsRequireApproval.
In
`@apps/web/src/app/api/v1/workspaces/`[wsId]/time-tracking/sessions/[sessionId]/route.ts:
- Around line 271-276: The DELETE query currently removed a session by id only;
add a defensive filter for workspace id to prevent TOCTOU or refactor
regressions by adding an equality filter for ws_id on the same query. Locate the
call chain created by createAdminClient() where
sbAdmin.from('time_tracking_sessions').delete().eq('id', sessionId) is used and
append an additional .eq('ws_id', wsId) to the delete query so the deletion is
scoped to the current workspace.
- Around line 24-63: Extract the repeated auth+workspace normalization logic
into a helper function (e.g., authenticateAndResolveWorkspace) that accepts
(request, wsId) and returns a successful result object { supabase, user,
normalizedWsId } or a NextResponse-like error; move the current sequence that
calls createClient(request), normalizeWorkspaceId(wsId, supabase),
supabase.auth.getUser(), and the workspace_members check into that helper, and
update the three handlers to call authenticateAndResolveWorkspace(...) and
early-return the error response if provided, otherwise use the returned
supabase, user, and normalizedWsId for the rest of the handler logic.
- Around line 219-287: The DELETE handler currently removes a session without
enforcing the same threshold/permission checks as PATCH; update the DELETE
function to call the same threshold validator (e.g.,
checkSessionThreshold(sessionId, normalizedWsId, sbAdmin) or equivalent) after
resolving normalizedWsId and before performing the admin delete, and also verify
the user’s bypass permission (bypass_time_tracking_request_approval) on their
workspace membership (same place you check memberCheck) so that if the session
exceeds the workspace's missed_entry_date_threshold you return a 403 (or the
same error/response PATCH uses) unless the member has the bypass permission;
keep using the existing symbols (DELETE, normalizeWorkspaceId,
createAdminClient, checkSessionThreshold, bypass_time_tracking_request_approval)
and throw/return consistent errors when checks fail before calling
sbAdmin.from('time_tracking_sessions').delete().
---
Duplicate comments:
In
`@apps/web/src/app/api/v1/workspaces/`[wsId]/time-tracking/sessions/[sessionId]/route.ts:
- Around line 167-209: The switch on body.action can fall through and return
undefined for unsupported actions; ensure the default branch returns a 400 JSON
response using NextResponse.json({ error: `Unsupported action` }, { status: 400
}) in
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
(the switch that calls handleStopAction, handlePauseAction, handleResumeAction,
handleEditAction) and confirm NextResponse is imported so callers never receive
undefined.
ℹ️ Review info
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
⛔ Files ignored due to path filters (3)
apps/mobile/lib/l10n/gen/app_localizations.dartis excluded by!**/gen/**apps/mobile/lib/l10n/gen/app_localizations_en.dartis excluded by!**/gen/**apps/mobile/lib/l10n/gen/app_localizations_vi.dartis excluded by!**/gen/**
📒 Files selected for processing (15)
apps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dartapps/mobile/lib/features/time_tracker/view/time_tracker_page.dartapps/mobile/lib/features/time_tracker/widgets/edit_session_dialog.dartapps/mobile/lib/features/time_tracker/widgets/history_tab.dartapps/mobile/lib/features/time_tracker/widgets/stats_cards.dartapps/mobile/lib/l10n/arb/app_en.arbapps/mobile/lib/l10n/arb/app_vi.arbapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/edit.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/pause.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/resume.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/stop.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/helpers.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/schemas.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/threshold.ts
- Introduced compact navigation label handling and adjusted icon sizes for better UI consistency. - Refactored pointer event handling to enhance responsiveness in the apps tab. - Added a new method to streamline the opening of the apps drawer from the apps tab. - Updated the TimeTrackerCubit to include error handling options and improved session management logic. - Enhanced API routes for time tracking sessions with better authentication and error handling. - Updated schemas to enforce ISO date formats for session timestamps.
There was a problem hiding this comment.
1 issue found across 9 files (changes from recent commits).
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/mobile/lib/features/time_tracker/view/time_tracker_page.dart">
<violation number="1" location="apps/mobile/lib/features/time_tracker/view/time_tracker_page.dart:73">
P2: Guard the post-frame callback with `mounted` before using `context` to avoid accessing a disposed State.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
Actionable comments posted: 11
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/mobile/lib/features/shell/view/shell_page.dart (1)
263-307:⚠️ Potential issue | 🟡 MinorUse
context.mountedafter awaits (guideline).After
awaitinLine 265andLine 304, the checks usemountedinstead ofcontext.mounted. The guideline explicitly requirescontext.mountedwhen guarding BuildContext usage after awaits.✅ Proposed update
- if (!mounted) { + if (!context.mounted) { return; }- if (!mounted) { + if (!context.mounted) { return; }As per coding guidelines: "Capture dependencies from
contextbefore the firstawaitin Flutter async methods and guardBuildContextusage afterawaitwithif (!context.mounted) return;."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/mobile/lib/features/shell/view/shell_page.dart` around lines 263 - 307, The async methods _openAppsDrawerFromAppsTab and _onItemTapped capture context-derived values (e.g., appTabCubit via context.read<AppTabCubit>() and state.hasSelection/appRoute) but currently use mounted checks after awaits; change those guards to if (!context.mounted) return; and ensure any context reads needed after awaits are captured before the first await (e.g., read appTabCubit and compute appRoute/state-dependent values before calling await appTabCubit.clearSelection() or any other await). Specifically update the mounted checks in _openAppsDrawerFromAppsTab and both await-return guards in _onItemTapped to use context.mounted and pre-fetch context dependencies before awaits.
♻️ Duplicate comments (2)
apps/mobile/lib/features/time_tracker/widgets/stats_cards.dart (2)
23-36:cardWidthclamping correctly applied.The
math.min(baseWidth, math.max(minCardWidth, ...))guard from the previous review is now in place — no overflow can occur even when the container is narrower thanminCardWidth.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/mobile/lib/features/time_tracker/widgets/stats_cards.dart` around lines 23 - 36, The cardWidth calculation in the LayoutBuilder already properly clamps widths to avoid overflow by using math.min(baseWidth, math.max(minCardWidth, availableWidth / 2)); no further fix is required—keep the current logic in the builder (variables: LayoutBuilder, cardWidth, availableWidth, minCardWidth, fallbackTotalWidth) as-is and merge the change.
116-133: Typography hierarchy correctly implemented.
theme.typography.p(bold) for the value andtheme.typography.textSmall(muted) for the label now align with the_OverviewCardpattern — consistent with the previous review suggestion (addressed in commit 7c9b8cc).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/mobile/lib/features/time_tracker/widgets/stats_cards.dart` around lines 116 - 133, The PR already applies the correct typography (using theme.typography.p for the value and theme.typography.textSmall for the label matching the _OverviewCard pattern), so remove the duplicate review tag/comment and mark the change as approved; no code changes required — just resolve the duplicate_comment/approve_code_changes metadata on the review thread referencing the Text(value...) and Text(label...) usages and close the review.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/mobile/lib/features/shell/view/shell_page.dart`:
- Around line 218-230: The Apps nav item uses _appsKey when useGlobalKey is
false but selectedKey is computed by _keyForIndex which always returns
_appsTabKey, so the Apps tab never shows selected in non-global mode; update the
selection logic by making _keyForIndex aware of useGlobalKey (add a parameter or
read the same flag) and return _appsKey when building for non-global layout, or
alternatively override selectedKey in _buildSideNavLayout to return _appsKey
when useGlobalKey is false (keep _keyForIndex behavior unchanged for other
callers). Ensure references to _appsKey, _appsTabKey, useGlobalKey, _keyForIndex
and _buildSideNavLayout are updated so the selectedKey matches the actual
NavigationItem key used.
In `@apps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart`:
- Around line 260-261: setHistoryViewMode updates _historyFirstDayOfWeek but
then returns early when state.historyViewMode == viewMode, preventing
loadHistoryInitial from running if only the locale's firstDayOfWeek changed.
Modify setHistoryViewMode so that after assigning _historyFirstDayOfWeek it
still calls loadHistoryInitial when the first-day-of-week changed even if
viewMode is unchanged: compare the new firstDayOfWeek to the previous value and
invoke loadHistoryInitial() (or re-run the logic that refreshes the displayed
range) whenever the first-day-of-week differs; keep the existing early return
only when neither the view mode nor the first-day-of-week changed.
- Around line 738-742: The _historyPreferencesLoaded boolean is set
synchronously before awaiting SharedPreferences.getInstance(), causing
concurrent callers to short-circuit and read stale state; replace that flag with
a Future<void>? (e.g. _historyPreferencesLoadFuture) so multiple callers await
the same in-progress work: if the future is null, assign it to an async function
that awaits SharedPreferences.getInstance(), reads
prefs.getBool(_historyStatsAccordionPrefsKey) ?? false, sets
isHistoryStatsAccordionOpen and emits the state, then clears the future on
completion; remove the old _historyPreferencesLoaded field and keep the static
const _historyStatsAccordionPrefsKey as-is.
In `@apps/mobile/lib/features/time_tracker/view/time_tracker_page.dart`:
- Around line 73-85: Add a mounted guard as the first statement inside the
WidgetsBinding.instance.addPostFrameCallback closure to avoid using a stale
BuildContext; i.e., inside that closure check "if (!mounted) return;" before
calling context.read<WorkspaceCubit>() or supabase.auth.currentUser, and capture
any context-dependent values (wsId via WorkspaceCubit.state.currentWorkspace?.id
and userId via supabase.auth.currentUser?.id) before any awaited work, then call
TimeTrackerCubit.loadData(wsId, userId, firstDayOfWeek:
_firstDayOfWeek(context)) only after the mounted check.
In `@apps/mobile/lib/features/time_tracker/widgets/stats_cards.dart`:
- Around line 38-72: The Wrap causes uneven card heights because _StatCard only
renders the icon when provided; update _StatCard.build to always reserve the
icon row space (e.g., render a fixed-size icon container or an Icon with
transparent color / a SizedBox matching the icon+padding height) so cards
without an icon (like "This Month") keep the same intrinsic height as those with
icon (like the "Streak" card); ensure the placeholder uses the same
dimensions/alignment as the real icon and lives in the same widget tree position
as the existing icon parameter.
In
`@apps/web/src/app/api/v1/workspaces/`[wsId]/time-tracking/sessions/[sessionId]/actions/pause.ts:
- Around line 75-121: The rollback after a failed break insert doesn't restore
the session's pending_approval flag set by the RPC pause_session_for_break;
update the rollback in the pause action to also reset pending_approval (the RPC
call is sbAdmin.rpc('pause_session_for_break') and the rollback runs via
sbAdmin.from('time_tracking_sessions').update(...).eq('id', sessionId)) —
include pending_approval: false (or the original previous value if available) in
that .update payload so the session is fully restored when break creation fails;
long-term, consider moving pause_session_for_break + break insert into a single
server-side transaction/RPC for atomicity.
In
`@apps/web/src/app/api/v1/workspaces/`[wsId]/time-tracking/sessions/[sessionId]/actions/resume.ts:
- Line 53: The server-side insert uses a hardcoded English fallback for the
title (title: session.title ?? 'Work session'); remove the literal fallback so
the server does not inject a locale-specific string—either omit the fallback
entirely and write title: session.title (letting the client provide a title) or
replace it with a locale-neutral token/constant; update the resume
handler/insert logic that sets the title field accordingly (reference: the title
property in the resume action where session.title is used).
In
`@apps/web/src/app/api/v1/workspaces/`[wsId]/time-tracking/sessions/[sessionId]/route.ts:
- Around line 99-102: The PATCH handler currently treats a missing permission
from getPermissions({ wsId: normalizedWsId, request }) as a 404; change it to
mirror the DELETE behavior by returning a 403 with a "Permissions not found"
message. Locate the permissions check in the PATCH flow (the variable
permissions and the NextResponse.json call) and replace the 404 response
payload/status with the same 403 response used in the DELETE branch so both
handlers consistently signal permission failures.
- Line 107: The unsafe cast "const sessionRecord = session as SessionRecord"
masks possible type drift between the Supabase-generated type and the
hand-maintained SessionRecord; replace this by either (preferred)
importing/deriving the DB type from packages/types/src/db.ts so the handler uses
the authoritative Supabase-derived type (remove the cast), or (short‑term)
validate and narrow the specific fields you rely on (e.g., check/convert
session.is_running to a non-null boolean and coerce/map other fields into a
validated SessionRecord object) before using them (and add a brief comment
justifying any remaining cast). Also update any downstream use sites (e.g.,
stop.ts where you read session.is_running) to use the validated/mapped object
rather than the raw cast.
- Around line 218-247: The code currently calls normalizeWorkspaceId before
authenticating the user, causing unnecessary DB work for unauthenticated
requests; reorder so you call supabase.auth.getUser() (from the supabase client
returned by createClient(request)) immediately after createClient and return the
401 on authError or missing user, then call normalizeWorkspaceId(wsId, supabase)
and keep the existing 404 handling for missing/failed normalization; ensure you
still use the same supabase instance and preserve the existing NextResponse.json
error responses and variable name normalizedWsId.
In
`@apps/web/src/app/api/v1/workspaces/`[wsId]/time-tracking/sessions/[sessionId]/schemas.ts:
- Around line 57-70: Replace the hand-written SessionRecord interface with the
canonical DB row type from packages/types/src/db.ts: remove the manual
SessionRecord declaration in schemas.ts and import the appropriate type that
represents the time_tracking_sessions row (use the exported name that maps to
time_tracking_sessions in packages/types, then use that type wherever
SessionRecord was used, including any casts). Ensure all references to
SessionRecord are updated to the imported DB type to prevent schema drift.
---
Outside diff comments:
In `@apps/mobile/lib/features/shell/view/shell_page.dart`:
- Around line 263-307: The async methods _openAppsDrawerFromAppsTab and
_onItemTapped capture context-derived values (e.g., appTabCubit via
context.read<AppTabCubit>() and state.hasSelection/appRoute) but currently use
mounted checks after awaits; change those guards to if (!context.mounted)
return; and ensure any context reads needed after awaits are captured before the
first await (e.g., read appTabCubit and compute appRoute/state-dependent values
before calling await appTabCubit.clearSelection() or any other await).
Specifically update the mounted checks in _openAppsDrawerFromAppsTab and both
await-return guards in _onItemTapped to use context.mounted and pre-fetch
context dependencies before awaits.
---
Duplicate comments:
In `@apps/mobile/lib/features/time_tracker/widgets/stats_cards.dart`:
- Around line 23-36: The cardWidth calculation in the LayoutBuilder already
properly clamps widths to avoid overflow by using math.min(baseWidth,
math.max(minCardWidth, availableWidth / 2)); no further fix is required—keep the
current logic in the builder (variables: LayoutBuilder, cardWidth,
availableWidth, minCardWidth, fallbackTotalWidth) as-is and merge the change.
- Around line 116-133: The PR already applies the correct typography (using
theme.typography.p for the value and theme.typography.textSmall for the label
matching the _OverviewCard pattern), so remove the duplicate review tag/comment
and mark the change as approved; no code changes required — just resolve the
duplicate_comment/approve_code_changes metadata on the review thread referencing
the Text(value...) and Text(label...) usages and close the review.
ℹ️ Review info
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (9)
apps/mobile/lib/features/shell/view/shell_page.dartapps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dartapps/mobile/lib/features/time_tracker/view/time_tracker_page.dartapps/mobile/lib/features/time_tracker/widgets/stats_cards.dartapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/pause.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/resume.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/stop.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/schemas.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
- GitHub Check: build / build
- GitHub Check: Build iOS (Development, simulator)
- GitHub Check: Build Android APK (Development)
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: Deploy-Preview
- GitHub Check: Playwright E2E
- GitHub Check: Deploy-Preview
- GitHub Check: Verify generated types
- GitHub Check: Run tests and collect coverage
- GitHub Check: cubic · AI code reviewer
- GitHub Check: Type Check
- GitHub Check: Analyze (javascript-typescript)
🧰 Additional context used
📓 Path-based instructions (15)
apps/mobile/lib/**/*.dart
📄 CodeRabbit inference engine (CLAUDE.md)
apps/mobile/lib/**/*.dart: Useflutter_blocwith Cubits for feature state management in Flutter app
Usego_routerwith auth-aware redirects and ShellRoute for bottom tabs in Flutter app
Usesupabase_flutterwithflutter_secure_storagefor token persistence in Flutter app
Followvery_good_analysislinting rules in Flutter app
Useon Exception catch (e)instead of barecatchin Flutter code; avoid catchingErrorsubclasses
Capturecontextdependencies before firstawaitand guardBuildContextusage afterawaitwithif (!context.mounted) return;in Flutter
Never return from afinallyblock in Dart/Flutter code
UseFuture<void> Function()callbacks for mutation-driven UI actions in Flutter; await before closing dialogs
apps/mobile/lib/**/*.dart: When refactoring duplicated Flutter editable fields into shared widgets, preserve per-field validation and success messaging. Email fields should keep the@check and any email-specific success note (useTextInputType.emailAddressor an explicit parameter).
Useon Exception catch (e)(or specific exception types) instead of barecatch, avoid catchingErrorsubclasses likeTypeError, capturecontextdependencies before the firstawait, guardBuildContextusage afterawaitwithif (!context.mounted) return;, and do notreturninsidefinallyblocks.
The Flutter mobile app uses BLoC/Cubit state management,go_routernavigation, andsupabase_flutterfor auth. Build flavors:main_development.dart,main_staging.dart,main_production.dart.
For mutation-driven Flutter UI actions (approve/reject/update), useFuture<void> Function()callbacks (notVoidCallback), await them before closing dialogs/sheets, and surface failures in the UI (e.g.,SnackBar).
apps/mobile/lib/**/*.dart: Useon Exception catch (e)with specific exception types in Flutter; avoid untypedcatchblocks
Checkcontext.mountedbefore usingBuildContextafterawaitin Flutter to preven...
Files:
apps/mobile/lib/features/time_tracker/widgets/stats_cards.dartapps/mobile/lib/features/time_tracker/view/time_tracker_page.dartapps/mobile/lib/features/shell/view/shell_page.dartapps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart
apps/mobile/**/*.dart
📄 CodeRabbit inference engine (GEMINI.md)
Always run
bun check:mobileafter changes toapps/mobile/. This runsdart format --set-exit-if-changed lib test && flutter analyze && flutter test. All three checks MUST pass. If it reports a Dart format failure, rerun to confirm a clean pass.
apps/mobile/**/*.dart: Rundart format --set-exit-if-changed lib testinapps/mobile/to verify code formatting; fix withdart format lib test
Runbun check:mobile(dart format + flutter analyze + flutter test) after making changes toapps/mobile/
Files:
apps/mobile/lib/features/time_tracker/widgets/stats_cards.dartapps/mobile/lib/features/time_tracker/view/time_tracker_page.dartapps/mobile/lib/features/shell/view/shell_page.dartapps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Use TypeScript with explicit return types for exported functions
Prefer interface for defining object shapes in TypeScript
Use TanStack Query (React Query) for ALL client-side data fetching - NEVER use useEffect for data fetching
Always includecache: 'no-store'in fetch() calls within TanStack Query queryFn to prevent dual-layer caching conflicts
Use Server Components by default; add'use client'only when necessary
Use@tuturuuu/ui/sonnerfor toast notifications instead of the deprecated@tuturuuu/ui/toast
Refactor files exceeding 400 LOC and components exceeding 200 LOC into smaller, focused units
Extract complex logic to separate utilities and custom hooks following single responsibility principle
Import database types frompackages/types/src/db.tsinstead of manually defining database types (only after user runs migrations viabun sb:pushand typegen viabun sb:typegen)
Always use stable array query keys with structure:[domain, subdomain?, paramsHash, version?]in TanStack Query
Use discriminated unions over enums for TypeScript type definitions
Narrowunknown/anyat boundaries; justify with comments if needed
Querypublic.user_private_detailstable for user email addresses instead ofpublic.userstable
Reference environment variables by name only in code - never echo values
Preferinterfaceovertypefor defining object shapes in TypeScript
Use camelCase for variable and function names in TypeScript/JavaScript
Use SCREAMING_SNAKE_CASE for constants
**/*.{ts,tsx}: Always prefer importing database types frompackages/types/src/db.ts. Never attempt to run migrations yourself - only after user runsbun sb:pushandbun sb:typegen.
Thepublic.userstable does NOT contain anpublic.user_private_details. When querying user email, always useuser_private_detailstable, notusers.
**/*.{ts,tsx}: Preferinterfacefor defining object shapes in TypeScript
N...
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/stop.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/pause.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/resume.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/schemas.ts
apps/*/src/app/api/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
apps/*/src/app/api/**/*.ts: Use Zod for runtime validation of external inputs at API boundaries
Validate inputs early in API routes and return 400/401/403 for bad requests
Never leak stack traces to client; wrap external service calls and surface sanitized error messages
Log detailed errors server-side only; never expose sensitive information to client
Define and export runtime if edge is required:export const runtime = 'edge'
Use@tuturuuu/supabaseclient for authentication in API routes
UsenormalizeWorkspaceId()helper to resolve workspace ID parameters in API routes
Enforce auth on AI endpoints by getting user viacreateClient()and returning 401 if absent
Check feature flags inworkspace_secretsbefore serving AI features
Use Vercel AI SDKstreamObject/generateObjectwith selected model for AI operations
SetmaxDurationfor long-running AI operations
Never log secrets or raw provider responses in AI endpoints
apps/*/src/app/api/**/*.ts: UsecreateClient(request)pattern in API routes to support both Bearer token auth (mobile) and cookie auth (web)
Useexport const runtime = 'edge'to explicitly target edge runtime execution in Next.js API routes when appropriate
Use Zod for runtime validation of external inputs (API bodies, env-derived config) at system boundaries
Resolve workspace ID parameters (wsId) to UUIDs before database operations; usenormalizeWorkspaceId()for special identifiers like 'personal' and 'internal'
Validate inputs early in API routes; return 4xx errors before processing to fail fast
Addexport const runtime = 'edge'for routes that should execute on the edge runtime
Use Vercel AI SDKgenerateObject/streamObjectwith Zod schema for deterministic AI output structure
UsecreateAdminClient()(sbAdmin) when bulk operations need to bypass trigger permission checks (e.g., user merge); always validate workspace membership first
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/stop.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/pause.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/resume.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/schemas.ts
apps/*/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
apps/*/src/**/*.{ts,tsx}: Extract functions used ≥2 times tosrc/lib/orsrc/utils/
Extract complex state logic to custom hooks insrc/hooks/
Extract pure computations as testable utilities
apps/*/src/**/*.{ts,tsx}: Never log secrets, API keys, tokens, or raw sensitive data to console; only reference environment variable names
Query thepublic.user_private_detailstable for user email addresses; thepublic.userstable does NOT contain an
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/stop.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/pause.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/resume.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/schemas.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (GEMINI.md)
Always use
bun type-checkcommand for type checking. Do NOT usenpx tsgo,bunx tsgo, or other alternatives.
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/stop.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/pause.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/resume.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/schemas.ts
**/*.{ts,tsx,js,jsx,json,yaml,yml}
📄 CodeRabbit inference engine (GEMINI.md)
Use Biome for linting and formatting. Run
bun format-and-lint:fixto automatically fix issues.
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/stop.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/pause.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/resume.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/schemas.ts
**/{src,lib}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (GEMINI.md)
Files >400 LOC and components >200 LOC should be refactored into smaller, focused units. Apply best practices to ALL code, regardless of age. Follow single responsibility principle, extract utilities/hooks for complex logic, and leave code better than you found it.
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/stop.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/pause.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/resume.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/schemas.ts
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (GEMINI.md)
apps/web/**/*.{ts,tsx}: Never useuseEffectfor data fetching. MANDATORY: Use TanStack Query (useQuery,useMutation,useInfiniteQuery) for all client-side data fetching.
Never use rawfetch()without TanStack Query wrapper in client components. Always use TanStack Query hooks for client-side data fetching.
Anyfetch()inside aqueryFnMUST includecache: 'no-store'to prevent browser HTTP cache from serving stale responses after TanStack Query invalidation.
Use stable array query keys following pattern:[domain, subdomain?, paramsHash, version?]. SetstaleTime> 0 for rarely-changing data. Implement optimistic updates with rollback. Use narrow invalidations, avoid globalinvalidateQueries().
Follow the Tailwind Dynamic Color Policy. Never use hard-coded color classes liketext-blue-500. Instead, use thedynamic-*tokens, e.g.,text-dynamic-blue.
Usesonnerfor toasts:import { toast } from '@tuturuuu/ui/sonner';. Avoid the deprecated@tuturuuu/ui/toast. Never use native browser dialogs likealert()orconfirm().
Use the dialog system for modals:import { Dialog, ... } from '@tuturuuu/ui/dialog';. Never use native browser dialogs.
Databasews_idcolumns ALWAYS store UUIDs. Route parameters (wsId) may contain special identifiers likepersonalorinternal. NEVER use rawwsIddirectly in database queries. UsenormalizeWorkspaceId(wsId)helper for API routes or acceptworkspaceprop in components.
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/stop.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/pause.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/resume.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/schemas.ts
apps/web/src/app/**/*.{ts,tsx}
📄 CodeRabbit inference engine (GEMINI.md)
When adding new pages or routes, ALWAYS update the main navigation file (
apps/web/src/app/[locale]/(dashboard)/[wsId]/navigation.tsx). Add routes to both thealiasesarray andchildrennavigation items with proper icons, permission checks, and translation keys in bothen.jsonANDvi.json.
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/stop.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/pause.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/resume.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/schemas.ts
apps/*/src/app/api/**/*.{ts,tsx}
📄 CodeRabbit inference engine (GEMINI.md)
Create new API routes in
apps/<app>/src/app/api/.... Use Supabase client wrappers for authentication and Zod for input validation.
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/stop.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/pause.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/resume.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/schemas.ts
apps/web/src/app/api/**/*.{ts,tsx}
📄 CodeRabbit inference engine (GEMINI.md)
The mobile app connects to
apps/webAPI routes via Bearer token auth (not cookies). When updating web API routes for mobile features, maintain backward compatibility and usecreateClient(request)for Bearer token auth.
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/stop.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/pause.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/resume.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/schemas.ts
apps/web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (GEMINI.md)
Extract utilities to
src/lib/, hooks tosrc/hooks/, and sub-components as needed. Apply best practices to BOTH old and new code. Follow single responsibility principle, use meaningful names, and eliminate duplication.
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/stop.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/pause.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/resume.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/schemas.ts
**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
**/*.ts: Avoid usinganytype in TypeScript; narrowunknownat boundaries with explicit type guards
Use discriminated unions over enums unless runtime enum is required
Always usebun type-checkfor TypeScript type checking; do NOT usenpx tsgo,bunx tsgo, or other alternatives
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/stop.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/pause.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/resume.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.tsapps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/schemas.ts
apps/mobile/lib/**/*{cubit,bloc}.dart
📄 CodeRabbit inference engine (GEMINI.md)
If a Flutter Cubit catches repository errors, rethrow after emitting error state so callers can handle failures locally.
Files:
apps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart
🧠 Learnings (31)
📚 Learning: 2026-02-23T06:51:05.456Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-23T06:51:05.456Z
Learning: Applies to **/*.{tsx,css} : Avoid hard-coded palette utility classes (e.g., `text-blue-500`, `bg-purple-300/10`); use Tailwind dynamic color tokens instead (e.g., `text-dynamic-blue`, `bg-dynamic-purple/10`)
Applied to files:
apps/mobile/lib/features/time_tracker/widgets/stats_cards.dart
📚 Learning: 2026-02-23T06:51:05.456Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-23T06:51:05.456Z
Learning: Applies to apps/mobile/test/**/*.dart : Wrap Flutter widgets using `shadcn_flutter` with `shad.ShadcnApp` in tests to provide theme context
Applied to files:
apps/mobile/lib/features/time_tracker/widgets/stats_cards.dart
📚 Learning: 2026-02-23T06:49:53.243Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: GEMINI.md:0-0
Timestamp: 2026-02-23T06:49:53.243Z
Learning: Applies to apps/mobile/lib/**/*.dart : The Flutter mobile app uses BLoC/Cubit state management, `go_router` navigation, and `supabase_flutter` for auth. Build flavors: `main_development.dart`, `main_staging.dart`, `main_production.dart`.
Applied to files:
apps/mobile/lib/features/time_tracker/widgets/stats_cards.dartapps/mobile/lib/features/time_tracker/view/time_tracker_page.dartapps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart
📚 Learning: 2026-02-23T06:49:53.243Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: GEMINI.md:0-0
Timestamp: 2026-02-23T06:49:53.243Z
Learning: Applies to apps/mobile/**/test/**/*.dart : Wrap widgets that use `shadcn_flutter` in `shad.ShadcnApp` with `shad.ShadcnLocalizations.delegate` so `shad.Theme.of(context)` resolves in tests.
Applied to files:
apps/mobile/lib/features/time_tracker/widgets/stats_cards.dart
📚 Learning: 2026-02-23T06:49:12.271Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-23T06:49:12.271Z
Learning: Applies to apps/*/src/app/api/**/*.ts : Set `maxDuration` for long-running AI operations
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/stop.ts
📚 Learning: 2026-02-23T06:49:12.271Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-23T06:49:12.271Z
Learning: Applies to apps/mobile/lib/**/*.dart : Capture `context` dependencies before first `await` and guard `BuildContext` usage after `await` with `if (!context.mounted) return;` in Flutter
Applied to files:
apps/mobile/lib/features/time_tracker/view/time_tracker_page.dartapps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart
📚 Learning: 2026-02-23T06:49:12.270Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-23T06:49:12.270Z
Learning: Applies to apps/mobile/lib/**/*.dart : Use `flutter_bloc` with Cubits for feature state management in Flutter app
Applied to files:
apps/mobile/lib/features/time_tracker/view/time_tracker_page.dartapps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart
📚 Learning: 2026-02-23T06:49:53.243Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: GEMINI.md:0-0
Timestamp: 2026-02-23T06:49:53.243Z
Learning: Applies to apps/mobile/lib/**/*.dart : When refactoring duplicated Flutter editable fields into shared widgets, preserve per-field validation and success messaging. Email fields should keep the `@` check and any email-specific success note (use `TextInputType.emailAddress` or an explicit parameter).
Applied to files:
apps/mobile/lib/features/time_tracker/view/time_tracker_page.dart
📚 Learning: 2026-02-23T06:49:12.271Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-23T06:49:12.271Z
Learning: Applies to apps/mobile/test/**/*.dart : Wrap Flutter widget tests rendering `shadcn_flutter` components with `shad.ShadcnApp` including `shad.ShadcnLocalizations.delegate`
Applied to files:
apps/mobile/lib/features/time_tracker/view/time_tracker_page.dart
📚 Learning: 2026-02-23T06:49:12.270Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-23T06:49:12.270Z
Learning: Applies to apps/*/src/app/api/**/*.ts : Use `normalizeWorkspaceId()` helper to resolve workspace ID parameters in API routes
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📚 Learning: 2026-02-23T06:49:12.270Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-23T06:49:12.270Z
Learning: Applies to apps/**/src/components/**/*.{ts,tsx} : Use `workspace.id` directly from workspace object in client components instead of raw `wsId` parameter
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📚 Learning: 2026-02-23T06:51:05.456Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-23T06:51:05.456Z
Learning: Applies to apps/*/src/app/api/**/*.ts : Resolve workspace ID parameters (`wsId`) to UUIDs before database operations; use `normalizeWorkspaceId()` for special identifiers like 'personal' and 'internal'
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📚 Learning: 2026-02-23T06:51:05.456Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-23T06:51:05.456Z
Learning: Applies to scripts/**/*.{js,ts} : Use workspace-relative paths in `apply_patch` (e.g., `apps/web/...`) instead of absolute Windows paths for better cross-platform compatibility
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📚 Learning: 2026-02-23T06:49:53.243Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: GEMINI.md:0-0
Timestamp: 2026-02-23T06:49:53.243Z
Learning: Applies to apps/web/**/*.{ts,tsx} : Database `ws_id` columns ALWAYS store UUIDs. Route parameters (`wsId`) may contain special identifiers like `personal` or `internal`. NEVER use raw `wsId` directly in database queries. Use `normalizeWorkspaceId(wsId)` helper for API routes or accept `workspace` prop in components.
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📚 Learning: 2026-02-23T06:49:53.243Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: GEMINI.md:0-0
Timestamp: 2026-02-23T06:49:53.243Z
Learning: Applies to packages/ui/src/**/*.{ts,tsx} : Extract portable UI components with no `@/` imports to `packages/ui`. Pass `workspace` prop to child components instead of raw `wsId`.
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📚 Learning: 2026-02-23T06:51:05.456Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-23T06:51:05.456Z
Learning: Applies to apps/web/src/app/[locale]/(dashboard)/[wsId]/navigation.tsx : Update main navigation file when adding new routes: add entries to both `aliases` array and `children` navigation items with proper icons and permission checks
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📚 Learning: 2026-02-23T06:49:12.270Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-23T06:49:12.270Z
Learning: Applies to apps/web/src/app/[locale]/(dashboard)/[wsId]/navigation.tsx : Update main navigation in `apps/web/src/app/[locale]/(dashboard)/[wsId]/navigation.tsx` when adding new routes - add to both `aliases` array and `children` navigation items with proper icons and permissions
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📚 Learning: 2026-02-23T06:49:53.243Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: GEMINI.md:0-0
Timestamp: 2026-02-23T06:49:53.243Z
Learning: Applies to apps/web/src/app/api/**/*.{ts,tsx} : The mobile app connects to `apps/web` API routes via Bearer token auth (not cookies). When updating web API routes for mobile features, maintain backward compatibility and use `createClient(request)` for Bearer token auth.
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📚 Learning: 2026-02-23T06:49:12.270Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-23T06:49:12.270Z
Learning: Applies to **/*.{ts,tsx} : Narrow `unknown`/`any` at boundaries; justify with comments if needed
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📚 Learning: 2026-02-23T06:49:12.271Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-23T06:49:12.271Z
Learning: Applies to packages/ai/src/object/types.ts : Define AI endpoint schemas in `packages/ai/src/object/types.ts` using Zod
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/schemas.ts
📚 Learning: 2026-02-23T06:51:05.456Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-23T06:51:05.456Z
Learning: Applies to apps/*/src/app/api/**/*.ts : Use Zod for runtime validation of external inputs (API bodies, env-derived config) at system boundaries
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/schemas.ts
📚 Learning: 2026-02-23T06:49:12.270Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-23T06:49:12.270Z
Learning: Applies to apps/*/src/app/api/**/*.ts : Use Zod for runtime validation of external inputs at API boundaries
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/schemas.ts
📚 Learning: 2026-02-23T06:49:12.270Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-23T06:49:12.270Z
Learning: Applies to apps/mobile/lib/**/*.dart : Use `go_router` with auth-aware redirects and ShellRoute for bottom tabs in Flutter app
Applied to files:
apps/mobile/lib/features/shell/view/shell_page.dart
📚 Learning: 2026-02-23T06:51:05.456Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-23T06:51:05.456Z
Learning: Applies to apps/mobile/lib/**/*.dart : Check `context.mounted` before using `BuildContext` after `await` in Flutter to prevent errors
Applied to files:
apps/mobile/lib/features/shell/view/shell_page.dart
📚 Learning: 2026-02-23T06:49:53.243Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: GEMINI.md:0-0
Timestamp: 2026-02-23T06:49:53.243Z
Learning: Applies to apps/mobile/lib/**/*{cubit,bloc}.dart : If a Flutter Cubit catches repository errors, rethrow after emitting error state so callers can handle failures locally.
Applied to files:
apps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart
📚 Learning: 2026-02-23T06:49:53.243Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: GEMINI.md:0-0
Timestamp: 2026-02-23T06:49:53.243Z
Learning: Applies to apps/mobile/lib/**/*.dart : Use `on Exception catch (e)` (or specific exception types) instead of bare `catch`, avoid catching `Error` subclasses like `TypeError`, capture `context` dependencies before the first `await`, guard `BuildContext` usage after `await` with `if (!context.mounted) return;`, and do not `return` inside `finally` blocks.
Applied to files:
apps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart
📚 Learning: 2026-02-23T06:49:12.271Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-23T06:49:12.271Z
Learning: Applies to apps/mobile/lib/**/*.dart : Use `on Exception catch (e)` instead of bare `catch` in Flutter code; avoid catching `Error` subclasses
Applied to files:
apps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart
📚 Learning: 2026-02-23T06:51:05.456Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-23T06:51:05.456Z
Learning: Applies to apps/mobile/lib/**/*.dart : Use `Future<void> Function()` callbacks for widget actions depending on server mutations; await them and only close UI on success
Applied to files:
apps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart
📚 Learning: 2026-02-23T06:49:12.271Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-23T06:49:12.271Z
Learning: Applies to apps/mobile/lib/**/*.dart : Use `Future<void> Function()` callbacks for mutation-driven UI actions in Flutter; await before closing dialogs
Applied to files:
apps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart
📚 Learning: 2026-02-23T06:49:53.243Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: GEMINI.md:0-0
Timestamp: 2026-02-23T06:49:53.243Z
Learning: Applies to apps/mobile/lib/**/*.dart : For mutation-driven Flutter UI actions (approve/reject/update), use `Future<void> Function()` callbacks (not `VoidCallback`), await them before closing dialogs/sheets, and surface failures in the UI (e.g., `SnackBar`).
Applied to files:
apps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart
📚 Learning: 2026-02-23T06:49:12.270Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-23T06:49:12.270Z
Learning: Applies to apps/mobile/lib/**/*.dart : Use `supabase_flutter` with `flutter_secure_storage` for token persistence in Flutter app
Applied to files:
apps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart
🧬 Code graph analysis (2)
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/stop.ts (3)
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/helpers.ts (3)
handleStopAction(5-5)SessionRecord(11-11)checkSessionThreshold(18-18)apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/schemas.ts (1)
SessionRecord(57-70)apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/threshold.ts (1)
checkSessionThreshold(25-101)
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/resume.ts (2)
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/helpers.ts (2)
handleResumeAction(3-3)SessionRecord(11-11)apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/schemas.ts (1)
SessionRecord(57-70)
🔇 Additional comments (8)
apps/mobile/lib/features/time_tracker/cubit/time_tracker_cubit.dart (3)
756-832: Month navigation fix and locale-aware week range — past blockers resolved ✓
_moveHistoryAnchornow usesDateTime(current.year, current.month + delta, 1, ...)to let Dart normalize the month/year, eliminating the previous truncation-division bug._historyPeriodRange's week branch correctly uses(normalizedAnchor.weekday - firstDayOfWeek + 7) % 7andanchor.toLocal()replaces the redundant field-reconstruction. All three previously flagged blockers are resolved.
332-402:loadHistoryInitial: rethrow and record.wait— past issues resolved ✓
throwOnErrorrethrow is now present (lines 398–400) and the parallel fetch uses the type-safe record.waitpattern (lines 364–377). Both previously flagged issues are closed.
839-848:_loadRecentAndSummaryalways fetches fresh data — past issue resolved ✓The previous stale-stats-on-null-userId fallback is gone;
_repo.getStats(wsId, userId)is always called. Record.waitensures parallel execution with correct types.apps/mobile/lib/features/time_tracker/view/time_tracker_page.dart (1)
25-39:_firstDayOfWeekdeduplication — past comment addressed ✓The helper is now a shared top-level function, eliminating the three-class duplication previously flagged.
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/stop.ts (1)
69-75: Break-close failure now properly aborts the stop — previous issue resolved.The 500 early-return on
updateErrorprevents the session from being stopped while an open break lingers in the database.apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/actions/resume.ts (1)
30-44: Break-close failure now properly aborts the resume — previous issue resolved.Returning 500 on
updateErrorprevents the new resumed session from being created while an open break still overlaps it.apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts (1)
145-149: Exhaustivedefaultcase added — previous issue resolved.The switch now has a fallback that returns a
400for unrecognized actions, preventing a silentundefinedresponse if the discriminated union is ever extended.apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/schemas.ts (1)
42-43: The code at lines 42–43 is correct and will work as intended. The project is already on Zod v4.3.6 across all workspaces, soz.iso.datetime()is available and valid. No runtime errors will occur.However,
SessionRecord(lines 57–70) manually defines database field types. Per coding guidelines, import database types frompackages/types/src/db.tsinstead.Likely an incorrect or invalid review comment.
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (3)
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts (3)
107-107:⚠️ Potential issue | 🟠 MajorUnsafe
as SessionRecordcast still present — previously flagged.The Supabase-generated type and the hand-maintained
SessionRecordinterface can drift silently. Ifis_runningisboolean | nullin the DB type butbooleaninSessionRecord, downstream handlers (e.g.,stop.ts) misinterpretnullasfalse. Prefer importing the generated type frompackages/types/src/db.ts, or at minimum add a justifying comment.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/src/app/api/v1/workspaces/`[wsId]/time-tracking/sessions/[sessionId]/route.ts at line 107, Replace the unsafe "const sessionRecord = session as SessionRecord;" cast: import and use the Supabase-generated type from packages/types/src/db.ts (the generated Session type) instead of the hand-maintained SessionRecord, and update the variable declaration to that generated type so nullable DB fields (e.g., is_running: boolean | null) are preserved; if importing the generated type is impossible, add a concise comment next to the cast naming the exact reason for the manual assertion and the invariants that guarantee non-nullability (and add a TODO to remove the assertion when types are aligned).
100-102:⚠️ Potential issue | 🟡 MinorPATCH returns
404for a missing-permissions result while DELETE correctly returns403(line 187).This inconsistency was flagged in a previous review and remains unaddressed.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/src/app/api/v1/workspaces/`[wsId]/time-tracking/sessions/[sessionId]/route.ts around lines 100 - 102, The PATCH handler currently returns NextResponse.json({ error: 'Not found' }, { status: 404 }) when permissions is falsy, causing inconsistency with DELETE which returns 403; change the PATCH permission check to return a 403 Forbidden (e.g., NextResponse.json({ error: 'Forbidden' }, { status: 403 })) when permissions is missing so both handlers use the same permission-denied status; update the branch that checks the permissions variable in the PATCH flow (the block that currently returns 404) to return 403 and a matching error message.
221-250:⚠️ Potential issue | 🟡 MinorAuthentication is still checked after
normalizeWorkspaceId— previously flagged.
normalizeWorkspaceId(line 224) issues a DB round-trip beforesupabase.auth.getUser()(line 242), causing unnecessary work for every unauthenticated request. Moving the auth check first is both cheaper and a more conventional defence-in-depth ordering.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/src/app/api/v1/workspaces/`[wsId]/time-tracking/sessions/[sessionId]/route.ts around lines 221 - 250, The code calls normalizeWorkspaceId (which performs a DB round-trip) before verifying the requester, causing wasted DB work for unauthenticated calls; change the order so you call supabase.auth.getUser() (via createClient and supabase.auth.getUser()) and return the 401 NextResponse.json on authError or missing user before calling normalizeWorkspaceId, then only call normalizeWorkspaceId and check normalizedWsId when the user is authenticated; update error returns to use NextResponse.json consistently for both auth and workspace-not-found cases.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@apps/web/src/app/api/v1/workspaces/`[wsId]/time-tracking/sessions/[sessionId]/route.ts:
- Around line 22-24: The exported route handler functions GET, PATCH, and DELETE
currently lack explicit return type annotations; update each function signature
to declare its return type as Promise<NextResponse> (e.g., export async function
GET(...): Promise<NextResponse>), keeping existing parameters and implementation
unchanged so the handlers conform to the project's TypeScript coding guidelines.
- Around line 233-240: The check for falsy normalizedWsId after
normalizeWorkspaceId is redundant and unreachable because normalizeWorkspaceId
either throws (already handled in the catch that returns) or returns a valid
string; remove the entire if (!normalizedWsId) block to simplify flow, keeping
the existing try/catch that handles errors from normalizeWorkspaceId and relying
on the returned normalizedWsId for subsequent logic in this route handler.
- Around line 192-196: The admin DELETE currently omits the user constraint
which creates a TOCTOU risk after the ownership check performed with the
RLS-scoped supabase client; update the sbAdmin.delete call against the
time_tracking_sessions table to include .eq('user_id', userId) (using the same
userId checked by the ownership logic) so the admin query narrows by id, ws_id
(normalizedWsId) and user_id, preventing deletion of another user's session even
if state changes between the check and delete.
---
Duplicate comments:
In
`@apps/web/src/app/api/v1/workspaces/`[wsId]/time-tracking/sessions/[sessionId]/route.ts:
- Line 107: Replace the unsafe "const sessionRecord = session as SessionRecord;"
cast: import and use the Supabase-generated type from packages/types/src/db.ts
(the generated Session type) instead of the hand-maintained SessionRecord, and
update the variable declaration to that generated type so nullable DB fields
(e.g., is_running: boolean | null) are preserved; if importing the generated
type is impossible, add a concise comment next to the cast naming the exact
reason for the manual assertion and the invariants that guarantee
non-nullability (and add a TODO to remove the assertion when types are aligned).
- Around line 100-102: The PATCH handler currently returns NextResponse.json({
error: 'Not found' }, { status: 404 }) when permissions is falsy, causing
inconsistency with DELETE which returns 403; change the PATCH permission check
to return a 403 Forbidden (e.g., NextResponse.json({ error: 'Forbidden' }, {
status: 403 })) when permissions is missing so both handlers use the same
permission-denied status; update the branch that checks the permissions variable
in the PATCH flow (the block that currently returns 404) to return 403 and a
matching error message.
- Around line 221-250: The code calls normalizeWorkspaceId (which performs a DB
round-trip) before verifying the requester, causing wasted DB work for
unauthenticated calls; change the order so you call supabase.auth.getUser() (via
createClient and supabase.auth.getUser()) and return the 401 NextResponse.json
on authError or missing user before calling normalizeWorkspaceId, then only call
normalizeWorkspaceId and check normalizedWsId when the user is authenticated;
update error returns to use NextResponse.json consistently for both auth and
workspace-not-found cases.
ℹ️ Review info
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (1)
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Build iOS (Development, simulator)
- GitHub Check: Playwright E2E
🧰 Additional context used
📓 Path-based instructions (12)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Use TypeScript with explicit return types for exported functions
Prefer interface for defining object shapes in TypeScript
Use TanStack Query (React Query) for ALL client-side data fetching - NEVER use useEffect for data fetching
Always includecache: 'no-store'in fetch() calls within TanStack Query queryFn to prevent dual-layer caching conflicts
Use Server Components by default; add'use client'only when necessary
Use@tuturuuu/ui/sonnerfor toast notifications instead of the deprecated@tuturuuu/ui/toast
Refactor files exceeding 400 LOC and components exceeding 200 LOC into smaller, focused units
Extract complex logic to separate utilities and custom hooks following single responsibility principle
Import database types frompackages/types/src/db.tsinstead of manually defining database types (only after user runs migrations viabun sb:pushand typegen viabun sb:typegen)
Always use stable array query keys with structure:[domain, subdomain?, paramsHash, version?]in TanStack Query
Use discriminated unions over enums for TypeScript type definitions
Narrowunknown/anyat boundaries; justify with comments if needed
Querypublic.user_private_detailstable for user email addresses instead ofpublic.userstable
Reference environment variables by name only in code - never echo values
Preferinterfaceovertypefor defining object shapes in TypeScript
Use camelCase for variable and function names in TypeScript/JavaScript
Use SCREAMING_SNAKE_CASE for constants
**/*.{ts,tsx}: Always prefer importing database types frompackages/types/src/db.ts. Never attempt to run migrations yourself - only after user runsbun sb:pushandbun sb:typegen.
Thepublic.userstable does NOT contain anpublic.user_private_details. When querying user email, always useuser_private_detailstable, notusers.
**/*.{ts,tsx}: Preferinterfacefor defining object shapes in TypeScript
N...
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
apps/*/src/app/api/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
apps/*/src/app/api/**/*.ts: Use Zod for runtime validation of external inputs at API boundaries
Validate inputs early in API routes and return 400/401/403 for bad requests
Never leak stack traces to client; wrap external service calls and surface sanitized error messages
Log detailed errors server-side only; never expose sensitive information to client
Define and export runtime if edge is required:export const runtime = 'edge'
Use@tuturuuu/supabaseclient for authentication in API routes
UsenormalizeWorkspaceId()helper to resolve workspace ID parameters in API routes
Enforce auth on AI endpoints by getting user viacreateClient()and returning 401 if absent
Check feature flags inworkspace_secretsbefore serving AI features
Use Vercel AI SDKstreamObject/generateObjectwith selected model for AI operations
SetmaxDurationfor long-running AI operations
Never log secrets or raw provider responses in AI endpoints
apps/*/src/app/api/**/*.ts: UsecreateClient(request)pattern in API routes to support both Bearer token auth (mobile) and cookie auth (web)
Useexport const runtime = 'edge'to explicitly target edge runtime execution in Next.js API routes when appropriate
Use Zod for runtime validation of external inputs (API bodies, env-derived config) at system boundaries
Resolve workspace ID parameters (wsId) to UUIDs before database operations; usenormalizeWorkspaceId()for special identifiers like 'personal' and 'internal'
Validate inputs early in API routes; return 4xx errors before processing to fail fast
Addexport const runtime = 'edge'for routes that should execute on the edge runtime
Use Vercel AI SDKgenerateObject/streamObjectwith Zod schema for deterministic AI output structure
UsecreateAdminClient()(sbAdmin) when bulk operations need to bypass trigger permission checks (e.g., user merge); always validate workspace membership first
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
apps/*/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
apps/*/src/**/*.{ts,tsx}: Extract functions used ≥2 times tosrc/lib/orsrc/utils/
Extract complex state logic to custom hooks insrc/hooks/
Extract pure computations as testable utilities
apps/*/src/**/*.{ts,tsx}: Never log secrets, API keys, tokens, or raw sensitive data to console; only reference environment variable names
Query thepublic.user_private_detailstable for user email addresses; thepublic.userstable does NOT contain an
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (GEMINI.md)
Always use
bun type-checkcommand for type checking. Do NOT usenpx tsgo,bunx tsgo, or other alternatives.
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
**/*.{ts,tsx,js,jsx,json,yaml,yml}
📄 CodeRabbit inference engine (GEMINI.md)
Use Biome for linting and formatting. Run
bun format-and-lint:fixto automatically fix issues.
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
**/{src,lib}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (GEMINI.md)
Files >400 LOC and components >200 LOC should be refactored into smaller, focused units. Apply best practices to ALL code, regardless of age. Follow single responsibility principle, extract utilities/hooks for complex logic, and leave code better than you found it.
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (GEMINI.md)
apps/web/**/*.{ts,tsx}: Never useuseEffectfor data fetching. MANDATORY: Use TanStack Query (useQuery,useMutation,useInfiniteQuery) for all client-side data fetching.
Never use rawfetch()without TanStack Query wrapper in client components. Always use TanStack Query hooks for client-side data fetching.
Anyfetch()inside aqueryFnMUST includecache: 'no-store'to prevent browser HTTP cache from serving stale responses after TanStack Query invalidation.
Use stable array query keys following pattern:[domain, subdomain?, paramsHash, version?]. SetstaleTime> 0 for rarely-changing data. Implement optimistic updates with rollback. Use narrow invalidations, avoid globalinvalidateQueries().
Follow the Tailwind Dynamic Color Policy. Never use hard-coded color classes liketext-blue-500. Instead, use thedynamic-*tokens, e.g.,text-dynamic-blue.
Usesonnerfor toasts:import { toast } from '@tuturuuu/ui/sonner';. Avoid the deprecated@tuturuuu/ui/toast. Never use native browser dialogs likealert()orconfirm().
Use the dialog system for modals:import { Dialog, ... } from '@tuturuuu/ui/dialog';. Never use native browser dialogs.
Databasews_idcolumns ALWAYS store UUIDs. Route parameters (wsId) may contain special identifiers likepersonalorinternal. NEVER use rawwsIddirectly in database queries. UsenormalizeWorkspaceId(wsId)helper for API routes or acceptworkspaceprop in components.
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
apps/web/src/app/**/*.{ts,tsx}
📄 CodeRabbit inference engine (GEMINI.md)
When adding new pages or routes, ALWAYS update the main navigation file (
apps/web/src/app/[locale]/(dashboard)/[wsId]/navigation.tsx). Add routes to both thealiasesarray andchildrennavigation items with proper icons, permission checks, and translation keys in bothen.jsonANDvi.json.
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
apps/*/src/app/api/**/*.{ts,tsx}
📄 CodeRabbit inference engine (GEMINI.md)
Create new API routes in
apps/<app>/src/app/api/.... Use Supabase client wrappers for authentication and Zod for input validation.
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
apps/web/src/app/api/**/*.{ts,tsx}
📄 CodeRabbit inference engine (GEMINI.md)
The mobile app connects to
apps/webAPI routes via Bearer token auth (not cookies). When updating web API routes for mobile features, maintain backward compatibility and usecreateClient(request)for Bearer token auth.
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
apps/web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (GEMINI.md)
Extract utilities to
src/lib/, hooks tosrc/hooks/, and sub-components as needed. Apply best practices to BOTH old and new code. Follow single responsibility principle, use meaningful names, and eliminate duplication.
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
**/*.ts: Avoid usinganytype in TypeScript; narrowunknownat boundaries with explicit type guards
Use discriminated unions over enums unless runtime enum is required
Always usebun type-checkfor TypeScript type checking; do NOT usenpx tsgo,bunx tsgo, or other alternatives
Files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
🧠 Learnings (14)
📚 Learning: 2026-02-23T06:49:12.270Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-23T06:49:12.270Z
Learning: Applies to apps/*/src/app/api/**/*.ts : Use `normalizeWorkspaceId()` helper to resolve workspace ID parameters in API routes
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📚 Learning: 2026-02-23T06:49:12.270Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-23T06:49:12.270Z
Learning: Applies to apps/**/src/components/**/*.{ts,tsx} : Use `workspace.id` directly from workspace object in client components instead of raw `wsId` parameter
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📚 Learning: 2026-02-23T06:51:05.456Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-23T06:51:05.456Z
Learning: Applies to apps/*/src/app/api/**/*.ts : Resolve workspace ID parameters (`wsId`) to UUIDs before database operations; use `normalizeWorkspaceId()` for special identifiers like 'personal' and 'internal'
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📚 Learning: 2026-02-23T06:49:53.243Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: GEMINI.md:0-0
Timestamp: 2026-02-23T06:49:53.243Z
Learning: Applies to apps/web/**/*.{ts,tsx} : Database `ws_id` columns ALWAYS store UUIDs. Route parameters (`wsId`) may contain special identifiers like `personal` or `internal`. NEVER use raw `wsId` directly in database queries. Use `normalizeWorkspaceId(wsId)` helper for API routes or accept `workspace` prop in components.
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📚 Learning: 2026-02-23T06:49:53.243Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: GEMINI.md:0-0
Timestamp: 2026-02-23T06:49:53.243Z
Learning: Applies to packages/ui/src/**/*.{ts,tsx} : Extract portable UI components with no `@/` imports to `packages/ui`. Pass `workspace` prop to child components instead of raw `wsId`.
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📚 Learning: 2026-02-23T06:51:05.456Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-23T06:51:05.456Z
Learning: Applies to scripts/**/*.{js,ts} : Use workspace-relative paths in `apply_patch` (e.g., `apps/web/...`) instead of absolute Windows paths for better cross-platform compatibility
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📚 Learning: 2026-02-23T06:51:05.456Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-23T06:51:05.456Z
Learning: Applies to apps/web/src/app/[locale]/(dashboard)/[wsId]/navigation.tsx : Update main navigation file when adding new routes: add entries to both `aliases` array and `children` navigation items with proper icons and permission checks
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📚 Learning: 2026-02-23T06:49:12.270Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-23T06:49:12.270Z
Learning: Applies to apps/web/src/app/[locale]/(dashboard)/[wsId]/navigation.tsx : Update main navigation in `apps/web/src/app/[locale]/(dashboard)/[wsId]/navigation.tsx` when adding new routes - add to both `aliases` array and `children` navigation items with proper icons and permissions
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📚 Learning: 2026-02-23T06:49:53.243Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: GEMINI.md:0-0
Timestamp: 2026-02-23T06:49:53.243Z
Learning: Applies to apps/web/src/app/api/**/*.{ts,tsx} : The mobile app connects to `apps/web` API routes via Bearer token auth (not cookies). When updating web API routes for mobile features, maintain backward compatibility and use `createClient(request)` for Bearer token auth.
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📚 Learning: 2026-02-23T06:49:12.270Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-23T06:49:12.270Z
Learning: Applies to **/*.{ts,tsx} : Narrow `unknown`/`any` at boundaries; justify with comments if needed
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📚 Learning: 2026-02-23T06:51:05.456Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-23T06:51:05.456Z
Learning: Applies to **/*.ts : Avoid using `any` type in TypeScript; narrow `unknown` at boundaries with explicit type guards
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📚 Learning: 2026-02-23T06:49:12.270Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-23T06:49:12.270Z
Learning: Applies to packages/types/src/supabase.ts : Never hand-edit generated type files in `packages/types/src/supabase.ts`
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📚 Learning: 2026-02-23T06:51:05.456Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-23T06:51:05.456Z
Learning: Applies to apps/*/src/app/api/**/*.ts : Use `createAdminClient()` (sbAdmin) when bulk operations need to bypass trigger permission checks (e.g., user merge); always validate workspace membership first
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
📚 Learning: 2026-02-23T06:49:12.271Z
Learnt from: CR
Repo: tutur3u/platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-23T06:49:12.271Z
Learning: Applies to apps/*/src/app/api/**/*.ts : Enforce auth on AI endpoints by getting user via `createClient()` and returning 401 if absent
Applied to files:
apps/web/src/app/api/v1/workspaces/[wsId]/time-tracking/sessions/[sessionId]/route.ts
- Updated `_keyForIndex` method in `ShellPage` to allow for non-global key usage. - Improved context handling in `TimeTrackerCubit` to ensure proper state management and loading of history preferences. - Refactored `initState` in `TimeTrackerView` to safely access context and load data. - Adjusted `StatCard` widget layout for better icon handling and consistency. - Enhanced API error handling for time tracking sessions, including permission checks and session state validation.
Description
Screenshots for proof (must have)
Summary by cubic
Added a mobile Time Tracker history view with day/week/month navigation, infinite scroll, and a stats overview accordion. Also improved locale-aware first-day-of-week behavior, edit-session UX with threshold-aware actions, and tightened session API logic, loading states, and navigation.
New Features
Refactors
Written for commit 2a8ee02. Summary will update on new commits. Review in cubic