Add admin order details modal and improve admin UI

Introduces an admin-only OrderDetailsModal component for viewing and managing order details, including status updates and transaction info. Updates OrdersTable to support the modal, and enhances the admin dashboard page with lazy loading and skeletons for better UX. Also fixes API client base URL handling for /api prefix.
This commit is contained in:
g
2025-12-17 23:38:17 +00:00
parent 93ec3d3642
commit 2db13cc9b7
5 changed files with 667 additions and 21 deletions

View File

@@ -1,16 +1,61 @@
"use client";
export const dynamic = "force-dynamic";
import React from "react";
import AdminAnalytics from "@/components/admin/AdminAnalytics";
import InviteVendorCard from "@/components/admin/InviteVendorCard";
import BanUserCard from "@/components/admin/BanUserCard";
import InvitationsListCard from "@/components/admin/InvitationsListCard";
import VendorsCard from "@/components/admin/VendorsCard";
import React, { Suspense, lazy, useState } from "react";
import { Button } from "@/components/ui/button";
import Link from "next/link";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Skeleton } from "@/components/ui/skeleton";
import { Card, CardContent, CardHeader } from "@/components/ui/card";
// Lazy load admin components
const AdminAnalytics = lazy(() => import("@/components/admin/AdminAnalytics"));
const InviteVendorCard = lazy(() => import("@/components/admin/InviteVendorCard"));
const BanUserCard = lazy(() => import("@/components/admin/BanUserCard"));
const InvitationsListCard = lazy(() => import("@/components/admin/InvitationsListCard"));
const VendorsCard = lazy(() => import("@/components/admin/VendorsCard"));
// Loading skeleton for admin components
function AdminComponentSkeleton() {
return (
<div className="space-y-4">
<Card>
<CardHeader>
<Skeleton className="h-6 w-48" />
</CardHeader>
<CardContent>
<div className="space-y-2">
<Skeleton className="h-4 w-full" />
<Skeleton className="h-4 w-3/4" />
<Skeleton className="h-4 w-1/2" />
</div>
</CardContent>
</Card>
</div>
);
}
// Loading skeleton for management cards
function ManagementCardsSkeleton() {
return (
<div className="grid gap-4 lg:gap-6 sm:grid-cols-2 lg:grid-cols-3 items-stretch">
{[1, 2, 3, 4].map((i) => (
<Card key={i}>
<CardHeader>
<Skeleton className="h-6 w-32" />
</CardHeader>
<CardContent>
<Skeleton className="h-20 w-full" />
</CardContent>
</Card>
))}
</div>
);
}
export default function AdminPage() {
const [activeTab, setActiveTab] = useState("analytics");
return (
<div className="space-y-6">
<div className="flex items-center justify-between">
@@ -23,23 +68,27 @@ export default function AdminPage() {
</Button>
</div>
<Tabs defaultValue="analytics" className="space-y-6">
<Tabs value={activeTab} onValueChange={setActiveTab} className="space-y-6">
<TabsList>
<TabsTrigger value="analytics">Analytics</TabsTrigger>
<TabsTrigger value="management">Management</TabsTrigger>
</TabsList>
<TabsContent value="analytics" className="space-y-6">
<AdminAnalytics />
<Suspense fallback={<AdminComponentSkeleton />}>
<AdminAnalytics />
</Suspense>
</TabsContent>
<TabsContent value="management" className="space-y-6">
<div className="grid gap-4 lg:gap-6 sm:grid-cols-2 lg:grid-cols-3 items-stretch">
<VendorsCard />
<InviteVendorCard />
<BanUserCard />
<InvitationsListCard />
</div>
<Suspense fallback={<ManagementCardsSkeleton />}>
<div className="grid gap-4 lg:gap-6 sm:grid-cols-2 lg:grid-cols-3 items-stretch">
<VendorsCard />
<InviteVendorCard />
<BanUserCard />
<InvitationsListCard />
</div>
</Suspense>
</TabsContent>
</Tabs>
</div>