import { useMemo, useState } from "react"; import { parseForm } from "@/shared/forms/validation"; import { usePaginatedQuery } from "@/shared/hooks/usePaginatedQuery.js"; import { useToast } from "@/shared/ui/ToastProvider.jsx"; import { createTaskDefinition, listTaskDefinitions, setTaskDefinitionStatus, updateTaskDefinition, } from "@/features/daily-tasks/api"; import { useDailyTaskAbilities } from "@/features/daily-tasks/permissions.js"; import { dailyTaskFormSchema } from "@/features/daily-tasks/schema"; const pageSize = 50; const emptyData = { items: [], page: 1, pageSize, total: 0 }; const emptyForm = (task = {}) => ({ category: task.category || "", description: task.description || "", effectiveFrom: msToDatetimeLocal(task.effectiveFromMs), effectiveTo: msToDatetimeLocal(task.effectiveToMs), metricType: task.metricType || "", rewardCoinAmount: task.rewardCoinAmount === 0 || task.rewardCoinAmount ? String(task.rewardCoinAmount) : "", sortOrder: task.sortOrder === 0 || task.sortOrder ? String(task.sortOrder) : "0", status: task.status || "draft", targetUnit: task.targetUnit || "count", targetValue: task.targetValue === 0 || task.targetValue ? String(task.targetValue) : "", taskType: task.taskType || "daily", title: task.title || "", }); export function useDailyTasksPage() { const abilities = useDailyTaskAbilities(); const { showToast } = useToast(); const [query, setQuery] = useState(""); const [status, setStatus] = useState(""); const [taskType, setTaskType] = useState(""); const [category, setCategory] = useState(""); const [page, setPage] = useState(1); const [activeAction, setActiveAction] = useState(""); const [selectedTask, setSelectedTask] = useState(null); const [form, setForm] = useState(emptyForm); const [loadingAction, setLoadingAction] = useState(""); const filters = useMemo( () => ({ category, keyword: query, status, task_type: taskType, }), [category, query, status, taskType], ); const { data = emptyData, error, loading, reload, } = usePaginatedQuery({ errorMessage: "加载每日任务失败", fetcher: listTaskDefinitions, filters, page, pageSize, queryKey: ["daily-tasks", filters, page], }); const changeQuery = resetSetter(setQuery, setPage); const changeStatus = resetSetter(setStatus, setPage); const changeTaskType = resetSetter(setTaskType, setPage); const changeCategory = resetSetter(setCategory, setPage); const resetFilters = () => { setQuery(""); setStatus(""); setTaskType(""); setCategory(""); setPage(1); }; const openCreate = () => { setSelectedTask(null); setForm(emptyForm()); setActiveAction("create"); }; const openEdit = (task) => { if (!task?.taskId) { return; } setSelectedTask(task); setForm(emptyForm(task)); setActiveAction("edit"); }; const closeAction = () => { setActiveAction(""); setSelectedTask(null); }; const submitTask = async (event) => { event.preventDefault(); const editing = activeAction === "edit"; if (editing && !selectedTask?.taskId) { return; } await runAction(editing ? "edit" : "create", editing ? "任务已更新" : "任务已创建", async () => { const payload = buildTaskPayload(parseForm(dailyTaskFormSchema, form)); if (editing) { await updateTaskDefinition(selectedTask.taskId, payload); } else { await createTaskDefinition(payload); } closeAction(); setForm(emptyForm()); await reload(); }); }; const toggleTask = async (task, nextEnabled = task.status !== "active") => { if (!abilities.canStatus || !task?.taskId || task.status === "archived") { return; } const nextStatus = nextEnabled ? "active" : "paused"; if (task.status === nextStatus) { return; } await runAction(`status-${task.taskId}`, nextEnabled ? "任务已启用" : "任务已暂停", async () => { await setTaskDefinitionStatus(task.taskId, nextStatus); await reload(); }); }; const runAction = async (action, successMessage, submitter) => { setLoadingAction(action); try { await submitter(); showToast(successMessage, "success"); } catch (err) { showToast(err.message || "操作失败", "error"); } finally { setLoadingAction(""); } }; return { abilities, activeAction, category, changeCategory, changeQuery, changeStatus, changeTaskType, closeAction, data, error, form, loading, loadingAction, openCreate, openEdit, page, query, reload, resetFilters, selectedTask, setForm, setPage, status, submitTask, taskType, toggleTask, }; } function buildTaskPayload(form) { return { category: form.category.trim(), description: form.description.trim(), effective_from_ms: datetimeLocalToMs(form.effectiveFrom), effective_to_ms: datetimeLocalToMs(form.effectiveTo), metric_type: form.metricType.trim(), reward_coin_amount: Number(form.rewardCoinAmount), sort_order: Number(form.sortOrder || 0), status: form.status, target_unit: form.targetUnit, target_value: Number(form.targetValue), task_type: form.taskType, title: form.title.trim(), }; } function resetSetter(setter, setPage) { return (value) => { setter(value); setPage(1); }; } function msToDatetimeLocal(value) { if (!value) { return ""; } const date = new Date(Number(value)); if (Number.isNaN(date.getTime())) { return ""; } const local = new Date(date.getTime() - date.getTimezoneOffset() * 60000); return local.toISOString().slice(0, 16); } function datetimeLocalToMs(value) { const trimmed = String(value || "").trim(); if (!trimmed) { return 0; } const parsed = new Date(trimmed).getTime(); return Number.isFinite(parsed) ? parsed : 0; }