109 lines
7.6 KiB
JavaScript
109 lines
7.6 KiB
JavaScript
import React from 'react'
|
|
import { Head } from '@inertiajs/react'
|
|
import AdminLayout from '../../../Layouts/AdminLayout'
|
|
import AnalyticsNav from './AnalyticsNav'
|
|
|
|
function SearchList({ title, items = [], emptyText }) {
|
|
return (
|
|
<div className="rounded-[28px] border border-white/[0.08] bg-white/[0.03] p-6">
|
|
<p className="text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500">{title}</p>
|
|
<div className="mt-4 space-y-3">
|
|
{items.length ? items.map((item, index) => (
|
|
<div key={`${item.query}-${index}`} className="rounded-2xl border border-white/[0.08] bg-black/20 px-4 py-4">
|
|
<div className="flex flex-wrap items-center justify-between gap-3">
|
|
<p className="font-semibold text-white">{item.query}</p>
|
|
{'searches' in item ? <p className="text-sm font-semibold text-sky-100">{item.searches}</p> : null}
|
|
</div>
|
|
{'avg_results' in item ? <p className="mt-2 text-sm text-slate-300">Average results: {item.avg_results}</p> : null}
|
|
{'clicks' in item ? <p className="mt-2 text-sm text-slate-300">Clicks: {item.clicks}</p> : null}
|
|
{'click_through_rate' in item ? <p className="mt-2 text-sm text-slate-300">CTR: {item.click_through_rate}%</p> : null}
|
|
{'results_count' in item ? <p className="mt-2 text-sm text-slate-300">Results: {item.results_count}</p> : null}
|
|
</div>
|
|
)) : <p className="rounded-2xl border border-dashed border-white/[0.08] bg-black/20 px-4 py-6 text-sm text-slate-400">{emptyText}</p>}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function FilterUsageList({ items = [] }) {
|
|
return (
|
|
<div className="rounded-[28px] border border-white/[0.08] bg-white/[0.03] p-6">
|
|
<p className="text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500">Filter Usage</p>
|
|
<div className="mt-4 space-y-3">
|
|
{items.length ? items.map((item) => (
|
|
<div key={`${item.filter}-${item.value}`} className="rounded-2xl border border-white/[0.08] bg-black/20 px-4 py-4">
|
|
<div className="flex flex-wrap items-center justify-between gap-3">
|
|
<p className="font-semibold text-white">{item.filter}: {item.value}</p>
|
|
<p className="text-sm font-semibold text-sky-100">{item.uses}</p>
|
|
</div>
|
|
</div>
|
|
)) : <p className="rounded-2xl border border-dashed border-white/[0.08] bg-black/20 px-4 py-6 text-sm text-slate-400">No Academy search filters were used in this range.</p>}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function ClickedResultsList({ items = [] }) {
|
|
return (
|
|
<div className="rounded-[28px] border border-white/[0.08] bg-white/[0.03] p-6">
|
|
<p className="text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500">Top Clicked Results</p>
|
|
<div className="mt-4 space-y-3">
|
|
{items.length ? items.map((item) => (
|
|
<div key={`${item.content_type}-${item.content_id}`} className="rounded-2xl border border-white/[0.08] bg-black/20 px-4 py-4">
|
|
<div className="flex flex-wrap items-center justify-between gap-3">
|
|
<p className="font-semibold text-white">{item.title}</p>
|
|
<p className="text-sm font-semibold text-sky-100">{item.clicks}</p>
|
|
</div>
|
|
<p className="mt-2 text-sm text-slate-300">{item.content_type}</p>
|
|
</div>
|
|
)) : <p className="rounded-2xl border border-dashed border-white/[0.08] bg-black/20 px-4 py-6 text-sm text-slate-400">No clicked Academy search results were logged in this range.</p>}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default function AcademyAnalyticsSearch({ nav = [], range, summary = {}, topSearches = [], zeroResults = [], lowClickThroughSearches = [], highestClickThroughSearches = [], searchesWithResultsNoClicks = [], topClickedResults = [], filterUsage = [], recentSearches = [] }) {
|
|
return (
|
|
<AdminLayout title="Academy Search Analytics" subtitle="Search demand, zero-result gaps, and recent Academy query activity.">
|
|
<Head title="Admin · Academy Search Analytics" />
|
|
|
|
<div className="space-y-6">
|
|
<AnalyticsNav items={nav} />
|
|
|
|
<div className="grid gap-4 sm:grid-cols-2 xl:grid-cols-4">
|
|
<div className="rounded-2xl border border-white/[0.08] bg-white/[0.04] p-5"><p className="text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500">Searches</p><p className="mt-3 text-3xl font-bold text-white">{Number(summary.searches || 0).toLocaleString()}</p></div>
|
|
<div className="rounded-2xl border border-white/[0.08] bg-white/[0.04] p-5"><p className="text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500">Zero Result Searches</p><p className="mt-3 text-3xl font-bold text-white">{Number(summary.zeroResultSearches || 0).toLocaleString()}</p></div>
|
|
<div className="rounded-2xl border border-white/[0.08] bg-white/[0.04] p-5"><p className="text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500">Logged-in Searches</p><p className="mt-3 text-3xl font-bold text-white">{Number(summary.loggedInSearches || 0).toLocaleString()}</p></div>
|
|
<div className="rounded-2xl border border-white/[0.08] bg-white/[0.04] p-5"><p className="text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500">Subscriber Searches</p><p className="mt-3 text-3xl font-bold text-white">{Number(summary.subscriberSearches || 0).toLocaleString()}</p></div>
|
|
</div>
|
|
|
|
<div className="grid gap-4 sm:grid-cols-2">
|
|
<div className="rounded-2xl border border-white/[0.08] bg-white/[0.04] p-5"><p className="text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500">Searches With Clicks</p><p className="mt-3 text-3xl font-bold text-white">{Number(summary.searchesWithClicks || 0).toLocaleString()}</p></div>
|
|
<div className="rounded-2xl border border-white/[0.08] bg-white/[0.04] p-5"><p className="text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500">Needs CTR Tracking</p><p className="mt-3 text-sm leading-6 text-slate-300">Low-click sections use stored search click attribution when present. Queries without clicked-result updates will stay at 0% CTR until that interaction is sent.</p></div>
|
|
</div>
|
|
|
|
<div className="rounded-[28px] border border-white/[0.08] bg-white/[0.03] p-6">
|
|
<p className="text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500">Range</p>
|
|
<p className="mt-3 text-sm text-slate-300">{range?.from} to {range?.to}</p>
|
|
</div>
|
|
|
|
<div className="grid gap-6 xl:grid-cols-3">
|
|
<SearchList title="Top Searches" items={topSearches} emptyText="No Academy searches were logged in this range." />
|
|
<SearchList title="Highest CTR Searches" items={highestClickThroughSearches} emptyText="No clicked Academy searches were logged in this range." />
|
|
<SearchList title="Zero-result Searches" items={zeroResults} emptyText="No zero-result searches were logged in this range." />
|
|
</div>
|
|
|
|
<div className="grid gap-6 xl:grid-cols-3">
|
|
<SearchList title="Low Click-through Searches" items={lowClickThroughSearches} emptyText="No low click-through Academy searches were logged in this range." />
|
|
<SearchList title="Results With No Clicks" items={searchesWithResultsNoClicks} emptyText="No Academy searches with results but no clicks were logged in this range." />
|
|
<ClickedResultsList items={topClickedResults} />
|
|
</div>
|
|
|
|
<div className="grid gap-6 xl:grid-cols-2">
|
|
<FilterUsageList items={filterUsage} />
|
|
<SearchList title="Recent Searches" items={recentSearches} emptyText="No recent Academy searches were logged in this range." />
|
|
</div>
|
|
</div>
|
|
</AdminLayout>
|
|
)
|
|
} |