mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-08 01:57:31 +01:00
PAP-13408 T2: Inbox-style attention toolbar, date grouping, curtains
Rework the What-needs-me queue around the approved PAP-13401 plan (§1, §2, curtains from §6, left-align from §9): - lib/attention.ts: date-first grouping (Today/Yesterday/This week/Earlier), Type/Project/Severity group-by, newest/oldest sort, and type/project/ workspace/severity filters — all pure functions with localStorage helpers mirroring the InboxWorkItemGroupBy pattern. Re-buckets client-side (no refetch). - WhatNeedsMe.tsx: Inbox-language toolbar (ListFilter / Layers / ArrowUpDown popovers), collapsible group sections via the shared IssueGroupHeader with counts, and Snoozed(n)/Dismissed(n) curtains listing hidden rows (fetched with includeDismissed=true) with Restore + snooze reappear time. Fetches under a distinct query key so dismissed rows never inflate the sidebar badge. Left-aligned container (dropped mx-auto). - AttentionQueueRow.tsx: snooze presets submenu + dismiss on active rows; "hidden" variant renders curtain rows with Restore and reappear time. - inboxDismissals API + useInboxDismissals hook: add snooze + restore, and invalidate the attention feed on any dismiss/snooze/restore. - Tests: 20+ new cases covering sort/date-bucket/group/filter/options. Backfilled fixtures for T1's new required fields (dismissal, project, workspace, detail, InboxDismissal.kind/snoozedUntil). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
9dcdb3ce60
commit
5b98e17c4f
9 files changed
+1201
-75
No files matched your search
@@ -89,11 +89,30 @@ export function useInboxDismissals(companyId: string | null | undefined) {
|
||||
},
|
||||
onSettled: () => {
|
||||
if (!companyId) return;
|
||||
queryClient.invalidateQueries({ queryKey });
|
||||
queryClient.invalidateQueries({ queryKey: queryKeys.sidebarBadges(companyId) });
|
||||
invalidateDismissalConsumers();
|
||||
},
|
||||
});
|
||||
|
||||
function invalidateDismissalConsumers() {
|
||||
if (!companyId) return;
|
||||
queryClient.invalidateQueries({ queryKey });
|
||||
queryClient.invalidateQueries({ queryKey: queryKeys.sidebarBadges(companyId) });
|
||||
// The attention feed derives its rows from server-side dismissals, so any
|
||||
// dismiss/snooze/restore must re-pull it to keep the queue and curtains in sync.
|
||||
queryClient.invalidateQueries({ queryKey: queryKeys.attention(companyId) });
|
||||
}
|
||||
|
||||
const snoozeMutation = useMutation({
|
||||
mutationFn: ({ itemKey, snoozedUntil }: { itemKey: string; snoozedUntil: string }) =>
|
||||
inboxDismissalsApi.snooze(companyId!, itemKey, snoozedUntil),
|
||||
onSettled: invalidateDismissalConsumers,
|
||||
});
|
||||
|
||||
const restoreMutation = useMutation({
|
||||
mutationFn: ({ itemKey }: { itemKey: string }) => inboxDismissalsApi.restore(companyId!, itemKey),
|
||||
onSettled: invalidateDismissalConsumers,
|
||||
});
|
||||
|
||||
const dismissedAtByKey = useMemo(
|
||||
() => buildInboxDismissedAtByKey(dismissals),
|
||||
[dismissals],
|
||||
@@ -103,7 +122,9 @@ export function useInboxDismissals(companyId: string | null | undefined) {
|
||||
dismissals,
|
||||
dismissedAtByKey,
|
||||
dismiss: (itemKey: string) => dismissMutation.mutate({ itemKey }),
|
||||
isPending: dismissMutation.isPending,
|
||||
snooze: (itemKey: string, snoozedUntil: string) => snoozeMutation.mutate({ itemKey, snoozedUntil }),
|
||||
restore: (itemKey: string) => restoreMutation.mutate({ itemKey }),
|
||||
isPending: dismissMutation.isPending || snoozeMutation.isPending || restoreMutation.isPending,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user