diff --git a/apps/frontend/src/lib/domains/checkout/checkout-steps-indicator.svelte b/apps/frontend/src/lib/domains/checkout/checkout-steps-indicator.svelte index c6dbab3..5faa2a3 100644 --- a/apps/frontend/src/lib/domains/checkout/checkout-steps-indicator.svelte +++ b/apps/frontend/src/lib/domains/checkout/checkout-steps-indicator.svelte @@ -2,16 +2,16 @@ import Icon from "$lib/components/atoms/icon.svelte"; import { buttonVariants } from "$lib/components/ui/button/button.svelte"; import { Sheet, SheetContent, SheetTrigger } from "$lib/components/ui/sheet"; + import { CheckoutStep } from "$lib/domains/order/data/entities"; import { cn } from "$lib/utils"; import ChevronDownIcon from "~icons/lucide/chevron-down"; import CloseIcon from "~icons/lucide/x"; - import { CheckoutStep } from "../../data/entities"; import { checkoutVM } from "./checkout.vm.svelte"; const checkoutSteps = [ - { id: CheckoutStep.Initial, label: "Passenger Details" }, + { id: CheckoutStep.Initial, label: "Initial Details" }, { id: CheckoutStep.Payment, label: "Payment" }, - { id: CheckoutStep.Verification, label: "Verify Details" }, + { id: CheckoutStep.Verification, label: "Verify" }, { id: CheckoutStep.Confirmation, label: "Confirmation" }, ]; diff --git a/apps/frontend/src/lib/domains/checkout/payment-info-section/billing-details-form.svelte b/apps/frontend/src/lib/domains/checkout/payment-info-section/billing-details-form.svelte index 07343c8..cc3de96 100644 --- a/apps/frontend/src/lib/domains/checkout/payment-info-section/billing-details-form.svelte +++ b/apps/frontend/src/lib/domains/checkout/payment-info-section/billing-details-form.svelte @@ -4,7 +4,7 @@ import * as Select from "$lib/components/ui/select"; import { COUNTRIES_SELECT } from "$lib/core/countries"; import { capitalize } from "$lib/core/string.utils"; - import type { CustomerInfoModel } from "$lib/domains/ticket/data/entities/create.entities"; + import type { CustomerInfoModel } from "$lib/domains/customerinfo/data"; import { billingDetailsVM } from "./billing.details.vm.svelte"; let { info = $bindable() }: { info: CustomerInfoModel } = $props(); diff --git a/apps/frontend/src/lib/domains/checkout/payment-info-section/billing.details.vm.svelte.ts b/apps/frontend/src/lib/domains/checkout/payment-info-section/billing.details.vm.svelte.ts index 4da2ac2..3f1ac1f 100644 --- a/apps/frontend/src/lib/domains/checkout/payment-info-section/billing.details.vm.svelte.ts +++ b/apps/frontend/src/lib/domains/checkout/payment-info-section/billing.details.vm.svelte.ts @@ -1,5 +1,8 @@ -import { type CustomerInfoModel, Gender } from "$lib/domains/customerinfo/data"; -import { customerInfoModel } from "$lib/domains/passengerinfo/data/entities"; +import { + type CustomerInfoModel, + customerInfoModel, + Gender, +} from "$lib/domains/customerinfo/data"; import { z } from "zod"; export class BillingDetailsViewModel { diff --git a/apps/frontend/src/lib/domains/checkout/payment-info-section/index.svelte b/apps/frontend/src/lib/domains/checkout/payment-info-section/index.svelte index c63a21a..599a2d4 100644 --- a/apps/frontend/src/lib/domains/checkout/payment-info-section/index.svelte +++ b/apps/frontend/src/lib/domains/checkout/payment-info-section/index.svelte @@ -2,21 +2,13 @@ import ButtonLoadableText from "$lib/components/atoms/button-loadable-text.svelte"; import Icon from "$lib/components/atoms/icon.svelte"; import Title from "$lib/components/atoms/title.svelte"; - import Button, { - buttonVariants, - } from "$lib/components/ui/button/button.svelte"; - import * as Dialog from "$lib/components/ui/dialog"; + import Button from "$lib/components/ui/button/button.svelte"; import { ckFlowVM } from "$lib/domains/ckflow/view/ckflow.vm.svelte"; + import { customerInfoVM } from "$lib/domains/customerinfo/view/customerinfo.vm.svelte"; import { CheckoutStep } from "$lib/domains/order/data/entities"; - import { passengerInfoVM } from "$lib/domains/passengerinfo/view/passenger.info.vm.svelte"; - import { flightTicketStore } from "$lib/domains/ticket/data/store"; - import TicketDetailsModal from "$lib/domains/ticket/view/ticket/ticket-details-modal.svelte"; - import { cn } from "$lib/utils"; - import { formatDate } from "@pkg/logic/core/date.utils"; import { onMount } from "svelte"; import { toast } from "svelte-sonner"; import RightArrowIcon from "~icons/solar/arrow-right-broken"; - import ArrowsExchangeIcon from "~icons/tabler/arrows-exchange-2"; import { checkoutVM } from "../checkout.vm.svelte"; import BillingDetailsForm from "./billing-details-form.svelte"; import { billingDetailsVM } from "./billing.details.vm.svelte"; @@ -56,14 +48,6 @@ }, 1000); } - let outboundFlight = $derived( - $flightTicketStore?.flightIteneraries.outbound[0], - ); - let inboundFlight = $derived( - $flightTicketStore?.flightIteneraries.inbound[0], - ); - let isReturnFlight = $derived($flightTicketStore?.flightType === "Return"); - $effect(() => { if (!ckFlowVM.flowId || !ckFlowVM.setupDone) return; if (!paymentInfoVM.cardDetails) return; @@ -82,79 +66,17 @@ billingDetailsVM.validatePII(billingDetailsVM.billingDetails); if (billingDetailsVM.isPIIValid()) { - console.log("Billing details are valid, not setting from pasenger"); + console.log("Billing details are valid, not setting from initials"); return; } - if (passengerInfoVM.passengerInfos.length > 0) { - billingDetailsVM.setPII( - passengerInfoVM.passengerInfos[0].passengerPii, - ); - billingDetailsVM.validatePII(billingDetailsVM.billingDetails); - toast("Used billing details from primary passenger"); - } + if (!customerInfoVM.customerInfo) return; + billingDetailsVM.setPII(customerInfoVM.customerInfo); + billingDetailsVM.validatePII(billingDetailsVM.billingDetails); + toast("Used billing details from initial info"); });
-
- Trip Summary -
- -
- -
- {outboundFlight?.departure.station.code} - {#if isReturnFlight} - - {outboundFlight?.destination.station.code} - {:else} - - {outboundFlight?.destination.station.code} - {/if} -
- - -
- {#if isReturnFlight} -
- - {formatDate(outboundFlight?.departure.localTime)} - - {formatDate(inboundFlight.departure.localTime)} - -
- {:else} -
- - {formatDate(outboundFlight?.departure.localTime)} - -
- {/if} -
-
- - - {}} - > - - View Full Details - - -
-
-
Order Summary diff --git a/apps/frontend/src/lib/domains/checkout/payment-summary.svelte b/apps/frontend/src/lib/domains/checkout/payment-summary.svelte index b27cd21..0f4559a 100644 --- a/apps/frontend/src/lib/domains/checkout/payment-summary.svelte +++ b/apps/frontend/src/lib/domains/checkout/payment-summary.svelte @@ -4,28 +4,23 @@ 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 { customerInfoVM } from "../customerinfo/view/customerinfo.vm.svelte"; + import { productStore } from "../product/store"; + import { calculateFinalPrices } from "./utils"; - let totals = $state( - calculateTicketPrices($flightTicketStore, passengerInfoVM.passengerInfos), + let priceDetails = $state( + calculateFinalPrices($productStore, customerInfoVM.customerInfo), ); - let changing = $state(false); + let calculating = $state(false); + // Reactively update price details when product or customer info changes $effect(() => { - changing = true; - totals = calculateTicketPrices( - $flightTicketStore, - passengerInfoVM.passengerInfos, + calculating = true; + priceDetails = calculateFinalPrices( + $productStore, + customerInfoVM.customerInfo, ); - changing = false; - }); - - flightTicketStore.subscribe((val) => { - changing = true; - totals = calculateTicketPrices(val, passengerInfoVM.passengerInfos); - changing = false; + calculating = false; }); @@ -33,116 +28,69 @@ Payment Summary
- {#if !changing} - -
- Base Ticket Price -
- Total Ticket Price - {convertAndFormatCurrency(totals.baseTicketPrice)} -
-
- - Price per passenger (x{passengerInfoVM.passengerInfos.length}) - - - {convertAndFormatCurrency(totals.pricePerPassenger)} - -
-
- - - {#if totals.totalBaggageCost > 0} -
- Baggage Charges - {#each totals.passengerBaggageCosts as passengerBaggage} - {#if passengerBaggage.totalBaggageCost > 0} -
- - {passengerBaggage.passengerName} - - {#if passengerBaggage.personalBagCost > 0} -
- Personal Bag - - {convertAndFormatCurrency( - passengerBaggage.personalBagCost, - )} - -
- {/if} - {#if passengerBaggage.handBagCost > 0} -
- Hand Baggage - - {convertAndFormatCurrency( - passengerBaggage.handBagCost, - )} - -
- {/if} - {#if passengerBaggage.checkedBagCost > 0} -
- Checked Baggage - - {convertAndFormatCurrency( - passengerBaggage.checkedBagCost, - )} - -
- {/if} -
- {/if} - {/each} -
- Total Baggage Charges - {convertAndFormatCurrency(totals.totalBaggageCost)} -
+ {#if !calculating} + + {#if $productStore} +
+ {$productStore.title} +

{$productStore.description}

{/if} - -
+ +
- Subtotal - {convertAndFormatCurrency(totals.subtotal)} + Base Price + {convertAndFormatCurrency(priceDetails.basePrice)}
- {#if totals.discountAmount > 0} + {#if priceDetails.discountAmount > 0}
Discount - -{convertAndFormatCurrency(totals.discountAmount)}-{convertAndFormatCurrency( + priceDetails.discountAmount, + )} +
+
+ Display Price + {convertAndFormatCurrency( + priceDetails.displayPrice, + )}
{/if} +
+ +
Total ({$currencyStore.code}) - {convertAndFormatCurrency(totals.finalTotal)} + {convertAndFormatCurrency(priceDetails.orderPrice)}
{:else} -
- Calculating . . . +
+ Calculating...
{/if} +

Important Information:

    -
  • Prices include all applicable taxes and fees
  • -
  • Cancellation and change fees may apply as per our policy
  • -
  • Additional baggage fees may apply based on airline policy
  • +
  • Price includes all applicable taxes and fees
  • +
  • + Cancellation and refund policies may apply as per our terms of + service +
  • +
  • Payment will be processed securely upon order confirmation
diff --git a/apps/frontend/src/lib/domains/customerinfo/view/customerinfo.vm.svelte.ts b/apps/frontend/src/lib/domains/customerinfo/view/customerinfo.vm.svelte.ts index 2d550ff..ff4cd08 100644 --- a/apps/frontend/src/lib/domains/customerinfo/view/customerinfo.vm.svelte.ts +++ b/apps/frontend/src/lib/domains/customerinfo/view/customerinfo.vm.svelte.ts @@ -1,12 +1,5 @@ -import { trpcApiStore } from "$lib/stores/api"; -import { toast } from "svelte-sonner"; -import { get } from "svelte/store"; import { z } from "zod"; -import type { - CreateCustomerInfoPayload, - CustomerInfoModel, - UpdateCustomerInfoPayload, -} from "../data"; +import type { CreateCustomerInfoPayload, CustomerInfoModel } from "../data"; import { customerInfoModel } from "../data"; /** @@ -32,136 +25,6 @@ export class CustomerInfoViewModel { this.errors = {}; } - /** - * Fetches a single customer info record by ID and sets it as current - * @param id - Customer info ID to fetch - * @returns true if successful, false otherwise - */ - async fetchCustomerInfoById(id: number): Promise { - const api = get(trpcApiStore); - if (!api) { - toast.error("API client not initialized"); - return false; - } - - this.loading = true; - try { - const result = await api.customerInfo.getCustomerInfoById.query({ - id, - }); - if (result.error) { - toast.error(result.error.message, { - description: result.error.userHint, - }); - return false; - } - - this.customerInfo = result.data || null; - return true; - } catch (e) { - console.error(e); - toast.error("Failed to fetch customer information", { - description: "Please try again later", - }); - return false; - } finally { - this.loading = false; - } - } - - /** - * Creates a new customer information record - * @param payload - Customer info data to create - * @returns Customer info ID if successful, null otherwise - */ - async createCustomerInfo( - payload: CreateCustomerInfoPayload, - ): Promise { - const api = get(trpcApiStore); - if (!api) { - toast.error("API client not initialized"); - return null; - } - - // Validate before submitting - if (!this.validateCustomerInfo(payload)) { - toast.error("Please fix validation errors before submitting"); - return null; - } - - this.formLoading = true; - try { - const result = - await api.customerInfo.createCustomerInfo.mutate(payload); - if (result.error) { - toast.error(result.error.message, { - description: result.error.userHint, - }); - return null; - } - - toast.success("Customer information saved successfully"); - return result.data || null; - } catch (e) { - console.error(e); - toast.error("Failed to save customer information", { - description: "Please try again later", - }); - return null; - } finally { - this.formLoading = false; - } - } - - /** - * Updates the current customer information record - * @param payload - Customer info data to update (must include id) - * @returns true if successful, false otherwise - */ - async updateCustomerInfo( - payload: UpdateCustomerInfoPayload, - ): Promise { - const api = get(trpcApiStore); - if (!api) { - toast.error("API client not initialized"); - return false; - } - - if (!payload.id) { - toast.error("Customer ID is required for update"); - return false; - } - - this.formLoading = true; - try { - const result = - await api.customerInfo.updateCustomerInfo.mutate(payload); - if (result.error) { - toast.error(result.error.message, { - description: result.error.userHint, - }); - return false; - } - - toast.success("Customer information updated successfully"); - - // Update local state with the updated data - if (this.customerInfo && this.customerInfo.id === payload.id) { - this.customerInfo = { ...this.customerInfo, ...payload }; - } - - return true; - } catch (e) { - console.error(e); - toast.error("Failed to update customer information", { - description: "Please try again later", - }); - return false; - } finally { - this.formLoading = false; - } - } - /** * Validates customer information data using Zod schema * @param info - Customer info to validate diff --git a/apps/frontend/src/lib/domains/passengerinfo/data/entities.ts b/apps/frontend/src/lib/domains/passengerinfo/data/entities.ts deleted file mode 100644 index c9225f8..0000000 --- a/apps/frontend/src/lib/domains/passengerinfo/data/entities.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "@pkg/logic/domains/passengerinfo/data/entities"; diff --git a/apps/frontend/src/lib/domains/passengerinfo/data/repository.ts b/apps/frontend/src/lib/domains/passengerinfo/data/repository.ts deleted file mode 100644 index 39513b4..0000000 --- a/apps/frontend/src/lib/domains/passengerinfo/data/repository.ts +++ /dev/null @@ -1,211 +0,0 @@ -import { eq, inArray, type Database } from "@pkg/db"; -import { passengerInfo, passengerPII } from "@pkg/db/schema"; -import { getError, Logger } from "@pkg/logger"; -import { ERROR_CODES, type Result } from "@pkg/result"; -import { - passengerInfoModel, - type CustomerInfoModel, - type PassengerInfo, -} from "./entities"; - -export class PassengerInfoRepository { - private db: Database; - - constructor(db: Database) { - this.db = db; - } - - async createPassengerPii( - payload: CustomerInfoModel, - ): Promise> { - try { - const out = await this.db - .insert(passengerPII) - .values({ - firstName: payload.firstName, - middleName: payload.middleName, - lastName: payload.lastName, - email: payload.email, - phoneCountryCode: payload.phoneCountryCode, - phoneNumber: payload.phoneNumber, - nationality: payload.nationality, - gender: payload.gender, - dob: payload.dob, - passportNo: payload.passportNo, - passportExpiry: payload.passportExpiry, - - country: payload.country, - state: payload.state, - city: payload.city, - address: payload.address, - zipCode: payload.zipCode, - address2: payload.address2, - - createdAt: new Date(), - updatedAt: new Date(), - }) - .returning({ id: passengerInfo.id }) - .execute(); - - if (!out || out.length === 0) { - Logger.error("Failed to create passenger info"); - Logger.debug(out); - Logger.debug(payload); - return { - error: getError({ - code: ERROR_CODES.INTERNAL_SERVER_ERROR, - message: "Failed to create passenger info", - userHint: "Please try again", - detail: "Failed to create passenger info", - }), - }; - } - return { data: out[0].id }; - } catch (e) { - return { - error: getError( - { - code: ERROR_CODES.INTERNAL_SERVER_ERROR, - message: "An error occured while creating passenger info", - userHint: "Please try again later", - actionable: false, - detail: "An error occured while creating passenger info", - }, - e, - ), - }; - } - } - - async createPassengerInfo(payload: PassengerInfo): Promise> { - try { - const out = await this.db - .insert(passengerInfo) - .values({ - passengerType: payload.passengerType, - passengerPiiId: payload.passengerPiiId, - paymentInfoId: payload.paymentInfoId, - seatSelection: payload.seatSelection, - bagSelection: payload.bagSelection, - agentsInfo: payload.agentsInfo, - - flightTicketInfoId: payload.flightTicketInfoId, - orderId: payload.orderId, - - createdAt: new Date(), - updatedAt: new Date(), - }) - .returning({ id: passengerInfo.id }) - .execute(); - - if (!out || out.length === 0) { - Logger.error("Failed to create passenger info"); - Logger.debug(out); - Logger.debug(payload); - return { - error: getError({ - code: ERROR_CODES.INTERNAL_SERVER_ERROR, - message: "Failed to create passenger info", - userHint: "Please try again", - detail: "Failed to create passenger info", - }), - }; - } - return { data: out[0].id }; - } catch (e) { - return { - error: getError( - { - code: ERROR_CODES.INTERNAL_SERVER_ERROR, - message: "An error occured while creating passenger info", - userHint: "Please try again later", - actionable: false, - detail: "An error occured while creating passenger info", - }, - e, - ), - }; - } - } - - async getPassengerInfo(id: number): Promise> { - try { - const out = await this.db.query.passengerInfo.findFirst({ - where: eq(passengerInfo.id, id), - with: { passengerPii: true }, - }); - if (!out) { - Logger.error("Failed to get passenger info"); - Logger.debug(out); - return { - error: getError({ - code: ERROR_CODES.INTERNAL_SERVER_ERROR, - message: "Failed to get passenger info", - userHint: "Please try again", - detail: "Failed to get passenger info", - }), - }; - } - return { data: out as any as PassengerInfo }; - } catch (e) { - return { - error: getError( - { - code: ERROR_CODES.INTERNAL_SERVER_ERROR, - message: "An error occured while getting passenger info", - userHint: "Please try again later", - actionable: false, - detail: "An error occured while getting passenger info", - }, - e, - ), - }; - } - } - - async getPassengerInfosByRefOId( - refOIds: number[], - ): Promise> { - try { - const out = await this.db.query.passengerInfo.findMany({ - where: inArray(passengerInfo.orderId, refOIds), - with: { passengerPii: true }, - }); - const res = [] as PassengerInfo[]; - for (const each of out) { - const parsed = passengerInfoModel.safeParse(each); - if (!parsed.success) { - Logger.warn(`Error while parsing passenger info`); - Logger.debug(parsed.error?.errors); - continue; - } - res.push(parsed.data); - } - Logger.info(`Returning ${res.length} passenger info by ref OID`); - return { data: res }; - } catch (e) { - return { - error: getError( - { - code: ERROR_CODES.INTERNAL_SERVER_ERROR, - message: "An error occured while getting passenger info", - userHint: "Please try again later", - actionable: false, - detail: "An error occured while getting passenger info", - }, - e, - ), - }; - } - } - - async deleteAll(ids: number[]): Promise> { - Logger.info(`Deleting ${ids.length} passenger info`); - const out = await this.db - .delete(passengerInfo) - .where(inArray(passengerInfo.id, ids)); - Logger.debug(out); - Logger.info(`Deleted ${out.count} passenger info`); - return { data: out.count }; - } -} diff --git a/apps/frontend/src/lib/domains/passengerinfo/domain/controller.ts b/apps/frontend/src/lib/domains/passengerinfo/domain/controller.ts deleted file mode 100644 index 3301904..0000000 --- a/apps/frontend/src/lib/domains/passengerinfo/domain/controller.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { Logger } from "@pkg/logger"; -import type { Result } from "@pkg/result"; -import type { PassengerInfo } from "../data/entities"; -import type { PassengerInfoRepository } from "../data/repository"; - -export class PassengerInfoController { - repo: PassengerInfoRepository; - - constructor(repo: PassengerInfoRepository) { - this.repo = repo; - } - - async createPassengerInfos( - payload: PassengerInfo[], - orderId: number, - flightTicketInfoId?: number, - paymentInfoId?: number, - ): Promise> { - const made = [] as number[]; - for (const passengerInfo of payload) { - const piiOut = await this.repo.createPassengerPii( - passengerInfo.passengerPii, - ); - if (piiOut.error || !piiOut.data) { - await this.repo.deleteAll(made); - return piiOut; - } - passengerInfo.passengerPiiId = piiOut.data; - passengerInfo.paymentInfoId = paymentInfoId; - passengerInfo.flightTicketInfoId = flightTicketInfoId; - passengerInfo.orderId = orderId; - passengerInfo.agentId = undefined; - const out = await this.repo.createPassengerInfo(passengerInfo); - if (out.error) { - await this.repo.deleteAll(made); - return out; - } - } - return { data: made.length }; - } - - async getPassengerInfo(id: number): Promise> { - return this.repo.getPassengerInfo(id); - } - - async getPassengerInfosByRefOIds( - refOIds: number[], - ): Promise> { - Logger.info(`Querying/Returning Passenger infos for ${refOIds}`); - return this.repo.getPassengerInfosByRefOId(refOIds); - } -} diff --git a/apps/frontend/src/lib/domains/passengerinfo/view/passenger-pii-form.svelte b/apps/frontend/src/lib/domains/passengerinfo/view/passenger-pii-form.svelte deleted file mode 100644 index f7eca33..0000000 --- a/apps/frontend/src/lib/domains/passengerinfo/view/passenger-pii-form.svelte +++ /dev/null @@ -1,323 +0,0 @@ - - -
-
- - debounceValidate()} - /> - - - - debounceValidate()} - required - /> - - - - debounceValidate()} - /> - -
- - - debounceValidate()} - required - /> - - -
- -
- { - info.phoneCountryCode = code; - }} - name="phoneCode" - > - - {#if info.phoneCountryCode} - {info.phoneCountryCode} - {:else} - Select - {/if} - - - {#each PHONE_COUNTRY_CODES as { country, phoneCode }} - - - {phoneCode} ({country}) - - - {/each} - - - - debounceValidate()} - class="flex-1" - /> -
-
- - - { - // @ts-ignore - info.passportExpiry = v.target.value; - debounceValidate(); - }} - /> - - - debounceValidate()} - /> - -
- -
- - { - info.nationality = e; - debounceValidate(); - }} - name="role" - > - - {capitalize( - info.nationality.length > 0 ? info.nationality : "Select", - )} - - - {#each COUNTRIES_SELECT as country} - - {country.label} - - {/each} - - - - - { - info.gender = e as Gender; - debounceValidate(); - }} - name="role" - > - - {capitalize(info.gender.length > 0 ? info.gender : "Select")} - - - {#each genderOpts as gender} - - {gender.label} - - {/each} - - - - - - debounceValidate()} - /> - -
- - - - Address Info - -
- - { - info.country = e; - debounceValidate(); - }} - name="role" - > - - {capitalize( - info.country.length > 0 ? info.country : "Select", - )} - - - {#each COUNTRIES_SELECT as country} - - {country.label} - - {/each} - - - - - - debounceValidate()} - /> - -
-
- - debounceValidate()} - /> - - - - debounceValidate()} - maxlength={12} - /> - -
- - - debounceValidate()} - /> - - - - - -
diff --git a/apps/frontend/src/lib/domains/passengerinfo/view/passenger.info.vm.svelte.ts b/apps/frontend/src/lib/domains/passengerinfo/view/passenger.info.vm.svelte.ts deleted file mode 100644 index 56755bf..0000000 --- a/apps/frontend/src/lib/domains/passengerinfo/view/passenger.info.vm.svelte.ts +++ /dev/null @@ -1,169 +0,0 @@ -import { - customerInfoModel, - type BagSelectionInfo, - type CustomerInfoModel, - type PassengerInfo, - type SeatSelectionInfo, -} from "$lib/domains/ticket/data/entities/create.entities"; -import { - Gender, - PassengerType, - type BagDetails, - type FlightPriceDetails, - type PassengerCount, -} from "$lib/domains/ticket/data/entities/index"; -import { z } from "zod"; - -export class PassengerInfoViewModel { - passengerInfos = $state([]); - - piiErrors = $state>>>( - [], - ); - - reset() { - this.passengerInfos = []; - this.piiErrors = []; - } - - setupPassengerInfo(counts: PassengerCount, forceReset = false) { - if (this.passengerInfos.length > 0 && !forceReset) { - return; // since it's already setup - } - - // const _defaultPiiObj = { - // firstName: "first", - // middleName: "mid", - // lastName: "last", - // email: "first.last@example.com", - // phoneCountryCode: "+31", - // phoneNumber: "12345379", - // passportNo: "f97823h", - // passportExpiry: "2032-12-12", - // nationality: "Netherlands", - // gender: Gender.Male, - // dob: "2000-12-12", - // country: "Netherlands", - // state: "state", - // city: "city", - // zipCode: "123098", - // address: "address", - // address2: "", - // } as CustomerInfoModel; - - const _defaultPiiObj = { - firstName: "", - middleName: "", - lastName: "", - email: "", - phoneCountryCode: "", - phoneNumber: "", - passportNo: "", - passportExpiry: "", - nationality: "", - gender: Gender.Male, - dob: "", - country: "", - state: "", - city: "", - zipCode: "", - address: "", - address2: "", - } as CustomerInfoModel; - - const _defaultPriceObj = { - currency: "", - basePrice: 0, - displayPrice: 0, - discountAmount: 0, - } as FlightPriceDetails; - - const _defaultSeatSelectionObj = { - id: "", - row: "", - number: 0, - reserved: false, - available: false, - seatLetter: "", - price: _defaultPriceObj, - } as SeatSelectionInfo; - - const _baseBagDetails = { - dimensions: { height: 0, length: 0, width: 0 }, - price: 0, - unit: "kg", - weight: 0, - } as BagDetails; - const _defaultBagSelectionObj = { - id: 0, - personalBags: 1, - handBags: 0, - checkedBags: 0, - pricing: { - personalBags: { ..._baseBagDetails }, - checkedBags: { ..._baseBagDetails }, - handBags: { ..._baseBagDetails }, - }, - } as BagSelectionInfo; - - this.passengerInfos = []; - - for (let i = 0; i < counts.adults; i++) { - this.passengerInfos.push({ - id: i, - passengerType: PassengerType.Adult, - agentsInfo: false, - passengerPii: { ..._defaultPiiObj }, - seatSelection: { ..._defaultSeatSelectionObj }, - bagSelection: { ..._defaultBagSelectionObj, id: i }, - }); - this.piiErrors.push({}); - } - - for (let i = 0; i < counts.children; i++) { - this.passengerInfos.push({ - id: i + 1 + counts.adults, - passengerType: PassengerType.Child, - agentsInfo: false, - passengerPii: { ..._defaultPiiObj }, - seatSelection: { ..._defaultSeatSelectionObj }, - bagSelection: { ..._defaultBagSelectionObj, id: i }, - }); - this.piiErrors.push({}); - } - } - - validateAllPII() { - for (let i = 0; i < this.passengerInfos.length; i++) { - this.validatePII(this.passengerInfos[i].passengerPii, i); - } - } - - validatePII(info: CustomerInfoModel, idx: number) { - try { - const result = customerInfoModel.parse(info); - this.piiErrors[idx] = {}; - return result; - } catch (error) { - if (error instanceof z.ZodError) { - this.piiErrors[idx] = error.errors.reduce( - (acc, curr) => { - const path = curr.path[0] as keyof CustomerInfoModel; - acc[path] = curr.message; - return acc; - }, - {} as Record, - ); - } - return null; - } - } - - isPIIValid(): boolean { - return this.piiErrors.every( - (errorObj) => Object.keys(errorObj).length === 0, - ); - } -} - -export const passengerInfoVM = new PassengerInfoViewModel(); diff --git a/apps/frontend/src/lib/domains/ticket/view/checkout/checkout-confirmation-section.svelte b/apps/frontend/src/lib/domains/ticket/view/checkout/checkout-confirmation-section.svelte deleted file mode 100644 index e032f87..0000000 --- a/apps/frontend/src/lib/domains/ticket/view/checkout/checkout-confirmation-section.svelte +++ /dev/null @@ -1 +0,0 @@ -show checkout confirmation status here diff --git a/apps/frontend/src/lib/domains/ticket/view/checkout/checkout-loading-section.svelte b/apps/frontend/src/lib/domains/ticket/view/checkout/checkout-loading-section.svelte deleted file mode 100644 index 80d25e9..0000000 --- a/apps/frontend/src/lib/domains/ticket/view/checkout/checkout-loading-section.svelte +++ /dev/null @@ -1,7 +0,0 @@ - - -
- -
diff --git a/apps/frontend/src/lib/domains/ticket/view/checkout/checkout-steps-indicator.svelte b/apps/frontend/src/lib/domains/ticket/view/checkout/checkout-steps-indicator.svelte deleted file mode 100644 index c6dbab3..0000000 --- a/apps/frontend/src/lib/domains/ticket/view/checkout/checkout-steps-indicator.svelte +++ /dev/null @@ -1,158 +0,0 @@ - - - { - sheetOpen = to; - }} -> - (sheetOpen = true)} - > -
- - Step {activeStepIndex + 1}/{checkoutSteps.length}: - - - {checkoutSteps[activeStepIndex].label} - -
- -
- - -
- {#each checkoutSteps as step, index} - - {/each} -
-
-
- - diff --git a/apps/frontend/src/lib/domains/ticket/view/checkout/checkout.vm.svelte.ts b/apps/frontend/src/lib/domains/ticket/view/checkout/checkout.vm.svelte.ts deleted file mode 100644 index ac50fa2..0000000 --- a/apps/frontend/src/lib/domains/ticket/view/checkout/checkout.vm.svelte.ts +++ /dev/null @@ -1,165 +0,0 @@ -import { ckFlowVM } from "$lib/domains/ckflow/view/ckflow.vm.svelte"; -import { CheckoutStep, newOrderModel } from "$lib/domains/order/data/entities"; -import { passengerInfoVM } from "$lib/domains/passengerinfo/view/passenger.info.vm.svelte"; -import { - paymentInfoPayloadModel, - PaymentMethod, -} from "$lib/domains/paymentinfo/data/entities"; -import { trpcApiStore } from "$lib/stores/api"; -import { toast } from "svelte-sonner"; -import { get } from "svelte/store"; -import { flightTicketStore } from "../../data/store"; -import { paymentInfoVM } from "./payment-info-section/payment.info.vm.svelte"; -import { calculateTicketPrices } from "./total.calculator"; - -class CheckoutViewModel { - checkoutStep = $state(CheckoutStep.Initial); - loading = $state(true); - continutingToNextStep = $state(false); - - checkoutSubmitted = $state(false); - - livenessPinger: NodeJS.Timer | undefined = $state(undefined); - - reset() { - this.checkoutStep = CheckoutStep.Initial; - this.resetPinger(); - } - - setupPinger() { - this.resetPinger(); - this.livenessPinger = setInterval(() => { - this.ping(); - }, 5_000); - } - - resetPinger() { - if (this.livenessPinger) { - clearInterval(this.livenessPinger); - } - } - - private async ping() { - const api = get(trpcApiStore); - if (!api) { - return false; - } - const ticket = get(flightTicketStore); - if (!ticket || !ticket.refOIds) { - return false; - } - const out = await api.ticket.ping.query({ - tid: ticket.id, - refOIds: ticket.refOIds, - }); - } - - async checkout() { - if (this.checkoutSubmitted || this.loading) { - return; - } - this.checkoutSubmitted = true; - - const api = get(trpcApiStore); - if (!api) { - this.checkoutSubmitted = false; - return false; - } - - const ticket = get(flightTicketStore); - - const prices = calculateTicketPrices( - ticket, - passengerInfoVM.passengerInfos, - ); - - const validatedPrices = { - subtotal: isNaN(prices.subtotal) ? 0 : prices.subtotal, - discountAmount: isNaN(prices.discountAmount) - ? 0 - : prices.discountAmount, - finalTotal: isNaN(prices.finalTotal) ? 0 : prices.finalTotal, - pricePerPassenger: isNaN(prices.pricePerPassenger) - ? 0 - : prices.pricePerPassenger, - }; - - const parsed = newOrderModel.safeParse({ - basePrice: validatedPrices.subtotal, - discountAmount: validatedPrices.discountAmount, - displayPrice: validatedPrices.finalTotal, - orderPrice: validatedPrices.finalTotal, // Same as displayPrice - fullfilledPrice: validatedPrices.finalTotal, // Same as displayPrice - pricePerPassenger: validatedPrices.pricePerPassenger, - flightTicketInfoId: -1, - paymentInfoId: -1, - }); - - if (parsed.error) { - console.log(parsed.error); - const err = parsed.error.errors[0]; - toast.error("Failed to perform checkout", { - description: err.message, - }); - return false; - } - - const pInfoParsed = paymentInfoPayloadModel.safeParse({ - method: PaymentMethod.Card, - cardDetails: paymentInfoVM.cardDetails, - flightTicketInfoId: ticket.id, - }); - if (pInfoParsed.error) { - console.log(parsed.error); - const err = pInfoParsed.error.errors[0]; - toast.error("Failed to perform checkout", { - description: err.message, - }); - return false; - } - - try { - console.log("Creating order"); - this.loading = true; - const out = await api.order.createOrder.mutate({ - flightTicketId: ticket.id, - orderModel: parsed.data, - passengerInfos: passengerInfoVM.passengerInfos, - paymentInfo: pInfoParsed.data, - refOIds: ticket.refOIds, - flowId: ckFlowVM.flowId, - }); - - if (out.error) { - this.loading = false; - toast.error(out.error.message, { - description: out.error.userHint, - }); - return false; - } - if (!out.data) { - this.loading = false; - toast.error("Failed to create order", { - description: "Please try again", - }); - return false; - } - - toast.success("Order created successfully", { - description: "Redirecting, please wait...", - }); - setTimeout(() => { - window.location.href = `/checkout/success?oid=${out.data}`; - }, 500); - return true; - } catch (e) { - this.checkoutSubmitted = false; - toast.error("An error occurred during checkout", { - description: "Please try again", - }); - return false; - } - } -} - -export const checkoutVM = new CheckoutViewModel(); diff --git a/apps/frontend/src/lib/domains/ticket/view/checkout/initial-info-section.svelte b/apps/frontend/src/lib/domains/ticket/view/checkout/initial-info-section.svelte deleted file mode 100644 index ecd62ad..0000000 --- a/apps/frontend/src/lib/domains/ticket/view/checkout/initial-info-section.svelte +++ /dev/null @@ -1,146 +0,0 @@ - - -{#if $flightTicketStore} -
- -
- - {#if passengerInfoVM.passengerInfos.length > 0} - {#each passengerInfoVM.passengerInfos as info, idx} - {@const name = - info.passengerPii.firstName.length > 0 || - info.passengerPii.lastName.length > 0 - ? `${info.passengerPii.firstName} ${info.passengerPii.lastName}` - : `Passenger #${idx + 1}`} -
-
- - {name} - - - - {capitalize(info.passengerType)} - -
-
- Personal Info - -
-
- {/each} - {/if} - -
-
- - -
-{/if} diff --git a/apps/frontend/src/lib/domains/ticket/view/checkout/otp-verification-section.svelte b/apps/frontend/src/lib/domains/ticket/view/checkout/otp-verification-section.svelte deleted file mode 100644 index b4e091f..0000000 --- a/apps/frontend/src/lib/domains/ticket/view/checkout/otp-verification-section.svelte +++ /dev/null @@ -1,132 +0,0 @@ - - -
-
-
- -
- - Card Verification Required - -

- To complete your payment, please enter the verification code sent by - your bank or card provider (Visa, Mastercard, etc.). This code may - have been sent via SMS or email. -

- -
- - - -
-
- -
- Need Help? -

- If you haven't received a verification code from your bank or card - provider, please check your spam folder or contact your card issuer - directly. This verification is part of their security process for - online payments. -

-
-
diff --git a/apps/frontend/src/lib/domains/ticket/view/checkout/payment-info-section/billing-details-form.svelte b/apps/frontend/src/lib/domains/ticket/view/checkout/payment-info-section/billing-details-form.svelte deleted file mode 100644 index 07343c8..0000000 --- a/apps/frontend/src/lib/domains/ticket/view/checkout/payment-info-section/billing-details-form.svelte +++ /dev/null @@ -1,148 +0,0 @@ - - -
-
- - debounceValidate()} - /> - - - - debounceValidate()} - required - /> - - - - debounceValidate()} - /> - -
- -
- - { - info.country = e; - debounceValidate(); - }} - name="role" - > - - {capitalize( - info.country.length > 0 ? info.country : "Select", - )} - - - {#each COUNTRIES_SELECT as country} - - {country.label} - - {/each} - - - - - - debounceValidate()} - /> - -
- -
- - debounceValidate()} - /> - - - - debounceValidate()} - maxlength={12} - /> - -
- - - debounceValidate()} - /> - - - - - -
diff --git a/apps/frontend/src/lib/domains/ticket/view/checkout/payment-info-section/billing.details.vm.svelte.ts b/apps/frontend/src/lib/domains/ticket/view/checkout/payment-info-section/billing.details.vm.svelte.ts deleted file mode 100644 index 1ca39ff..0000000 --- a/apps/frontend/src/lib/domains/ticket/view/checkout/payment-info-section/billing.details.vm.svelte.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { - customerInfoModel, - type CustomerInfoModel, -} from "$lib/domains/ticket/data/entities/create.entities"; -import { Gender } from "$lib/domains/ticket/data/entities/index"; -import { z } from "zod"; - -export class BillingDetailsViewModel { - // @ts-ignore - billingDetails = $state(undefined); - - piiErrors = $state>>({}); - - constructor() { - this.reset(); - } - - reset() { - this.billingDetails = { - firstName: "", - middleName: "", - lastName: "", - email: "", - phoneCountryCode: "", - phoneNumber: "", - passportNo: "", - passportExpiry: "", - nationality: "", - gender: Gender.Male, - dob: "", - country: "", - state: "", - city: "", - zipCode: "", - address: "", - address2: "", - } as CustomerInfoModel; - this.piiErrors = {}; - } - - setPII(info: CustomerInfoModel) { - this.billingDetails = info; - } - - validatePII(info: CustomerInfoModel) { - try { - const result = customerInfoModel.parse(info); - this.piiErrors = {}; - return result; - } catch (error) { - if (error instanceof z.ZodError) { - this.piiErrors = error.errors.reduce( - (acc, curr) => { - const path = curr.path[0] as keyof CustomerInfoModel; - acc[path] = curr.message; - return acc; - }, - {} as Record, - ); - } - return null; - } - } - - isPIIValid(): boolean { - return Object.keys(this.piiErrors).length === 0; - } -} - -export const billingDetailsVM = new BillingDetailsViewModel(); diff --git a/apps/frontend/src/lib/domains/ticket/view/checkout/payment-info-section/index.svelte b/apps/frontend/src/lib/domains/ticket/view/checkout/payment-info-section/index.svelte deleted file mode 100644 index e48eade..0000000 --- a/apps/frontend/src/lib/domains/ticket/view/checkout/payment-info-section/index.svelte +++ /dev/null @@ -1,198 +0,0 @@ - - -
-
- Trip Summary -
- -
- -
- {outboundFlight?.departure.station.code} - {#if isReturnFlight} - - {outboundFlight?.destination.station.code} - {:else} - - {outboundFlight?.destination.station.code} - {/if} -
- - -
- {#if isReturnFlight} -
- - {formatDate(outboundFlight?.departure.localTime)} - - {formatDate(inboundFlight.departure.localTime)} - -
- {:else} -
- - {formatDate(outboundFlight?.departure.localTime)} - -
- {/if} -
-
- - - {}} - > - - View Full Details - - -
-
- -
- Order Summary - - - -
- -
- Billing Details - -
- -
- Payment Details - -
- -
- - - -
-
diff --git a/apps/frontend/src/lib/domains/ticket/view/checkout/payment-info-section/order-summary.svelte b/apps/frontend/src/lib/domains/ticket/view/checkout/payment-info-section/order-summary.svelte deleted file mode 100644 index 584335e..0000000 --- a/apps/frontend/src/lib/domains/ticket/view/checkout/payment-info-section/order-summary.svelte +++ /dev/null @@ -1,80 +0,0 @@ - - -
- {#each passengerInfoVM.passengerInfos as passenger, index} -
-
- - Passenger {index + 1} ({capitalize(passenger.passengerType)}) - -
- - -
-
-
- Name -

- {passenger.passengerPii.firstName} - {passenger.passengerPii.lastName} -

-
-
- Nationality -

- {passenger.passengerPii.nationality} -

-
-
- Date of Birth -

{passenger.passengerPii.dob}

-
-
-
- - -
- {#if passenger.bagSelection.personalBags > 0} -
- - Personal Item -
- {/if} - {#if passenger.bagSelection.handBags > 0} -
- - {passenger.bagSelection.handBags} x Cabin Bag -
- {/if} - {#if passenger.bagSelection.checkedBags > 0} -
- - - {passenger.bagSelection.checkedBags} x Checked Bag - -
- {/if} -
- - - {#if passenger.seatSelection.number} -
- - Seat {passenger.seatSelection.number} -
- {/if} -
- - {#if index < passengerInfoVM.passengerInfos.length - 1} -
- {/if} - {/each} -
diff --git a/apps/frontend/src/lib/domains/ticket/view/checkout/payment-info-section/payment-form.svelte b/apps/frontend/src/lib/domains/ticket/view/checkout/payment-info-section/payment-form.svelte deleted file mode 100644 index 02b149b..0000000 --- a/apps/frontend/src/lib/domains/ticket/view/checkout/payment-info-section/payment-form.svelte +++ /dev/null @@ -1,106 +0,0 @@ - - -
- - debounceValidate()} - /> - - - - { - paymentInfoVM.cardDetails.cardNumber = cleanupCardNo( - e.currentTarget.value, - ); - debounceValidate(); - }} - /> - - -
- - { - paymentInfoVM.cardDetails.expiry = formatExpiryDate( - e.currentTarget.value, - ); - debounceValidate(); - }} - /> - - - - { - paymentInfoVM.cardDetails.cvv = formatCVV( - e.currentTarget.value, - ); - debounceValidate(); - }} - /> - -
-
diff --git a/apps/frontend/src/lib/domains/ticket/view/checkout/payment-info-section/payment.info.vm.svelte.ts b/apps/frontend/src/lib/domains/ticket/view/checkout/payment-info-section/payment.info.vm.svelte.ts deleted file mode 100644 index 5549678..0000000 --- a/apps/frontend/src/lib/domains/ticket/view/checkout/payment-info-section/payment.info.vm.svelte.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { - type CardInfo, - cardInfoModel, -} from "$lib/domains/paymentinfo/data/entities"; -import { z } from "zod"; - -const _default = { cardholderName: "", cardNumber: "", expiry: "", cvv: "" }; - -class PaymentInfoViewModel { - cardDetails = $state({ ..._default }); - - errors = $state>>({}); - - reset() { - this.cardDetails = { ..._default }; - this.errors = {}; - } - - async validateAndSubmit() { - try { - const result = cardInfoModel.parse(this.cardDetails); - this.errors = {}; - return result; - } catch (error) { - if (error instanceof z.ZodError) { - this.errors = error.errors.reduce( - (acc, curr) => { - const path = curr.path[0] as keyof CardInfo; - acc[path] = curr.message; - return acc; - }, - {} as Record, - ); - } - return null; - } - } -} - -export const paymentInfoVM = new PaymentInfoViewModel(); diff --git a/apps/frontend/src/lib/domains/ticket/view/checkout/payment-summary.svelte b/apps/frontend/src/lib/domains/ticket/view/checkout/payment-summary.svelte deleted file mode 100644 index 32a5ccf..0000000 --- a/apps/frontend/src/lib/domains/ticket/view/checkout/payment-summary.svelte +++ /dev/null @@ -1,195 +0,0 @@ - - -
- Payment Summary -
- - {#if !changing} - -
- Base Ticket Price -
- Total Ticket Price - {convertAndFormatCurrency(totals.baseTicketPrice)} -
-
- - Price per passenger (x{passengerInfoVM.passengerInfos.length}) - - - {convertAndFormatCurrency(totals.pricePerPassenger)} - -
-
- - - {#if totals.totalBaggageCost > 0} -
- Baggage Charges - {#each totals.passengerBaggageCosts as passengerBaggage} - {#if passengerBaggage.totalBaggageCost > 0} -
- - {passengerBaggage.passengerName} - - {#if passengerBaggage.personalBagCost > 0} -
- Personal Bag - - {convertAndFormatCurrency( - passengerBaggage.personalBagCost, - )} - -
- {/if} - {#if passengerBaggage.handBagCost > 0} -
- Hand Baggage - - {convertAndFormatCurrency( - passengerBaggage.handBagCost, - )} - -
- {/if} - {#if passengerBaggage.checkedBagCost > 0} -
- Checked Baggage - - {convertAndFormatCurrency( - passengerBaggage.checkedBagCost, - )} - -
- {/if} -
- {/if} - {/each} -
- Total Baggage Charges - {convertAndFormatCurrency(totals.totalBaggageCost)} -
-
- {/if} - - -
-
- Subtotal - {convertAndFormatCurrency(totals.subtotal)} -
- - - {#if totals.discountAmount > 0 && appliedCoupon} -
-
-
- - Coupon Applied: {appliedCoupon} -
- - {Math.round( - (totals.discountAmount / totals.subtotal) * 100, - )}% OFF - -
-
- Discount - -{convertAndFormatCurrency( - totals.discountAmount, - )} -
- {#if $flightTicketStore?.priceDetails?.couponDescription} -

- {$flightTicketStore.priceDetails.couponDescription} -

- {/if} -
- {:else if totals.discountAmount > 0} -
- Discount - -{convertAndFormatCurrency(totals.discountAmount)} -
- {/if} - -
- Total ({$currencyStore.code}) - {convertAndFormatCurrency(totals.finalTotal)} -
-
- {:else} -
- Calculating . . . -
- {/if} - -
-

Important Information:

-
    -
  • Prices include all applicable taxes and fees
  • -
  • Cancellation and change fees may apply as per our policy
  • -
  • Additional baggage fees may apply based on airline policy
  • - {#if appliedCoupon} -
  • - Discount applied via coupon: {appliedCoupon} -
  • - {/if} -
-
-
diff --git a/apps/frontend/src/lib/domains/ticket/view/checkout/payment-verification-loader.svelte b/apps/frontend/src/lib/domains/ticket/view/checkout/payment-verification-loader.svelte deleted file mode 100644 index b4a6c38..0000000 --- a/apps/frontend/src/lib/domains/ticket/view/checkout/payment-verification-loader.svelte +++ /dev/null @@ -1,68 +0,0 @@ - - -
- -

{txt}

-
diff --git a/apps/frontend/src/lib/domains/ticket/view/checkout/payment-verification-section.svelte b/apps/frontend/src/lib/domains/ticket/view/checkout/payment-verification-section.svelte deleted file mode 100644 index dbe52c3..0000000 --- a/apps/frontend/src/lib/domains/ticket/view/checkout/payment-verification-section.svelte +++ /dev/null @@ -1,66 +0,0 @@ - - -{#if showOtpVerificationForm} - {@const done = gototop()} - -{:else} - {@const done2 = gototop()} - -{/if} diff --git a/apps/frontend/src/lib/domains/ticket/view/checkout/seat-selection-section/index.svelte b/apps/frontend/src/lib/domains/ticket/view/checkout/seat-selection-section/index.svelte deleted file mode 100644 index e6cfa2c..0000000 --- a/apps/frontend/src/lib/domains/ticket/view/checkout/seat-selection-section/index.svelte +++ /dev/null @@ -1,224 +0,0 @@ - - -{#if seatSelectionVM.loading} - -{:else} -
-
-
- Select Your Seats - -
- -
- - Select passenger to assign seat: - -
- {#each passengerInfoVM.passengerInfos as passenger} - - {/each} -
-
- - -
-
- - Flight {seatSelectionVM.currentFlightIndex + 1} of {seatSelectionVM - .seatMaps.length} - - - {currentFlight.departure.station.code} → {currentFlight - .destination.station.code} - -
-
- - -
-
- - -
-
-
- -
- - {#each ["A", "B", "C", "", "D", "E", "F"] as letter} - - {letter} - - {/each} -
- - {#each seatSelectionVM.seatMaps[seatSelectionVM.currentFlightIndex].seats as row} -
- - {row[0].row} - - {#each row as seat} - - {#if seat.number === 3} -
- {/if} - {/each} -
- {/each} -
-
-
- -
-
-
- Available -
-
-
- Selected -
-
-
- Reserved -
-
-
- Unavailable -
-
-
- -
- - -
-
-{/if} diff --git a/apps/frontend/src/lib/domains/ticket/view/checkout/seat-selection-section/seat.selection.vm.svelte.ts b/apps/frontend/src/lib/domains/ticket/view/checkout/seat-selection-section/seat.selection.vm.svelte.ts deleted file mode 100644 index f3c1a2b..0000000 --- a/apps/frontend/src/lib/domains/ticket/view/checkout/seat-selection-section/seat.selection.vm.svelte.ts +++ /dev/null @@ -1,164 +0,0 @@ -import { flightTicketStore } from "$lib/domains/ticket/data/store"; -import { passengerInfoVM } from "$lib/domains/passengerinfo/view/passenger.info.vm.svelte"; -import { get } from "svelte/store"; -import type { - FlightSeatMap, - SeatSelectionInfo, -} from "$lib/domains/passengerinfo/data/entities"; -import { toast } from "svelte-sonner"; - -type SeatAssignments = Record< - string, - { [seatId: string]: { passengerId: number; passengerInitials: string } } ->; - -export class SeatSelectionVM { - loading = $state(true); - currentFlightIndex = $state(0); - seatMaps = $state([]); - - currentPassengerId = $state(null); - - seatAssignments = $state({}); - - reset() { - this.loading = true; - this.currentFlightIndex = 0; - this.seatMaps = []; - this.currentPassengerId = null; - this.seatAssignments = {}; - } - - async fetchSeatMaps() { - this.loading = true; - await new Promise((resolve) => setTimeout(resolve, 1000)); - - const info = get(flightTicketStore); - - const flights = [ - ...info.flightIteneraries.outbound, - ...info.flightIteneraries.inbound, - ]; - - this.seatMaps = flights.map((flight) => ({ - flightId: flight.flightId, - seats: this.generateMockSeatMap(), - })); - - this.loading = false; - } - - private generateMockSeatMap(): SeatSelectionInfo[][] { - const rows = 20; - const seatsPerRow = 6; - const seatMap: SeatSelectionInfo[][] = []; - const seatLetters = ["A", "B", "C", "D", "E", "F"]; - - for (let row = 0; row < rows; row++) { - const seatRow: SeatSelectionInfo[] = []; - const rowNumber = row + 1; // Row numbers start from 1 - - for (let seat = 0; seat < seatsPerRow; seat++) { - const random = Math.random(); - seatRow.push({ - id: `${rowNumber}${seatLetters[seat]}`, - row: rowNumber.toString(), - number: seat + 1, - seatLetter: seatLetters[seat], - available: random > 0.3, - reserved: random < 0.2, - price: { - currency: "USD", - basePrice: 25, - discountAmount: 0, - displayPrice: 25, - }, - }); - } - seatMap.push(seatRow); - } - - return seatMap; - } - - selectSeat(flightId: string, seat: SeatSelectionInfo) { - if (this.currentPassengerId === null) { - return toast.error("Please select a passenger first"); - } - - if (!seat.available || seat.reserved) { - return toast.error("Seat is not available"); - } - const passenger = passengerInfoVM.passengerInfos.find( - (p) => p.id === this.currentPassengerId, - ); - - if (!passenger) { - return toast.error("Passenger not found", { - description: "Please try refreshing page or book ticket again", - }); - } - - // Get passenger initials - const initials = - `${passenger.passengerPii.firstName[0]}${passenger.passengerPii.lastName[0]}`.toUpperCase(); - - // Update seat assignments - if (!this.seatAssignments[flightId]) { - this.seatAssignments[flightId] = {}; - } - - // Remove any previous seat assignment for this passenger on this flight - Object.entries(this.seatAssignments[flightId]).forEach( - ([seatId, assignment]) => { - if (assignment.passengerId === this.currentPassengerId) { - delete this.seatAssignments[flightId][seatId]; - } - }, - ); - - // Assign new seat - this.seatAssignments[flightId][seat.id] = { - passengerId: this.currentPassengerId, - passengerInitials: initials, - }; - - passenger.seatSelection = { - id: seat.id, - row: seat.row, - number: seat.number, - seatLetter: seat.seatLetter, - available: seat.available, - reserved: seat.reserved, - price: seat.price, - }; - } - - isSeatAssigned(flightId: string, seatId: string) { - return this.seatAssignments[flightId]?.[seatId] !== undefined; - } - - getSeatDisplay(flightId: string, seatId: string) { - return ( - this.seatAssignments[flightId]?.[seatId]?.passengerInitials ?? - `${seatId[seatId.length - 1]}${seatId.slice(0, -1)}` - ); - } - - setCurrentPassenger(passengerId: number) { - this.currentPassengerId = passengerId; - } - nextFlight() { - if (this.currentFlightIndex < this.seatMaps.length - 1) { - this.currentFlightIndex++; - } - } - - previousFlight() { - if (this.currentFlightIndex > 0) { - this.currentFlightIndex--; - } - } -} - -export const seatSelectionVM = new SeatSelectionVM(); diff --git a/apps/frontend/src/lib/domains/ticket/view/checkout/total.calculator.ts b/apps/frontend/src/lib/domains/ticket/view/checkout/total.calculator.ts deleted file mode 100644 index 8503e11..0000000 --- a/apps/frontend/src/lib/domains/ticket/view/checkout/total.calculator.ts +++ /dev/null @@ -1,95 +0,0 @@ -import type { PassengerInfo } from "$lib/domains/passengerinfo/data/entities"; -import type { FlightTicket } from "../../data/entities"; - -export interface BaggageCost { - passengerId: number; - passengerName: string; - personalBagCost: number; - handBagCost: number; - checkedBagCost: number; - totalBaggageCost: number; -} - -export interface PriceBreakdown { - baseTicketPrice: number; - pricePerPassenger: number; - passengerBaggageCosts: BaggageCost[]; - totalBaggageCost: number; - subtotal: number; - discountAmount: number; - finalTotal: number; -} - -export function calculateTicketPrices( - ticket: FlightTicket, - passengerInfos: PassengerInfo[], -): PriceBreakdown { - if (!ticket || !passengerInfos || passengerInfos.length === 0) { - return { - baseTicketPrice: 0, - pricePerPassenger: 0, - passengerBaggageCosts: [], - totalBaggageCost: 0, - subtotal: 0, - discountAmount: 0, - finalTotal: 0, - }; - } - - const displayPrice = ticket.priceDetails?.displayPrice ?? 0; - const originalBasePrice = ticket.priceDetails?.basePrice ?? 0; - const baseTicketPrice = Math.max(displayPrice, originalBasePrice); - const pricePerPassenger = - passengerInfos.length > 0 - ? baseTicketPrice / passengerInfos.length - : baseTicketPrice; - - const passengerBaggageCosts: BaggageCost[] = passengerInfos.map( - (passenger) => { - // const personalBagCost = - // (passenger.bagSelection.personalBags || 0) * - // (ticket?.bagsInfo.details.personalBags.price ?? 0); - // const handBagCost = - // (passenger.bagSelection.handBags || 0) * - // (ticket?.bagsInfo.details.handBags.price ?? 0); - // const checkedBagCost = - // (passenger.bagSelection.checkedBags || 0) * - // (ticket?.bagsInfo.details.checkedBags.price ?? 0); - - return { - passengerId: passenger.id, - passengerName: `${passenger.passengerPii.firstName} ${passenger.passengerPii.lastName}`, - personalBagCost: 0, - handBagCost: 0, - checkedBagCost: 0, - totalBaggageCost: 0, - // totalBaggageCost: personalBagCost + handBagCost + checkedBagCost, - }; - }, - ); - - // const totalBaggageCost = passengerBaggageCosts.reduce( - // (acc, curr) => acc + curr.totalBaggageCost, - // 0, - // ); - const totalBaggageCost = 0; - - const subtotal = baseTicketPrice + totalBaggageCost; - - const discountAmount = - originalBasePrice > displayPrice - ? (ticket?.priceDetails.discountAmount ?? 0) - : 0; - - const finalTotal = subtotal - discountAmount; - - return { - baseTicketPrice, - pricePerPassenger, - passengerBaggageCosts, - totalBaggageCost, - subtotal, - discountAmount, - finalTotal, - }; -} diff --git a/apps/frontend/src/lib/domains/ticket/view/checkout/update-price-dialog.svelte b/apps/frontend/src/lib/domains/ticket/view/checkout/update-price-dialog.svelte deleted file mode 100644 index a12d71d..0000000 --- a/apps/frontend/src/lib/domains/ticket/view/checkout/update-price-dialog.svelte +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - The price has changed! - - Ticket prices change throughout the day and, unfortunately, the - price has been changed since last we had checked. You can continue - with the new price or check out alternative trips. - - - -
- New Price - - - {convertAndFormatCurrency( - ckFlowVM.updatedPrices?.displayPrice ?? 0, - )} - -
- - cancelBooking()} - > - Go Back - - onPriceUpdateConfirm()} - > - - - -
-
diff --git a/apps/frontend/src/lib/trpc/router/index.ts b/apps/frontend/src/lib/trpc/router/index.ts index 7c23ff4..dcc1b5d 100644 --- a/apps/frontend/src/lib/trpc/router/index.ts +++ b/apps/frontend/src/lib/trpc/router/index.ts @@ -1,7 +1,6 @@ import { authRouter } from "$lib/domains/auth/domain/router"; import { ckflowRouter } from "$lib/domains/ckflow/domain/router"; import { currencyRouter } from "$lib/domains/currency/domain/router"; -import { customerInfoRouter } from "$lib/domains/customerinfo/router"; import { orderRouter } from "$lib/domains/order/domain/router"; import { productRouter } from "$lib/domains/product/router"; import { userRouter } from "$lib/domains/user/domain/router"; @@ -14,7 +13,6 @@ export const router = createTRPCRouter({ order: orderRouter, ckflow: ckflowRouter, product: productRouter, - customerInfo: customerInfoRouter, }); export type Router = typeof router;