diff --git a/src/app/(dashboard)/jobs/new/components/new-job-form.tsx b/src/app/(dashboard)/jobs/new/components/new-job-form.tsx index 8814fcd..7726d2b 100644 --- a/src/app/(dashboard)/jobs/new/components/new-job-form.tsx +++ b/src/app/(dashboard)/jobs/new/components/new-job-form.tsx @@ -3,7 +3,7 @@ import { useActionState, useEffect, useMemo, useState } from "react"; import Link from "next/link"; import { useRouter } from "next/navigation"; -import { Loader2, Send } from "lucide-react"; +import { Loader2, Send, Sparkles, TrendingDown } from "lucide-react"; import { toast } from "sonner"; import { Button } from "@/components/ui/button"; @@ -39,6 +39,26 @@ const PROSTHETIC_TYPES: ProstheticType[] = [ type PatientOption = { id: string; code: string; label: string }; +type Quote = { + amount: number; + currency: string; + source: "clinic_custom" | "clinic_discount" | "catalog"; + unitPrice: number; + catalogUnitPrice?: number; + catalogTotal?: number; + savings?: number; + discountPercent?: number; + teethCount: number; +}; + +function formatMoney(amount: number, currency: string): string { + try { + return new Intl.NumberFormat("tr-TR", { style: "currency", currency }).format(amount); + } catch { + return `${amount.toFixed(2)} ${currency}`; + } +} + export function NewJobForm({ labs, patients, @@ -54,6 +74,10 @@ export function NewJobForm({ patients.length > 0 ? patients[0].id : NONE_PATIENT, ); const [teeth, setTeeth] = useState([]); + const [labTenantId, setLabTenantId] = useState(labs[0]?.tenantId ?? ""); + const [prostheticType, setProstheticType] = useState(""); + const [quote, setQuote] = useState(null); + const [quoteLoading, setQuoteLoading] = useState(false); const patientById = useMemo( () => new Map(patients.map((p) => [p.id, p])), @@ -70,12 +94,48 @@ export function NewJobForm({ } }, [state, router]); + // Live price quote — debounced to a single fetch per 250ms when inputs settle. + useEffect(() => { + if (!labTenantId || !prostheticType || teeth.length === 0) { + setQuote(null); + setQuoteLoading(false); + return; + } + setQuoteLoading(true); + const ctrl = new AbortController(); + const timer = setTimeout(() => { + fetch("/api/pricing/quote", { + method: "POST", + signal: ctrl.signal, + headers: { "content-type": "application/json" }, + body: JSON.stringify({ + labTenantId, + prostheticType, + teethCount: teeth.length, + }), + }) + .then((r) => r.json()) + .then((d) => { + setQuote(d?.quote ?? null); + setQuoteLoading(false); + }) + .catch(() => { + if (!ctrl.signal.aborted) setQuoteLoading(false); + }); + }, 250); + return () => { + ctrl.abort(); + clearTimeout(timer); + }; + }, [labTenantId, prostheticType, teeth.length]); + return (
- + + +