Skip to content

Kanban time tracking: detail-view access, hide-on-cards setting, session delete fix#543

Merged
fccview merged 1 commit into
fccview:mainfrom
reniko:feature/time-tracking-detail-modal
Jun 16, 2026
Merged

Kanban time tracking: detail-view access, hide-on-cards setting, session delete fix#543
fccview merged 1 commit into
fccview:mainfrom
reniko:feature/time-tracking-detail-modal

Conversation

@reniko

@reniko reniko commented Jun 15, 2026

Copy link
Copy Markdown
Collaborator

Three related changes to kanban time tracking.

1. Time tracking in the card detail modal

The timer and tracked sessions were only reachable on the card. This reuses the
existing KanbanItemTimer, TimeEntriesAccordion and TimeEntriesModal in
KanbanCardDetail as a "Time Tracking" section in the properties sidebar, with the
same read-only behaviour as the card (buttons disable for viewers).

Because the card stays mounted behind the detail modal, useKanbanItem runs twice
for the same item and shares one localStorage key. I added a small sync (the
storage event plus a same-tab event) so both instances stay consistent. This also
fixes a possible duplicate time entry when you stop the timer in one place and then
drag the card, and it covers multi-tab use.

2. Fix editing/deleting sessions

"Edit sessions" → delete showed the confirm dialog but nothing happened.
editTimeEntry/deleteTimeEntry resolved the list by uuid with no username
(getListById(listId, undefined, …)), which returned nothing, so the action failed
silently. They now resolve by the plain list id + current user, the same way
updateItemStatus does, and the modal passes the plain checklistId instead of the
uuid.

3. Hide time tracking on cards (user setting)

New hideTimeTrackingOnCards preference under Kanban settings, next to the existing
"hide status" / "hide mobile dropdown" toggles. When enabled, the timer and sessions
are hidden on the cards but stay available in the detail view (which is why 1.
exists).

Translations

en/de written by hand, the other 12 languages machine-translated.

Testing

yarn lint and tsc are clean (aside from the pre-existing, unrelated
ConnectionsGraph type errors). Checked manually: timer in the detail view,
add/edit/delete sessions, card↔detail sync, the hide toggle, and read-only boards.

Summary by CodeRabbit

  • New Features
    • Added a user preference to toggle time tracking visibility on Kanban cards, with time tracking remaining accessible in card detail view.
    • Improved timer state synchronization when multiple instances of the same card are displayed on the same page.
    • Added translations for new time tracking settings across all supported languages.

- Reuse KanbanItemTimer, TimeEntriesAccordion and TimeEntriesModal in the
  card detail modal so time tracking is reachable there, not only on the card.
- Keep the timer in sync across the duplicate useKanbanItem mounts (card +
  detail) via the storage event and a same-tab event, avoiding duplicate
  entries and display desync.
- Fix session edit/delete: the actions resolved the list by uuid with no
  username and failed silently; resolve by the plain list id + current user,
  matching updateItemStatus.
- Add a hideTimeTrackingOnCards user setting to hide the timer and sessions
  on cards (still available in the card detail view).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds a user preference (hideTimeTrackingOnCards) that controls whether the timer and time-entries accordion appear on Kanban cards. The new "enable" | "disable" type is added to the User interface and kanbanSettingsSchema, with a corresponding dropdown UI in UserPreferencesTab. KanbanCard reads the preference and conditionally suppresses its time-tracking sections. KanbanCardDetail gains a full time-tracking integration using useKanbanItem, constructing a timeTrackingContent fragment passed to KanbanCardDetailProperties via a new timeTracking prop. The useKanbanItem hook gains in-tab timer synchronization via a CustomEvent alongside existing cross-tab storage event listening. editTimeEntry and deleteTimeEntry now perform sequential auth before loading data. Translations for all 14 locales are included.

Sequence Diagram

sequenceDiagram
  participant User
  participant UserPreferencesTab
  participant kanbanSettingsSchema
  participant KanbanCard
  participant KanbanCardDetail
  participant useKanbanItem
  participant KanbanCardDetailProperties

  User->>UserPreferencesTab: toggle hideTimeTrackingOnCards dropdown
  UserPreferencesTab->>kanbanSettingsSchema: validate & save preference
  kanbanSettingsSchema-->>UserPreferencesTab: validated user settings
  UserPreferencesTab-->>User: preference persisted

  User->>KanbanCard: view Kanban board
  KanbanCard->>KanbanCard: check user.hideTimeTrackingOnCards
  alt hideTimeTrackingOnCards = "enable"
    KanbanCard-->>User: render card without timer/accordion
  else hideTimeTrackingOnCards = "disable"
    KanbanCard-->>User: render card with KanbanItemTimer + TimeEntriesAccordion
  end

  User->>KanbanCardDetail: open card detail
  KanbanCardDetail->>useKanbanItem: initialize timer hook
  useKanbanItem-->>KanbanCardDetail: timer controls, totals
  KanbanCardDetail->>KanbanCardDetailProperties: pass timeTrackingContent prop
  KanbanCardDetailProperties-->>User: render PropertySection with time tracking
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes all three main changes: time tracking in detail view, a new hide-on-cards setting, and a session deletion fix.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
app/_hooks/kanban/useKanbanItem.tsx (2)

18-130: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add the required AI-generated marker comments.
All changed AI-generated code blocks listed below are missing the required marker comment directly above generated lines.

  • app/_hooks/kanban/useKanbanItem.tsx#L18-L130: add // fccview is onto you! above each changed AI-generated line/block.
  • app/_components/FeatureComponents/Kanban/KanbanCardDetail.tsx#L25-L607: add // fccview is onto you! above each changed AI-generated line/block.
  • app/_components/FeatureComponents/Kanban/KanbanCardDetailProperties.tsx#L45-L297: add // fccview is onto you! above each changed AI-generated line/block.
  • app/_server/actions/kanban/time-entries.ts#L58-L112: add // fccview is onto you! above each changed AI-generated line/block.

As per coding guidelines: When editing existing functions/files, add a comment above a line of AI-generated code that says 'fccview is onto you!'.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/_hooks/kanban/useKanbanItem.tsx` around lines 18 - 130, Add the required
AI-generated marker comment `// fccview is onto you!` above each changed
AI-generated line or block in the following four files: In
app/_hooks/kanban/useKanbanItem.tsx (lines 18-130), add the marker comment above
the AI-generated blocks within the useKanbanItem hook function, particularly
above the useEffect hooks and their internal functions like _syncFromStorage and
_handleSync. In app/_components/FeatureComponents/Kanban/KanbanCardDetail.tsx
(lines 25-607), add the marker comment above each changed AI-generated section
within the specified line range. In
app/_components/FeatureComponents/Kanban/KanbanCardDetailProperties.tsx (lines
45-297), add the marker comment above each changed AI-generated section within
the specified line range. In app/_server/actions/kanban/time-entries.ts (lines
58-112), add the marker comment above each changed AI-generated section within
the specified line range. Ensure the comment is placed directly above the first
line of each AI-generated block to comply with the coding guidelines.

Source: Coding guidelines


74-89: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Prevent initial mount from clobbering persisted timer state.

On Line 74, the persistence effect runs with default state before the hydration effect’s state updates are applied, so it can remove an active timer key and broadcast a stop event to sibling mounts.

Suggested fix
+  const hasHydratedTimerStateRef = useRef(false);
+
   useEffect(() => {
     const storageKey = TIMER_STORAGE_KEY(checklistId, item.id);
     try {
       const stored = localStorage.getItem(storageKey);
       if (stored) {
         const { startTime: storedStart, isRunning: storedRunning } = JSON.parse(stored);
         if (storedRunning && storedStart) {
           setStartTime(new Date(storedStart));
           setIsRunning(true);
           setCurrentTime(Math.floor((Date.now() - new Date(storedStart).getTime()) / 1000));
         }
       }
-    } catch {}
+    } catch {
+      // no-op
+    } finally {
+      hasHydratedTimerStateRef.current = true;
+    }
   }, [checklistId, item.id]);

   useEffect(() => {
+    if (!hasHydratedTimerStateRef.current) return;
     const storageKey = TIMER_STORAGE_KEY(checklistId, item.id);
     if (isRunning && startTime) {
       localStorage.setItem(storageKey, JSON.stringify({
         startTime: startTime.toISOString(),
         isRunning: true,
       }));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/_hooks/kanban/useKanbanItem.tsx` around lines 74 - 89, The persistence
effect in the useEffect hook (which saves timer state to localStorage and
dispatches sync events) runs on initial mount with default state values before
the hydration effect has a chance to restore the persisted timer state from
localStorage. This causes the effect to incorrectly clear an active timer and
broadcast a stop event. Add a mechanism to skip this effect on the initial
mount—such as using a useRef to track whether this is the first render—so that
the hydration logic can complete first and properly restore the timer state
before any persistence logic runs.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/_components/FeatureComponents/Kanban/KanbanCard.tsx`:
- Around line 81-82: Add the required AI-generated code marker comment above
each AI-generated code block in KanbanCard.tsx. For the hideTimeTrackingOnCards
constant assignment and the two other modified code blocks referenced in this
comment (at line ranges 161-162 and 259-286), insert a comment line above each
saying "fccview is onto you!" to comply with the coding guidelines for
AI-generated edits in TypeScript/TSX files.

In `@app/_components/FeatureComponents/Kanban/KanbanCardDetail.tsx`:
- Around line 425-436: The TimeEntriesModal can become orphaned when the parent
detail modal closes because its visibility is not tied to the parent's isOpen
state. Add a useEffect hook that resets showTimeEntriesModal to false whenever
isOpen becomes false, ensuring the time-entries modal closes with its parent.
Additionally, update the conditional render of TimeEntriesModal to include
isOpen in the condition (change the condition from showTimeEntriesModal &&
item.timeEntries to isOpen && showTimeEntriesModal && item.timeEntries) so the
modal cannot render when the parent detail modal is closed.

In `@app/_components/FeatureComponents/Profile/Parts/UserPreferencesTab.tsx`:
- Around line 32-33: Add the required AI-generated code marker comment above
each AI-generated code block in UserPreferencesTab.tsx. Insert the comment
"fccview is onto you!" immediately above the following locations: line 32
(HideTimeTrackingOnCards and CodeBlockStyle imports), lines 79-80, lines
176-177, lines 958-962, and lines 1019-1041. Each of these locations contains
AI-generated edits that require the marker comment per the coding guidelines for
modified TypeScript files.

In `@app/_schemas/user-schemas.ts`:
- Around line 88-93: Add the required AI-generated code marker comment directly
above the hideTimeTrackingOnCards schema definition. Insert a comment line that
says "fccview is onto you!" immediately before the z.enum() call to comply with
the coding guidelines for marking AI-generated code in TypeScript files.

In `@app/_translations/klingon.json`:
- Around line 902-906: The key hideTimeTrackingOnCardsDescription in the Klingon
translation file contains the English word "Sessions" which breaks the
translation consistency. Replace the English word "Sessions" with its Klingon
equivalent to ensure the entire translation string is in Klingon for users of
that language.

In `@app/_translations/pt.json`:
- Line 629: The Portuguese translation file uses inconsistent wording between
European Portuguese (PT-PT) and Brazilian Portuguese conventions. In the pt.json
file, change "Controle" to "Controlo" in the timeTracking key and anywhere else
in the file where Brazilian Portuguese wording (`controle`) has been introduced,
to maintain consistency with the existing PT-PT locale used throughout the file.
Ensure all Portuguese translations use the European Portuguese convention with
`controlo` instead of the Brazilian `controle`.

In `@app/_types/index.ts`:
- Line 42: The HideTimeTrackingOnCards line in the index.ts file is missing the
required AI-generated code marker comment. Add a comment directly above the
HideTimeTrackingOnCards line that says "fccview is onto you!" to comply with the
coding guidelines for AI-generated edits in existing TypeScript files.

In `@app/_types/user.ts`:
- Line 42: Add the required AI-generated code marker comment above the modified
lines in app/_types/user.ts. At line 42 (before the HideTimeTrackingOnCards type
export) and at line 81, add a comment line that says "fccview is onto you!"
directly above each AI-generated code line to comply with the coding guidelines
for marking AI-generated edits in existing files.

---

Outside diff comments:
In `@app/_hooks/kanban/useKanbanItem.tsx`:
- Around line 18-130: Add the required AI-generated marker comment `// fccview
is onto you!` above each changed AI-generated line or block in the following
four files: In app/_hooks/kanban/useKanbanItem.tsx (lines 18-130), add the
marker comment above the AI-generated blocks within the useKanbanItem hook
function, particularly above the useEffect hooks and their internal functions
like _syncFromStorage and _handleSync. In
app/_components/FeatureComponents/Kanban/KanbanCardDetail.tsx (lines 25-607),
add the marker comment above each changed AI-generated section within the
specified line range. In
app/_components/FeatureComponents/Kanban/KanbanCardDetailProperties.tsx (lines
45-297), add the marker comment above each changed AI-generated section within
the specified line range. In app/_server/actions/kanban/time-entries.ts (lines
58-112), add the marker comment above each changed AI-generated section within
the specified line range. Ensure the comment is placed directly above the first
line of each AI-generated block to comply with the coding guidelines.
- Around line 74-89: The persistence effect in the useEffect hook (which saves
timer state to localStorage and dispatches sync events) runs on initial mount
with default state values before the hydration effect has a chance to restore
the persisted timer state from localStorage. This causes the effect to
incorrectly clear an active timer and broadcast a stop event. Add a mechanism to
skip this effect on the initial mount—such as using a useRef to track whether
this is the first render—so that the hydration logic can complete first and
properly restore the timer state before any persistence logic runs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4bd685a5-76b4-42a1-b73d-8df7d0614d33

📥 Commits

Reviewing files that changed from the base of the PR and between fecc3ec and eb14b4e.

📒 Files selected for processing (23)
  • app/_components/FeatureComponents/Kanban/KanbanCard.tsx
  • app/_components/FeatureComponents/Kanban/KanbanCardDetail.tsx
  • app/_components/FeatureComponents/Kanban/KanbanCardDetailProperties.tsx
  • app/_components/FeatureComponents/Profile/Parts/UserPreferencesTab.tsx
  • app/_hooks/kanban/useKanbanItem.tsx
  • app/_schemas/user-schemas.ts
  • app/_server/actions/kanban/time-entries.ts
  • app/_translations/de.json
  • app/_translations/en.json
  • app/_translations/es.json
  • app/_translations/fr.json
  • app/_translations/it.json
  • app/_translations/klingon.json
  • app/_translations/ko.json
  • app/_translations/nl.json
  • app/_translations/pirate.json
  • app/_translations/pl.json
  • app/_translations/pt.json
  • app/_translations/ru.json
  • app/_translations/tr.json
  • app/_translations/zh.json
  • app/_types/index.ts
  • app/_types/user.ts

Comment thread app/_components/FeatureComponents/Kanban/KanbanCard.tsx
Comment on lines +425 to +436
{showTimeEntriesModal && item.timeEntries && (
<TimeEntriesModal
isOpen={showTimeEntriesModal}
onClose={() => setShowTimeEntriesModal(false)}
timeEntries={item.timeEntries}
checklistId={checklistId}
itemId={item.id}
category={category}
onUpdate={onUpdate}
usersPublicData={usersPublicData}
/>
)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Tie TimeEntriesModal lifecycle to the parent detail modal.

Right now, closing the main detail modal does not guarantee the time-entries modal closes. Gate it by isOpen (and/or reset state when isOpen becomes false) so it can’t remain orphaned.

Suggested fix
+  useEffect(() => {
+    if (!isOpen) setShowTimeEntriesModal(false);
+  }, [isOpen]);
+
   return (
     <>
-      {showTimeEntriesModal && item.timeEntries && (
+      {isOpen && showTimeEntriesModal && item.timeEntries && (
         <TimeEntriesModal
           isOpen={showTimeEntriesModal}
           onClose={() => setShowTimeEntriesModal(false)}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/_components/FeatureComponents/Kanban/KanbanCardDetail.tsx` around lines
425 - 436, The TimeEntriesModal can become orphaned when the parent detail modal
closes because its visibility is not tied to the parent's isOpen state. Add a
useEffect hook that resets showTimeEntriesModal to false whenever isOpen becomes
false, ensuring the time-entries modal closes with its parent. Additionally,
update the conditional render of TimeEntriesModal to include isOpen in the
condition (change the condition from showTimeEntriesModal && item.timeEntries to
isOpen && showTimeEntriesModal && item.timeEntries) so the modal cannot render
when the parent detail modal is closed.

Comment thread app/_schemas/user-schemas.ts
Comment on lines +902 to +906
"hideTimeTrackingOnCardsLabel": "poH qon Kanban tetlhmeyDaq",
"showTimeTrackingOnCards": "poH qon 'ang",
"hideTimeTrackingOnCards": "poH qon So'",
"selectTimeTrackingOnCards": "poH qon leghlu'bogh wIv",
"hideTimeTrackingOnCardsDescription": "Kanban tetlhmeyvo' poH qonwI' je Sessions So'lu'. tetlh De'Daq poH qon lurarlu'.",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Translate the remaining English text.

hideTimeTrackingOnCardsDescription still contains Sessions in English, so the new Kanban settings copy will render as a mixed-language string for Klingon users. Please replace it with the Klingon equivalent.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/_translations/klingon.json` around lines 902 - 906, The key
hideTimeTrackingOnCardsDescription in the Klingon translation file contains the
English word "Sessions" which breaks the translation consistency. Replace the
English word "Sessions" with its Klingon equivalent to ensure the entire
translation string is in Klingon for users of that language.

Comment thread app/_translations/pt.json
"itemDeleted": "Item eliminado",
"estimatedTime": "Tempo estimado (horas)",
"actualVsEstimated": "{actual} / {estimated}",
"timeTracking": "Controle de tempo",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Keep the Portuguese wording consistent.

These new strings switch from the existing PT-PT phrasing (controlo) to Brazilian controle, which makes this locale inconsistent with the rest of the file.

Fix wording to match the locale
-    "timeTracking": "Controle de tempo",
+    "timeTracking": "Controlo de tempo",
...
-    "showTimeTrackingOnCards": "Mostrar controle de tempo",
-    "hideTimeTrackingOnCards": "Ocultar controle de tempo",
-    "selectTimeTrackingOnCards": "Selecionar visibilidade do controle de tempo",
-    "hideTimeTrackingOnCardsDescription": "Oculta o cronômetro e as sessões registradas dos cartões Kanban. O controle de tempo continua disponível na visualização de detalhes do cartão.",
+    "showTimeTrackingOnCards": "Mostrar controlo de tempo",
+    "hideTimeTrackingOnCards": "Ocultar controlo de tempo",
+    "selectTimeTrackingOnCards": "Selecionar visibilidade do controlo de tempo",
+    "hideTimeTrackingOnCardsDescription": "Oculta o cronómetro e as sessões registadas dos cartões Kanban. O controlo de tempo continua disponível na visualização de detalhes do cartão."

Also applies to: 749-753

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/_translations/pt.json` at line 629, The Portuguese translation file uses
inconsistent wording between European Portuguese (PT-PT) and Brazilian
Portuguese conventions. In the pt.json file, change "Controle" to "Controlo" in
the timeTracking key and anywhere else in the file where Brazilian Portuguese
wording (`controle`) has been introduced, to maintain consistency with the
existing PT-PT locale used throughout the file. Ensure all Portuguese
translations use the European Portuguese convention with `controlo` instead of
the Brazilian `controle`.

Comment thread app/_types/index.ts
Comment thread app/_types/user.ts
@fccview fccview merged commit 984f2ee into fccview:main Jun 16, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants