Skip to content

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 color property 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 patterns
  • resources/views/default/components/dropdown/dropdown.blade.php - Dropdown behavior
  • resources/views/default/components/button.blade.php - Button variants
  • resources/views/default/js/app.js - Alpine.js initialization

Migration Notes for Phase 4

When implementing Phase 4 (Reporting & Analytics UI):

  1. Dashboard widgets can use tag-list and ai-confidence for displaying tag performance
  2. Attribution reports can use tag-filter for filtering by campaign/product tags
  3. Bulk tagging interface should use tag-input with allowCreate based on permissions
  4. AI backfill progress can extend the suggestion card pattern

The components are designed for composition - combine them as needed for complex UIs.