Repository navigation
feat(desktop): separate tasks and pull requests in sidebar #5975
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
Changes from all commits
1307efa
dacae65
c352f78
62e9192
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| import { ToggleGroup, ToggleGroupItem } from "@superset/ui/toggle-group"; | ||
|
|
||
| interface OpenClosedFilterProps { | ||
| includeClosed: boolean; | ||
| onChange: (includeClosed: boolean) => void; | ||
| } | ||
|
|
||
| export function OpenClosedFilter({ | ||
| includeClosed, | ||
| onChange, | ||
| }: OpenClosedFilterProps) { | ||
| return ( | ||
| <div className="flex items-center gap-2"> | ||
| <span className="text-xs text-muted-foreground">State</span> | ||
| <ToggleGroup | ||
| type="single" | ||
| value={includeClosed ? "all" : "open"} | ||
| onValueChange={(value) => { | ||
| if (value) onChange(value === "all"); | ||
| }} | ||
| variant="outline" | ||
| size="sm" | ||
| aria-label="Filter by state" | ||
| className="h-8 rounded-md border-0 bg-muted/50 p-0.5" | ||
| > | ||
| <ToggleGroupItem | ||
| value="open" | ||
| aria-label="Show open items" | ||
| className="h-7 border-0 px-2 text-xs data-[state=on]:bg-background data-[state=on]:text-foreground" | ||
| > | ||
| Open | ||
| </ToggleGroupItem> | ||
| <ToggleGroupItem | ||
| value="all" | ||
| aria-label="Show items in all states" | ||
| className="h-7 border-0 px-2 text-xs data-[state=on]:bg-background data-[state=on]:text-foreground" | ||
| > | ||
| All | ||
| </ToggleGroupItem> | ||
| </ToggleGroup> | ||
| </div> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| export { OpenClosedFilter } from "./OpenClosedFilter"; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| import { Button } from "@superset/ui/button"; | ||
| import type { ReactNode } from "react"; | ||
| import { HiArrowLeft } from "react-icons/hi2"; | ||
| import { LuExternalLink, LuPlus } from "react-icons/lu"; | ||
|
|
||
| interface WorkItemDetailHeaderProps { | ||
| itemNumber: number | null; | ||
| icon: ReactNode; | ||
| backLabel: string; | ||
| externalLabel: string; | ||
| url: string | null; | ||
| onBack: () => void; | ||
| onAddToWorkspace: (() => void) | null; | ||
| } | ||
|
|
||
| export function WorkItemDetailHeader({ | ||
| itemNumber, | ||
| icon, | ||
| backLabel, | ||
| externalLabel, | ||
| url, | ||
| onBack, | ||
| onAddToWorkspace, | ||
| }: WorkItemDetailHeaderProps) { | ||
| return ( | ||
| <div className="@container flex shrink-0 items-center gap-2 border-b border-border px-4 py-3 @md:gap-3 @md:px-6 @md:py-4"> | ||
| <Button | ||
| variant="ghost" | ||
| size="icon" | ||
| className="size-8 shrink-0" | ||
| onClick={onBack} | ||
| aria-label={backLabel} | ||
| > | ||
| <HiArrowLeft className="size-4" /> | ||
| </Button> | ||
| {icon} | ||
| <span className="min-w-0 truncate font-mono text-sm tabular-nums text-muted-foreground"> | ||
| {itemNumber === null ? "#—" : `#${itemNumber}`} | ||
| </span> | ||
| {/* Window-drag leaf standing in for the hidden TopBar. */} | ||
| <div className="drag -my-3 min-w-0 flex-1 self-stretch @md:-my-4" /> | ||
| <div className="ml-auto flex shrink-0 items-center gap-1"> | ||
| {url && ( | ||
| <Button variant="ghost" size="icon" className="size-8" asChild> | ||
| <a | ||
| href={url} | ||
| target="_blank" | ||
| rel="noopener noreferrer" | ||
| aria-label={externalLabel} | ||
| title={externalLabel} | ||
| > | ||
| <LuExternalLink className="size-4" /> | ||
| </a> | ||
| </Button> | ||
| )} | ||
| {onAddToWorkspace && ( | ||
| <Button | ||
| variant="outline" | ||
| size="sm" | ||
| className="h-8 gap-1.5 px-2 @md:px-3" | ||
| onClick={onAddToWorkspace} | ||
| aria-label="Add to workspace" | ||
| title="Add to workspace" | ||
| > | ||
| <LuPlus className="size-4" /> | ||
| <span className="hidden @md:inline">Add to workspace</span> | ||
| </Button> | ||
| )} | ||
| </div> | ||
| </div> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| export { WorkItemDetailHeader } from "./WorkItemDetailHeader"; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| import { Button } from "@superset/ui/button"; | ||
| import { cn } from "@superset/ui/utils"; | ||
| import { LuRefreshCw } from "react-icons/lu"; | ||
|
|
||
| interface WorkItemDetailStateProps { | ||
| message: string; | ||
| isLoading?: boolean; | ||
| isError?: boolean; | ||
| onRetry?: () => void; | ||
| } | ||
|
|
||
| export function WorkItemDetailState({ | ||
| message, | ||
| isLoading = false, | ||
| isError = false, | ||
| onRetry, | ||
| }: WorkItemDetailStateProps) { | ||
| return ( | ||
| <div | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P3: When Prompt for AI agents |
||
| className={cn( | ||
| "flex flex-1 flex-col items-center justify-center gap-3 px-6 py-10 text-center text-sm", | ||
| isError ? "text-destructive" : "text-muted-foreground", | ||
| (isError || onRetry) && "cursor-text select-text", | ||
| )} | ||
| role={isError ? "alert" : "status"} | ||
| aria-live="polite" | ||
| > | ||
| {isLoading && ( | ||
| <LuRefreshCw className="size-4 animate-spin motion-reduce:animate-none" /> | ||
| )} | ||
| <span className="max-w-prose text-wrap-pretty">{message}</span> | ||
| {onRetry && ( | ||
| <Button variant="outline" size="sm" onClick={onRetry}> | ||
| Try again | ||
| </Button> | ||
| )} | ||
| </div> | ||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| export { WorkItemDetailState } from "./WorkItemDetailState"; |
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.
P2: Both
DashboardSidebarHeaderandWorkspaceSidebarHeadernow contain near-identicalhandlePullRequestsClickfunctions, identicalusePullRequestsFilterStoresubscriptions, and identicalincludeClosedIssuesadditions tohandleTasksClick. This duplicated business logic means any future change to PR navigation behavior (adding a filter param, changing the gate feature, or altering the navigate call) must be applied in two separate files. The pattern matches prior team feedback about extracting shared navigation logic into a hook or helper to keep behavior consistent and reduce cognitive load when changes are made.Prompt for AI agents