186 lines
4.3 KiB
TypeScript
186 lines
4.3 KiB
TypeScript
"use client";
|
|
import { useState } from "react";
|
|
import { toast } from "sonner";
|
|
import {
|
|
bulkAdjustCurrency,
|
|
bulkBan,
|
|
bulkGiveBadge,
|
|
bulkUnban,
|
|
} from "@/actions/bulk-users";
|
|
|
|
type PendingDialog =
|
|
| { kind: "ban"; ids: number[] }
|
|
| { kind: "give-currency"; ids: number[] }
|
|
| { kind: "give-badge"; ids: number[] }
|
|
| null;
|
|
|
|
export function useUserBulkActions() {
|
|
const [pending, setPending] = useState<PendingDialog>(null);
|
|
const [busy, setBusy] = useState(false);
|
|
const [banReason, setBanReason] = useState("Bulk ban");
|
|
const [banDuration, setBanDuration] = useState("-1");
|
|
const [currencyType, setCurrencyType] = useState<
|
|
"credits" | "pixels" | "points"
|
|
>("credits");
|
|
const [currencyAmount, setCurrencyAmount] = useState("100");
|
|
const [badgeCode, setBadgeCode] = useState("");
|
|
|
|
function reportResult(
|
|
label: string,
|
|
ok: number,
|
|
total: number,
|
|
failures = 0,
|
|
) {
|
|
if (ok > 0)
|
|
toast.success(
|
|
`${label}: ${ok}/${total}${failures ? ` · ${failures} failed` : ""}`,
|
|
);
|
|
if (failures > 0 && ok === 0)
|
|
toast.error(`${label} failed for ${failures} users`);
|
|
}
|
|
|
|
async function handleBulkAction(action: string, ids: (number | string)[]) {
|
|
if (busy) return;
|
|
const numericIds = ids.map((i) => Number(i));
|
|
|
|
if (action === "unban") {
|
|
setBusy(true);
|
|
try {
|
|
const result = await bulkUnban({ userIds: numericIds });
|
|
if (result.ok) {
|
|
const data = result.data as { unbanned: number; total: number };
|
|
reportResult("Unbanned", data.unbanned, data.total);
|
|
} else toast.error(result.error);
|
|
} catch {
|
|
toast.error(
|
|
"Network error. The outcome is uncertain; check the user before retrying.",
|
|
);
|
|
} finally {
|
|
setBusy(false);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (action === "ban") {
|
|
setPending({ kind: "ban", ids: numericIds });
|
|
return;
|
|
}
|
|
|
|
if (action === "give-currency") {
|
|
setPending({ kind: "give-currency", ids: numericIds });
|
|
return;
|
|
}
|
|
|
|
if (action === "give-badge") {
|
|
setPending({ kind: "give-badge", ids: numericIds });
|
|
return;
|
|
}
|
|
}
|
|
|
|
async function confirmBan() {
|
|
if (pending?.kind !== "ban") return;
|
|
setBusy(true);
|
|
try {
|
|
const result = await bulkBan({
|
|
userIds: pending.ids,
|
|
reason: banReason,
|
|
duration: Number(banDuration),
|
|
});
|
|
if (result.ok) {
|
|
const data = result.data as { banned: number };
|
|
reportResult("Banned", data.banned, pending.ids.length);
|
|
setPending(null);
|
|
} else toast.error(result.error);
|
|
} catch {
|
|
toast.error(
|
|
"Network error. The outcome is uncertain; check the user before retrying.",
|
|
);
|
|
} finally {
|
|
setBusy(false);
|
|
}
|
|
}
|
|
|
|
async function confirmGiveCurrency() {
|
|
if (pending?.kind !== "give-currency") return;
|
|
setBusy(true);
|
|
try {
|
|
const result = await bulkAdjustCurrency({
|
|
userIds: pending.ids,
|
|
amount: Number(currencyAmount),
|
|
type: currencyType,
|
|
});
|
|
if (result.ok) {
|
|
const data = result.data as {
|
|
adjusted: number;
|
|
total: number;
|
|
failedIds: { userId: number; reason: string }[];
|
|
};
|
|
reportResult(
|
|
`Adjusted ${currencyAmount} ${currencyType}`,
|
|
data.adjusted,
|
|
data.total,
|
|
data.failedIds.length,
|
|
);
|
|
setPending(null);
|
|
} else toast.error(result.error);
|
|
} catch {
|
|
toast.error(
|
|
"Network error. The outcome is uncertain; check the user before retrying.",
|
|
);
|
|
} finally {
|
|
setBusy(false);
|
|
}
|
|
}
|
|
|
|
async function confirmGiveBadge() {
|
|
if (pending?.kind !== "give-badge") return;
|
|
setBusy(true);
|
|
try {
|
|
const result = await bulkGiveBadge({
|
|
userIds: pending.ids,
|
|
badgeCode: badgeCode.trim().toUpperCase(),
|
|
});
|
|
if (result.ok) {
|
|
const data = result.data as {
|
|
given: number;
|
|
total: number;
|
|
failedIds: { userId: number; reason: string }[];
|
|
};
|
|
reportResult(
|
|
`Given badge ${badgeCode}`,
|
|
data.given,
|
|
data.total,
|
|
data.failedIds.length,
|
|
);
|
|
setPending(null);
|
|
} else toast.error(result.error);
|
|
} catch {
|
|
toast.error(
|
|
"Network error. The outcome is uncertain; check the user before retrying.",
|
|
);
|
|
} finally {
|
|
setBusy(false);
|
|
}
|
|
}
|
|
|
|
return {
|
|
pending,
|
|
setPending,
|
|
busy,
|
|
banReason,
|
|
setBanReason,
|
|
banDuration,
|
|
setBanDuration,
|
|
currencyType,
|
|
setCurrencyType,
|
|
currencyAmount,
|
|
setCurrencyAmount,
|
|
badgeCode,
|
|
setBadgeCode,
|
|
handleBulkAction,
|
|
confirmBan,
|
|
confirmGiveCurrency,
|
|
confirmGiveBadge,
|
|
};
|
|
}
|