75a0ba593d
git-subtree-dir: admin git-subtree-mainline:35f5084005git-subtree-split:9f1eccd2f7
163 lines
5.6 KiB
TypeScript
163 lines
5.6 KiB
TypeScript
"use client";
|
||
|
||
import { useState } from "react";
|
||
import { ShieldAlert, ShieldCheck, Flag } from "lucide-react";
|
||
import { Button } from "@/components/ui/Button";
|
||
import { Modal } from "@/components/ui/Modal";
|
||
import { useToast } from "@/components/ui/Toast";
|
||
import { formatApiError } from "@/lib/hooks";
|
||
import { blockUser, unblockUser, reportUser, UserState } from "@/lib/api";
|
||
|
||
// دکمههای بلاک/آنبلاک/گزارش یک کاربر؛ هم توی لیست همهی کاربران و هم توی
|
||
// صفحهی جزئیات یک کاربر استفاده میشه.
|
||
export function UserModerationActions({
|
||
userId,
|
||
state,
|
||
onChanged,
|
||
}: {
|
||
userId: number;
|
||
state: UserState;
|
||
onChanged: () => void;
|
||
}) {
|
||
const toast = useToast();
|
||
const [blockOpen, setBlockOpen] = useState(false);
|
||
const [reportOpen, setReportOpen] = useState(false);
|
||
const [blockReason, setBlockReason] = useState("");
|
||
const [reportReason, setReportReason] = useState("");
|
||
const [loading, setLoading] = useState(false);
|
||
|
||
async function handleBlock() {
|
||
if (blockReason.trim().length < 3) return;
|
||
setLoading(true);
|
||
try {
|
||
await blockUser(userId, blockReason.trim());
|
||
toast.showSuccess("کاربر بلاک شد");
|
||
setBlockOpen(false);
|
||
setBlockReason("");
|
||
onChanged();
|
||
} catch (err) {
|
||
toast.showError(formatApiError(err));
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
}
|
||
|
||
async function handleUnblock() {
|
||
setLoading(true);
|
||
try {
|
||
await unblockUser(userId);
|
||
toast.showSuccess("کاربر آنبلاک شد");
|
||
onChanged();
|
||
} catch (err) {
|
||
toast.showError(formatApiError(err));
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
}
|
||
|
||
async function handleReport() {
|
||
if (reportReason.trim().length < 3) return;
|
||
setLoading(true);
|
||
try {
|
||
await reportUser(userId, reportReason.trim());
|
||
toast.showSuccess("گزارش ثبت شد");
|
||
setReportOpen(false);
|
||
setReportReason("");
|
||
onChanged();
|
||
} catch (err) {
|
||
toast.showError(formatApiError(err));
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
}
|
||
|
||
return (
|
||
<>
|
||
<div className="flex items-center gap-2">
|
||
{state === "blocked" ? (
|
||
<Button
|
||
variant="tertiary"
|
||
size="sm"
|
||
icon={<ShieldCheck size={14} />}
|
||
isLoading={loading}
|
||
onClick={handleUnblock}
|
||
>
|
||
آنبلاک
|
||
</Button>
|
||
) : (
|
||
<Button
|
||
variant="tertiary"
|
||
size="sm"
|
||
icon={<ShieldAlert size={14} />}
|
||
className="!text-(--color-status-error) !border-red-200"
|
||
onClick={() => setBlockOpen(true)}
|
||
>
|
||
بلاک
|
||
</Button>
|
||
)}
|
||
<Button variant="ghost" size="sm" icon={<Flag size={14} />} onClick={() => setReportOpen(true)}>
|
||
گزارش
|
||
</Button>
|
||
</div>
|
||
|
||
<Modal open={blockOpen} onClose={() => setBlockOpen(false)} title="بلاک کردن کاربر">
|
||
<div className="flex flex-col gap-4">
|
||
<p className="text-[13px] text-(--color-content-secondary)">
|
||
کاربر بلاکشده دیگه نمیتونه وارد اپ بشه (حتی با توکن معتبر فعلیش).
|
||
</p>
|
||
<textarea
|
||
value={blockReason}
|
||
onChange={(e) => setBlockReason(e.target.value)}
|
||
rows={3}
|
||
placeholder="دلیل بلاک کردن..."
|
||
className="rounded-(--radius-sm) border-2 border-(--color-border-default) bg-(--color-surface-default) px-4 py-3 text-[14px] focus:outline-none focus:border-(--color-border-interactive) focus:ring-4 focus:ring-(--color-brand-100) resize-none"
|
||
/>
|
||
<div className="flex justify-end gap-2">
|
||
<Button variant="ghost" size="sm" onClick={() => setBlockOpen(false)}>
|
||
انصراف
|
||
</Button>
|
||
<Button
|
||
size="sm"
|
||
isLoading={loading}
|
||
disabled={blockReason.trim().length < 3}
|
||
className="!bg-(--color-status-error) !shadow-none hover:!bg-red-700"
|
||
onClick={handleBlock}
|
||
>
|
||
بلاک کن
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
</Modal>
|
||
|
||
<Modal open={reportOpen} onClose={() => setReportOpen(false)} title="ثبت گزارش روی کاربر">
|
||
<div className="flex flex-col gap-4">
|
||
<p className="text-[13px] text-(--color-content-secondary)">
|
||
یک یادداشت نظارتی برای این کاربر ثبت میشه (مستقل از بلاک کردن)؛ برای پیگیری
|
||
سابقهی شکایتها.
|
||
</p>
|
||
<textarea
|
||
value={reportReason}
|
||
onChange={(e) => setReportReason(e.target.value)}
|
||
rows={3}
|
||
placeholder="دلیل گزارش..."
|
||
className="rounded-(--radius-sm) border-2 border-(--color-border-default) bg-(--color-surface-default) px-4 py-3 text-[14px] focus:outline-none focus:border-(--color-border-interactive) focus:ring-4 focus:ring-(--color-brand-100) resize-none"
|
||
/>
|
||
<div className="flex justify-end gap-2">
|
||
<Button variant="ghost" size="sm" onClick={() => setReportOpen(false)}>
|
||
انصراف
|
||
</Button>
|
||
<Button
|
||
size="sm"
|
||
isLoading={loading}
|
||
disabled={reportReason.trim().length < 3}
|
||
onClick={handleReport}
|
||
>
|
||
ثبت گزارش
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
</Modal>
|
||
</>
|
||
);
|
||
}
|