feat: add tag discovery analytics and reporting

This commit is contained in:
2026-03-17 18:23:38 +01:00
parent b3fc889452
commit 2728644477
29 changed files with 2660 additions and 112 deletions

View File

@@ -154,6 +154,8 @@ export default function BulkTagModal({ open, mode = 'add', onClose, onConfirm })
{!loading &&
results.map((tag) => {
const isSelected = selected.some((t) => t.id === tag.id)
const recentClicks = Number(tag.recent_clicks || 0)
const usageCount = Number(tag.usage_count || 0)
return (
<button
key={tag.id}
@@ -174,7 +176,9 @@ export default function BulkTagModal({ open, mode = 'add', onClose, onConfirm })
/>
{tag.name}
</span>
<span className="text-xs text-slate-500">{tag.usage_count?.toLocaleString() ?? 0} uses</span>
<span className="text-xs text-slate-500">
{recentClicks > 0 ? `${recentClicks.toLocaleString()} recent clicks` : `${usageCount.toLocaleString()} uses`}
</span>
</button>
)
})}

View File

@@ -0,0 +1,63 @@
import React from 'react'
import { beforeEach, afterEach, describe, expect, it, vi } from 'vitest'
import { render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import BulkTagModal from './BulkTagModal'
describe('BulkTagModal', () => {
beforeEach(() => {
document.head.innerHTML = '<meta name="csrf-token" content="test-token">'
global.fetch = vi.fn(async (url) => {
const requestUrl = String(url)
if (requestUrl.includes('?q=high')) {
return {
json: async () => ([
{ id: 2, name: 'High Contrast', slug: 'high-contrast', usage_count: 120, recent_clicks: 18 },
{ id: 3, name: 'High Detail', slug: 'high-detail', usage_count: 90, recent_clicks: 0 },
]),
}
}
return {
json: async () => ([
{ id: 1, name: 'Popular Pick', slug: 'popular-pick', usage_count: 300, recent_clicks: 9 },
]),
}
})
})
afterEach(() => {
vi.restoreAllMocks()
document.head.innerHTML = ''
})
it('shows recent click momentum for initial results', async () => {
render(<BulkTagModal open mode="add" onClose={() => {}} onConfirm={() => {}} />)
await waitFor(() => {
expect(screen.getByText('Popular Pick')).not.toBeNull()
})
expect(screen.getByText('9 recent clicks')).not.toBeNull()
})
it('returns selected tag ids and shows recent click momentum in search results', async () => {
const onConfirm = vi.fn()
render(<BulkTagModal open mode="add" onClose={() => {}} onConfirm={onConfirm} />)
const input = screen.getByPlaceholderText('Search tags…')
await userEvent.type(input, 'high')
await waitFor(() => {
expect(screen.getByText('18 recent clicks')).not.toBeNull()
})
await userEvent.click(screen.getByRole('button', { name: /High Contrast/i }))
await userEvent.click(screen.getByRole('button', { name: /Add 1 tag/i }))
expect(onConfirm).toHaveBeenCalledWith([2])
})
})