Content Tagging UI Architecture¶
Overview¶
This document describes the modular UI architecture for the Content Tagging system, designed to support Phase 4 (Reporting & Analytics UI) implementation. The architecture follows existing patterns in the codebase using Blade components, Alpine.js, and Tailwind CSS.
Component Structure¶
resources/views/default/components/content-tags/
├── tag-badge.blade.php # Single tag display with color, category, confidence
├── tag-input.blade.php # Multi-select tag input with search & create
├── tag-filter.blade.php # Dropdown filter for content by tags
├── tag-list.blade.php # Display list of tags (flat or grouped)
├── tag-suggestion-card.blade.php # AI suggestion card with accept/reject
└── ai-confidence.blade.php # Confidence indicator (badge, bar, icon variants)
resources/views/default/js/components/
└── contentTags.js # Alpine.js store and manager module
Component Reference¶
1. Tag Badge (x-content-tags.tag-badge)¶
Displays a single tag with its color, optional category icon, and AI confidence indicator.
Props¶
| Prop | Type | Default | Description |
|---|---|---|---|
tag |
object|null | null | Tag object (takes precedence over individual props) |
name |
string | null | Tag name |
color |
string | '#6366f1' | Hex color code |
category |
string | 'custom' | Tag category (campaign, product, team, source, custom) |
removable |
bool | false | Show remove button |
showCategory |
bool | false | Show category icon |
showConfidence |
bool | false | Show AI confidence percentage |
confidence |
float|null | null | Confidence score (0.0-1.0) |
size |
string | 'sm' | Size variant (xs, sm, md, lg) |
clickable |
bool | false | Make badge clickable |
Usage¶
{{-- Basic tag badge --}}
<x-content-tags.tag-badge name="Q1 Campaign" color="#6366f1" />
{{-- From tag object --}}
<x-content-tags.tag-badge :tag="$tag" removable showCategory />
{{-- With AI confidence --}}
<x-content-tags.tag-badge
:tag="$suggestion->tag"
:showConfidence="true"
:confidence="$suggestion->confidence"
/>
Events¶
tag-remove- Dispatched when remove button clicked (includes tag data)
2. Tag Input (x-content-tags.tag-input)¶
Multi-select tag input with search, category tabs, and optional tag creation.
Props¶
| Prop | Type | Default | Description |
|---|---|---|---|
tags |
array | [] | Available tags to select from |
selectedTags |
array | [] | Currently selected tags |
placeholder |
string | 'Search or select tags...' | Input placeholder |
name |
string | 'tags' | Form input name |
multiple |
bool | true | Allow multiple selection |
allowCreate |
bool | false | Allow creating new tags |
showCategories |
bool | true | Show category filter tabs |
categories |
array | ['campaign', 'product', 'team', 'source', 'custom'] | Allowed categories |
maxTags |
int|null | null | Maximum selectable tags |
disabled |
bool | false | Disable input |
apiEndpoint |
string | '/api/content-tags' | API endpoint for fetching/creating |
size |
string | 'md' | Size variant (sm, md, lg) |
Usage¶
{{-- Basic tag input --}}
<x-content-tags.tag-input
:tags="$availableTags"
:selectedTags="$content->tags"
name="content_tags"
/>
{{-- With tag creation (Team Lead+) --}}
<x-content-tags.tag-input
:tags="$availableTags"
:allowCreate="$user->canCreateTags()"
:maxTags="10"
/>
{{-- Single selection mode --}}
<x-content-tags.tag-input
:tags="$campaignTags"
:multiple="false"
:categories="['campaign']"
name="primary_campaign"
/>
Events¶
tags-changed- Dispatched when selection changes (includes tags array)
3. Tag Filter (x-content-tags.tag-filter)¶
Dropdown filter for filtering content by tags and categories.
Props¶
| Prop | Type | Default | Description |
|---|---|---|---|
tags |
array | [] | Available tags |
selectedTagIds |
array | [] | Initially selected tag IDs |
selectedCategories |
array | [] | Initially selected categories |
showCounts |
bool | true | Show usage counts |
showCategories |
bool | true | Show category filter chips |
categories |
array | ['campaign', 'product', 'team', 'source', 'custom'] | Available categories |
multiSelect |
bool | true | Allow multiple tag selection |
label |
string | 'Filter by Tags' | Button label |
anchor |
string | 'end' | Dropdown anchor position |
Usage¶
{{-- In content list header --}}
<x-content-tags.tag-filter
:tags="$allTags"
:selectedTagIds="request('tags', [])"
@tag-filter-changed="applyFilters($event.detail)"
/>
Events¶
tag-filter-changed- Dispatched when filter changes (includes tagIds and categories)
4. Tag List (x-content-tags.tag-list)¶
Displays a collection of tags, optionally grouped by category.
Props¶
| Prop | Type | Default | Description |
|---|---|---|---|
tags |
array | [] | Tags to display |
max |
int|null | null | Maximum tags to show (rest collapsed) |
size |
string | 'sm' | Badge size |
removable |
bool | false | Show remove buttons |
showCategories |
bool | false | Show category icons |
showAiIndicator |
bool | false | Show AI confidence for AI-assigned tags |
grouped |
bool | false | Group tags by category |
emptyText |
string | 'No tags' | Text when empty |
clickable |
bool | false | Make badges clickable |
Usage¶
{{-- Simple list --}}
<x-content-tags.tag-list :tags="$content->tags" />
{{-- With overflow and AI indicators --}}
<x-content-tags.tag-list
:tags="$content->tagAssignments"
:max="5"
:showAiIndicator="true"
/>
{{-- Grouped by category --}}
<x-content-tags.tag-list :tags="$tags" :grouped="true" />
5. AI Confidence (x-content-tags.ai-confidence)¶
Visual indicator for AI classification confidence.
Props¶
| Prop | Type | Default | Description |
|---|---|---|---|
confidence |
float | 0 | Confidence score (0.0-1.0) |
showLabel |
bool | true | Show confidence level label |
showPercentage |
bool | true | Show percentage |
size |
string | 'sm' | Size (xs, sm, md, lg) |
variant |
string | 'badge' | Display variant (badge, bar, icon) |
Confidence Levels¶
| Score | Level | Color |
|---|---|---|
| ≥ 0.9 | Very High | Emerald |
| ≥ 0.8 | High | Emerald |
| ≥ 0.7 | Medium | Amber |
| ≥ 0.5 | Low | Orange |
| < 0.5 | Very Low | Red |
Usage¶
{{-- Badge variant (default) --}}
<x-content-tags.ai-confidence :confidence="0.92" />
{{-- Progress bar --}}
<x-content-tags.ai-confidence :confidence="0.85" variant="bar" />
{{-- Icon only --}}
<x-content-tags.ai-confidence :confidence="0.75" variant="icon" :showLabel="false" />
6. Tag Suggestion Card (x-content-tags.tag-suggestion-card)¶
Card displaying AI-suggested tag with evidence and accept/reject actions.
Props¶
| Prop | Type | Default | Description |
|---|---|---|---|
suggestion |
object | null | Suggestion object with tag, confidence, reason, evidence |
showContent |
bool | true | Show associated content preview |
showActions |
bool | true | Show accept/reject buttons |
compact |
bool | false | Compact display mode |
Usage¶
{{-- Full suggestion card --}}
<x-content-tags.tag-suggestion-card :suggestion="$suggestion" />
{{-- Compact mode in list --}}
@foreach($suggestions as $suggestion)
<x-content-tags.tag-suggestion-card
:suggestion="$suggestion"
:compact="true"
/>
@endforeach
Events¶
accept-suggestion- Dispatched when accept clicked (includes suggestion ID)reject-suggestion- Dispatched when reject clicked (includes suggestion ID)
Alpine.js Integration¶
Content Tags Store¶
A global Alpine.js store for managing tags across the application.
// Access the store
Alpine.store('contentTags')
// Available methods
await Alpine.store('contentTags').fetchTags()
await Alpine.store('contentTags').createTag({ name, category, color })
await Alpine.store('contentTags').assignTags(contentType, contentId, tagIds)
await Alpine.store('contentTags').getAiSuggestions(contentType, contentId)
Content Tags Manager¶
Local component state for managing tags on specific content.
<div x-data="contentTagsManager({
contentType: 'user_openai',
contentId: {{ $content->id }},
tags: {{ Js::from($content->tags) }}
})">
{{-- Current tags --}}
<template x-for="tag in currentTags">
<x-content-tags.tag-badge
::tag="tag"
:removable="true"
@tag-remove="removeTag(tag)"
/>
</template>
{{-- AI Suggestions --}}
<template x-for="suggestion in suggestions">
<x-content-tags.tag-suggestion-card
::suggestion="suggestion"
@accept-suggestion="acceptSuggestion(suggestion)"
@reject-suggestion="rejectSuggestion(suggestion)"
/>
</template>
</div>
Plan-Gated Features¶
Components respect plan-tier restrictions:
| Feature | Starter | Professional | Enterprise |
|---|---|---|---|
| Basic tags (product, team, source, custom) | ✓ | ✓ | ✓ |
| Campaign tags | - | ✓ | ✓ |
| Tag creation | - | ✓ | ✓ |
| Bulk operations | - | ✓ | ✓ |
| AI suggestions | - | - | ✓ |
| AI confidence display | - | - | ✓ |
Components automatically check features via the API and adjust UI accordingly.
Styling & Theming¶
All components follow the existing design system:
- Colors: Use tag's
colorproperty with CSS transparency - Sizes: Consistent xs/sm/md/lg scale matching
badge.blade.php - Dark mode: Supported via Tailwind dark: variants
- Spacing: Follow existing padding/margin patterns
CSS Variables¶
Components use existing CSS custom properties:
- --primary, --primary-foreground
- --dropdown-background, --dropdown-border
- --input-background, --input-border, --input-foreground
Integration Examples¶
Adding Tags to Content Detail Page¶
{{-- resources/views/content/show.blade.php --}}
<div class="content-tags-section">
<h3 class="text-sm font-medium mb-2">{{ __('Tags') }}</h3>
@can('tag', $content)
<x-content-tags.tag-input
:tags="$availableTags"
:selectedTags="$content->tags"
:allowCreate="auth()->user()->canCreateTags()"
:categories="$allowedCategories"
name="tags"
@tags-changed="saveTags($event.detail.tags)"
/>
@else
<x-content-tags.tag-list :tags="$content->tags" />
@endcan
</div>
Tag Filter in Content List¶
{{-- resources/views/content/index.blade.php --}}
<div class="flex items-center gap-3 mb-4">
<x-content-tags.tag-filter
:tags="$allTags"
:selectedTagIds="$selectedTagIds"
x-on:tag-filter-changed="window.location.href = updateQueryParams('tags', $event.detail.tagIds)"
/>
</div>
AI Suggestion Review Queue¶
{{-- resources/views/tags/suggestions.blade.php --}}
<div class="space-y-4">
<div class="flex items-center justify-between">
<h2>{{ __('AI Tag Suggestions') }}</h2>
<button @click="$store.contentTags.acceptAllHighConfidence()">
{{ __('Accept All High Confidence') }}
</button>
</div>
@foreach($pendingSuggestions as $suggestion)
<x-content-tags.tag-suggestion-card :suggestion="$suggestion" />
@endforeach
</div>
API Endpoints Reference¶
The components use these backend API endpoints:
| Endpoint | Method | Description |
|---|---|---|
/api/content-tags |
GET | List all tags |
/api/content-tags |
POST | Create new tag |
/api/content-tags/{id} |
PUT | Update tag |
/api/content-tags/{id} |
DELETE | Delete tag |
/api/content-tags/features |
GET | Get feature flags for plan |
/api/content/{type}/{id}/tags |
GET | Get tags for content |
/api/content/{type}/{id}/tags |
POST | Assign tags |
/api/content/{type}/{id}/tags |
DELETE | Remove tags |
/api/content-tags/bulk-assign |
POST | Bulk assign (Pro+) |
/api/ai-tagging/suggest |
POST | Get AI suggestions (Ent) |
/api/ai-tagging/apply |
POST | Apply AI suggestions (Ent) |
File Dependencies¶
Components depend on these existing resources:
resources/views/default/components/badge.blade.php- Badge styling patternsresources/views/default/components/dropdown/dropdown.blade.php- Dropdown behaviorresources/views/default/components/button.blade.php- Button variantsresources/views/default/js/app.js- Alpine.js initialization
Migration Notes for Phase 4¶
When implementing Phase 4 (Reporting & Analytics UI):
- Dashboard widgets can use
tag-listandai-confidencefor displaying tag performance - Attribution reports can use
tag-filterfor filtering by campaign/product tags - Bulk tagging interface should use
tag-inputwithallowCreatebased on permissions - AI backfill progress can extend the suggestion card pattern
The components are designed for composition - combine them as needed for complex UIs.