# Story 3.1: History Feed UI Status: done ## Story As a user, I want to see a list of my past growing moments, So that I can reflect on my journey. ## Acceptance Criteria 1. **History Feed on Home Screen** - Given the user is on the Home screen - When they view the feed - Then they see a chronological list of past "Completed" sessions (Title, Date, Tags) - And the list supports lazy loading/pagination for performance 2. **View Past Enlightenment Artifact** - Given the user clicks a history card - When the card opens - Then the full "Enlightenment" artifact allows for reading - And the "Copy" action is available ## Tasks / Subtasks - [x] Create History Feed Data Layer - [x] Add `getCompletedDrafts()` method to DraftService - [x] Implement pagination/offset support (limit 20 items per page) - [x] Support reverse chronological sorting (newest first) - [ ] Add filtering by tags (optional enhancement) - [x] Create History Feed Store State - [x] Create `useHistoryStore` in `src/lib/store/history-store.ts` - [x] State: drafts array, loading, hasMore, error - [x] Actions: loadMore, refreshHistory, selectDraft - [x] Use atomic selectors for performance - [x] Create HistoryCard Component - [x] Create `HistoryCard.tsx` in `src/components/features/journal/` - [x] Display: Title, Date (formatted), Tags array - [x] Show snippet preview (first 100 chars of content) - [x] Support click-to-view-full action - [x] Accessible: semantic button/link with aria-label - [x] Responsive: proper touch targets (44px minimum) - [x] Create HistoryFeed List Component - [x] Create `HistoryFeed.tsx` in `src/components/features/journal/` - [x] Implement virtual scrolling or lazy loading - [x] Show loading spinner at bottom when loading more - [x] Handle empty state (no drafts yet) - [x] Handle error state with retry option - [x] Create HistoryDetailSheet Component - [x] Create `HistoryDetailSheet.tsx` in `src/components/features/journal/` - [x] Reuse Sheet component from DraftViewSheet (Story 2.2) - [x] Display full draft content with Merriweather font - [x] Include Copy button (reuse from Story 2.4) - [x] Support swipe-to-dismiss on mobile - [x] Handle edit/resume-chat action (future: Story 3.2) - [x] Integrate History Feed into Home Page - [x] Update `src/app/page.tsx` to include HistoryFeed - [x] Layout: New Chat button at bottom, history above - [x] Handle navigation: tap card -> open detail sheet - [ ] Add pull-to-refresh gesture (deferred - can be enhancement) - [x] Initial load shows first 20 drafts - [x] Implement Date Formatting Utility - [x] Create `formatRelativeDate()` in `src/lib/utils/date.ts` - [x] Support: "Today", "Yesterday", "X days ago", full date - [ ] Support i18n (future enhancement) - [x] Test edge cases: future dates, null dates - [x] Add Empty State for New Users - [x] Create `EmptyHistoryState.tsx` component - [x] Show encouraging message when no drafts exist - [x] Include CTA to start first vent - [x] Use calming illustration or icon - [x] Implement Loading States - [x] Skeleton screens for history cards - [ ] Progressive loading animation (deferred - can be enhancement) - [ ] Smooth fade-in for new items (deferred - can be enhancement) - [x] Test History Feed End-to-End - [x] Unit test: DraftService.getCompletedDrafts() with pagination - [x] Unit test: DraftService.getCompletedCount() - [x] Unit test: HistoryStore loadMore action - [x] Unit test: formatRelativeDate() - [x] Component test: HistoryCard rendering - [x] Component test: HistoryFeed rendering - [x] Component test: Home page integration - [ ] Edge case: No drafts (empty state) - [ ] Edge case: Draft with no tags - [ ] Edge case: Short content preview - [ ] Integration tests: Deferred to integration test suite ## Dev Notes ### Architecture Compliance (CRITICAL) **Logic Sandwich Pattern - DO NOT VIOLATE:** - **UI Components** MUST NOT import `src/lib/db` directly - All history data MUST go through `DraftService` layer - DraftService queries `db.drafts` table (status='completed') - Components use Zustand store via atomic selectors only - Services return plain arrays, not Dexie observables **State Management - Atomic Selectors Required:** ```typescript // BAD - Causes unnecessary re-renders const { drafts, loading } = useHistoryStore(); // GOOD - Atomic selectors const drafts = useHistoryStore(s => s.drafts); const loadMore = useHistoryStore(s => s.loadMore); ``` **Local-First Data Boundary:** - History data is queried from IndexedDB only - No server API calls for history (privacy requirement) - Completed drafts persist locally (Story 2.4 completion flow) - Offline support: History must be viewable without network **Performance Requirements:** - NFR-02: App must load in <1.5s - Use lazy loading/virtual scrolling for large histories - Limit initial load to 20 drafts - Pagination loads additional drafts on demand ### Architecture Implementation Details **Story Purpose:** This story implements the **"History Journal"** - the persistent feed where users can revisit all their past "Enlightenment" artifacts. This transforms the app from a single-use tool into a **growth journal**, enabling reflection on past insights. **Data Source - Completed Drafts:** - Story 2.4 established draft completion flow - Completed drafts have `status: 'completed'` and `completedAt` timestamp - History queries `db.drafts.where('[status+completedAt]').between(...)` - Sorted by `completedAt` descending (newest first) using compound index **HistoryStore Architecture:** ```typescript // src/lib/store/history-store.ts interface HistoryState { drafts: Draft[]; // Loaded drafts (paginated) loading: boolean; hasMore: boolean; // Can load more? error: string | null; selectedDraft: Draft | null; // Actions loadMore: () => Promise; refreshHistory: () => Promise; selectDraft: (draft: Draft) => void; closeDetail: () => void; } ``` **DraftService History Methods:** ```typescript // src/lib/db/draft-service.ts export class DraftService { // Get completed drafts with pagination static async getCompletedDrafts(options: { limit?: number; offset?: number; }): Promise { return await db.drafts .where('status') .equals('completed') .reverse() // Newest first .offset(options.offset || 0) .limit(options.limit || 20) .toArray(); } // Count total completed drafts static async getCompletedCount(): Promise { return await db.drafts .where('status') .equals('completed') .count(); } } ``` **Files to Create:** - `src/lib/store/history-store.ts` - History feed state management - `src/components/features/journal/HistoryCard.tsx` - Individual history item - `src/components/features/journal/HistoryFeed.tsx` - List with lazy loading - `src/components/features/journal/HistoryDetailSheet.tsx` - Full draft view - `src/components/features/journal/EmptyHistoryState.tsx` - Empty state - `src/lib/utils/date.ts` - Date formatting utilities - `src/components/features/journal/index.ts` - Feature exports **Files to Modify:** - `src/lib/db/draft-service.ts` - Add getCompletedDrafts(), getCompletedCount() - `src/app/(main)/page.tsx` - Integrate HistoryFeed into home page ### UX Design Specifications **From UX Design Document:** **History Feed as Home Screen:** - The home screen IS the history feed - Latest generated post shown at top (reminder of value) - Large "+" or "New" button at bottom for new vent - Scrolling through past wins provides dopamine hits **Visual Hierarchy:** ```mermaid graph TD A[Home Screen] --> B[Header: My Journal] A --> C[History Feed - Scrollable] A --> D[FAB: + New Vent] C --> E[History Card 1 - Latest] C --> F[History Card 2] C --> G[History Card 3...] C --> H[Load More Indicator] ``` **HistoryCard Design:** - **Title:** Merriweather font, bold, truncate after 2 lines - **Date:** Inter font, subtle gray, relative format ("Today", "Yesterday") - **Tags:** Pill badges, subtle background - **Preview:** First 100 chars of content, light gray - **Elevation:** Subtle shadow on hover (desktop) or tap (mobile) **HistoryDetailSheet Pattern:** - Reuses DraftViewSheet component from Story 2.2 - Same "Medium-style" typography (Merriweather) - Copy button in footer (from Story 2.4) - Swipe down to dismiss (mobile pattern) **Empty State Design:** - Message: "Your journey starts here" - Subtext: "Every venting session becomes a learning moment" - CTA: "Start My First Vent" button - Calming illustration: Mountain path or sunrise **Loading States:** - **Initial Load:** 3 skeleton cards with shimmer effect - **Load More:** Spinner at bottom of list - **Progressive Fade-in:** New items fade in smoothly **Typography Specifications:** ```css /* HistoryCard */ .history-title { font-family: 'Merriweather', serif; font-weight: 700; font-size: 1.125rem; line-height: 1.4; } .history-date { font-family: 'Inter', sans-serif; color: #64748B; /* Slate-500 */ font-size: 0.875rem; } .history-preview { font-family: 'Inter', sans-serif; color: #94A3B8; /* Slate-400 */ font-size: 0.875rem; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; } ``` **Interaction Design:** - **Tap card:** Opens detail sheet (smooth slide-up) - **Long press:** Show context menu (Copy, Delete - Story 3.2) - **Pull to refresh:** Reload history from database - **Scroll to bottom:** Trigger loadMore when 5 items from end ### Previous Story Intelligence (from Epic 2) **Patterns Established (must follow):** - **Logic Sandwich Pattern:** UI -> Zustand -> Service -> DB (strictly enforced) - **Atomic Selectors:** All state access uses `useStore(s => s.field)` - **Draft Storage:** Completed drafts in `drafts` table with status='completed' - **Sheet Pattern:** Reuse DraftViewSheet for detail view - **Copy Utility:** Reuse clipboard.copyMarkdown() from Story 2.4 **Key Files from Previous Stories:** - `src/lib/db/draft-service.ts` - Add history query methods - `src/lib/store/chat-store.ts` - Pattern for atomic selectors - `src/components/features/draft/DraftViewSheet.tsx` - Reuse for detail view - `src/lib/utils/clipboard.ts` - Reuse copy functionality - `src/components/ui/` - ShadCN primitives (Card, Button, Sheet) **Learnings to Apply:** - Epic 1 retrospective: **Atomic selectors are non-negotiable** for performance - Story 2.2 established Sheet auto-open/close pattern - reuse for history detail - Story 2.4 established completion status - query by status='completed' - Use same shimmer loading pattern from Story 2.2 (DraftViewSheet skeleton) - Follow same error handling pattern (retry with exponential backoff) **Draft Data Structure (from Story 2.1):** ```typescript interface DraftRecord { id?: number; sessionId: string; title: string; content: string; // Markdown formatted tags: string[]; createdAt: number; completedAt?: number; // Set by Story 2.4 status: 'draft' | 'completed' | 'regenerated'; } ``` **Integration with Copy (Story 2.4):** - Copy button in HistoryDetailSheet reuses clipboard utility - Copy action doesn't change draft status (already completed) - Success toast from Story 2.4 provides feedback ### Pagination & Performance Strategy **Lazy Loading Implementation:** ```typescript // HistoryStore pagination const PAGE_SIZE = 20; loadMore: async () => { const currentLength = get().drafts.length; const newDrafts = await DraftService.getCompletedDrafts({ limit: PAGE_SIZE, offset: currentLength }); set(state => ({ drafts: [...state.drafts, ...newDrafts], hasMore: newDrafts.length === PAGE_SIZE })); } ``` **Virtual Scrolling vs Lazy Loading:** - **MVP Approach:** Lazy loading (simpler, sufficient for <100 drafts) - **Future Enhancement:** Virtual scrolling for very large histories - Lazy loading appends to array, React renders all - For MVP, 100 drafts * ~2KB each = ~200KB in memory (acceptable) **Performance Optimizations:** - **Memoization:** React.memo on HistoryCard component - **Key Prop:** Use draft.id as key (stable across re-renders) - **Avoid Inline Functions:** Define click handlers in component, not render - **Debounce Scroll:** Trigger loadMore only when near bottom **Load More Trigger:** ```typescript // Infinite scroll trigger const handleScroll = (e: React.UIEvent) => { const target = e.target as HTMLElement; const scrollBottom = target.scrollHeight - target.scrollTop - target.clientHeight; // Trigger when 200px from bottom if (scrollBottom < 200 && hasMore && !loading) { loadMore(); } }; ``` ### Date Formatting Implementation **Utility Function:** ```typescript // src/lib/utils/date.ts export function formatRelativeDate(timestamp: number): string { const now = Date.now(); const diff = now - timestamp; const days = Math.floor(diff / (1000 * 60 * 60 * 24)); if (days === 0) return 'Today'; if (days === 1) return 'Yesterday'; if (days < 7) return `${days} days ago`; if (days < 30) return `${Math.floor(days / 7)} weeks ago`; // Full date for older posts const date = new Date(timestamp); return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: date.getFullYear() !== new Date().getFullYear() ? 'numeric' : undefined }); } ``` **Time Zones:** - Store timestamps as UTC (Date.now()) - Format in user's local timezone (toLocaleDateString handles this) - Avoid timezone conversion bugs ### Component Implementation Details **HistoryCard Component:** ```typescript // src/components/features/journal/HistoryCard.tsx interface HistoryCardProps { draft: Draft; onClick: (draft: Draft) => void; } export function HistoryCard({ draft, onClick }: HistoryCardProps) { return ( ); } ``` **HistoryFeed Component:** ```typescript // src/components/features/journal/HistoryFeed.tsx export function HistoryFeed() { const drafts = useHistoryStore(s => s.drafts); const loading = useHistoryStore(s => s.loading); const hasMore = useHistoryStore(s => s.hasMore); const loadMore = useHistoryStore(s => s.loadMore); const selectDraft = useHistoryStore(s => s.selectDraft); useEffect(() => { // Initial load loadMore(); }, []); if (drafts.length === 0 && !loading) { return ; } return (
{drafts.map(draft => ( ))} {loading && } {!hasMore && drafts.length > 0 && (

You've reached the beginning

)}
); } ``` **HistoryDetailSheet Component:** ```typescript // src/components/features/journal/HistoryDetailSheet.tsx export function HistoryDetailSheet() { const selectedDraft = useHistoryStore(s => s.selectedDraft); const closeDetail = useHistoryStore(s => s.closeDetail); if (!selectedDraft) return null; return ( ); } ``` ### Home Page Integration **Layout Strategy:** ```typescript // src/app/(main)/page.tsx export default function HomePage() { return (

My Journal

); } ``` **Navigation Flow:** - Home page shows history feed by default - FAB (Floating Action Button) navigates to `/chat` for new vent - Tapping history card opens detail sheet (stays on home page) - Detail sheet swipe-to-dismiss returns to feed ### Accessibility Requirements **WCAG AA Compliance:** - History cards must be buttons or links with proper semantics - All interactive elements have 44px minimum touch targets - Focus management: Detail sheet traps focus until dismissed - Screen reader announces draft title and date - Empty state is accessible and encouraging **Keyboard Navigation:** - Tab through history cards in order - Enter/Space opens detail sheet - Escape closes detail sheet - Focus returns to triggering card after close **Screen Reader Support:** ```typescript ``` ### Testing Requirements **Unit Tests:** - `DraftService.getCompletedDrafts()` returns completed drafts only - `DraftService.getCompletedDrafts()` respects pagination (limit, offset) - `formatRelativeDate()` returns correct relative dates - `HistoryStore.loadMore()` appends drafts correctly - `HistoryStore.loadMore()` sets hasMore to false when exhausted **Integration Tests:** - HistoryFeed loads drafts from IndexedDB on mount - Tapping card opens detail sheet with correct draft - LoadMore triggers when scrolling near bottom - Copy button in detail sheet copies content - Empty state shows when no completed drafts exist **Edge Cases:** - No completed drafts: Show empty state - Single draft: Show draft, hide load more - Exactly 20 drafts: Load more shows, loads empty second page - 100+ drafts: Pagination works smoothly - Draft with very long title: Truncate properly - Draft with no tags: Render without tag section - Draft with special characters in content: Escape properly - Navigate away during load: Handle gracefully (cancel in-flight request) **Performance Tests:** - Initial page load <1.5s (NFR-02) - Scroll performance: 60fps with 100 items in DOM - Load more completes within 500ms - Detail sheet opens within 100ms **Accessibility Tests:** - Keyboard navigation through entire history - Screen reader announces all content - Focus management in detail sheet - Touch targets are 44px minimum ### Project Structure Notes **Following Feature-First Lite Pattern:** - New feature folder: `src/components/features/journal/` - All history-related components in this folder - Service extensions in existing `draft-service.ts` - New store in `src/lib/store/history-store.ts` **Alignment with Unified Project Structure:** ``` src/ components/ features/ journal/ # NEW: History feed HistoryCard.tsx HistoryFeed.tsx HistoryDetailSheet.tsx EmptyHistoryState.tsx index.ts draft/ # Existing: From Epic 2 chat/ # Existing: From Epic 1 lib/ store/ history-store.ts # NEW utils/ date.ts # NEW ``` **No Conflicts Detected:** - Journal is new feature, no overlap with existing code - Reuses existing Sheet, Button, Card from ShadCN - DraftService extension adds new methods (no breaking changes) ### Service Layer Extensions **DraftService History Methods:** ```typescript // src/lib/db/draft-service.ts export class DraftService { // Existing methods... // NEW: Get completed drafts with pagination static async getCompletedDrafts(options: { limit?: number; offset?: number; }): Promise { const query = db.drafts .where('status') .equals('completed') .reverse(); // Newest first if (options.offset) query = query.offset(options.offset); if (options.limit) query = query.limit(options.limit); return await query.toArray(); } // NEW: Count completed drafts static async getCompletedCount(): Promise { return await db.drafts .where('status') .equals('completed') .count(); } // NEW: Get single draft by ID (for detail view) static async getDraftById(id: number): Promise { return await db.drafts.get(id); } } ``` **HistoryStore Implementation:** ```typescript // src/lib/store/history-store.ts import { create } from 'zustand'; import { DraftService } from '../db/draft-service'; interface HistoryState { drafts: Draft[]; loading: boolean; hasMore: boolean; error: string | null; selectedDraft: Draft | null; loadMore: () => Promise; refreshHistory: () => Promise; selectDraft: (draft: Draft) => void; closeDetail: () => void; } export const useHistoryStore = create((set, get) => ({ drafts: [], loading: false, hasMore: true, error: null, selectedDraft: null, loadMore: async () => { const { drafts, loading } = get(); if (loading) return; set({ loading: true, error: null }); try { const newDrafts = await DraftService.getCompletedDrafts({ limit: 20, offset: drafts.length }); set(state => ({ drafts: [...state.drafts, ...newDrafts], hasMore: newDrafts.length === 20, loading: false })); } catch (error) { set({ error: 'Failed to load history', loading: false }); } }, refreshHistory: async () => { set({ drafts: [], hasMore: true }); await get().loadMore(); }, selectDraft: (draft: Draft) => set({ selectedDraft: draft }), closeDetail: () => set({ selectedDraft: null }), })); ``` ### Mobile-First Responsive Design **Breakpoint Strategy (from UX):** - **Mobile (<768px):** Full width, bottom FAB, swipe gestures - **Desktop (>=768px):** Centered container (600px max), extra whitespace **Mobile Optimizations:** - Pull-to-refresh gesture for reloading history - Swipe-to-dismiss for detail sheet - Haptic feedback on tap (optional enhancement) - Touch targets minimum 44px **Desktop Adaptations:** ```css /* Center container on desktop */ @media (min-width: 768px) { .history-feed-container { max-width: 600px; margin: 0 auto; padding: 2rem 1rem; } } ``` ### Performance Requirements **NFR-02 Compliance (App Load Time):** - Initial history load must complete within 1.5s - Lazy load ensures fast initial render (20 drafts max) - Subsequent loads happen on-demand **State Updates:** - HistoryStore uses immer middleware for efficient updates - Atomic selectors prevent unnecessary re-renders - Memoization on HistoryCard component **Database Query Optimization:** - Use IndexedDB indexes (completedAt is indexed, [status+completedAt] compound index used for sorting) - Pagination limits query results - Reverse sorting uses index efficiently ### Offline Behavior **NFR-05 Compliance (Offline Access):** - History feed must be viewable offline - All data is local (IndexedDB) - No network requests required - Show "Offline" indicator if no network (optional) **Offline Sync Queue:** - Story 3.3 will implement full sync queue - For this story, offline is default (no sync needed yet) - Future: New drafts sync when online (Story 3.3) ### Security & Privacy Requirements **NFR-03 & NFR-04 Compliance:** - All history data stays on device (IndexedDB) - No server API calls for history - No analytics or tracking on history views - User owns their complete journal locally ### References **Epic Reference:** - [Epic 3: "My Legacy" - History, Offline Sync & PWA Polish](file:///home/maximilienmao/Projects/Test01/_bmad-output/planning-artifacts/epics.md#epic-3-my-legacy---history-offline-sync--pwa-polish) - [Story 3.1: History Feed UI](file:///home/maximilienmao/Projects/Test01/_bmad-output/planning-artifacts/epics.md#story-31-history-feed-ui) - FR-06: "Users can view a chronological feed of past 'Enlightenments'" - FR-14: "Users can export their entire history as a JSON/Markdown file" (future: Story 3.4) **Architecture Documents:** - [Project Context: Logic Sandwich](file:///home/maximilienmao/Projects/Test01/_bmad-output/project-context.md#1-the-logic-sandwich-pattern-service-layer) - [Project Context: State Management](file:///home/maximilienmao/Projects/Test01/_bmad-output/project-context.md#2-state-management-zustand) - [Project Context: Local-First Boundary](file:///home/maximilienmao/Projects/Test01/_bmad-output/project-context.md#3-local-first-data-boundary) **UX Design Specifications:** - [UX: History Feed Pattern](file:///home/maximilienmao/Projects/Test01/_bmad-output/planning-artifacts/ux-design-specification.md#3-history--reflection) - [UX: Empty State Design](file:///home/maximilienmao/Projects/Test01/_bmad-output/planning-artifacts/ux-design-specification.md#responsive-strategy) - [UX: Typography System](file:///home/maximilienmao/Projects/Test01/_bmad-output/planning-artifacts/ux-design-specification.md#typography-system) **Previous Stories:** - [Story 2.1: Ghostwriter Agent & Markdown Generation](file:///home/maximilienmao/Projects/Test01/_bmad-output/implementation-artifacts/2-1-ghostwriter-agent-markdown-generation.md) - Draft data structure - [Story 2.2: Draft View UI (The Slide-Up)](file:///home/maximilienmao/Projects/Test01/_bmad-output/implementation-artifacts/2-2-draft-view-ui-the-slide-up.md) - Sheet pattern to reuse - [Story 2.4: Export & Copy Actions](file:///home/maximilienmao/Projects/Test01/_bmad-output/implementation-artifacts/2-4-export-copy-actions.md) - Copy functionality to reuse **Epic Retrospectives:** - [Epic 1 Retrospective](file:///home/maximilienmao/Projects/Test01/_bmad-output/implementation-artifacts/epic-1-retro-2026-01-22.md) - Atomic selector lessons ## Dev Agent Record ### Agent Model Used Claude Opus 4.5 (model ID: 'claude-opus-4-5-20251101') ### Debug Log References Session file: `/tmp/claude/-home-maximilienmao-Projects-Test01/bf525d3f-3c0e-417a-81e7-dad92fec28ad/scratchpad` ### Completion Notes List **Story Analysis Completed:** - Extracted story requirements from Epic 3, Story 3.1 - Analyzed previous Epics 1 and 2 for established patterns - Reviewed architecture for compliance requirements (Logic Sandwich, State Management, Local-First) - Reviewed UX specification for history feed and empty state patterns - Identified all files to create and modify **Implementation Context Summary:** **Story Purpose:** This story implements the **"History Journal"** - the persistent feed where users can revisit all their past "Enlightenment" artifacts. This transforms the app from a single-use tool into a **growth journal**, enabling reflection on past insights and building the "My Legacy" emotional connection. **Key Technical Decisions:** 1. **New HistoryStore:** Separate Zustand store for history state (not chat store) 2. **Pagination:** Lazy load 20 drafts at a time for performance 3. **Reusable Components:** Leverage Sheet from Story 2.2, Copy from Story 2.4 4. **Empty State:** Encouraging entry point for new users 5. **Relative Dates:** Human-readable timestamps ("Today", "Yesterday") 6. **Mobile-First:** Full width on mobile, centered container on desktop **Dependencies:** - No new external dependencies required - Uses existing ShadCN components (Card, Sheet, Button) - Reuses clipboard utility from Story 2.4 - May need pull-to-refresh library (optional enhancement) **Integration Points:** - Home page shows history feed by default - FAB navigates to `/chat` for new venting session - Tapping history card opens detail sheet (reuses DraftViewSheet) - Story 3.2 will add delete actions to detail sheet - Story 3.3 will add offline sync queue **Files to Create:** - `src/lib/store/history-store.ts` - History feed state management - `src/components/features/journal/HistoryCard.tsx` - Individual history item - `src/components/features/journal/HistoryFeed.tsx` - List with lazy loading - `src/components/features/journal/HistoryDetailSheet.tsx` - Full draft view - `src/components/features/journal/EmptyHistoryState.tsx` - Empty state - `src/lib/utils/date.ts` - Date formatting utilities - `src/components/features/journal/index.ts` - Feature exports **Files to Modify:** - `src/lib/db/draft-service.ts` - Add getCompletedDrafts(), getCompletedCount(), getDraftById() - `src/app/(main)/page.tsx` - Integrate HistoryFeed into home page **Testing Strategy:** - Unit tests for DraftService history methods - Unit tests for date formatting utility - Integration tests for feed loading and pagination - Edge case tests (empty, single, large history) - Accessibility tests (keyboard, screen reader) **History Feed Pattern:** ``` Home Page Load ↓ HistoryStore.loadMore() - Initial 20 drafts ↓ DraftService.getCompletedDrafts({ limit: 20, offset: 0 }) ↓ Query db.drafts.where('status').equals('completed').reverse() ↓ HistoryFeed renders cards for each draft ↓ User scrolls near bottom ↓ Trigger loadMore() - Append next 20 drafts ↓ User taps card ↓ HistoryStore.selectDraft(draft) ↓ HistoryDetailSheet opens with full content ↓ User taps Copy or swipes to dismiss ``` **User Experience Flow:** - First-time user sees empty state with CTA to start first vent - Returning user sees their journal of completed posts - Tapping any post opens the full "Enlightenment" artifact - Copy button allows quick export (from Story 2.4) - Emotional payoff: Seeing past wins reinforces value of app **Lessons from Epic 1 Retrospective Applied:** - **Atomic Selectors:** All HistoryStore access uses `useHistoryStore(s => s.field)` - **Performance:** Pagination prevents rendering 100+ items at once - **Testing:** Comprehensive unit and integration tests - **State Management:** Separate store avoids chat store bloat ### File List **New Files to Create:** - `src/lib/store/history-store.ts` - History feed state management - `src/components/features/journal/HistoryCard.tsx` - Individual history item - `src/components/features/journal/HistoryFeed.tsx` - List component - `src/components/features/journal/HistoryDetailSheet.tsx` - Detail view sheet - `src/components/features/journal/EmptyHistoryState.tsx` - Empty state - `src/components/features/journal/index.ts` - Feature exports - `src/lib/utils/date.ts` - Date formatting utilities - `src/lib/utils/date.test.ts` - Date utility tests - `src/lib/store/history-store.test.ts` - History store tests - `src/integration/history-feed.test.ts` - End-to-end history tests - `src/components/features/journal/HistoryCard.test.tsx` - HistoryCard tests - `src/components/features/journal/HistoryFeed.test.tsx` - HistoryFeed tests **Files to Modify:** - `src/lib/db/draft-service.ts` - Add history query methods - `src/lib/db/draft-service.test.ts` - Add history method tests - `src/app/(main)/page.tsx` - Integrate history feed