Skip to content

[#398] COMPONENTS - Added reusable issue fetcher pane#429

Open
Jomak-x wants to merge 3 commits intomainfrom
jakob/issue-fetcher-clean
Open

[#398] COMPONENTS - Added reusable issue fetcher pane#429
Jomak-x wants to merge 3 commits intomainfrom
jakob/issue-fetcher-clean

Conversation

@Jomak-x
Copy link

@Jomak-x Jomak-x commented Mar 25, 2026

Why

One shared issue filter/fetch component that can be reused across different Blade views later.

What

Issue(s): #398

Added a reusable IssueFetcherPane component that handles shared issue filtering and fetching.

Also added a small IssueFetcherPanePlayground example to show how a parent view can receive the filtered issues and use them for later views like list, kanban, or calendar.

Test Plan

image

Checklist

  • Database: No schema changes, OR I have contacted the Development Lead to run db:push before merging
  • Environment Variables: No environment variables changed, OR I have contacted the Development Lead to modify them on Coolify BEFORE merging.

Summary by CodeRabbit

  • New Features
    • Added an issue fetching pane with advanced filters: status, team selector, type (event-linked vs tasks), root-only, date range, and free-text search.
    • Issue list UI with refresh, loading/error/empty states, clear-filters, and count display.
    • Issue cards show name, status/priority badges, event-linked and blocked indicators, description, team, due date, parent/subtask flags, assignment/visibility counts, and ID.

@coderabbitai
Copy link

coderabbitai bot commented Mar 25, 2026

Warning

Rate limit exceeded

@Jomak-x has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 6 minutes and 9 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 648ec2b4-9d58-4d40-95b9-acc5463c918b

📥 Commits

Reviewing files that changed from the base of the PR and between 14ecd0b and 7e3bf7d.

📒 Files selected for processing (2)
  • apps/blade/src/app/_components/issues/issue-fetcher-pane-playground.tsx
  • apps/blade/src/app/_components/issues/issue-fetcher-pane.tsx
📝 Walkthrough

Walkthrough

Adds two client-side React components: IssueFetcherPane (fetches, filters, and computes derived issue data) and IssueFetcherPanePlayground (local demo that renders filtered issue cards and consumes pane output).

Changes

Cohort / File(s) Summary
Issue Fetcher Core
apps/blade/src/app/_components/issues/issue-fetcher-pane.tsx
New IssueFetcherPane component plus exported types (IssueFilters, IssueFetcherPaneIssue, IssueFetcherPaneData) and DEFAULT_ISSUE_FILTERS. Builds TRPC query input from filters (converts YYYY-MM-DD to Date bounds), fetches roles, computes blockedParentIds, applies client-side search/type/root-only filters, memoizes outputs, and exposes onDataChange/setIssues callbacks. Attention: date parsing, blocked-parent grouping, and memoization correctness.
Playground / UI Rendering
apps/blade/src/app/_components/issues/issue-fetcher-pane-playground.tsx
New IssueFetcherPanePlayground demo component that holds local issues and fetcherData state, renders header/count, conditional loading/error/empty messages, and issue cards with badges (status, priority, EVENT-LINKED, BLOCKED), description, and derived fields (team via roleNameById, formatted due date, parent/subtask indicators, visibility/assignee counts, ID).

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant Playground as IssueFetcherPanePlayground
  participant Pane as IssueFetcherPane
  participant TRPC as TRPC Server
  participant DB as Database

  User->>Playground: open UI / interact filters
  Playground->>Pane: mount / setFilters (props)
  Pane->>TRPC: query issues.getAllIssues(filters)
  Pane->>TRPC: query roles.getRoleLinks()
  TRPC->>DB: fetch issues & roles
  DB-->>TRPC: issues & roles
  TRPC-->>Pane: issues data, role links
  Pane->>Pane: compute blockedParentIds, apply client-side filters, memoize IssueFetcherPaneData
  Pane-->>Playground: emit IssueFetcherPaneData (onDataChange) / setIssues
  Playground->>User: render issue cards (badges, derived fields)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Suggested labels

Blade, Feature, Minor

🚥 Pre-merge checks | ✅ 5 | ❌ 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 (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
No Hardcoded Secrets ✅ Passed Both new files contain no hardcoded API keys, passwords, tokens, or secrets. All external API interactions properly use TRPC client configuration.
Validated Env Access ✅ Passed New component files properly delegate API calls to TRPC endpoints, avoiding direct process.env access and following codebase convention of centralizing environment variable handling server-side.
No Typescript Escape Hatches ✅ Passed No instances of any, @ts-ignore, @ts-expect-error, or non-null assertions (!.) found. Code demonstrates strong TypeScript practices with optional chaining and nullish coalescing.
Title check ✅ Passed The title includes the required issue number in brackets, follows the specified format, and is under 72 characters (53 chars total).

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jakob/issue-fetcher-clean

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 and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 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/blade/src/app/_components/issues/issue-fetcher-pane-playground.tsx`:
- Around line 13-15: The header/body currently render as if there are zero
results when fetcherData is null; change the component to treat fetcherData ===
null as "not ready" by gating the header and body rendering on fetcherData !==
null (i.e., only render counts, "No issues match…" messaging, and result UI when
fetcherData is non-null), and ensure any places referencing fetcherData (the
useState [fetcherData, setFetcherData] and the IssueFetcherPaneData-dependent
sections) early-return or show a loading/placeholder until fetcherData is
populated so the pane shows a single coherent loading/error/result state.

In `@apps/blade/src/app/_components/issues/issue-fetcher-pane.tsx`:
- Around line 84-118: The pane currently treats rolesQuery and issuesQuery
independently (rolesQuery, issuesQuery, fetchedIssues, issuesError, isLoading,
refetch, roles, roleNameById, issueErrorMessage) which allows partial renders
when one query fails; instead create a single combined query state: compute
combinedIsLoading = rolesQuery.isLoading || issuesQuery.isLoading, combinedError
= rolesQuery.error ?? issuesQuery.error (or aggregate both), and a
combinedRefetch that calls both refetch functions (e.g.
Promise.all([rolesQuery.refetch(), issuesQuery.refetch()])); use
combinedIsLoading/combinedError/combinedRefetch for all rendering, error
reporting, and the parent refresh API so the component only renders "ready" when
both queries succeed and the Refresh button retries both queries.
- Around line 219-352: Add id/htmlFor associations for each labeled control:
give the Status SelectTrigger an id (e.g., "status-select") and set the
corresponding Label htmlFor to that id; do the same for Team ("team-select"),
Type ("type-select"), Search Input ("search-input"), Date From
("date-from-input"), Date To ("date-to-input"), and the Checkbox
("rootOnly-checkbox"). Update the Label components to include htmlFor and pass
matching id props into SelectTrigger / Input / Checkbox so the Label, Select
(SelectTrigger/SelectValue), Input, and Checkbox components (used in this file)
are properly associated and remain controlled via filters/setFilters (leave
existing onValueChange/onChange/onCheckedChange handlers intact); no logic
changes to DEFAULT_ISSUE_FILTERS required.
🪄 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: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 7ea50fb7-1a8f-4ddc-9675-15fe5eff5fe6

📥 Commits

Reviewing files that changed from the base of the PR and between a2a5348 and 14b9eb1.

📒 Files selected for processing (2)
  • apps/blade/src/app/_components/issues/issue-fetcher-pane-playground.tsx
  • apps/blade/src/app/_components/issues/issue-fetcher-pane.tsx

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (2)
apps/blade/src/app/_components/issues/issue-fetcher-pane.tsx (2)

85-118: ⚠️ Potential issue | 🟠 Major

Unify pane loading/error/refresh across both required queries.

Line 114/117 and Line 173 only track issuesQuery, so the pane can report “ready” while roles are still loading/failed, and Refresh won’t retry roles. That breaks the single-source contract for parent views.

Suggested fix
-  const rolesQuery = api.roles.getAllLinks.useQuery(undefined, {
+  const {
+    data: rolesData,
+    error: rolesError,
+    isLoading: rolesLoading,
+    refetch: refetchRoles,
+  } = api.roles.getAllLinks.useQuery(undefined, {
     refetchOnWindowFocus: false,
   });

@@
-  const {
-    data: fetchedIssues,
-    error: issuesError,
-    isLoading,
-    refetch,
-  } = issuesQuery;
-  const issueErrorMessage = issuesError?.message ?? null;
+  const {
+    data: fetchedIssues,
+    error: issuesError,
+    isLoading: issuesLoading,
+    refetch: refetchIssues,
+  } = issuesQuery;
+  const isLoading = rolesLoading || issuesLoading;
+  const errorMessage = rolesError?.message ?? issuesError?.message ?? null;

-  const roles = useMemo(() => rolesQuery.data ?? [], [rolesQuery.data]);
+  const roles = useMemo(() => rolesData ?? [], [rolesData]);

   const refresh = useCallback(() => {
-    void refetch();
-  }, [refetch]);
+    void Promise.all([refetchRoles(), refetchIssues()]);
+  }, [refetchIssues, refetchRoles]);

@@
-      error: issueErrorMessage,
+      error: errorMessage,
@@
-      issueErrorMessage,
+      errorMessage,

Based on learnings: "gating rendering should occur only when all required data fetches succeed. Do not render partial success when some queries fail; instead, implement a unified loading/state or error handling that surfaces a single, coherent state once all data is ready or failed."

Also applies to: 172-183, 224-229

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/blade/src/app/_components/issues/issue-fetcher-pane.tsx` around lines 85
- 118, The pane currently only observes issuesQuery
(fetchedIssues/issuesError/isLoading/refetch) so rolesQuery can be
loading/failed while the UI reports "ready"; update the component to derive a
unified state from both rolesQuery and issuesQuery (e.g., combine
rolesQuery.data && issuesQuery.data for ready, rolesQuery.isLoading ||
issuesQuery.isLoading for loading, and prefer a merged error message from
rolesQuery.error or issuesQuery.error), and change the refresh handling to call
both rolesQuery.refetch() and issuesQuery.refetch() so Refresh retries both;
locate the rolesQuery and issuesQuery variables in issue-fetcher-pane.tsx to
implement this unified loading/error/refresh behavior.

234-366: ⚠️ Potential issue | 🟠 Major

Add explicit Label ↔ control associations (htmlFor/id).

The filter labels are visually present but not programmatically bound to inputs/select triggers/checkbox. This hurts screen-reader labeling and label click behavior.

Suggested fix
-          <Label>Status</Label>
+          <Label htmlFor="issue-filter-status">Status</Label>
@@
-            <SelectTrigger>
+            <SelectTrigger id="issue-filter-status">
@@
-          <Label>Team</Label>
+          <Label htmlFor="issue-filter-team">Team</Label>
@@
-            <SelectTrigger>
+            <SelectTrigger id="issue-filter-team">
@@
-          <Label>Type</Label>
+          <Label htmlFor="issue-filter-type">Type</Label>
@@
-            <SelectTrigger>
+            <SelectTrigger id="issue-filter-type">
@@
-          <Label>Search</Label>
+          <Label htmlFor="issue-filter-search">Search</Label>
           <Input
+            id="issue-filter-search"
             placeholder="Search name/description/id..."
@@
-          <Label>Date From</Label>
+          <Label htmlFor="issue-filter-date-from">Date From</Label>
           <Input
+            id="issue-filter-date-from"
             type="date"
@@
-          <Label>Date To</Label>
+          <Label htmlFor="issue-filter-date-to">Date To</Label>
           <Input
+            id="issue-filter-date-to"
             type="date"
@@
         <Checkbox
+          id="issue-filter-root-only"
           checked={filters.rootOnly}
@@
-        <Label>Show root issues only (hide subtasks)</Label>
+        <Label htmlFor="issue-filter-root-only">
+          Show root issues only (hide subtasks)
+        </Label>

As per coding guidelines: "apps/blade/** ... Accessibility (alt text, ARIA, semantic HTML)."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/blade/src/app/_components/issues/issue-fetcher-pane.tsx` around lines
234 - 366, Labels in the filter pane are not associated with their form controls
(Label vs Input/Select/Checkbox), breaking accessibility; add explicit id
attributes to each control and set the corresponding Label htmlFor to match.
Specifically, assign unique ids for the Status Select (tie to Select or
SelectTrigger depending on the Select implementation), Team Select, Type Select,
Search/Input, Date From, Date To, and the rootOnly Checkbox, and update each
Label's htmlFor to those ids; keep ids unique (e.g., status-filter, team-filter,
type-filter, search-filter, date-from-filter, date-to-filter, root-only-filter)
and ensure any custom Select component uses the id in the actionable trigger
element so clicking the Label focuses the control.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@apps/blade/src/app/_components/issues/issue-fetcher-pane.tsx`:
- Around line 85-118: The pane currently only observes issuesQuery
(fetchedIssues/issuesError/isLoading/refetch) so rolesQuery can be
loading/failed while the UI reports "ready"; update the component to derive a
unified state from both rolesQuery and issuesQuery (e.g., combine
rolesQuery.data && issuesQuery.data for ready, rolesQuery.isLoading ||
issuesQuery.isLoading for loading, and prefer a merged error message from
rolesQuery.error or issuesQuery.error), and change the refresh handling to call
both rolesQuery.refetch() and issuesQuery.refetch() so Refresh retries both;
locate the rolesQuery and issuesQuery variables in issue-fetcher-pane.tsx to
implement this unified loading/error/refresh behavior.
- Around line 234-366: Labels in the filter pane are not associated with their
form controls (Label vs Input/Select/Checkbox), breaking accessibility; add
explicit id attributes to each control and set the corresponding Label htmlFor
to match. Specifically, assign unique ids for the Status Select (tie to Select
or SelectTrigger depending on the Select implementation), Team Select, Type
Select, Search/Input, Date From, Date To, and the rootOnly Checkbox, and update
each Label's htmlFor to those ids; keep ids unique (e.g., status-filter,
team-filter, type-filter, search-filter, date-from-filter, date-to-filter,
root-only-filter) and ensure any custom Select component uses the id in the
actionable trigger element so clicking the Label focuses the control.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 89eb541b-0f9e-4a80-8a50-93db5a3a4812

📥 Commits

Reviewing files that changed from the base of the PR and between 14b9eb1 and 14ecd0b.

📒 Files selected for processing (1)
  • apps/blade/src/app/_components/issues/issue-fetcher-pane.tsx

@Jomak-x Jomak-x self-assigned this Mar 25, 2026
@Jomak-x Jomak-x added the UI Changes modifies code in the global UI package label Mar 25, 2026
@Jomak-x Jomak-x changed the title add reusable issue fetcher pane COMPONENTS - Added reusable issue fetcher pane Mar 25, 2026
@Jomak-x Jomak-x changed the title COMPONENTS - Added reusable issue fetcher pane [#398] COMPONENTS - Added reusable issue fetcher pane Mar 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

UI Changes modifies code in the global UI package

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant