add: more things

Signed-off-by: Kenneth Obsequio <k80308392@gmail.com>
This commit is contained in:
2026-07-04 03:35:22 +08:00
parent 72298b86cd
commit b0f40e5133
78 changed files with 2116 additions and 1621 deletions
@@ -3,6 +3,7 @@ import { useNavigate } from 'react-router-dom';
import { useAdminTask } from '@/contexts/AdminTaskContext';
import GroupMultiSelect from '@/components/generic/GroupMultiSelect';
import TaskQueueStep from './TaskQueueStep';
import api from '@/utils/api.util';
import { cn } from '@/lib/utils';
@@ -12,13 +13,14 @@ import { Textarea } from '@/components/ui/textarea';
import { Label } from '@/components/ui/label';
import { Card, CardContent } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { ArrowLeft, ChevronRight, ChevronLeft, Check, FileText, Users, ClipboardList } from 'lucide-react';
import { ArrowLeft, ChevronRight, ChevronLeft, Check, FileText, Users, ListChecks, ClipboardList } from 'lucide-react';
// ─── Steps ────────────────────────────────────────────────────────────────────
const STEPS = [
{ id: 0, label: 'Details', icon: FileText },
{ id: 1, label: 'Assign Groups', icon: Users },
{ id: 2, label: 'Review', icon: ClipboardList },
{ id: 2, label: 'Tasks', icon: ListChecks },
{ id: 3, label: 'Review', icon: ClipboardList },
];
// ─── Summary row ──────────────────────────────────────────────────────────────
@@ -34,12 +36,20 @@ function SummaryRow({ label, value }) {
export default function CreateTaskList() {
const navigate = useNavigate();
const { createTaskList, assignGroups, loading } = useAdminTask();
const {
createTaskList, assignGroups, createTask,
fetchCoursesFlat, fetchUnitsFlat, fetchLessonsFlat,
loading,
} = useAdminTask();
const [step, setStep] = useState(0);
const [form, setForm] = useState({ name: '', description: '' });
const [selectedGroupIds, setSelectedGroupIds] = useState([]);
const [allGroups, setAllGroups] = useState([]);
const [queuedTasks, setQueuedTasks] = useState([]);
const [courses, setCourses] = useState([]);
const [units, setUnits] = useState([]);
const [lessons, setLessons] = useState([]);
const [errors, setErrors] = useState({});
// Fetch groups for the review step's summary (names, not just ids)
@@ -49,6 +59,13 @@ export default function CreateTaskList() {
.catch(() => {});
}, []);
// Fetch flat content lists for the Tasks step's requirement builder
useEffect(() => {
fetchCoursesFlat().then((d) => d && setCourses(d));
fetchUnitsFlat().then((d) => d && setUnits(d));
fetchLessonsFlat().then((d) => d && setLessons(d));
}, []);
const validateDetails = () => {
const e = {};
if (!form.name.trim()) e.name = 'Task list name is required.';
@@ -81,6 +98,21 @@ export default function CreateTaskList() {
await assignGroups(created.task_list_id, selectedGroupIds);
}
// Create any queued tasks under the new task list — non-blocking: navigate regardless
for (const t of queuedTasks) {
await createTask(created.task_list_id, {
name: t.name.trim(),
description: t.description?.trim() || null,
deadline: t.deadline || null,
// strip duration_seconds — it's only used for local validation
requirements: t.requirements.map((r) => {
const req = { ...r };
delete req.duration_seconds;
return req;
}),
});
}
navigate(`/admin/taskList/${created.task_list_id}/view`);
};
@@ -196,8 +228,24 @@ export default function CreateTaskList() {
</div>
)}
{/* ── Step 3: Review ── */}
{/* ── Step 3: Tasks ── */}
{step === 2 && (
<div className="space-y-3">
<p className="text-xs text-muted-foreground -mt-1">
Optionally add the tasks users will need to complete for this task list.
</p>
<TaskQueueStep
tasks={queuedTasks}
onChange={setQueuedTasks}
courses={courses}
units={units}
lessons={lessons}
/>
</div>
)}
{/* ── Step 4: Review ── */}
{step === 3 && (
<div className="space-y-4">
<div className="border border-border rounded-lg p-4 space-y-1">
<div className="flex items-center gap-2 mb-2">
@@ -223,6 +271,31 @@ export default function CreateTaskList() {
<p className="text-sm text-muted-foreground">No groups assigned — task list will not be visible to any users yet.</p>
)}
</div>
<div className="border border-border rounded-lg p-4 space-y-2">
<div className="flex items-center gap-2">
<ListChecks className="h-4 w-4 text-muted-foreground" />
<span className="text-sm font-medium">Tasks</span>
<Badge variant="secondary" className="ml-auto text-xs">{queuedTasks.length}</Badge>
</div>
{queuedTasks.length > 0 ? (
<div className="space-y-1.5 pt-1">
{queuedTasks.map((t, i) => (
<div key={t._key} className="flex items-center gap-2 text-sm">
<Badge variant="outline" className="text-xs shrink-0">{i + 1}</Badge>
<span className="truncate flex-1">{t.name}</span>
{t.requirements.length > 0 && (
<span className="text-xs text-muted-foreground shrink-0">
{t.requirements.length} requirement(s)
</span>
)}
</div>
))}
</div>
) : (
<p className="text-sm text-muted-foreground">No tasks added yet — you can add them later from the task list's Tasks tab.</p>
)}
</div>
</div>
)}
</CardContent>