196 lines
5.9 KiB
Svelte
196 lines
5.9 KiB
Svelte
<script lang="ts">
|
|
import Title from "$lib/components/atoms/title.svelte";
|
|
import {
|
|
convertAndFormatCurrency,
|
|
currencyStore,
|
|
} from "$lib/domains/currency/view/currency.vm.svelte";
|
|
import { passengerInfoVM } from "$lib/domains/passengerinfo/view/passenger.info.vm.svelte";
|
|
import { flightTicketStore } from "../../data/store";
|
|
import { calculateTicketPrices } from "./total.calculator";
|
|
import { Badge } from "$lib/components/ui/badge";
|
|
import Icon from "$lib/components/atoms/icon.svelte";
|
|
import TagIcon from "~icons/lucide/tag"; // Import a tag/coupon icon
|
|
|
|
let totals = $state(
|
|
calculateTicketPrices($flightTicketStore, passengerInfoVM.passengerInfos),
|
|
);
|
|
let changing = $state(false);
|
|
let appliedCoupon = $state(
|
|
$flightTicketStore?.priceDetails?.appliedCoupon || null,
|
|
);
|
|
let couponDescription = $state("");
|
|
|
|
$effect(() => {
|
|
changing = true;
|
|
totals = calculateTicketPrices(
|
|
$flightTicketStore,
|
|
passengerInfoVM.passengerInfos,
|
|
);
|
|
appliedCoupon = $flightTicketStore?.priceDetails?.appliedCoupon || null;
|
|
changing = false;
|
|
});
|
|
|
|
flightTicketStore.subscribe((val) => {
|
|
changing = true;
|
|
totals = calculateTicketPrices(val, passengerInfoVM.passengerInfos);
|
|
appliedCoupon = val?.priceDetails?.appliedCoupon || null;
|
|
changing = false;
|
|
});
|
|
</script>
|
|
|
|
<div class="flex flex-col gap-4 rounded-lg bg-white p-4 drop-shadow-lg md:p-8">
|
|
<Title size="h4" weight="medium">Payment Summary</Title>
|
|
<div class="h-0.5 w-full border-t-2 border-gray-200"></div>
|
|
|
|
{#if !changing}
|
|
<!-- Base Ticket Price Breakdown -->
|
|
<div class="flex flex-col gap-2">
|
|
<Title size="p" weight="medium">Base Ticket Price</Title>
|
|
<div class="flex justify-between text-sm">
|
|
<span>Total Ticket Price</span>
|
|
<span>{convertAndFormatCurrency(totals.baseTicketPrice)}</span>
|
|
</div>
|
|
<div class="ml-4 text-sm text-gray-600">
|
|
<span>
|
|
Price per passenger (x{passengerInfoVM.passengerInfos.length})
|
|
</span>
|
|
<span class="float-right">
|
|
{convertAndFormatCurrency(totals.pricePerPassenger)}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Baggage Costs -->
|
|
{#if totals.totalBaggageCost > 0}
|
|
<div class="mt-2 flex flex-col gap-2 border-t pt-2">
|
|
<Title size="p" weight="medium">Baggage Charges</Title>
|
|
{#each totals.passengerBaggageCosts as passengerBaggage}
|
|
{#if passengerBaggage.totalBaggageCost > 0}
|
|
<div class="flex flex-col gap-1">
|
|
<span class="text-sm font-medium">
|
|
{passengerBaggage.passengerName}
|
|
</span>
|
|
{#if passengerBaggage.personalBagCost > 0}
|
|
<div
|
|
class="ml-4 flex justify-between text-sm text-gray-600"
|
|
>
|
|
<span>Personal Bag</span>
|
|
<span>
|
|
{convertAndFormatCurrency(
|
|
passengerBaggage.personalBagCost,
|
|
)}
|
|
</span>
|
|
</div>
|
|
{/if}
|
|
{#if passengerBaggage.handBagCost > 0}
|
|
<div
|
|
class="ml-4 flex justify-between text-sm text-gray-600"
|
|
>
|
|
<span>Hand Baggage</span>
|
|
<span>
|
|
{convertAndFormatCurrency(
|
|
passengerBaggage.handBagCost,
|
|
)}
|
|
</span>
|
|
</div>
|
|
{/if}
|
|
{#if passengerBaggage.checkedBagCost > 0}
|
|
<div
|
|
class="ml-4 flex justify-between text-sm text-gray-600"
|
|
>
|
|
<span>Checked Baggage</span>
|
|
<span>
|
|
{convertAndFormatCurrency(
|
|
passengerBaggage.checkedBagCost,
|
|
)}
|
|
</span>
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
{/if}
|
|
{/each}
|
|
<div class="flex justify-between text-sm font-medium">
|
|
<span>Total Baggage Charges</span>
|
|
<span
|
|
>{convertAndFormatCurrency(totals.totalBaggageCost)}</span
|
|
>
|
|
</div>
|
|
</div>
|
|
{/if}
|
|
|
|
<!-- Final Total -->
|
|
<div class="mt-4 flex flex-col gap-2 border-t pt-4">
|
|
<div class="flex justify-between text-sm">
|
|
<span>Subtotal</span>
|
|
<span>{convertAndFormatCurrency(totals.subtotal)}</span>
|
|
</div>
|
|
|
|
<!-- Coupon section -->
|
|
{#if totals.discountAmount > 0 && appliedCoupon}
|
|
<div class="my-2 flex flex-col gap-1 rounded-lg bg-green-50 p-3">
|
|
<div class="flex items-center justify-between">
|
|
<div class="flex items-center gap-2 text-green-700">
|
|
<Icon icon={TagIcon} cls="h-4 w-4" />
|
|
<span class="font-medium"
|
|
>Coupon Applied: {appliedCoupon}</span
|
|
>
|
|
</div>
|
|
<Badge
|
|
variant="outline"
|
|
class="border-green-600 px-2 py-0.5 text-green-600"
|
|
>
|
|
{Math.round(
|
|
(totals.discountAmount / totals.subtotal) * 100,
|
|
)}% OFF
|
|
</Badge>
|
|
</div>
|
|
<div class="mt-1 flex justify-between text-sm text-green-600">
|
|
<span>Discount</span>
|
|
<span
|
|
>-{convertAndFormatCurrency(
|
|
totals.discountAmount,
|
|
)}</span
|
|
>
|
|
</div>
|
|
{#if $flightTicketStore?.priceDetails?.couponDescription}
|
|
<p class="mt-1 text-xs text-green-700">
|
|
{$flightTicketStore.priceDetails.couponDescription}
|
|
</p>
|
|
{/if}
|
|
</div>
|
|
{:else if totals.discountAmount > 0}
|
|
<div class="flex justify-between text-sm text-green-600">
|
|
<span>Discount</span>
|
|
<span>-{convertAndFormatCurrency(totals.discountAmount)}</span
|
|
>
|
|
</div>
|
|
{/if}
|
|
|
|
<div class="flex justify-between font-medium">
|
|
<Title size="h5" weight="medium"
|
|
>Total ({$currencyStore.code})</Title
|
|
>
|
|
<span>{convertAndFormatCurrency(totals.finalTotal)}</span>
|
|
</div>
|
|
</div>
|
|
{:else}
|
|
<div class="grid place-items-center p-2 text-center">
|
|
<span>Calculating . . .</span>
|
|
</div>
|
|
{/if}
|
|
|
|
<div class="mt-4 rounded-lg bg-gray-50 p-4 text-xs text-gray-600">
|
|
<p class="mb-2 font-medium">Important Information:</p>
|
|
<ul class="list-disc space-y-1 pl-4">
|
|
<li>Prices include all applicable taxes and fees</li>
|
|
<li>Cancellation and change fees may apply as per our policy</li>
|
|
<li>Additional baggage fees may apply based on airline policy</li>
|
|
{#if appliedCoupon}
|
|
<li class="text-green-600">
|
|
Discount applied via coupon: {appliedCoupon}
|
|
</li>
|
|
{/if}
|
|
</ul>
|
|
</div>
|
|
</div>
|