Skip to content

feat: assigned me issues#152

Merged
Flo0806 merged 3 commits intomainfrom
feat/assigned-me-issues
Feb 27, 2026
Merged

feat: assigned me issues#152
Flo0806 merged 3 commits intomainfrom
feat/assigned-me-issues

Conversation

@Flo0806
Copy link
Contributor

@Flo0806 Flo0806 commented Feb 27, 2026

Summary

  • Assigned me on issues page

Related issue(s)

Closes #30

Type of change

  • Bug fix
  • Feature
  • Refactor
  • Docs
  • CI

Checklist

  • Tests added/updated
  • i18n keys added/updated (if needed)
  • No breaking changes

Summary by CodeRabbit

  • New Features

    • Added "Assigned to me" filter.
    • Added skeleton loading placeholders for issue lists.
  • Improvements

    • Switched to server-backed, cursor-based pagination for more consistent paging and accurate total counts.
    • Updated issue list sorting and count display to reflect server results.
  • Bug Fixes

    • Enforced mutual exclusivity for conflicting issue filters.
  • Tests

    • Updated tests to align with server-driven responses and new filter behavior.
  • Localization

    • Updated translations for the new/removed filters.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 27, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8cdcc68 and 58de8f0.

📒 Files selected for processing (5)
  • app/stores/issues.ts
  • i18n/locales/de.json
  • i18n/locales/en.json
  • i18n/schema.json
  • server/api/issues/index.get.ts

📝 Walkthrough

Walkthrough

Refactors issue pagination to use a new composable, moves filtering from client to server (adds assignedToMe and exclusive-filter logic), updates API response shape and default page size, replaces loading text with skeleton UI, and updates tests and i18n keys to match the new contract. (46 words)

Changes

Cohort / File(s) Summary
Pagination Composable & Removal
app/composables/usePaginatedSection.ts, app/stores/focus.ts
Adds usePaginatedSection composable (cursor caching, staleness, navigation) and removes the old paginated-section implementation from focus.ts.
Issue Store Integration
app/stores/issues.ts
Replaces local client-side filtering/pagination with usePaginatedSection; exposes section.data as issues, adds sortedIssues, and maps pagination actions to the composable.
Toolbar & Filters UI
app/components/issue/IssueToolbar.vue, app/components/issue/IssueRowSkeleton.vue, app/pages/issues/index.vue
Introduces exclusive filter handling, adds assignedToMe filter chip (removes hasLinkedPr/noLinkedPr), updates loading UI to skeleton components, and switches listing to sortedIssues.
API Changes
server/api/issues/index.get.ts
Extends query params (assignedToMe, unassigned, label, milestone), enforces mutual exclusivity for assigned/unassigned, changes response shape from { issues } to { items }, returns PaginatedResponse<Issue>, and sets default page size to 20.
i18n / Schema
i18n/locales/en.json, i18n/locales/de.json, i18n/schema.json
Adds assignedToMe translation key and schema entry; removes serverSearch, hasLinkedPr, and noLinkedPr keys.
Tests
test/nuxt/issueStore.test.ts
Updates mocks and assertions to new API shape (items) and server-driven filtering; switches expectations from filteredIssues to sortedIssues.

Sequence Diagram(s)

sequenceDiagram
    participant UI as Issue Page UI
    participant Store as Issues Store
    participant Composable as usePaginatedSection
    participant Cache as Local Cache
    participant API as Issues API

    UI->>Store: request nextPage()
    Store->>Composable: nextPage()
    Composable->>Cache: check cached page (by cursor)
    alt cached
        Cache-->>Composable: return cached items
        Composable->>Composable: apply cached page
    else not cached
        Composable->>API: fetch(after: endCursor, params)
        API-->>Composable: { items, totalCount, pageInfo }
        Composable->>Cache: store result by cursor
    end
    Composable->>Store: update reactive state (data,totalCount,hasMore)
    Store-->>UI: reactive update (sortedIssues)
Loading
sequenceDiagram
    participant UI as IssueToolbar UI
    participant Store as Issues Store
    participant API as Issues API

    UI->>Store: toggleFilter("assignedToMe")
    Store->>Store: enforce EXCLUSIVE_FILTERS (remove conflicts)
    Store->>Store: update activeFilters
    Store->>Store: buildParams() with filters
    Store->>Composable: section.refresh()
    Composable->>API: fetch(first:20, params)
    API-->>Composable: { items, totalCount, pageInfo }
    Composable->>Store: updated section.data
    Store-->>UI: reactive update (sortedIssues)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • Gonzo17

"🐰
I hopped through cursors, cached each page,
Filters exclusive, no duplicate lane.
Skeletons shimmer as items arrive,
Assigned to me — now I thrive! ✨"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR includes significant pagination refactoring (usePaginatedSection composable and store restructuring) that goes beyond the stated objective of adding an 'Assigned to You' section to the Issues page. Clarify whether the pagination refactoring is necessary infrastructure for the feature, or consider separating it into a distinct refactoring PR for better review clarity.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: assigned me issues' refers to a core feature in the changeset but is somewhat vague and incomplete regarding the full scope of pagination and UI improvements.
Description check ✅ Passed The description covers the main summary, related issue, and type of change, but lacks detailed explanation of what changed and why, particularly regarding the pagination refactoring.
Linked Issues check ✅ Passed The PR implements the core requirement from issue #30 by adding an 'Assigned to Me' filter and UI treatment, though the description could clarify the pagination refactoring as supporting infrastructure.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/assigned-me-issues

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

Copy link
Contributor

@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

Caution

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

⚠️ Outside diff range comments (2)
i18n/locales/en.json (1)

282-288: ⚠️ Potential issue | 🟡 Minor

Pipeline failure: remove unused keys to fix CI.

Same as de.json — remove issues.serverSearch, issues.filter.hasLinkedPr, and issues.filter.noLinkedPr to resolve the pipeline errors.

🧹 Proposed fix
     "serverSearch": "Searching...",
     "filter": {
       "assignedToMe": "Assigned to me",
       "unassigned": "Unassigned",
-      "hasLinkedPr": "Has PR",
-      "noLinkedPr": "No PR",
       "hasMilestone": "Milestone"
     },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@i18n/locales/en.json` around lines 282 - 288, Remove the unused localization
keys that are causing CI failures: delete issues.serverSearch and the two filter
keys issues.filter.hasLinkedPr and issues.filter.noLinkedPr from en.json so it
matches the de.json changes; update the JSON object under "filter" to only
include the remaining keys (e.g., "assignedToMe", "unassigned", "hasMilestone")
and ensure the file remains valid JSON after removing those entries.
i18n/locales/de.json (1)

282-288: ⚠️ Potential issue | 🟡 Minor

Pipeline failure: unused translation keys need cleanup.

The CI pipeline reports these keys as unused:

  • issues.serverSearch (line 282)
  • issues.filter.hasLinkedPr (line 286)
  • issues.filter.noLinkedPr (line 287)

Since the PR removed the hasLinkedPr and noLinkedPr filter chips from IssueToolbar.vue, these keys should be removed from both locale files and the schema to fix the pipeline.

🧹 Proposed fix to remove unused keys
     "serverSearch": "Suche läuft...",
     "filter": {
       "assignedToMe": "Mir zugewiesen",
       "unassigned": "Nicht zugewiesen",
-      "hasLinkedPr": "Hat PR",
-      "noLinkedPr": "Kein PR",
       "hasMilestone": "Meilenstein"
     },

Also remove issues.serverSearch if it's no longer used in the codebase.

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

In `@i18n/locales/de.json` around lines 282 - 288, Remove the unused i18n keys
reported by CI: delete "issues.serverSearch", "issues.filter.hasLinkedPr", and
"issues.filter.noLinkedPr" from the German locale (i18n/locales/de.json) and the
corresponding English locale entry, and also remove these keys from the i18n
schema/translation types so they are no longer expected by the build; ensure you
also remove any remaining references in components such as IssueToolbar.vue (the
PR already removed the filter chips) and run the i18n validation to confirm the
pipeline error is resolved.
🧹 Nitpick comments (2)
test/nuxt/issueStore.test.ts (1)

167-175: Consider adding a test for the assignedToMe filter.

The PR introduces an assignedToMe filter, but there's no corresponding test. Consider adding one for completeness:

📝 Example test
const assignedToMeIssue: Issue = {
  ...mockIssue,
  id: 'I_assigned_me',
  assignees: [{ login: 'currentUser', avatarUrl: '' }],
}

// In registerEndpoint handler:
if (url.searchParams.has('assignedToMe')) {
  issues = [assignedToMeIssue]
}

// Test:
it('assignedToMe filter fetches issues assigned to current user from server', async () => {
  await withStore(async (store) => {
    await store.selectRepo('org/repo')
    store.activeFilters = ['assignedToMe']
    await store.fetchIssues()
    expect(store.issues).toHaveLength(1)
    expect(store.issues[0]?.id).toBe('I_assigned_me')
  })
})

Would you like me to open an issue to track adding this test coverage?

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

In `@test/nuxt/issueStore.test.ts` around lines 167 - 175, Add a unit test that
verifies the new assignedToMe filter returns only issues assigned to the current
user: in test/nuxt/issueStore.test.ts add a mock Issue (e.g., id 'I_assigned_me'
with assignees containing 'currentUser'), update the test server handler used in
registerEndpoint to return that issue when url.searchParams.has('assignedToMe'),
and add a new test case (similar to the existing unassigned test) that uses
withStore, calls store.selectRepo('org/repo'), sets store.activeFilters =
['assignedToMe'], awaits store.fetchIssues(), and asserts store.issues has
length 1 and store.issues[0]?.id === 'I_assigned_me'; reference functions:
fetchIssues, selectRepo, withStore, and the mockIssue used for other tests to
place the new mock consistently.
app/composables/usePaginatedSection.ts (1)

132-139: Consider preserving totalCount on refresh failure.

When refresh() fails, cursorHistory is restored but totalCount and data may have been cleared or left in an inconsistent state from the failed fetch. The current implementation clears the cache before fetching, so a failure leaves the user with potentially stale or empty data.

This is acceptable for a refresh operation (user explicitly requested fresh data), but worth noting.

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

In `@app/composables/usePaginatedSection.ts` around lines 132 - 139, The refresh()
implementation clears pageCache and resets cursorHistory but does not preserve
totalCount or data on a failed fetch; modify refresh() to snapshot current
totalCount and data (and cursorHistory as already done) before clearing, then
perform the clear/fetch, and if fetchData() returns false restore cursorHistory,
totalCount, and data (and optionally pageCache) to their previous values so the
UI stays consistent; reference functions/vars: refresh(), fetchData(),
pageCache, cursorHistory, totalCount, and data.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/stores/issues.ts`:
- Around line 55-60: handleError currently assumes section.refresh() will throw
on failure, but section.refresh() instead sets section.error, so fetchIssues
(and the code that sets loaded = true around lines 87–92) ends up marking
success on failed fetches; update the fetchIssues flow to check section.error
after calling section.refresh() (or inspect its result) and, if an error exists,
call handleError(section.error) and do not set loaded to true — reference the
section.refresh() call, the section.error property, the loaded flag, and the
handleError function when making this change.

In `@server/api/issues/index.get.ts`:
- Around line 52-53: Parse the incoming query params for assignedToMe and
unassigned explicitly into booleans (e.g., compare string values to 'true' or
'1') instead of relying on truthiness, and then validate their combination
before building the GitHub search query: if both assignedToMe and unassigned are
true, return a 400 error (or otherwise reject) because building both
`assignee:${login}` and `no:assignee` is contradictory; otherwise only append
`assignee:${login}` to query when the parsed assignedToMe boolean is true and
append `no:assignee` only when the parsed unassigned boolean is true. Ensure you
reference the same variables used in this file (assignedToMe, unassigned, query,
login) when adding the parsing and validation logic.
- Around line 54-58: The milestone/label values are inserted into the GitHub
search query unescaped and the special milestone value "*" is handled
incorrectly; update the code that builds query (using the milestone variable and
the label loop that splits String(label).split(',')) to (1) treat milestone ===
"*" by skipping adding a milestone filter (or use the explicit -no:milestone
handling if that was intended) and (2) escape any backslashes and double quotes
in milestone and each label value before interpolation (e.g., replace \ with \\
and " with \"), so that query += ` milestone:"${escapedMilestone}"` and query +=
` label:"${escapedLabel}"` use the safe, escaped strings.

---

Outside diff comments:
In `@i18n/locales/de.json`:
- Around line 282-288: Remove the unused i18n keys reported by CI: delete
"issues.serverSearch", "issues.filter.hasLinkedPr", and
"issues.filter.noLinkedPr" from the German locale (i18n/locales/de.json) and the
corresponding English locale entry, and also remove these keys from the i18n
schema/translation types so they are no longer expected by the build; ensure you
also remove any remaining references in components such as IssueToolbar.vue (the
PR already removed the filter chips) and run the i18n validation to confirm the
pipeline error is resolved.

In `@i18n/locales/en.json`:
- Around line 282-288: Remove the unused localization keys that are causing CI
failures: delete issues.serverSearch and the two filter keys
issues.filter.hasLinkedPr and issues.filter.noLinkedPr from en.json so it
matches the de.json changes; update the JSON object under "filter" to only
include the remaining keys (e.g., "assignedToMe", "unassigned", "hasMilestone")
and ensure the file remains valid JSON after removing those entries.

---

Nitpick comments:
In `@app/composables/usePaginatedSection.ts`:
- Around line 132-139: The refresh() implementation clears pageCache and resets
cursorHistory but does not preserve totalCount or data on a failed fetch; modify
refresh() to snapshot current totalCount and data (and cursorHistory as already
done) before clearing, then perform the clear/fetch, and if fetchData() returns
false restore cursorHistory, totalCount, and data (and optionally pageCache) to
their previous values so the UI stays consistent; reference functions/vars:
refresh(), fetchData(), pageCache, cursorHistory, totalCount, and data.

In `@test/nuxt/issueStore.test.ts`:
- Around line 167-175: Add a unit test that verifies the new assignedToMe filter
returns only issues assigned to the current user: in
test/nuxt/issueStore.test.ts add a mock Issue (e.g., id 'I_assigned_me' with
assignees containing 'currentUser'), update the test server handler used in
registerEndpoint to return that issue when url.searchParams.has('assignedToMe'),
and add a new test case (similar to the existing unassigned test) that uses
withStore, calls store.selectRepo('org/repo'), sets store.activeFilters =
['assignedToMe'], awaits store.fetchIssues(), and asserts store.issues has
length 1 and store.issues[0]?.id === 'I_assigned_me'; reference functions:
fetchIssues, selectRepo, withStore, and the mockIssue used for other tests to
place the new mock consistently.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 86b0745 and 8cdcc68.

📒 Files selected for processing (11)
  • app/components/issue/IssueRowSkeleton.vue
  • app/components/issue/IssueToolbar.vue
  • app/composables/usePaginatedSection.ts
  • app/pages/issues/index.vue
  • app/stores/focus.ts
  • app/stores/issues.ts
  • i18n/locales/de.json
  • i18n/locales/en.json
  • i18n/schema.json
  • server/api/issues/index.get.ts
  • test/nuxt/issueStore.test.ts
💤 Files with no reviewable changes (1)
  • app/stores/focus.ts

Comment on lines +54 to +58
if (milestone) query += ` milestone:"${milestone}"`
if (label) {
for (const l of String(label).split(',')) {
if (l.trim()) query += ` label:"${l.trim()}"`
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's examine the file to understand the search system context
cat -n server/api/issues/index.get.ts | head -80

Repository: flumen-dev/flumen.dev

Length of output: 2938


🌐 Web query:

GitHub search syntax: milestone qualifier, how to query "any milestone" or "has milestone"

💡 Result:

In GitHub issues/PR search:

  • Specific milestone: milestone:"My Milestone" (quote if it contains spaces) [1]
  • No milestone set: no:milestone [2]
  • “Has any milestone” / “any milestone”: negate the “no milestone” qualifier: -no:milestone (GitHub search supports excluding a term/qualifier by prefixing it with -) [3]

Examples:

  • All open issues with any milestone: is:issue is:open -no:milestone
  • All open issues with no milestone: is:issue is:open no:milestone

Note: There isn’t a documented wildcard like milestone:* for classic issues/PR search; -no:milestone is the usual way to express “has milestone.” [2][3]

Sources: [1] [2] [3]


Escape milestone/label qualifier values and fix milestone wildcard handling.

Unescaped quote/backslash characters can break search qualifiers (e.g., if milestone contains ", the query becomes malformed). Additionally, milestone:"*" does not represent "any milestone" in GitHub search syntax; the correct syntax is to omit the filter or use the -no:milestone pattern.

🔧 Suggested fix
+const escapeSearchValue = (value: string) => value.replace(/["\\]/g, '\\$&').trim()
+
-  if (milestone) query += ` milestone:"${milestone}"`
+  if (milestone && milestone !== '*') {
+    const m = escapeSearchValue(String(milestone))
+    if (m) query += ` milestone:"${m}"`
+  }
   if (label) {
     for (const l of String(label).split(',')) {
-      if (l.trim()) query += ` label:"${l.trim()}"`
+      const safe = escapeSearchValue(l)
+      if (safe) query += ` label:"${safe}"`
     }
   }
📝 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 (milestone) query += ` milestone:"${milestone}"`
if (label) {
for (const l of String(label).split(',')) {
if (l.trim()) query += ` label:"${l.trim()}"`
}
const escapeSearchValue = (value: string) => value.replace(/["\\]/g, '\\$&').trim()
if (milestone && milestone !== '*') {
const m = escapeSearchValue(String(milestone))
if (m) query += ` milestone:"${m}"`
}
if (label) {
for (const l of String(label).split(',')) {
const safe = escapeSearchValue(l)
if (safe) query += ` label:"${safe}"`
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/api/issues/index.get.ts` around lines 54 - 58, The milestone/label
values are inserted into the GitHub search query unescaped and the special
milestone value "*" is handled incorrectly; update the code that builds query
(using the milestone variable and the label loop that splits
String(label).split(',')) to (1) treat milestone === "*" by skipping adding a
milestone filter (or use the explicit -no:milestone handling if that was
intended) and (2) escape any backslashes and double quotes in milestone and each
label value before interpolation (e.g., replace \ with \\ and " with \"), so
that query += ` milestone:"${escapedMilestone}"` and query += `
label:"${escapedLabel}"` use the safe, escaped strings.

@Flo0806 Flo0806 merged commit 5a25bec into main Feb 27, 2026
8 of 10 checks passed
@Flo0806 Flo0806 deleted the feat/assigned-me-issues branch February 28, 2026 13:30
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.

Add Assigned to You section to Issues overview page

1 participant