122 lines
5.2 KiB
TypeScript
122 lines
5.2 KiB
TypeScript
"use client";
|
|
|
|
import Link from "next/link";
|
|
import { FormEvent, useEffect, useState } from "react";
|
|
import { api, Subject, Week } from "@/lib/api";
|
|
import { Shell } from "@/components/Shell";
|
|
import { Button, Card, Input, Label } from "@/components/ui";
|
|
|
|
export default function SubjectPage({ params }: { params: { subjectId: string } }) {
|
|
const subjectId = Number(params.subjectId);
|
|
const [subject, setSubject] = useState<Subject | null>(null);
|
|
const [weeks, setWeeks] = useState<Week[]>([]);
|
|
const [number, setNumber] = useState(1);
|
|
const [title, setTitle] = useState("");
|
|
const [editingWeek, setEditingWeek] = useState<Week | null>(null);
|
|
const [editNumber, setEditNumber] = useState(1);
|
|
const [editTitle, setEditTitle] = useState("");
|
|
const [error, setError] = useState("");
|
|
|
|
async function load() {
|
|
try {
|
|
const [subjectData, weekList] = await Promise.all([api.subject(subjectId), api.weeks(subjectId)]);
|
|
setSubject(subjectData);
|
|
setWeeks(weekList);
|
|
setNumber((weekList.at(-1)?.number || 0) + 1);
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err.message : "No se pudo cargar la materia");
|
|
}
|
|
}
|
|
|
|
useEffect(() => { load(); }, [subjectId]);
|
|
|
|
async function createWeek(event: FormEvent) {
|
|
event.preventDefault();
|
|
try {
|
|
setError("");
|
|
await api.createWeek(subjectId, number, title);
|
|
setTitle("");
|
|
await load();
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err.message : "No se pudo crear la semana");
|
|
}
|
|
}
|
|
|
|
function startEdit(week: Week) {
|
|
setEditingWeek(week);
|
|
setEditNumber(week.number);
|
|
setEditTitle(week.title || "");
|
|
setError("");
|
|
}
|
|
|
|
async function saveEdit(event: FormEvent) {
|
|
event.preventDefault();
|
|
if (!editingWeek) return;
|
|
try {
|
|
setError("");
|
|
await api.updateWeek(subjectId, editingWeek.number, editNumber, editTitle);
|
|
setEditingWeek(null);
|
|
await load();
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err.message : "No se pudo editar la semana");
|
|
}
|
|
}
|
|
|
|
return (
|
|
<Shell>
|
|
<Link href="/" className="text-sm text-blue-300">Volver a materias</Link>
|
|
<div className="mt-4 grid gap-6 lg:grid-cols-[1fr_360px]">
|
|
<section>
|
|
<h1 className="text-4xl font-bold tracking-tight">{subject?.name || "Materia"}</h1>
|
|
<p className="mt-2 text-slate-400">Divide el conocimiento por semanas y sube fuentes a cada una.</p>
|
|
{error && <Card className="mt-4 border-red-500/30 bg-red-500/10 text-red-200">{error}</Card>}
|
|
<div className="mt-6 grid gap-4 md:grid-cols-2">
|
|
{weeks.map((week) => (
|
|
<Card key={week.id} className="transition hover:border-blue-400/40 hover:bg-blue-500/10">
|
|
<div className="text-2xl font-semibold">Semana {week.number}</div>
|
|
<div className="mt-2 text-slate-400">{week.title || "Sin titulo"}</div>
|
|
<div className="mt-5 flex flex-wrap gap-2 text-sm">
|
|
<Link className="rounded-lg bg-blue-600 px-3 py-2 font-medium text-white hover:bg-blue-500" href={`/subjects/${subjectId}/weeks/${week.number}`}>Abrir semana</Link>
|
|
<button className="rounded-lg border border-white/10 px-3 py-2 text-slate-200 hover:bg-white/10" type="button" onClick={() => startEdit(week)}>Editar</button>
|
|
</div>
|
|
</Card>
|
|
))}
|
|
{weeks.length === 0 && <Card className="md:col-span-2">Todavia no hay semanas.</Card>}
|
|
</div>
|
|
</section>
|
|
<aside>
|
|
<Card>
|
|
{editingWeek ? (
|
|
<>
|
|
<h2 className="text-lg font-semibold">Editar semana</h2>
|
|
<p className="mt-1 text-sm text-slate-400">Cambia el numero o titulo. Si cambias el numero, se renombra la carpeta de la semana.</p>
|
|
<form className="mt-4 space-y-3" onSubmit={saveEdit}>
|
|
<Label>Numero</Label>
|
|
<Input type="number" min={1} value={editNumber} onChange={(event) => setEditNumber(Number(event.target.value))} />
|
|
<Label>Titulo</Label>
|
|
<Input value={editTitle} onChange={(event) => setEditTitle(event.target.value)} placeholder="Ej. Grupos y equipos" />
|
|
<div className="flex gap-2">
|
|
<Button className="flex-1">Guardar</Button>
|
|
<button className="flex-1 rounded-xl border border-white/10 px-4 py-2 hover:bg-white/10" type="button" onClick={() => setEditingWeek(null)}>Cancelar</button>
|
|
</div>
|
|
</form>
|
|
</>
|
|
) : (
|
|
<>
|
|
<h2 className="text-lg font-semibold">Nueva semana</h2>
|
|
<form className="mt-4 space-y-3" onSubmit={createWeek}>
|
|
<Label>Numero</Label>
|
|
<Input type="number" min={1} value={number} onChange={(event) => setNumber(Number(event.target.value))} />
|
|
<Label>Titulo opcional</Label>
|
|
<Input value={title} onChange={(event) => setTitle(event.target.value)} placeholder="Ej. Grupos y equipos" />
|
|
<Button className="w-full">Crear semana</Button>
|
|
</form>
|
|
</>
|
|
)}
|
|
</Card>
|
|
</aside>
|
|
</div>
|
|
</Shell>
|
|
);
|
|
}
|