- Implemented TemplateCheckpointEditDialog for creating and editing inspection checkpoints. - Added VendorActions component for managing vendor actions including edit, activate/deactivate, and delete. - Created VendorContext for managing vendor state across components. - Developed VendorGeneralInfo component to display detailed vendor information. - Introduced AedSymbol and Money components for consistent currency representation. - Added PromptDialog for user input prompts throughout the application. - Implemented RelationLink component for unified related-data display in CRUD tables. - Created InspectionTemplatesClient for API interactions related to inspection templates.
85 lines
3.6 KiB
TypeScript
85 lines
3.6 KiB
TypeScript
"use client"
|
|
|
|
import { Card, CardContent } from "@/shared/components/ui/card"
|
|
import { Separator } from "@/shared/components/ui/separator"
|
|
import { formatEnum } from "@/shared/utils/formatters"
|
|
import { useEstimate } from "./estimate-context"
|
|
import { Money } from "@/shared/components/money"
|
|
|
|
export function EstimateTotalsSummary() {
|
|
const estimate = useEstimate()
|
|
|
|
if (!estimate) return null
|
|
|
|
const parts = estimate.estimate_parts ?? []
|
|
const services = estimate.estimate_services ?? []
|
|
const expenses = estimate.estimate_expense_items ?? []
|
|
const discount = estimate.discount
|
|
const displayTotal = parseFloat(String(estimate.total ?? 0)) || 0
|
|
|
|
const hasItems = parts.length > 0 || services.length > 0 || expenses.length > 0
|
|
|
|
if (!hasItems && displayTotal === 0) return null
|
|
|
|
const subTotal = parseFloat(String(estimate.sub_total ?? 0)) || 0
|
|
|
|
function lineTotal(items: { quantity?: string | number; rate?: string | number }[]) {
|
|
return items.reduce((sum, item) => {
|
|
const qty = parseFloat(String(item.quantity ?? 0))
|
|
const rate = parseFloat(String(item.rate ?? 0))
|
|
return sum + (isNaN(qty) || isNaN(rate) ? 0 : qty * rate)
|
|
}, 0)
|
|
}
|
|
|
|
return (
|
|
<Card>
|
|
<CardContent className="pt-6">
|
|
<div className="ml-auto max-w-sm space-y-2">
|
|
{hasItems && (
|
|
<>
|
|
{parts.length > 0 && (
|
|
<div className="flex justify-between text-sm">
|
|
<span className="text-muted-foreground">Parts ({parts.length})</span>
|
|
<span>{<Money value={lineTotal(parts)} />}</span>
|
|
</div>
|
|
)}
|
|
{services.length > 0 && (
|
|
<div className="flex justify-between text-sm">
|
|
<span className="text-muted-foreground">Services ({services.length})</span>
|
|
<span>{<Money value={lineTotal(services)} />}</span>
|
|
</div>
|
|
)}
|
|
{expenses.length > 0 && (
|
|
<div className="flex justify-between text-sm">
|
|
<span className="text-muted-foreground">Expenses ({expenses.length})</span>
|
|
<span>{<Money value={lineTotal(expenses)} />}</span>
|
|
</div>
|
|
)}
|
|
<Separator />
|
|
</>
|
|
)}
|
|
|
|
<div className="flex justify-between text-sm font-medium">
|
|
<span>Subtotal</span>
|
|
<span>{<Money value={subTotal} />}</span>
|
|
</div>
|
|
|
|
{discount && discount !== "no" && (
|
|
<div className="flex justify-between text-sm">
|
|
<span className="text-muted-foreground">Discount ({formatEnum(discount)})</span>
|
|
<span className="text-muted-foreground">Applied</span>
|
|
</div>
|
|
)}
|
|
|
|
<Separator />
|
|
|
|
<div className="flex justify-between text-base font-semibold">
|
|
<span>Total</span>
|
|
<span>{<Money value={displayTotal} />}</span>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
)
|
|
}
|