---
phase: 11-chat-finalization
plan: 01
type: execute
---

<objective>
Add error handling, accessibility improvements, and polish to complete the chat feature.

Purpose: Ensure chat handles errors gracefully, meets accessibility standards, and provides excellent UX with proper status indicators and loading states.
Output: Production-ready chat with comprehensive error handling, ARIA attributes, keyboard navigation, and polished edge cases.
</objective>

<execution_context>
./.claude/get-shit-done/workflows/execute-phase.md
./summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/09-message-composition/09-02-SUMMARY.md
@.planning/phases/10-read-unread-status/10-01-SUMMARY.md
@jam-ui/src/components/client/chat/JKChatComposer.js
@jam-ui/src/components/client/JKSessionChatWindow.js
@jam-ui/src/store/features/sessionChatSlice.js

**Tech stack available:**
- React 16.13.1 with hooks
- Redux Toolkit with async thunks
- Playwright for integration tests
- WebSocket via JamServerContext

**Established patterns:**
- Error display with color coding (red for critical, yellow for warnings)
- Loading states with isOperating flags
- React.memo and useCallback for performance (already in place)
- WebSocket connection status via useJamServerContext

**Constraining decisions:**
- Performance optimizations already applied (React.memo on all components, useCallback/useMemo in place)
- Basic accessibility started (aria-label on close button)
- Error handling exists for validation but not for API/WebSocket failures

**Issues being addressed:**
- Missing error display for API failures (sendMessage thunk errors)
- No visual indicator for WebSocket disconnection
- Missing accessibility attributes (roles, labels, keyboard shortcuts)
- No loading spinner during fetchChatHistory
- Focus management not implemented (input not auto-focused on open)
</context>

<tasks>

<task type="auto">
  <name>Task 1: Add comprehensive error handling and status display</name>
  <files>jam-ui/src/components/client/chat/JKChatComposer.js, jam-ui/src/components/client/JKSessionChatWindow.js, jam-ui/src/components/client/chat/JKChatMessageList.js</files>
  <action>Enhance error handling and status display:

**In JKChatComposer.js:**
1. Add persistent error display for sendError (below validation message)
   - Show red error box: "Failed to send message. Please try again."
   - Include Retry button that clears input and allows re-sending
   - Position below textarea, above send button
2. Enhance disconnection warning (already exists, verify it's prominent)
   - Current: "Waiting for connection..." in validation message
   - Ensure it's visible and clear when !isConnected
3. Add retry handler:
   ```javascript
   const handleRetry = useCallback(() => {
     dispatch(clearSendError()); // Add this action to slice if needed
   }, [dispatch]);
   ```

**In JKSessionChatWindow.js:**
1. Add WebSocket disconnection banner at top (below header)
   - Only show when !isConnected from useJamServerContext
   - Yellow background (#fff3cd), amber text
   - Message: "⚠️ Connection lost. Reconnecting..."
   - Auto-hide when connection restored
2. Position: Between JKChatHeader and message list container

**In JKChatMessageList.js:**
1. Add loading spinner for fetchChatHistory
   - Show JKChatLoadingSpinner when fetchStatus === 'loading'
   - Position at top of message list
   - Hide when loaded or failed
2. Add error display for fetch failures
   - Show error message when fetchStatus === 'failed'
   - Red text: "Failed to load message history"
   - No retry needed (user can reload page)

**Error styling:**
- Send errors: Red (#dc3545) background, white text
- Connection warnings: Yellow (#fff3cd) background, dark text (#856404)
- Fetch errors: Red text only (no background)

Use inline styles for consistency with existing code.</action>
  <verify>
- Disconnect WebSocket in browser DevTools → see yellow banner
- Force sendMessage error (break API) → see red error with retry button
- Retry button clears error and re-enables input
- fetchChatHistory error → see red error text in message list
- All errors have appropriate colors and clear messaging
  </verify>
  <done>Error handling complete with persistent error displays, retry logic, disconnection banner, and loading states for all async operations</done>
</task>

<task type="auto">
  <name>Task 2: Add accessibility and keyboard navigation</name>
  <files>jam-ui/src/components/client/JKSessionChatWindow.js, jam-ui/src/components/client/chat/JKChatComposer.js, jam-ui/src/components/client/chat/JKChatHeader.js, jam-ui/src/components/client/JKSessionChatButton.js</files>
  <action>Add comprehensive accessibility attributes and keyboard navigation:

**In JKSessionChatWindow.js:**
1. Add role and ARIA attributes to window container:
   ```jsx
   <div role="dialog" aria-labelledby="chat-window-title" aria-modal="false">
   ```
   - Not aria-modal="true" since it's modeless (WindowPortal)
2. Add keyboard shortcut: Escape to close window
   ```javascript
   useEffect(() => {
     const handleKeyDown = (e) => {
       if (e.key === 'Escape') {
         dispatch(closeChatWindow());
       }
     };
     window.addEventListener('keydown', handleKeyDown);
     return () => window.removeEventListener('keydown', handleKeyDown);
   }, [dispatch]);
   ```
3. Focus management: Auto-focus textarea when window opens
   - Pass ref to JKChatComposer
   - Focus on mount when isWindowOpen becomes true

**In JKChatComposer.js:**
1. Add ARIA attributes to textarea:
   ```jsx
   aria-label="Type your message"
   aria-describedby="char-count"
   aria-invalid={isOverLimit}
   ```
2. Add aria-live region for validation messages:
   ```jsx
   <div role="alert" aria-live="polite" aria-atomic="true">
     {validationMessage}
   </div>
   ```
3. Accept textareaRef prop for focus management:
   ```javascript
   const JKChatComposer = ({ textareaRef }) => {
     // ...
     <textarea ref={textareaRef} ... />
   }
   ```
4. Add id="char-count" to character counter div

**In JKChatHeader.js:**
1. Add id="chat-window-title" to channel name span
2. Close button already has aria-label="Close chat" ✓

**In JKSessionChatButton.js:**
1. Add aria-label: `aria-label="Open chat (${unreadCount} unread)"`
2. Add aria-pressed state: `aria-pressed={isWindowOpen}`

**Keyboard navigation testing:**
- Tab through all focusable elements (button, textarea, send button, close button)
- Enter in textarea sends message (already works)
- Escape closes window (new)
- Focus moves to textarea on open (new)</action>
  <verify>
- Use browser accessibility inspector to verify all ARIA attributes
- Tab through chat interface → proper focus order
- Press Escape with window open → window closes
- Open window → textarea receives focus automatically
- Screen reader announces validation messages (test with macOS VoiceOver if available)
  </verify>
  <done>Accessibility complete with ARIA attributes, keyboard shortcuts (Escape), focus management, and proper semantic roles</done>
</task>

<task type="auto">
  <name>Task 3: Polish and edge case handling</name>
  <files>jam-ui/src/components/client/chat/JKChatMessageList.js, jam-ui/src/components/client/JKSessionChatWindow.js, jam-ui/src/store/features/sessionChatSlice.js</files>
  <action>Polish edge cases and improve UX details:

**Edge case: Empty channel on first open**
- In JKChatMessageList.js: Already handles with JKChatEmptyState ✓
- Verify empty state shows before first fetchChatHistory completes
- Message: "No messages yet. Start the conversation!"

**Edge case: Very long messages**
- Already handled by 255 character limit in composer ✓
- Verify truncation doesn't break display

**Edge case: Rapid window open/close**
- In JKSessionChatWindow.js: Add debounce or gate to prevent rapid toggling
- Use isOperating pattern if needed
- Actually: WindowPortal handles this naturally, verify it works

**Edge case: Send while disconnected**
- Already handled: send button disabled when !isConnected ✓
- Verify validation message appears

**Edge case: Multiple unread messages arriving rapidly**
- Already handled: addMessageFromWebSocket reducer increments correctly ✓
- Verify badge updates for rapid messages (5+ in quick succession)

**Polish: Loading state for fetchChatHistory**
- Show spinner at top of message list (added in Task 1)
- Verify it doesn't interfere with auto-scroll

**Polish: Timestamp formatting edge cases**
- Already handled by formatTimestamp utility (Phase 8) ✓
- Verify "Just now", "5 minutes ago", "Yesterday" all work
- Test with messages from different dates

**Polish: Window position persistence**
- Already in Redux state (windowPosition) ✓
- Verify position persists during session
- localStorage persistence not needed (acceptable to reset on reload)

**Redux cleanup:**
- Add clearSendError action to sessionChatSlice if not present:
  ```javascript
  clearSendError: (state) => {
    state.sendError = null;
    state.sendStatus = 'idle';
  }
  ```
- This enables retry functionality from Task 1

**Testing checklist:**
1. Open/close window 10 times rapidly → no errors
2. Send 5 messages while disconnected → all queued/failed gracefully
3. Receive 10 messages rapidly → badge updates correctly
4. Drag window position → persists during session
5. Messages with various timestamps → all formatted correctly</action>
  <verify>
- All edge cases tested and handle gracefully
- No console errors during stress testing (rapid actions)
- Redux state remains consistent after edge case scenarios
- clearSendError action exists and works with retry button
  </verify>
  <done>Edge cases handled, polish complete, clearSendError action added, all stress tests passing</done>
</task>

</tasks>

<verification>
Before declaring plan complete:
- [ ] Error handling: API errors, WebSocket disconnection, fetch failures all display user-friendly messages
- [ ] Accessibility: All ARIA attributes present, keyboard navigation works (Tab, Enter, Escape)
- [ ] Focus management: Textarea auto-focused on window open
- [ ] Edge cases: Rapid actions, disconnection, empty states all handled
- [ ] No console errors during testing
- [ ] No ESLint warnings introduced
</verification>

<success_criteria>

- All tasks completed
- All verification checks pass
- Error handling comprehensive (API, WebSocket, validation)
- Accessibility meets ARIA standards (roles, labels, keyboard navigation)
- Focus management works (auto-focus textarea, Escape closes)
- Edge cases handled gracefully
- No regressions in existing functionality
  </success_criteria>

<output>
After completion, create `.planning/phases/11-chat-finalization/11-01-SUMMARY.md`:

# Phase 11 Plan 1: Error Handling, Accessibility & Polish Summary

**[Substantive one-liner - what shipped]**

## Accomplishments

- Error handling for API failures, WebSocket disconnection, and fetch failures
- Comprehensive accessibility attributes (ARIA roles, labels, descriptions)
- Keyboard navigation (Escape to close, Tab order, auto-focus)
- Edge case handling (rapid actions, disconnection, empty states)
- clearSendError action for retry functionality

## Files Created/Modified

- `jam-ui/src/components/client/chat/JKChatComposer.js` - Error display, retry button, ARIA attributes, focus ref
- `jam-ui/src/components/client/JKSessionChatWindow.js` - Disconnection banner, keyboard shortcuts, focus management
- `jam-ui/src/components/client/chat/JKChatMessageList.js` - Loading spinner, fetch error display
- `jam-ui/src/components/client/chat/JKChatHeader.js` - Accessibility IDs
- `jam-ui/src/components/client/JKSessionChatButton.js` - ARIA attributes
- `jam-ui/src/store/features/sessionChatSlice.js` - clearSendError action

## Decisions Made

[Key decisions and rationale, or "None"]

## Issues Encountered

[Problems and resolutions, or "None"]

## Next Phase Readiness

Ready for Phase 11 Plan 2 (UAT & Final Integration Testing).
</output>
