garage-erp/apps/dashboard/modules/vendors/vendor-general-info.tsx
humam kerdiah 4f0a2f790f feat: add logo field to settings schema and update settings client to handle file uploads
feat: integrate dialog close context in vendor select field and CRUD dialog components

feat: enhance vendor general info to format status using utility function

feat: implement form dialog context for managing dialog close actions

feat: add async select field dialog close context for better form handling

fix: update form mutation hook to close dialog on successful submission

feat: extend document print types to include expense and credit note

feat: add settings update payload type to include logo and other fields

feat: create employee attendance and work history pages with resource management

feat: implement payment made and received detail pages with actions

feat: add quick shortcuts component for easy navigation in the dashboard

feat: create actions for payment made and received with print and delete options

feat: implement dialog close context for better dialog management

feat: add error parsing utility for improved error handling in API responses
2026-05-19 17:56:39 +04:00

137 lines
4.8 KiB
TypeScript

import {
Building2,
CreditCard,
DollarSign,
Globe,
Mail,
Phone,
User,
} 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"
import { Money } from "@/shared/components/money"
import { formatEnum } from "@/shared/utils/formatters"
type VendorData = {
id?: number
salutation?: string | null
first_name?: string | null
last_name?: string | null
company_name?: string | null
email?: string | null
phone?: string | null
alternate_phone?: string | null
website?: string | null
opening_balance?: number | string | null
credit_limit?: number | string | null
status?: string | null
created_at?: string
updated_at?: string
}
type VendorGeneralInfoProps = {
vendor: VendorData
}
function InfoItem({
icon: Icon,
label,
value,
}: {
icon: React.ComponentType<{ className?: string }>
label: string
value?: React.ReactNode
}) {
const isEmpty = value == null || value === ""
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">
{isEmpty ? <span className="text-muted-foreground"></span> : value}
</span>
</div>
</div>
)
}
export function VendorGeneralInfo({ vendor }: VendorGeneralInfoProps) {
const fullName = [vendor.salutation, vendor.first_name, vendor.last_name]
.filter(Boolean)
.join(" ")
.trim()
return (
<div className="grid gap-6 md:grid-cols-2">
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Building2 className="size-4" />
Vendor Information
</CardTitle>
</CardHeader>
<CardContent className="grid gap-4">
<div className="flex flex-wrap items-center gap-2">
<Badge variant="secondary">{vendor.company_name || fullName || "Unknown vendor"}</Badge>
{vendor.status && (
<Badge variant={vendor.status === "active" ? "default" : "outline"}>
{formatEnum(vendor.status)}
</Badge>
)}
</div>
<Separator />
<div className="grid gap-4 sm:grid-cols-2">
<InfoItem icon={Building2} label="Company" value={vendor.company_name} />
<InfoItem icon={User} label="Contact Name" value={fullName} />
</div>
</CardContent>
</Card>
<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={vendor.email} />
<InfoItem icon={Phone} label="Phone" value={vendor.phone} />
<InfoItem icon={Phone} label="Alternate Phone" value={vendor.alternate_phone} />
<InfoItem icon={Globe} label="Website" value={vendor.website} />
</CardContent>
</Card>
<Card className="md:col-span-2">
<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={vendor.opening_balance != null ? <Money value={vendor.opening_balance} /> : null}
/>
<InfoItem
icon={CreditCard}
label="Credit Limit"
value={vendor.credit_limit != null ? <Money value={vendor.credit_limit} /> : null}
/>
</CardContent>
</Card>
</div>
)
}