🔄 cleanup: order with 3 key relations, and ckflow now upto speeded
This commit is contained in:
@@ -1,3 +1,6 @@
|
|||||||
|
import { and, desc, eq, type Database } from "@pkg/db";
|
||||||
|
import { checkoutFlowSession, product } from "@pkg/db/schema";
|
||||||
|
import { getError, Logger } from "@pkg/logger";
|
||||||
import { ERROR_CODES, type Result } from "@pkg/result";
|
import { ERROR_CODES, type Result } from "@pkg/result";
|
||||||
import {
|
import {
|
||||||
CKActionType,
|
CKActionType,
|
||||||
@@ -5,14 +8,11 @@ import {
|
|||||||
type FlowInfo,
|
type FlowInfo,
|
||||||
type PendingAction,
|
type PendingAction,
|
||||||
} from "./data";
|
} from "./data";
|
||||||
import { getError, Logger } from "@pkg/logger";
|
|
||||||
import { and, desc, eq, type Database } from "@pkg/db";
|
|
||||||
import { checkoutFlowSession, flightTicketInfo } from "@pkg/db/schema";
|
|
||||||
|
|
||||||
export class CheckoutFlowRepository {
|
export class CheckoutFlowRepository {
|
||||||
constructor(private db: Database) {}
|
constructor(private db: Database) {}
|
||||||
|
|
||||||
// Common method to parse and enrich flow info with ticket details
|
// Common method to parse and enrich flow info with product details
|
||||||
private async parseFlowInfo(
|
private async parseFlowInfo(
|
||||||
sessionData: any,
|
sessionData: any,
|
||||||
includeStaleness = true,
|
includeStaleness = true,
|
||||||
@@ -43,48 +43,30 @@ export class CheckoutFlowRepository {
|
|||||||
: false;
|
: false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch ticket info if we have a ticket ID
|
// Fetch product info if we have a product ID
|
||||||
let ticketInfo = undefined;
|
let productInfo = undefined;
|
||||||
if (sessionData.ticketId) {
|
if (sessionData.productId) {
|
||||||
try {
|
try {
|
||||||
const ticketResult =
|
const prodResult = await this.db.query.product.findFirst({
|
||||||
await this.db.query.flightTicketInfo.findFirst({
|
where: eq(product.id, sessionData.productId),
|
||||||
where: eq(flightTicketInfo.id, sessionData.ticketId),
|
|
||||||
});
|
});
|
||||||
|
if (prodResult) {
|
||||||
if (ticketResult) {
|
productInfo = { ...prodResult };
|
||||||
ticketInfo = {
|
|
||||||
id: ticketResult.id,
|
|
||||||
ticketId: ticketResult.ticketId,
|
|
||||||
departure: ticketResult.departure,
|
|
||||||
arrival: ticketResult.arrival,
|
|
||||||
departureDate: ticketResult.departureDate
|
|
||||||
.toISOString()
|
|
||||||
.split("T")[0],
|
|
||||||
returnDate: ticketResult.returnDate
|
|
||||||
? ticketResult.returnDate
|
|
||||||
.toISOString()
|
|
||||||
.split("T")[0]
|
|
||||||
: undefined,
|
|
||||||
flightType: ticketResult.flightType,
|
|
||||||
cabinClass: ticketResult.cabinClass,
|
|
||||||
priceDetails: ticketResult.priceDetails as any,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Logger.warn(
|
Logger.warn(
|
||||||
"Failed to fetch ticket info for session detail",
|
"Failed to fetch product info for session detail",
|
||||||
err,
|
err,
|
||||||
);
|
);
|
||||||
// Continue without ticket info - it's optional
|
// Continue without product info - it's optional
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare dates for the model
|
// Prepare dates for the model
|
||||||
const flowInfoData = {
|
const flowInfoData = {
|
||||||
...sessionData,
|
...sessionData,
|
||||||
ticketInfo,
|
productInfo: productInfo,
|
||||||
ticketId: sessionData.ticketId,
|
productId: sessionData.productId,
|
||||||
pendingActions: sessionData.pendingActions as any,
|
pendingActions: sessionData.pendingActions as any,
|
||||||
personalInfo: sessionData.personalInfo as any,
|
personalInfo: sessionData.personalInfo as any,
|
||||||
paymentInfo: sessionData.paymentInfo as any,
|
paymentInfo: sessionData.paymentInfo as any,
|
||||||
|
|||||||
@@ -1,18 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { formatDistanceToNow } from "date-fns";
|
|
||||||
import { Badge } from "$lib/components/ui/badge";
|
|
||||||
import { ScrollArea } from "$lib/components/ui/scroll-area";
|
|
||||||
import type { FlowInfo } from "@pkg/logic/domains/ckflow/data/entities";
|
|
||||||
import Icon from "$lib/components/atoms/icon.svelte";
|
import Icon from "$lib/components/atoms/icon.svelte";
|
||||||
import { CheckoutStep } from "$lib/domains/ticket/data/entities";
|
import { Badge } from "$lib/components/ui/badge";
|
||||||
import LoaderIcon from "~icons/bx/loader-alt";
|
import { Button } from "$lib/components/ui/button";
|
||||||
import UserIcon from "~icons/material-symbols/account-circle-full";
|
import * as ContextMenu from "$lib/components/ui/context-menu/index";
|
||||||
import CreditCardIcon from "~icons/solar/card-2-linear";
|
|
||||||
import QuestionMarkIcon from "~icons/solar/question-circle-linear";
|
|
||||||
import CheckCircleIcon from "~icons/solar/check-circle-linear";
|
|
||||||
import StopCircleIcon from "~icons/solar/stop-circle-linear";
|
|
||||||
import ShieldAlertIcon from "~icons/solar/shield-warning-linear";
|
|
||||||
import TrashIcon from "~icons/solar/trash-bin-trash-linear";
|
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@@ -21,11 +11,21 @@
|
|||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "$lib/components/ui/dialog";
|
} from "$lib/components/ui/dialog";
|
||||||
import { Button } from "$lib/components/ui/button";
|
import { ScrollArea } from "$lib/components/ui/scroll-area";
|
||||||
import { isSessionActive } from "../utils";
|
import { CheckoutStep } from "$lib/domains/order/data/entities";
|
||||||
import { trpcApiStore } from "$lib/stores/api";
|
import { trpcApiStore } from "$lib/stores/api";
|
||||||
|
import type { FlowInfo } from "@pkg/logic/domains/ckflow/data/entities";
|
||||||
|
import { formatDistanceToNow } from "date-fns";
|
||||||
import { toast } from "svelte-sonner";
|
import { toast } from "svelte-sonner";
|
||||||
import * as ContextMenu from "$lib/components/ui/context-menu/index";
|
import LoaderIcon from "~icons/bx/loader-alt";
|
||||||
|
import UserIcon from "~icons/material-symbols/account-circle-full";
|
||||||
|
import CreditCardIcon from "~icons/solar/card-2-linear";
|
||||||
|
import CheckCircleIcon from "~icons/solar/check-circle-linear";
|
||||||
|
import QuestionMarkIcon from "~icons/solar/question-circle-linear";
|
||||||
|
import ShieldAlertIcon from "~icons/solar/shield-warning-linear";
|
||||||
|
import StopCircleIcon from "~icons/solar/stop-circle-linear";
|
||||||
|
import TrashIcon from "~icons/solar/trash-bin-trash-linear";
|
||||||
|
import { isSessionActive } from "../utils";
|
||||||
|
|
||||||
let {
|
let {
|
||||||
sessions,
|
sessions,
|
||||||
|
|||||||
@@ -8,11 +8,13 @@
|
|||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "$lib/components/ui/dialog";
|
} from "$lib/components/ui/dialog";
|
||||||
import { PaymentErrorType } from "@pkg/logic/domains/ckflow/data/entities";
|
|
||||||
import { RadioGroup, RadioGroupItem } from "$lib/components/ui/radio-group";
|
|
||||||
import { Label } from "$lib/components/ui/label";
|
import { Label } from "$lib/components/ui/label";
|
||||||
|
import { RadioGroup, RadioGroupItem } from "$lib/components/ui/radio-group";
|
||||||
import { Textarea } from "$lib/components/ui/textarea";
|
import { Textarea } from "$lib/components/ui/textarea";
|
||||||
import { CKActionType } from "@pkg/logic/domains/ckflow/data/entities";
|
import {
|
||||||
|
CKActionType,
|
||||||
|
PaymentErrorType,
|
||||||
|
} from "@pkg/logic/domains/ckflow/data/entities";
|
||||||
|
|
||||||
let {
|
let {
|
||||||
open = $bindable<boolean>(false),
|
open = $bindable<boolean>(false),
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { createEventDispatcher } from "svelte";
|
import Icon from "$lib/components/atoms/icon.svelte";
|
||||||
|
import { Alert, AlertDescription } from "$lib/components/ui/alert";
|
||||||
|
import { Badge } from "$lib/components/ui/badge";
|
||||||
import { Button } from "$lib/components/ui/button";
|
import { Button } from "$lib/components/ui/button";
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
@@ -9,25 +11,21 @@
|
|||||||
CardHeader,
|
CardHeader,
|
||||||
CardTitle,
|
CardTitle,
|
||||||
} from "$lib/components/ui/card";
|
} from "$lib/components/ui/card";
|
||||||
import { Badge } from "$lib/components/ui/badge";
|
|
||||||
import { Alert, AlertDescription } from "$lib/components/ui/alert";
|
|
||||||
import { Separator } from "$lib/components/ui/separator";
|
import { Separator } from "$lib/components/ui/separator";
|
||||||
import { CheckoutStep } from "@pkg/logic/domains/ticket/data/entities";
|
import { trpcApiStore } from "$lib/stores/api";
|
||||||
import {
|
import {
|
||||||
CKActionType,
|
CKActionType,
|
||||||
type FlowInfo,
|
type FlowInfo,
|
||||||
} from "@pkg/logic/domains/ckflow/data/entities";
|
} from "@pkg/logic/domains/ckflow/data/entities";
|
||||||
|
import { CheckoutStep } from "@pkg/logic/domains/order/data/enums";
|
||||||
import { formatDistanceToNow } from "date-fns";
|
import { formatDistanceToNow } from "date-fns";
|
||||||
import { trpcApiStore } from "$lib/stores/api";
|
import { createEventDispatcher } from "svelte";
|
||||||
import { toast } from "svelte-sonner";
|
import { toast } from "svelte-sonner";
|
||||||
import Icon from "$lib/components/atoms/icon.svelte";
|
import XIcon from "~icons/material-symbols/close-rounded";
|
||||||
import ArrowLeftIcon from "~icons/solar/arrow-left-outline";
|
import ArrowLeftIcon from "~icons/solar/arrow-left-outline";
|
||||||
import CheckCircleIcon from "~icons/solar/check-circle-broken";
|
import CheckCircleIcon from "~icons/solar/check-circle-broken";
|
||||||
import XIcon from "~icons/material-symbols/close-rounded";
|
import TrashIcon from "~icons/solar/trash-bin-trash-linear";
|
||||||
import TrashIcon from "~icons/solar/trash-bin-trash-linear"; // Add trash icon
|
// Add trash icon
|
||||||
import ShieldIcon from "~icons/solar/shield-keyhole-minimalistic-broken";
|
|
||||||
import { ckflowVM } from "../ckflow.vm.svelte";
|
|
||||||
import { isSessionActive } from "../utils";
|
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@@ -40,7 +38,9 @@
|
|||||||
capitalize,
|
capitalize,
|
||||||
snakeToSpacedPascal,
|
snakeToSpacedPascal,
|
||||||
} from "@pkg/logic/core/string.utils";
|
} from "@pkg/logic/core/string.utils";
|
||||||
import { formatCurrency } from "@pkg/logic/core/currency.utils";
|
import ShieldIcon from "~icons/solar/shield-keyhole-minimalistic-broken";
|
||||||
|
import { ckflowVM } from "../ckflow.vm.svelte";
|
||||||
|
import { isSessionActive } from "../utils";
|
||||||
import BackToPaymentDialog from "./back-to-payment-dialog.svelte";
|
import BackToPaymentDialog from "./back-to-payment-dialog.svelte";
|
||||||
|
|
||||||
let { session = $bindable() }: { session: FlowInfo } = $props();
|
let { session = $bindable() }: { session: FlowInfo } = $props();
|
||||||
@@ -251,61 +251,11 @@
|
|||||||
|
|
||||||
<Separator />
|
<Separator />
|
||||||
|
|
||||||
{#if session.ticketInfo}
|
{#if session.productInfo}
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<h3 class="text-lg font-medium">Booking Details</h3>
|
<h3 class="text-lg font-medium">Product Details</h3>
|
||||||
<div class="rounded-md border p-3">
|
<div class="rounded-md border p-3">
|
||||||
<div class="mb-2 flex items-center justify-between">
|
<span>TODO: show product details here</span>
|
||||||
<div class="font-medium">
|
|
||||||
{session.ticketInfo.departure} → {session
|
|
||||||
.ticketInfo.arrival}
|
|
||||||
</div>
|
|
||||||
<Badge variant="outline">
|
|
||||||
{session.ticketInfo.flightType === "ONEWAY"
|
|
||||||
? "One Way"
|
|
||||||
: "Round Trip"}
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="grid grid-cols-2 gap-2 text-sm">
|
|
||||||
<span class="text-muted-foreground">Departure</span>
|
|
||||||
<span class="font-medium">
|
|
||||||
{session.ticketInfo.departureDate}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
{#if session.ticketInfo.flightType !== "ONEWAY" && session.ticketInfo.returnDate}
|
|
||||||
<span class="text-muted-foreground">Return</span>
|
|
||||||
<span class="font-medium">
|
|
||||||
{session.ticketInfo.returnDate}
|
|
||||||
</span>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<span class="text-muted-foreground">Cabin Class</span>
|
|
||||||
<span class="font-medium">
|
|
||||||
{session.ticketInfo.cabinClass}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mt-4 flex justify-between border-t pt-3">
|
|
||||||
<span class="font-medium">Original Price</span>
|
|
||||||
<span class="text-lg font-bold text-primary">
|
|
||||||
{formatCurrency(
|
|
||||||
session.ticketInfo.priceDetails.basePrice ??
|
|
||||||
0,
|
|
||||||
session.ticketInfo.priceDetails.currency,
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mt-4 flex justify-between">
|
|
||||||
<span class="font-medium">Total Price</span>
|
|
||||||
<span class="text-lg font-bold text-primary">
|
|
||||||
{formatCurrency(
|
|
||||||
session.ticketInfo.priceDetails.displayPrice,
|
|
||||||
session.ticketInfo.priceDetails.currency,
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,14 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import Icon from "$lib/components/atoms/icon.svelte";
|
||||||
import { Badge } from "$lib/components/ui/badge";
|
import { Badge } from "$lib/components/ui/badge";
|
||||||
import { Button } from "$lib/components/ui/button";
|
import { Button } from "$lib/components/ui/button";
|
||||||
import type { FlowInfo } from "@pkg/logic/domains/ckflow/data/entities";
|
import { Separator } from "$lib/components/ui/separator";
|
||||||
import {
|
import {
|
||||||
capitalize,
|
capitalize,
|
||||||
snakeToSpacedPascal,
|
snakeToSpacedPascal,
|
||||||
} from "@pkg/logic/core/string.utils";
|
} from "@pkg/logic/core/string.utils";
|
||||||
import { Separator } from "$lib/components/ui/separator";
|
import type { FlowInfo } from "@pkg/logic/domains/ckflow/data/entities";
|
||||||
import Icon from "$lib/components/atoms/icon.svelte";
|
|
||||||
|
|
||||||
// Icons
|
// Icons
|
||||||
import TrashIcon from "~icons/solar/trash-bin-trash-linear";
|
|
||||||
import RestoreIcon from "~icons/solar/restart-bold";
|
|
||||||
import CloseIcon from "~icons/solar/close-circle-linear";
|
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@@ -22,7 +18,9 @@
|
|||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "$lib/components/ui/dialog";
|
} from "$lib/components/ui/dialog";
|
||||||
import { formatTime } from "@pkg/logic/core/date.utils";
|
import { formatTime } from "@pkg/logic/core/date.utils";
|
||||||
import { formatCurrency } from "@pkg/logic/core/currency.utils";
|
import CloseIcon from "~icons/solar/close-circle-linear";
|
||||||
|
import RestoreIcon from "~icons/solar/restart-bold";
|
||||||
|
import TrashIcon from "~icons/solar/trash-bin-trash-linear";
|
||||||
|
|
||||||
let {
|
let {
|
||||||
session,
|
session,
|
||||||
@@ -116,76 +114,13 @@
|
|||||||
|
|
||||||
<Separator />
|
<Separator />
|
||||||
|
|
||||||
{#if session.ticketInfo}
|
{#if session.productInfo}
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<h3 class="text-lg font-medium">Booking Details</h3>
|
<h3 class="text-lg font-medium">Product Details</h3>
|
||||||
<div class="rounded-md border p-3">
|
<div class="rounded-md border p-3">
|
||||||
<div
|
<span>
|
||||||
class="mb-2 flex items-center justify-between"
|
TODO: SHOW THE PRODUCT INFO AT THIS PLACE
|
||||||
>
|
|
||||||
<div class="font-medium">
|
|
||||||
{session.ticketInfo.departure} → {session
|
|
||||||
.ticketInfo.arrival}
|
|
||||||
</div>
|
|
||||||
<Badge variant="outline">
|
|
||||||
{session.ticketInfo.flightType ===
|
|
||||||
"ONEWAY"
|
|
||||||
? "One Way"
|
|
||||||
: "Round Trip"}
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="grid grid-cols-2 gap-2 text-sm">
|
|
||||||
<span class="text-muted-foreground"
|
|
||||||
>Departure</span
|
|
||||||
>
|
|
||||||
<span class="font-medium">
|
|
||||||
{session.ticketInfo.departureDate}
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
{#if session.ticketInfo.flightType !== "ONEWAY" && session.ticketInfo.returnDate}
|
|
||||||
<span class="text-muted-foreground"
|
|
||||||
>Return</span
|
|
||||||
>
|
|
||||||
<span class="font-medium">
|
|
||||||
{session.ticketInfo.returnDate}
|
|
||||||
</span>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<span class="text-muted-foreground"
|
|
||||||
>Cabin Class</span
|
|
||||||
>
|
|
||||||
<span class="font-medium">
|
|
||||||
{session.ticketInfo.cabinClass}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="mt-4 flex justify-between border-t pt-3"
|
|
||||||
>
|
|
||||||
<span class="font-medium">Original Price</span
|
|
||||||
>
|
|
||||||
<span class="text-lg font-bold text-primary">
|
|
||||||
{formatCurrency(
|
|
||||||
session.ticketInfo.priceDetails
|
|
||||||
.basePrice ?? 0,
|
|
||||||
session.ticketInfo.priceDetails
|
|
||||||
.currency,
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mt-4 flex justify-between">
|
|
||||||
<span class="font-medium">Total Price</span>
|
|
||||||
<span class="text-lg font-bold text-primary">
|
|
||||||
{formatCurrency(
|
|
||||||
session.ticketInfo.priceDetails
|
|
||||||
.displayPrice,
|
|
||||||
session.ticketInfo.priceDetails
|
|
||||||
.currency,
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { formatDistanceToNow } from "date-fns";
|
|
||||||
import { Badge } from "$lib/components/ui/badge";
|
|
||||||
import { ScrollArea } from "$lib/components/ui/scroll-area";
|
|
||||||
import Icon from "$lib/components/atoms/icon.svelte";
|
import Icon from "$lib/components/atoms/icon.svelte";
|
||||||
import { CheckoutStep } from "$lib/domains/ticket/data/entities";
|
import { Badge } from "$lib/components/ui/badge";
|
||||||
import type { FlowInfo } from "@pkg/logic/domains/ckflow/data/entities";
|
|
||||||
import * as ContextMenu from "$lib/components/ui/context-menu/index";
|
import * as ContextMenu from "$lib/components/ui/context-menu/index";
|
||||||
import { isSessionActive } from "../utils";
|
import { ScrollArea } from "$lib/components/ui/scroll-area";
|
||||||
|
import { CheckoutStep } from "$lib/domains/order/data/entities";
|
||||||
|
import type { FlowInfo } from "@pkg/logic/domains/ckflow/data/entities";
|
||||||
|
import { formatDistanceToNow } from "date-fns";
|
||||||
import LoaderIcon from "~icons/bx/loader-alt";
|
import LoaderIcon from "~icons/bx/loader-alt";
|
||||||
import UserIcon from "~icons/material-symbols/account-circle-full";
|
import UserIcon from "~icons/material-symbols/account-circle-full";
|
||||||
import CreditCardIcon from "~icons/solar/card-2-linear";
|
import CreditCardIcon from "~icons/solar/card-2-linear";
|
||||||
import QuestionMarkIcon from "~icons/solar/question-circle-linear";
|
|
||||||
import CheckCircleIcon from "~icons/solar/check-circle-linear";
|
import CheckCircleIcon from "~icons/solar/check-circle-linear";
|
||||||
import StopCircleIcon from "~icons/solar/stop-circle-linear";
|
import QuestionMarkIcon from "~icons/solar/question-circle-linear";
|
||||||
import ShieldAlertIcon from "~icons/solar/shield-warning-linear";
|
|
||||||
import TrashIcon from "~icons/solar/trash-bin-trash-linear";
|
|
||||||
import RestoreIcon from "~icons/solar/restart-bold";
|
import RestoreIcon from "~icons/solar/restart-bold";
|
||||||
|
import ShieldAlertIcon from "~icons/solar/shield-warning-linear";
|
||||||
|
import StopCircleIcon from "~icons/solar/stop-circle-linear";
|
||||||
|
import TrashIcon from "~icons/solar/trash-bin-trash-linear";
|
||||||
|
import { isSessionActive } from "../utils";
|
||||||
|
|
||||||
let {
|
let {
|
||||||
sessions,
|
sessions,
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { capitalize } from "@pkg/logic/core/string.utils";
|
|
||||||
import Icon from "$lib/components/atoms/icon.svelte";
|
import Icon from "$lib/components/atoms/icon.svelte";
|
||||||
import {
|
import {
|
||||||
formatDateTime,
|
formatDateTime,
|
||||||
formatDuration,
|
formatDuration,
|
||||||
formatTime,
|
formatTime,
|
||||||
} from "@pkg/logic/core/date.utils";
|
} from "@pkg/logic/core/date.utils";
|
||||||
import XIcon from "~icons/solar/close-circle-linear";
|
import { capitalize } from "@pkg/logic/core/string.utils";
|
||||||
import CheckIcon from "~icons/solar/check-read-linear";
|
|
||||||
import FlagIcon from "~icons/material-symbols/flag";
|
import FlagIcon from "~icons/material-symbols/flag";
|
||||||
import UserIcon from "~icons/solar/user-circle-linear";
|
|
||||||
import ClockIcon from "~icons/solar/clock-circle-linear";
|
|
||||||
import CardIcon from "~icons/solar/card-linear";
|
import CardIcon from "~icons/solar/card-linear";
|
||||||
|
import CheckIcon from "~icons/solar/check-read-linear";
|
||||||
|
import ClockIcon from "~icons/solar/clock-circle-linear";
|
||||||
|
import XIcon from "~icons/solar/close-circle-linear";
|
||||||
import KeyIcon from "~icons/solar/key-linear";
|
import KeyIcon from "~icons/solar/key-linear";
|
||||||
|
import UserIcon from "~icons/solar/user-circle-linear";
|
||||||
import type { FlowInfo } from "../data";
|
import type { FlowInfo } from "../data";
|
||||||
|
|
||||||
let { session }: { session: FlowInfo } = $props();
|
let { session }: { session: FlowInfo } = $props();
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
export * from "@pkg/logic/domains/order/data/entities";
|
export * from "@pkg/logic/domains/order/data/entities";
|
||||||
|
export * from "@pkg/logic/domains/order/data/enums";
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export class OrderRepository {
|
|||||||
async listAllOrders(): Promise<Result<FullOrderModel[]>> {
|
async listAllOrders(): Promise<Result<FullOrderModel[]>> {
|
||||||
try {
|
try {
|
||||||
const res = await this.db.query.order.findMany({
|
const res = await this.db.query.order.findMany({
|
||||||
with: { customerInfo: true, product: true },
|
with: { customerInfo: true, product: true, paymentInfo: true },
|
||||||
});
|
});
|
||||||
const out = [] as FullOrderModel[];
|
const out = [] as FullOrderModel[];
|
||||||
for (const each of res) {
|
for (const each of res) {
|
||||||
@@ -56,7 +56,7 @@ export class OrderRepository {
|
|||||||
async getOrder(oid: number): Promise<Result<FullOrderModel>> {
|
async getOrder(oid: number): Promise<Result<FullOrderModel>> {
|
||||||
const out = await this.db.query.order.findFirst({
|
const out = await this.db.query.order.findFirst({
|
||||||
where: eq(order.id, oid),
|
where: eq(order.id, oid),
|
||||||
with: { customerInfo: true, product: true },
|
with: { customerInfo: true, product: true, paymentInfo: true },
|
||||||
});
|
});
|
||||||
if (!out) return {};
|
if (!out) return {};
|
||||||
const parsed = fullOrderModel.safeParse({
|
const parsed = fullOrderModel.safeParse({
|
||||||
|
|||||||
@@ -1,9 +1,4 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Button from "$lib/components/ui/button/button.svelte";
|
|
||||||
import LabelWrapper from "$lib/components/atoms/label-wrapper.svelte";
|
|
||||||
import Input from "$lib/components/ui/input/input.svelte";
|
|
||||||
import Checkbox from "$lib/components/ui/checkbox/checkbox.svelte";
|
|
||||||
|
|
||||||
let { order }: { order?: any } = $props();
|
let { order }: { order?: any } = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Title from "$lib/components/atoms/title.svelte";
|
|
||||||
import Icon from "$lib/components/atoms/icon.svelte";
|
import Icon from "$lib/components/atoms/icon.svelte";
|
||||||
import EmailIcon from "~icons/solar/letter-broken";
|
import Title from "$lib/components/atoms/title.svelte";
|
||||||
import TicketIcon from "~icons/solar/ticket-broken";
|
|
||||||
import CreditCardIcon from "~icons/solar/card-broken";
|
|
||||||
import { Badge } from "$lib/components/ui/badge";
|
import { Badge } from "$lib/components/ui/badge";
|
||||||
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 TicketLegsOverview from "$lib/domains/ticket/view/ticket/ticket-legs-overview.svelte";
|
||||||
|
import CreditCardIcon from "~icons/solar/card-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();
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
import Title from "$lib/components/atoms/title.svelte";
|
import Title from "$lib/components/atoms/title.svelte";
|
||||||
import { adminSiteNavMap } from "$lib/core/constants";
|
import { adminSiteNavMap } from "$lib/core/constants";
|
||||||
import { capitalize } from "$lib/core/string.utils";
|
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 PinfoCard from "$lib/domains/passengerinfo/view/pinfo-card.svelte";
|
|
||||||
import SuitcaseIcon from "~icons/bi/suitcase2";
|
import SuitcaseIcon from "~icons/bi/suitcase2";
|
||||||
import BagIcon from "~icons/lucide/briefcase";
|
import BagIcon from "~icons/lucide/briefcase";
|
||||||
import BackpackIcon from "~icons/solar/backpack-linear";
|
import BackpackIcon from "~icons/solar/backpack-linear";
|
||||||
@@ -105,14 +105,14 @@
|
|||||||
|
|
||||||
{#if order.flightTicketInfo.refOIds}
|
{#if order.flightTicketInfo.refOIds}
|
||||||
{#each order.flightTicketInfo.refOIds as refOId}
|
{#each order.flightTicketInfo.refOIds as refOId}
|
||||||
<PinfoCard icon={PackageIcon} title="Order">
|
<CinfoCard icon={PackageIcon} title="Order">
|
||||||
<a
|
<a
|
||||||
href={`${adminSiteNavMap.orders}/${refOId}`}
|
href={`${adminSiteNavMap.orders}/${refOId}`}
|
||||||
class="mt-1 inline-block font-medium text-primary hover:underline"
|
class="mt-1 inline-block font-medium text-primary hover:underline"
|
||||||
>
|
>
|
||||||
Reference Order #{refOId}
|
Reference Order #{refOId}
|
||||||
</a>
|
</a>
|
||||||
</PinfoCard>
|
</CinfoCard>
|
||||||
{/each}
|
{/each}
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import { eq, type Database } from "@pkg/db";
|
import { eq, type Database } from "@pkg/db";
|
||||||
import { paymentDetailsModel, type PaymentDetails } from "./data";
|
import { paymentInfo } from "@pkg/db/schema";
|
||||||
import type { Result } from "@pkg/result";
|
|
||||||
import { paymentDetails } from "@pkg/db/schema";
|
|
||||||
import { Logger } from "@pkg/logger";
|
import { Logger } from "@pkg/logger";
|
||||||
|
import type { Result } from "@pkg/result";
|
||||||
|
import { paymentDetailsModel, type PaymentDetails } 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<PaymentDetails>> {
|
||||||
Logger.info(`Getting payment info with id ${id}`);
|
Logger.info(`Getting payment info with id ${id}`);
|
||||||
const out = await this.db.query.paymentDetails.findFirst({
|
const out = await this.db.query.paymentInfo.findFirst({
|
||||||
where: eq(paymentDetails.id, id),
|
where: eq(paymentInfo.id, id),
|
||||||
});
|
});
|
||||||
const parsed = paymentDetailsModel.safeParse(out);
|
const parsed = paymentDetailsModel.safeParse(out);
|
||||||
if (parsed.error) {
|
if (parsed.error) {
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
);
|
);
|
||||||
|
|
||||||
let pii = data.data?.passengerPii;
|
let pii = data.data?.passengerPii;
|
||||||
let paymentDetails = data.data?.paymentDetails;
|
let paymentInfo = data.data?.paymentInfo;
|
||||||
|
|
||||||
const piiData = [
|
const piiData = [
|
||||||
{
|
{
|
||||||
@@ -110,30 +110,30 @@
|
|||||||
];
|
];
|
||||||
|
|
||||||
// Card information
|
// Card information
|
||||||
const cardInfo = paymentDetails
|
const cardInfo = paymentInfo
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
icon: CardUserIcon,
|
icon: CardUserIcon,
|
||||||
title: "Cardholder Name",
|
title: "Cardholder Name",
|
||||||
value: paymentDetails.cardholderName ?? "",
|
value: paymentInfo.cardholderName ?? "",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: CardNumberIcon,
|
icon: CardNumberIcon,
|
||||||
title: "Card Number",
|
title: "Card Number",
|
||||||
value: paymentDetails.cardNumber
|
value: paymentInfo.cardNumber
|
||||||
? // add spaces of 4 between each group of 4 digits
|
? // add spaces of 4 between each group of 4 digits
|
||||||
paymentDetails.cardNumber.match(/.{1,4}/g)?.join(" ")
|
paymentInfo.cardNumber.match(/.{1,4}/g)?.join(" ")
|
||||||
: "",
|
: "",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: CalendarCheckIcon,
|
icon: CalendarCheckIcon,
|
||||||
title: "Expiry Date",
|
title: "Expiry Date",
|
||||||
value: paymentDetails.expiry ?? "",
|
value: paymentInfo.expiry ?? "",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: LockKeyIcon,
|
icon: LockKeyIcon,
|
||||||
title: "CVV",
|
title: "CVV",
|
||||||
value: paymentDetails.cvv ?? "",
|
value: paymentInfo.cvv ?? "",
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
: [];
|
: [];
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { and, eq, type Database, isNotNull, or } from "@pkg/db";
|
|
||||||
import { ERROR_CODES, type Result } from "$lib/core/data.types";
|
import { ERROR_CODES, type Result } from "$lib/core/data.types";
|
||||||
|
import { and, eq, isNotNull, or, type Database } from "@pkg/db";
|
||||||
|
import { order, passengerInfo } from "@pkg/db/schema";
|
||||||
|
import { getError, Logger } from "@pkg/logger";
|
||||||
|
import { nanoid } from "nanoid";
|
||||||
import {
|
import {
|
||||||
fullOrderModel,
|
fullOrderModel,
|
||||||
limitedOrderWithTicketInfoModel,
|
limitedOrderWithTicketInfoModel,
|
||||||
@@ -8,9 +11,6 @@ import {
|
|||||||
type LimitedOrderWithTicketInfoModel,
|
type LimitedOrderWithTicketInfoModel,
|
||||||
type NewOrderModel,
|
type NewOrderModel,
|
||||||
} from "./entities";
|
} from "./entities";
|
||||||
import { getError, Logger } from "@pkg/logger";
|
|
||||||
import { order, passengerInfo } from "@pkg/db/schema";
|
|
||||||
import { nanoid } from "nanoid";
|
|
||||||
|
|
||||||
export class OrderRepository {
|
export class OrderRepository {
|
||||||
private db: Database;
|
private db: Database;
|
||||||
@@ -122,7 +122,7 @@ export class OrderRepository {
|
|||||||
discountAmount: payload.discountAmount.toFixed(3),
|
discountAmount: payload.discountAmount.toFixed(3),
|
||||||
|
|
||||||
flightTicketInfoId: payload.flightTicketInfoId,
|
flightTicketInfoId: payload.flightTicketInfoId,
|
||||||
paymentDetailsId: payload.paymentDetailsId,
|
paymentInfoId: payload.paymentInfoId,
|
||||||
|
|
||||||
status: OrderStatus.PENDING_FULLFILLMENT,
|
status: OrderStatus.PENDING_FULLFILLMENT,
|
||||||
pnr,
|
pnr,
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
|
import { SessionOutcome } from "$lib/domains/ckflow/data/entities";
|
||||||
|
import { getCKUseCases } from "$lib/domains/ckflow/domain/usecases";
|
||||||
|
import { EmailerUseCases } from "$lib/domains/email/domain/usecases";
|
||||||
import { createOrderPayloadModel } from "$lib/domains/order/data/entities";
|
import { createOrderPayloadModel } from "$lib/domains/order/data/entities";
|
||||||
|
import { PassengerInfoRepository } from "$lib/domains/passengerinfo/data/repository";
|
||||||
|
import { PassengerInfoController } from "$lib/domains/passengerinfo/domain/controller";
|
||||||
|
import { PaymentInfoRepository } from "$lib/domains/paymentinfo/data/repository";
|
||||||
|
import { PaymentInfoUseCases } from "$lib/domains/paymentinfo/domain/usecases";
|
||||||
|
import { CheckoutStep } from "$lib/domains/ticket/data/entities";
|
||||||
|
import { getTC } from "$lib/domains/ticket/domain/controller";
|
||||||
import { createTRPCRouter, publicProcedure } from "$lib/trpc/t";
|
import { createTRPCRouter, publicProcedure } from "$lib/trpc/t";
|
||||||
import { db } from "@pkg/db";
|
import { db } from "@pkg/db";
|
||||||
import { OrderRepository } from "../data/repository";
|
|
||||||
import { OrderController } from "./controller";
|
|
||||||
import { getTC } from "$lib/domains/ticket/domain/controller";
|
|
||||||
import { getError, Logger } from "@pkg/logger";
|
import { getError, Logger } from "@pkg/logger";
|
||||||
import { PassengerInfoController } from "$lib/domains/passengerinfo/domain/controller";
|
|
||||||
import { PassengerInfoRepository } from "$lib/domains/passengerinfo/data/repository";
|
|
||||||
import { PaymentInfoUseCases } from "$lib/domains/paymentinfo/domain/usecases";
|
|
||||||
import { PaymentInfoRepository } from "$lib/domains/paymentinfo/data/repository";
|
|
||||||
import { ERROR_CODES } from "@pkg/result";
|
import { ERROR_CODES } from "@pkg/result";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { getCKUseCases } from "$lib/domains/ckflow/domain/usecases";
|
import { OrderRepository } from "../data/repository";
|
||||||
import { SessionOutcome } from "$lib/domains/ckflow/data/entities";
|
import { OrderController } from "./controller";
|
||||||
import { CheckoutStep } from "$lib/domains/ticket/data/entities";
|
|
||||||
import { EmailerUseCases } from "$lib/domains/email/domain/usecases";
|
|
||||||
|
|
||||||
export const orderRouter = createTRPCRouter({
|
export const orderRouter = createTRPCRouter({
|
||||||
createOrder: publicProcedure
|
createOrder: publicProcedure
|
||||||
@@ -33,7 +33,7 @@ export const orderRouter = createTRPCRouter({
|
|||||||
return { error: ftRes.error };
|
return { error: ftRes.error };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!input.flightTicketId || !input.paymentDetails) {
|
if (!input.flightTicketId || !input.paymentInfo) {
|
||||||
return {
|
return {
|
||||||
error: getError({
|
error: getError({
|
||||||
code: ERROR_CODES.INPUT_ERROR,
|
code: ERROR_CODES.INPUT_ERROR,
|
||||||
@@ -43,7 +43,7 @@ export const orderRouter = createTRPCRouter({
|
|||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const pdRes = await pduc.createPaymentInfo(input.paymentDetails!);
|
const pdRes = await pduc.createPaymentInfo(input.paymentInfo!);
|
||||||
if (pdRes.error || !pdRes.data) {
|
if (pdRes.error || !pdRes.data) {
|
||||||
return { error: pdRes.error };
|
return { error: pdRes.error };
|
||||||
}
|
}
|
||||||
@@ -52,7 +52,7 @@ export const orderRouter = createTRPCRouter({
|
|||||||
input.orderModel.flightTicketInfoId = ftRes.data;
|
input.orderModel.flightTicketInfoId = ftRes.data;
|
||||||
|
|
||||||
Logger.info(`Setting payment details id ${pdRes.data}`);
|
Logger.info(`Setting payment details id ${pdRes.data}`);
|
||||||
input.orderModel.paymentDetailsId = pdRes.data;
|
input.orderModel.paymentInfoId = pdRes.data;
|
||||||
|
|
||||||
Logger.info("Creating order");
|
Logger.info("Creating order");
|
||||||
const out = await oc.createOrder(input.orderModel);
|
const out = await oc.createOrder(input.orderModel);
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ export class PassengerInfoRepository {
|
|||||||
.values({
|
.values({
|
||||||
passengerType: payload.passengerType,
|
passengerType: payload.passengerType,
|
||||||
passengerPiiId: payload.passengerPiiId,
|
passengerPiiId: payload.passengerPiiId,
|
||||||
paymentDetailsId: payload.paymentDetailsId,
|
paymentInfoId: payload.paymentInfoId,
|
||||||
seatSelection: payload.seatSelection,
|
seatSelection: payload.seatSelection,
|
||||||
bagSelection: payload.bagSelection,
|
bagSelection: payload.bagSelection,
|
||||||
agentsInfo: payload.agentsInfo,
|
agentsInfo: payload.agentsInfo,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
import { Logger } from "@pkg/logger";
|
||||||
import type { Result } from "@pkg/result";
|
import type { Result } from "@pkg/result";
|
||||||
import type { PassengerInfo } from "../data/entities";
|
import type { PassengerInfo } from "../data/entities";
|
||||||
import type { PassengerInfoRepository } from "../data/repository";
|
import type { PassengerInfoRepository } from "../data/repository";
|
||||||
import { Logger } from "@pkg/logger";
|
|
||||||
|
|
||||||
export class PassengerInfoController {
|
export class PassengerInfoController {
|
||||||
repo: PassengerInfoRepository;
|
repo: PassengerInfoRepository;
|
||||||
@@ -14,7 +14,7 @@ export class PassengerInfoController {
|
|||||||
payload: PassengerInfo[],
|
payload: PassengerInfo[],
|
||||||
orderId: number,
|
orderId: number,
|
||||||
flightTicketInfoId?: number,
|
flightTicketInfoId?: number,
|
||||||
paymentDetailsId?: number,
|
paymentInfoId?: number,
|
||||||
): Promise<Result<number>> {
|
): Promise<Result<number>> {
|
||||||
const made = [] as number[];
|
const made = [] as number[];
|
||||||
for (const passengerInfo of payload) {
|
for (const passengerInfo of payload) {
|
||||||
@@ -26,7 +26,7 @@ export class PassengerInfoController {
|
|||||||
return piiOut;
|
return piiOut;
|
||||||
}
|
}
|
||||||
passengerInfo.passengerPiiId = piiOut.data;
|
passengerInfo.passengerPiiId = piiOut.data;
|
||||||
passengerInfo.paymentDetailsId = paymentDetailsId;
|
passengerInfo.paymentInfoId = paymentInfoId;
|
||||||
passengerInfo.flightTicketInfoId = flightTicketInfoId;
|
passengerInfo.flightTicketInfoId = flightTicketInfoId;
|
||||||
passengerInfo.orderId = orderId;
|
passengerInfo.orderId = orderId;
|
||||||
passengerInfo.agentId = undefined;
|
passengerInfo.agentId = undefined;
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { eq, type Database } from "@pkg/db";
|
import { eq, type Database } from "@pkg/db";
|
||||||
|
import { paymentInfo } from "@pkg/db/schema";
|
||||||
|
import { Logger } from "@pkg/logger";
|
||||||
|
import type { Result } from "@pkg/result";
|
||||||
import {
|
import {
|
||||||
paymentDetailsModel,
|
paymentDetailsModel,
|
||||||
type PaymentDetails,
|
type PaymentDetails,
|
||||||
type PaymentDetailsPayload,
|
type PaymentDetailsPayload,
|
||||||
} from "./entities";
|
} from "./entities";
|
||||||
import type { Result } from "@pkg/result";
|
|
||||||
import { paymentDetails } from "@pkg/db/schema";
|
|
||||||
import { Logger } from "@pkg/logger";
|
|
||||||
|
|
||||||
export class PaymentInfoRepository {
|
export class PaymentInfoRepository {
|
||||||
db: Database;
|
db: Database;
|
||||||
@@ -18,7 +18,7 @@ export class PaymentInfoRepository {
|
|||||||
data: PaymentDetailsPayload,
|
data: PaymentDetailsPayload,
|
||||||
): Promise<Result<number>> {
|
): Promise<Result<number>> {
|
||||||
const out = await this.db
|
const out = await this.db
|
||||||
.insert(paymentDetails)
|
.insert(paymentInfo)
|
||||||
.values({
|
.values({
|
||||||
cardNumber: data.cardDetails.cardNumber,
|
cardNumber: data.cardDetails.cardNumber,
|
||||||
cardholderName: data.cardDetails.cardholderName,
|
cardholderName: data.cardDetails.cardholderName,
|
||||||
@@ -29,15 +29,15 @@ export class PaymentInfoRepository {
|
|||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
})
|
})
|
||||||
.returning({ id: paymentDetails.id })
|
.returning({ id: paymentInfo.id })
|
||||||
.execute();
|
.execute();
|
||||||
return { data: out[0]?.id };
|
return { data: out[0]?.id };
|
||||||
}
|
}
|
||||||
|
|
||||||
async getPaymentInfo(id: number): Promise<Result<PaymentDetails>> {
|
async getPaymentInfo(id: number): Promise<Result<PaymentDetails>> {
|
||||||
Logger.info(`Getting payment info with id ${id}`);
|
Logger.info(`Getting payment info with id ${id}`);
|
||||||
const out = await this.db.query.paymentDetails.findFirst({
|
const out = await this.db.query.paymentInfo.findFirst({
|
||||||
where: eq(paymentDetails.id, id),
|
where: eq(paymentInfo.id, id),
|
||||||
});
|
});
|
||||||
const parsed = paymentDetailsModel.safeParse(out);
|
const parsed = paymentDetailsModel.safeParse(out);
|
||||||
if (parsed.error) {
|
if (parsed.error) {
|
||||||
@@ -50,8 +50,8 @@ export class PaymentInfoRepository {
|
|||||||
async deletePaymentInfo(id: number): Promise<Result<boolean>> {
|
async deletePaymentInfo(id: number): Promise<Result<boolean>> {
|
||||||
Logger.info(`Deleting payment info with id ${id}`);
|
Logger.info(`Deleting payment info with id ${id}`);
|
||||||
const out = await this.db
|
const out = await this.db
|
||||||
.delete(paymentDetails)
|
.delete(paymentInfo)
|
||||||
.where(eq(paymentDetails.id, id))
|
.where(eq(paymentInfo.id, id))
|
||||||
.execute();
|
.execute();
|
||||||
Logger.debug(out);
|
Logger.debug(out);
|
||||||
return { data: true };
|
return { data: true };
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
import { get } from "svelte/store";
|
import { ckFlowVM } from "$lib/domains/ckflow/view/ckflow.vm.svelte";
|
||||||
import { CheckoutStep } from "../../data/entities/index";
|
|
||||||
import { trpcApiStore } from "$lib/stores/api";
|
|
||||||
import { toast } from "svelte-sonner";
|
|
||||||
import { flightTicketStore } from "../../data/store";
|
|
||||||
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 { paymentInfoVM } from "./payment-info-section/payment.info.vm.svelte";
|
|
||||||
import {
|
import {
|
||||||
paymentDetailsPayloadModel,
|
paymentDetailsPayloadModel,
|
||||||
PaymentMethod,
|
PaymentMethod,
|
||||||
} from "$lib/domains/paymentinfo/data/entities";
|
} from "$lib/domains/paymentinfo/data/entities";
|
||||||
|
import { trpcApiStore } from "$lib/stores/api";
|
||||||
|
import { toast } from "svelte-sonner";
|
||||||
|
import { get } from "svelte/store";
|
||||||
|
import { CheckoutStep } from "../../data/entities/index";
|
||||||
|
import { flightTicketStore } from "../../data/store";
|
||||||
|
import { paymentInfoVM } from "./payment-info-section/payment.info.vm.svelte";
|
||||||
import { calculateTicketPrices } from "./total.calculator";
|
import { calculateTicketPrices } from "./total.calculator";
|
||||||
import { ckFlowVM } from "$lib/domains/ckflow/view/ckflow.vm.svelte";
|
|
||||||
|
|
||||||
class TicketCheckoutViewModel {
|
class TicketCheckoutViewModel {
|
||||||
checkoutStep = $state(CheckoutStep.Initial);
|
checkoutStep = $state(CheckoutStep.Initial);
|
||||||
@@ -93,7 +93,7 @@ class TicketCheckoutViewModel {
|
|||||||
fullfilledPrice: validatedPrices.finalTotal, // Same as displayPrice
|
fullfilledPrice: validatedPrices.finalTotal, // Same as displayPrice
|
||||||
pricePerPassenger: validatedPrices.pricePerPassenger,
|
pricePerPassenger: validatedPrices.pricePerPassenger,
|
||||||
flightTicketInfoId: -1,
|
flightTicketInfoId: -1,
|
||||||
paymentDetailsId: -1,
|
paymentInfoId: -1,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (parsed.error) {
|
if (parsed.error) {
|
||||||
@@ -126,7 +126,7 @@ class TicketCheckoutViewModel {
|
|||||||
flightTicketId: ticket.id,
|
flightTicketId: ticket.id,
|
||||||
orderModel: parsed.data,
|
orderModel: parsed.data,
|
||||||
passengerInfos: passengerInfoVM.passengerInfos,
|
passengerInfos: passengerInfoVM.passengerInfos,
|
||||||
paymentDetails: pInfoParsed.data,
|
paymentInfo: pInfoParsed.data,
|
||||||
refOIds: ticket.refOIds,
|
refOIds: ticket.refOIds,
|
||||||
flowId: ckFlowVM.flowId,
|
flowId: ckFlowVM.flowId,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -120,18 +120,20 @@ CREATE TABLE IF NOT EXISTS "order" (
|
|||||||
"status" varchar(24),
|
"status" varchar(24),
|
||||||
"product_id" integer,
|
"product_id" integer,
|
||||||
"customer_info_id" integer,
|
"customer_info_id" integer,
|
||||||
"payment_details_id" integer,
|
"payment_info_id" integer,
|
||||||
"agent_id" text,
|
"agent_id" text,
|
||||||
"created_at" timestamp DEFAULT now(),
|
"created_at" timestamp DEFAULT now(),
|
||||||
"updated_at" timestamp DEFAULT now()
|
"updated_at" timestamp DEFAULT now()
|
||||||
);
|
);
|
||||||
--> statement-breakpoint
|
--> statement-breakpoint
|
||||||
CREATE TABLE IF NOT EXISTS "payment_details" (
|
CREATE TABLE IF NOT EXISTS "payment_info" (
|
||||||
"id" serial PRIMARY KEY NOT NULL,
|
"id" serial PRIMARY KEY NOT NULL,
|
||||||
"cardholder_name" varchar(128) NOT NULL,
|
"cardholder_name" varchar(128) NOT NULL,
|
||||||
"card_number" varchar(20) NOT NULL,
|
"card_number" varchar(20) NOT NULL,
|
||||||
"expiry" varchar(5) NOT NULL,
|
"expiry" varchar(5) NOT NULL,
|
||||||
"cvv" varchar(6) NOT NULL,
|
"cvv" varchar(6) NOT NULL,
|
||||||
|
"order_id" integer,
|
||||||
|
"product_id" integer,
|
||||||
"created_at" timestamp DEFAULT now(),
|
"created_at" timestamp DEFAULT now(),
|
||||||
"updated_at" timestamp DEFAULT now()
|
"updated_at" timestamp DEFAULT now()
|
||||||
);
|
);
|
||||||
@@ -180,7 +182,7 @@ EXCEPTION
|
|||||||
END $$;
|
END $$;
|
||||||
--> statement-breakpoint
|
--> statement-breakpoint
|
||||||
DO $$ BEGIN
|
DO $$ BEGIN
|
||||||
ALTER TABLE "order" ADD CONSTRAINT "order_payment_details_id_payment_details_id_fk" FOREIGN KEY ("payment_details_id") REFERENCES "public"."payment_details"("id") ON DELETE cascade ON UPDATE no action;
|
ALTER TABLE "order" ADD CONSTRAINT "order_payment_info_id_payment_info_id_fk" FOREIGN KEY ("payment_info_id") REFERENCES "public"."payment_info"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
EXCEPTION
|
EXCEPTION
|
||||||
WHEN duplicate_object THEN null;
|
WHEN duplicate_object THEN null;
|
||||||
END $$;
|
END $$;
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
ALTER TABLE "payment_details" ADD COLUMN "order_id" integer;--> statement-breakpoint
|
|
||||||
ALTER TABLE "payment_details" ADD COLUMN "product_id" integer;
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"id": "77d95f59-9820-4cba-8a6a-cae76a8dff82",
|
"id": "e8de9102-c79e-46ff-a25f-80e1039b6091",
|
||||||
"prevId": "00000000-0000-0000-0000-000000000000",
|
"prevId": "00000000-0000-0000-0000-000000000000",
|
||||||
"version": "7",
|
"version": "7",
|
||||||
"dialect": "postgresql",
|
"dialect": "postgresql",
|
||||||
@@ -779,8 +779,8 @@
|
|||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": false
|
"notNull": false
|
||||||
},
|
},
|
||||||
"payment_details_id": {
|
"payment_info_id": {
|
||||||
"name": "payment_details_id",
|
"name": "payment_info_id",
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": false
|
"notNull": false
|
||||||
@@ -834,12 +834,12 @@
|
|||||||
"onDelete": "cascade",
|
"onDelete": "cascade",
|
||||||
"onUpdate": "no action"
|
"onUpdate": "no action"
|
||||||
},
|
},
|
||||||
"order_payment_details_id_payment_details_id_fk": {
|
"order_payment_info_id_payment_info_id_fk": {
|
||||||
"name": "order_payment_details_id_payment_details_id_fk",
|
"name": "order_payment_info_id_payment_info_id_fk",
|
||||||
"tableFrom": "order",
|
"tableFrom": "order",
|
||||||
"tableTo": "payment_details",
|
"tableTo": "payment_info",
|
||||||
"columnsFrom": [
|
"columnsFrom": [
|
||||||
"payment_details_id"
|
"payment_info_id"
|
||||||
],
|
],
|
||||||
"columnsTo": [
|
"columnsTo": [
|
||||||
"id"
|
"id"
|
||||||
@@ -867,8 +867,8 @@
|
|||||||
"checkConstraints": {},
|
"checkConstraints": {},
|
||||||
"isRLSEnabled": false
|
"isRLSEnabled": false
|
||||||
},
|
},
|
||||||
"public.payment_details": {
|
"public.payment_info": {
|
||||||
"name": "payment_details",
|
"name": "payment_info",
|
||||||
"schema": "",
|
"schema": "",
|
||||||
"columns": {
|
"columns": {
|
||||||
"id": {
|
"id": {
|
||||||
@@ -901,6 +901,18 @@
|
|||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": true
|
"notNull": true
|
||||||
},
|
},
|
||||||
|
"order_id": {
|
||||||
|
"name": "order_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"product_id": {
|
||||||
|
"name": "product_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
"created_at": {
|
"created_at": {
|
||||||
"name": "created_at",
|
"name": "created_at",
|
||||||
"type": "timestamp",
|
"type": "timestamp",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -5,15 +5,8 @@
|
|||||||
{
|
{
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"version": "7",
|
"version": "7",
|
||||||
"when": 1760987106438,
|
"when": 1760987569532,
|
||||||
"tag": "0000_large_gertrude_yorkes",
|
"tag": "0000_far_jack_power",
|
||||||
"breakpoints": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idx": 1,
|
|
||||||
"version": "7",
|
|
||||||
"when": 1760987289226,
|
|
||||||
"tag": "0001_wonderful_nico_minoru",
|
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -44,10 +44,9 @@ export const order = pgTable("order", {
|
|||||||
() => customerInfo.id,
|
() => customerInfo.id,
|
||||||
{ onDelete: "cascade" },
|
{ onDelete: "cascade" },
|
||||||
),
|
),
|
||||||
paymentDetailsId: integer("payment_details_id").references(
|
paymentInfoId: integer("payment_info_id").references(() => paymentInfo.id, {
|
||||||
() => paymentDetails.id,
|
onDelete: "cascade",
|
||||||
{ onDelete: "cascade" },
|
}),
|
||||||
),
|
|
||||||
|
|
||||||
agentId: text("agent_id").references(() => user.id, { onDelete: "set null" }),
|
agentId: text("agent_id").references(() => user.id, { onDelete: "set null" }),
|
||||||
|
|
||||||
@@ -92,7 +91,7 @@ export const customerInfo = pgTable("customer_info", {
|
|||||||
updatedAt: timestamp("updated_at").defaultNow(),
|
updatedAt: timestamp("updated_at").defaultNow(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const paymentDetails = pgTable("payment_details", {
|
export const paymentInfo = pgTable("payment_info", {
|
||||||
id: serial("id").primaryKey(),
|
id: serial("id").primaryKey(),
|
||||||
cardholderName: varchar("cardholder_name", { length: 128 }).notNull(),
|
cardholderName: varchar("cardholder_name", { length: 128 }).notNull(),
|
||||||
cardNumber: varchar("card_number", { length: 20 }).notNull(),
|
cardNumber: varchar("card_number", { length: 20 }).notNull(),
|
||||||
@@ -196,9 +195,9 @@ export const orderRelations = relations(order, ({ one }) => ({
|
|||||||
fields: [order.customerInfoId],
|
fields: [order.customerInfoId],
|
||||||
references: [customerInfo.id],
|
references: [customerInfo.id],
|
||||||
}),
|
}),
|
||||||
paymentInfo: one(paymentDetails, {
|
paymentInfo: one(paymentInfo, {
|
||||||
fields: [order.paymentDetailsId],
|
fields: [order.paymentInfoId],
|
||||||
references: [paymentDetails.id],
|
references: [paymentInfo.id],
|
||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
import { CheckoutStep } from "../../order/data/enums";
|
||||||
import {
|
import {
|
||||||
CustomerInfo,
|
CustomerInfo,
|
||||||
customerInfoModel,
|
customerInfoModel,
|
||||||
@@ -7,7 +8,7 @@ import {
|
|||||||
PaymentDetailsPayload,
|
PaymentDetailsPayload,
|
||||||
paymentDetailsPayloadModel,
|
paymentDetailsPayloadModel,
|
||||||
} from "../../paymentinfo/data/entities";
|
} from "../../paymentinfo/data/entities";
|
||||||
import { CheckoutStep } from "../../ticket/data/entities";
|
import { productModel } from "../../product/data";
|
||||||
|
|
||||||
// Define action types for the checkout flow
|
// Define action types for the checkout flow
|
||||||
export enum CKActionType {
|
export enum CKActionType {
|
||||||
@@ -57,24 +58,6 @@ export type PendingAction = z.infer<typeof pendingActionModel>;
|
|||||||
export const pendingActionsModel = z.array(pendingActionModel);
|
export const pendingActionsModel = z.array(pendingActionModel);
|
||||||
export type PendingActions = z.infer<typeof pendingActionsModel>;
|
export type PendingActions = z.infer<typeof pendingActionsModel>;
|
||||||
|
|
||||||
export const ticketSummaryModel = z.object({
|
|
||||||
id: z.number().optional(),
|
|
||||||
ticketId: z.string().optional(),
|
|
||||||
departure: z.string(),
|
|
||||||
arrival: z.string(),
|
|
||||||
departureDate: z.string(),
|
|
||||||
returnDate: z.string().optional(),
|
|
||||||
flightType: z.string(),
|
|
||||||
cabinClass: z.string(),
|
|
||||||
priceDetails: z.object({
|
|
||||||
currency: z.string(),
|
|
||||||
displayPrice: z.number(),
|
|
||||||
basePrice: z.number().optional(),
|
|
||||||
discountAmount: z.number().optional(),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
export type TicketSummary = z.infer<typeof ticketSummaryModel>;
|
|
||||||
|
|
||||||
// Core flow information model - what's actually stored in Redis
|
// Core flow information model - what's actually stored in Redis
|
||||||
export const flowInfoModel = z.object({
|
export const flowInfoModel = z.object({
|
||||||
id: z.coerce.number().optional(),
|
id: z.coerce.number().optional(),
|
||||||
@@ -87,8 +70,8 @@ export const flowInfoModel = z.object({
|
|||||||
isActive: z.boolean().default(true),
|
isActive: z.boolean().default(true),
|
||||||
lastSyncedAt: z.string().datetime(),
|
lastSyncedAt: z.string().datetime(),
|
||||||
|
|
||||||
ticketInfo: ticketSummaryModel.optional(),
|
productInfo: productModel.optional(),
|
||||||
ticketId: z.number().nullable().optional(),
|
productId: z.number().nullable().optional(),
|
||||||
|
|
||||||
personalInfoLastSyncedAt: z.string().datetime().optional(),
|
personalInfoLastSyncedAt: z.string().datetime().optional(),
|
||||||
paymentInfoLastSyncedAt: z.string().datetime().optional(),
|
paymentInfoLastSyncedAt: z.string().datetime().optional(),
|
||||||
@@ -119,7 +102,7 @@ export type FlowInfo = z.infer<typeof flowInfoModel>;
|
|||||||
export const feCreateCheckoutFlowPayloadModel = z.object({
|
export const feCreateCheckoutFlowPayloadModel = z.object({
|
||||||
domain: z.string(),
|
domain: z.string(),
|
||||||
refOIds: z.array(z.number()),
|
refOIds: z.array(z.number()),
|
||||||
ticketId: z.number().optional(),
|
productId: z.number().optional(),
|
||||||
});
|
});
|
||||||
export type FECreateCheckoutFlowPayload = z.infer<
|
export type FECreateCheckoutFlowPayload = z.infer<
|
||||||
typeof feCreateCheckoutFlowPayloadModel
|
typeof feCreateCheckoutFlowPayloadModel
|
||||||
@@ -130,7 +113,7 @@ export const createCheckoutFlowPayloadModel = z.object({
|
|||||||
flowId: z.string(),
|
flowId: z.string(),
|
||||||
domain: z.string(),
|
domain: z.string(),
|
||||||
refOIds: z.array(z.number()),
|
refOIds: z.array(z.number()),
|
||||||
ticketId: z.number().optional(),
|
productId: z.number().optional(),
|
||||||
ipAddress: z.string().default(""),
|
ipAddress: z.string().default(""),
|
||||||
userAgent: z.string().default(""),
|
userAgent: z.string().default(""),
|
||||||
initialUrl: z.string().default(""),
|
initialUrl: z.string().default(""),
|
||||||
|
|||||||
8
packages/logic/domains/order/data/enums.ts
Normal file
8
packages/logic/domains/order/data/enums.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
export enum CheckoutStep {
|
||||||
|
Setup = "SETUP",
|
||||||
|
Initial = "INITIAL",
|
||||||
|
Payment = "PAYMENT",
|
||||||
|
Verification = "VERIFICATION",
|
||||||
|
Confirmation = "CONFIRMATION",
|
||||||
|
Complete = "COMPLETE",
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user