feat: Introduce course marketing features with dedicated metadata, image generation, and UI in both studio and experience apps.
This commit is contained in:
@@ -12,6 +12,7 @@ import { cmsApi, Course } from "@/lib/api";
|
||||
|
||||
type TabKey =
|
||||
| "outline"
|
||||
| "marketing"
|
||||
| "grading"
|
||||
| "rubrics"
|
||||
| "calendar"
|
||||
@@ -73,6 +74,7 @@ export default function CourseEditorLayout({
|
||||
icon: BookOpen,
|
||||
tabs: [
|
||||
{ key: "outline", label: "Outline", icon: Layout, href: `/courses/${id}` },
|
||||
{ key: "marketing", label: "Marketing", icon: Megaphone, href: `/courses/${id}/marketing` },
|
||||
{ key: "files", label: "Archivos", icon: Folder, href: `/courses/${id}/files` },
|
||||
{ key: "sessions", label: "Sesiones en Vivo", icon: Video, href: `/courses/${id}/sessions` },
|
||||
],
|
||||
|
||||
@@ -151,6 +151,18 @@ export default function MediaPlayer({ src, type, transcription, locked, onEnded,
|
||||
);
|
||||
}
|
||||
|
||||
if (type === "image") {
|
||||
return (
|
||||
<div className={`relative glass overflow-hidden border border-white/10 ${locked ? 'blur-xl grayscale' : ''}`}>
|
||||
<img
|
||||
src={src}
|
||||
alt="Lesson Content"
|
||||
className="w-full aspect-video object-contain"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`relative glass overflow-hidden border border-white/10 ${locked ? 'blur-xl grayscale' : ''}`}>
|
||||
{type === "video" ? (
|
||||
|
||||
@@ -16,7 +16,7 @@ interface MediaBlockProps {
|
||||
id: string;
|
||||
title?: string;
|
||||
url: string;
|
||||
type: 'video' | 'audio';
|
||||
type: 'video' | 'audio' | 'image';
|
||||
config: {
|
||||
maxPlays?: number;
|
||||
currentPlays?: number;
|
||||
@@ -44,7 +44,9 @@ interface MediaBlockProps {
|
||||
|
||||
export default function MediaBlock({ title, url, type, config, editMode, onChange, transcription, isGraded }: MediaBlockProps) {
|
||||
const [localPlays, setLocalPlays] = useState(config.currentPlays || 0);
|
||||
const [sourceType, setSourceType] = useState<"url" | "upload">(url.startsWith("/assets/") ? "upload" : "url");
|
||||
const [sourceType, setSourceType] = useState<"url" | "upload">(
|
||||
(url.startsWith("/assets/") || url.includes("/assets/")) ? "upload" : "url"
|
||||
);
|
||||
const maxPlays = config.maxPlays || 0;
|
||||
const isLocked = maxPlays > 0 && localPlays >= maxPlays;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user