feat(admin/users): bannedReason textarea on user detail page
Adds a Textarea below the ban Switch that lets the admin write the reason shown to the mobile user in the BannedModal. The reason is persisted on blur via PATCH /api/users/[id] (existing route), and only rendered when the user is currently banned to keep the UI tight. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
792e969c00
commit
af767879e3
|
|
@ -1,11 +1,14 @@
|
|||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { ArrowLeft, ScanLine, Trophy, Zap, Calendar } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
|
|
@ -45,6 +48,7 @@ const SEVERITY_STYLES: Record<string, string> = {
|
|||
|
||||
export default function UserDetailClient({ user }: UserDetailProps) {
|
||||
const router = useRouter();
|
||||
const [reasonDraft, setReasonDraft] = useState(user.bannedReason ?? "");
|
||||
|
||||
async function handleUpdate(data: Record<string, unknown>) {
|
||||
try {
|
||||
|
|
@ -61,6 +65,13 @@ export default function UserDetailClient({ user }: UserDetailProps) {
|
|||
}
|
||||
}
|
||||
|
||||
async function handleReasonBlur() {
|
||||
const trimmed = reasonDraft.trim();
|
||||
const current = user.bannedReason ?? "";
|
||||
if (trimmed === current) return;
|
||||
await handleUpdate({ bannedReason: trimmed.length > 0 ? trimmed : null });
|
||||
}
|
||||
|
||||
const STAT_ITEMS = [
|
||||
{ label: "Scans", value: user._count.scans, icon: ScanLine, color: "text-vine" },
|
||||
{ label: "XP", value: user.xp, icon: Zap, color: "text-gold" },
|
||||
|
|
@ -164,6 +175,26 @@ export default function UserDetailClient({ user }: UserDetailProps) {
|
|||
onCheckedChange={(banned) => handleUpdate({ banned })}
|
||||
/>
|
||||
</div>
|
||||
{user.banned && (
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="bannedReason" className="text-[12px] text-stone-400">
|
||||
Raison du bannissement
|
||||
</Label>
|
||||
<Textarea
|
||||
id="bannedReason"
|
||||
value={reasonDraft}
|
||||
onChange={(e) => setReasonDraft(e.target.value)}
|
||||
onBlur={handleReasonBlur}
|
||||
placeholder="Visible par l'utilisateur sur mobile"
|
||||
maxLength={500}
|
||||
rows={3}
|
||||
className="bg-[oklch(0.12_0.005_60)] border-[oklch(0.22_0.005_60)] text-cream"
|
||||
/>
|
||||
<p className="text-[11px] text-stone-600">
|
||||
Affichee dans le mobile au prochain app boot. Maxi 500 caracteres.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Scan history */}
|
||||
|
|
|
|||
Loading…
Reference in a new issue