Files
ACVE/components/teacher/TeacherUploadsLibraryClient.tsx
2026-02-17 00:07:00 +00:00

141 lines
6.0 KiB
TypeScript

"use client";
import { useEffect, useMemo, useState } from "react";
import Link from "next/link";
import { getTeacherCourses, teacherCoursesUpdatedEventName } from "@/lib/data/teacherCourses";
import type { Course } from "@/types/course";
type UploadAsset = {
id: string;
fileName: string;
type: "video" | "pdf" | "audio";
duration?: string;
size: string;
uploadedAt: string;
};
const mockAssets: UploadAsset[] = [
{ id: "asset-1", fileName: "contracts-intro-v1.mp4", type: "video", duration: "08:34", size: "48 MB", uploadedAt: "2026-02-01" },
{ id: "asset-2", fileName: "clause-red-flags.mp4", type: "video", duration: "12:12", size: "66 MB", uploadedAt: "2026-02-03" },
{ id: "asset-3", fileName: "brief-writing-reference.pdf", type: "pdf", size: "2.3 MB", uploadedAt: "2026-02-05" },
];
export default function TeacherUploadsLibraryClient() {
const [courses, setCourses] = useState<Course[]>([]);
const [selectedCourseByAsset, setSelectedCourseByAsset] = useState<Record<string, string>>({});
const [selectedLessonByAsset, setSelectedLessonByAsset] = useState<Record<string, string>>({});
const [message, setMessage] = useState<string | null>(null);
useEffect(() => {
const load = () => setCourses(getTeacherCourses());
load();
window.addEventListener(teacherCoursesUpdatedEventName, load);
return () => window.removeEventListener(teacherCoursesUpdatedEventName, load);
}, []);
const lessonOptionsByCourse = useMemo(() => {
const map: Record<string, Array<{ id: string; title: string }>> = {};
for (const course of courses) {
map[course.slug] = course.lessons.map((lesson) => ({ id: lesson.id, title: lesson.title }));
}
return map;
}, [courses]);
const attachAsset = (assetId: string) => {
const courseSlug = selectedCourseByAsset[assetId];
const lessonId = selectedLessonByAsset[assetId];
if (!courseSlug || !lessonId) {
setMessage("Select a course and lesson before attaching.");
return;
}
setMessage(`Placeholder only: asset attached to ${courseSlug} / ${lessonId}.`);
};
return (
<div className="space-y-6">
<header className="rounded-xl border border-slate-200 bg-white p-5 shadow-sm">
<h1 className="text-3xl font-bold text-slate-900">Upload Library</h1>
<p className="mt-1 text-sm text-slate-600">Central cloud-style asset library for reusing videos and files across courses.</p>
<p className="mt-3 rounded-lg border border-amber-200 bg-amber-50 px-3 py-2 text-sm text-amber-900">
Upload disabled in MVP. This is a placeholder for backend integration.
</p>
<div className="mt-4 flex gap-2">
<Link className="rounded-md border border-slate-300 px-3 py-1.5 text-sm hover:bg-slate-50" href="/teacher">
Back to dashboard
</Link>
<button className="rounded-md bg-slate-200 px-3 py-1.5 text-sm font-semibold text-slate-700" type="button">
Upload file (disabled)
</button>
</div>
</header>
{message ? (
<p className="rounded-md border border-slate-200 bg-white px-3 py-2 text-sm text-slate-700">{message}</p>
) : null}
<section className="grid gap-4">
{mockAssets.map((asset) => {
const selectedCourse = selectedCourseByAsset[asset.id] ?? "";
const lessonOptions = selectedCourse ? lessonOptionsByCourse[selectedCourse] ?? [] : [];
return (
<article key={asset.id} className="rounded-xl border border-slate-200 bg-white p-5 shadow-sm">
<div className="flex flex-wrap items-start justify-between gap-3">
<div>
<p className="text-xs font-semibold uppercase tracking-wide text-brand">{asset.type}</p>
<h2 className="text-lg font-semibold text-slate-900">{asset.fileName}</h2>
<p className="mt-1 text-sm text-slate-600">
{asset.size} {asset.duration ? `| ${asset.duration}` : ""} | Uploaded {asset.uploadedAt}
</p>
</div>
</div>
<div className="mt-4 grid gap-3 sm:grid-cols-[1fr_1fr_auto]">
<select
className="rounded-md border border-slate-300 px-3 py-2 text-sm outline-none focus:border-brand"
onChange={(event) => {
const slug = event.target.value;
setSelectedCourseByAsset((current) => ({ ...current, [asset.id]: slug }));
setSelectedLessonByAsset((current) => ({ ...current, [asset.id]: "" }));
}}
value={selectedCourse}
>
<option value="">Select course</option>
{courses.map((course) => (
<option key={course.slug} value={course.slug}>
{course.title}
</option>
))}
</select>
<select
className="rounded-md border border-slate-300 px-3 py-2 text-sm outline-none focus:border-brand"
disabled={!selectedCourse}
onChange={(event) =>
setSelectedLessonByAsset((current) => ({ ...current, [asset.id]: event.target.value }))
}
value={selectedLessonByAsset[asset.id] ?? ""}
>
<option value="">Select lesson</option>
{lessonOptions.map((lesson) => (
<option key={lesson.id} value={lesson.id}>
{lesson.title}
</option>
))}
</select>
<button
className="rounded-md border border-slate-300 px-3 py-2 text-sm font-semibold hover:bg-slate-50"
onClick={() => attachAsset(asset.id)}
type="button"
>
Attach to lesson
</button>
</div>
</article>
);
})}
</section>
</div>
);
}