import { useEffect, useMemo, useState } from 'react'; import { CreateCourseModal } from '../components/shared/CreateCourseModal'; import { EntityListView, StatusBadge } from '../components/shared/list'; import { ButtonPrimary } from '../components/shared/buttons'; import type { Column } from '../components/shared/DataTable'; import { NavIcon } from '../components/NavIcon'; import { EmbeddableShell } from '../components/shared/EmbeddableShell'; import { appViewHref } from '../lib/appUrl'; import { isFeatureEnabled } from '../lib/licensing'; import { courseMetaString, coursePriceLabel, embeddedAuthorName, formatCourseDurationListCell } from '../lib/courseListMeta'; import { formatDisplaySlug } from '../lib/formatDisplaySlug'; import { embeddedTermNames } from '../lib/wpPostTerms'; import { formatPostDate } from '../lib/formatPostDate'; import { term, termLower } from '../lib/terminology'; import type { SikshyaReactConfig, WpPost } from '../types'; import { FeaturedThumb } from '../components/shared/list/FeaturedThumb'; import { __ } from '../lib/i18n'; function isBundleRow(r: WpPost): boolean { const m = r.meta as Record | undefined; const v = (m && (m._sikshya_course_type ?? (m as Record).sikshya_course_type)) ?? r.sikshya_course_type ?? // Defensive: some WP setups/plugins flatten meta unexpectedly. (r as unknown as { _sikshya_course_type?: unknown })._sikshya_course_type; return String(v || '') === 'bundle'; } function isSubscriptionRow(r: WpPost): boolean { const m = r.meta as Record | undefined; const v = (m && (m._sikshya_course_type ?? (m as Record).sikshya_course_type)) ?? r.sikshya_course_type ?? (r as unknown as { _sikshya_course_type?: unknown })._sikshya_course_type; return String(v || '') === 'subscription'; } function stripTags(html: string): string { return html.replace(/<[^>]*>/g, '').trim(); } function excerptPlain(r: WpPost): string { const raw = r.excerpt?.rendered; if (!raw) { return ''; } return stripTags(raw).replace(/\s+/g, ' ').trim(); } const COURSE_CAT_TAX = 'sikshya_course_category'; export function CoursesPage(props: { embedded?: boolean; config: SikshyaReactConfig; title: string; restBase: string }) { const { config, title, restBase } = props; const [createOpen, setCreateOpen] = useState(false); const [typeFilter, setTypeFilter] = useState<'any' | 'regular' | 'subscription' | 'bundle'>('any'); const showCourseStaff = isFeatureEnabled(config, 'multi_instructor'); const course = term(config, 'course'); const courseLower = termLower(config, 'course'); const coursesLower = termLower(config, 'courses'); useEffect(() => { try { const sp = new URLSearchParams(window.location.search); if (sp.get('create') === '1' || sp.get('create') === 'true') { setCreateOpen(true); } } catch { // no-op (SSR / malformed URL) } }, []); const columns: Column[] = useMemo( () => [ { id: 'id', header: 'ID', sortKey: 'id', alwaysVisible: true, cellClassName: 'whitespace-nowrap tabular-nums text-slate-600 dark:text-slate-400', render: (r) => r.id, }, { id: 'thumb', header: '', columnPickerLabel: 'Image', alwaysVisible: true, headerClassName: 'w-16', cellClassName: 'w-16', render: (r) => { return ( ); }, }, { id: 'title', header: course, sortKey: 'title', render: (r) => (
{isBundleRow(r) ? ( Bundle ) : isSubscriptionRow(r) ? ( Subscription ) : null}
{r.slug ? (
{formatDisplaySlug(r.slug, r.status)}
) : null}
), }, { id: 'categories', header: 'Categories', defaultHidden: false, cellClassName: 'max-w-[14rem] text-slate-600 dark:text-slate-400', render: (r) => { const names = embeddedTermNames(r, COURSE_CAT_TAX); return names.length ? names.join(', ') : '—'; }, }, { id: 'author', header: 'Author', sortKey: 'author', defaultHidden: true, cellClassName: 'whitespace-nowrap text-slate-600 dark:text-slate-400', render: (r) => embeddedAuthorName(r), }, { id: 'price', header: 'Price', cellClassName: 'whitespace-nowrap tabular-nums', render: (r) => coursePriceLabel(r), }, { id: 'duration', header: 'Duration', defaultHidden: true, cellClassName: 'whitespace-nowrap text-slate-600 dark:text-slate-400', render: (r) => formatCourseDurationListCell( courseMetaString(r, '_sikshya_course_duration', '_sikshya_duration', 'sikshya_course_duration') ), }, { id: 'level', header: 'Level', cellClassName: 'whitespace-nowrap text-slate-600 dark:text-slate-400', render: (r) => courseMetaString(r, '_sikshya_course_level', '_sikshya_difficulty', 'sikshya_course_level'), }, { id: 'excerpt', header: 'Excerpt', defaultHidden: true, cellClassName: 'max-w-xs text-slate-600 dark:text-slate-400', render: (r) => { const t = excerptPlain(r); return t ? {t} : '—'; }, }, { id: 'date', header: 'Published', sortKey: 'date', cellClassName: 'whitespace-nowrap text-slate-600 dark:text-slate-400', render: (r) => formatPostDate(r.date), }, { id: 'modified', header: 'Updated', sortKey: 'modified', cellClassName: 'whitespace-nowrap text-slate-600 dark:text-slate-400', render: (r) => formatPostDate(r.modified || r.date), }, { id: 'status', header: 'Status', render: (r) => , }, ], [config, course] ); return ( setCreateOpen(true)}>+ Add new {courseLower} } > setCreateOpen(false)} /> } postRowActions={{ buildLeadingItems: (r) => { const items = [ { key: 'builder', label: isBundleRow(r) ? 'Edit bundle' : `Edit ${courseLower} in builder`, href: appViewHref(config, 'add-course', { course_id: String(r.id), ...(isBundleRow(r) ? { force_bundle_ui: '1' } : null), }), }, ]; if (showCourseStaff) { items.push({ key: 'course-team', label: 'Manage staff', href: appViewHref(config, 'course-team', { course_id: String(r.id) }), }); } return items; }, }} columns={columns} emptyMessage={`No ${coursesLower} match your filters. Try clearing search or choosing another status.`} emptyStateTitle={`No ${coursesLower} found`} emptyStateDescription={`Create a draft ${courseLower} to see it listed here.`} emptyStateAction={ setCreateOpen(true)}>+ Add new {courseLower} } skeletonHeaders={[ 'ID', '', course, 'Categories', 'Price', 'Level', 'Published', 'Updated', 'Status', ]} /> ); }