jedmund-svelte/PRD-enhanced-tag-system.md

610 lines
No EOL
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Product Requirements Document: Enhanced Tag System
## Overview
Upgrade the current JSON-based tag system to a relational database model with advanced tagging features including tag filtering, related posts, tag management, and an improved tag input UI with typeahead functionality.
## Goals
- Enable efficient querying and filtering of posts by tags
- Provide tag management capabilities for content curation
- Show related posts based on shared tags
- Implement intuitive tag input with typeahead and keyboard shortcuts
- Build analytics and insights around tag usage
- Maintain backward compatibility during migration
## Technical Constraints
- **Framework**: SvelteKit with Svelte 5 runes mode
- **Database**: PostgreSQL with Prisma ORM
- **Hosting**: Railway (existing infrastructure)
- **Design System**: Use existing admin component library
- **Performance**: Tag operations should be sub-100ms
## Current State vs Target State
### Current Implementation
- Tags stored as JSON arrays: `tags: ['announcement', 'meta', 'cms']`
- Simple display-only functionality
- No querying capabilities
- Manual tag input with Add button
### Target Implementation
- Relational many-to-many tag system
- Full CRUD operations for tags
- Advanced filtering and search
- Typeahead tag input with keyboard navigation
- Tag analytics and management interface
## Database Schema Changes
### New Tables
```sql
-- Tags table
CREATE TABLE tags (
id SERIAL PRIMARY KEY,
name VARCHAR(100) UNIQUE NOT NULL,
slug VARCHAR(100) UNIQUE NOT NULL,
description TEXT,
color VARCHAR(7), -- Hex color for tag styling
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Post-Tag junction table
CREATE TABLE post_tags (
id SERIAL PRIMARY KEY,
post_id INTEGER REFERENCES posts(id) ON DELETE CASCADE,
tag_id INTEGER REFERENCES tags(id) ON DELETE CASCADE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(post_id, tag_id)
);
-- Tag usage analytics (optional)
CREATE TABLE tag_analytics (
id SERIAL PRIMARY KEY,
tag_id INTEGER REFERENCES tags(id) ON DELETE CASCADE,
usage_count INTEGER DEFAULT 1,
last_used TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
```
### Prisma Schema Updates
```prisma
model Tag {
id Int @id @default(autoincrement())
name String @unique @db.VarChar(100)
slug String @unique @db.VarChar(100)
description String? @db.Text
color String? @db.VarChar(7) // Hex color
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
posts PostTag[]
@@index([name])
@@index([slug])
}
model PostTag {
id Int @id @default(autoincrement())
postId Int
tagId Int
createdAt DateTime @default(now())
// Relations
post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
tag Tag @relation(fields: [tagId], references: [id], onDelete: Cascade)
@@unique([postId, tagId])
@@index([postId])
@@index([tagId])
}
// Update existing Post model
model Post {
// ... existing fields
tags PostTag[] // Replace: tags Json?
}
```
## Core Features
### 1. Tag Management Interface
#### Admin Tag Manager (`/admin/tags`)
- **Tag List View**
- DataTable with tag name, usage count, created date
- Search and filter capabilities
- Bulk operations (delete, merge, rename)
- Color coding and visual indicators
- **Tag Detail/Edit View**
- Edit tag name, description, color
- View all posts using this tag
- Usage analytics and trends
- Merge with other tags functionality
#### Tag Analytics Dashboard
- **Usage Statistics**
- Most/least used tags
- Tag usage trends over time
- Orphaned tags (no posts)
- Tag co-occurrence patterns
- **Tag Insights**
- Suggested tag consolidations
- Similar tags detection
- Tag performance metrics
### 2. Enhanced Tag Input Component (`TagInput.svelte`)
#### Features
- **Typeahead Search**: Real-time search of existing tags
- **Keyboard Navigation**: Arrow keys to navigate suggestions
- **Instant Add**: Press Enter to add tag without button click
- **Visual Feedback**: Highlight matching text in suggestions
- **Tag Validation**: Prevent duplicates and invalid characters
- **Quick Actions**: Backspace to remove last tag
#### Component API
```typescript
interface TagInputProps {
tags: string[] | Tag[] // Current tags
suggestions?: Tag[] // Available tags for typeahead
placeholder?: string // Input placeholder text
maxTags?: number // Maximum number of tags
allowNew?: boolean // Allow creating new tags
size?: 'small' | 'medium' | 'large'
disabled?: boolean
onTagAdd?: (tag: Tag) => void
onTagRemove?: (tag: Tag) => void
onTagCreate?: (name: string) => void
}
```
#### Svelte 5 Implementation
```svelte
<script lang="ts">
let {
tags = $bindable([]),
suggestions = [],
placeholder = "Add tags...",
maxTags = 10,
allowNew = true,
size = 'medium',
disabled = false,
onTagAdd,
onTagRemove,
onTagCreate
}: TagInputProps = $props()
let inputValue = $state('')
let showSuggestions = $state(false)
let selectedIndex = $state(-1)
let inputElement: HTMLInputElement
// Filtered suggestions based on input
let filteredSuggestions = $derived(
suggestions.filter(tag =>
tag.name.toLowerCase().includes(inputValue.toLowerCase()) &&
!tags.some(t => t.id === tag.id)
)
)
// Handle keyboard navigation
function handleKeydown(e: KeyboardEvent) {
switch (e.key) {
case 'Enter':
e.preventDefault()
if (selectedIndex >= 0) {
addExistingTag(filteredSuggestions[selectedIndex])
} else if (inputValue.trim() && allowNew) {
createNewTag(inputValue.trim())
}
break
case 'ArrowDown':
e.preventDefault()
selectedIndex = Math.min(selectedIndex + 1, filteredSuggestions.length - 1)
break
case 'ArrowUp':
e.preventDefault()
selectedIndex = Math.max(selectedIndex - 1, -1)
break
case 'Backspace':
if (!inputValue && tags.length > 0) {
removeTag(tags[tags.length - 1])
}
break
case 'Escape':
showSuggestions = false
selectedIndex = -1
break
}
}
</script>
```
### 3. Post Filtering by Tags
#### Frontend Components
- **Tag Filter Bar**: Multi-select tag filtering
- **Tag Cloud**: Visual tag representation with usage counts
- **Search Integration**: Combine text search with tag filters
#### API Endpoints
```typescript
// GET /api/posts?tags=javascript,react&operation=AND
// GET /api/posts?tags=design,ux&operation=OR
interface PostsQueryParams {
tags?: string[] // Tag names or IDs
operation?: 'AND' | 'OR' // How to combine multiple tags
page?: number
limit?: number
status?: 'published' | 'draft'
}
// GET /api/tags/suggest?q=java
interface TagSuggestResponse {
tags: Array<{
id: number
name: string
slug: string
usageCount: number
}>
}
```
### 4. Related Posts Feature
#### Implementation
- **Algorithm**: Find posts sharing the most tags
- **Weighting**: Consider tag importance and recency
- **Exclusions**: Don't show current post in related list
- **Limit**: Show 3-6 related posts maximum
#### Component (`RelatedPosts.svelte`)
```svelte
<script lang="ts">
let { postId, tags, limit = 4 }: {
postId: number
tags: Tag[]
limit?: number
} = $props()
let relatedPosts = $state<Post[]>([])
$effect(async () => {
const tagIds = tags.map(t => t.id)
const response = await fetch(`/api/posts/related?postId=${postId}&tagIds=${tagIds.join(',')}&limit=${limit}`)
relatedPosts = await response.json()
})
</script>
```
## API Specification
### Tag Management APIs
```typescript
// GET /api/tags - List all tags
interface TagsResponse {
tags: Tag[]
total: number
page: number
limit: number
}
// POST /api/tags - Create new tag
interface CreateTagRequest {
name: string
description?: string
color?: string
}
// PUT /api/tags/[id] - Update tag
interface UpdateTagRequest {
name?: string
description?: string
color?: string
}
// DELETE /api/tags/[id] - Delete tag
// Returns: 204 No Content
// POST /api/tags/merge - Merge tags
interface MergeTagsRequest {
sourceTagIds: number[]
targetTagId: number
}
// GET /api/tags/[id]/posts - Get posts for tag
interface TagPostsResponse {
posts: Post[]
tag: Tag
total: number
}
// GET /api/tags/analytics - Tag usage analytics
interface TagAnalyticsResponse {
mostUsed: Array<{ tag: Tag; count: number }>
leastUsed: Array<{ tag: Tag; count: number }>
trending: Array<{ tag: Tag; growth: number }>
orphaned: Tag[]
}
```
### Enhanced Post APIs
```typescript
// GET /api/posts/related?postId=123&tagIds=1,2,3&limit=4
interface RelatedPostsResponse {
posts: Array<{
id: number
title: string
slug: string
excerpt?: string
publishedAt: string
tags: Tag[]
sharedTagsCount: number // Number of tags in common
}>
}
// PUT /api/posts/[id]/tags - Update post tags
interface UpdatePostTagsRequest {
tagIds: number[]
}
```
## User Interface Components
### 1. TagInput Component Features
#### Visual States
- **Default**: Clean input with placeholder
- **Focused**: Show suggestions dropdown
- **Typing**: Filter and highlight matches
- **Selected**: Navigate with keyboard
- **Adding**: Smooth animation for new tags
- **Full**: Disable input when max tags reached
#### Accessibility
- **ARIA Labels**: Proper labeling for screen readers
- **Keyboard Navigation**: Full keyboard accessibility
- **Focus Management**: Logical tab order
- **Announcements**: Screen reader feedback for actions
### 2. Tag Display Components
#### TagPill Component
```svelte
<script lang="ts">
let {
tag,
size = 'medium',
removable = false,
clickable = false,
showCount = false,
onRemove,
onClick
}: TagPillProps = $props()
</script>
<span
class="tag-pill tag-pill-{size}"
style="--tag-color: {tag.color}"
class:clickable
class:removable
onclick={onClick}
>
{tag.name}
{#if showCount}
<span class="tag-count">({tag.usageCount})</span>
{/if}
{#if removable}
<button onclick={onRemove} class="tag-remove">×</button>
{/if}
</span>
```
#### TagCloud Component
```svelte
<script lang="ts">
let {
tags,
maxTags = 50,
minFontSize = 12,
maxFontSize = 24,
onClick
}: TagCloudProps = $props()
// Calculate font sizes based on usage
let tagSizes = $derived(calculateTagSizes(tags, minFontSize, maxFontSize))
</script>
```
### 3. Admin Interface Updates
#### Posts List with Tag Filtering
- **Filter Bar**: Multi-select tag filter above posts list
- **Tag Pills**: Show tags on each post item
- **Quick Filter**: Click tag to filter by that tag
- **Clear Filters**: Easy way to reset all filters
#### Posts Edit Form Integration
- **Replace Current**: Swap existing tag input with new TagInput
- **Preserve UX**: Maintain current metadata popover
- **Tag Management**: Quick access to create/edit tags
## Migration Strategy
### Phase 1: Database Migration (Week 1)
1. **Create Migration Script**
- Create new tables (tags, post_tags)
- Migrate existing JSON tags to relational format
- Create indexes for performance
2. **Data Migration**
- Extract unique tags from existing posts
- Create tag records with auto-generated slugs
- Create post_tag relationships
- Validate data integrity
3. **Backward Compatibility**
- Keep original tags JSON field temporarily
- Dual-write to both systems during transition
### Phase 2: API Development (Week 1-2)
1. **Tag Management APIs**
- CRUD operations for tags
- Tag suggestions and search
- Analytics endpoints
2. **Enhanced Post APIs**
- Update post endpoints for relational tags
- Related posts algorithm
- Tag filtering capabilities
3. **Testing & Validation**
- Unit tests for all endpoints
- Performance testing for queries
- Data consistency checks
### Phase 3: Frontend Components (Week 2-3)
1. **Core Components**
- TagInput with typeahead
- TagPill and TagCloud
- Tag management interface
2. **Integration**
- Update MetadataPopover
- Add tag filtering to posts list
- Implement related posts component
3. **Admin Interface**
- Tag management dashboard
- Analytics views
- Bulk operations interface
### Phase 4: Features & Polish (Week 3-4)
1. **Advanced Features**
- Tag merging functionality
- Usage analytics
- Tag suggestions based on content
2. **Performance Optimization**
- Query optimization
- Caching strategies
- Load testing
3. **Cleanup**
- Remove JSON tags field
- Documentation updates
- Final testing
## Success Metrics
### Performance
- Tag search responses under 50ms
- Post filtering responses under 100ms
- Page load times maintained or improved
### Usability
- Reduced clicks to add tags (eliminate Add button)
- Faster tag input with typeahead
- Improved content discovery through related posts
### Content Management
- Ability to merge duplicate tags
- Insights into tag usage patterns
- Better content organization capabilities
### Analytics
- Track tag usage growth over time
- Identify content gaps through tag analysis
- Measure impact on content engagement
## Technical Considerations
### Performance
- **Database Indexes**: Proper indexing on tag names and relationships
- **Query Optimization**: Efficient joins for tag filtering
- **Caching**: Cache popular tag lists and related posts
- **Pagination**: Handle large tag lists efficiently
### Data Integrity
- **Constraints**: Prevent duplicate tag names
- **Cascading Deletes**: Properly handle tag/post deletions
- **Validation**: Ensure tag names follow naming conventions
- **Backup Strategy**: Safe migration with rollback capability
### User Experience
- **Progressive Enhancement**: Graceful degradation if JS fails
- **Loading States**: Smooth loading indicators
- **Error Handling**: Clear error messages for users
- **Responsive Design**: Works well on all device sizes
## Future Enhancements
### Advanced Features (Post-MVP)
- **Hierarchical Tags**: Parent/child tag relationships
- **Tag Synonyms**: Alternative names for the same concept
- **Auto-tagging**: ML-based tag suggestions from content
- **Tag Templates**: Predefined tag sets for different content types
### Integrations
- **External APIs**: Import tags from external sources
- **Search Integration**: Enhanced search with tag faceting
- **Analytics**: Deep tag performance analytics
- **Content Recommendations**: AI-powered related content
## Risk Assessment
### High Risk
- **Data Migration**: Complex migration of existing tag data
- **Performance Impact**: New queries might affect page load times
- **User Adoption**: Users need to learn new tag input interface
### Mitigation Strategies
- **Staged Rollout**: Deploy to staging first, then gradual production rollout
- **Performance Monitoring**: Continuous monitoring during migration
- **User Training**: Clear documentation and smooth UX transitions
- **Rollback Plan**: Ability to revert to JSON tags if needed
## Success Criteria
### Must Have
- ✅ All existing tags migrated successfully
- ✅ Tag input works with keyboard-only navigation
- ✅ Posts can be filtered by single or multiple tags
- ✅ Related posts show based on shared tags
- ✅ Performance remains acceptable (< 100ms for most operations)
### Should Have
- Tag management interface for admins
- Tag usage analytics and insights
- Ability to merge duplicate tags
- Tag color coding and visual improvements
### Could Have
- Tag auto-suggestions based on post content
- Tag trending and popularity metrics
- Advanced tag analytics and reporting
- Integration with external tag sources
## Timeline
**Total Duration**: 4 weeks
- **Week 1**: Database migration and API development
- **Week 2**: Core frontend components and basic integration
- **Week 3**: Advanced features and admin interface
- **Week 4**: Polish, testing, and production deployment
## Conclusion
This enhanced tag system will significantly improve content organization, discoverability, and management capabilities while providing a modern, intuitive user interface built with Svelte 5 runes. The migration strategy ensures minimal disruption while delivering substantial improvements in functionality and user experience.