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>
|
|||
|
|
</>
|
|||
|
|
);
|
|||
|
|
}
|