diff --git a/frontend/src/components/AttentionFeed.tsx b/frontend/src/components/AttentionFeed.tsx index aed65ea0..00b3f6e8 100644 --- a/frontend/src/components/AttentionFeed.tsx +++ b/frontend/src/components/AttentionFeed.tsx @@ -43,18 +43,25 @@ function AttentionCard({ // ─── Main component ──────────────────────────────────────────────────────── +const DEFAULT_VISIBLE_COUNT = 5; + export function AttentionFeed() { const { items, tier1Count, loading } = useAttentionFeed(); const navigate = useNavigate(); const hasUrgent = tier1Count > 0; const [manualOpen, setManualOpen] = useState(null); + const [showAll, setShowAll] = useState(false); const isOpen = manualOpen ?? true; // always open by default const toggleOpen = useCallback(() => { setManualOpen((prev) => !(prev ?? true)); }, []); + const toggleShowAll = useCallback(() => { + setShowAll((prev) => !prev); + }, []); + const handleTap = useCallback( (item: AttentionItem) => { selectionChanged(); @@ -69,6 +76,9 @@ export function AttentionFeed() { const t2Count = items.filter((i) => i.tier === 2).length; if (t2Count > 0) summaryParts.push(`${t2Count} in focus`); + const visibleItems = showAll ? items : items.slice(0, DEFAULT_VISIBLE_COUNT); + const hasMore = items.length > DEFAULT_VISIBLE_COUNT; + return (
+ )}
)} diff --git a/frontend/src/hooks/__tests__/useAttentionFeed.test.ts b/frontend/src/hooks/__tests__/useAttentionFeed.test.ts index 334f9073..ea43d6c8 100644 --- a/frontend/src/hooks/__tests__/useAttentionFeed.test.ts +++ b/frontend/src/hooks/__tests__/useAttentionFeed.test.ts @@ -175,7 +175,7 @@ describe('useAttentionFeed', () => { expect(result.current.items[1].tier).toBe(2); }); - it('caps output at 5 items', async () => { + it('returns all items without capping', async () => { const items = Array.from({ length: 10 }, (_, i) => makeTodo({ id: `t${i}`, summary: `Item ${i}`, starred: true, urgency: 0.9 }), ); @@ -190,7 +190,8 @@ describe('useAttentionFeed', () => { expect(result.current.loading).toBe(false); }); - expect(result.current.items).toHaveLength(5); + expect(result.current.items).toHaveLength(10); + expect(result.current.tier1Count).toBe(10); }); it('subscribes to SSE events for live updates', () => { diff --git a/frontend/src/hooks/useAttentionFeed.ts b/frontend/src/hooks/useAttentionFeed.ts index f8dc2820..e5353549 100644 --- a/frontend/src/hooks/useAttentionFeed.ts +++ b/frontend/src/hooks/useAttentionFeed.ts @@ -214,8 +214,6 @@ function sortAttention(items: AttentionItem[]): AttentionItem[] { // ─── Hook ────────────────────────────────────────────────────────────────── -const MAX_ITEMS = 5; - export interface UseAttentionFeedReturn { items: AttentionItem[]; tier1Count: number; @@ -286,8 +284,7 @@ export function useAttentionFeed(): UseAttentionFeedReturn { const telosItems = telosToAttention(todos); const atbItems = atbToAttention(tasks); const sessionItems = sessionsToAttention(activities); - const all = sortAttention([...telosItems, ...atbItems, ...sessionItems]); - return all.slice(0, MAX_ITEMS); + return sortAttention([...telosItems, ...atbItems, ...sessionItems]); }, [todos, tasks, activities]); const tier1Count = useMemo(() => feed.filter((i) => i.tier === 1).length, [feed]); diff --git a/frontend/src/styles/global.css b/frontend/src/styles/global.css index 0a1941c2..57b79e87 100644 --- a/frontend/src/styles/global.css +++ b/frontend/src/styles/global.css @@ -833,6 +833,26 @@ textarea:focus { text-align: center; } +.attention-show-more { + width: 100%; + padding: var(--space-2) var(--space-4); + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius-md); + font-size: var(--text-xs); + color: var(--primary); + font-family: inherit; + font-weight: 600; + cursor: pointer; + text-align: center; + -webkit-tap-highlight-color: transparent; + transition: background 0.15s; +} + +.attention-show-more:active { + background: var(--hover); +} + .service-status { display: flex; gap: var(--space-4);