Enhance customer and profit analysis dialogs UI/UX
All checks were successful
Build Frontend / build (push) Successful in 1m14s
All checks were successful
Build Frontend / build (push) Successful in 1m14s
Revamps the customer details dialog with improved layout, animations, and clearer stats breakdown. Upgrades the profit analysis modal with animated cards, clearer tier breakdown, and improved cost/margin/profit explanations. Also increases recent activity fetch limit, fixes quote hydration in dashboard content, and minor animation tweak in order table.
This commit is contained in:
@@ -43,7 +43,9 @@ import {
|
||||
X,
|
||||
CreditCard,
|
||||
Calendar,
|
||||
ShoppingBag
|
||||
ShoppingBag,
|
||||
Truck,
|
||||
CheckCircle,
|
||||
} from "lucide-react";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Input } from "@/components/ui/input";
|
||||
@@ -469,117 +471,166 @@ export default function CustomerManagementPage() {
|
||||
</Card>
|
||||
|
||||
{/* Customer Details Dialog */}
|
||||
{selectedCustomer && (
|
||||
<Dialog open={!!selectedCustomer} onOpenChange={(open) => !open && setSelectedCustomer(null)}>
|
||||
<DialogContent className="max-w-[95vw] sm:max-w-2xl w-full overflow-y-auto max-h-[90vh] z-[80]">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-base">
|
||||
Customer Details
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 py-4">
|
||||
{/* Customer Information */}
|
||||
<div>
|
||||
<div className="mb-4">
|
||||
<h3 className="text-sm font-medium mb-2">Customer Information</h3>
|
||||
<div className="space-y-3">
|
||||
<div className="flex justify-between items-center text-sm">
|
||||
<div className="text-muted-foreground">Username:</div>
|
||||
<div className="font-medium">@{selectedCustomer.telegramUsername || "Unknown"}</div>
|
||||
</div>
|
||||
<div className="flex justify-between items-center text-sm">
|
||||
<div className="text-muted-foreground">Telegram ID:</div>
|
||||
<div className="font-medium">{selectedCustomer.telegramUserId}</div>
|
||||
</div>
|
||||
<div className="flex justify-between items-center text-sm">
|
||||
<div className="text-muted-foreground">Chat ID:</div>
|
||||
<div className="font-medium">{selectedCustomer.chatId}</div>
|
||||
<AnimatePresence>
|
||||
{selectedCustomer && (
|
||||
<Dialog open={!!selectedCustomer} onOpenChange={(open) => !open && setSelectedCustomer(null)}>
|
||||
<DialogContent className="max-w-[95vw] sm:max-w-2xl w-full overflow-y-auto max-h-[90vh] z-[80] bg-black/80 backdrop-blur-xl border-white/10 shadow-2xl p-0 gap-0">
|
||||
<DialogHeader className="p-6 pb-2 border-b border-white/5">
|
||||
<DialogTitle className="text-xl flex items-center gap-3">
|
||||
<div className="h-10 w-10 rounded-full bg-gradient-to-br from-indigo-500 to-purple-600 flex items-center justify-center text-white font-bold text-lg shadow-lg shadow-indigo-500/20">
|
||||
{selectedCustomer.telegramUsername ? selectedCustomer.telegramUsername.substring(0, 1).toUpperCase() : 'U'}
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-bold">Customer Details</div>
|
||||
<div className="text-sm font-normal text-muted-foreground flex items-center gap-2">
|
||||
@{selectedCustomer.telegramUsername || "Unknown"}
|
||||
<span className="w-1 h-1 rounded-full bg-indigo-500" />
|
||||
<span className="font-mono text-xs opacity-70">ID: {selectedCustomer.telegramUserId}</span>
|
||||
</div>
|
||||
</div>
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="p-6 space-y-6">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{/* Customer Information */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: 0.1 }}
|
||||
className="space-y-4"
|
||||
>
|
||||
<h3 className="text-xs font-semibold text-muted-foreground uppercase tracking-wider pl-1">Contact Info</h3>
|
||||
<div className="rounded-xl border border-white/5 bg-white/5 p-4 space-y-4 backdrop-blur-sm">
|
||||
<div className="space-y-3">
|
||||
<div className="flex justify-between items-center text-sm group">
|
||||
<div className="text-muted-foreground flex items-center gap-2">
|
||||
<Users className="h-4 w-4 opacity-50" />
|
||||
Username
|
||||
</div>
|
||||
<div className="font-medium text-white group-hover:text-primary transition-colors">@{selectedCustomer.telegramUsername || "Unknown"}</div>
|
||||
</div>
|
||||
<div className="flex justify-between items-center text-sm group">
|
||||
<div className="text-muted-foreground flex items-center gap-2">
|
||||
<CreditCard className="h-4 w-4 opacity-50" />
|
||||
User ID
|
||||
</div>
|
||||
<div className="font-medium font-mono text-white/80">{selectedCustomer.telegramUserId}</div>
|
||||
</div>
|
||||
<div className="flex justify-between items-center text-sm group">
|
||||
<div className="text-muted-foreground flex items-center gap-2">
|
||||
<MessageCircle className="h-4 w-4 opacity-50" />
|
||||
Chat ID
|
||||
</div>
|
||||
<div className="font-medium font-mono text-white/80">{selectedCustomer.chatId}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="w-full border-indigo-500/20 hover:bg-indigo-500/10 hover:text-indigo-400 text-indigo-300 transition-colors"
|
||||
onClick={() => {
|
||||
window.open(`https://t.me/${selectedCustomer.telegramUsername || selectedCustomer.telegramUserId}`, '_blank');
|
||||
}}
|
||||
>
|
||||
<MessageCircle className="h-4 w-4 mr-2" />
|
||||
Open Telegram Chat
|
||||
</Button>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Order Statistics */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: 0.2 }}
|
||||
className="space-y-4"
|
||||
>
|
||||
<h3 className="text-xs font-semibold text-muted-foreground uppercase tracking-wider pl-1">Lifetime Stats</h3>
|
||||
<div className="rounded-xl border border-white/5 bg-gradient-to-br from-white/5 to-white/[0.02] p-4 space-y-4 backdrop-blur-sm">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="bg-emerald-500/10 rounded-lg p-3 border border-emerald-500/20">
|
||||
<div className="text-xs text-emerald-400/70 uppercase font-medium mb-1">Total Spent</div>
|
||||
<div className="text-xl font-bold text-emerald-400">{formatCurrency(selectedCustomer.totalSpent)}</div>
|
||||
</div>
|
||||
<div className="bg-blue-500/10 rounded-lg p-3 border border-blue-500/20">
|
||||
<div className="text-xs text-blue-400/70 uppercase font-medium mb-1">Total Orders</div>
|
||||
<div className="text-xl font-bold text-blue-400">{selectedCustomer.totalOrders}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2 pt-2 border-t border-white/5">
|
||||
<div className="flex justify-between items-center text-sm">
|
||||
<div className="text-muted-foreground text-xs">First Order</div>
|
||||
<div className="font-medium text-white/70 text-xs bg-white/5 px-2 py-1 rounded">
|
||||
{formatDate(selectedCustomer.firstOrderDate)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-between items-center text-sm">
|
||||
<div className="text-muted-foreground text-xs">Last Activity</div>
|
||||
<div className="font-medium text-white/70 text-xs bg-white/5 px-2 py-1 rounded">
|
||||
{formatDate(selectedCustomer.lastOrderDate)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
{/* Order Status Breakdown */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: 0.3 }}
|
||||
className="space-y-4"
|
||||
>
|
||||
<h3 className="text-xs font-semibold text-muted-foreground uppercase tracking-wider pl-1">Order History Breakdown</h3>
|
||||
<div className="grid grid-cols-2 sm:grid-cols-4 gap-3">
|
||||
<div className="bg-blue-500/5 hover:bg-blue-500/10 transition-colors rounded-xl border border-blue-500/20 p-4 text-center group">
|
||||
<ShoppingBag className="h-5 w-5 text-blue-500 mx-auto mb-2 opacity-70 group-hover:opacity-100 transition-opacity" />
|
||||
<p className="text-2xl font-bold text-white mb-1">{selectedCustomer.ordersByStatus.paid}</p>
|
||||
<p className="text-xs font-medium text-blue-400/70 uppercase">Paid</p>
|
||||
</div>
|
||||
<div className="bg-purple-500/5 hover:bg-purple-500/10 transition-colors rounded-xl border border-purple-500/20 p-4 text-center group">
|
||||
<Loader2 className="h-5 w-5 text-purple-500 mx-auto mb-2 opacity-70 group-hover:opacity-100 transition-opacity" />
|
||||
<p className="text-2xl font-bold text-white mb-1">{selectedCustomer.ordersByStatus.acknowledged}</p>
|
||||
<p className="text-xs font-medium text-purple-400/70 uppercase">Processing</p>
|
||||
</div>
|
||||
<div className="bg-amber-500/5 hover:bg-amber-500/10 transition-colors rounded-xl border border-amber-500/20 p-4 text-center group">
|
||||
<Truck className="h-5 w-5 text-amber-500 mx-auto mb-2 opacity-70 group-hover:opacity-100 transition-opacity" />
|
||||
<p className="text-2xl font-bold text-white mb-1">{selectedCustomer.ordersByStatus.shipped}</p>
|
||||
<p className="text-xs font-medium text-amber-400/70 uppercase">Shipped</p>
|
||||
</div>
|
||||
<div className="bg-emerald-500/5 hover:bg-emerald-500/10 transition-colors rounded-xl border border-emerald-500/20 p-4 text-center group">
|
||||
<CheckCircle className="h-5 w-5 text-emerald-500 mx-auto mb-2 opacity-70 group-hover:opacity-100 transition-opacity" />
|
||||
<p className="text-2xl font-bold text-white mb-1">{selectedCustomer.ordersByStatus.completed}</p>
|
||||
<p className="text-xs font-medium text-emerald-400/70 uppercase">Completed</p>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
<DialogFooter className="p-6 pt-2 border-t border-white/5 bg-white/[0.02]">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="w-full"
|
||||
onClick={() => {
|
||||
window.open(`https://t.me/${selectedCustomer.telegramUsername || selectedCustomer.telegramUserId}`, '_blank');
|
||||
}}
|
||||
variant="ghost"
|
||||
onClick={() => setSelectedCustomer(null)}
|
||||
className="hover:bg-white/5 text-muted-foreground hover:text-white"
|
||||
>
|
||||
Close Profile
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => router.push(`/dashboard/storefront/chat/new?userId=${selectedCustomer.telegramUserId}`)}
|
||||
className="bg-gradient-to-r from-indigo-600 to-purple-600 hover:from-indigo-500 hover:to-purple-500 text-white shadow-lg shadow-indigo-500/25 border-0"
|
||||
>
|
||||
<MessageCircle className="h-4 w-4 mr-2" />
|
||||
Open Telegram Chat
|
||||
Message Customer
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Order Statistics */}
|
||||
<div>
|
||||
<h3 className="text-sm font-medium mb-2">Order Statistics</h3>
|
||||
<div className="bg-background rounded-lg border border-border p-4 space-y-3">
|
||||
<div className="flex justify-between items-center text-sm">
|
||||
<div className="text-muted-foreground">Total Orders:</div>
|
||||
<div className="font-medium">{selectedCustomer.totalOrders}</div>
|
||||
</div>
|
||||
<div className="flex justify-between items-center text-sm">
|
||||
<div className="text-muted-foreground">Total Spent:</div>
|
||||
<div className="font-medium">{formatCurrency(selectedCustomer.totalSpent)}</div>
|
||||
</div>
|
||||
<div className="flex justify-between items-center text-sm">
|
||||
<div className="text-muted-foreground">First Order:</div>
|
||||
<div className="font-medium">
|
||||
{formatDate(selectedCustomer.firstOrderDate)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-between items-center text-sm">
|
||||
<div className="text-muted-foreground">Last Order:</div>
|
||||
<div className="font-medium">
|
||||
{formatDate(selectedCustomer.lastOrderDate)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Order Status Breakdown */}
|
||||
<div className="mb-4">
|
||||
<h3 className="text-sm font-medium mb-2">Order Status Breakdown</h3>
|
||||
<div className="grid grid-cols-2 sm:grid-cols-4 gap-4">
|
||||
<div className="bg-blue-500/10 rounded-lg border border-blue-500/20 p-3">
|
||||
<p className="text-sm text-muted-foreground">Paid</p>
|
||||
<p className="text-xl font-semibold">{selectedCustomer.ordersByStatus.paid}</p>
|
||||
</div>
|
||||
<div className="bg-purple-500/10 rounded-lg border border-purple-500/20 p-3">
|
||||
<p className="text-sm text-muted-foreground">Acknowledged</p>
|
||||
<p className="text-xl font-semibold">{selectedCustomer.ordersByStatus.acknowledged}</p>
|
||||
</div>
|
||||
<div className="bg-amber-500/10 rounded-lg border border-amber-500/20 p-3">
|
||||
<p className="text-sm text-muted-foreground">Shipped</p>
|
||||
<p className="text-xl font-semibold">{selectedCustomer.ordersByStatus.shipped}</p>
|
||||
</div>
|
||||
<div className="bg-green-500/10 rounded-lg border border-green-500/20 p-3">
|
||||
<p className="text-sm text-muted-foreground">Completed</p>
|
||||
<p className="text-xl font-semibold">{selectedCustomer.ordersByStatus.completed}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setSelectedCustomer(null)}
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => router.push(`/dashboard/storefront/chat/new?userId=${selectedCustomer.telegramUserId}`)}
|
||||
>
|
||||
<MessageCircle className="h-4 w-4 mr-2" />
|
||||
Start Chat
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)}
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user