Skip to content

Feat(csv): adding csv exports that are server side#313

Merged
martian56 merged 31 commits into
Devlaner:mainfrom
LADsy8:main
Jul 25, 2026
Merged

Feat(csv): adding csv exports that are server side#313
martian56 merged 31 commits into
Devlaner:mainfrom
LADsy8:main

Conversation

@LADsy8

@LADsy8 LADsy8 commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR implements the server-side analytics endpoints and the CSV export capability for workspaces and projects, resolving the client-side scalability issues and wiring up the "Export as CSV" button in the UI.

Linked issues

Closes #204

Type of change

  • Feature (feat:) — user-visible new capability

Surface

  • API (apps/api/)
  • UI (apps/web/)

What changed

API Backend (apps/api/)

  • Router: Registered server-side analytics and CSV export routes for workspaces and projects.
  • Analytics Handlers: Added logic to compute real-time aggregate counts (by status, priority, assignee, and label) and created-vs-resolved trends.
  • CSV Export: Created a working CSV builder that serializes issue details including assignee name, priorities, and labels, rather than spitting out raw IDs.
  • Database Schema Mappings: Correctly joined the relational schemas (resolving joins across issues, assignees, and labels).

Frontend UI (apps/web/)

  • Analytics Pages: Refactored AnalyticsOverviewPage and AnalyticsWorkItemsPage to fetch metrics from the new backend endpoints instead of doing expensive client-side aggregation.
  • CSV Export Button: Wired the dead "Export as CSV" button to trigger a browser download from the new API export endpoints.

Why this approach

Processing aggregations on the server ensures the platform scales efficiently as workspaces grow to thousands of issues. Using optimized database joins prevents the "N+1 query" performance pitfall.

Database / migrations

No schema migrations required.

Breaking changes

  • No

Test plan

  • Verified running npm run validate from the repository root passes cleanly (TypeScript check + golang test suites).
  • Manual Verification:
    • Seeded local development data using go run ./cmd/api seed.
    • Loaded the analytics dashboard to verify that percentages and status metrics compute correctly.
    • Tested the "Export as CSV" button in the UI and verified the downloaded CSV contains properly structured project details.

Screenshots / recordings (UI changes)

Before After
Dead Export button Fully functional CSV file download & snappy loaded charts

AI assistance

  • AI tools were used — tool(s): Gemini — and AI-assisted commits include a Co-Authored-By: trailer

Checklist

  • PR title follows Conventional Commits and is ≤ 100 chars (feat(api/ui): implement server-side analytics and working CSV export #204)
  • Hooks ran cleanly (no --no-verify bypass)
  • No secrets, tokens, or .env values committed

Review Notes & Future Improvements

While testing locally, I noticed a few behaviors and structural details to highlight:

  1. Percentage Calculations & Charts:

    • Completion percentages do not seem to increase automatically when tasks are moved to "Done".
    • While the correct number of issues linked to labels like done and backlog are visible in the data, they do not render properly inside the main chart. This might be an existing issue with how state types/cycles are mapped, which we can refine later.
  2. Database Schema Details (Assignees & Labels):

    • Note on the CSV Export: Since there are no direct assignee or label columns on the issues table, and because setting up the many-to-many joins (issue_assignees and issue_labels) was causing SQL errors, I skipped adding assignees and labels to the CSV columns for now to keep the code stable. We can add these relations in a follow-up PR once we iron out the database join logic

Summary by CodeRabbit

  • New Features
    • Enhanced Analytics dashboard with workspace/project analytics powered by a single server response, including trend data.
    • Added authenticated CSV export for workspace analytics and per-project work items with safe, consistent download filenames and streaming.
  • Bug Fixes
    • Improved Analytics and Issue List page resilience by handling non-critical request failures without breaking the whole page.
    • Hardened CSV exports to prevent spreadsheet formula injection and improved export error handling.
  • Maintenance
    • Updated web tooling dependencies (Node/Vite) and refreshed analytics chart/label text.

@LADsy8
LADsy8 requested a review from a team as a code owner July 14, 2026 21:15
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c9e54976-5a64-4e2a-aeed-3cc7af0c8e19

📥 Commits

Reviewing files that changed from the base of the PR and between 17a7e01 and db99f5f.

📒 Files selected for processing (3)
  • apps/api/internal/handler/analytics.go
  • apps/api/internal/model/analytics.go
  • apps/api/internal/store/analytics.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/api/internal/store/analytics.go
  • apps/api/internal/handler/analytics.go

📝 Walkthrough

Walkthrough

Adds server-side workspace and project analytics, authenticated CSV export endpoints, and frontend consumption of aggregated data. It also makes secondary issue-list requests resilient to individual failures and updates web tooling versions.

Changes

Analytics feature

Layer / File(s) Summary
Analytics data contracts and queries
apps/api/internal/model/analytics.go, apps/api/internal/store/analytics.go
Defines analytics/export models, grouped workspace/project queries, trend aggregation, and streaming issue export callbacks.
Analytics access and aggregation
apps/api/internal/service/analytics.go
Adds access checks, analytics aggregation, trend response support, and workspace/project export delegation.
Authenticated analytics HTTP endpoints
apps/api/internal/handler/analytics.go, apps/api/internal/router/router.go
Adds authenticated analytics and CSV routes, service delegation, error mapping, and CSV sanitization.
Analytics page data and export flow
apps/web/src/pages/AnalyticsWorkItemsPage.tsx
Consumes API analytics, adds authenticated CSV downloads and export states, and updates dashboard rendering.

Issue list loading behavior

Layer / File(s) Summary
Resilient issue-list loading
apps/web/src/pages/IssueListPage.tsx
Adds fallback handling for secondary requests and changes critical-error state clearing.

Web tooling versions

Layer / File(s) Summary
Web package versions
apps/web/package.json
Adds the Node dependency and updates Vite.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AnalyticsWorkItemsPage
  participant GinRouter
  participant AnalyticsHandler
  participant AnalyticsService
  participant AnalyticsStore
  AnalyticsWorkItemsPage->>GinRouter: Request analytics or CSV
  GinRouter->>AnalyticsHandler: Dispatch authenticated request
  AnalyticsHandler->>AnalyticsService: Request analytics or exports
  AnalyticsService->>AnalyticsStore: Query counts or stream issues
  AnalyticsStore-->>AnalyticsService: Return analytics rows or export items
  AnalyticsService-->>AnalyticsHandler: Return response data
  AnalyticsHandler-->>AnalyticsWorkItemsPage: Return JSON or CSV download
Loading

Possibly related PRs

Suggested labels: enhancement, API, UI

Suggested reviewers: martian56

Poem

A rabbit found counts in a burrow of rows,
And CSV carrots began to compose.
Charts hopped from the page,
Exports took the stage,
While failed little fetches found fallback repose.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The Vite/node dependency bump and IssueListPage fetch-hardening are unrelated to the analytics and CSV export scope. Move the unrelated dependency and IssueListPage changes to a separate PR, or explain why they are required for the analytics/export work.
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and clearly describes the main change: server-side CSV exports.
Description check ✅ Passed The PR description includes the required summary, linked issue, change list, rationale, migrations, test plan, screenshots, and checklist sections.
Linked Issues check ✅ Passed The changes satisfy #204 by adding server-side analytics, trend counts, CSV export routes, and UI wiring to the new backend endpoints.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


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: 11

🧹 Nitpick comments (1)
apps/web/src/pages/IssueListPage.tsx (1)

589-590: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove leftover debugging statements.

These console.log statements appear to be debugging artifacts and should be removed to keep the production console clean.

🧹 Proposed fix
-    console.log(project)
-    console.log(workspace)
🤖 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 `@apps/web/src/pages/IssueListPage.tsx` around lines 589 - 590, Remove the
leftover console.log statements for project and workspace from the surrounding
IssueListPage component, leaving the production logic unchanged.
🤖 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 `@apps/api/internal/handler/analytics.go`:
- Around line 254-266: Update the CSV export handler around csv.Writer creation
to check errors from both the header and per-issue writer.Write calls, and
ensure writer.Flush is executed before inspecting writer.Error(). Propagate or
return an appropriate error when any write or flush error occurs, including the
analogous export block around the second issue loop.
- Around line 14-16: Refactor AnalyticsHandler and all four analytics/export
handlers so they resolve the authenticated user and delegate authorization-aware
workspace/project access checks to a service, including validating the project
slug. Move every GORM query out of the handlers into a store called by that
service, preserving the existing response behavior only after access is
authorized.
- Around line 19-24: The analytics response contract lacks populated
created-versus-resolved trend data. In
apps/api/internal/handler/analytics.go:19-24, extend AnalyticsResponse with a
typed time-series field and populate it through the analytics handler’s existing
data flow; in apps/web/src/pages/AnalyticsWorkItemsPage.tsx:246, consume that
response field with the corresponding concrete type instead of any[] so the
chart receives real backend data.
- Around line 257-265: Update the issue row export in the analytics CSV handler
to neutralize spreadsheet formulas in user-controlled fields before passing them
to csv.Writer. Apply the same sanitization to issue.Name, issue.State, and
issue.Priority, and also to the corresponding cells in the referenced export
block around lines 300-307; preserve IDs and headers unless they are subject to
the same unsafe-value requirement.
- Around line 90-120: Update the analytics handler’s aggregate-query error
handling, including the assignee and label flows and the secondary project-query
section, so any database failure is returned or surfaced through an explicit
partial-result status instead of leaving empty maps that imply zero counts.
Preserve successful aggregation behavior and ensure the response clearly
distinguishes valid empty metrics from failed queries.

In `@apps/api/internal/router/router.go`:
- Around line 436-437: Preserve trailing slashes for both project analytics
routes in apps/api/internal/router/router.go at lines 436-437 by registering
slash-terminated paths. Update the export request in
apps/web/src/pages/AnalyticsWorkItemsPage.tsx at lines 206-208 to use the
corresponding slash-terminated URL.

In `@apps/web/src/pages/AnalyticsWorkItemsPage.tsx`:
- Line 190: Update both catch clauses in AnalyticsWorkItemsPage, including the
blocks around the existing catch handlers, to remove the unused err bindings and
use bindingless catch syntax while preserving their current error-handling
bodies.
- Line 267: Replace the fixed English user-facing labels in
AnalyticsWorkItemsPage, including the KPI, chart-axis, table, action, and export
labels near the referenced sections, with the existing t(...) translation flow.
Add or reuse appropriate translation keys for each label while preserving the
current rendered text and behavior.
- Around line 141-174: Update the workspace-loading effect around getBySlug so
the analytics and project requests are awaited before setLoading(false) runs,
rather than being launched in nested promise chains. Add and maintain an
analytics error state when the analytics fetch fails, and render that error
instead of silently treating missing analytics data as zero; preserve
cancellation checks before state updates.
- Around line 235-239: Update the KPI count logic in AnalyticsWorkItemsPage
around backlogCount, startedCount, unstartedCount, completedCount, and
totalIssues to derive counts from the configured stable state groups rather than
hard-coded display-name keys. Ensure totalIssues sums every value in
analytics.by_state, including Cancelled and custom states, while preserving the
existing KPI bucket semantics through the group definitions.

In `@apps/web/src/pages/IssueListPage.tsx`:
- Line 185: Update the safeFetch helper to use a generic type parameter, such as
the TSX-safe T, so its promise and fallback share the same type and the returned
Promise is typed accordingly. Preserve the existing fallback behavior and ensure
downstream Promise.all results are inferred without any.

---

Nitpick comments:
In `@apps/web/src/pages/IssueListPage.tsx`:
- Around line 589-590: Remove the leftover console.log statements for project
and workspace from the surrounding IssueListPage component, leaving the
production logic unchanged.
🪄 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: bb305e98-fd9a-42a8-be2f-354597bce129

📥 Commits

Reviewing files that changed from the base of the PR and between a81f6a3 and 112424c.

⛔ Files ignored due to path filters (1)
  • apps/web/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (5)
  • apps/api/internal/handler/analytics.go
  • apps/api/internal/router/router.go
  • apps/web/package.json
  • apps/web/src/pages/AnalyticsWorkItemsPage.tsx
  • apps/web/src/pages/IssueListPage.tsx

Comment thread apps/api/internal/handler/analytics.go Outdated
Comment thread apps/api/internal/handler/analytics.go Outdated
Comment thread apps/api/internal/handler/analytics.go Outdated
Comment thread apps/api/internal/handler/analytics.go Outdated
Comment thread apps/api/internal/handler/analytics.go Outdated
Comment thread apps/web/src/pages/AnalyticsWorkItemsPage.tsx Outdated
Comment thread apps/web/src/pages/AnalyticsWorkItemsPage.tsx Outdated
Comment thread apps/web/src/pages/AnalyticsWorkItemsPage.tsx Outdated
Comment thread apps/web/src/pages/AnalyticsWorkItemsPage.tsx Outdated
Comment thread apps/web/src/pages/IssueListPage.tsx Outdated
@LADsy8 LADsy8 closed this Jul 22, 2026
@LADsy8 LADsy8 reopened this Jul 24, 2026
@LADsy8 LADsy8 closed this Jul 24, 2026
@LADsy8 LADsy8 reopened this Jul 24, 2026

@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: 4

🤖 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 `@apps/api/internal/service/analytics.go`:
- Around line 97-113: Update GetWorkspaceAnalytics and GetProjectAnalytics so
failures from GetWorkspaceAssigneeAnalytics or GetWorkspaceLabelAnalytics are
propagated as errors instead of returning successful responses with empty maps.
Preserve the existing map population for successful queries, and retain warning
logs only if compatible with the returned error flow.
- Around line 15-20: Extend AnalyticsResponse with a JSON-serialized time-series
field for created-versus-resolved trend data, using the project’s established
trend data type or shape and naming it consistently with the existing response
fields. Ensure the analytics handler populates this field so the frontend chart
receives the trend data.
- Around line 172-188: Change ExportWorkspaceCSV and ExportProjectCSV to avoid
accumulating all rows in slices; thread a row callback or io.Writer from the CSV
handler through the service and store so each item is written or processed as
StreamWorkspaceIssuesForExport/StreamProjectIssuesForExport scans it. Preserve
workspace access checks and error propagation while ensuring CSV output is
streamed incrementally.

In `@apps/api/internal/store/analytics.go`:
- Around line 47-60: The assignee analytics queries omit issues with nullable
assignee_id. In GetWorkspaceAssigneeAnalytics and GetProjectAssigneeAnalytics,
change the users join to preserve unassigned issues and group them into an
explicit “Unassigned” bucket, while retaining user email values for assigned
issues and keeping the existing workspace/project filters intact.
🪄 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: 4d3a4a45-2996-4760-b1ab-fc1148ed7c63

📥 Commits

Reviewing files that changed from the base of the PR and between 112424c and c61acc9.

📒 Files selected for processing (7)
  • apps/api/internal/handler/analytics.go
  • apps/api/internal/model/analytics.go
  • apps/api/internal/router/router.go
  • apps/api/internal/service/analytics.go
  • apps/api/internal/store/analytics.go
  • apps/web/src/pages/AnalyticsWorkItemsPage.tsx
  • apps/web/src/pages/IssueListPage.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/api/internal/router/router.go
  • apps/web/src/pages/AnalyticsWorkItemsPage.tsx

Comment thread apps/api/internal/service/analytics.go
Comment thread apps/api/internal/service/analytics.go
Comment thread apps/api/internal/service/analytics.go Outdated
Comment thread apps/api/internal/store/analytics.go

@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: 1

🤖 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 `@apps/api/internal/store/analytics.go`:
- Around line 181-188: Update both workspace and project trend queries at
apps/api/internal/store/analytics.go:181-188 and
apps/api/internal/store/analytics.go:198-203 to derive resolved counts from
completed/cancelled state transitions, using state_id/states.group and
issue_activities.created_at where appropriate, instead of issues.created_at and
issues.state. Ensure resolved issues are bucketed by the timestamp when they
entered the resolved state while preserving created counts by creation date.
🪄 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: ccc4d2b1-699f-479e-94ca-4ba11fd2e343

📥 Commits

Reviewing files that changed from the base of the PR and between c61acc9 and 8932557.

📒 Files selected for processing (3)
  • apps/api/internal/model/analytics.go
  • apps/api/internal/service/analytics.go
  • apps/api/internal/store/analytics.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/api/internal/service/analytics.go
  • apps/api/internal/model/analytics.go

Comment thread apps/api/internal/store/analytics.go Outdated

@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: 1

🤖 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 `@apps/api/internal/service/analytics.go`:
- Around line 189-201: Complete the CSV export contract across the workspace and
project paths: extend the reduced export models in analytics.go to carry
assignee and label values, update the corresponding store queries to select and
join those fields, propagate them through the ExportWorkspaceCSV and
ExportProjectCSV callback types, and add matching columns in the analytics
handlers’ CSV output. Keep model fields, query results, callback payloads,
headers, and row serialization aligned for both exports.
🪄 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: 1c051704-f3a6-4db8-b484-bd6ebebda0f5

📥 Commits

Reviewing files that changed from the base of the PR and between 8932557 and 17a7e01.

📒 Files selected for processing (3)
  • apps/api/internal/handler/analytics.go
  • apps/api/internal/service/analytics.go
  • apps/api/internal/store/analytics.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/api/internal/store/analytics.go
  • apps/api/internal/handler/analytics.go

Comment thread apps/api/internal/service/analytics.go
@LADsy8

LADsy8 commented Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

@martian56, i think i went above the scope of the assigned issues, but there is a few things i didnt add or restrained myself of adding because it was not in my scope

@martian56 martian56 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM

@martian56
martian56 merged commit 303f75d into Devlaner:main Jul 25, 2026
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.

[FEAT] Add server-side analytics endpoints and working CSV export

2 participants