Epic 5 compleet: Chat orchestration voor Swift Agenda Planning. Alle agenda intents worden nu correct gerouteerd naar AgendaBlock met user-friendly error handling en fallback opties. E5.S1 - Action Routing (2 SP) - routeIntentToArtifact() functie in action-parser - Maps agenda intents naar juiste AgendaBlock mode - Confidence threshold enforcement (< 0.7 → fallback) - Required entity validation (patient voor create, identifier voor reschedule) - Command-input gebruikt nieuwe routing ipv legacy openBlock - Migratie naar modern artifact systeem (openArtifact) E5.S2 - Chat Prompt Update (2 SP) - 4 agenda intents toegevoegd aan Swift chat system prompt - agenda_query: afspraken opvragen op datumrange - create_appointment: nieuwe afspraak maken (required: patient, datetime) - cancel_appointment: afspraak annuleren (disambiguation support) - reschedule_appointment: afspraak verzetten (required: identifier) - Entity extraction rules gedocumenteerd (dateRange, datetime, identifier) - 4 complete voorbeelden met JSON action format - Clarification questions voor incomplete data - Prompt size: ~325 → ~525 regels (+60%) E5.S3 - Error States (2 SP) - AgendaErrorState component voor full-page errors - AgendaErrorAlert component voor inline form errors - getUserFriendlyMessage() vertaalt technical → user-friendly Dutch - Auto-redirect bij auth errors (401 → /login) - Fallback link naar /epd/agenda in alle error states - Context-aware messaging (query/create/cancel/reschedule) - Retry functionaliteit voor recoverable errors - Dev-only technical details collapsible Error message mapping: - 401 → "Je sessie is verlopen. Log opnieuw in." + auto-redirect - 403 → "Je hebt geen toegang tot deze afspraak." - 404 → "De gevraagde afspraak kon niet worden gevonden." - 500 → "Er ging iets mis op de server. Probeer het opnieuw." - Network → "Geen internetverbinding. Controleer je netwerkverbinding." - Timeout → "De aanvraag duurde te lang. Probeer het opnieuw." Components updated: - command-input: gebruikt routeIntentToArtifact + openArtifact - agenda-create-form: gebruikt AgendaErrorAlert met fallback link - chat/route: uitgebreide system prompt met agenda sectie Nieuwe files: - lib/swift/action-parser.ts: routeIntentToArtifact() functie - components/swift/artifacts/blocks/agenda-error-state.tsx (225 regels) - docs/swift/implementation-e5-s1-action-routing.md - docs/swift/implementation-e5-s2-chat-prompt.md - docs/swift/implementation-e5-s3-error-states.md Documentatie: - Bouwplan bijgewerkt: Epic 5 → Done - 3 implementation docs met API specs en testing scenarios - Error handling best practices gedocumenteerd Progress: 48 SP / 51 SP (94%) - Epic 6 (QA) remaining 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
400 lines
11 KiB
Markdown
400 lines
11 KiB
Markdown
# Epic 5.S3 Implementation: Error States
|
||
|
||
**Story**: E5.S3 - Error states
|
||
**Date**: 2025-12-27
|
||
**Status**: ✅ Complete
|
||
**Story Points**: 2 SP
|
||
|
||
---
|
||
|
||
## 🎯 Objective
|
||
|
||
Add user-friendly error states to agenda functionality with fallback links to `/epd/agenda` and proper error messaging in Dutch.
|
||
|
||
---
|
||
|
||
## 📝 Implementation Summary
|
||
|
||
### New Component Created
|
||
|
||
**`components/swift/artifacts/blocks/agenda-error-state.tsx`**
|
||
|
||
Created two reusable error components for consistent error handling across all agenda views:
|
||
|
||
1. **AgendaErrorState** - Full-page error state with retry button
|
||
2. **AgendaErrorAlert** - Inline error alert for forms
|
||
|
||
### Updated Components
|
||
|
||
**`components/swift/artifacts/blocks/agenda-create-form.tsx`**
|
||
- Replaced basic error display with `AgendaErrorAlert`
|
||
- Added fallback link to full agenda
|
||
- Added dismiss functionality
|
||
|
||
---
|
||
|
||
## 🔑 Key Features
|
||
|
||
### 1. User-Friendly Error Messages
|
||
|
||
The `getUserFriendlyMessage()` function maps technical errors to Dutch user-facing messages:
|
||
|
||
| Error Type | Technical | User Message |
|
||
|------------|-----------|--------------|
|
||
| **Auth (401)** | "401 Unauthorized" | "Je sessie is verlopen. Log opnieuw in." |
|
||
| **Not Found (404)** | "404 Not Found" | "De gevraagde afspraak kon niet worden gevonden." |
|
||
| **Forbidden (403)** | "403 Forbidden" | "Je hebt geen toegang tot deze afspraak." |
|
||
| **Server (500)** | "500 Internal Server Error" | "Er ging iets mis op de server. Probeer het opnieuw." |
|
||
| **Network** | "Failed to fetch" | "Geen internetverbinding. Controleer je netwerkverbinding." |
|
||
| **Timeout** | "Request timeout" | "De aanvraag duurde te lang. Probeer het opnieuw." |
|
||
|
||
### 2. Context-Aware Messages
|
||
|
||
Different default messages based on operation context:
|
||
|
||
```typescript
|
||
context: 'query' → "Er ging iets mis bij het ophalen van je afspraken."
|
||
context: 'create' → "Er ging iets mis bij het aanmaken van de afspraak."
|
||
context: 'cancel' → "Er ging iets mis bij het annuleren van de afspraak."
|
||
context: 'reschedule' → "Er ging iets mis bij het verzetten van de afspraak."
|
||
```
|
||
|
||
### 3. Fallback Link to Full Agenda
|
||
|
||
All error states include a prominent link to `/epd/agenda`:
|
||
|
||
```tsx
|
||
<Button onClick={() => window.location.href = '/epd/agenda'}>
|
||
<ExternalLink className="h-4 w-4" />
|
||
Open volledige agenda
|
||
</Button>
|
||
```
|
||
|
||
### 4. Automatic Auth Redirect
|
||
|
||
Auth errors (401) automatically redirect to `/login`:
|
||
|
||
```typescript
|
||
if (isAuthError) {
|
||
window.location.href = '/login';
|
||
return null;
|
||
}
|
||
```
|
||
|
||
### 5. Retry Functionality
|
||
|
||
Optional retry button for recoverable errors:
|
||
|
||
```tsx
|
||
<AgendaErrorState
|
||
error={error}
|
||
onRetry={() => fetchAppointments()}
|
||
showFallbackLink={true}
|
||
context="query"
|
||
/>
|
||
```
|
||
|
||
### 6. Dev-Only Technical Details
|
||
|
||
In development mode, shows collapsible technical details:
|
||
|
||
```tsx
|
||
{process.env.NODE_ENV === 'development' && (
|
||
<details>
|
||
<summary>Technische details (dev only)</summary>
|
||
<pre>{error.stack}</pre>
|
||
</details>
|
||
)}
|
||
```
|
||
|
||
---
|
||
|
||
## 📐 Component API
|
||
|
||
### AgendaErrorState (Full-Page Error)
|
||
|
||
```typescript
|
||
interface AgendaErrorStateProps {
|
||
error: string | Error; // Error to display
|
||
onRetry?: () => void; // Optional retry function
|
||
showFallbackLink?: boolean; // Show link to /epd/agenda (default: true)
|
||
context?: 'query' | 'create' | 'cancel' | 'reschedule';
|
||
}
|
||
```
|
||
|
||
**Usage Example**:
|
||
```tsx
|
||
<AgendaErrorState
|
||
error="Failed to fetch appointments"
|
||
onRetry={() => refetch()}
|
||
context="query"
|
||
/>
|
||
```
|
||
|
||
### AgendaErrorAlert (Inline Alert)
|
||
|
||
```typescript
|
||
interface AgendaErrorAlertProps {
|
||
error: string | Error; // Error to display
|
||
onDismiss?: () => void; // Optional dismiss function
|
||
showFallbackLink?: boolean; // Show link to /epd/agenda (default: false)
|
||
}
|
||
```
|
||
|
||
**Usage Example**:
|
||
```tsx
|
||
{error && (
|
||
<AgendaErrorAlert
|
||
error={error}
|
||
onDismiss={() => setError(null)}
|
||
showFallbackLink={true}
|
||
/>
|
||
)}
|
||
```
|
||
|
||
---
|
||
|
||
## ✅ Acceptance Criteria
|
||
|
||
| Criterion | Status | Implementation |
|
||
|-----------|--------|----------------|
|
||
| User-friendly error messages | ✅ | `getUserFriendlyMessage()` function |
|
||
| Dutch language errors | ✅ | All messages in Dutch |
|
||
| Fallback link to /epd/agenda | ✅ | "Open volledige agenda" button |
|
||
| Auth error redirect | ✅ | Automatic redirect to /login |
|
||
| Retry functionality | ✅ | Optional `onRetry` prop |
|
||
| Context-aware messages | ✅ | Different messages per operation type |
|
||
| Network error handling | ✅ | "Geen internetverbinding" message |
|
||
| Server error handling | ✅ | "Er ging iets mis op de server" message |
|
||
| TypeScript type safety | ✅ | Full type definitions |
|
||
| Consistent styling | ✅ | Matches existing UI patterns |
|
||
|
||
---
|
||
|
||
## 🎨 UI Design
|
||
|
||
### Full-Page Error State
|
||
|
||
```
|
||
┌─────────────────────────────────────┐
|
||
│ │
|
||
│ 🔴 (AlertCircle Icon) │
|
||
│ │
|
||
│ Er ging iets mis │
|
||
│ │
|
||
│ Er ging iets mis bij het │
|
||
│ ophalen van je afspraken. │
|
||
│ │
|
||
│ ┌──────────────┐ ┌──────────────┐ │
|
||
│ │ 🔄 Probeer │ │ 🔗 Open │ │
|
||
│ │ opnieuw │ │ volledige │ │
|
||
│ └──────────────┘ │ agenda │ │
|
||
│ └──────────────┘ │
|
||
│ │
|
||
└─────────────────────────────────────┘
|
||
```
|
||
|
||
### Inline Error Alert
|
||
|
||
```
|
||
┌─────────────────────────────────────┐
|
||
│ ⚠️ Er ging iets mis bij het │
|
||
│ aanmaken van de afspraak. │
|
||
│ Open volledige agenda → [×] │
|
||
└─────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## 🧪 Testing Scenarios
|
||
|
||
### Scenario 1: Network Error
|
||
**Trigger**: Disconnect internet, try to create appointment
|
||
**Expected**:
|
||
- Message: "Geen internetverbinding. Controleer je netwerkverbinding."
|
||
- Retry button enabled
|
||
- Fallback link visible
|
||
|
||
### Scenario 2: Auth Error (401)
|
||
**Trigger**: Expired session token
|
||
**Expected**:
|
||
- Automatic redirect to `/login`
|
||
- No error component shown
|
||
|
||
### Scenario 3: Server Error (500)
|
||
**Trigger**: Backend returns 500
|
||
**Expected**:
|
||
- Message: "Er ging iets mis op de server. Probeer het opnieuw."
|
||
- Retry button enabled
|
||
- Fallback link visible
|
||
|
||
### Scenario 4: Not Found (404)
|
||
**Trigger**: Try to cancel non-existent appointment
|
||
**Expected**:
|
||
- Message: "De gevraagde afspraak kon niet worden gevonden."
|
||
- Fallback link visible
|
||
|
||
### Scenario 5: Validation Error
|
||
**Trigger**: Submit form with invalid data
|
||
**Expected**:
|
||
- Inline alert with specific validation message
|
||
- Fallback link visible
|
||
- Dismiss button works
|
||
|
||
### Scenario 6: Retry Success
|
||
**Trigger**: Network error → reconnect → click retry
|
||
**Expected**:
|
||
- Retry function called
|
||
- Error cleared on success
|
||
- Content loads normally
|
||
|
||
---
|
||
|
||
## 💡 Design Decisions
|
||
|
||
### Why Two Components?
|
||
|
||
1. **AgendaErrorState**: For full-page failures (query, list loading)
|
||
2. **AgendaErrorAlert**: For form-level errors (create, cancel, reschedule)
|
||
|
||
Different UI patterns for different contexts.
|
||
|
||
### Why Auto-Redirect for Auth Errors?
|
||
|
||
Auth errors (401) are not user-recoverable in the UI. User must log in again, so immediate redirect provides better UX than showing an error message.
|
||
|
||
### Why Show Fallback Link?
|
||
|
||
If Swift fails, users can always fall back to the classic agenda UI at `/epd/agenda`. This provides a safety net and reduces frustration.
|
||
|
||
### Why Context Parameter?
|
||
|
||
Different operations have different error messages. Context makes messages more specific and actionable:
|
||
- Query failure → "bij het ophalen"
|
||
- Create failure → "bij het aanmaken"
|
||
- etc.
|
||
|
||
### Why Dev-Only Technical Details?
|
||
|
||
Technical stack traces are only useful for developers debugging issues. Production users should see user-friendly messages only.
|
||
|
||
---
|
||
|
||
## 🚀 Error Handling Best Practices
|
||
|
||
### 1. Always Use getUserFriendlyMessage()
|
||
|
||
```typescript
|
||
// ❌ Bad - Technical error exposed to user
|
||
setError(error.message);
|
||
|
||
// ✅ Good - User-friendly Dutch message
|
||
const friendlyMessage = getUserFriendlyMessage(error, 'create');
|
||
setError(friendlyMessage);
|
||
```
|
||
|
||
### 2. Provide Context
|
||
|
||
```typescript
|
||
// ❌ Bad - Generic error
|
||
<AgendaErrorState error={error} />
|
||
|
||
// ✅ Good - Context-specific error
|
||
<AgendaErrorState error={error} context="query" />
|
||
```
|
||
|
||
### 3. Offer Retry When Possible
|
||
|
||
```typescript
|
||
// ❌ Bad - No recovery path
|
||
<AgendaErrorState error={error} />
|
||
|
||
// ✅ Good - User can retry
|
||
<AgendaErrorState
|
||
error={error}
|
||
onRetry={() => refetchAppointments()}
|
||
/>
|
||
```
|
||
|
||
### 4. Use Inline Alerts for Forms
|
||
|
||
```typescript
|
||
// ❌ Bad - Full-page error for form validation
|
||
<AgendaErrorState error="Patient required" />
|
||
|
||
// ✅ Good - Inline alert in form
|
||
<AgendaErrorAlert
|
||
error="Selecteer a.u.b. een patiënt."
|
||
onDismiss={() => setError(null)}
|
||
/>
|
||
```
|
||
|
||
---
|
||
|
||
## 📊 Error Message Coverage
|
||
|
||
Covered error types:
|
||
- ✅ Authentication (401)
|
||
- ✅ Authorization (403)
|
||
- ✅ Not Found (404)
|
||
- ✅ Server Error (500)
|
||
- ✅ Network/Offline
|
||
- ✅ Timeout
|
||
- ✅ Validation
|
||
- ✅ Generic/Unknown
|
||
|
||
All with Dutch user-friendly messages.
|
||
|
||
---
|
||
|
||
## 📁 Files Modified/Created
|
||
|
||
```
|
||
✅ components/swift/artifacts/blocks/agenda-error-state.tsx (NEW - 225 lines)
|
||
├── AgendaErrorState component
|
||
├── AgendaErrorAlert component
|
||
└── getUserFriendlyMessage() utility
|
||
|
||
✅ components/swift/artifacts/blocks/agenda-create-form.tsx (UPDATED)
|
||
└── Replaced basic error with AgendaErrorAlert
|
||
|
||
✅ docs/swift/bouwplan-swift-agenda-planning.md (UPDATED)
|
||
└── E5.S3 → Done, Epic 5 → Done
|
||
|
||
✅ docs/swift/implementation-e5-s3-error-states.md (NEW)
|
||
└── This documentation
|
||
```
|
||
|
||
---
|
||
|
||
## 🎯 Impact
|
||
|
||
### Before E5.S3
|
||
- ❌ Technical error messages exposed to users
|
||
- ❌ No fallback when errors occur
|
||
- ❌ No retry functionality
|
||
- ❌ Inconsistent error handling
|
||
|
||
### After E5.S3
|
||
- ✅ User-friendly Dutch error messages
|
||
- ✅ Always provide fallback link to full agenda
|
||
- ✅ Retry button for recoverable errors
|
||
- ✅ Consistent error handling across all views
|
||
- ✅ Auto-redirect for auth errors
|
||
- ✅ Context-aware messaging
|
||
|
||
---
|
||
|
||
## 🚀 Next Steps
|
||
|
||
**Epic 6 - QA & Docs**
|
||
- E6.S1: Manual test checklist (20 scenarios from build plan)
|
||
- E6.S2: Docs update (bouwplan + release note)
|
||
- E6.S3: Regression checks (Swift + klassieke agenda)
|
||
|
||
---
|
||
|
||
**Implementation Status**: ✅ Complete
|
||
**Epic 5 Status**: ✅ Complete (All 3 stories done)
|
||
**Ready for**: Epic 6 (QA & Documentation)
|