```
### Featured Quote Display
```tsx
{storyteller.featured_quote && (
"{storyteller.featured_quote}"
)}
```
## AI Enrichment API Patterns
### Trigger Enrichment
```typescript
// POST /api/storytellers/{id}/enrich
async function enrichStoryteller(storytellerId: string) {
const response = await fetch(`/api/storytellers/${storytellerId}/enrich`, {
method: 'POST',
body: JSON.stringify({
enrich_bio: true,
extract_quotes: true,
analyze_themes: true,
suggest_connections: true
})
})
// Returns enrichment job ID for polling
return response.json()
}
```
### Display Enriched Data
```tsx
// Check for AI-enriched fields
const hasAIData = storyteller.ai_summary || storyteller.theme_expertise?.length
{hasAIData && (
AI Enhanced
)}
```
## Database Fields for AI Enrichment
### Profiles Table Additions
```sql
-- AI-generated summary
ALTER TABLE profiles ADD COLUMN ai_summary TEXT;
-- Extracted theme expertise (from stories analysis)
ALTER TABLE profiles ADD COLUMN theme_expertise TEXT[];
-- AI-generated featured quote selection
ALTER TABLE profiles ADD COLUMN featured_quote TEXT;
ALTER TABLE profiles ADD COLUMN featured_quote_story_id UUID;
-- Connection strength scores
ALTER TABLE profiles ADD COLUMN connection_scores JSONB DEFAULT '{}';
-- Enrichment status
ALTER TABLE profiles ADD COLUMN ai_enrichment_status TEXT DEFAULT 'pending';
ALTER TABLE profiles ADD COLUMN ai_enriched_at TIMESTAMPTZ;
```
### Story Suggestions View
```sql
CREATE VIEW storyteller_connections AS
SELECT
p1.id as storyteller_id,
p2.id as connected_storyteller_id,
array_length(
ARRAY(SELECT unnest(p1.theme_expertise) INTERSECT SELECT unnest(p2.theme_expertise)),
1
) as theme_overlap,
p1.cultural_background = p2.cultural_background as same_culture
FROM profiles p1
CROSS JOIN profiles p2
WHERE p1.id != p2.id
AND p1.is_storyteller = true
AND p2.is_storyteller = true;
```
## Component Checklist
### Before Creating a Card Component
- [ ] Identify all data fields needed
- [ ] Define which fields are required vs optional
- [ ] Plan fallback states for missing data
- [ ] Consider dark mode colors
- [ ] Add hover states
- [ ] Plan loading skeleton
- [ ] Add accessibility labels
### Cultural Sensitivity
- [ ] Respectful terminology used
- [ ] Elder status prominently displayed
- [ ] Cultural background shown with respect
- [ ] Traditional territory acknowledged
- [ ] No appropriation of cultural symbols
- [ ] Languages displayed respectfully
### AI Enrichment
- [ ] Mark AI-generated content
- [ ] Allow user override of AI suggestions
- [ ] Handle missing AI data gracefully
- [ ] Show enrichment in progress states
## Reference Components
| Component | Location | Purpose |
|-----------|----------|---------|
| `StorytellerCard` | `src/components/storyteller/storyteller-card.tsx` | Main card component |
| `UnifiedStorytellerCard` | `src/components/storyteller/unified-storyteller-card.tsx` | Flexible card variants |
| `ElegantStorytellerCard` | `src/components/storyteller/elegant-storyteller-card.tsx` | Premium design variant |
| `StoryCard` | `src/components/story/story-card.tsx` | Story display card |
| `QuoteCard` | `src/components/ui/quote-card.tsx` | Quote display |
| `ThemeBadge` | `src/components/ui/theme-badge.tsx` | Theme display |
## Syndication Dashboard Design Patterns (NEW - Sprint 4)
### Consent Status Badge
**Purpose:** Visual indicator of syndication consent status
**Variants:**
```typescript
type ConsentStatus = 'approved' | 'pending' | 'revoked' | 'expired'
const statusConfig = {
approved: { color: 'sage', icon: CheckCircle, label: 'Active' },
pending: { color: 'amber', icon: Clock, label: 'Pending' },
revoked: { color: 'ember', icon: XCircle, label: 'Revoked' },
expired: { color: 'muted', icon: AlertCircle, label: 'Expired' }
}
```
**Design:**
```tsx
{label}
```
---
### Consent Card Layout
**Purpose:** Display individual syndication consent with site info and controls
**Structure:**
```
┌─────────────────────────────────────────────┐
│ [Site Logo] JusticeHub │
│ justicehub.org.au │
│ │
│ Status: [Active Badge] │
│ Cultural Level: Public │
│ Created: Jan 5, 2026 │
│ │
│ 📊 456 views • Last accessed 2 hours ago │
│ │
│ [View Analytics] [Revoke Access] │
└─────────────────────────────────────────────┘
```
**Colors:**
- **Card Border:** Use site brand color (if provided)
- **Status Badge:** Follow status config above
- **Cultural Level:** Use cultural color (clay/sage/sky)
- **Actions:** Primary (sage) for analytics, destructive (ember) for revoke
---
### Embed Token Display
**Purpose:** Show token with security-conscious masking
**Pattern:**
```tsx
{tokenMasked ? 'LRK••••••••••XA' : token}
```
**Security:**
- Default: Token masked
- Click to reveal (temporary, 10 seconds)
- Copy button with confirmation toast
- Never log unmasked tokens
---
### Analytics Chart Styling
**Purpose:** Consistent chart design across dashboards
**Colors (Recharts):**
```typescript
const siteColors = {
justicehub: '#C85A54', // Ember
actfarm: '#6B8E72', // Sage
theharvest: '#D97757', // Clay
actplacemat: '#4A90A4' // Sky
}
// Usage
```
**Grid & Axes:**
```typescript
```
---
### Revocation Dialog
**Purpose:** Confirm consent revocation with cultural messaging
**Layout:**
```tsx
```
**Messaging Principles:**
- ✅ Affirm storyteller control ("You maintain full control")
- ✅ Explain consequences clearly ("immediately remove access")
- ✅ Reassure reversibility ("grant consent again")
- ❌ No guilt-tripping ("Are you sure?")
- ❌ No fear language ("This cannot be undone")
---
### Cultural Permission Level Indicator
**Purpose:** Show content sensitivity level with cultural context
**Levels:**
```typescript
const culturalLevels = {
public: {
color: 'sage',
icon: Globe,
label: 'Public',
description: 'Safe to share widely'
},
community: {
color: 'clay',
icon: Users,
label: 'Community',
description: 'Indigenous communities only'
},
restricted: {
color: 'amber',
icon: Lock,
label: 'Restricted',
description: 'Requires elder approval'
},
sacred: {
color: 'ember',
icon: ShieldAlert,
label: 'Sacred',
description: 'Not for external sharing'
}
}
```
**Component:**
```tsx
{label}
{description}
```
---
### Empty States for Syndication
**No Consents Yet:**
```tsx
No syndication consents yet
Your stories are safe with you. When you're ready to share with
external platforms like JusticeHub, you'll see them here.
```
**All Consents Revoked:**
```tsx
You're in control
All your stories have been removed from external platforms.
You can re-share whenever you're ready.
```
---
## When to Use This Skill
Invoke when:
- Designing new card components
- Adding fields to storyteller/story cards
- Implementing AI enrichment features
- Creating profile displays
- Building list views with storyteller data
- Adding cultural indicators to UI
- Implementing hover/expand states
- Creating loading skeletons
- **Designing syndication dashboard UI**
- **Building consent management interfaces**
- **Creating analytics visualizations**
- **Implementing revocation workflows**