Add structured rejection reasons (checkable presets + free text)
The reject modal now offers 5 common rejection reasons as tickable options (no photo, incomplete info, looks fake/bot, inappropriate photos, contact info in profile), combined with an optional free-text note. The combined string is sent as the existing reason field — no backend change needed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,7 @@ import {
|
|||||||
Heart,
|
Heart,
|
||||||
Sparkles,
|
Sparkles,
|
||||||
Search,
|
Search,
|
||||||
|
Check,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { Card } from "@/components/ui/Card";
|
import { Card } from "@/components/ui/Card";
|
||||||
import { Avatar } from "@/components/ui/Avatar";
|
import { Avatar } from "@/components/ui/Avatar";
|
||||||
@@ -260,6 +261,15 @@ export default function UserReviewPage() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// دلایل رایج رد پروفایل — ادمین تیک میزنه، متن نهایی خودکار برای کاربر ساخته میشه
|
||||||
|
const COMMON_REASONS = [
|
||||||
|
"عکس پروفایل نداره یا واضح نیست",
|
||||||
|
"اطلاعات پروفایل ناقصه",
|
||||||
|
"پروفایل به نظر جعلی یا ربات میاد",
|
||||||
|
"محتوای عکسها نامناسبه",
|
||||||
|
"توی پروفایل شماره تماس یا لینک گذاشته",
|
||||||
|
];
|
||||||
|
|
||||||
function RejectModal({
|
function RejectModal({
|
||||||
open,
|
open,
|
||||||
onClose,
|
onClose,
|
||||||
@@ -271,14 +281,25 @@ function RejectModal({
|
|||||||
onConfirm: (reason: string) => void;
|
onConfirm: (reason: string) => void;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
}) {
|
}) {
|
||||||
const [reason, setReason] = useState("");
|
const [selected, setSelected] = useState<string[]>([]);
|
||||||
|
const [extra, setExtra] = useState("");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// eslint-disable-next-line react-hooks/set-state-in-effect
|
if (open) {
|
||||||
if (open) setReason("");
|
// eslint-disable-next-line react-hooks/set-state-in-effect
|
||||||
|
setSelected([]);
|
||||||
|
setExtra("");
|
||||||
|
}
|
||||||
}, [open]);
|
}, [open]);
|
||||||
|
|
||||||
const valid = reason.trim().length >= 3;
|
function toggle(text: string) {
|
||||||
|
setSelected((prev) =>
|
||||||
|
prev.includes(text) ? prev.filter((t) => t !== text) : [...prev, text]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const finalReason = [...selected, extra.trim()].filter(Boolean).join("؛ ");
|
||||||
|
const valid = finalReason.length >= 3;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal open={open} onClose={onClose} title="رد کردن کاربر">
|
<Modal open={open} onClose={onClose} title="رد کردن کاربر">
|
||||||
@@ -287,15 +308,49 @@ function RejectModal({
|
|||||||
<label className="text-[13px] font-semibold text-(--color-content-secondary) px-1">
|
<label className="text-[13px] font-semibold text-(--color-content-secondary) px-1">
|
||||||
دلیل رد (برای کاربر نمایش داده میشه)
|
دلیل رد (برای کاربر نمایش داده میشه)
|
||||||
</label>
|
</label>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
{COMMON_REASONS.map((text) => {
|
||||||
|
const checked = selected.includes(text);
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={text}
|
||||||
|
type="button"
|
||||||
|
onClick={() => toggle(text)}
|
||||||
|
className={`flex items-center gap-2.5 rounded-(--radius-sm) border-2 px-3.5 py-2.5 text-right text-[13px] font-medium transition-colors ${
|
||||||
|
checked
|
||||||
|
? "border-(--color-brand-500) bg-(--color-surface-interactive) text-(--color-brand-700)"
|
||||||
|
: "border-(--color-border-default) text-(--color-content-secondary)"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
className={`h-5 w-5 rounded-(--radius-xs) flex items-center justify-center shrink-0 border-2 ${
|
||||||
|
checked
|
||||||
|
? "bg-(--color-brand-500) border-(--color-brand-500)"
|
||||||
|
: "border-(--color-border-default)"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{checked && <Check size={13} className="text-white" strokeWidth={3} />}
|
||||||
|
</span>
|
||||||
|
{text}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-1.5">
|
||||||
|
<label className="text-[13px] font-semibold text-(--color-content-secondary) px-1">
|
||||||
|
توضیح تکمیلی (اختیاری)
|
||||||
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
value={reason}
|
value={extra}
|
||||||
onChange={(e) => setReason(e.target.value)}
|
onChange={(e) => setExtra(e.target.value)}
|
||||||
rows={4}
|
rows={2}
|
||||||
placeholder="مثلاً: عکس پروفایل نداره یا اطلاعات ناقصه"
|
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"
|
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"
|
||||||
autoFocus
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-end gap-2 pt-2">
|
<div className="flex justify-end gap-2 pt-2">
|
||||||
<Button variant="ghost" size="sm" onClick={onClose}>
|
<Button variant="ghost" size="sm" onClick={onClose}>
|
||||||
انصراف
|
انصراف
|
||||||
@@ -305,7 +360,7 @@ function RejectModal({
|
|||||||
isLoading={loading}
|
isLoading={loading}
|
||||||
disabled={!valid}
|
disabled={!valid}
|
||||||
className="!bg-(--color-status-error) !shadow-none hover:!bg-red-700"
|
className="!bg-(--color-status-error) !shadow-none hover:!bg-red-700"
|
||||||
onClick={() => onConfirm(reason.trim())}
|
onClick={() => onConfirm(finalReason)}
|
||||||
>
|
>
|
||||||
رد کردن کاربر
|
رد کردن کاربر
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
Reference in New Issue
Block a user