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.
This commit is contained in:
@@ -3,7 +3,8 @@
|
||||
import { Receipt } from "lucide-react"
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/shared/components/ui/card"
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/shared/components/ui/table"
|
||||
import { formatCurrency, formatNumber } from "@/shared/utils/formatters"
|
||||
import { formatNumber } from "@/shared/utils/formatters"
|
||||
import { Money } from "@/shared/components/money"
|
||||
|
||||
type BillExpense = {
|
||||
id: number
|
||||
@@ -62,14 +63,14 @@ export function BillExpensesSection({ expenses = [] }: BillExpensesSectionProps)
|
||||
{expense.description || "—"}
|
||||
</TableCell>
|
||||
<TableCell className="text-right">{formatNumber(qty)}</TableCell>
|
||||
<TableCell className="text-right">{formatCurrency(rate)}</TableCell>
|
||||
<TableCell className="text-right font-medium">{formatCurrency(amount)}</TableCell>
|
||||
<TableCell className="text-right">{<Money value={rate} />}</TableCell>
|
||||
<TableCell className="text-right font-medium">{<Money value={amount} />}</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
})}
|
||||
<TableRow className="bg-muted/50 font-medium">
|
||||
<TableCell colSpan={4} className="text-right">Subtotal</TableCell>
|
||||
<TableCell className="text-right">{formatCurrency(subtotal)}</TableCell>
|
||||
<TableCell className="text-right">{<Money value={subtotal} />}</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
|
||||
@@ -19,9 +19,10 @@ import {
|
||||
} from "@/shared/components/ui/card"
|
||||
import { Badge } from "@/shared/components/ui/badge"
|
||||
import { cn } from "@/shared/lib/utils"
|
||||
import { formatDate, formatCurrency, formatEnum } from "@/shared/utils/formatters"
|
||||
import { formatDate, formatEnum } from "@/shared/utils/formatters"
|
||||
import { getFullName } from "@/shared/utils/getFullName"
|
||||
import { useBill } from "./bill-context"
|
||||
import { Money } from "@/shared/components/money"
|
||||
|
||||
function InfoItem({
|
||||
icon: Icon,
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
import { Wrench } from "lucide-react"
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/shared/components/ui/card"
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/shared/components/ui/table"
|
||||
import { formatCurrency, formatNumber } from "@/shared/utils/formatters"
|
||||
import { formatNumber } from "@/shared/utils/formatters"
|
||||
import { Money } from "@/shared/components/money"
|
||||
|
||||
type BillPart = {
|
||||
id: number
|
||||
@@ -62,14 +63,14 @@ export function BillPartsSection({ parts = [] }: BillPartsSectionProps) {
|
||||
{part.description || "—"}
|
||||
</TableCell>
|
||||
<TableCell className="text-right">{formatNumber(qty)}</TableCell>
|
||||
<TableCell className="text-right">{formatCurrency(rate)}</TableCell>
|
||||
<TableCell className="text-right font-medium">{formatCurrency(amount)}</TableCell>
|
||||
<TableCell className="text-right">{<Money value={rate} />}</TableCell>
|
||||
<TableCell className="text-right font-medium">{<Money value={amount} />}</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
})}
|
||||
<TableRow className="bg-muted/50 font-medium">
|
||||
<TableCell colSpan={4} className="text-right">Subtotal</TableCell>
|
||||
<TableCell className="text-right">{formatCurrency(subtotal)}</TableCell>
|
||||
<TableCell className="text-right">{<Money value={subtotal} />}</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
|
||||
@@ -17,7 +17,6 @@ import { PaymentMadeForm } from "@/modules/payment-mades/payment-made-form"
|
||||
import { useBill } from "./bill-context"
|
||||
import { formatDate } from "@/shared/utils/formatters"
|
||||
import { getFullName } from "@/shared/utils/getFullName"
|
||||
import { toRelation } from "@/shared/lib/utils"
|
||||
|
||||
export function BillPaymentsSection() {
|
||||
const bill = useBill()
|
||||
@@ -39,8 +38,17 @@ export function BillPaymentsSection() {
|
||||
{(resourceId) => (
|
||||
<PaymentMadeForm
|
||||
initialData={{
|
||||
amount: bill?.balance_due,
|
||||
vendor: toRelation(bill?.vendor?.id, getFullName(bill?.vendor as any)),
|
||||
// API-shape keys so PaymentMadeForm.mapToFormValues
|
||||
// can pick them up (it reads `payment_made` / `vendor_id`
|
||||
// / `vendor.*`, not the form-shape fields). bill_id +
|
||||
// bill_number are required because the bill field is
|
||||
// hidden when billId is supplied, but the schema still
|
||||
// demands a populated bill relation.
|
||||
bill_id: bill?.id,
|
||||
bill_number: bill?.bill_number,
|
||||
payment_made: bill?.balance_due,
|
||||
vendor_id: bill?.vendor?.id,
|
||||
vendor: bill?.vendor,
|
||||
payment_for: "bill",
|
||||
}}
|
||||
billId={bill?.id}
|
||||
@@ -77,7 +85,7 @@ export function BillPaymentsSection() {
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<UserIcon className="h-4 w-4 text-muted-foreground" />
|
||||
<span>{item.vendor?.name || item.vendor_name || "—"}</span>
|
||||
<span>{item.vendor?.company_name || getFullName(item.vendor) || item.vendor_name || "—"}</span>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
import { Briefcase } from "lucide-react"
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/shared/components/ui/card"
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/shared/components/ui/table"
|
||||
import { formatCurrency, formatNumber } from "@/shared/utils/formatters"
|
||||
import { formatNumber } from "@/shared/utils/formatters"
|
||||
import { Money } from "@/shared/components/money"
|
||||
|
||||
type BillService = {
|
||||
id: number
|
||||
@@ -62,14 +63,14 @@ export function BillServicesSection({ services = [] }: BillServicesSectionProps)
|
||||
{service.description || "—"}
|
||||
</TableCell>
|
||||
<TableCell className="text-right">{formatNumber(qty)}</TableCell>
|
||||
<TableCell className="text-right">{formatCurrency(rate)}</TableCell>
|
||||
<TableCell className="text-right font-medium">{formatCurrency(amount)}</TableCell>
|
||||
<TableCell className="text-right">{<Money value={rate} />}</TableCell>
|
||||
<TableCell className="text-right font-medium">{<Money value={amount} />}</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
})}
|
||||
<TableRow className="bg-muted/50 font-medium">
|
||||
<TableCell colSpan={4} className="text-right">Subtotal</TableCell>
|
||||
<TableCell className="text-right">{formatCurrency(subtotal)}</TableCell>
|
||||
<TableCell className="text-right">{<Money value={subtotal} />}</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
|
||||
import { Card, CardContent } from "@/shared/components/ui/card"
|
||||
import { Separator } from "@/shared/components/ui/separator"
|
||||
import { formatCurrency, formatEnum } from "@/shared/utils/formatters"
|
||||
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()
|
||||
@@ -46,70 +47,65 @@ export function BillTotalsSummary() {
|
||||
{parts.length > 0 && (
|
||||
<div className="flex justify-between text-sm">
|
||||
<span className="text-muted-foreground">Parts ({parts.length})</span>
|
||||
<span>{formatCurrency(partsTotal)}</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>{formatCurrency(servicesTotal)}</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>{formatCurrency(expensesTotal)}</span>
|
||||
<span>{<Money value={expensesTotal} />}</span>
|
||||
</div>
|
||||
)}
|
||||
<Separator />
|
||||
|
||||
<div className="flex justify-between text-sm font-medium">
|
||||
<span>Subtotal</span>
|
||||
<span>{formatCurrency(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">−{formatCurrency(discountAmount)}</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>{formatCurrency(taxAmount)}</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 bg-muted/50",
|
||||
"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>Total</span>
|
||||
<span>{formatCurrency(total)}</span>
|
||||
<span>Balance Due</span>
|
||||
<span>{<Money value={balanceDue} />}</span>
|
||||
</div>
|
||||
|
||||
{paymentsMade > 0 && (
|
||||
<div className="flex justify-between text-sm">
|
||||
<span className="text-muted-foreground">Payments Made</span>
|
||||
<span className="text-emerald-600">−{formatCurrency(paymentsMade)}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{paymentsMade > 0 && (
|
||||
<>
|
||||
<Separator />
|
||||
<div className={cn(
|
||||
"flex justify-between rounded-lg px-3 py-2 text-base font-semibold",
|
||||
balanceDue > 0 ? "text-destructive bg-destructive/5" : "text-emerald-700 bg-emerald-50 dark:bg-emerald-950/20",
|
||||
)}>
|
||||
<span>Balance Due</span>
|
||||
<span>{formatCurrency(balanceDue)}</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
Reference in New Issue
Block a user