Split dashboard InboxPanel card renderer
This commit is contained in:
@@ -77,101 +77,37 @@ function inboxTargetHref(item: InboxItem): string | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
export function InboxPanel({
|
||||
overview,
|
||||
tasks,
|
||||
interface InboxCardProps {
|
||||
inboxActionKey: InboxActionKey | null;
|
||||
item: InboxItem;
|
||||
locale: Locale;
|
||||
onInboxAction: (item: InboxItem, action: DashboardInboxAction) => void;
|
||||
onTaskAction: (task: DashboardTask, action: DashboardTaskAction) => void;
|
||||
taskActionKey: string | null;
|
||||
tasks: DashboardTask[];
|
||||
t: Messages;
|
||||
}
|
||||
|
||||
function InboxCard({
|
||||
inboxActionKey,
|
||||
item,
|
||||
locale,
|
||||
onInboxAction,
|
||||
onTaskAction,
|
||||
inboxActionKey,
|
||||
taskActionKey,
|
||||
tasks,
|
||||
t,
|
||||
}: InboxPanelProps) {
|
||||
const [filter, setFilter] = useState<InboxFilter>('all');
|
||||
const items = overview.inbox ?? [];
|
||||
const counts = useMemo(() => {
|
||||
const next: Record<InboxFilter, number> = {
|
||||
all: items.length,
|
||||
'pending-room': 0,
|
||||
'reviewer-request': 0,
|
||||
approval: 0,
|
||||
'arbiter-request': 0,
|
||||
'ci-failure': 0,
|
||||
mention: 0,
|
||||
};
|
||||
for (const item of items) next[item.kind] += 1;
|
||||
return next;
|
||||
}, [items]);
|
||||
const filteredItems =
|
||||
filter === 'all' ? items : items.filter((item) => item.kind === filter);
|
||||
const severityCounts = items.reduce(
|
||||
(acc, item) => {
|
||||
acc[item.severity] += 1;
|
||||
return acc;
|
||||
},
|
||||
{ error: 0, warn: 0, info: 0 },
|
||||
);
|
||||
|
||||
if (items.length === 0) {
|
||||
return <EmptyState>{t.inbox.empty}</EmptyState>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="inbox-board">
|
||||
<section className="inbox-summary" aria-label={t.inbox.summary}>
|
||||
<div>
|
||||
<span>{t.inbox.total}</span>
|
||||
<strong>{items.length}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>{t.inbox.severity.error}</span>
|
||||
<strong>{severityCounts.error}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>{t.inbox.severity.warn}</span>
|
||||
<strong>{severityCounts.warn}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>{t.inbox.severity.info}</span>
|
||||
<strong>{severityCounts.info}</strong>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div className="inbox-filters" aria-label={t.inbox.filters}>
|
||||
{INBOX_FILTERS.map((item) => {
|
||||
if (item !== 'all' && counts[item] === 0) return null;
|
||||
const label = item === 'all' ? t.inbox.all : t.inbox.kinds[item];
|
||||
return (
|
||||
<button
|
||||
aria-pressed={filter === item}
|
||||
className={filter === item ? 'is-active' : undefined}
|
||||
key={item}
|
||||
onClick={() => setFilter(item)}
|
||||
type="button"
|
||||
>
|
||||
{label}
|
||||
<span>{counts[item]}</span>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="inbox-list" aria-label={t.inbox.cardsAria}>
|
||||
{filteredItems.map((item) => {
|
||||
}: InboxCardProps) {
|
||||
const href = inboxTargetHref(item);
|
||||
const linkedTask =
|
||||
item.source === 'scheduled-task' && item.taskId
|
||||
? tasks.find((task) => task.id === item.taskId)
|
||||
: undefined;
|
||||
const linkedTaskActions = linkedTask
|
||||
? taskActionsFor(linkedTask)
|
||||
: [];
|
||||
const linkedTaskActions = linkedTask ? taskActionsFor(linkedTask) : [];
|
||||
const inboxActions = inboxActionsFor(item);
|
||||
|
||||
return (
|
||||
<article
|
||||
className={`inbox-card inbox-${item.severity}`}
|
||||
key={item.id}
|
||||
>
|
||||
<article className={`inbox-card inbox-${item.severity}`}>
|
||||
<div className="inbox-card-head">
|
||||
<div>
|
||||
<span className="eyebrow">{t.inbox.kinds[item.kind]}</span>
|
||||
@@ -256,8 +192,102 @@ export function InboxPanel({
|
||||
) : null}
|
||||
</article>
|
||||
);
|
||||
}
|
||||
|
||||
export function InboxPanel({
|
||||
overview,
|
||||
tasks,
|
||||
locale,
|
||||
onInboxAction,
|
||||
onTaskAction,
|
||||
inboxActionKey,
|
||||
taskActionKey,
|
||||
t,
|
||||
}: InboxPanelProps) {
|
||||
const [filter, setFilter] = useState<InboxFilter>('all');
|
||||
const items = overview.inbox ?? [];
|
||||
const counts = useMemo(() => {
|
||||
const next: Record<InboxFilter, number> = {
|
||||
all: items.length,
|
||||
'pending-room': 0,
|
||||
'reviewer-request': 0,
|
||||
approval: 0,
|
||||
'arbiter-request': 0,
|
||||
'ci-failure': 0,
|
||||
mention: 0,
|
||||
};
|
||||
for (const item of items) next[item.kind] += 1;
|
||||
return next;
|
||||
}, [items]);
|
||||
const filteredItems =
|
||||
filter === 'all' ? items : items.filter((item) => item.kind === filter);
|
||||
const severityCounts = items.reduce(
|
||||
(acc, item) => {
|
||||
acc[item.severity] += 1;
|
||||
return acc;
|
||||
},
|
||||
{ error: 0, warn: 0, info: 0 },
|
||||
);
|
||||
|
||||
if (items.length === 0) {
|
||||
return <EmptyState>{t.inbox.empty}</EmptyState>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="inbox-board">
|
||||
<section className="inbox-summary" aria-label={t.inbox.summary}>
|
||||
<div>
|
||||
<span>{t.inbox.total}</span>
|
||||
<strong>{items.length}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>{t.inbox.severity.error}</span>
|
||||
<strong>{severityCounts.error}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>{t.inbox.severity.warn}</span>
|
||||
<strong>{severityCounts.warn}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>{t.inbox.severity.info}</span>
|
||||
<strong>{severityCounts.info}</strong>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div className="inbox-filters" aria-label={t.inbox.filters}>
|
||||
{INBOX_FILTERS.map((item) => {
|
||||
if (item !== 'all' && counts[item] === 0) return null;
|
||||
const label = item === 'all' ? t.inbox.all : t.inbox.kinds[item];
|
||||
return (
|
||||
<button
|
||||
aria-pressed={filter === item}
|
||||
className={filter === item ? 'is-active' : undefined}
|
||||
key={item}
|
||||
onClick={() => setFilter(item)}
|
||||
type="button"
|
||||
>
|
||||
{label}
|
||||
<span>{counts[item]}</span>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="inbox-list" aria-label={t.inbox.cardsAria}>
|
||||
{filteredItems.map((item) => (
|
||||
<InboxCard
|
||||
inboxActionKey={inboxActionKey}
|
||||
item={item}
|
||||
key={item.id}
|
||||
locale={locale}
|
||||
onInboxAction={onInboxAction}
|
||||
onTaskAction={onTaskAction}
|
||||
taskActionKey={taskActionKey}
|
||||
tasks={tasks}
|
||||
t={t}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user