Skip to content

feat(linear): functional Filter button + fix PATCH /api/issues 404#10

Open
obvious-autobuild[bot] wants to merge 3 commits into
mainfrom
fix/linear-functional-filter
Open

feat(linear): functional Filter button + fix PATCH /api/issues 404#10
obvious-autobuild[bot] wants to merge 3 commits into
mainfrom
fix/linear-functional-filter

Conversation

@obvious-autobuild

@obvious-autobuild obvious-autobuild Bot commented Jun 16, 2026

Copy link
Copy Markdown

Why

Two issues in the linear app, both surfaced while making the Filter control work:

  1. The Filter button did nothing. In main-view.tsx the button rendered the Filter icon + label with no onClick, no state, and no UI — users could not narrow the issue list at all.
  2. PATCH /api/issues/:id intermittently 404'd, so status changes never stuck. The backend kept issues in a module-level array. Netlify scales functions out to multiple isolated instances, so an issue created (or deleted) on one instance was invisible to a request routed to another. Reproduced on the preview deploy: a freshly created issue 404'd on ~70% of concurrent PATCHes, while seeded issues (present in every instance's initial array) always succeeded.

The avatar-contrast fix this PR originally also carried has since landed on main independently (getContrastTextColor in @/lib/utils). After rebasing, this PR drops its duplicate helper and reuses main's — and extends it to the one new avatar call site the Filter feature introduces (the assignee rows in the filter popover).

What

Filter feature — A Radix popover anchored to the Filter button lets users filter by status, priority, and assignee. Selections compose on top of the existing sidebar view filter (view filter first, then user filters); empty in a dimension means "no constraint". A count badge shows active filters, a clear action resets them, and a filter-aware empty state renders when nothing matches. New filtering logic lives in lib/issue-filters.ts (applyIssueFilters, countActiveFilters); filter state follows the existing Zustand ui-store pattern. The new assignee-row avatars use main's getContrastTextColor so initials stay AA-readable.

PATCH 404 (root cause) — Backed the issue collection with Netlify Blobs (getDeployStore, strong consistency), seeded from the static seed data, so every function instance shares one durable view. A local in-memory fallback keeps netlify dev working when Blobs is unavailable. The route regex now tolerates an optional trailing slash. Create/list/search/delete all go through the shared store, so no route can desync.

How to Review

  • Filter: components/issue-filter-popover.tsx, components/main-view.tsx, lib/issue-filters.ts, store/ui-store.ts.
  • PATCH fix: netlify/functions/lib/issue-store.ts (new durable store), netlify/functions/api.mts (routes now async + trailing-slash tolerant), netlify/functions/lib/data.ts (seed split into SEED_ISSUES/SEED_COUNTERS, makeIssue now pure).
  • Rebased onto latest main; the avatar-contrast work is now main's getContrastTextColor, reused (not re-implemented) at the new filter-popover call site.
  • Excluded: the dead "Display" button is intentionally left out of scope.

Test Evidence

  • Filter / build: vite build succeeds (no runtime errors). Lint clean for all changed/new files (3 remaining errors are pre-existing in untouched files — confirmed on base main).
  • PATCH root cause, reproduced on the preview deploy: 30 concurrent PATCHes on a freshly-created issue returned a mix of 200/404 (~70% 404); 30 concurrent PATCHes on a seeded issue all returned 200. After the Blobs-backed store, creates persist across instances.
  • npm ci --dry-run passes with the updated package-lock.json (adds @netlify/blobs).

Acceptance Criteria

  • Filter button opens a popover; filter by status/priority/assignee, combined with the view filter
  • Filter dimensions combine (AND across dimensions; empty = no constraint)
  • Active-filter count badge + clear action
  • Filter-aware empty state when zero matches
  • PATCH /api/issues/:id succeeds and persists status + patchable fields across refetch; no regression to list/create/delete
  • Avatar initials meet AA contrast (via main's shared getContrastTextColor) at all call sites including the new filter popover
  • Lint passes; Vite build passes

@obvious-autobuild obvious-autobuild Bot marked this pull request as ready for review June 16, 2026 14:50
@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown

Netlify PR Previews

Base: origin/main
Head: HEAD
PR: #10

Project URL Deploy
linear replay-linear logs

Replay QA

Project Preview QA Project Result Open Bugs
linear preview project timed out 0

@obvious-autobuild obvious-autobuild Bot changed the title feat(linear): make Filter button functional feat(linear): functional Filter button + fix PATCH 404 & avatar contrast Jun 16, 2026
@obvious-autobuild

Copy link
Copy Markdown
Author

Replying to this comment

Both open bugs from this QA run are addressed in the latest push (e7b7019):

  • PATCH /api/issues/:id 404 (high) — root cause was the backend keeping issues in a module-level array that is not shared across Netlify function instances; a created/deleted issue on one instance was invisible to the next, so PATCH intermittently 404'd. Now backed by Netlify Blobs (getDeployStore, strong consistency) so every instance shares one durable view; route also tolerates an optional trailing slash. Reproduced the failure on the preview (freshly-created issue 404'd ~70% under concurrent PATCH) and confirmed seeded issues always 200.
  • Avatar initials contrast (medium) — fallbacks hardcoded text-white; added a readableTextColor() luminance helper + shared InitialsAvatar. Verified: indigo 4.70:1, teal 8.58:1, orange 9.43:1 — all >= 4.5:1 AA.

A fresh QA run should pick up these fixes.

Obvious Agent added 3 commits June 16, 2026 17:05
The "Filter" button in the issue list header was purely decorative — a
Button with no onClick and no filter state — so users could not narrow
the visible issue list beyond the sidebar view.

Add a filter popover anchored to the button that filters issues by
status, priority, and assignee on top of the existing view filter:

- issue-filters.ts: pure `IssueFilters` type plus `applyIssueFilters`
  (composes view filter then status/priority/assignee) and
  `countActiveFilters`. Empty dimension = no constraint; dimensions
  combine with AND.
- ui-store.ts: `filters` state with `setFilters`/`clearFilters`,
  following the existing Zustand pattern.
- issue-filter-popover.tsx: Radix Popover + Checkbox built from existing
  UI primitives, with a count badge on the trigger, a clear action, and
  a loading/empty state for assignees.
- main-view.tsx: wire the popover in and show a filter-aware empty state
  with a clear-filters action when no issues match.
Replay QA flagged a redundant /api/users refetch on page load. The
shared `useUsers` query (consumed by the issue list, issue detail, and
the new filter popover's assignee section) had no staleTime, so React
Query treated cached user data as immediately stale and refetched it.

Users are reference data that rarely change within a session — add a
5-minute staleTime so the cached result is reused across consumers.
The backend stored issues in a module-level array. Netlify scales
functions out to multiple isolated instances, so an issue created or
deleted on one instance was invisible to a request routed to another:
PATCH /api/issues/:id intermittently returned 404 and status changes
never persisted. Reproduced on the preview deploy: a freshly created
issue 404'd on ~70% of concurrent PATCHes while seeded issues (present
in every instance's initial array) always succeeded.

Back the issue collection with Netlify Blobs (getDeployStore, strong
consistency) seeded from the static seed data, giving every instance one
shared, durable view. A local in-memory fallback keeps `netlify dev`
working when the Blobs backend is unavailable. Also tolerate an optional
trailing slash on /issues/:id so PATCH/DELETE match consistently.
@obvious-autobuild obvious-autobuild Bot force-pushed the fix/linear-functional-filter branch from e7b7019 to 7a5df18 Compare June 16, 2026 17:06
@obvious-autobuild obvious-autobuild Bot changed the title feat(linear): functional Filter button + fix PATCH 404 & avatar contrast feat(linear): functional Filter button + fix PATCH /api/issues 404 Jun 16, 2026
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.

0 participants