168 lines
6.1 KiB
TypeScript
168 lines
6.1 KiB
TypeScript
import {
|
|
User,
|
|
Mail,
|
|
Phone,
|
|
MapPin,
|
|
Building2,
|
|
Globe,
|
|
DollarSign,
|
|
CreditCard,
|
|
Tag,
|
|
Users,
|
|
FileText,
|
|
} from "lucide-react"
|
|
import {
|
|
Card,
|
|
CardContent,
|
|
CardHeader,
|
|
CardTitle,
|
|
} from "@/shared/components/ui/card"
|
|
import { Badge } from "@/shared/components/ui/badge"
|
|
import { Separator } from "@/shared/components/ui/separator"
|
|
|
|
type CustomerData = {
|
|
id?: number
|
|
customer_type_id?: number
|
|
salutation?: string
|
|
first_name?: string
|
|
last_name?: string
|
|
company_name?: string
|
|
email?: string
|
|
phone?: string
|
|
alternate_phone?: string
|
|
opening_balance?: number
|
|
credit_limit?: number
|
|
website?: string
|
|
referral_source_id?: number
|
|
payment_terms_id?: number
|
|
address_line_1?: string
|
|
address_line_2?: string
|
|
country_id?: number
|
|
state_id?: number
|
|
city?: string
|
|
zip_code?: string
|
|
created_at?: string
|
|
updated_at?: string
|
|
customer_type?: { id?: number; name?: string }
|
|
referral_source?: { id?: number; title?: string }
|
|
payment_terms?: { id?: number; title?: string }
|
|
country?: { id?: number; name?: string }
|
|
state?: { id?: number; name?: string }
|
|
}
|
|
|
|
type CustomerGeneralInfoProps = {
|
|
customer: CustomerData
|
|
}
|
|
|
|
function InfoItem({
|
|
icon: Icon,
|
|
label,
|
|
value,
|
|
}: {
|
|
icon: React.ComponentType<{ className?: string }>
|
|
label: string
|
|
value?: string | number | null
|
|
}) {
|
|
return (
|
|
<div className="flex items-start gap-3">
|
|
<div className="flex size-9 shrink-0 items-center justify-center rounded-lg bg-muted text-muted-foreground">
|
|
<Icon className="size-4" />
|
|
</div>
|
|
<div className="flex flex-col gap-0.5">
|
|
<span className="text-xs text-muted-foreground">{label}</span>
|
|
<span className="text-sm font-medium">
|
|
{value != null && value !== "" ? (
|
|
String(value)
|
|
) : (
|
|
<span className="text-muted-foreground">—</span>
|
|
)}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export function CustomerGeneralInfo({ customer }: CustomerGeneralInfoProps) {
|
|
const fullName = [customer.salutation, customer.first_name, customer.last_name]
|
|
.filter(Boolean)
|
|
.join(" ")
|
|
|
|
return (
|
|
<div className="grid gap-6 md:grid-cols-2">
|
|
{/* Personal Info */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center gap-2">
|
|
<User className="size-4" />
|
|
Personal Information
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="grid gap-4">
|
|
<div className="flex flex-wrap items-center gap-2">
|
|
<Badge variant="secondary">{fullName || "Unknown"}</Badge>
|
|
{customer.customer_type?.name && (
|
|
<Badge variant="outline">{customer.customer_type.name}</Badge>
|
|
)}
|
|
</div>
|
|
<Separator />
|
|
<div className="grid gap-4 sm:grid-cols-2">
|
|
<InfoItem icon={User} label="Full Name" value={fullName} />
|
|
<InfoItem icon={Building2} label="Company" value={customer.company_name} />
|
|
<InfoItem icon={Tag} label="Customer Type" value={customer.customer_type?.name} />
|
|
<InfoItem icon={Users} label="Referral Source" value={customer.referral_source?.title} />
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Contact Info */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center gap-2">
|
|
<Phone className="size-4" />
|
|
Contact Details
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="grid gap-4 sm:grid-cols-2">
|
|
<InfoItem icon={Mail} label="Email" value={customer.email} />
|
|
<InfoItem icon={Phone} label="Phone" value={customer.phone} />
|
|
<InfoItem icon={Phone} label="Alternate Phone" value={customer.alternate_phone} />
|
|
<InfoItem icon={Globe} label="Website" value={customer.website} />
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Address */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center gap-2">
|
|
<MapPin className="size-4" />
|
|
Address
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="grid gap-4 sm:grid-cols-2">
|
|
<InfoItem icon={MapPin} label="Address Line 1" value={customer.address_line_1} />
|
|
<InfoItem icon={MapPin} label="Address Line 2" value={customer.address_line_2} />
|
|
<InfoItem icon={MapPin} label="City" value={customer.city} />
|
|
<InfoItem icon={MapPin} label="ZIP Code" value={customer.zip_code} />
|
|
<InfoItem icon={MapPin} label="State" value={customer.state?.name} />
|
|
<InfoItem icon={MapPin} label="Country" value={customer.country?.name} />
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Financial Info */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center gap-2">
|
|
<DollarSign className="size-4" />
|
|
Financial Details
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="grid gap-4 sm:grid-cols-2">
|
|
<InfoItem icon={DollarSign} label="Opening Balance" value={customer.opening_balance} />
|
|
<InfoItem icon={CreditCard} label="Credit Limit" value={customer.credit_limit} />
|
|
<InfoItem icon={FileText} label="Payment Terms" value={customer.payment_terms?.title} />
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
)
|
|
}
|