refactor: create order vm | remove: order email account id thingy admin-side

This commit is contained in:
user
2025-10-21 16:26:57 +03:00
parent 49abd1246b
commit 94bb51bdc7
5 changed files with 139 additions and 115 deletions

View File

@@ -6,7 +6,6 @@
import type { FullOrderModel } from "$lib/domains/order/data/entities";
import ProductIcon from "~icons/solar/box-broken";
import CreditCardIcon from "~icons/solar/card-broken";
import EmailIcon from "~icons/solar/letter-broken";
let { order }: { order: FullOrderModel } = $props();
@@ -17,17 +16,6 @@
</script>
<div class="flex flex-col gap-6">
{#if order.emailAccountId}
<!-- Email Account Info -->
<div class={cardStyle}>
<div class="flex items-center gap-2">
<Icon icon={EmailIcon} cls="w-5 h-5" />
<Title size="h5" color="black">Account Information</Title>
</div>
<p class="text-gray-800">Email Account ID: #{order.emailAccountId}</p>
</div>
{/if}
<!-- Product Info -->
<div class={cardStyle}>
<div class="flex items-center justify-between">

View File

@@ -88,28 +88,4 @@
</div>
</div>
</div>
<!-- Additional Info -->
{#if order.emailAccountId}
<div class={cardStyle}>
<div class="flex items-center gap-2">
<Icon icon={InfoIcon} cls="w-5 h-5" />
<Title size="h5" color="black">Additional Information</Title>
</div>
<div class="flex flex-col gap-2 text-sm">
<div class="flex items-center justify-between">
<span class="text-gray-600">Email Account ID</span>
<span class="font-medium">#{order.emailAccountId}</span>
</div>
{#if order.paymentInfoId}
<div class="flex items-center justify-between">
<span class="text-gray-600">Payment Info ID</span>
<span class="font-medium">#{order.paymentInfoId}</span>
</div>
{/if}
</div>
</div>
{/if}
</div>

View File

@@ -81,14 +81,6 @@
return snakeToSpacedPascal(r.status.toLowerCase());
},
},
{
header: "Order Type",
accessorKey: "ordertype",
cell: ({ row }) => {
const r = row.original as FullOrderModel;
return r.emailAccountId ? "Agent" : "Customer";
},
},
{
header: "Action",
id: "actions",

View File

@@ -1,4 +1,5 @@
import { billingDetailsVM } from "$lib/domains/checkout/payment-info-section/billing.details.vm.svelte";
import { calculateFinalPrices } from "$lib/domains/checkout/utils";
import { ckFlowVM } from "$lib/domains/ckflow/view/ckflow.vm.svelte";
import { customerInfoVM } from "$lib/domains/customerinfo/view/customerinfo.vm.svelte";
import {
@@ -10,104 +11,176 @@ import { trpcApiStore } from "$lib/stores/api";
import { toast } from "svelte-sonner";
import { get } from "svelte/store";
/**
* CreateOrderViewModel manages the order creation flow for product checkout.
* Handles step progression, validation, and order submission.
*/
export class CreateOrderViewModel {
orderStep = $state(OrderCreationStep.ACCOUNT_SELECTION);
// Current step in the order creation flow
orderStep = $state(OrderCreationStep.CUSTOMER_INFO);
passengerInfosOk = $state(false);
loading = $state(true);
// Loading state
loading = $state(false);
/**
* Sets the current order creation step
* @param step - The step to navigate to
*/
setStep(step: OrderCreationStep) {
if (step === OrderCreationStep.ACCOUNT_SELECTION && this.accountInfoOk) {
this.orderStep = step;
} else if (
step === OrderCreationStep.TICKET_SELECTION &&
this.ticketInfoOk
) {
this.orderStep = step;
} else {
this.orderStep = step;
}
this.orderStep = step;
}
/**
* Advances to the next step in the order creation flow
*/
setNextStep() {
if (this.orderStep === OrderCreationStep.ACCOUNT_SELECTION) {
this.orderStep = OrderCreationStep.TICKET_SELECTION;
} else if (this.orderStep === OrderCreationStep.TICKET_SELECTION) {
this.orderStep = OrderCreationStep.CUSTOMER_INFO;
} else if (this.orderStep === OrderCreationStep.CUSTOMER_INFO) {
if (this.orderStep === OrderCreationStep.CUSTOMER_INFO) {
// Validate customer info before proceeding
if (!this.isCustomerInfoValid()) {
toast.error("Please complete customer information");
return;
}
this.orderStep = OrderCreationStep.PAYMENT;
} else if (this.orderStep === OrderCreationStep.PAYMENT) {
this.orderStep = OrderCreationStep.SUMMARY;
} else {
this.orderStep = OrderCreationStep.ACCOUNT_SELECTION;
}
}
/**
* Goes back to the previous step
*/
setPrevStep() {
if (this.orderStep === OrderCreationStep.SUMMARY) {
this.orderStep = OrderCreationStep.PAYMENT;
} else if (this.orderStep === OrderCreationStep.PAYMENT) {
this.orderStep = OrderCreationStep.CUSTOMER_INFO;
} else if (this.orderStep === OrderCreationStep.CUSTOMER_INFO) {
this.orderStep = OrderCreationStep.TICKET_SELECTION;
} else {
this.orderStep = OrderCreationStep.ACCOUNT_SELECTION;
}
}
async createOrder() {
/**
* Validates if customer information is complete
* @returns true if customer info is valid, false otherwise
*/
isCustomerInfoValid(): boolean {
if (!customerInfoVM.customerInfo) {
return false;
}
return customerInfoVM.isValid();
}
/**
* Validates if product is selected
* @returns true if product exists, false otherwise
*/
isProductValid(): boolean {
const product = get(productStore);
return product !== null && product.id !== undefined;
}
/**
* Checks if order can be submitted (all validations pass)
* @returns true if order is ready to submit, false otherwise
*/
canSubmitOrder(): boolean {
return this.isProductValid() && this.isCustomerInfoValid();
}
/**
* Creates and submits the order
* @returns true if successful, false otherwise
*/
async createOrder(): Promise<boolean> {
const api = get(trpcApiStore);
if (!api) {
return;
toast.error("API client not initialized");
return false;
}
let basePrice = 0;
let displayPrice = 0;
let discountAmount = 0;
if (this.ticketInfo) {
basePrice = this.ticketInfo.priceDetails.basePrice;
displayPrice = this.ticketInfo.priceDetails.displayPrice;
discountAmount = this.ticketInfo.priceDetails.discountAmount;
const product = get(productStore);
if (!product || !customerInfoVM.customerInfo) {
toast.error("Missing required information", {
description: "Product or customer information is incomplete",
});
return false;
}
// Calculate price details from product
const priceDetails = calculateFinalPrices(
product,
customerInfoVM.customerInfo,
);
// Build the order payload
const parsed = createOrderPayloadModel.safeParse({
product: get(productStore),
productId: get(productStore)?.id,
product: product,
productId: product.id,
customerInfo: customerInfoVM.customerInfo,
paymentInfo: billingDetailsVM.billingDetails,
orderModel: {
basePrice,
displayPrice,
discountAmount,
flightTicketInfoId: 0,
emailAccountId: 0,
...priceDetails,
productId: product.id,
customerInfoId: customerInfoVM.customerInfo.id,
paymentInfoId: undefined,
},
flowId: ckFlowVM.flowId,
});
if (parsed.error) {
console.log(parsed.error.errors);
console.error("Order payload validation error:", parsed.error.errors);
const msg = parsed.error.errors[0].message;
return toast.error(msg);
toast.error("Invalid order data", {
description: msg,
});
return false;
}
this.loading = true;
const out = await api.order.createOrder.mutate(parsed.data);
try {
const out = await api.order.createOrder.mutate(parsed.data);
if (out.error) {
toast.error(out.error.message, {
description: out.error.userHint,
});
return false;
}
if (!out.data) {
toast.error("Order creation failed", {
description:
"Please try again, or contact support if the issue persists",
});
return false;
}
toast.success("Order created successfully", {
description: "Please wait, redirecting...",
});
// Redirect to success page after a short delay
setTimeout(() => {
window.location.href = `/order/success?id=${out.data}`;
}, 1000);
return true;
} catch (e) {
console.error("Order creation error:", e);
toast.error("An unexpected error occurred", {
description: "Please try again later",
});
return false;
} finally {
this.loading = false;
}
}
/**
* Resets the view model state
*/
reset() {
this.orderStep = OrderCreationStep.CUSTOMER_INFO;
this.loading = false;
console.log(out);
if (out.error) {
return toast.error(out.error.message, {
description: out.error.userHint,
});
}
if (!out.data) {
return toast.error("Order likely failed to create", {
description:
"Please try again, or contact us to resolve the issue",
});
}
toast.success("Order created successfully, redirecting");
setTimeout(() => {
window.location.replace("/");
}, 1000);
}
}