🔄 cleanup: more order logic cleanup on the admin side mostly
This commit is contained in:
@@ -1,5 +1,78 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
let { order }: { order?: any } = $props();
|
import { Badge } from "$lib/components/ui/badge";
|
||||||
|
import { Separator } from "$lib/components/ui/separator";
|
||||||
|
import type { FullOrderModel } from "$lib/domains/order/data/entities";
|
||||||
|
import { OrderStatus } from "$lib/domains/order/data/entities";
|
||||||
|
import OrderMainInfo from "./order-main-info.svelte";
|
||||||
|
import OrderMiscInfo from "./order-misc-info.svelte";
|
||||||
|
|
||||||
|
let { order }: { order?: FullOrderModel } = $props();
|
||||||
|
|
||||||
|
function getStatusVariant(status: OrderStatus) {
|
||||||
|
switch (status) {
|
||||||
|
case OrderStatus.FULFILLED:
|
||||||
|
return "success";
|
||||||
|
case OrderStatus.PARTIALLY_FULFILLED:
|
||||||
|
return "default";
|
||||||
|
case OrderStatus.PENDING_FULFILLMENT:
|
||||||
|
return "secondary";
|
||||||
|
case OrderStatus.CANCELLED:
|
||||||
|
return "destructive";
|
||||||
|
default:
|
||||||
|
return "outline";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatStatus(status: OrderStatus): string {
|
||||||
|
return status
|
||||||
|
.split("_")
|
||||||
|
.map((word) => word.charAt(0) + word.slice(1).toLowerCase())
|
||||||
|
.join(" ");
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<span>show the order details - todo</span>
|
{#if order}
|
||||||
|
<div class="container mx-auto py-6">
|
||||||
|
<!-- Order Header -->
|
||||||
|
<div class="mb-6 flex items-center justify-between">
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<h1 class="text-3xl font-bold">Order #{order.id}</h1>
|
||||||
|
<p class="text-gray-600">
|
||||||
|
Created on {new Date(order.createdAt).toLocaleDateString(
|
||||||
|
"en-US",
|
||||||
|
{
|
||||||
|
year: "numeric",
|
||||||
|
month: "long",
|
||||||
|
day: "numeric",
|
||||||
|
},
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<Badge
|
||||||
|
variant={getStatusVariant(order.status)}
|
||||||
|
class="px-4 py-2 text-base"
|
||||||
|
>
|
||||||
|
{formatStatus(order.status)}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Separator class="my-6" />
|
||||||
|
|
||||||
|
<!-- Order Content Grid -->
|
||||||
|
<div class="grid grid-cols-1 gap-6 lg:grid-cols-2">
|
||||||
|
<!-- Main Info Column -->
|
||||||
|
<div class="flex flex-col gap-6">
|
||||||
|
<OrderMainInfo {order} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Misc Info Column -->
|
||||||
|
<div class="flex flex-col gap-6">
|
||||||
|
<OrderMiscInfo {order} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div class="container mx-auto py-12 text-center">
|
||||||
|
<p class="text-xl text-gray-500">Order not found</p>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
import Icon from "$lib/components/atoms/icon.svelte";
|
import Icon from "$lib/components/atoms/icon.svelte";
|
||||||
import Title from "$lib/components/atoms/title.svelte";
|
import Title from "$lib/components/atoms/title.svelte";
|
||||||
import { Badge } from "$lib/components/ui/badge";
|
import { Badge } from "$lib/components/ui/badge";
|
||||||
|
import CustomerDetailsCard from "$lib/domains/customerinfo/view/customer-details-card.svelte";
|
||||||
import type { FullOrderModel } from "$lib/domains/order/data/entities";
|
import type { FullOrderModel } from "$lib/domains/order/data/entities";
|
||||||
import TicketLegsOverview from "$lib/domains/ticket/view/ticket/ticket-legs-overview.svelte";
|
import ProductIcon from "~icons/solar/box-broken";
|
||||||
import CreditCardIcon from "~icons/solar/card-broken";
|
import CreditCardIcon from "~icons/solar/card-broken";
|
||||||
import EmailIcon from "~icons/solar/letter-broken";
|
import EmailIcon from "~icons/solar/letter-broken";
|
||||||
import TicketIcon from "~icons/solar/ticket-broken";
|
|
||||||
|
|
||||||
let { order }: { order: FullOrderModel } = $props();
|
let { order }: { order: FullOrderModel } = $props();
|
||||||
|
|
||||||
@@ -17,38 +17,59 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex flex-col gap-6">
|
<div class="flex flex-col gap-6">
|
||||||
{#if order.emailAccountId && order.emailAccount}
|
{#if order.emailAccountId}
|
||||||
<!-- Email Account Info -->
|
<!-- Email Account Info -->
|
||||||
<div class={cardStyle}>
|
<div class={cardStyle}>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<Icon icon={EmailIcon} cls="w-5 h-5" />
|
<Icon icon={EmailIcon} cls="w-5 h-5" />
|
||||||
<Title size="h5" color="black">Account Information</Title>
|
<Title size="h5" color="black">Account Information</Title>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-gray-800">{order.emailAccount?.email}</p>
|
<p class="text-gray-800">Email Account ID: #{order.emailAccountId}</p>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<!-- Flight Ticket Info -->
|
<!-- Product Info -->
|
||||||
<div class={cardStyle}>
|
<div class={cardStyle}>
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<Icon icon={TicketIcon} cls="w-5 h-5" />
|
<Icon icon={ProductIcon} cls="w-5 h-5" />
|
||||||
<Title size="h5" color="black">Flight Details</Title>
|
<Title size="h5" color="black">Product Details</Title>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex gap-2">
|
{#if order.product.discountPrice > 0 && order.product.discountPrice < order.product.price}
|
||||||
<Badge variant="outline">
|
<Badge variant="success">Discounted</Badge>
|
||||||
{order.flightTicketInfo.flightType}
|
{/if}
|
||||||
</Badge>
|
</div>
|
||||||
<Badge variant="secondary">
|
|
||||||
{order.flightTicketInfo.cabinClass}
|
<div class="flex flex-col gap-3">
|
||||||
</Badge>
|
<div>
|
||||||
|
<span class="text-sm text-gray-500">Product Name</span>
|
||||||
|
<p class="font-medium">{order.product.title}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<span class="text-sm text-gray-500">Description</span>
|
||||||
|
<p class="text-gray-700">{order.product.description}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-2 gap-4">
|
||||||
|
<div>
|
||||||
|
<span class="text-sm text-gray-500">Regular Price</span>
|
||||||
|
<p class="font-medium">${order.product.price.toFixed(2)}</p>
|
||||||
|
</div>
|
||||||
|
{#if order.product.discountPrice > 0}
|
||||||
|
<div>
|
||||||
|
<span class="text-sm text-gray-500">Discount Price</span>
|
||||||
|
<p class="font-medium text-green-600">
|
||||||
|
${order.product.discountPrice.toFixed(2)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<TicketLegsOverview data={order.flightTicketInfo} />
|
<!-- Price Summary -->
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class={cardStyle}>
|
<div class={cardStyle}>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<Icon icon={CreditCardIcon} cls="w-5 h-5" />
|
<Icon icon={CreditCardIcon} cls="w-5 h-5" />
|
||||||
@@ -58,6 +79,11 @@
|
|||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<span>Base Price</span>
|
<span>Base Price</span>
|
||||||
|
<span>${order.basePrice.toFixed(2)}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<span>Display Price</span>
|
||||||
<span>${order.displayPrice.toFixed(2)}</span>
|
<span>${order.displayPrice.toFixed(2)}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -71,9 +97,19 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="mt-2 flex items-center justify-between border-t pt-2">
|
<div class="mt-2 flex items-center justify-between border-t pt-2">
|
||||||
<span class="font-medium">Total Price</span>
|
<span class="font-medium">Order Price</span>
|
||||||
<span class="font-medium">${order.orderPrice.toFixed(2)}</span>
|
<span class="font-medium">${order.orderPrice.toFixed(2)}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<span class="text-sm text-gray-500">Fulfilled</span>
|
||||||
|
<span class="text-sm">${order.fullfilledPrice.toFixed(2)}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{#if order.customerInfo}
|
||||||
|
<!-- Customer Information -->
|
||||||
|
<CustomerDetailsCard customerInfo={order.customerInfo} />
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|||||||
@@ -1,118 +1,115 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Icon from "$lib/components/atoms/icon.svelte";
|
import Icon from "$lib/components/atoms/icon.svelte";
|
||||||
import Title from "$lib/components/atoms/title.svelte";
|
import Title from "$lib/components/atoms/title.svelte";
|
||||||
import { adminSiteNavMap } from "$lib/core/constants";
|
import { Badge } from "$lib/components/ui/badge";
|
||||||
import { capitalize } from "$lib/core/string.utils";
|
|
||||||
import CinfoCard from "$lib/domains/customerinfo/view/cinfo-card.svelte";
|
|
||||||
import type { FullOrderModel } from "$lib/domains/order/data/entities";
|
import type { FullOrderModel } from "$lib/domains/order/data/entities";
|
||||||
import SuitcaseIcon from "~icons/bi/suitcase2";
|
import { OrderStatus } from "$lib/domains/order/data/entities";
|
||||||
import BagIcon from "~icons/lucide/briefcase";
|
import CalendarIcon from "~icons/solar/calendar-broken";
|
||||||
import BackpackIcon from "~icons/solar/backpack-linear";
|
import InfoIcon from "~icons/solar/info-circle-broken";
|
||||||
import PackageIcon from "~icons/solar/box-broken";
|
|
||||||
import UsersIcon from "~icons/solar/users-group-rounded-broken";
|
|
||||||
|
|
||||||
let { order }: { order: FullOrderModel } = $props();
|
let { order }: { order: FullOrderModel } = $props();
|
||||||
|
|
||||||
const cardStyle =
|
const cardStyle =
|
||||||
"flex flex-col gap-4 rounded-lg border border-gray-200 bg-white p-6 shadow-md";
|
"flex flex-col gap-4 rounded-lg border border-gray-200 bg-white p-6 shadow-md";
|
||||||
|
|
||||||
|
function getStatusVariant(status: OrderStatus) {
|
||||||
|
switch (status) {
|
||||||
|
case OrderStatus.FULFILLED:
|
||||||
|
return "success";
|
||||||
|
case OrderStatus.PARTIALLY_FULFILLED:
|
||||||
|
return "default";
|
||||||
|
case OrderStatus.PENDING_FULFILLMENT:
|
||||||
|
return "secondary";
|
||||||
|
case OrderStatus.CANCELLED:
|
||||||
|
return "destructive";
|
||||||
|
default:
|
||||||
|
return "outline";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatStatus(status: OrderStatus): string {
|
||||||
|
return status
|
||||||
|
.split("_")
|
||||||
|
.map((word) => word.charAt(0) + word.slice(1).toLowerCase())
|
||||||
|
.join(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatDate(dateString: string): string {
|
||||||
|
const date = new Date(dateString);
|
||||||
|
return date.toLocaleDateString("en-US", {
|
||||||
|
year: "numeric",
|
||||||
|
month: "long",
|
||||||
|
day: "numeric",
|
||||||
|
hour: "2-digit",
|
||||||
|
minute: "2-digit",
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex flex-col gap-6">
|
<div class="flex flex-col gap-6">
|
||||||
<!-- Passenger Information -->
|
<!-- Order Status -->
|
||||||
<div class={cardStyle}>
|
<div class={cardStyle}>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<Icon icon={UsersIcon} cls="w-5 h-5" />
|
<Icon icon={InfoIcon} cls="w-5 h-5" />
|
||||||
<Title size="h5" color="black">Passengers</Title>
|
<Title size="h5" color="black">Order Status</Title>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#each order.passengerInfos as passenger, index}
|
|
||||||
<div class="flex flex-col gap-4">
|
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<span class="font-semibold">
|
<span class="text-gray-600">Current Status</span>
|
||||||
Passenger {index + 1} ({capitalize(
|
<Badge variant={getStatusVariant(order.status)}>
|
||||||
passenger.passengerType,
|
{formatStatus(order.status)}
|
||||||
)})
|
</Badge>
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Personal Info -->
|
<div class="flex flex-col gap-2 text-sm">
|
||||||
<div class="rounded-lg border bg-gray-50 p-4">
|
<div class="flex items-center justify-between">
|
||||||
<div class="grid grid-cols-2 gap-3 text-sm md:grid-cols-3">
|
<span class="text-gray-600">Order ID</span>
|
||||||
<div>
|
<span class="font-medium">#{order.id}</span>
|
||||||
<span class="text-gray-500">Name</span>
|
|
||||||
<p class="font-medium">
|
|
||||||
{passenger.passengerPii.firstName}
|
|
||||||
{passenger.passengerPii.middleName}
|
|
||||||
{passenger.passengerPii.lastName}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span class="text-gray-500">Nationality</span>
|
|
||||||
<p class="font-medium">
|
|
||||||
{capitalize(passenger.passengerPii.nationality)}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span class="text-gray-500">Date of Birth</span>
|
|
||||||
<p class="font-medium">
|
|
||||||
{passenger.passengerPii.dob}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Baggage Info -->
|
<!-- Timestamps -->
|
||||||
<div class="flex flex-wrap gap-4 text-sm">
|
<div class={cardStyle}>
|
||||||
{#if passenger.bagSelection.personalBags > 0}
|
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<Icon
|
<Icon icon={CalendarIcon} cls="w-5 h-5" />
|
||||||
icon={BackpackIcon}
|
<Title size="h5" color="black">Timeline</Title>
|
||||||
cls="h-5 w-5 text-gray-600"
|
|
||||||
/>
|
|
||||||
<span
|
|
||||||
>{passenger.bagSelection.personalBags}x Personal
|
|
||||||
Item</span
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
|
||||||
{#if passenger.bagSelection.handBags > 0}
|
<div class="flex flex-col gap-3">
|
||||||
<div class="flex items-center gap-2">
|
<div>
|
||||||
<Icon
|
<span class="text-sm text-gray-500">Created At</span>
|
||||||
icon={SuitcaseIcon}
|
<p class="font-medium">{formatDate(order.createdAt)}</p>
|
||||||
cls="h-5 w-5 text-gray-600"
|
|
||||||
/>
|
|
||||||
<span
|
|
||||||
>{passenger.bagSelection.handBags}x Cabin Bag</span
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
|
||||||
{#if passenger.bagSelection.checkedBags > 0}
|
<div>
|
||||||
<div class="flex items-center gap-2">
|
<span class="text-sm text-gray-500">Last Updated</span>
|
||||||
<Icon icon={BagIcon} cls="h-5 w-5 text-gray-600" />
|
<p class="font-medium">{formatDate(order.updatedAt)}</p>
|
||||||
<span
|
|
||||||
>{passenger.bagSelection.checkedBags}x Checked Bag</span
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if index < order.passengerInfos.length - 1}
|
<!-- Additional Info -->
|
||||||
<div class="border-b border-dashed"></div>
|
{#if order.emailAccountId}
|
||||||
{/if}
|
<div class={cardStyle}>
|
||||||
{/each}
|
<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>
|
||||||
|
|
||||||
{#if order.flightTicketInfo.refOIds}
|
<div class="flex flex-col gap-2 text-sm">
|
||||||
{#each order.flightTicketInfo.refOIds as refOId}
|
<div class="flex items-center justify-between">
|
||||||
<CinfoCard icon={PackageIcon} title="Order">
|
<span class="text-gray-600">Email Account ID</span>
|
||||||
<a
|
<span class="font-medium">#{order.emailAccountId}</span>
|
||||||
href={`${adminSiteNavMap.orders}/${refOId}`}
|
</div>
|
||||||
class="mt-1 inline-block font-medium text-primary hover:underline"
|
|
||||||
>
|
{#if order.paymentInfoId}
|
||||||
Reference Order #{refOId}
|
<div class="flex items-center justify-between">
|
||||||
</a>
|
<span class="text-gray-600">Payment Info ID</span>
|
||||||
</CinfoCard>
|
<span class="font-medium">#{order.paymentInfoId}</span>
|
||||||
{/each}
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { get } from "svelte/store";
|
import { encodeCursor } from "$lib/core/string.utils";
|
||||||
import {
|
|
||||||
getDefaultOrderCursor,
|
|
||||||
type PaginatedOrderInfoModel,
|
|
||||||
type OrderCursorModel,
|
|
||||||
getDefaultPaginatedOrderInfoModel,
|
|
||||||
} from "../data/entities";
|
|
||||||
import { trpcApiStore } from "$lib/stores/api";
|
import { trpcApiStore } from "$lib/stores/api";
|
||||||
import type { Result } from "@pkg/result";
|
import type { Result } from "@pkg/result";
|
||||||
import { toast } from "svelte-sonner";
|
import { toast } from "svelte-sonner";
|
||||||
import { encodeCursor } from "$lib/core/string.utils";
|
import { get } from "svelte/store";
|
||||||
|
import {
|
||||||
|
getDefaultOrderCursor,
|
||||||
|
getDefaultPaginatedOrderInfoModel,
|
||||||
|
type OrderCursorModel,
|
||||||
|
type PaginatedOrderInfoModel,
|
||||||
|
} from "../data/entities";
|
||||||
|
|
||||||
export class OrderViewModel {
|
export class OrderViewModel {
|
||||||
orderInfo = $state<PaginatedOrderInfoModel>(
|
orderInfo = $state<PaginatedOrderInfoModel>(
|
||||||
@@ -82,16 +82,7 @@ export class OrderViewModel {
|
|||||||
this.resetOrderInfo();
|
this.resetOrderInfo();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.orderInfo = {
|
this.orderInfo = { ...res.data, data: res.data.data };
|
||||||
...res.data,
|
|
||||||
data: res.data.data.map((item) => {
|
|
||||||
return {
|
|
||||||
...item,
|
|
||||||
createdAt: new Date(item.createdAt),
|
|
||||||
updatedAt: new Date(item.updatedAt),
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async searchOrders() {
|
async searchOrders() {
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {
|
import { goto } from "$app/navigation";
|
||||||
getCoreRowModel,
|
import Title from "$lib/components/atoms/title.svelte";
|
||||||
getPaginationRowModel,
|
import DataTableActions from "$lib/components/molecules/data-table/data-table-actions.svelte";
|
||||||
getFilteredRowModel,
|
import DataTable from "$lib/components/molecules/data-table/data-table.svelte";
|
||||||
type ColumnDef,
|
|
||||||
type PaginationState,
|
|
||||||
type ColumnFiltersState,
|
|
||||||
} from "@tanstack/table-core";
|
|
||||||
import {
|
import {
|
||||||
createSvelteTable,
|
createSvelteTable,
|
||||||
renderComponent,
|
renderComponent,
|
||||||
} from "$lib/components/ui/data-table";
|
} from "$lib/components/ui/data-table";
|
||||||
import DataTable from "$lib/components/molecules/data-table/data-table.svelte";
|
|
||||||
import DataTableActions from "$lib/components/molecules/data-table/data-table-actions.svelte";
|
|
||||||
import Title from "$lib/components/atoms/title.svelte";
|
|
||||||
import { goto } from "$app/navigation";
|
|
||||||
import { adminBasePath } from "$lib/core/constants";
|
import { adminBasePath } from "$lib/core/constants";
|
||||||
|
import { snakeToSpacedPascal } from "$lib/core/string.utils";
|
||||||
|
import {
|
||||||
|
getCoreRowModel,
|
||||||
|
getFilteredRowModel,
|
||||||
|
getPaginationRowModel,
|
||||||
|
type ColumnDef,
|
||||||
|
type ColumnFiltersState,
|
||||||
|
type PaginationState,
|
||||||
|
} from "@tanstack/table-core";
|
||||||
import { useDebounce } from "runed";
|
import { useDebounce } from "runed";
|
||||||
import type { FullOrderModel } from "../data/entities";
|
import type { FullOrderModel } from "../data/entities";
|
||||||
import { orderVM } from "./order.vm.svelte";
|
import { orderVM } from "./order.vm.svelte";
|
||||||
import { capitalize, snakeToSpacedPascal } from "$lib/core/string.utils";
|
|
||||||
|
|
||||||
let { data }: { data: FullOrderModel[] } = $props();
|
let { data }: { data: FullOrderModel[] } = $props();
|
||||||
|
|
||||||
@@ -33,34 +33,45 @@
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: "Places",
|
header: "Order ID",
|
||||||
accessorKey: "direction",
|
accessorKey: "orderId",
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const r = row.original as FullOrderModel;
|
const r = row.original as FullOrderModel;
|
||||||
return `${r.flightTicketInfo.departure} - ${r.flightTicketInfo.arrival}`;
|
return `#${r.id}`;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: "Type",
|
header: "Product",
|
||||||
accessorKey: "type",
|
accessorKey: "product",
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const r = row.original as FullOrderModel;
|
const r = row.original as FullOrderModel;
|
||||||
return capitalize(r.flightTicketInfo.flightType.toLowerCase());
|
return r.product.title;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: "Passenger(s)",
|
header: "Customer",
|
||||||
accessorKey: "passengers",
|
accessorKey: "customer",
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const _o = (row.original as FullOrderModel).flightTicketInfo
|
const r = row.original as FullOrderModel;
|
||||||
.passengerCounts;
|
if (!r.customerInfo) return "N/A";
|
||||||
return `${_o.adults + _o.children}`;
|
return `${r.customerInfo.firstName} ${r.customerInfo.lastName}`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: "Customer Email",
|
||||||
|
accessorKey: "customerEmail",
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const r = row.original as FullOrderModel;
|
||||||
|
return r.customerInfo?.email || "N/A";
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: "Price",
|
header: "Price",
|
||||||
accessorKey: "orderPrice",
|
accessorKey: "orderPrice",
|
||||||
cell: ({ row }) => (row.original as FullOrderModel).orderPrice!,
|
cell: ({ row }) => {
|
||||||
|
const r = row.original as FullOrderModel;
|
||||||
|
return `$${r.orderPrice.toFixed(2)}`;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: "Status",
|
header: "Status",
|
||||||
@@ -141,7 +152,7 @@
|
|||||||
() => debounceDuration,
|
() => debounceDuration,
|
||||||
);
|
);
|
||||||
|
|
||||||
let pageCount = $derived(2);
|
let pageCount = $derived(Math.ceil(data.length / pagination.pageSize) || 1);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if data.length > 0}
|
{#if data.length > 0}
|
||||||
@@ -162,7 +173,7 @@
|
|||||||
orderVM.query = q;
|
orderVM.query = q;
|
||||||
debouncedSearch();
|
debouncedSearch();
|
||||||
}}
|
}}
|
||||||
filterFieldPlaceholder="Search users..."
|
filterFieldPlaceholder="Search orders..."
|
||||||
/>
|
/>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="grid h-full place-items-center p-4 py-12 md:p-8 md:py-32">
|
<div class="grid h-full place-items-center p-4 py-12 md:p-8 md:py-32">
|
||||||
|
|||||||
@@ -1,34 +1,80 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {
|
import { goto } from "$app/navigation";
|
||||||
getCoreRowModel,
|
import Title from "$lib/components/atoms/title.svelte";
|
||||||
getPaginationRowModel,
|
import DataTableActions from "$lib/components/molecules/data-table/data-table-actions.svelte";
|
||||||
getFilteredRowModel,
|
import DataTable from "$lib/components/molecules/data-table/data-table.svelte";
|
||||||
type ColumnDef,
|
|
||||||
type PaginationState,
|
|
||||||
type ColumnFiltersState,
|
|
||||||
} from "@tanstack/table-core";
|
|
||||||
import {
|
import {
|
||||||
createSvelteTable,
|
createSvelteTable,
|
||||||
renderComponent,
|
renderComponent,
|
||||||
} from "$lib/components/ui/data-table";
|
} from "$lib/components/ui/data-table";
|
||||||
import DataTable from "$lib/components/molecules/data-table/data-table.svelte";
|
|
||||||
import DataTableActions from "$lib/components/molecules/data-table/data-table-actions.svelte";
|
|
||||||
import Title from "$lib/components/atoms/title.svelte";
|
|
||||||
import { goto } from "$app/navigation";
|
|
||||||
import { adminBasePath } from "$lib/core/constants";
|
import { adminBasePath } from "$lib/core/constants";
|
||||||
import { orderVM } from "./order.vm.svelte";
|
import {
|
||||||
|
getCoreRowModel,
|
||||||
|
getFilteredRowModel,
|
||||||
|
getPaginationRowModel,
|
||||||
|
type ColumnDef,
|
||||||
|
type ColumnFiltersState,
|
||||||
|
type PaginationState,
|
||||||
|
} from "@tanstack/table-core";
|
||||||
import { useDebounce } from "runed";
|
import { useDebounce } from "runed";
|
||||||
|
import { OrderStatus } from "../data/entities";
|
||||||
|
import { orderVM } from "./order.vm.svelte";
|
||||||
|
|
||||||
|
function getStatusVariant(status: OrderStatus) {
|
||||||
|
switch (status) {
|
||||||
|
case OrderStatus.FULFILLED:
|
||||||
|
return "success";
|
||||||
|
case OrderStatus.PARTIALLY_FULFILLED:
|
||||||
|
return "default";
|
||||||
|
case OrderStatus.PENDING_FULFILLMENT:
|
||||||
|
return "secondary";
|
||||||
|
case OrderStatus.CANCELLED:
|
||||||
|
return "destructive";
|
||||||
|
default:
|
||||||
|
return "outline";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatStatus(status: OrderStatus): string {
|
||||||
|
return status
|
||||||
|
.split("_")
|
||||||
|
.map((word) => word.charAt(0) + word.slice(1).toLowerCase())
|
||||||
|
.join(" ");
|
||||||
|
}
|
||||||
|
|
||||||
// Define columns
|
// Define columns
|
||||||
const columns: ColumnDef<any>[] = [
|
const columns: ColumnDef<any>[] = [
|
||||||
{
|
{
|
||||||
header: "Username",
|
header: "Order ID",
|
||||||
accessorKey: "username",
|
accessorKey: "id",
|
||||||
|
cell: ({ row }) => `#${row.original.id}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: "Email",
|
header: "Product",
|
||||||
accessorKey: "email",
|
id: "product",
|
||||||
cell: ({ getValue }) => getValue<string>().toLowerCase(),
|
cell: ({ row }) => {
|
||||||
|
return row.original.product.title;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: "Price",
|
||||||
|
id: "price",
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const basePrice = row.original.displayPrice;
|
||||||
|
const finalPrice = row.original.basePrice;
|
||||||
|
const hasDiscount = row.original.discountAmount > 0;
|
||||||
|
|
||||||
|
return hasDiscount
|
||||||
|
? `$${finalPrice.toFixed(2)} (was $${basePrice.toFixed(2)})`
|
||||||
|
: `$${finalPrice.toFixed(2)}`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: "Status",
|
||||||
|
accessorKey: "status",
|
||||||
|
cell: ({ row }) => {
|
||||||
|
return formatStatus(row.original.status);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: "Action",
|
header: "Action",
|
||||||
@@ -37,9 +83,11 @@
|
|||||||
return renderComponent(DataTableActions, {
|
return renderComponent(DataTableActions, {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
title: "View agent",
|
title: "View Order",
|
||||||
action: () => {
|
action: () => {
|
||||||
goto(`${adminBasePath}/users/${row.original.id}`);
|
goto(
|
||||||
|
`${adminBasePath}/orders/${row.original.id}`,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -113,7 +161,7 @@
|
|||||||
orderVM.query = q;
|
orderVM.query = q;
|
||||||
debouncedSearch();
|
debouncedSearch();
|
||||||
}}
|
}}
|
||||||
filterFieldPlaceholder="Search users..."
|
filterFieldPlaceholder="Search orders..."
|
||||||
/>
|
/>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="grid place-items-center p-4 py-12 md:p-8 md:py-24">
|
<div class="grid place-items-center p-4 py-12 md:p-8 md:py-24">
|
||||||
|
|||||||
@@ -2,17 +2,17 @@ import { eq, type Database } from "@pkg/db";
|
|||||||
import { paymentInfo } from "@pkg/db/schema";
|
import { paymentInfo } from "@pkg/db/schema";
|
||||||
import { Logger } from "@pkg/logger";
|
import { Logger } from "@pkg/logger";
|
||||||
import type { Result } from "@pkg/result";
|
import type { Result } from "@pkg/result";
|
||||||
import { paymentDetailsModel, type PaymentDetails } from "./data";
|
import { paymentInfoModel, type PaymentInfo } from "./data";
|
||||||
|
|
||||||
export class PaymentInfoRepository {
|
export class PaymentInfoRepository {
|
||||||
constructor(private db: Database) {}
|
constructor(private db: Database) {}
|
||||||
|
|
||||||
async getPaymentInfo(id: number): Promise<Result<PaymentDetails>> {
|
async getPaymentInfo(id: number): Promise<Result<PaymentInfo>> {
|
||||||
Logger.info(`Getting payment info with id ${id}`);
|
Logger.info(`Getting payment info with id ${id}`);
|
||||||
const out = await this.db.query.paymentInfo.findFirst({
|
const out = await this.db.query.paymentInfo.findFirst({
|
||||||
where: eq(paymentInfo.id, id),
|
where: eq(paymentInfo.id, id),
|
||||||
});
|
});
|
||||||
const parsed = paymentDetailsModel.safeParse(out);
|
const parsed = paymentInfoModel.safeParse(out);
|
||||||
if (parsed.error) {
|
if (parsed.error) {
|
||||||
Logger.error(parsed.error);
|
Logger.error(parsed.error);
|
||||||
return {};
|
return {};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { CustomerInfo } from "$lib/domains/passengerinfo/data/entities";
|
import type { CustomerInfo } from "$lib/domains/passengerinfo/data/entities";
|
||||||
import type { PaymentDetailsPayload } from "$lib/domains/paymentinfo/data/entities";
|
import type { PaymentInfoPayload } from "$lib/domains/paymentinfo/data/entities";
|
||||||
import { CheckoutStep } from "$lib/domains/ticket/data/entities";
|
import { CheckoutStep } from "$lib/domains/ticket/data/entities";
|
||||||
import type { Database } from "@pkg/db";
|
import type { Database } from "@pkg/db";
|
||||||
import { and, eq } from "@pkg/db";
|
import { and, eq } from "@pkg/db";
|
||||||
@@ -254,7 +254,7 @@ export class CheckoutFlowRepository {
|
|||||||
|
|
||||||
async syncPaymentInfo(
|
async syncPaymentInfo(
|
||||||
flowId: string,
|
flowId: string,
|
||||||
paymentInfo: PaymentDetailsPayload,
|
paymentInfo: PaymentInfoPayload,
|
||||||
): Promise<Result<boolean>> {
|
): Promise<Result<boolean>> {
|
||||||
try {
|
try {
|
||||||
const existingSession = await this.db
|
const existingSession = await this.db
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import {
|
|||||||
type CustomerInfo,
|
type CustomerInfo,
|
||||||
} from "$lib/domains/passengerinfo/data/entities";
|
} from "$lib/domains/passengerinfo/data/entities";
|
||||||
import {
|
import {
|
||||||
paymentDetailsPayloadModel,
|
paymentInfoPayloadModel,
|
||||||
type PaymentDetailsPayload,
|
type PaymentInfoPayload,
|
||||||
} from "$lib/domains/paymentinfo/data/entities";
|
} from "$lib/domains/paymentinfo/data/entities";
|
||||||
import { CheckoutStep } from "$lib/domains/ticket/data/entities";
|
import { CheckoutStep } from "$lib/domains/ticket/data/entities";
|
||||||
import { createTRPCRouter, publicProcedure } from "$lib/trpc/t";
|
import { createTRPCRouter, publicProcedure } from "$lib/trpc/t";
|
||||||
@@ -86,7 +86,7 @@ export const ckflowRouter = createTRPCRouter({
|
|||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input }) => {
|
||||||
return getCKUseCases().syncPaymentInfo(
|
return getCKUseCases().syncPaymentInfo(
|
||||||
input.flowId,
|
input.flowId,
|
||||||
input.paymentInfo as PaymentDetailsPayload,
|
input.paymentInfo as PaymentInfoPayload,
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
@@ -119,7 +119,7 @@ export const ckflowRouter = createTRPCRouter({
|
|||||||
flowId: z.string(),
|
flowId: z.string(),
|
||||||
payload: z.object({
|
payload: z.object({
|
||||||
personalInfo: customerInfoModel.optional(),
|
personalInfo: customerInfoModel.optional(),
|
||||||
paymentInfo: paymentDetailsPayloadModel.optional(),
|
paymentInfo: paymentInfoPayloadModel.optional(),
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { CustomerInfo } from "$lib/domains/passengerinfo/data/entities";
|
import type { CustomerInfo } from "$lib/domains/passengerinfo/data/entities";
|
||||||
import type { PaymentDetailsPayload } from "$lib/domains/paymentinfo/data/entities";
|
import type { PaymentInfoPayload } from "$lib/domains/paymentinfo/data/entities";
|
||||||
import { db } from "@pkg/db";
|
import { db } from "@pkg/db";
|
||||||
import { isTimestampMoreThan1MinAgo } from "@pkg/logic/core/date.utils";
|
import { isTimestampMoreThan1MinAgo } from "@pkg/logic/core/date.utils";
|
||||||
import type {
|
import type {
|
||||||
@@ -58,7 +58,7 @@ export class CheckoutFlowUseCases {
|
|||||||
return this.repo.syncPersonalInfo(flowId, personalInfo);
|
return this.repo.syncPersonalInfo(flowId, personalInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
async syncPaymentInfo(flowId: string, paymentInfo: PaymentDetailsPayload) {
|
async syncPaymentInfo(flowId: string, paymentInfo: PaymentInfoPayload) {
|
||||||
return this.repo.syncPaymentInfo(flowId, paymentInfo);
|
return this.repo.syncPaymentInfo(flowId, paymentInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
type CustomerInfo,
|
type CustomerInfo,
|
||||||
} from "$lib/domains/passengerinfo/data/entities";
|
} from "$lib/domains/passengerinfo/data/entities";
|
||||||
import { passengerInfoVM } from "$lib/domains/passengerinfo/view/passenger.info.vm.svelte";
|
import { passengerInfoVM } from "$lib/domains/passengerinfo/view/passenger.info.vm.svelte";
|
||||||
import type { PaymentDetailsPayload } from "$lib/domains/paymentinfo/data/entities";
|
import type { PaymentInfoPayload } from "$lib/domains/paymentinfo/data/entities";
|
||||||
import { PaymentMethod } from "$lib/domains/paymentinfo/data/entities";
|
import { PaymentMethod } from "$lib/domains/paymentinfo/data/entities";
|
||||||
import {
|
import {
|
||||||
CheckoutStep,
|
CheckoutStep,
|
||||||
@@ -302,7 +302,7 @@ export class CKFlowViewModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async syncPaymentInfo(paymentInfo: PaymentDetailsPayload) {
|
async syncPaymentInfo(paymentInfo: PaymentInfoPayload) {
|
||||||
if (!this.flowId || !this.setupDone || !paymentInfo.cardDetails) {
|
if (!this.flowId || !this.setupDone || !paymentInfo.cardDetails) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ import { paymentInfo } from "@pkg/db/schema";
|
|||||||
import { Logger } from "@pkg/logger";
|
import { Logger } from "@pkg/logger";
|
||||||
import type { Result } from "@pkg/result";
|
import type { Result } from "@pkg/result";
|
||||||
import {
|
import {
|
||||||
paymentDetailsModel,
|
paymentInfoModel,
|
||||||
type PaymentDetails,
|
type PaymentInfo,
|
||||||
type PaymentDetailsPayload,
|
type PaymentInfoPayload,
|
||||||
} from "./entities";
|
} from "./entities";
|
||||||
|
|
||||||
export class PaymentInfoRepository {
|
export class PaymentInfoRepository {
|
||||||
@@ -14,9 +14,7 @@ export class PaymentInfoRepository {
|
|||||||
this.db = db;
|
this.db = db;
|
||||||
}
|
}
|
||||||
|
|
||||||
async createPaymentInfo(
|
async createPaymentInfo(data: PaymentInfoPayload): Promise<Result<number>> {
|
||||||
data: PaymentDetailsPayload,
|
|
||||||
): Promise<Result<number>> {
|
|
||||||
const out = await this.db
|
const out = await this.db
|
||||||
.insert(paymentInfo)
|
.insert(paymentInfo)
|
||||||
.values({
|
.values({
|
||||||
@@ -34,12 +32,12 @@ export class PaymentInfoRepository {
|
|||||||
return { data: out[0]?.id };
|
return { data: out[0]?.id };
|
||||||
}
|
}
|
||||||
|
|
||||||
async getPaymentInfo(id: number): Promise<Result<PaymentDetails>> {
|
async getPaymentInfo(id: number): Promise<Result<PaymentInfo>> {
|
||||||
Logger.info(`Getting payment info with id ${id}`);
|
Logger.info(`Getting payment info with id ${id}`);
|
||||||
const out = await this.db.query.paymentInfo.findFirst({
|
const out = await this.db.query.paymentInfo.findFirst({
|
||||||
where: eq(paymentInfo.id, id),
|
where: eq(paymentInfo.id, id),
|
||||||
});
|
});
|
||||||
const parsed = paymentDetailsModel.safeParse(out);
|
const parsed = paymentInfoModel.safeParse(out);
|
||||||
if (parsed.error) {
|
if (parsed.error) {
|
||||||
Logger.error(parsed.error);
|
Logger.error(parsed.error);
|
||||||
return {};
|
return {};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { PaymentDetailsPayload } from "../data/entities";
|
import type { PaymentInfoPayload } from "../data/entities";
|
||||||
import type { PaymentInfoRepository } from "../data/repository";
|
import type { PaymentInfoRepository } from "../data/repository";
|
||||||
|
|
||||||
export class PaymentInfoUseCases {
|
export class PaymentInfoUseCases {
|
||||||
@@ -8,7 +8,7 @@ export class PaymentInfoUseCases {
|
|||||||
this.repo = repo;
|
this.repo = repo;
|
||||||
}
|
}
|
||||||
|
|
||||||
async createPaymentInfo(payload: PaymentDetailsPayload) {
|
async createPaymentInfo(payload: PaymentInfoPayload) {
|
||||||
return this.repo.createPaymentInfo(payload);
|
return this.repo.createPaymentInfo(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { ckFlowVM } from "$lib/domains/ckflow/view/ckflow.vm.svelte";
|
|||||||
import { newOrderModel } from "$lib/domains/order/data/entities";
|
import { newOrderModel } from "$lib/domains/order/data/entities";
|
||||||
import { passengerInfoVM } from "$lib/domains/passengerinfo/view/passenger.info.vm.svelte";
|
import { passengerInfoVM } from "$lib/domains/passengerinfo/view/passenger.info.vm.svelte";
|
||||||
import {
|
import {
|
||||||
paymentDetailsPayloadModel,
|
paymentInfoPayloadModel,
|
||||||
PaymentMethod,
|
PaymentMethod,
|
||||||
} from "$lib/domains/paymentinfo/data/entities";
|
} from "$lib/domains/paymentinfo/data/entities";
|
||||||
import { trpcApiStore } from "$lib/stores/api";
|
import { trpcApiStore } from "$lib/stores/api";
|
||||||
@@ -105,7 +105,7 @@ class TicketCheckoutViewModel {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const pInfoParsed = paymentDetailsPayloadModel.safeParse({
|
const pInfoParsed = paymentInfoPayloadModel.safeParse({
|
||||||
method: PaymentMethod.Card,
|
method: PaymentMethod.Card,
|
||||||
cardDetails: paymentInfoVM.cardDetails,
|
cardDetails: paymentInfoVM.cardDetails,
|
||||||
flightTicketInfoId: ticket.id,
|
flightTicketInfoId: ticket.id,
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import {
|
|||||||
customerInfoModel,
|
customerInfoModel,
|
||||||
} from "../../passengerinfo/data/entities";
|
} from "../../passengerinfo/data/entities";
|
||||||
import {
|
import {
|
||||||
PaymentDetailsPayload,
|
PaymentInfoPayload,
|
||||||
paymentDetailsPayloadModel,
|
paymentInfoPayloadModel,
|
||||||
} from "../../paymentinfo/data/entities";
|
} from "../../paymentinfo/data/entities";
|
||||||
import { productModel } from "../../product/data";
|
import { productModel } from "../../product/data";
|
||||||
|
|
||||||
@@ -78,7 +78,7 @@ export const flowInfoModel = z.object({
|
|||||||
|
|
||||||
pendingActions: pendingActionsModel.default([]),
|
pendingActions: pendingActionsModel.default([]),
|
||||||
personalInfo: z.custom<CustomerInfo>().optional(),
|
personalInfo: z.custom<CustomerInfo>().optional(),
|
||||||
paymentInfo: z.custom<PaymentDetailsPayload>().optional(),
|
paymentInfo: z.custom<PaymentInfoPayload>().optional(),
|
||||||
refOids: z.array(z.number()).optional(),
|
refOids: z.array(z.number()).optional(),
|
||||||
|
|
||||||
otpCode: z.coerce.string().optional(),
|
otpCode: z.coerce.string().optional(),
|
||||||
@@ -134,7 +134,7 @@ export type PrePaymentFlowStepPayload = z.infer<
|
|||||||
|
|
||||||
export const paymentFlowStepPayloadModel = z.object({
|
export const paymentFlowStepPayloadModel = z.object({
|
||||||
personalInfo: customerInfoModel.optional(),
|
personalInfo: customerInfoModel.optional(),
|
||||||
paymentInfo: paymentDetailsPayloadModel.optional(),
|
paymentInfo: paymentInfoPayloadModel.optional(),
|
||||||
});
|
});
|
||||||
export type PaymentFlowStepPayload = z.infer<
|
export type PaymentFlowStepPayload = z.infer<
|
||||||
typeof paymentFlowStepPayloadModel
|
typeof paymentFlowStepPayloadModel
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { z } from "zod";
|
|||||||
import { paginationModel } from "../../../core/pagination.utils";
|
import { paginationModel } from "../../../core/pagination.utils";
|
||||||
import { encodeCursor } from "../../../core/string.utils";
|
import { encodeCursor } from "../../../core/string.utils";
|
||||||
import { customerInfoModel } from "../../customerinfo/data";
|
import { customerInfoModel } from "../../customerinfo/data";
|
||||||
import { paymentDetailsPayloadModel } from "../../paymentinfo/data/entities";
|
import { paymentInfoPayloadModel } from "../../paymentinfo/data/entities";
|
||||||
import { productModel } from "../../product/data";
|
import { productModel } from "../../product/data";
|
||||||
|
|
||||||
export enum OrderCreationStep {
|
export enum OrderCreationStep {
|
||||||
@@ -35,7 +35,7 @@ export const orderModel = z.object({
|
|||||||
productId: z.number(),
|
productId: z.number(),
|
||||||
customerInfoId: z.number().nullish().optional(),
|
customerInfoId: z.number().nullish().optional(),
|
||||||
emailAccountId: z.number().nullish().optional(),
|
emailAccountId: z.number().nullish().optional(),
|
||||||
paymentDetailsId: z.number().nullish().optional(),
|
paymentInfoId: z.number().nullish().optional(),
|
||||||
|
|
||||||
createdAt: z.coerce.string(),
|
createdAt: z.coerce.string(),
|
||||||
updatedAt: z.coerce.string(),
|
updatedAt: z.coerce.string(),
|
||||||
@@ -108,7 +108,7 @@ export const newOrderModel = orderModel.pick({
|
|||||||
fullfilledPrice: true,
|
fullfilledPrice: true,
|
||||||
productId: true,
|
productId: true,
|
||||||
customerInfoId: true,
|
customerInfoId: true,
|
||||||
paymentDetailsId: true,
|
paymentInfoId: true,
|
||||||
emailAccountId: true,
|
emailAccountId: true,
|
||||||
});
|
});
|
||||||
export type NewOrderModel = z.infer<typeof newOrderModel>;
|
export type NewOrderModel = z.infer<typeof newOrderModel>;
|
||||||
@@ -117,7 +117,7 @@ export const createOrderPayloadModel = z.object({
|
|||||||
product: productModel.optional(),
|
product: productModel.optional(),
|
||||||
productId: z.number().optional(),
|
productId: z.number().optional(),
|
||||||
customerInfo: customerInfoModel,
|
customerInfo: customerInfoModel,
|
||||||
paymentDetails: paymentDetailsPayloadModel.optional(),
|
paymentInfo: paymentInfoPayloadModel.optional(),
|
||||||
orderModel: newOrderModel,
|
orderModel: newOrderModel,
|
||||||
});
|
});
|
||||||
export type CreateOrderModel = z.infer<typeof createOrderPayloadModel>;
|
export type CreateOrderModel = z.infer<typeof createOrderPayloadModel>;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { paymentDetailsModel } from "../../paymentinfo/data/entities";
|
import { paymentInfoModel } from "../../paymentinfo/data/entities";
|
||||||
|
|
||||||
export enum Gender {
|
export enum Gender {
|
||||||
Male = "male",
|
Male = "male",
|
||||||
@@ -45,9 +45,9 @@ export const passengerInfoModel = z.object({
|
|||||||
id: z.number(),
|
id: z.number(),
|
||||||
passengerType: z.enum([PassengerType.Adult, PassengerType.Child]),
|
passengerType: z.enum([PassengerType.Adult, PassengerType.Child]),
|
||||||
passengerPii: customerInfoModel,
|
passengerPii: customerInfoModel,
|
||||||
paymentDetails: paymentDetailsModel.optional(),
|
paymentInfo: paymentInfoModel.optional(),
|
||||||
passengerPiiId: z.number().optional(),
|
passengerPiiId: z.number().optional(),
|
||||||
paymentDetailsId: z.number().optional(),
|
paymentInfoId: z.number().optional(),
|
||||||
seatSelection: z.any(),
|
seatSelection: z.any(),
|
||||||
bagSelection: z.any(),
|
bagSelection: z.any(),
|
||||||
|
|
||||||
|
|||||||
@@ -74,15 +74,15 @@ export const cardInfoModel = z.object({
|
|||||||
});
|
});
|
||||||
export type CardInfo = z.infer<typeof cardInfoModel>;
|
export type CardInfo = z.infer<typeof cardInfoModel>;
|
||||||
|
|
||||||
export const paymentDetailsPayloadModel = z.object({
|
export const paymentInfoPayloadModel = z.object({
|
||||||
method: z.enum([PaymentMethod.Card]),
|
method: z.enum([PaymentMethod.Card]),
|
||||||
cardDetails: cardInfoModel,
|
cardDetails: cardInfoModel,
|
||||||
productId: z.number().int(),
|
productId: z.number().int(),
|
||||||
orderId: z.number().int(),
|
orderId: z.number().int(),
|
||||||
});
|
});
|
||||||
export type PaymentDetailsPayload = z.infer<typeof paymentDetailsPayloadModel>;
|
export type PaymentInfoPayload = z.infer<typeof paymentInfoPayloadModel>;
|
||||||
|
|
||||||
export const paymentDetailsModel = cardInfoModel.merge(
|
export const paymentInfoModel = cardInfoModel.merge(
|
||||||
z.object({
|
z.object({
|
||||||
id: z.number().int(),
|
id: z.number().int(),
|
||||||
productId: z.number().int(),
|
productId: z.number().int(),
|
||||||
@@ -91,4 +91,4 @@ export const paymentDetailsModel = cardInfoModel.merge(
|
|||||||
updatedAt: z.string().datetime(),
|
updatedAt: z.string().datetime(),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
export type PaymentDetails = z.infer<typeof paymentDetailsModel>;
|
export type PaymentInfo = z.infer<typeof paymentInfoModel>;
|
||||||
|
|||||||
Reference in New Issue
Block a user