Add dashboard navigation and request timeouts
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.
This commit is contained in:
@@ -5,6 +5,7 @@ import { Button } from "@/components/ui/button";
|
|||||||
import { Alert, AlertDescription } from "@/components/ui/alert";
|
import { Alert, AlertDescription } from "@/components/ui/alert";
|
||||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
||||||
import { AlertTriangle, CheckCircle, XCircle, Clock, Bell, Shield } from "lucide-react";
|
import { AlertTriangle, CheckCircle, XCircle, Clock, Bell, Shield } from "lucide-react";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
export default function AdminAlertsPage() {
|
export default function AdminAlertsPage() {
|
||||||
// Mock data for system alerts
|
// Mock data for system alerts
|
||||||
@@ -93,9 +94,14 @@ export default function AdminAlertsPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div>
|
<div className="flex items-center justify-between">
|
||||||
<h1 className="text-2xl font-semibold tracking-tight">System Alerts</h1>
|
<div>
|
||||||
<p className="text-sm text-muted-foreground mt-1">Monitor system alerts and security notifications</p>
|
<h1 className="text-2xl font-semibold tracking-tight">System Alerts</h1>
|
||||||
|
<p className="text-sm text-muted-foreground mt-1">Monitor system alerts and security notifications</p>
|
||||||
|
</div>
|
||||||
|
<Button asChild variant="outline" size="sm">
|
||||||
|
<Link href="/dashboard">Back to Dashboard</Link>
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Alert Summary */}
|
{/* Alert Summary */}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@
|
|||||||
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@/components/ui/alert-dialog";
|
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 { UserX, Shield, Search, Ban, Unlock } from "lucide-react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
export default function AdminBanPage() {
|
export default function AdminBanPage() {
|
||||||
const [banData, setBanData] = useState({
|
const [banData, setBanData] = useState({
|
||||||
@@ -62,9 +63,14 @@ export default function AdminBanPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div>
|
<div className="flex items-center justify-between">
|
||||||
<h1 className="text-2xl font-semibold tracking-tight">Ban Users</h1>
|
<div>
|
||||||
<p className="text-sm text-muted-foreground mt-1">Manage user bans and suspensions</p>
|
<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>
|
</div>
|
||||||
|
|
||||||
{/* Stats Cards */}
|
{/* Stats Cards */}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@
|
|||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { UserPlus, Mail, Copy, Check } from "lucide-react";
|
import { UserPlus, Mail, Copy, Check } from "lucide-react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
export default function AdminInvitePage() {
|
export default function AdminInvitePage() {
|
||||||
const [inviteData, setInviteData] = useState({
|
const [inviteData, setInviteData] = useState({
|
||||||
@@ -35,9 +36,14 @@ export default function AdminInvitePage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div>
|
<div className="flex items-center justify-between">
|
||||||
<h1 className="text-2xl font-semibold tracking-tight">Invite Vendor</h1>
|
<div>
|
||||||
<p className="text-sm text-muted-foreground mt-1">Send invitations to new vendors to join the platform</p>
|
<h1 className="text-2xl font-semibold tracking-tight">Invite Vendor</h1>
|
||||||
|
<p className="text-sm text-muted-foreground mt-1">Send invitations to new vendors to join the platform</p>
|
||||||
|
</div>
|
||||||
|
<Button asChild variant="outline" size="sm">
|
||||||
|
<Link href="/dashboard">Back to Dashboard</Link>
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid gap-6 lg:grid-cols-2">
|
<div className="grid gap-6 lg:grid-cols-2">
|
||||||
|
|||||||
@@ -7,13 +7,20 @@ import RecentOrdersCard from "@/components/admin/RecentOrdersCard";
|
|||||||
import SystemStatusCard from "@/components/admin/SystemStatusCard";
|
import SystemStatusCard from "@/components/admin/SystemStatusCard";
|
||||||
import InvitationsListCard from "@/components/admin/InvitationsListCard";
|
import InvitationsListCard from "@/components/admin/InvitationsListCard";
|
||||||
import VendorsCard from "@/components/admin/VendorsCard";
|
import VendorsCard from "@/components/admin/VendorsCard";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
export default function AdminPage() {
|
export default function AdminPage() {
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div>
|
<div className="flex items-center justify-between">
|
||||||
<h1 className="text-2xl font-semibold tracking-tight">Admin</h1>
|
<div>
|
||||||
<p className="text-sm text-muted-foreground mt-1">Restricted area. Only admin1 can access.</p>
|
<h1 className="text-2xl font-semibold tracking-tight">Admin</h1>
|
||||||
|
<p className="text-sm text-muted-foreground mt-1">Restricted area. Only admin1 can access.</p>
|
||||||
|
</div>
|
||||||
|
<Button asChild variant="outline" size="sm">
|
||||||
|
<Link href="/dashboard">Back to Dashboard</Link>
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid gap-4 lg:gap-6 sm:grid-cols-2 lg:grid-cols-3 items-stretch">
|
<div className="grid gap-4 lg:gap-6 sm:grid-cols-2 lg:grid-cols-3 items-stretch">
|
||||||
|
|||||||
@@ -8,13 +8,19 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@
|
|||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
import { Settings, Shield, Bell, Database, Globe, Key, Save } from "lucide-react";
|
import { Settings, Shield, Bell, Database, Globe, Key, Save } from "lucide-react";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
export default function AdminSettingsPage() {
|
export default function AdminSettingsPage() {
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div>
|
<div className="flex items-center justify-between">
|
||||||
<h1 className="text-2xl font-semibold tracking-tight">Admin Settings</h1>
|
<div>
|
||||||
<p className="text-sm text-muted-foreground mt-1">Configure system settings and preferences</p>
|
<h1 className="text-2xl font-semibold tracking-tight">Admin Settings</h1>
|
||||||
|
<p className="text-sm text-muted-foreground mt-1">Configure system settings and preferences</p>
|
||||||
|
</div>
|
||||||
|
<Button asChild variant="outline" size="sm">
|
||||||
|
<Link href="/dashboard">Back to Dashboard</Link>
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid gap-6 lg:grid-cols-2">
|
<div className="grid gap-6 lg:grid-cols-2">
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ import {
|
|||||||
CardHeader,
|
CardHeader,
|
||||||
CardTitle,
|
CardTitle,
|
||||||
} from "@/components/ui/card";
|
} from "@/components/ui/card";
|
||||||
import { Clipboard, Truck, Package, ArrowRight, ChevronDown, AlertTriangle, Copy, Loader2, RefreshCw } from "lucide-react";
|
import { Clipboard, Truck, Package, ArrowRight, ChevronDown, AlertTriangle, Copy, Loader2, RefreshCw, MessageCircle } from "lucide-react";
|
||||||
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import {
|
import {
|
||||||
@@ -829,6 +830,22 @@ export default function OrderDetailsPage() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{(order?.telegramBuyerId || order?.telegramUsername) && (
|
||||||
|
<div className="pt-2">
|
||||||
|
<Button
|
||||||
|
variant="secondary"
|
||||||
|
size="sm"
|
||||||
|
asChild
|
||||||
|
>
|
||||||
|
<Link href={`/dashboard/chats/new?buyerId=${order?.telegramBuyerId || order?.telegramUsername}`}
|
||||||
|
title={`Chat with customer${order?.telegramUsername ? ` @${order.telegramUsername}` : ''}`}
|
||||||
|
>
|
||||||
|
<MessageCircle className="h-4 w-4 mr-2" />
|
||||||
|
Message customer
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -232,7 +232,8 @@ export async function clientFetch<T = any>(url: string, options: RequestInit = {
|
|||||||
headers,
|
headers,
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
mode: 'cors',
|
mode: 'cors',
|
||||||
referrerPolicy: 'strict-origin-when-cross-origin'
|
referrerPolicy: 'strict-origin-when-cross-origin',
|
||||||
|
signal: AbortSignal.timeout(30000), // 30 second timeout
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
@@ -303,6 +304,7 @@ export async function fetchClient<T>(
|
|||||||
method,
|
method,
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
headers: requestHeaders,
|
headers: requestHeaders,
|
||||||
|
signal: AbortSignal.timeout(30000), // 30 second timeout
|
||||||
...rest,
|
...rest,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ export async function middleware(req: NextRequest) {
|
|||||||
method: "GET",
|
method: "GET",
|
||||||
headers,
|
headers,
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
|
signal: AbortSignal.timeout(10000), // 10 second timeout
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`Middleware: Auth check responded with status ${res.status}`);
|
console.log(`Middleware: Auth check responded with status ${res.status}`);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"commitHash": "1fc29e6",
|
"commitHash": "fcba1a8",
|
||||||
"buildTime": "2025-10-23T20:42:39.661Z"
|
"buildTime": "2025-10-30T01:13:38.854Z"
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user