201 lines
6.2 KiB
JavaScript
201 lines
6.2 KiB
JavaScript
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
import { usePaginatedQuery } from "@/shared/hooks/usePaginatedQuery.js";
|
|
import { useToast } from "@/shared/ui/ToastProvider.jsx";
|
|
import { emptyUserIdentityFilter, userIdentityFilterParams } from "@/shared/ui/userIdentityFilter.js";
|
|
import { listResourceGroups } from "@/features/resources/api";
|
|
import {
|
|
getRegistrationRewardConfig,
|
|
listRegistrationRewardClaims,
|
|
updateRegistrationRewardConfig,
|
|
} from "@/features/registration-reward/api";
|
|
import { useRegistrationRewardAbilities } from "@/features/registration-reward/permissions.js";
|
|
|
|
const pageSize = 50;
|
|
const emptyClaims = { items: [], page: 1, pageSize, todayClaimedCount: 0, total: 0 };
|
|
const emptyTimeRange = { endMs: "", startMs: "" };
|
|
|
|
const emptyForm = {
|
|
coinAmount: "",
|
|
dailyLimit: "0",
|
|
enabled: false,
|
|
resourceGroupId: "",
|
|
rewardType: "coin",
|
|
};
|
|
|
|
export function useRegistrationRewardPage() {
|
|
const abilities = useRegistrationRewardAbilities();
|
|
const { showToast } = useToast();
|
|
const [config, setConfig] = useState(null);
|
|
const [configDrawerOpen, setConfigDrawerOpen] = useState(false);
|
|
const [form, setForm] = useState(emptyForm);
|
|
const [configLoading, setConfigLoading] = useState(false);
|
|
const [configSaving, setConfigSaving] = useState(false);
|
|
const [resourceGroups, setResourceGroups] = useState([]);
|
|
const [userFilter, setUserFilter] = useState(emptyUserIdentityFilter);
|
|
const [status, setStatus] = useState("");
|
|
const [timeRange, setTimeRange] = useState(emptyTimeRange);
|
|
const [page, setPage] = useState(1);
|
|
const filters = useMemo(
|
|
() => ({
|
|
claimed_end_ms: timeRange.endMs || undefined,
|
|
claimed_start_ms: timeRange.startMs || undefined,
|
|
status,
|
|
...userIdentityFilterParams(userFilter),
|
|
}),
|
|
[status, timeRange, userFilter],
|
|
);
|
|
const {
|
|
data: claims = emptyClaims,
|
|
error: claimsError,
|
|
loading: claimsLoading,
|
|
reload: reloadClaims,
|
|
} = usePaginatedQuery({
|
|
errorMessage: "加载领取记录失败",
|
|
fetcher: listRegistrationRewardClaims,
|
|
filters,
|
|
page,
|
|
pageSize,
|
|
queryKey: ["registration-reward-claims", filters, page],
|
|
});
|
|
|
|
const reloadConfig = useCallback(async () => {
|
|
setConfigLoading(true);
|
|
try {
|
|
const [config, groups] = await Promise.all([
|
|
getRegistrationRewardConfig(),
|
|
listResourceGroups({ page: 1, page_size: 100, status: "active" }),
|
|
]);
|
|
setConfig(config);
|
|
setForm(formFromConfig(config));
|
|
setResourceGroups(groups.items || []);
|
|
} catch (err) {
|
|
showToast(err.message || "加载注册奖励配置失败", "error");
|
|
} finally {
|
|
setConfigLoading(false);
|
|
}
|
|
}, [showToast]);
|
|
|
|
useEffect(() => {
|
|
void reloadConfig();
|
|
}, [reloadConfig]);
|
|
|
|
const openConfigDrawer = useCallback(() => {
|
|
if (config) {
|
|
setForm(formFromConfig(config));
|
|
}
|
|
setConfigDrawerOpen(true);
|
|
}, [config]);
|
|
|
|
const closeConfigDrawer = useCallback(() => {
|
|
setConfigDrawerOpen(false);
|
|
if (config) {
|
|
setForm(formFromConfig(config));
|
|
return;
|
|
}
|
|
setForm(emptyForm);
|
|
}, [config]);
|
|
|
|
const changeUserFilter = (value) => {
|
|
setUserFilter(value);
|
|
setPage(1);
|
|
};
|
|
|
|
const changeStatus = (value) => {
|
|
setStatus(value);
|
|
setPage(1);
|
|
};
|
|
|
|
const changeTimeRange = (value) => {
|
|
setTimeRange(value || emptyTimeRange);
|
|
setPage(1);
|
|
};
|
|
|
|
const resetFilters = () => {
|
|
setUserFilter(emptyUserIdentityFilter);
|
|
setStatus("");
|
|
setTimeRange(emptyTimeRange);
|
|
setPage(1);
|
|
};
|
|
|
|
const submitConfig = async (event) => {
|
|
event.preventDefault();
|
|
if (!abilities.canUpdate) {
|
|
return;
|
|
}
|
|
setConfigSaving(true);
|
|
try {
|
|
const saved = await updateRegistrationRewardConfig(payloadFromForm(form));
|
|
setConfig(saved);
|
|
setForm(formFromConfig(saved));
|
|
setConfigDrawerOpen(false);
|
|
showToast("注册奖励配置已保存", "success");
|
|
await reloadClaims();
|
|
} catch (err) {
|
|
showToast(err.message || "保存注册奖励配置失败", "error");
|
|
} finally {
|
|
setConfigSaving(false);
|
|
}
|
|
};
|
|
|
|
return {
|
|
abilities,
|
|
changeStatus,
|
|
changeTimeRange,
|
|
changeUserFilter,
|
|
claims,
|
|
claimsError,
|
|
claimsLoading,
|
|
closeConfigDrawer,
|
|
config,
|
|
configDrawerOpen,
|
|
configLoading,
|
|
configSaving,
|
|
form,
|
|
openConfigDrawer,
|
|
page,
|
|
reloadClaims,
|
|
reloadConfig,
|
|
resetFilters,
|
|
resourceGroups,
|
|
setForm,
|
|
setPage,
|
|
status,
|
|
submitConfig,
|
|
timeRange,
|
|
userFilter,
|
|
};
|
|
}
|
|
|
|
function formFromConfig(config) {
|
|
return {
|
|
coinAmount: config.coinAmount ? String(config.coinAmount) : "",
|
|
dailyLimit: String(config.dailyLimit || 0),
|
|
enabled: Boolean(config.enabled),
|
|
resourceGroupId: config.resourceGroupId ? String(config.resourceGroupId) : "",
|
|
rewardType: config.rewardType || "coin",
|
|
};
|
|
}
|
|
|
|
function payloadFromForm(form) {
|
|
const rewardType = form.rewardType === "resource_group" ? "resource_group" : "coin";
|
|
const coinAmount = Number(form.coinAmount || 0);
|
|
const resourceGroupId = Number(form.resourceGroupId || 0);
|
|
const dailyLimit = Number(form.dailyLimit || 0);
|
|
if (dailyLimit < 0) {
|
|
throw new Error("每日限制不能小于 0");
|
|
}
|
|
if (rewardType === "coin" && coinAmount <= 0) {
|
|
throw new Error("金币奖励必须大于 0");
|
|
}
|
|
if (rewardType === "resource_group" && resourceGroupId <= 0) {
|
|
throw new Error("请选择资源组");
|
|
}
|
|
return {
|
|
coin_amount: rewardType === "coin" ? coinAmount : 0,
|
|
daily_limit: dailyLimit,
|
|
enabled: Boolean(form.enabled),
|
|
resource_group_id: rewardType === "resource_group" ? resourceGroupId : 0,
|
|
reward_type: rewardType,
|
|
};
|
|
}
|