'use client' import { useState } from 'react' import { useParams, useRouter } from 'next/navigation' import Link from 'next/link' import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from '@/components/ui/card' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { useCustomer, useDeleteCustomer } from '@/hooks/use-customers' import { useQueryClient } from '@tanstack/react-query' import { customerKeys } from '@/hooks/use-customers' import { SliderConfirmDialog } from '@/components/ui/slider-confirm-dialog' import { ArrowLeft, User, Mail, Building2, Calendar, Server, Loader2, AlertCircle, RefreshCw, Edit, Ban, CheckCircle, ExternalLink, CreditCard, Activity, Package, X, Save, ShoppingCart, Sparkles, TrendingUp, Trash2, Plus, } from 'lucide-react' import { CreateOrderDialog } from '@/components/admin/create-order-dialog' type UserStatus = 'ACTIVE' | 'SUSPENDED' | 'PENDING_VERIFICATION' type SubscriptionStatus = 'TRIAL' | 'ACTIVE' | 'CANCELED' | 'PAST_DUE' type OrderStatus = 'PAYMENT_CONFIRMED' | 'AWAITING_SERVER' | 'SERVER_READY' | 'DNS_PENDING' | 'DNS_READY' | 'PROVISIONING' | 'FULFILLED' | 'EMAIL_CONFIGURED' | 'FAILED' // Status badge components with enhanced styling function UserStatusBadge({ status }: { status: UserStatus }) { const statusConfig: Record = { ACTIVE: { label: 'Active', bgColor: 'bg-emerald-50 dark:bg-emerald-900/20', textColor: 'text-emerald-700 dark:text-emerald-400', borderColor: 'border-emerald-200 dark:border-emerald-800', dotColor: 'bg-emerald-500' }, SUSPENDED: { label: 'Suspended', bgColor: 'bg-red-50 dark:bg-red-900/20', textColor: 'text-red-700 dark:text-red-400', borderColor: 'border-red-200 dark:border-red-800', dotColor: 'bg-red-500' }, PENDING_VERIFICATION: { label: 'Pending', bgColor: 'bg-amber-50 dark:bg-amber-900/20', textColor: 'text-amber-700 dark:text-amber-400', borderColor: 'border-amber-200 dark:border-amber-800', dotColor: 'bg-amber-500' }, } const config = statusConfig[status] return ( {config.label} ) } function SubscriptionBadge({ status }: { status: SubscriptionStatus }) { const statusConfig: Record = { TRIAL: { label: 'Trial', bgColor: 'bg-blue-50 dark:bg-blue-900/20', textColor: 'text-blue-700 dark:text-blue-400', borderColor: 'border-blue-200 dark:border-blue-800' }, ACTIVE: { label: 'Active', bgColor: 'bg-emerald-50 dark:bg-emerald-900/20', textColor: 'text-emerald-700 dark:text-emerald-400', borderColor: 'border-emerald-200 dark:border-emerald-800' }, CANCELED: { label: 'Canceled', bgColor: 'bg-slate-50 dark:bg-slate-900/20', textColor: 'text-slate-600 dark:text-slate-400', borderColor: 'border-slate-200 dark:border-slate-700' }, PAST_DUE: { label: 'Past Due', bgColor: 'bg-red-50 dark:bg-red-900/20', textColor: 'text-red-700 dark:text-red-400', borderColor: 'border-red-200 dark:border-red-800' }, } const config = statusConfig[status] return ( {config.label} ) } function OrderStatusBadge({ status }: { status: OrderStatus }) { const statusConfig: Record = { PAYMENT_CONFIRMED: { label: 'Payment Confirmed', bgColor: 'bg-blue-50 dark:bg-blue-900/20', textColor: 'text-blue-700 dark:text-blue-400', borderColor: 'border-blue-200 dark:border-blue-800' }, AWAITING_SERVER: { label: 'Awaiting Server', bgColor: 'bg-amber-50 dark:bg-amber-900/20', textColor: 'text-amber-700 dark:text-amber-400', borderColor: 'border-amber-200 dark:border-amber-800' }, SERVER_READY: { label: 'Server Ready', bgColor: 'bg-cyan-50 dark:bg-cyan-900/20', textColor: 'text-cyan-700 dark:text-cyan-400', borderColor: 'border-cyan-200 dark:border-cyan-800' }, DNS_PENDING: { label: 'DNS Pending', bgColor: 'bg-orange-50 dark:bg-orange-900/20', textColor: 'text-orange-700 dark:text-orange-400', borderColor: 'border-orange-200 dark:border-orange-800' }, DNS_READY: { label: 'DNS Ready', bgColor: 'bg-teal-50 dark:bg-teal-900/20', textColor: 'text-teal-700 dark:text-teal-400', borderColor: 'border-teal-200 dark:border-teal-800' }, PROVISIONING: { label: 'Provisioning', bgColor: 'bg-purple-50 dark:bg-purple-900/20', textColor: 'text-purple-700 dark:text-purple-400', borderColor: 'border-purple-200 dark:border-purple-800' }, FULFILLED: { label: 'Fulfilled', bgColor: 'bg-emerald-50 dark:bg-emerald-900/20', textColor: 'text-emerald-700 dark:text-emerald-400', borderColor: 'border-emerald-200 dark:border-emerald-800' }, EMAIL_CONFIGURED: { label: 'Email Configured', bgColor: 'bg-emerald-50 dark:bg-emerald-900/20', textColor: 'text-emerald-700 dark:text-emerald-400', borderColor: 'border-emerald-200 dark:border-emerald-800' }, FAILED: { label: 'Failed', bgColor: 'bg-red-50 dark:bg-red-900/20', textColor: 'text-red-700 dark:text-red-400', borderColor: 'border-red-200 dark:border-red-800' }, } const config = statusConfig[status] || { label: status, bgColor: 'bg-slate-50 dark:bg-slate-900/20', textColor: 'text-slate-600 dark:text-slate-400', borderColor: 'border-slate-200 dark:border-slate-700' } return ( {config.label} ) } // Token usage progress bar with threshold colors function TokenUsageBar({ used, limit }: { used: number; limit: number }) { const percentage = Math.min((used / limit) * 100, 100) const getBarColor = () => { if (percentage > 90) return 'bg-gradient-to-r from-red-500 to-red-600' if (percentage > 75) return 'bg-gradient-to-r from-amber-500 to-orange-500' if (percentage > 50) return 'bg-gradient-to-r from-yellow-400 to-amber-500' return 'bg-gradient-to-r from-emerald-500 to-emerald-600' } const getTextColor = () => { if (percentage > 90) return 'text-red-600 dark:text-red-400' if (percentage > 75) return 'text-amber-600 dark:text-amber-400' return 'text-emerald-600 dark:text-emerald-400' } return (
Token Usage {used.toLocaleString()} / {limit.toLocaleString()}
{percentage.toFixed(1)}% used {(100 - percentage).toFixed(1)}% remaining
) } export default function CustomerDetailPage() { const params = useParams() const router = useRouter() const queryClient = useQueryClient() const customerId = params.id as string const [isEditing, setIsEditing] = useState(false) const [editForm, setEditForm] = useState({ name: '', company: '' }) const [isUpdating, setIsUpdating] = useState(false) const [showDeleteDialog, setShowDeleteDialog] = useState(false) const [showCreateOrderDialog, setShowCreateOrderDialog] = useState(false) const { data: customer, isLoading, isError, error, refetch, isFetching } = useCustomer(customerId) const deleteCustomer = useDeleteCustomer() const handleEdit = () => { if (customer) { setEditForm({ name: customer.name || '', company: customer.company || '', }) setIsEditing(true) } } const handleSave = async () => { setIsUpdating(true) try { const response = await fetch(`/api/v1/admin/customers/${customerId}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(editForm), }) if (!response.ok) { throw new Error('Failed to update customer') } await queryClient.invalidateQueries({ queryKey: customerKeys.detail(customerId) }) setIsEditing(false) } catch (err) { console.error('Error updating customer:', err) } finally { setIsUpdating(false) } } const handleStatusChange = async (newStatus: UserStatus) => { setIsUpdating(true) try { const response = await fetch(`/api/v1/admin/customers/${customerId}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ status: newStatus }), }) if (!response.ok) { throw new Error('Failed to update status') } await queryClient.invalidateQueries({ queryKey: customerKeys.detail(customerId) }) } catch (err) { console.error('Error updating status:', err) } finally { setIsUpdating(false) } } const handleDeleteCustomer = async () => { await deleteCustomer.mutateAsync(customerId) router.push('/admin/customers') } // Loading state with enhanced styling if (isLoading) { return (

Loading customer details...

) } // Error state with enhanced styling if (isError) { return (

Failed to load customer

{error instanceof Error ? error.message : 'An error occurred'}

) } if (!customer) { return (

Customer not found

The customer you are looking for does not exist or you do not have access.

) } const currentSubscription = customer.subscriptions?.[0] const totalTokensUsed = customer.totalTokensUsed || 0 const tokenUsagePercent = currentSubscription ? Math.min((totalTokensUsed / currentSubscription.tokenLimit) * 100, 100) : 0 return (
{/* Hero Header Section */}
{/* Background decoration */}
{/* Back link */} Back to Customers
{/* Customer identity */}

{customer.name || customer.email}

{customer.email}

{/* Action buttons */}
{customer.status === 'ACTIVE' ? ( ) : ( )}
{/* Delete confirmation dialog */} {/* Create Order dialog */} { queryClient.invalidateQueries({ queryKey: customerKeys.detail(customerId) }) }} /> {/* Stats Row with colored icon backgrounds */}
{customer._count?.orders || 0}

Total Orders

{customer.orders?.filter((o: { status: string }) => o.status === 'FULFILLED').length || 0}

Active Servers

{totalTokensUsed.toLocaleString()}

Tokens Used

{currentSubscription?.plan.toLowerCase() || 'None'}

Current Plan

{/* Customer Profile Card */}

Profile

Customer information

{!isEditing && ( )}
{isEditing ? (
setEditForm({ ...editForm, name: e.target.value })} className="bg-background" />
setEditForm({ ...editForm, company: e.target.value })} className="bg-background" />
) : (

{customer.name || 'No name'}

Name

{customer.email}

Email

{customer.company || 'Not set'}

Company

{new Date(customer.createdAt).toLocaleDateString()}

Member Since

)}
{/* Subscription Card */}

Subscription

Current plan and token usage

{currentSubscription ? (

{currentSubscription.plan.toLowerCase()} Plan

{currentSubscription.tier.replace('_', ' ').toLowerCase()} tier

{currentSubscription.trialEndsAt && (

Trial Period Active

Ends {new Date(currentSubscription.trialEndsAt).toLocaleDateString()}

)}
) : (

No Active Subscription

This customer does not have an active subscription plan

)}
{/* Orders History */}

Orders History

{customer.orders?.length || 0} order{(customer.orders?.length || 0) !== 1 ? 's' : ''} total

{customer.orders && customer.orders.length > 0 ? (
{customer.orders.map((order: { id: string domain: string tier: string status: OrderStatus serverIp: string | null createdAt: Date | string }) => ( ))}
Domain Tier Status Server IP Created Actions
{order.domain} {order.tier.replace('_', ' ').toLowerCase()} {order.serverIp || '-'} {new Date(order.createdAt).toLocaleDateString()}
) : (

No Orders Yet

This customer has not placed any orders

)}
) }