garage-erp/apps/dashboard/modules/bills/bill-totals-summary.tsx
humam kerdiah 4bfd8c84a9 feat: add template checkpoint edit dialog and vendor management components
- 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.
2026-05-18 12:08:42 +04:00

114 lines
4.8 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client"
import { Card, CardContent } from "@/shared/components/ui/card"
import { Separator } from "@/shared/components/ui/separator"
import { formatEnum } from "@/shared/utils/formatters"
import { cn } from "@/shared/lib/utils"
import { useBill } from "./bill-context"
import { Money } from "@/shared/components/money"
export function BillTotalsSummary() {
const bill = useBill()
if (!bill) return null
const parts = bill.parts ?? []
const services = bill.services ?? []
const expenses = bill.expenses ?? []
const hasItems = parts.length > 0 || services.length > 0 || expenses.length > 0
if (!hasItems) return null
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)
}
const partsTotal = lineTotal(parts)
const servicesTotal = lineTotal(services)
const expensesTotal = lineTotal(expenses)
// Use API-computed values when available, fall back to manual calc
const subTotal = bill.sub_total ?? (partsTotal + servicesTotal + expensesTotal)
const taxAmount = bill.tax_amount ?? 0
const discountAmount = bill.discount_amount_major ?? 0
const total = bill.total ?? subTotal
const paymentsMade = bill.payments_made ?? 0
const balanceDue = bill.balance_due ?? total
const discount = bill.discount_type
return (
<Card>
<CardContent className="pt-6">
<div className="ml-auto max-w-sm space-y-2">
{parts.length > 0 && (
<div className="flex justify-between text-sm">
<span className="text-muted-foreground">Parts ({parts.length})</span>
<span>{<Money value={partsTotal} />}</span>
</div>
)}
{services.length > 0 && (
<div className="flex justify-between text-sm">
<span className="text-muted-foreground">Services ({services.length})</span>
<span>{<Money value={servicesTotal} />}</span>
</div>
)}
{expenses.length > 0 && (
<div className="flex justify-between text-sm">
<span className="text-muted-foreground">Expenses ({expenses.length})</span>
<span>{<Money value={expensesTotal} />}</span>
</div>
)}
<Separator />
<div className="flex justify-between text-sm font-medium">
<span>Subtotal</span>
<span>{<Money value={subTotal} />}</span>
</div>
{discountAmount > 0 && (
<div className="flex justify-between text-sm">
<span className="text-muted-foreground">Discount{discount && discount !== "no" ? ` (${formatEnum(discount)})` : ""}</span>
<span className="text-muted-foreground">{<Money value={discountAmount} />}</span>
</div>
)}
{taxAmount > 0 && (
<div className="flex justify-between text-sm">
<span className="text-muted-foreground">Tax{bill.tax?.name ? ` (${bill.tax.name})` : ""}</span>
<span>{<Money value={taxAmount} />}</span>
</div>
)}
<Separator />
<div className="flex justify-between text-base font-semibold">
<span>Total</span>
<span>{<Money value={total} />}</span>
</div>
{paymentsMade > 0 && (
<div className="flex justify-between text-sm text-muted-foreground">
<span>Amount Paid</span>
<span> {<Money value={paymentsMade} />}</span>
</div>
)}
<Separator />
<div className={cn(
"flex justify-between rounded-lg px-3 py-2 text-base font-bold",
balanceDue > 0 ? "bg-primary/10 text-primary" : "bg-green-500/10 text-green-600",
)}>
<span>Balance Due</span>
<span>{<Money value={balanceDue} />}</span>
</div>
</div>
</CardContent>
</Card>
)
}