-
Notifications
You must be signed in to change notification settings - Fork 0
feat: enhance Action Required page with comprehensive improvements #36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: enhance Action Required page with comprehensive improvements #36
Conversation
Major Features: 1. Detail Side Panel Enhancements: - Add quick actions (Start, Add to Kanban, Close) in panel header - Display comments from GitHub issues/PRs with avatars and timestamps - Add priority selector dropdown (Low, Medium, High, Urgent) - Show action feedback messages (success/error states) - Remove direct GitHub links from titles, add dedicated link icon 2. New Issue Creation: - Add "New Issue" button in page header - Create NewIssueDialog component with form validation - Support title, description, repository, and labels input - Implement Ctrl+N / Cmd+N keyboard shortcut to open modal - Show success state with link to created issue 3. "All" Tab Implementation: - Add new "All" tab showing combined items (assigned, mentions, stale) - Update tab navigation to support 4 tabs instead of 3 - Handle refresh logic for combined data sources - Dynamic itemType detection for QuickActionsMenu 4. GitHub API Client Extensions: - Add getIssueComments() method to fetch issue/PR comments - Add createIssue() method to create new GitHub issues - Support comment pagination and error handling 5. UI/UX Improvements: - Replace title anchor tags with clickable rows - Add ExternalLink icon button for GitHub navigation - Improve click event handling with stopPropagation - Add keyboard shortcut (Ctrl+N / Cmd+N) for new issue creation This update significantly improves the Action Required workflow by providing quick access to issue details, comments, and actions without leaving the page.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Rate limit exceeded@ArjinAlbay has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 16 minutes and 29 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the 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. 📒 Files selected for processing (1)
WalkthroughThis pull request adds an "All" tab aggregating assigned, mentioned, and stale action items; integrates a NewIssueDialog for creating GitHub issues; extends DetailPanel with comments, priority selection, and Kanban actions; and adds GitHub API client methods getIssueComments and createIssue, plus URL validation and adjusted refresh logic. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Page as action-required/page
participant Tabs as Tabs Component
participant ActionItems as ActionItemsList
User->>Page: Click "All" tab
Page->>Page: set currentTab = "all"
Page->>Page: Aggregate items: assigned + mentions + stale
Page->>Tabs: Render TabsContent value="all"
Tabs->>ActionItems: Render with type="all"
ActionItems->>ActionItems: Map over combined items (deduce itemType per item)
ActionItems->>User: Display All Action Items
sequenceDiagram
participant User
participant Dialog as NewIssueDialog
participant Store as useAuthStore
participant GitHubAPI as GitHubAPIClient
participant GitHub as GitHub API
User->>Dialog: Open (Ctrl/Cmd+N or button)
User->>Dialog: Fill repo, title, body, labels
User->>Dialog: Click Create Issue
Dialog->>Store: Get orgData.token
Dialog->>GitHubAPI: createIssue(owner, repo, title, body, labels)
GitHubAPI->>GitHub: POST /repos/{owner}/{repo}/issues
GitHub-->>GitHubAPI: { html_url, number }
GitHubAPI-->>Dialog: { success: true, issue }
Dialog->>User: Show success UI with link, auto-close after 2s
sequenceDiagram
participant User
participant Detail as DetailPanel
participant GitHubAPI as GitHubAPIClient
participant GitHub as GitHub API
participant Stores as Stores (Auth/Kanban)
User->>Detail: Open issue panel
Detail->>GitHubAPI: getIssueComments(owner, repo, number)
GitHubAPI->>GitHub: GET /repos/{owner}/{repo}/issues/{number}/comments
GitHub-->>GitHubAPI: comments array
GitHubAPI-->>Detail: comments metadata
Detail->>User: Render comments section
User->>Detail: Click Add to Kanban / Change Priority / Close
Detail->>Stores: Call store action
Detail->>User: Show success/error feedback
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
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. Comment |
- Replace 'any' type casts with proper type checking using 'in' operator - Convert DetailPanelIssue to ActionItem format in Kanban functions - Remove unused getPriorityColor function - Ensure strict TypeScript compliance without suppressing types
There was a problem hiding this 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
🧹 Nitpick comments (4)
src/components/ui/detail-panel.tsx (1)
53-55: Priority selector is currently cosmetic only
selectedPriorityis initialized and wired to the<Select>, but all Kanban actions still useissue.priority || "medium"instead of the user’s current selection. This means changing the priority in the panel has no effect on any downstream state.If the intent is to let users adjust priority before “Start” / “Add to Kanban”, consider using
selectedPrioritywhen building theactionItem:- priority: issue.priority || ("medium" as const), + priority: selectedPriority,(and similarly in
handleQuickStart).Also applies to: 121-177, 356-377
src/components/action-required/NewIssueDialog.tsx (1)
26-52: Consider resetting dialog state when closed via overlay/ESC
handleResetis only called fromhandleClose, which is wired to the Cancel button and the post‑success timeout. If the user closes the dialog via the backdrop or Escape key,onOpenChange(false)fires without callinghandleReset, so previous input, errors, or success state can persist into the next open.If you want a fresh form each time it opens, you can hook into
onOpenChange:- return ( - <Dialog open={open} onOpenChange={onOpenChange}> + const handleOpenChange = (nextOpen: boolean) => { + if (!nextOpen) { + handleReset(); + } + onOpenChange(nextOpen); + }; + + return ( + <Dialog open={open} onOpenChange={handleOpenChange}>This keeps the external
opencontrol intact while ensuring a clean slate on every close.Also applies to: 107-226
src/app/action-required/page.tsx (2)
208-226: Consider de‑duplicating items in the “All” tab
actionItemsByType.allis built as a simple concatenation ofassignedItems,mentionItems, andstaleItems. If the same underlying issue/PR appears in more than one list (e.g., assigned + mentioned), it will show multiple times in the All view.If the intent is to see each item once, you could dedupe by
id:- const actionItemsByType = useMemo( - () => ({ - all: [...assignedItems, ...mentionItems, ...staleItems], - assigned: assignedItems, - mentions: mentionItems, - stale: staleItems, - }), - [assignedItems, mentionItems, staleItems] - ); + const actionItemsByType = useMemo(() => { + const allRaw = [...assignedItems, ...mentionItems, ...staleItems]; + const seen = new Set<string | number>(); + const all = allRaw.filter((item) => { + if (seen.has(item.id)) return false; + seen.add(item.id); + return true; + }); + + return { + all, + assigned: assignedItems, + mentions: mentionItems, + stale: staleItems, + }; + }, [assignedItems, mentionItems, staleItems]);This will keep the All tab from over‑representing items that fall into multiple categories.
255-275: Loading/error aggregation anditemTypeinference for “All” look coherentThe way
ActionItemsListderivesisLoading/errorfortype === "all"(OR-ing across assigned/mentions/stale) and howQuickActionsMenuinfersitemTypefrommentionType/mentionedAtvsdaysStale/lastActivitymatches the store’s item shapes and keeps actions routed to the right bucket.This is a nice, minimal approach to making the All tab behave correctly without changing the store surface.
Also applies to: 630-641
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/app/action-required/page.tsx(13 hunks)src/components/action-required/NewIssueDialog.tsx(1 hunks)src/components/ui/detail-panel.tsx(6 hunks)src/lib/api/github-api-client.ts(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
src/components/ui/detail-panel.tsx (3)
src/stores/index.ts (3)
useKanbanStore(16-16)useActionItemsStore(15-15)useAuthStore(10-10)src/lib/api/github-api-client.ts (1)
githubAPIClient(1666-1666)src/components/ui/select.tsx (5)
Select(175-175)SelectTrigger(183-183)SelectValue(184-184)SelectContent(176-176)SelectItem(178-178)
src/components/action-required/NewIssueDialog.tsx (6)
src/lib/api/github-api-client.ts (1)
githubAPIClient(1666-1666)src/components/ui/dialog.tsx (6)
Dialog(133-133)DialogContent(135-135)DialogHeader(138-138)DialogTitle(141-141)DialogDescription(136-136)DialogFooter(137-137)src/components/ui/button.tsx (1)
Button(59-59)src/components/ui/label.tsx (1)
Label(24-24)src/components/ui/input.tsx (1)
Input(21-21)src/components/ui/textarea.tsx (1)
Textarea(19-19)
src/app/action-required/page.tsx (2)
src/stores/actionItems.ts (1)
useActionItemsStore(115-574)src/components/action-required/NewIssueDialog.tsx (1)
NewIssueDialog(26-227)
🔇 Additional comments (2)
src/lib/api/github-api-client.ts (1)
1571-1663: New issue/comments API methods look consistent and robustThe new
getIssueCommentsandcreateIssuemethods align with the existing client patterns (token checks, error handling, and minimal return shapes) and should integrate cleanly with the new UI flows. I don’t see any correctness or security issues in this implementation as written.src/app/action-required/page.tsx (1)
125-151: New Issue shortcut and wiring are straightforwardThe
isNewIssueOpenstate, Ctrl/Cmd+N global handler, header “New Issue” button, and<NewIssueDialog>wiring are all consistent and self‑contained. The stopPropagation rules on row clicks vs the external link icon also look correct for opening the detail panel vs GitHub.No changes needed here.
Also applies to: 665-705, 849-851
| const handleSubmit = async () => { | ||
| if (!title.trim() || !repo.trim()) { | ||
| setError("Title and repository are required"); | ||
| return; | ||
| } | ||
|
|
||
| if (!orgData?.token) { | ||
| setError("GitHub token not found. Please reconnect your GitHub account."); | ||
| return; | ||
| } | ||
|
|
||
| const repoParts = repo.trim().split("/"); | ||
| if (repoParts.length !== 2) { | ||
| setError("Repository must be in format: owner/repo"); | ||
| return; | ||
| } | ||
|
|
||
| const [owner, repoName] = repoParts; | ||
|
|
||
| setIsCreating(true); | ||
| setError(null); | ||
|
|
||
| githubAPIClient.setUserToken(orgData.token); | ||
|
|
||
| try { | ||
| const labelArray = labels | ||
| .split(",") | ||
| .map((l) => l.trim()) | ||
| .filter((l) => l.length > 0); | ||
|
|
||
| const result = await githubAPIClient.createIssue( | ||
| owner, | ||
| repoName, | ||
| title.trim(), | ||
| body.trim() || undefined, | ||
| labelArray.length > 0 ? labelArray : undefined | ||
| ); | ||
|
|
||
| if (result.success && result.issue) { | ||
| setSuccess(true); | ||
| setCreatedIssueUrl(result.issue.html_url); | ||
| setTimeout(() => { | ||
| handleClose(); | ||
| }, 2000); | ||
| } else { | ||
| setError(result.error || "Failed to create issue"); | ||
| } | ||
| } catch (err) { | ||
| setError(err instanceof Error ? err.message : "Failed to create issue"); | ||
| } finally { | ||
| setIsCreating(false); | ||
| } | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move setUserToken inside try to avoid breaking the dialog state
githubAPIClient.setUserToken(orgData.token) is called before the try block. If it throws (invalid/unexpected token), the function will exit before finally runs, leaving isCreating stuck at true and the dialog effectively broken for the user.
Wrap the token assignment in the try so all failures go through the same error path and isCreating is always reset:
- setIsCreating(true);
- setError(null);
-
- githubAPIClient.setUserToken(orgData.token);
-
- try {
+ setIsCreating(true);
+ setError(null);
+
+ try {
+ githubAPIClient.setUserToken(orgData.token);
const labelArray = labels
.split(",")
.map((l) => l.trim())
.filter((l) => l.length > 0);
@@
- if (result.success && result.issue) {
+ if (result.success && result.issue) {
...This keeps the loading flag and error handling consistent even if token validation fails.
🤖 Prompt for AI Agents
In src/components/action-required/NewIssueDialog.tsx around lines 53 to 105,
move the call githubAPIClient.setUserToken(orgData.token) into the try block
immediately before creating the issue so any exception from setting the token is
caught by the same try/catch/finally flow; keep setting setIsCreating(true) and
setError(null) before the try, then inside try set the user token, proceed to
create the issue, and let the existing catch/finally handle errors and ensure
setIsCreating(false) always runs.
| useEffect(() => { | ||
| const fetchComments = async () => { | ||
| if (!issue?.url || !isOpen || !orgData?.token) return; | ||
|
|
||
| const urlMatch = issue.url.match(/github\.com\/([^/]+)\/([^/]+)\/(issues|pull)\/(\d+)/); | ||
| if (!urlMatch) return; | ||
|
|
||
| const [, owner, repo, , issueNumber] = urlMatch; | ||
|
|
||
| setLoadingComments(true); | ||
| githubAPIClient.setUserToken(orgData.token); | ||
|
|
||
| try { | ||
| const fetchedComments = await githubAPIClient.getIssueComments( | ||
| owner, | ||
| repo, | ||
| parseInt(issueNumber, 10) | ||
| ); | ||
| setComments(fetchedComments); | ||
| } catch (error) { | ||
| console.error("Failed to fetch comments:", error); | ||
| } finally { | ||
| setLoadingComments(false); | ||
| } | ||
| }; | ||
|
|
||
| fetchComments(); | ||
| }, [issue, isOpen, orgData?.token]); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Guard against setUserToken throwing and ensure loading state is always cleared
In the comments effect, githubAPIClient.setUserToken(orgData.token) is called outside the try/catch. If setUserToken throws (e.g., unexpected token format), the error will escape React’s effect, and loadingComments will stay true because the finally block is never reached.
Move the token assignment inside the try block (or wrap it in its own try/catch) so errors are handled and loading state is reset:
- setLoadingComments(true);
- githubAPIClient.setUserToken(orgData.token);
-
- try {
+ setLoadingComments(true);
+
+ try {
+ githubAPIClient.setUserToken(orgData.token);
const fetchedComments = await githubAPIClient.getIssueComments(
owner,
repo,
parseInt(issueNumber, 10)
);
setComments(fetchedComments);This keeps the UI resilient even if token validation fails.
📝 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.
| useEffect(() => { | |
| const fetchComments = async () => { | |
| if (!issue?.url || !isOpen || !orgData?.token) return; | |
| const urlMatch = issue.url.match(/github\.com\/([^/]+)\/([^/]+)\/(issues|pull)\/(\d+)/); | |
| if (!urlMatch) return; | |
| const [, owner, repo, , issueNumber] = urlMatch; | |
| setLoadingComments(true); | |
| githubAPIClient.setUserToken(orgData.token); | |
| try { | |
| const fetchedComments = await githubAPIClient.getIssueComments( | |
| owner, | |
| repo, | |
| parseInt(issueNumber, 10) | |
| ); | |
| setComments(fetchedComments); | |
| } catch (error) { | |
| console.error("Failed to fetch comments:", error); | |
| } finally { | |
| setLoadingComments(false); | |
| } | |
| }; | |
| fetchComments(); | |
| }, [issue, isOpen, orgData?.token]); | |
| useEffect(() => { | |
| const fetchComments = async () => { | |
| if (!issue?.url || !isOpen || !orgData?.token) return; | |
| const urlMatch = issue.url.match(/github\.com\/([^/]+)\/([^/]+)\/(issues|pull)\/(\d+)/); | |
| if (!urlMatch) return; | |
| const [, owner, repo, , issueNumber] = urlMatch; | |
| setLoadingComments(true); | |
| try { | |
| githubAPIClient.setUserToken(orgData.token); | |
| const fetchedComments = await githubAPIClient.getIssueComments( | |
| owner, | |
| repo, | |
| parseInt(issueNumber, 10) | |
| ); | |
| setComments(fetchedComments); | |
| } catch (error) { | |
| console.error("Failed to fetch comments:", error); | |
| } finally { | |
| setLoadingComments(false); | |
| } | |
| }; | |
| fetchComments(); | |
| }, [issue, isOpen, orgData?.token]); |
🤖 Prompt for AI Agents
In src/components/ui/detail-panel.tsx around lines 69 to 97, the call
githubAPIClient.setUserToken(orgData.token) runs outside the try/catch so if it
throws the effect will exit early and setLoadingComments(true) will never be
cleared; move the setUserToken call inside the try block (or wrap it in its own
try/catch) before calling getIssueComments, keep the setLoadingComments(true) at
the start, and ensure the existing finally block always runs to call
setLoadingComments(false) so loading state is cleared even when setting the
token fails.
| const handleClose = async () => { | ||
| if (!issue?.url) return; | ||
|
|
||
| const urlMatch = issue.url.match(/github\.com\/([^/]+)\/([^/]+)\/(issues|pull)\/(\d+)/); | ||
| if (!urlMatch) return; | ||
|
|
||
| setIsClosing(true); | ||
|
|
||
| try { | ||
| const itemType = issue.type === "pullRequest" ? "stale" : "assigned"; | ||
| await markAsRead(itemType as any, issue.id.toString()); | ||
| setActionMessage({ type: "success", text: `${issue.type === "pullRequest" ? "PR" : "Issue"} closed` }); | ||
| setTimeout(() => { | ||
| setActionMessage(null); | ||
| onClose(); | ||
| }, 2000); | ||
| } catch (error) { | ||
| console.error("Failed to close:", error); | ||
| setActionMessage({ type: "error", text: "Failed to close item" }); | ||
| setTimeout(() => setActionMessage(null), 3000); | ||
| } finally { | ||
| setIsClosing(false); | ||
| } | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
handleClose infers wrong itemType for many items
handleClose maps issue.type === "pullRequest" to "stale" and everything else to "assigned" when calling markAsRead. This loses the original source bucket (assigned vs mentions vs stale):
- A PR from the Assigned tab will be treated as
"stale". - An item opened from Mentions will always be treated as
"assigned"or"stale".
That can cause markAsRead to look in the wrong list and fail to update/remove the item even though you show a “closed” success message.
To align with QuickActionsMenu, derive itemType from the underlying item shape instead of issue.type:
- const itemType = issue.type === "pullRequest" ? "stale" : "assigned";
- await markAsRead(itemType as any, issue.id.toString());
+ const itemType =
+ "mentionType" in (issue as any) || "mentionedAt" in (issue as any)
+ ? "mentions"
+ : "daysStale" in (issue as any) || "lastActivity" in (issue as any)
+ ? "stale"
+ : "assigned";
+
+ await markAsRead(itemType as any, issue.id.toString());(or, better, store the source type alongside issue in useDetailPanelStore so you don’t need runtime heuristics).
🤖 Prompt for AI Agents
In src/components/ui/detail-panel.tsx around lines 192 to 215, handleClose
currently picks itemType based on issue.type (mapping pullRequest -> "stale"
else "assigned"), which loses the original source bucket and causes markAsRead
to target the wrong list; fix by deriving the itemType from the underlying item
shape/source instead of issue.type — e.g. read a source/sortedBucket field that
was stored with the issue (or update useDetailPanelStore to include the original
source when opening the panel) and pass that value into markAsRead; keep the
existing success/error UI unchanged but ensure markAsRead receives the true
source bucket (assigned/mentions/stale) so the item is removed/updated from the
correct list.
- Updated ActionItemsList error state retry button to handle 'all' tab type - When type is 'all', refresh all three data sources (assigned, mentions, stale) - Fixes TypeScript error: Type 'all' is not assignable to refreshData parameter
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (2)
src/components/ui/detail-panel.tsx (2)
45-52: Harden comment fetching: protectsetUserToken, avoid stale state, and surface errorsThe comment-loading flow has a few robustness issues:
githubAPIClient.setUserToken(orgData.token)sits outside thetryblock. If it throws, the effect will bail andloadingCommentswill never be reset (spinner stuck). This is the same concern raised in a previous review.- On paths where the effect early-returns (e.g., URL/token missing),
commentsandloadingCommentsare left as-is, which can leak comments from a previously opened issue into the next one.- When
getIssueCommentsfails, the user sees “No comments yet” instead of an error, which is misleading.You can address all three with something like:
@@ - const [comments, setComments] = useState<Array<{ + const [comments, setComments] = useState<Array<{ id: number; user: { login: string; avatar_url: string }; body: string; created_at: string; }>>([]); const [loadingComments, setLoadingComments] = useState(false); + const [commentsError, setCommentsError] = useState<string | null>(null); @@ useEffect(() => { const fetchComments = async () => { - if (!issue?.url || !isOpen || !orgData?.token) return; + // Clear previous state when switching issues / opening panel + setComments([]); + setCommentsError(null); + + if (!issue?.url || !isOpen || !orgData?.token) { + setLoadingComments(false); + return; + } @@ - setLoadingComments(true); - githubAPIClient.setUserToken(orgData.token); - - try { - const fetchedComments = await githubAPIClient.getIssueComments( + setLoadingComments(true); + + try { + githubAPIClient.setUserToken(orgData.token); + const fetchedComments = await githubAPIClient.getIssueComments( owner, repo, parseInt(issueNumber, 10) ); setComments(fetchedComments); } catch (error) { console.error("Failed to fetch comments:", error); + setCommentsError("Failed to load comments"); } finally { setLoadingComments(false); } @@ - <div className="border-t border-gray-200 dark:border-gray-800 pt-6"> + <div className="border-t border-gray-200 dark:border-gray-800 pt-6"> @@ - {loadingComments ? ( + {loadingComments ? ( <div className="flex items-center justify-center py-8"> <Loader2 className="w-6 h-6 animate-spin text-gray-400" /> </div> - ) : comments.length === 0 ? ( + ) : commentsError ? ( + <p className="text-sm text-red-600 dark:text-red-400 text-center py-6"> + {commentsError} + </p> + ) : comments.length === 0 ? ( <p className="text-sm text-gray-500 dark:text-gray-400 text-center py-6"> No comments yet </p>At minimum, please move
setUserTokeninto thetrysoloadingCommentsis always cleared even if token handling fails.Also applies to: 69-96, 503-544
192-215: DeriveitemTypefrom the original source bucket instead ofissue.type
handleClosestill infersitemTypefromissue.type:const itemType: "assigned" | "mentions" | "stale" = issue.type === "pullRequest" ? "stale" : "assigned"; await markAsRead(itemType, issue.id.toString());This misclassifies many items and can cause
markAsReadto operate on the wrong list, especially now that there’s an All tab:
- A pull request opened from the Assigned list is treated as
"stale".- Items from Mentions are never treated as
"mentions".A previous review already flagged this; it’s still unresolved and will lead to inconsistent close behavior compared with
QuickActionsMenu.Ideally, store the source bucket alongside the issue in
useDetailPanelStoreand pass that through:- const handleClose = async () => { + const handleClose = async () => { if (!issue?.url) return; @@ - try { - const itemType: "assigned" | "mentions" | "stale" = issue.type === "pullRequest" ? "stale" : "assigned"; - await markAsRead(itemType, issue.id.toString()); + try { + // Prefer: sourceType is set when opening the panel (e.g. "assigned" | "mentions" | "stale") + const itemType = (issue as any).sourceType as "assigned" | "mentions" | "stale"; + await markAsRead(itemType, issue.id.toString());If you don’t yet track
sourceType, at least reuse the same heuristics you’re already using inQuickActionsMenu(e.g., based onmentionType,mentionedAt,daysStale,lastActivity) so list and panel stay in sync.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/components/ui/detail-panel.tsx(6 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/components/ui/detail-panel.tsx (7)
src/stores/index.ts (3)
useKanbanStore(16-16)useActionItemsStore(15-15)useAuthStore(10-10)src/lib/api/github-api-client.ts (1)
githubAPIClient(1666-1666)src/components/ui/button.tsx (1)
Button(59-59)src/lib/utils.ts (1)
cn(4-6)src/components/ui/select.tsx (5)
Select(175-175)SelectTrigger(183-183)SelectValue(184-184)SelectContent(176-176)SelectItem(178-178)src/components/ui/badge.tsx (1)
Badge(46-46)src/components/ui/avatar.tsx (3)
Avatar(53-53)AvatarImage(53-53)AvatarFallback(53-53)
🔇 Additional comments (2)
src/components/ui/detail-panel.tsx (2)
179-190: Kanban removal flow looks consistent with add/start behaviorUsing
isActionItemAdded(issue.id.toString())to gate between add vs remove and thenremoveActionItemFromKanban(issue.id.toString())here keeps the ID handling consistent across the Kanban actions, and the success/error messaging matches the other handlers. No changes needed.
253-331: Header layout and priority UI are clear; consider minor polish only if desiredThe updated header—with close button, Kanban actions, destructive “Close” action, and the inline Priority
<Select>usingBadgevariants—reads cleanly and matches the rest of the UI component patterns. The use ofisClosingto disable the Close button and show a spinner is also solid.No required changes here; once the priority is wired into the Kanban actions (see separate comment), this section is in good shape.
Also applies to: 356-377
| const [selectedPriority, setSelectedPriority] = useState<"low" | "medium" | "high" | "urgent">("medium"); | ||
| const [isClosing, setIsClosing] = useState(false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ensure Kanban actions respect the user-selected priority (and consider de‑duplicating action item construction)
selectedPriority drives the Priority <Select>, but the Kanban actions still use issue.priority || "medium" when constructing the action item. This means:
- If a user changes the priority in the panel, then clicks Start or Add to Kanban, the Kanban entry will have the old priority, ignoring their choice.
You can wire the selected value through by replacing issue.priority usages with selectedPriority:
@@ const handleAddToKanban = () => {
- const actionItem = {
+ const actionItem = {
id: issue.id.toString(),
title: issue.title,
repo: issue.repository || issue.repo || "Unknown",
type: issue.type === "pullRequest" ? ("pullRequest" as const) : ("issue" as const),
- priority: issue.priority || ("medium" as const),
+ priority: selectedPriority,
@@ const handleQuickStart = () => {
- const actionItem = {
+ const actionItem = {
id: issue.id.toString(),
title: issue.title,
repo: issue.repository || issue.repo || "Unknown",
type: issue.type === "pullRequest" ? ("pullRequest" as const) : ("issue" as const),
- priority: issue.priority || ("medium" as const),
+ priority: selectedPriority,Given the duplication between handleAddToKanban and handleQuickStart, you might also extract a small helper like buildActionItem(issue, selectedPriority) to keep the object construction in one place.
Also applies to: 121-177, 356-377
🤖 Prompt for AI Agents
In src/components/ui/detail-panel.tsx around lines 53-54 (and also apply to
ranges 121-177 and 356-377), the Kanban action creators are using issue.priority
|| "medium" instead of the user-controlled selectedPriority, so clicks like
Start/Add to Kanban ignore the current Select. Replace uses of issue.priority
with the component state selectedPriority when building the action payloads, and
extract the duplicated object construction into a small helper (e.g.,
buildActionItem(issue, selectedPriority)) that both handleAddToKanban and
handleQuickStart call to avoid duplication and keep priority wiring consistent.
Major Features:
Detail Side Panel Enhancements:
New Issue Creation:
"All" Tab Implementation:
GitHub API Client Extensions:
UI/UX Improvements:
This update significantly improves the Action Required workflow by providing quick access to issue details, comments, and actions without leaving the page.
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.