Skip to content

Feat/toggl row collapsing#1118

Merged
Klakurka merged 3 commits into
masterfrom
feat/toggl-row-collapsing
Jun 24, 2026
Merged

Feat/toggl row collapsing#1118
Klakurka merged 3 commits into
masterfrom
feat/toggl-row-collapsing

Conversation

@lissavxo

@lissavxo lissavxo commented Feb 28, 2026

Copy link
Copy Markdown
Collaborator

Related to #953

Description

This PR introduces configurability for CSV row collapsing behavior.

Changes

  • Added a new prop to UserProfile to control CSV row collapsing behavior.

  • Added a toggle setting in Account Settings to allow users to define their preferred row collapsing behavior.

  • Refactored row collapsing logic to be prop driven instead of predefined (hardcoded).

Row collapsing behavior is now fully configurable and controlled via user settings, improving flexibility and user customization.

Test plan

Test CSV row collapsing by enabling and disabling the toggle in Account Settings, and verify that the exported CSV reflects the selected configuration correctly.

Summary by CodeRabbit

  • New Features

    • Added a CSV Row Collapsing toggle to the Account page.
    • Users can enable or disable CSV row collapsing, and the preference is saved immediately.
    • Transaction file downloads now reflect the setting (default is enabled).
  • UI Improvements

    • Improved account feedback messages with smaller success/error variants.
    • Added layout spacing for account rows.
  • Backend Support

    • Added server-side validation for the CSV row collapsing update request.

@coderabbitai

coderabbitai Bot commented Feb 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cc9694d5-ba5f-4b36-9494-fb2d2d6a4bae

📥 Commits

Reviewing files that changed from the base of the PR and between a26a143 and a3fcce0.

📒 Files selected for processing (2)
  • constants/index.ts
  • prisma-local/schema.prisma
🚧 Files skipped from review as they are similar to previous changes (2)
  • prisma-local/schema.prisma
  • constants/index.ts

📝 Walkthrough

Walkthrough

This PR adds a new CSV row collapsing setting with persisted user profile storage, validation support, and a new error constant. It also wires the setting into account UI, API updates, and CSV download behavior.

Changes

CSV row collapsing setting

Layer / File(s) Summary
UI toggle and styles
components/Account/CsvRowCollapsing.tsx, components/Account/account.module.css
Adds a toggle component that reads an initial value, updates the setting through /api/user, shows success or error messages, and defines the related toggle and message styles.
Account page integration
pages/account/index.tsx, pages/account/account.module.css
Renders the new CSV Row Collapsing control in the account page and adds spacing in the account row layout.
User setting API and validation
pages/api/user/index.ts, utils/validators.ts, constants/index.ts, services/userService.ts
Adds PUT handling for csvRowCollapsing, validates the request body, returns the new 400 error message on invalid input, and persists the value through a new service function.
Storage and migration
prisma-local/schema.prisma, prisma-local/migrations/20260228024317_csv_row_collapsing/migration.sql
Adds csvRowCollapsing to the UserProfile model and creates the matching database column with a default of true.
CSV download wiring and fixtures
pages/api/paybutton/download/transactions/[paybuttonId].ts, pages/api/payments/download/index.ts, tests/mockedObjects.ts
Passes the user’s csvRowCollapsing value into transaction download calls and updates mocked user profiles with the new field.

Sequence Diagram

sequenceDiagram
    actor User
    participant AccountPage as Account page
    participant ApiUser as PUT /api/user
    participant UserService as updateCsvRowCollapsing
    participant Prisma as UserProfile table

    User->>AccountPage: Toggle CSV row collapsing
    AccountPage->>ApiUser: PUT csvRowCollapsing
    ApiUser->>UserService: updateCsvRowCollapsing(id, value)
    UserService->>Prisma: update csvRowCollapsing
    Prisma-->>UserService: updated row
    UserService-->>ApiUser: success
    ApiUser-->>AccountPage: { success: true }
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 Hop hop, a toggle is in sight,
One tiny switch to make rows right.
The burrow hums with stored delight,
And CSVs collapse just fine tonight.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and clearly refers to the row collapsing feature change.
Description check ✅ Passed The description includes the required related issue, description, and test plan sections, and is mostly complete.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/toggl-row-collapsing

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.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@components/Account/CsvRowCollapsing.tsx`:
- Around line 24-31: The current handler only reverts the optimistic toggle in
the catch block and assumes axios-style errors; update the logic around the
fetch response (variable res) so non-2xx responses are handled: after awaiting
the fetch response check res.ok, and if false parse the response body (await
res.json() or text) to extract an error message, call setError(...) with that
message, clear setSuccess(''), and revert the optimistic change via
setCsvRowCollapsing(!newValue); otherwise on success call setError('') and
setSuccess('Updated successfully.'); also adjust the catch block to use a
generic fallback message (err.message) for network failures.

In `@pages/api/user/index.ts`:
- Around line 17-22: Wrap the call to parseUpdateCsvRowCollapsingPUTRequest(...)
in a try/catch similar to the existing parseUpdatePUTRequest handling: call
parseUpdateCsvRowCollapsingPUTRequest(req.body) inside try, and on validation
error catch it and respond with res.status(400).json({ success: false, error:
"INVALID_CSV_ROW_COLLAPSING_400" }); otherwise proceed to await
userService.updateCsvRowCollapsing(session.userId, csvRowCollapsing) and return
200. Ensure you only catch validation errors thrown by
parseUpdateCsvRowCollapsingPUTRequest and keep other errors unhandled or
rethrown as appropriate.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4fb9f5f and a26a143.

📒 Files selected for processing (13)
  • components/Account/CsvRowCollapsing.tsx
  • components/Account/account.module.css
  • constants/index.ts
  • pages/account/account.module.css
  • pages/account/index.tsx
  • pages/api/paybutton/download/transactions/[paybuttonId].ts
  • pages/api/payments/download/index.ts
  • pages/api/user/index.ts
  • prisma-local/migrations/20260228024317_csv_row_collapsing/migration.sql
  • prisma-local/schema.prisma
  • services/userService.ts
  • tests/mockedObjects.ts
  • utils/validators.ts

Comment on lines +24 to +31
if (res.status === 200) {
setError('')
setSuccess('Updated successfully.')
}
} catch (err: any) {
setSuccess('')
setError(err.response?.data?.message ?? 'Failed to update setting.')
setCsvRowCollapsing(!newValue)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Non-2xx HTTP responses won't revert the optimistic update.

The fetch API doesn't throw on HTTP error statuses (4xx, 5xx), so the catch block only handles network failures. When the API returns an error status, the UI will remain in the toggled state without showing an error message.

Additionally, err.response?.data?.message is an axios-style pattern; native fetch errors don't have this structure.

🐛 Proposed fix to handle non-2xx responses
     try {
       const res = await fetch('/api/user', {
         method: 'PUT',
         headers: {
           'Content-Type': 'application/json'
         },
         body: JSON.stringify({ csvRowCollapsing: newValue })
       })
       if (res.status === 200) {
         setError('')
         setSuccess('Updated successfully.')
+      } else {
+        const data = await res.json().catch(() => ({}))
+        setSuccess('')
+        setError(data.message ?? 'Failed to update setting.')
+        setCsvRowCollapsing(!newValue)
       }
     } catch (err: any) {
       setSuccess('')
-      setError(err.response?.data?.message ?? 'Failed to update setting.')
+      setError('Failed to update setting.')
       setCsvRowCollapsing(!newValue)
     } finally {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (res.status === 200) {
setError('')
setSuccess('Updated successfully.')
}
} catch (err: any) {
setSuccess('')
setError(err.response?.data?.message ?? 'Failed to update setting.')
setCsvRowCollapsing(!newValue)
if (res.status === 200) {
setError('')
setSuccess('Updated successfully.')
} else {
const data = await res.json().catch(() => ({}))
setSuccess('')
setError(data.message ?? 'Failed to update setting.')
setCsvRowCollapsing(!newValue)
}
} catch (err: any) {
setSuccess('')
setError('Failed to update setting.')
setCsvRowCollapsing(!newValue)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/Account/CsvRowCollapsing.tsx` around lines 24 - 31, The current
handler only reverts the optimistic toggle in the catch block and assumes
axios-style errors; update the logic around the fetch response (variable res) so
non-2xx responses are handled: after awaiting the fetch response check res.ok,
and if false parse the response body (await res.json() or text) to extract an
error message, call setError(...) with that message, clear setSuccess(''), and
revert the optimistic change via setCsvRowCollapsing(!newValue); otherwise on
success call setError('') and setSuccess('Updated successfully.'); also adjust
the catch block to use a generic fallback message (err.message) for network
failures.

Comment thread pages/api/user/index.ts
Comment on lines +17 to +22
if (req.body.csvRowCollapsing !== undefined) {
const { csvRowCollapsing } = parseUpdateCsvRowCollapsingPUTRequest(req.body)
await userService.updateCsvRowCollapsing(session.userId, csvRowCollapsing)
res.status(200).json({ success: true })
return
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Missing error handling for validation failures.

If parseUpdateCsvRowCollapsingPUTRequest throws (when csvRowCollapsing is not a boolean), the error will propagate as an unhandled exception, resulting in a 500 response instead of the intended 400 status with INVALID_CSV_ROW_COLLAPSING_400.

Note: This mirrors the existing pattern for parseUpdatePUTRequest (line 23), so it's consistent but worth addressing for proper error responses.

🛡️ Proposed fix to add error handling
   if (req.method === 'PUT') {
     if (req.body.csvRowCollapsing !== undefined) {
+      try {
         const { csvRowCollapsing } = parseUpdateCsvRowCollapsingPUTRequest(req.body)
         await userService.updateCsvRowCollapsing(session.userId, csvRowCollapsing)
         res.status(200).json({ success: true })
         return
+      } catch (error: any) {
+        res.status(400).json({ message: error.message })
+        return
+      }
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (req.body.csvRowCollapsing !== undefined) {
const { csvRowCollapsing } = parseUpdateCsvRowCollapsingPUTRequest(req.body)
await userService.updateCsvRowCollapsing(session.userId, csvRowCollapsing)
res.status(200).json({ success: true })
return
}
if (req.body.csvRowCollapsing !== undefined) {
try {
const { csvRowCollapsing } = parseUpdateCsvRowCollapsingPUTRequest(req.body)
await userService.updateCsvRowCollapsing(session.userId, csvRowCollapsing)
res.status(200).json({ success: true })
return
} catch (error: any) {
res.status(400).json({ message: error.message })
return
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pages/api/user/index.ts` around lines 17 - 22, Wrap the call to
parseUpdateCsvRowCollapsingPUTRequest(...) in a try/catch similar to the
existing parseUpdatePUTRequest handling: call
parseUpdateCsvRowCollapsingPUTRequest(req.body) inside try, and on validation
error catch it and respond with res.status(400).json({ success: false, error:
"INVALID_CSV_ROW_COLLAPSING_400" }); otherwise proceed to await
userService.updateCsvRowCollapsing(session.userId, csvRowCollapsing) and return
200. Ensure you only catch validation errors thrown by
parseUpdateCsvRowCollapsingPUTRequest and keep other errors unhandled or
rethrown as appropriate.

@Klakurka Klakurka self-requested a review June 24, 2026 05:22
@Klakurka Klakurka added the enhancement (UI/UX/feature) New feature or request label Jun 24, 2026
@Klakurka Klakurka merged commit 525622e into master Jun 24, 2026
3 checks passed
@Klakurka Klakurka added this to the Phase 3 milestone Jun 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement (UI/UX/feature) New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants