This commit is contained in:
101
app/dashboard/admin/vendors/page.tsx
vendored
101
app/dashboard/admin/vendors/page.tsx
vendored
@@ -6,7 +6,9 @@ import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
||||
import { Search, MoreHorizontal, UserCheck, UserX, Mail, Loader2, Store, Shield, ShieldAlert, Clock, Calendar } from "lucide-react";
|
||||
import { Search, MoreHorizontal, UserCheck, UserX, Mail, Loader2, Store, Shield, ShieldAlert, Clock, Calendar, Pencil, Plus } from "lucide-react";
|
||||
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { fetchClient } from "@/lib/api-client";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
@@ -41,6 +43,43 @@ export default function AdminVendorsPage() {
|
||||
const [page, setPage] = useState(1);
|
||||
const [pagination, setPagination] = useState<PaginationResponse['pagination'] | null>(null);
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const [isEditStoreOpen, setIsEditStoreOpen] = useState(false);
|
||||
const [editingVendor, setEditingVendor] = useState<Vendor | null>(null);
|
||||
const [newStoreId, setNewStoreId] = useState("");
|
||||
const [updating, setUpdating] = useState(false);
|
||||
|
||||
const handleEditStore = (vendor: Vendor) => {
|
||||
setEditingVendor(vendor);
|
||||
setNewStoreId(vendor.storeId || "");
|
||||
setIsEditStoreOpen(true);
|
||||
};
|
||||
|
||||
const saveStoreId = async () => {
|
||||
if (!editingVendor) return;
|
||||
|
||||
try {
|
||||
setUpdating(true);
|
||||
await fetchClient(`/admin/vendors/${editingVendor._id}/store-id`, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify({ storeId: newStoreId })
|
||||
});
|
||||
|
||||
toast({
|
||||
title: "Success",
|
||||
description: "Store ID updated successfully",
|
||||
});
|
||||
setIsEditStoreOpen(false);
|
||||
fetchVendors(); // Refresh list
|
||||
} catch (error: any) {
|
||||
toast({
|
||||
title: "Error",
|
||||
description: error.message || "Failed to update store ID",
|
||||
variant: "destructive",
|
||||
});
|
||||
} finally {
|
||||
setUpdating(false);
|
||||
}
|
||||
};
|
||||
|
||||
const fetchVendors = useCallback(async () => {
|
||||
try {
|
||||
@@ -209,9 +248,29 @@ export default function AdminVendorsPage() {
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{vendor.storeId ? (
|
||||
<span className="font-mono text-xs">{vendor.storeId}</span>
|
||||
<div className="flex items-center gap-2 group/store">
|
||||
<span className="font-mono text-xs">{vendor.storeId}</span>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-6 w-6 opacity-0 group-hover/store:opacity-100 transition-opacity"
|
||||
onClick={() => handleEditStore(vendor)}
|
||||
>
|
||||
<Pencil className="h-3 w-3" />
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<span className="text-muted-foreground italic text-xs">No store</span>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-muted-foreground italic text-xs">No store</span>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-6 w-6 text-muted-foreground hover:text-primary"
|
||||
onClick={() => handleEditStore(vendor)}
|
||||
>
|
||||
<Plus className="h-3 w-3" />
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
@@ -291,6 +350,40 @@ export default function AdminVendorsPage() {
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Dialog open={isEditStoreOpen} onOpenChange={setIsEditStoreOpen}>
|
||||
<DialogContent className="sm:max-w-[425px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Update Vendor Store</DialogTitle>
|
||||
<DialogDescription>
|
||||
Enter the Store ID to assign to vendor <span className="font-semibold text-foreground">{editingVendor?.username}</span>.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="grid gap-4 py-4">
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="storeId">Store ID</Label>
|
||||
<Input
|
||||
id="storeId"
|
||||
value={newStoreId}
|
||||
onChange={(e) => setNewStoreId(e.target.value)}
|
||||
placeholder="Enter 24-character Store ID"
|
||||
className="col-span-3 font-mono"
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Ensure the Store ID corresponds to an existing store in the system.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => setIsEditStoreOpen(false)} disabled={updating}>Cancel</Button>
|
||||
<Button onClick={saveStoreId} disabled={updating || !newStoreId || newStoreId.length < 24}>
|
||||
{updating && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
|
||||
Save Changes
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div >
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user