Migrate tickets and CFH lists to DataTable; confirm destructive mod actions.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
SimoandCursor committed 2026-07-17 20:37:50 +02:00
1 parent cbdea70f83
commit 06610e9ae3
7 files changed
+622 -414

No files matched your search

@@ -19,6 +19,7 @@ import {
quickRoomKick,
quickUnmute,
} from "@/actions/moderation";
import { useConfirmDialog } from "@/components/admin/confirm-dialog";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
@@ -27,24 +28,65 @@ import { useServerAction } from "@/hooks/use-server-action";
export function ModActionsClient() {
const { run, isPending } = useServerAction();
const { confirm, dialog: confirmDialog } = useConfirmDialog();
// User actions
const [userId, setUserId] = useState("");
const [muteDuration, setMuteDuration] = useState("10");
const [alertMessage, setAlertMessage] = useState("");
// Room actions
const [roomId, setRoomId] = useState("");
// Broadcast
const [broadcastMsg, setBroadcastMsg] = useState("");
const uid = parseInt(userId, 10);
const rid = parseInt(roomId, 10);
async function handleKick() {
const ok = await confirm({
title: "Kick user",
description: `Disconnect user #${uid}?`,
confirmLabel: "Kick",
});
if (!ok) return;
run(() => quickKick({ userId: uid }), {
successMessage: "User disconnected",
});
}
async function handleRoomKick() {
const ok = await confirm({
title: "Kick all from room",
description: `Kick every user from room #${rid}?`,
confirmLabel: "Kick all",
});
if (!ok) return;
run(() => quickRoomKick({ roomId: rid }), {
successMessage: "All users kicked from room",
});
}
async function handleHotelBroadcast() {
const ok = await confirm({
title: "Hotel-wide alert",
description: "Send this alert to everyone in the hotel?",
confirmLabel: "Send",
variant: "default",
});
if (!ok) return;
run(
() =>
broadcastAlert({
message: broadcastMsg.trim(),
type: "hotel",
}),
{
successMessage: "Hotel alert sent!",
onSuccess: () => setBroadcastMsg(""),
},
);
}
return (
<div className="grid gap-6 lg:grid-cols-2">
{/* User Actions */}
{confirmDialog}
<Card>
<CardHeader>
<CardTitle className="text-sm flex items-center gap-2">
@@ -70,11 +112,7 @@ export function ModActionsClient() {
variant="destructive"
size="sm"
disabled={isPending || !uid}
onClick={() =>
run(() => quickKick({ userId: uid }), {
successMessage: "User disconnected",
})
}
onClick={handleKick}
>
{isPending ? (
<Loader2 className="mr-1 h-3 w-3 animate-spin" />
@@ -159,7 +197,6 @@ export function ModActionsClient() {
</CardContent>
</Card>
{/* Room Actions */}
<div className="space-y-6">
<Card>
<CardHeader>
@@ -185,18 +222,13 @@ export function ModActionsClient() {
size="sm"
className="w-full"
disabled={isPending || !rid}
onClick={() =>
run(() => quickRoomKick({ roomId: rid }), {
successMessage: "All users kicked from room",
})
}
onClick={handleRoomKick}
>
<Zap className="mr-1 h-3 w-3" /> Kick All From Room
</Button>
</CardContent>
</Card>
{/* Broadcast */}
<Card>
<CardHeader>
<CardTitle className="text-sm flex items-center gap-2">
@@ -216,19 +248,7 @@ export function ModActionsClient() {
<Button
size="sm"
disabled={isPending || !broadcastMsg.trim()}
onClick={() => {
run(
() =>
broadcastAlert({
message: broadcastMsg.trim(),
type: "hotel",
}),
{
successMessage: "Hotel alert sent!",
onSuccess: () => setBroadcastMsg(""),
},
);
}}
onClick={handleHotelBroadcast}
>
<Megaphone className="mr-1 h-3 w-3" /> Hotel Alert
</Button>