mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user