import React from 'react' import { Link, router, usePage } from '@inertiajs/react' import SeoHead from '../../components/seo/SeoHead' import NovaSelect from '../../components/ui/NovaSelect' import { trackAcademySearchResultClick, trackUpgradeClick, useAcademyPageAnalytics } from '../../lib/academyAnalytics' function academyHref(section, slug) { return `/academy/${section}/${encodeURIComponent(slug)}` } function Breadcrumbs({ items = [] }) { if (!items.length) { return null } return ( ) } function QueryFilters({ pageType, filters, categories }) { if (pageType !== 'lessons' && pageType !== 'prompts') { return null } const categoryOptions = [{ value: '', label: 'All categories' }, ...(categories || []).map((category) => ({ value: category.slug, label: category.name }))] const difficultyOptions = [ { value: '', label: 'All levels' }, { value: 'beginner', label: 'Beginner' }, { value: 'intermediate', label: 'Intermediate' }, { value: 'advanced', label: 'Advanced' }, { value: 'pro', label: 'Pro' }, ] return (
{ if (event.key !== 'Enter') return router.get(window.location.pathname, { ...filters, q: event.currentTarget.value }, { preserveState: true, preserveScroll: true }) }} /> router.get(window.location.pathname, { ...filters, category: nextValue || undefined }, { preserveState: true, preserveScroll: true })} options={categoryOptions} searchable={false} className="rounded-2xl bg-white/[0.04]" placeholder="All categories" /> router.get(window.location.pathname, { ...filters, difficulty: nextValue || undefined }, { preserveState: true, preserveScroll: true })} options={difficultyOptions} searchable={false} className="rounded-2xl bg-white/[0.04]" placeholder="All levels" />
) } function LockBadge({ item }) { if (!item?.locked) return {item.access_level} return Locked · {item.access_level} } function itemHref(pageType, item) { if (pageType === 'lessons') return academyHref('lessons', item.slug) if (pageType === 'prompts') return academyHref('prompts', item.slug) if (pageType === 'packs') return academyHref('packs', item.slug) return academyHref('challenges', item.slug) } function searchResultContentType(pageType) { if (pageType === 'prompts') return 'academy_prompt' if (pageType === 'lessons') return 'academy_lesson' if (pageType === 'packs') return 'academy_prompt_pack' if (pageType === 'challenges') return 'academy_challenge' return null } function promptPreviewAsset(item) { const full = item?.preview_image || '' const thumb = item?.preview_image_thumb || full if (!thumb) { return null } return { src: thumb, srcSet: item?.preview_image_srcset || '', } } function lessonPreviewAsset(item) { const src = item?.cover_image_url || item?.article_cover_image_url || item?.cover_image || item?.article_cover_image || '' if (!src) { return null } return { src } } function PromptSpotlightCard({ item }) { const preview = promptPreviewAsset(item) return (
{preview ? : null}
{item?.spotlight?.eyebrow || 'Prompt pick'} {item?.difficulty ? {item.difficulty} : null}

{item.title}

{item.excerpt || item.prompt_preview || item.description || 'Reusable prompt template.'}

{item?.category?.name || 'Academy'} {item?.tags?.[0] ? {item.tags[0]} : null}
) } function PromptDiscoverySection({ id, title, description, items = [], href, ctaLabel }) { if (!items.length) { return null } return (

Prompt discovery

{title}

{description}

{href && ctaLabel ? {ctaLabel} : null}
{items.map((item) => )}
) } function PopularPromptPeriodTabs({ currentPeriod, periods = [] }) { if (!periods.length) { return null } return (
{periods.map((period) => ( {period.label} {period.description} ))} {currentPeriod?.description ?
{currentPeriod.description}
: null}
) } function formatAccessDate(value) { if (!value) { return null } const parsed = new Date(value) if (Number.isNaN(parsed.getTime())) { return null } return new Intl.DateTimeFormat(undefined, { year: 'numeric', month: 'short', day: 'numeric', }).format(parsed) } function academyAccessHeading(access) { switch (access?.status) { case 'staff_access': return 'You currently have full staff access to the Academy.' case 'grace_period': return `${access.tierLabel} access is still active.` case 'trialing': return `${access.tierLabel} trial is active right now.` case 'active': return access?.hasPaidAccess ? `${access.tierLabel} access is active.` : 'Your Academy access is active.' case 'free': return 'You currently have Free access to the Academy.' default: return null } } function academyAccessMeta(access) { if (!access?.signedIn) { return [] } const items = [ { label: 'Current tier', value: access?.tierLabel || 'Free' }, { label: 'Status', value: access?.statusLabel || 'Free access' }, ] const formattedDate = formatAccessDate(access?.expiresAt) if (formattedDate && access?.dateLabel) { items.push({ label: access.dateLabel, value: formattedDate }) } else if (access?.renewsAutomatically) { items.push({ label: 'Billing', value: 'Renews automatically' }) } else if (!access?.hasPaidAccess) { items.push({ label: 'Upgrade', value: 'Creator and Pro unlock premium prompts' }) } return items } function PromptLibraryHero({ promptView = 'library', title, description, items, pricingUrl, coursesUrl, packsUrl, promptPopularUrl, promptLibraryUrl, popularPeriod, popularPeriods = [], totalCount, analytics, hasPopularSection, academyAccess = null }) { const isPopularView = promptView === 'popular' const statLabel = isPopularView ? `ranked prompts ${currentPeriodStatSuffix(popularPeriod)}` : 'prompts available' const showSignedInAccess = Boolean(academyAccess?.signedIn) const accessHeading = academyAccessHeading(academyAccess) const accessMeta = academyAccessMeta(academyAccess) const useBillingAction = showSignedInAccess && academyAccess?.hasPaidAccess && academyAccess?.billingUrl const primaryActionLabel = useBillingAction ? (academyAccess?.status === 'grace_period' ? 'Renew now' : 'Manage billing') : 'Upgrade now' const primaryActionIcon = useBillingAction ? (academyAccess?.status === 'grace_period' ? 'fa-solid fa-rotate-right' : 'fa-solid fa-sliders') : 'fa-solid fa-arrow-up-right-from-square' const primaryActionHref = useBillingAction ? academyAccess.billingUrl : pricingUrl const secondaryAction = isPopularView ? { href: promptLibraryUrl, label: 'Browse full library', icon: 'fa-solid fa-grid-2' } : (hasPopularSection ? { href: promptPopularUrl, label: 'Top prompts', icon: 'fa-solid fa-fire' } : { href: coursesUrl, label: 'Explore courses', icon: 'fa-solid fa-graduation-cap' }) const heroHighlights = [ { label: isPopularView ? 'Ranking window' : 'Templates', value: isPopularView ? `${totalCount || 0} ranked prompts` : `${totalCount || 0} prompts`, }, { label: 'Use case', value: isPopularView ? 'High-performing systems + trend tracking' : 'Reusable systems + premium previews', }, ] const heroTags = isPopularView ? ['Momentum picks', 'Copy trends', 'Compare windows'] : ['Fast starts', 'Visual workflows', 'Copy + adapt'] const handlePrimaryAction = () => { if (!useBillingAction) { trackUpgradeClick(analytics, { source: 'prompts_library_hero_primary' }) } } return (
Skinbase AI Academy {isPopularView ? 'Popular prompts' : 'Prompt Library'} {totalCount || 0} {statLabel}

{title}

{description}

{isPopularView ?
: null}
{heroHighlights.map((item) => (

{item.label}

{item.value}

))}
{heroTags.map((tag) => ( {tag} ))}
{primaryActionLabel} {secondaryAction.label} See prompt packs

Quick routes

{totalCount || 0} total
{isPopularView ? 'Browse full prompt library' : 'Browse Academy courses'} See prompt packs {isPopularView ? ( Explore Academy courses ) : hasPopularSection ? ( Open top prompts page ) : null}
{isPopularView ? 'Use the period tabs to compare momentum windows.' : 'Jump straight into packs, courses, or ranked prompts.'}

{showSignedInAccess ? 'Your Academy access' : (isPopularView ? 'Turn rankings into results' : 'Upgrade for full access')}

{showSignedInAccess ? accessHeading : (isPopularView ? 'Open the highest-performing prompts, then unlock the full text, helper prompts, variants, and premium workflows.' : 'Unlock full prompt text, helper prompts, variants, and premium workflows.')}

{showSignedInAccess ? (
{accessMeta.map((item) => (

{item.label}

{item.value}

))}
) : null}
{primaryActionLabel} {secondaryAction.label}
{academyAccess?.status === 'grace_period' ?

Opens billing account to restore renewal before access ends.

: null}
) } function LessonsLibraryHero({ title, description, items = [], totalCount, pricingUrl, coursesUrl, promptLibraryUrl, academyAccess = null, analytics }) { const featuredLesson = items.find((item) => lessonPreviewAsset(item)) || items[0] || null const featuredPreview = lessonPreviewAsset(featuredLesson) const showSignedInAccess = Boolean(academyAccess?.signedIn) const accessHeading = academyAccessHeading(academyAccess) const accessMeta = academyAccessMeta(academyAccess) const useBillingAction = showSignedInAccess && academyAccess?.hasPaidAccess && academyAccess?.billingUrl const primaryActionLabel = useBillingAction ? (academyAccess?.status === 'grace_period' ? 'Renew now' : 'Manage billing') : 'See plans' const primaryActionIcon = useBillingAction ? (academyAccess?.status === 'grace_period' ? 'fa-solid fa-rotate-right' : 'fa-solid fa-sliders') : 'fa-solid fa-arrow-up-right-from-square' const primaryActionHref = useBillingAction ? academyAccess.billingUrl : pricingUrl const handlePrimaryAction = () => { if (!useBillingAction) { trackUpgradeClick(analytics, { source: 'lessons_library_hero_primary' }) } } return (
Skinbase AI Academy Lessons {totalCount || 0} tutorials

{title}

{description}

Library

{totalCount || 0} structured lessons

Focus

Prompt craft + workflow cleanup

Short wins Creative habits Practical steps
Browse courses Prompt library {primaryActionLabel}

Latest lesson

{featuredLesson?.difficulty ? {featuredLesson.difficulty} : null}
{featuredPreview ? : null}
{featuredLesson?.formatted_lesson_number ? {featuredLesson.formatted_lesson_number} : null} {featuredLesson ? : null}

{String(featuredLesson?.series_name || featuredLesson?.category?.name || 'Academy lesson').trim()}

{featuredLesson?.title || 'Explore lessons'}

{featuredLesson?.excerpt || featuredLesson?.content_preview || featuredLesson?.description || 'Open a practical Academy lesson.'}

{showSignedInAccess ? 'Your Academy access' : 'Upgrade for full access'}

{showSignedInAccess ? accessHeading : 'Unlock the full lesson library, premium workflows, and the broader Academy learning track.'}

{showSignedInAccess ? (
{accessMeta.map((item) => (

{item.label}

{item.value}

))}
) : null}
{primaryActionLabel} Browse courses
{academyAccess?.status === 'grace_period' ?

Opens billing account to restore renewal before access ends.

: null}
) } function currentPeriodStatSuffix(popularPeriod) { if (!popularPeriod?.label) { return 'this month' } return popularPeriod.label === '30 days' ? 'this month' : `for ${popularPeriod.label.toLowerCase()}` } function AcademyCard({ pageType, item, analytics, searchContext, position }) { const lessonSeries = String(item?.series_name || '').trim() const promptPreviewImage = item?.preview_image_thumb || item?.preview_image || '' const promptPreviewSrcSet = item?.preview_image_srcset || '' const lessonPreview = lessonPreviewAsset(item) const contentType = searchResultContentType(pageType) const href = itemHref(pageType, item) const trackSearchClick = () => { if (!searchContext?.query || !contentType) { return } trackAcademySearchResultClick(analytics, searchContext, { contentType, contentId: item?.id, position, }) } if (pageType === 'prompts') { return (
{promptPreviewImage ? : null}
{item?.ranking?.rank ? `#${item.ranking.rank} this month` : 'Prompt template'}
{item?.difficulty ? {item.difficulty} : null} {item?.aspect_ratio ? {item.aspect_ratio} : null}

{item?.category?.name || 'Academy'}

{Array.isArray(item?.tool_notes) && item.tool_notes.length ? {item.tool_notes.length} comparisons : null}

{item.title}

{item.excerpt || item.description || item.prompt_preview || 'No description yet.'}

{item?.ranking ?

{item.ranking.prompt_copies > 0 ? `${item.ranking.prompt_copies} copies` : `${item.ranking.views} views`} · popularity {item.ranking.popularity_score}

: null} {item.tags?.length ?

{item.tags.slice(0, 4).join(' · ')}

: null}
) } if (pageType === 'lessons') { return (
{lessonPreview ? : null}
{item?.formatted_lesson_number ? {item.formatted_lesson_number} : null}
{item?.difficulty ? {item.difficulty} : null} {item?.reading_minutes ? {item.reading_minutes} min : null}

{item?.category?.name || 'Academy lesson'}

{lessonSeries ? {lessonSeries} : null}

{item.title}

{item.excerpt || item.description || item.content_preview || 'No description yet.'}

{item.tags?.length ?

{item.tags.slice(0, 4).join(' · ')}

: null}
) } return (

{pageType.slice(0, -1)}

{pageType === 'lessons' && item?.formatted_lesson_number ? (
{item.formatted_lesson_number} {lessonSeries ? {lessonSeries} : null}
) : null}

{item.title}

{item.excerpt || item.description || item.prompt_preview || item.content_preview || 'No description yet.'}

{pageType === 'lessons' && item.tags?.length ?

{item.tags.slice(0, 4).join(' · ')}

: null} {pageType === 'prompts' && item.tags?.length ?

{item.tags.slice(0, 4).join(' · ')}

: null} {pageType === 'challenges' ?

{item.status} · {item.submission_count ?? 0} submissions

: null} ) } async function fetchAcademyPage(url) { const response = await fetch(url, { headers: { Accept: 'application/json', 'X-Requested-With': 'XMLHttpRequest', }, credentials: 'same-origin', }) if (!response.ok) { throw new Error('Failed to load the next page.') } return response.json() } export default function AcademyList({ pageType, promptView = 'library', title, description, seo, breadcrumbs = [], items, filters, categories, pricingUrl, coursesUrl, packsUrl, promptPopularUrl, promptLibraryUrl, popularPeriod = null, popularPeriods = [], featuredPrompts = [], popularPrompts = [], academyAccess = null, analytics }) { const flash = usePage().props.flash || {} useAcademyPageAnalytics(analytics) const searchContext = analytics?.search ? { query: analytics.search.query, normalizedQuery: analytics.search.normalizedQuery, resultsCount: analytics.search.resultsCount, filters, } : null const initialItems = React.useMemo(() => (Array.isArray(items?.data) ? items.data : []), [items]) const [visibleItems, setVisibleItems] = React.useState(initialItems) const [pagination, setPagination] = React.useState({ currentPage: Number(items?.current_page || 1), lastPage: Number(items?.last_page || 1), prevPageUrl: items?.prev_page_url || null, nextPageUrl: items?.next_page_url || null, }) const [loadingMore, setLoadingMore] = React.useState(false) const sentinelRef = React.useRef(null) const hasActivePromptFilters = pageType === 'prompts' && promptView === 'library' && Boolean(filters?.q || filters?.category || filters?.difficulty || filters?.tag) const showPromptDiscovery = pageType === 'prompts' && promptView === 'library' && !hasActivePromptFilters const showPopularFeatured = pageType === 'prompts' && promptView === 'popular' && featuredPrompts.length > 0 const infiniteLoadLabel = pageType === 'lessons' ? 'lessons' : 'prompts' const usesInfiniteLoad = (pageType === 'prompts' && promptView === 'library') || pageType === 'lessons' React.useEffect(() => { setVisibleItems(initialItems) setPagination({ currentPage: Number(items?.current_page || 1), lastPage: Number(items?.last_page || 1), prevPageUrl: items?.prev_page_url || null, nextPageUrl: items?.next_page_url || null, }) setLoadingMore(false) }, [initialItems, items?.current_page, items?.last_page, items?.next_page_url, items?.prev_page_url, pageType]) const hasMorePages = usesInfiniteLoad && pagination.currentPage < pagination.lastPage && Boolean(pagination.nextPageUrl) const hasFallbackPagination = usesInfiniteLoad && pagination.lastPage > 1 const loadMore = React.useCallback(async () => { if (!usesInfiniteLoad || loadingMore || !pagination.nextPageUrl) { return } setLoadingMore(true) try { const payload = await fetchAcademyPage(pagination.nextPageUrl) const nextItems = Array.isArray(payload?.data) ? payload.data : [] setVisibleItems((current) => [...current, ...nextItems.filter((item) => !current.some((existing) => String(existing.id) === String(item.id)))]) setPagination({ currentPage: Number(payload?.current_page || pagination.currentPage), lastPage: Number(payload?.last_page || pagination.lastPage), prevPageUrl: payload?.prev_page_url || pagination.prevPageUrl, nextPageUrl: payload?.next_page_url || null, }) } catch { setPagination((current) => ({ ...current, nextPageUrl: null })) } finally { setLoadingMore(false) } }, [loadingMore, pagination.currentPage, pagination.lastPage, pagination.nextPageUrl, pagination.prevPageUrl, usesInfiniteLoad]) React.useEffect(() => { const sentinel = sentinelRef.current if (!sentinel || !hasMorePages || loadingMore || typeof window === 'undefined' || typeof window.IntersectionObserver !== 'function') { return undefined } const observer = new window.IntersectionObserver((entries) => { if (entries[0]?.isIntersecting) { void loadMore() } }, { rootMargin: '360px 0px' }) observer.observe(sentinel) return () => observer.disconnect() }, [hasMorePages, loadMore, loadingMore]) return (
{(pageType === 'prompts' || pageType === 'lessons') ? : null} {pageType === 'prompts' ? 0} academyAccess={academyAccess} /> : pageType === 'lessons' ? : (

Skinbase AI Academy

{title}

{description}

trackUpgradeClick(analytics, { source: `${pageType}_list_hero` })} className="rounded-full border border-sky-300/25 bg-sky-300/12 px-5 py-3 text-sm font-semibold text-sky-100">Upgrade preview
)} {flash.success ?
{flash.success}
: null} {flash.error ?
{flash.error}
: null} {promptView === 'library' ? : null} {showPromptDiscovery ? ( <> ) : null} {showPopularFeatured ? : null} {visibleItems.length === 0 ? (
Nothing matched this Academy view yet.
) : ( <>
{visibleItems.map((item, index) => )}
{usesInfiniteLoad ? (
) : null} )}
) }