Improve admin ban UX, add product cloning, and enhance auth handling
Refines the admin ban page with better dialog state management and feedback during ban/unban actions. Adds a product cloning feature to the products dashboard and updates the product table to support cloning. Improves error handling in ChatDetail for authentication errors, and enhances middleware to handle auth check timeouts and network errors more gracefully. Also updates BanUserCard to validate user ID and ensure correct request formatting.
This commit is contained in:
@@ -13,9 +13,20 @@ export default function BanUserCard() {
|
||||
setLoading(true);
|
||||
setMessage(null);
|
||||
try {
|
||||
const userId = parseInt(telegramUserId);
|
||||
if (isNaN(userId)) {
|
||||
setMessage("Invalid Telegram User ID");
|
||||
return;
|
||||
}
|
||||
await fetchClient("/admin/ban", {
|
||||
method: "POST",
|
||||
body: { telegramUserId, reason }
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
telegramUserId: userId,
|
||||
reason: reason || undefined
|
||||
})
|
||||
});
|
||||
setMessage("User banned");
|
||||
setTelegramUserId("");
|
||||
|
||||
@@ -277,9 +277,17 @@ export default function ChatDetail({ chatId }: { chatId: string }) {
|
||||
setTimeout(() => {
|
||||
scrollToBottomHandler();
|
||||
}, 100);
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
console.error("Error fetching chat data:", error);
|
||||
toast.error("Failed to load chat");
|
||||
|
||||
// Don't redirect on auth errors - let the middleware handle it
|
||||
// Only show error toast for non-auth errors
|
||||
if (error?.message?.includes('401') || error?.message?.includes('403')) {
|
||||
// Auth errors will be handled by middleware, don't show toast
|
||||
console.log("Auth error detected, middleware will handle redirect");
|
||||
} else {
|
||||
toast.error("Failed to load chat");
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -352,8 +360,14 @@ export default function ChatDetail({ chatId }: { chatId: string }) {
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
console.error("Error polling new messages:", error);
|
||||
|
||||
// Silently fail on auth errors during polling - don't disrupt the user
|
||||
if (error?.message?.includes('401') || error?.message?.includes('403')) {
|
||||
console.log("Auth error during polling, stopping poll");
|
||||
return;
|
||||
}
|
||||
} finally {
|
||||
isPollingRef.current = false;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
||||
import { Edit, Trash, AlertTriangle, CheckCircle, AlertCircle, Calculator } from "lucide-react";
|
||||
import { Edit, Trash, AlertTriangle, CheckCircle, AlertCircle, Calculator, Copy } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Product } from "@/models/products";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
@@ -9,6 +9,7 @@ interface ProductTableProps {
|
||||
products: Product[];
|
||||
loading: boolean;
|
||||
onEdit: (product: Product) => void;
|
||||
onClone?: (product: Product) => void;
|
||||
onDelete: (productId: string) => void;
|
||||
onToggleEnabled: (productId: string, enabled: boolean) => void;
|
||||
onProfitAnalysis?: (productId: string, productName: string) => void;
|
||||
@@ -19,6 +20,7 @@ const ProductTable = ({
|
||||
products,
|
||||
loading,
|
||||
onEdit,
|
||||
onClone,
|
||||
onDelete,
|
||||
onToggleEnabled,
|
||||
onProfitAnalysis,
|
||||
@@ -109,7 +111,18 @@ const ProductTable = ({
|
||||
<Calculator className="h-4 w-4" />
|
||||
</Button>
|
||||
)}
|
||||
<Button variant="ghost" size="sm" onClick={() => onEdit(product)}>
|
||||
{onClone && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => onClone(product)}
|
||||
className="text-blue-600 hover:text-blue-700 hover:bg-blue-50 dark:hover:bg-blue-950/20"
|
||||
title="Clone Listing"
|
||||
>
|
||||
<Copy className="h-4 w-4" />
|
||||
</Button>
|
||||
)}
|
||||
<Button variant="ghost" size="sm" onClick={() => onEdit(product)} title="Edit Product">
|
||||
<Edit className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
@@ -117,6 +130,7 @@ const ProductTable = ({
|
||||
size="sm"
|
||||
onClick={() => onDelete(product._id as string)}
|
||||
className="text-red-500 hover:text-red-600 hover:bg-red-50 dark:hover:bg-red-950/20"
|
||||
title="Delete Product"
|
||||
>
|
||||
<Trash className="h-4 w-4" />
|
||||
</Button>
|
||||
|
||||
Reference in New Issue
Block a user