'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 } from '@/hooks/use-customers' import { useQueryClient } from '@tanstack/react-query' import { customerKeys } from '@/hooks/use-customers' import { ArrowLeft, User, Mail, Building2, Calendar, Server, Loader2, AlertCircle, RefreshCw, Edit, Ban, CheckCircle, ExternalLink, CreditCard, Activity, Package, X, Save, } from 'lucide-react' 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 function UserStatusBadge({ status }: { status: UserStatus }) { const statusConfig: Record = { ACTIVE: { label: 'Active', className: 'bg-green-100 text-green-800' }, SUSPENDED: { label: 'Suspended', className: 'bg-red-100 text-red-800' }, PENDING_VERIFICATION: { label: 'Pending', className: 'bg-yellow-100 text-yellow-800' }, } const config = statusConfig[status] return ( {config.label} ) } function SubscriptionBadge({ status }: { status: SubscriptionStatus }) { const statusConfig: Record = { TRIAL: { label: 'Trial', className: 'bg-blue-100 text-blue-800' }, ACTIVE: { label: 'Active', className: 'bg-green-100 text-green-800' }, CANCELED: { label: 'Canceled', className: 'bg-gray-100 text-gray-800' }, PAST_DUE: { label: 'Past Due', className: 'bg-red-100 text-red-800' }, } const config = statusConfig[status] return ( {config.label} ) } function OrderStatusBadge({ status }: { status: OrderStatus }) { const statusConfig: Record = { PAYMENT_CONFIRMED: { label: 'Payment Confirmed', className: 'bg-blue-100 text-blue-800' }, AWAITING_SERVER: { label: 'Awaiting Server', className: 'bg-yellow-100 text-yellow-800' }, SERVER_READY: { label: 'Server Ready', className: 'bg-cyan-100 text-cyan-800' }, DNS_PENDING: { label: 'DNS Pending', className: 'bg-orange-100 text-orange-800' }, DNS_READY: { label: 'DNS Ready', className: 'bg-teal-100 text-teal-800' }, PROVISIONING: { label: 'Provisioning', className: 'bg-purple-100 text-purple-800' }, FULFILLED: { label: 'Fulfilled', className: 'bg-green-100 text-green-800' }, EMAIL_CONFIGURED: { label: 'Email Configured', className: 'bg-emerald-100 text-emerald-800' }, FAILED: { label: 'Failed', className: 'bg-red-100 text-red-800' }, } const config = statusConfig[status] || { label: status, className: 'bg-gray-100 text-gray-800' } return ( {config.label} ) } 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 { data: customer, isLoading, isError, error, refetch, isFetching } = useCustomer(customerId) 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) } } // Loading state if (isLoading) { return (

Loading customer details...

) } // Error state if (isError) { return (

Failed to load customer

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

) } if (!customer) { return (

Customer not found

) } const currentSubscription = customer.subscriptions?.[0] const totalTokensUsed = customer.totalTokensUsed || 0 const tokenUsagePercent = currentSubscription ? Math.min((totalTokensUsed / currentSubscription.tokenLimit) * 100, 100) : 0 return (
{/* Header */}

{customer.name || customer.email}

{customer.email}

{customer.status === 'ACTIVE' ? ( ) : ( )}
{/* Stats Row */}
{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 {!isEditing && ( )}
{isEditing ? ( <>
setEditForm({ ...editForm, name: e.target.value })} />
setEditForm({ ...editForm, company: e.target.value })} />
) : ( <>

{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 ends {new Date(currentSubscription.trialEndsAt).toLocaleDateString()}

)}
Token Usage {totalTokensUsed.toLocaleString()} / {currentSubscription.tokenLimit.toLocaleString()}
90 ? 'bg-red-500' : tokenUsagePercent > 70 ? 'bg-yellow-500' : 'bg-primary' }`} style={{ width: `${tokenUsagePercent}%` }} />

{(100 - tokenUsagePercent).toFixed(1)}% remaining

) : (

No active subscription

)}
{/* Orders History */}
Orders History {customer.orders?.length || 0} order{(customer.orders?.length || 0) !== 1 ? 's' : ''}
{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

)}
) }