Added 'Back to Dashboard' buttons to all admin dashboard pages for improved navigation. Introduced AbortSignal timeouts to API client and middleware requests to prevent hanging network calls. Also enabled messaging customers from the order details page if Telegram info is available.
289 lines
11 KiB
TypeScript
289 lines
11 KiB
TypeScript
"use client";
|
|
|
|
import React from "react";
|
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Input } from "@/components/ui/input";
|
|
import { Label } from "@/components/ui/label";
|
|
import { Textarea } from "@/components/ui/textarea";
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
|
import { Badge } from "@/components/ui/badge";
|
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
|
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@/components/ui/alert-dialog";
|
|
import { UserX, Shield, Search, Ban, Unlock } from "lucide-react";
|
|
import { useState } from "react";
|
|
import Link from "next/link";
|
|
|
|
export default function AdminBanPage() {
|
|
const [banData, setBanData] = useState({
|
|
username: "",
|
|
reason: "",
|
|
duration: "",
|
|
description: ""
|
|
});
|
|
|
|
// Mock data for banned users
|
|
const bannedUsers = [
|
|
{
|
|
id: "1",
|
|
username: "spam_user",
|
|
email: "spam@example.com",
|
|
reason: "Spam",
|
|
bannedBy: "admin1",
|
|
banDate: "2024-01-15",
|
|
duration: "Permanent",
|
|
status: "active"
|
|
},
|
|
{
|
|
id: "2",
|
|
username: "fraud_vendor",
|
|
email: "fraud@example.com",
|
|
reason: "Fraud",
|
|
bannedBy: "admin1",
|
|
banDate: "2024-01-20",
|
|
duration: "30 days",
|
|
status: "active"
|
|
},
|
|
{
|
|
id: "3",
|
|
username: "policy_violator",
|
|
email: "violator@example.com",
|
|
reason: "Policy Violation",
|
|
bannedBy: "admin1",
|
|
banDate: "2024-01-25",
|
|
duration: "7 days",
|
|
status: "expired"
|
|
}
|
|
];
|
|
|
|
const handleBanUser = () => {
|
|
// Handle ban user logic
|
|
console.log("Banning user:", banData);
|
|
};
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<h1 className="text-2xl font-semibold tracking-tight">Ban Users</h1>
|
|
<p className="text-sm text-muted-foreground mt-1">Manage user bans and suspensions</p>
|
|
</div>
|
|
<Button asChild variant="outline" size="sm">
|
|
<Link href="/dashboard">Back to Dashboard</Link>
|
|
</Button>
|
|
</div>
|
|
|
|
{/* Stats Cards */}
|
|
<div className="grid gap-4 md:grid-cols-4">
|
|
<Card>
|
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
<CardTitle className="text-sm font-medium">Active Bans</CardTitle>
|
|
<Shield className="h-4 w-4 text-muted-foreground" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="text-2xl font-bold">12</div>
|
|
<p className="text-xs text-muted-foreground">Currently banned</p>
|
|
</CardContent>
|
|
</Card>
|
|
<Card>
|
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
<CardTitle className="text-sm font-medium">Permanent Bans</CardTitle>
|
|
<UserX className="h-4 w-4 text-muted-foreground" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="text-2xl font-bold">3</div>
|
|
<p className="text-xs text-muted-foreground">Permanent suspensions</p>
|
|
</CardContent>
|
|
</Card>
|
|
<Card>
|
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
<CardTitle className="text-sm font-medium">Temporary Bans</CardTitle>
|
|
<Ban className="h-4 w-4 text-muted-foreground" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="text-2xl font-bold">9</div>
|
|
<p className="text-xs text-muted-foreground">Time-limited bans</p>
|
|
</CardContent>
|
|
</Card>
|
|
<Card>
|
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
<CardTitle className="text-sm font-medium">Appeals Pending</CardTitle>
|
|
<Unlock className="h-4 w-4 text-muted-foreground" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="text-2xl font-bold">2</div>
|
|
<p className="text-xs text-muted-foreground">Awaiting review</p>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
|
|
<div className="grid gap-6 lg:grid-cols-2">
|
|
{/* Ban User Form */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center">
|
|
<UserX className="h-5 w-5 mr-2" />
|
|
Ban User
|
|
</CardTitle>
|
|
<CardDescription>
|
|
Enter user details and reason for banning
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent className="space-y-4">
|
|
<div className="space-y-2">
|
|
<Label htmlFor="username">Username</Label>
|
|
<Input
|
|
id="username"
|
|
placeholder="Enter username to ban"
|
|
value={banData.username}
|
|
onChange={(e) => setBanData({...banData, username: e.target.value})}
|
|
/>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="reason">Reason</Label>
|
|
<Select value={banData.reason} onValueChange={(value) => setBanData({...banData, reason: value})}>
|
|
<SelectTrigger>
|
|
<SelectValue placeholder="Select ban reason" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="spam">Spam</SelectItem>
|
|
<SelectItem value="fraud">Fraud</SelectItem>
|
|
<SelectItem value="harassment">Harassment</SelectItem>
|
|
<SelectItem value="policy_violation">Policy Violation</SelectItem>
|
|
<SelectItem value="suspicious_activity">Suspicious Activity</SelectItem>
|
|
<SelectItem value="other">Other</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="duration">Duration</Label>
|
|
<Select value={banData.duration} onValueChange={(value) => setBanData({...banData, duration: value})}>
|
|
<SelectTrigger>
|
|
<SelectValue placeholder="Select ban duration" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="1_day">1 Day</SelectItem>
|
|
<SelectItem value="7_days">7 Days</SelectItem>
|
|
<SelectItem value="30_days">30 Days</SelectItem>
|
|
<SelectItem value="90_days">90 Days</SelectItem>
|
|
<SelectItem value="permanent">Permanent</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="description">Additional Details</Label>
|
|
<Textarea
|
|
id="description"
|
|
placeholder="Provide additional context for the ban..."
|
|
value={banData.description}
|
|
onChange={(e) => setBanData({...banData, description: e.target.value})}
|
|
/>
|
|
</div>
|
|
|
|
<AlertDialog>
|
|
<AlertDialogTrigger asChild>
|
|
<Button variant="destructive" className="w-full">
|
|
<Ban className="h-4 w-4 mr-2" />
|
|
Ban User
|
|
</Button>
|
|
</AlertDialogTrigger>
|
|
<AlertDialogContent>
|
|
<AlertDialogHeader>
|
|
<AlertDialogTitle>Confirm Ban</AlertDialogTitle>
|
|
<AlertDialogDescription>
|
|
Are you sure you want to ban user "{banData.username}"? This action cannot be undone.
|
|
</AlertDialogDescription>
|
|
</AlertDialogHeader>
|
|
<AlertDialogFooter>
|
|
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
|
<AlertDialogAction onClick={handleBanUser}>
|
|
Confirm Ban
|
|
</AlertDialogAction>
|
|
</AlertDialogFooter>
|
|
</AlertDialogContent>
|
|
</AlertDialog>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Search Banned Users */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Search Banned Users</CardTitle>
|
|
<CardDescription>Look up existing bans and their status</CardDescription>
|
|
</CardHeader>
|
|
<CardContent className="space-y-4">
|
|
<div className="relative">
|
|
<Search className="absolute left-2 top-2.5 h-4 w-4 text-muted-foreground" />
|
|
<Input placeholder="Search by username or email..." className="pl-8" />
|
|
</div>
|
|
<Button variant="outline" className="w-full">
|
|
Search Bans
|
|
</Button>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
|
|
{/* Banned Users Table */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Recent Bans</CardTitle>
|
|
<CardDescription>View and manage current and past bans</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<Table>
|
|
<TableHeader>
|
|
<TableRow>
|
|
<TableHead>User</TableHead>
|
|
<TableHead>Reason</TableHead>
|
|
<TableHead>Duration</TableHead>
|
|
<TableHead>Banned By</TableHead>
|
|
<TableHead>Date</TableHead>
|
|
<TableHead>Status</TableHead>
|
|
<TableHead className="text-right">Actions</TableHead>
|
|
</TableRow>
|
|
</TableHeader>
|
|
<TableBody>
|
|
{bannedUsers.map((user) => (
|
|
<TableRow key={user.id}>
|
|
<TableCell>
|
|
<div>
|
|
<div className="font-medium">{user.username}</div>
|
|
<div className="text-sm text-muted-foreground">{user.email}</div>
|
|
</div>
|
|
</TableCell>
|
|
<TableCell>{user.reason}</TableCell>
|
|
<TableCell>{user.duration}</TableCell>
|
|
<TableCell>{user.bannedBy}</TableCell>
|
|
<TableCell>{user.banDate}</TableCell>
|
|
<TableCell>
|
|
<Badge
|
|
variant={user.status === "active" ? "destructive" : "secondary"}
|
|
>
|
|
{user.status}
|
|
</Badge>
|
|
</TableCell>
|
|
<TableCell className="text-right">
|
|
<div className="flex items-center justify-end space-x-2">
|
|
{user.status === "active" && (
|
|
<Button variant="outline" size="sm">
|
|
<Unlock className="h-4 w-4" />
|
|
</Button>
|
|
)}
|
|
<Button variant="outline" size="sm">
|
|
View Details
|
|
</Button>
|
|
</div>
|
|
</TableCell>
|
|
</TableRow>
|
|
))}
|
|
</TableBody>
|
|
</Table>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
);
|
|
}
|