1
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
"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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user