import React, { useDeferredValue, useEffect, useMemo, useRef, useState } from 'react'
import { Head, Link, router, useForm } from '@inertiajs/react'
import AdminLayout from '../../../Layouts/AdminLayout'
import RichTextEditor from '../../../components/forum/RichTextEditor'
import WorldMediaUploadField from '../../../components/worlds/editor/WorldMediaUploadField'
import DateTimePicker from '../../../components/ui/DateTimePicker'
import NovaSelect from '../../../components/ui/NovaSelect'
const COURSE_EDITOR_TABS = [
{
id: 'overview',
label: 'Overview',
description: 'Title, slug, positioning, and the short summary shown on course cards.',
icon: 'fa-compass-drafting',
sections: ['course-identity'],
},
{
id: 'content',
label: 'Content',
description: 'Use the richer WYSIWYG surface for the main course description and learning pitch.',
icon: 'fa-pen-nib',
sections: ['course-description'],
},
{
id: 'media',
label: 'Media',
description: 'Upload and tune the cover and teaser visuals used across the public course surfaces.',
icon: 'fa-images',
sections: ['course-media'],
},
{
id: 'lessons',
label: 'Lessons',
description: 'Build the lesson sequence, drag to reorder, and add or remove lessons without opening the full builder.',
icon: 'fa-list-ol',
sections: ['course-lessons-manager'],
},
{
id: 'publish',
label: 'Publish',
description: 'Control access, status, ordering, scheduling, and featured placement.',
icon: 'fa-rocket-launch',
sections: ['course-publishing', 'course-seo'],
},
{
id: 'preview',
label: 'Preview',
description: 'Scan the public-facing course card, media, and rendered long description before publishing.',
icon: 'fa-eye',
sections: ['course-preview'],
},
]
const COURSE_FIELD_TAB_MAP = {
title: 'overview',
slug: 'overview',
subtitle: 'overview',
excerpt: 'overview',
description: 'content',
cover_image: 'media',
teaser_image: 'media',
access_level: 'publish',
difficulty: 'publish',
status: 'publish',
order_num: 'publish',
estimated_minutes: 'publish',
published_at: 'publish',
is_featured: 'publish',
seo_title: 'publish',
seo_description: 'publish',
meta_keywords: 'publish',
og_title: 'publish',
og_description: 'publish',
og_image: 'publish',
}
function getField(fields, name) {
return fields.find((field) => field.name === name) || null
}
function FieldError({ message }) {
if (!message) return null
return
{message}
}
function SectionCard({ id, eyebrow, title, description, actions, children, tone = 'default', className = '', contentClassName = '' }) {
const toneClass = tone === 'feature'
? 'bg-[radial-gradient(circle_at_top_left,rgba(56,189,248,0.16),transparent_38%),linear-gradient(180deg,rgba(15,23,42,0.96),rgba(2,6,23,0.92))] shadow-[0_24px_70px_rgba(2,6,23,0.28)]'
: 'bg-white/[0.03]'
return (
{eyebrow ?
{eyebrow}
: null}
{title}
{description ?
{description}
: null}
{actions ?
{actions}
: null}
{children}
)
}
function EditorWorkspaceTabs({ tabs, activeTab, onChange, errorCounts }) {
const activeMeta = tabs.find((tab) => tab.id === activeTab) || tabs[0]
return (
{tabs.map((tab) => {
const isActive = tab.id === activeTab
const errorCount = Number(errorCounts?.[tab.id] || 0)
return (
)
})}
{activeMeta.description}
{activeMeta.sections.map((section) => (
{section.replace('course-', '').replace(/-/g, ' ')}
))}
)
}
function TextField({ label, value, onChange, error, hint, ...rest }) {
return (
)
}
function TextAreaField({ label, value, onChange, error, rows = 4, hint }) {
return (
)
}
function CheckboxCardField({ label, checked, onChange, description, error }) {
return (
)
}
function OutlineSectionPill({ section }) {
return (
{section.title}
{section.is_visible ? 'Visible section' : 'Hidden section'}
{section.lesson_count}
)
}
function slugifyCourseTitle(value) {
return String(value || '')
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '')
.slice(0, 180)
}
function formatLessonStep(orderNum) {
const numeric = Number(orderNum)
if (!Number.isFinite(numeric) || numeric < 0) return null
return `Step ${String(numeric + 1).padStart(2, '0')}`
}
function normalizeLessonManagerLessons(lessons) {
return (Array.isArray(lessons) ? [...lessons] : [])
.sort((a, b) => {
const diff = Number(a?.order_num || 0) - Number(b?.order_num || 0)
return diff !== 0 ? diff : Number(a?.id || 0) - Number(b?.id || 0)
})
.map((lesson, index) => ({ ...lesson, order_num: index, display_order: index + 1 }))
}
function reorderLessonManagerLessons(lessons, draggedId, targetId) {
const current = normalizeLessonManagerLessons(lessons)
const di = current.findIndex((l) => Number(l.id) === Number(draggedId))
const ti = current.findIndex((l) => Number(l.id) === Number(targetId))
if (di === -1 || ti === -1 || di === ti) return current
const next = [...current]
const [moved] = next.splice(di, 1)
next.splice(ti, 0, moved)
return normalizeLessonManagerLessons(next)
}
function moveLessonManagerLesson(lessons, lessonId, direction) {
const current = normalizeLessonManagerLessons(lessons)
const idx = current.findIndex((l) => Number(l.id) === Number(lessonId))
const nextIdx = idx + direction
if (idx === -1 || nextIdx < 0 || nextIdx >= current.length) return current
const next = [...current]
const [moved] = next.splice(idx, 1)
next.splice(nextIdx, 0, moved)
return normalizeLessonManagerLessons(next)
}
function lessonManagerSignature(lessons) {
return JSON.stringify(normalizeLessonManagerLessons(lessons).map((l) => ({
id: Number(l.id),
order_num: Number(l.order_num || 0),
section_id: l.section_id == null ? null : Number(l.section_id),
})))
}
function stripHtml(value) {
return String(value || '')
.replace(/