Skip to content

Commit 1b758a4

Browse files
committed
fix(ui): corrects tab badge counts, toolbar layout, and ignore flash
1 parent cc8874d commit 1b758a4

File tree

6 files changed

+240
-107
lines changed

6 files changed

+240
-107
lines changed

src/app/components/dashboard/ActionsTab.tsx

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -211,37 +211,41 @@ export default function ActionsTab(props: ActionsTabProps) {
211211
const highlightedReposActions = createReorderHighlight(
212212
() => repoGroups().map(g => g.repoFullName),
213213
() => viewState.lockedRepos.actions,
214+
() => viewState.ignoredItems.length,
214215
);
215216

216217
return (
217218
<div class="divide-y divide-base-300">
218219
{/* Toolbar */}
219-
<div class="flex flex-wrap items-center gap-3 px-4 py-2 border-b border-base-300 bg-base-100">
220-
<label class="flex items-center gap-1.5 text-sm text-base-content/70 cursor-pointer select-none">
221-
<input
222-
type="checkbox"
223-
checked={viewState.showPrRuns}
224-
onChange={(e) => setViewState("showPrRuns", e.currentTarget.checked)}
225-
class="checkbox checkbox-sm checkbox-primary"
220+
<div class="flex items-start gap-3 px-4 py-2 border-b border-base-300 bg-base-100">
221+
<div class="flex flex-wrap items-center gap-3 min-w-0 flex-1">
222+
<label class="flex items-center gap-1.5 text-sm text-base-content/70 cursor-pointer select-none">
223+
<input
224+
type="checkbox"
225+
checked={viewState.showPrRuns}
226+
onChange={(e) => setViewState("showPrRuns", e.currentTarget.checked)}
227+
class="checkbox checkbox-sm checkbox-primary"
228+
/>
229+
Show PR runs
230+
</label>
231+
<FilterChips
232+
groups={actionsFilterGroups}
233+
values={viewState.tabFilters.actions}
234+
onChange={(field, value) => setTabFilter("actions", field as ActionsFilterField, value)}
235+
onReset={(field) => resetTabFilter("actions", field as ActionsFilterField)}
236+
onResetAll={() => resetAllTabFilters("actions")}
226237
/>
227-
Show PR runs
228-
</label>
229-
<FilterChips
230-
groups={actionsFilterGroups}
231-
values={viewState.tabFilters.actions}
232-
onChange={(field, value) => setTabFilter("actions", field as ActionsFilterField, value)}
233-
onReset={(field) => resetTabFilter("actions", field as ActionsFilterField)}
234-
onResetAll={() => resetAllTabFilters("actions")}
235-
/>
236-
<div class="flex-1" />
237-
<ExpandCollapseButtons
238-
onExpandAll={() => setAllExpanded("actions", repoGroups().map((g) => g.repoFullName), true)}
239-
onCollapseAll={() => setAllExpanded("actions", repoGroups().map((g) => g.repoFullName), false)}
240-
/>
241-
<IgnoreBadge
242-
items={viewState.ignoredItems.filter((i) => i.type === "workflowRun")}
243-
onUnignore={unignoreItem}
244-
/>
238+
</div>
239+
<div class="shrink-0 flex items-center gap-2 py-0.5">
240+
<ExpandCollapseButtons
241+
onExpandAll={() => setAllExpanded("actions", repoGroups().map((g) => g.repoFullName), true)}
242+
onCollapseAll={() => setAllExpanded("actions", repoGroups().map((g) => g.repoFullName), false)}
243+
/>
244+
<IgnoreBadge
245+
items={viewState.ignoredItems.filter((i) => i.type === "workflowRun")}
246+
onUnignore={unignoreItem}
247+
/>
248+
</div>
245249
</div>
246250

247251
{/* Loading skeleton — only when no data exists yet */}

src/app/components/dashboard/DashboardPage.tsx

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -323,11 +323,30 @@ export default function DashboardPage() {
323323

324324
const refreshTick = createMemo(() => (dashboardData.lastRefreshedAt?.getTime() ?? 0) + clockTick());
325325

326-
const tabCounts = createMemo(() => ({
327-
issues: dashboardData.issues.length,
328-
pullRequests: dashboardData.pullRequests.length,
329-
actions: dashboardData.workflowRuns.length,
330-
}));
326+
const tabCounts = createMemo(() => {
327+
const ignoredByType = (type: string) =>
328+
new Set(viewState.ignoredItems.filter((i) => i.type === type).map((i) => i.id));
329+
330+
const ignoredIssues = ignoredByType("issue");
331+
const ignoredPRs = ignoredByType("pullRequest");
332+
const ignoredRuns = ignoredByType("workflowRun");
333+
334+
return {
335+
issues: dashboardData.issues.filter((i) => {
336+
if (ignoredIssues.has(String(i.id))) return false;
337+
if (viewState.hideDepDashboard && i.title === "Dependency Dashboard") return false;
338+
return true;
339+
}).length,
340+
pullRequests: dashboardData.pullRequests.filter(
341+
(p) => !ignoredPRs.has(String(p.id))
342+
).length,
343+
actions: dashboardData.workflowRuns.filter((w) => {
344+
if (ignoredRuns.has(String(w.id))) return false;
345+
if (!viewState.showPrRuns && w.isPrRun) return false;
346+
return true;
347+
}).length,
348+
};
349+
});
331350

332351
const userLogin = createMemo(() => user()?.login ?? "");
333352
const allUsers = createMemo(() => {

src/app/components/dashboard/IssuesTab.tsx

Lines changed: 47 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ export default function IssuesTab(props: IssuesTabProps) {
203203
const highlightedReposIssues = createReorderHighlight(
204204
() => repoGroups().map(g => g.repoFullName),
205205
() => viewState.lockedRepos.issues,
206+
() => viewState.ignoredItems.length,
206207
);
207208

208209
function handleSort(field: string, direction: "asc" | "desc") {
@@ -223,49 +224,52 @@ export default function IssuesTab(props: IssuesTabProps) {
223224
return (
224225
<div class="flex flex-col h-full">
225226
{/* Sort dropdown + filter chips + ignore badge toolbar */}
226-
<div class="flex flex-wrap items-center gap-3 px-4 py-2 border-b border-base-300 bg-base-100">
227-
<SortDropdown
228-
options={sortOptions}
229-
value={sortPref().field}
230-
direction={sortPref().direction}
231-
onChange={handleSort}
232-
/>
233-
<FilterChips
234-
groups={filterGroups()}
235-
values={viewState.tabFilters.issues}
236-
onChange={(field, value) => {
237-
setTabFilter("issues", field as IssueFilterField, value);
238-
setPage(0);
239-
}}
240-
onReset={(field) => {
241-
resetTabFilter("issues", field as IssueFilterField);
242-
setPage(0);
243-
}}
244-
onResetAll={() => {
245-
resetAllTabFilters("issues");
246-
setPage(0);
247-
}}
248-
/>
249-
<button
250-
onClick={() => {
251-
updateViewState({ hideDepDashboard: !viewState.hideDepDashboard });
252-
setPage(0);
253-
}}
254-
class={`btn btn-xs rounded-full ${!viewState.hideDepDashboard ? "btn-primary" : "btn-ghost text-base-content/50"}`}
255-
aria-pressed={!viewState.hideDepDashboard}
256-
title="Toggle visibility of Dependency Dashboard issues"
257-
>
258-
Show Dep Dashboard
259-
</button>
260-
<div class="flex-1" />
261-
<ExpandCollapseButtons
262-
onExpandAll={() => setAllExpanded("issues", repoGroups().map((g) => g.repoFullName), true)}
263-
onCollapseAll={() => setAllExpanded("issues", repoGroups().map((g) => g.repoFullName), false)}
264-
/>
265-
<IgnoreBadge
266-
items={viewState.ignoredItems.filter((i) => i.type === "issue")}
267-
onUnignore={unignoreItem}
268-
/>
227+
<div class="flex items-start gap-3 px-4 py-2 border-b border-base-300 bg-base-100">
228+
<div class="flex flex-wrap items-center gap-3 min-w-0 flex-1">
229+
<SortDropdown
230+
options={sortOptions}
231+
value={sortPref().field}
232+
direction={sortPref().direction}
233+
onChange={handleSort}
234+
/>
235+
<FilterChips
236+
groups={filterGroups()}
237+
values={viewState.tabFilters.issues}
238+
onChange={(field, value) => {
239+
setTabFilter("issues", field as IssueFilterField, value);
240+
setPage(0);
241+
}}
242+
onReset={(field) => {
243+
resetTabFilter("issues", field as IssueFilterField);
244+
setPage(0);
245+
}}
246+
onResetAll={() => {
247+
resetAllTabFilters("issues");
248+
setPage(0);
249+
}}
250+
/>
251+
<button
252+
onClick={() => {
253+
updateViewState({ hideDepDashboard: !viewState.hideDepDashboard });
254+
setPage(0);
255+
}}
256+
class={`btn btn-xs rounded-full ${!viewState.hideDepDashboard ? "btn-primary" : "btn-ghost text-base-content/50"}`}
257+
aria-pressed={!viewState.hideDepDashboard}
258+
title="Toggle visibility of Dependency Dashboard issues"
259+
>
260+
Show Dep Dashboard
261+
</button>
262+
</div>
263+
<div class="shrink-0 flex items-center gap-2 py-0.5">
264+
<ExpandCollapseButtons
265+
onExpandAll={() => setAllExpanded("issues", repoGroups().map((g) => g.repoFullName), true)}
266+
onCollapseAll={() => setAllExpanded("issues", repoGroups().map((g) => g.repoFullName), false)}
267+
/>
268+
<IgnoreBadge
269+
items={viewState.ignoredItems.filter((i) => i.type === "issue")}
270+
onUnignore={unignoreItem}
271+
/>
272+
</div>
269273
</div>
270274

271275
{/* Loading skeleton — only when no data exists yet */}

src/app/components/dashboard/PullRequestsTab.tsx

Lines changed: 36 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ export default function PullRequestsTab(props: PullRequestsTabProps) {
301301
const highlightedReposPRs = createReorderHighlight(
302302
() => repoGroups().map(g => g.repoFullName),
303303
() => viewState.lockedRepos.pullRequests,
304+
() => viewState.ignoredItems.length,
304305
);
305306

306307
function handleSort(field: string, direction: "asc" | "desc") {
@@ -321,38 +322,41 @@ export default function PullRequestsTab(props: PullRequestsTabProps) {
321322
return (
322323
<div class="flex flex-col h-full">
323324
{/* Filter toolbar with SortDropdown */}
324-
<div class="flex flex-wrap items-center gap-3 px-4 py-2 border-b border-base-300 bg-base-100">
325-
<SortDropdown
326-
options={sortOptions}
327-
value={sortPref().field}
328-
direction={sortPref().direction}
329-
onChange={handleSort}
330-
/>
331-
<FilterChips
332-
groups={filterGroups()}
333-
values={viewState.tabFilters.pullRequests}
334-
onChange={(field, value) => {
335-
setTabFilter("pullRequests", field as PullRequestFilterField, value);
336-
setPage(0);
337-
}}
338-
onReset={(field) => {
339-
resetTabFilter("pullRequests", field as PullRequestFilterField);
340-
setPage(0);
341-
}}
342-
onResetAll={() => {
343-
resetAllTabFilters("pullRequests");
344-
setPage(0);
345-
}}
346-
/>
347-
<div class="flex-1" />
348-
<ExpandCollapseButtons
349-
onExpandAll={() => setAllExpanded("pullRequests", repoGroups().map((g) => g.repoFullName), true)}
350-
onCollapseAll={() => setAllExpanded("pullRequests", repoGroups().map((g) => g.repoFullName), false)}
351-
/>
352-
<IgnoreBadge
353-
items={viewState.ignoredItems.filter((i) => i.type === "pullRequest")}
354-
onUnignore={unignoreItem}
355-
/>
325+
<div class="flex items-start gap-3 px-4 py-2 border-b border-base-300 bg-base-100">
326+
<div class="flex flex-wrap items-center gap-3 min-w-0 flex-1">
327+
<SortDropdown
328+
options={sortOptions}
329+
value={sortPref().field}
330+
direction={sortPref().direction}
331+
onChange={handleSort}
332+
/>
333+
<FilterChips
334+
groups={filterGroups()}
335+
values={viewState.tabFilters.pullRequests}
336+
onChange={(field, value) => {
337+
setTabFilter("pullRequests", field as PullRequestFilterField, value);
338+
setPage(0);
339+
}}
340+
onReset={(field) => {
341+
resetTabFilter("pullRequests", field as PullRequestFilterField);
342+
setPage(0);
343+
}}
344+
onResetAll={() => {
345+
resetAllTabFilters("pullRequests");
346+
setPage(0);
347+
}}
348+
/>
349+
</div>
350+
<div class="shrink-0 flex items-center gap-2 py-0.5">
351+
<ExpandCollapseButtons
352+
onExpandAll={() => setAllExpanded("pullRequests", repoGroups().map((g) => g.repoFullName), true)}
353+
onCollapseAll={() => setAllExpanded("pullRequests", repoGroups().map((g) => g.repoFullName), false)}
354+
/>
355+
<IgnoreBadge
356+
items={viewState.ignoredItems.filter((i) => i.type === "pullRequest")}
357+
onUnignore={unignoreItem}
358+
/>
359+
</div>
356360
</div>
357361

358362
{/* Loading skeleton — only when no data exists yet */}

src/app/lib/reorderHighlight.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,24 @@ import { detectReorderedRepos } from "./grouping";
44
export function createReorderHighlight(
55
getRepoOrder: Accessor<string[]>,
66
getLockedOrder: Accessor<string[]>,
7+
getIgnoredCount?: Accessor<number>,
78
): Accessor<ReadonlySet<string>> {
89
let prevOrder: string[] = [];
910
let prevLocked: string[] = [];
11+
let prevIgnoredCount = getIgnoredCount?.() ?? 0;
1012
let timeout: ReturnType<typeof setTimeout> | undefined;
1113
const [highlighted, setHighlighted] = createSignal<ReadonlySet<string>>(new Set());
1214

1315
createEffect(() => {
1416
const currentOrder = getRepoOrder();
1517
const currentLocked = getLockedOrder();
18+
const currentIgnoredCount = getIgnoredCount?.() ?? 0;
1619

1720
const lockedChanged = currentLocked.length !== prevLocked.length
1821
|| currentLocked.some((r, i) => r !== prevLocked[i]);
22+
const ignoredChanged = currentIgnoredCount !== prevIgnoredCount;
1923

20-
if (prevOrder.length > 0 && !lockedChanged) {
24+
if (prevOrder.length > 0 && !lockedChanged && !ignoredChanged) {
2125
const moved = detectReorderedRepos(prevOrder, currentOrder);
2226
if (moved.size > 0) {
2327
setHighlighted(moved);
@@ -28,6 +32,7 @@ export function createReorderHighlight(
2832

2933
prevOrder = currentOrder;
3034
prevLocked = [...currentLocked];
35+
prevIgnoredCount = currentIgnoredCount;
3136
});
3237
onCleanup(() => clearTimeout(timeout));
3338

0 commit comments

Comments
 (0)