diff --git a/client/src/components/Planner/BookingCostsSection.tsx b/client/src/components/Planner/BookingCostsSection.tsx index 1683b895..2938b178 100644 --- a/client/src/components/Planner/BookingCostsSection.tsx +++ b/client/src/components/Planner/BookingCostsSection.tsx @@ -12,8 +12,10 @@ import type { BudgetItem } from '../../types' * button (the modal saves the booking first, then opens the full Costs editor); * once linked it shows the expense with edit / remove actions. */ -export function BookingCostsSection({ reservationId, onCreate, onEdit, onRemove }: { +export function BookingCostsSection({ reservationId, pendingExpense, onCreate, onEdit, onRemove }: { reservationId: number | null + /** A cost parsed from an import that will be linked on save — previewed before the booking exists. */ + pendingExpense?: { total_price: number; currency?: string | null; category: string } | null onCreate: () => void onEdit: (item: BudgetItem) => void onRemove: (item: BudgetItem) => void @@ -27,6 +29,25 @@ export function BookingCostsSection({ reservationId, onCreate, onEdit, onRemove const labelCls = 'block text-[11px] font-semibold uppercase tracking-[0.08em] text-content-faint mb-[6px]' + // Import review (booking not saved yet): preview the parsed cost that will be linked on save. + if (!linked && pendingExpense && pendingExpense.total_price > 0) { + const meta = catMeta(pendingExpense.category) + const Icon = meta.Icon + return ( +
+ +
+ +
+
{t(meta.labelKey)}
+
{t('reservations.createExpenseHint')}
+
+ {formatMoney(pendingExpense.total_price, pendingExpense.currency || base, locale)} +
+
+ ) + } + if (linked) { const meta = catMeta(linked.category) const Icon = meta.Icon diff --git a/client/src/components/Planner/ReservationModal.tsx b/client/src/components/Planner/ReservationModal.tsx index 2518f2dd..c5bae7b1 100644 --- a/client/src/components/Planner/ReservationModal.tsx +++ b/client/src/components/Planner/ReservationModal.tsx @@ -299,6 +299,13 @@ export function ReservationModal({ isOpen, onClose, onSave, reservation, days, p try { await deleteBudgetItem(Number(tripId), item.id) } catch { toast.error(t('common.unknownError')) } } + // On an import review (not yet saved), preview the parsed price as the cost that will be linked. + const prefillMeta = prefill?.metadata && typeof prefill.metadata === 'object' ? (prefill.metadata as Record) : null + const prefillPrice = Number(prefillMeta?.price) + const pendingExpense = !reservation && Number.isFinite(prefillPrice) && prefillPrice > 0 + ? { total_price: prefillPrice, currency: (prefillMeta?.priceCurrency as string | null) ?? null, category: typeToCostCategory(form.type) } + : null + const handleFileChange = async (e) => { const file = (e.target as HTMLInputElement).files?.[0] if (!file) return @@ -685,6 +692,7 @@ export function ReservationModal({ isOpen, onClose, onSave, reservation, days, p {isBudgetEnabled && ( ) : null + const prefillPrice = Number(prefillMeta?.price) + const pendingExpense = !reservation && Number.isFinite(prefillPrice) && prefillPrice > 0 + ? { total_price: prefillPrice, currency: (prefillMeta?.priceCurrency as string | null) ?? null, category: typeToCostCategory(form.type) } + : null + const handleFileChange = async (e: React.ChangeEvent) => { const file = e.target.files?.[0] if (!file) return @@ -740,6 +747,7 @@ export function TransportModal({ isOpen, onClose, onSave, reservation, days, sel {isBudgetEnabled && (