implemented task more

This commit is contained in:
rgrgogu
2026-07-17 17:27:59 +08:00
parent 2e9ab5786c
commit 5eefbe0dc9
10 changed files with 120 additions and 31 deletions
+39 -1
View File
@@ -14,7 +14,7 @@ import { Switch } from "@/components/ui/switch";
import { Spinner } from "@/components/ui/spinner";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { CRON_PRESET_OPTIONS, JOB_LABELS } from "@/data/cronPresets.data";
import { CRON_PRESET_OPTIONS, JOB_LABELS, TARGET_STATUS_OPTIONS } from "@/data/cronPresets.data";
function SectionCard({ children }) {
return <div className="rounded-lg border bg-card p-4">{children}</div>;
@@ -84,6 +84,23 @@ export default function Jobs() {
}
}
async function handleTargetStatusChange(jobName, target_status) {
setSavingJob(jobName);
try {
const { data } = await api.patch(`/admin/announcement-settings/${jobName}`, {
target_status,
updatedBy: user?.user_id ?? null,
});
const updated = data?.data?.data;
setSettings((prev) => prev.map((s) => (s.job_name === jobName ? { ...s, ...updated, target_status } : s)));
toast("Target status updated.");
} catch (err) {
toast(err?.response?.data?.message ?? "Failed to update target status.");
} finally {
setSavingJob(null);
}
}
return (
<section className="bg-muted h-full">
<div className="lg:container lg:mx-auto lg:px-0 flex flex-col items-center px-4">
@@ -151,6 +168,27 @@ export default function Jobs() {
</Select>
{isSaving && <Spinner className="size-3.5" />}
</div>
{s.target_status != null && (
<div className="mt-2 flex items-center gap-2">
<span className="text-xs text-muted-foreground">Marks tasks as:</span>
<Select
value={s.target_status}
disabled={isSaving}
onValueChange={(v) => handleTargetStatusChange(s.job_name, v)}
>
<SelectTrigger className="w-[180px] h-8 text-xs">
<SelectValue />
</SelectTrigger>
<SelectContent>
{TARGET_STATUS_OPTIONS.map((o) => (
<SelectItem key={o.value} value={o.value}>{o.label}</SelectItem>
))}
</SelectContent>
</Select>
{isSaving && <Spinner className="size-3.5" />}
</div>
)}
</SectionCard>
);
})}
@@ -137,7 +137,7 @@ export default function ViewTaskList() {
const navigate = useNavigate();
const { taskListId } = useParams();
const { fetchTaskList } = useAdminTask();
const { fmtDate } = useDateFormat();
const { fmtDateTime } = useDateFormat();
const [taskList, setTaskList] = useState(null);
@@ -275,7 +275,7 @@ export default function ViewTaskList() {
<span className="text-sm">
Deadline:{' '}
<span className="text-foreground font-medium">
{fmtDate(task.deadline)}
{fmtDateTime(task.deadline)}
</span>
</span>
</div>
@@ -15,13 +15,15 @@ import { RestoreDialog } from '@/components/generic/Dialogs/RestoreDialog';
import { Skeleton } from '@/components/ui/skeleton';
import { Button } from '@/components/ui/button';
import { Textarea } from '@/components/ui/textarea';
import { Switch } from '@/components/ui/switch';
import { Label } from '@/components/ui/label';
import {
AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent,
AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle,
} from '@/components/ui/alert-dialog';
import { House, NotebookPen, Users, Paperclip, Check, X } from 'lucide-react';
import AppBreadcrumb from '@/components/generic/Breadcrumb/AppBreadcrumb';
import { formatDate } from '@/utils/table.util';
import { formatDate, formatDateTime } from '@/utils/table.util';
import { getTimestamp } from '@/utils/timestamp.util';
import { buildDataColumns, columnPinning } from '@/modules/admin/config/task_list/task_completion/columns.config';
@@ -51,13 +53,14 @@ export default function TaskCompletions() {
task, taskList,
completions, completionPagination, setCompletionPagination, completionLoading,
completionAttributes,
fetchTask, fetchTaskList,
fetchTask, fetchTaskList, updateTask,
fetchCompletions, reviewSubmission,
archiveCompletion, restoreCompletion,
bulkArchiveCompletions, bulkRestoreCompletions,
} = useAdminTask();
const [showArchived, setShowArchived] = useState(false);
const [togglingSubmissions, setTogglingSubmissions] = useState(false);
const [archiveTarget, setArchiveTarget] = useState(null);
const [restoreTarget, setRestoreTarget] = useState(null);
const [bulkArchiveIds, setBulkArchiveIds] = useState(null);
@@ -78,6 +81,17 @@ export default function TaskCompletions() {
const handleRefsReady = (refs) => { tableRefsRef.current = refs; };
// ── Toggle accepting submissions ─────────────────────────────────────────
async function handleToggleAcceptingSubmissions(v) {
setTogglingSubmissions(true);
try {
await updateTask(taskListId, taskId, { accepts_submissions: v });
await fetchTask(taskListId, taskId);
} finally {
setTogglingSubmissions(false);
}
}
// ── Fetch handler ─────────────────────────────────────────────────────────
const handleFetch = useCallback((params) => {
return fetchCompletions(taskListId, taskId, params);
@@ -186,18 +200,28 @@ export default function TaskCompletions() {
{/* ── Detail card ───────────────────────────────────────────────── */}
<div className="bg-card border rounded-xl p-5 flex flex-col gap-4 w-full mb-6">
<div className="flex flex-col gap-1 min-w-0">
{task
? <h1 className="text-lg font-medium leading-none">{task.name}</h1>
: <Skeleton className="h-5 w-48" />
}
{task
? <p className="text-sm text-muted-foreground mt-1">
{taskList?.name ?? '—'}
{task.deadline ? ` · Due ${formatDate(task.deadline)}` : ''}
</p>
: <Skeleton className="h-4 w-72 mt-1" />
}
<div className="flex items-start justify-between gap-4">
<div className="flex flex-col gap-1 min-w-0">
{task
? <h1 className="text-lg font-medium leading-none">{task.name}</h1>
: <Skeleton className="h-5 w-48" />
}
{task
? <p className="text-sm text-muted-foreground mt-1">
{taskList?.name ?? '—'}
{task.deadline ? ` · Due ${formatDateTime(task.deadline)}` : ''}
</p>
: <Skeleton className="h-4 w-72 mt-1" />
}
</div>
<div className="flex items-center gap-2 shrink-0">
<Label className="text-sm text-muted-foreground">Accepting submissions</Label>
<Switch
checked={task?.accepts_submissions !== false}
disabled={!task || togglingSubmissions}
onCheckedChange={handleToggleAcceptingSubmissions}
/>
</div>
</div>
<div className="grid grid-cols-2 sm:grid-cols-4 gap-3">
@@ -21,7 +21,7 @@ import {
FileVideo, FileAudio, File, ArrowLeft,
Clock, CalendarDays, ExternalLink, NotebookPen,
} from 'lucide-react';
import { formatDate } from '@/utils/table.util';
import { formatDate, formatDateTime } from '@/utils/table.util';
// ─── Helpers ──────────────────────────────────────────────────────────────────
const getInitials = (name = '') =>
@@ -275,7 +275,7 @@ export default function ViewTaskCompletion() {
{task?.deadline && (
<span className="flex items-center gap-1.5 text-muted-foreground">
<CalendarDays className="size-3.5" />
Due {formatDate(task.deadline)}
Due {formatDateTime(task.deadline)}
</span>
)}
<span className="flex items-center gap-1.5 text-muted-foreground">