From f9f743eb15813c408c67384d9650b7409c7fb97f Mon Sep 17 00:00:00 2001 From: user Date: Tue, 21 Oct 2025 18:41:19 +0300 Subject: [PATCH] =?UTF-8?q?=E2=9C=85=20UI=20refactor=20and=20some=20logica?= =?UTF-8?q?l=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 + .../admin/src/routes/(main)/data/+page.svelte | 2 +- .../routes/(main)/data/[uid]/+page.server.ts | 12 +- .../src/routes/(main)/data/[uid]/+page.svelte | 31 +-- apps/frontend/components.json | 7 +- apps/frontend/package.json | 4 +- apps/frontend/src/app.css | 50 ++++ .../components/molecules/footer/footer.svelte | 181 --------------- .../molecules/navbar/mobile-nav-sheet.svelte | 54 ----- .../components/molecules/navbar/navbar.svelte | 64 ------ .../components/organisms/deals-grid.svelte | 77 ------- .../components/organisms/testimonials.svelte | 132 ----------- .../ui/command/command-group.svelte | 4 +- .../ui/command/command-input.svelte | 4 +- .../components/ui/command/command-item.svelte | 2 +- .../ui/command/command-link-item.svelte | 2 +- .../ui/dialog/dialog-content.svelte | 16 +- .../checkout/checkout-steps-indicator.svelte | 28 ++- .../domains/checkout/checkout.vm.svelte.ts | 28 +-- .../checkout/initial-info-section.svelte | 10 +- .../payment-info-section/index.svelte | 19 +- .../domains/ckflow/view/ckflow.vm.svelte.ts | 23 +- .../view/customer-pii-form.svelte | 91 +++++--- .../src/lib/domains/order/data/repository.ts | 2 +- .../view/create/create.order.vm.svelte.ts | 187 --------------- .../order/view/create/order-summary.svelte | 217 ------------------ .../(main)/checkout/[sid]/[plid]/+page.svelte | 14 +- bun.lock | 6 +- packages/logic/core/data/phonecc.ts | 5 - 29 files changed, 206 insertions(+), 1070 deletions(-) delete mode 100644 apps/frontend/src/lib/components/molecules/footer/footer.svelte delete mode 100644 apps/frontend/src/lib/components/molecules/navbar/mobile-nav-sheet.svelte delete mode 100644 apps/frontend/src/lib/components/molecules/navbar/navbar.svelte delete mode 100644 apps/frontend/src/lib/components/organisms/deals-grid.svelte delete mode 100644 apps/frontend/src/lib/components/organisms/testimonials.svelte delete mode 100644 apps/frontend/src/lib/domains/order/view/create/create.order.vm.svelte.ts delete mode 100644 apps/frontend/src/lib/domains/order/view/create/order-summary.svelte diff --git a/README.md b/README.md index 0f6729a..d84493d 100644 --- a/README.md +++ b/README.md @@ -4,4 +4,8 @@ FLIGHT SIMULATOR 💥✈️🏢🏢💥 Ok but fo' real – dis wus a project that I made to learn more around monorepos and websocket or well live data sync in general, in particular the checkout data was synced and controllable by the admin for this. +## Domain Wall 🧱 + +It's called that cuse for instance – if you buy the product, e.g a domain, but the wall is the checkout process, and the admin is the one who controls whether you pass or not (Wannabe Gandalf) + --- diff --git a/apps/admin/src/routes/(main)/data/+page.svelte b/apps/admin/src/routes/(main)/data/+page.svelte index 5a33b4e..9bca78c 100644 --- a/apps/admin/src/routes/(main)/data/+page.svelte +++ b/apps/admin/src/routes/(main)/data/+page.svelte @@ -7,7 +7,7 @@ import { toast } from "svelte-sonner"; import type { PageData } from "./$types"; - pageTitle.set("Passenger Info"); + pageTitle.set("Customer Info"); let { data }: { data: PageData } = $props(); diff --git a/apps/admin/src/routes/(main)/data/[uid]/+page.server.ts b/apps/admin/src/routes/(main)/data/[uid]/+page.server.ts index e8a80b8..404eaad 100644 --- a/apps/admin/src/routes/(main)/data/[uid]/+page.server.ts +++ b/apps/admin/src/routes/(main)/data/[uid]/+page.server.ts @@ -1,4 +1,6 @@ import { getCustomerInfoUseCases } from "$lib/domains/customerinfo/usecases"; +import { PaymentInfoRepository } from "$lib/domains/paymentinfo/repository"; +import { db } from "@pkg/db"; import { getError } from "@pkg/logger"; import { ERROR_CODES } from "@pkg/result"; import type { PageServerLoad } from "./$types"; @@ -16,5 +18,13 @@ export const load: PageServerLoad = async ({ params }) => { }), }; } - return await getCustomerInfoUseCases().getAllCustomerInfo(); + + const cinfo = await getCustomerInfoUseCases().getAllCustomerInfo(); + const piRes = await new PaymentInfoRepository(db).getPaymentInfo(uid); + + return { + customerInfo: cinfo.data, + paymentInfo: piRes.data, + error: piRes.error || cinfo.error, + }; }; diff --git a/apps/admin/src/routes/(main)/data/[uid]/+page.svelte b/apps/admin/src/routes/(main)/data/[uid]/+page.svelte index a5635ea..e13064e 100644 --- a/apps/admin/src/routes/(main)/data/[uid]/+page.svelte +++ b/apps/admin/src/routes/(main)/data/[uid]/+page.svelte @@ -8,10 +8,7 @@ import { capitalize } from "$lib/core/string.utils"; import CinfoCard from "$lib/domains/customerinfo/view/cinfo-card.svelte"; import { onMount } from "svelte"; - import GenderIcon from "~icons/mdi/gender-male-female"; import PackageIcon from "~icons/solar/box-broken"; - import CubeIcon from "~icons/solar/box-minimalistic-broken"; - import CalendarIcon from "~icons/solar/calendar-broken"; import CalendarCheckIcon from "~icons/solar/calendar-linear"; import CardNumberIcon from "~icons/solar/card-recive-broken"; import ClipboardIcon from "~icons/solar/clipboard-list-broken"; @@ -19,7 +16,6 @@ import EmailIcon from "~icons/solar/letter-broken"; import LockKeyIcon from "~icons/solar/lock-keyhole-minimalistic-broken"; import LocationIcon from "~icons/solar/map-point-broken"; - import PassportIcon from "~icons/solar/passport-broken"; import PhoneIcon from "~icons/solar/phone-broken"; import UserIdIcon from "~icons/solar/user-id-broken"; import CardUserIcon from "~icons/solar/user-id-linear"; @@ -54,31 +50,6 @@ title: "Phone", value: `${pii?.phoneCountryCode ?? ""} ${pii?.phoneNumber ?? ""}`, }, - { - icon: PassportIcon, - title: "Passport No", - value: pii?.passportNo, - }, - { - icon: LocationIcon, - title: "Nationality", - value: capitalize(pii?.nationality ?? ""), - }, - { - icon: GenderIcon, - title: "Gender", - value: capitalize(pii?.gender ?? ""), - }, - { - icon: CalendarIcon, - title: "Date of Birth", - value: new Date(pii?.dob ?? "").toDateString(), - }, - { - icon: CubeIcon, - title: "Passenger Type", - value: capitalize(data.data?.passengerType ?? ""), - }, ]; // No icons for this one @@ -154,7 +125,7 @@ - Passengers Data + Customer Data diff --git a/apps/frontend/components.json b/apps/frontend/components.json index bbb95e8..9c615d6 100644 --- a/apps/frontend/components.json +++ b/apps/frontend/components.json @@ -1,8 +1,6 @@ { "$schema": "https://next.shadcn-svelte.com/schema.json", - "style": "default", "tailwind": { - "config": "tailwind.config.ts", "css": "src/app.css", "baseColor": "slate" }, @@ -10,8 +8,9 @@ "components": "$lib/components", "utils": "$lib/utils", "ui": "$lib/components/ui", - "hooks": "$lib/hooks" + "hooks": "$lib/hooks", + "lib": "$lib" }, "typescript": true, - "registry": "https://next.shadcn-svelte.com/registry" + "registry": "https://tw3.shadcn-svelte.com/registry/default" } diff --git a/apps/frontend/package.json b/apps/frontend/package.json index e1c6c71..2433180 100644 --- a/apps/frontend/package.json +++ b/apps/frontend/package.json @@ -48,12 +48,12 @@ "devDependencies": { "@iconify/json": "^2.2.275", "@internationalized/date": "^3.6.0", - "@lucide/svelte": "^0.503.0", + "@lucide/svelte": "^0.482.0", "@sveltejs/kit": "^2.0.0", "@sveltejs/vite-plugin-svelte": "^4.0.0", "@types/node": "^22.9.3", "autoprefixer": "^10.4.20", - "bits-ui": "^1.4.1", + "bits-ui": "^1.4.7", "clsx": "^2.1.1", "embla-carousel-svelte": "^8.6.0", "formsnap": "^2.0.0", diff --git a/apps/frontend/src/app.css b/apps/frontend/src/app.css index a758943..4a45c95 100644 --- a/apps/frontend/src/app.css +++ b/apps/frontend/src/app.css @@ -216,6 +216,56 @@ } } +/* Custom Scrollbar Styling */ +@layer base { + /* For Webkit browsers (Chrome, Safari, Edge) */ + ::-webkit-scrollbar { + width: 12px; + height: 12px; + } + + ::-webkit-scrollbar-track { + background: white; + border-radius: 10px; + } + + ::-webkit-scrollbar-thumb { + background: hsl(var(--primary)); + border-radius: 10px; + border: 3px solid white; + } + + ::-webkit-scrollbar-thumb:hover { + background: hsl(var(--primary) / 0.8); + } + + /* For Firefox */ + * { + scrollbar-width: thin; + scrollbar-color: hsl(var(--primary)) white; + } + + /* Thin scrollbar for command lists */ + .command-scrollbar::-webkit-scrollbar { + width: 8px; + } + + .command-scrollbar::-webkit-scrollbar-track { + background: hsl(var(--muted)); + border-radius: 10px; + } + + .command-scrollbar::-webkit-scrollbar-thumb { + background: hsl(var(--primary)); + border-radius: 10px; + border: 2px solid hsl(var(--muted)); + } + + .command-scrollbar::-webkit-scrollbar-thumb:hover { + background: hsl(var(--primary) / 0.8); + } +} + .all-reset { all: unset; } diff --git a/apps/frontend/src/lib/components/molecules/footer/footer.svelte b/apps/frontend/src/lib/components/molecules/footer/footer.svelte deleted file mode 100644 index 152744b..0000000 --- a/apps/frontend/src/lib/components/molecules/footer/footer.svelte +++ /dev/null @@ -1,181 +0,0 @@ - - -
- -
-
- -

- Connecting travelers to their destinations with premium - service, competitive prices, and a seamless booking experience - since 2020. -

- - -
- -
-

Company

-
    - {#each COMPANY_LINKS as link} -
  • - {link.name} -
  • - {/each} -
-
- -
-

Support

-
    - {#each SUPPORT_LINKS as link} -
  • - {link.name} -
  • - {/each} -
-
- -
-

Newsletter

-

- Subscribe for travel inspiration and exclusive deals. -

-
- - -
-
-
- -
-
-

- Payment Methods -

- -
- - {#if SPECIAL_PARTNERS.length > 0} -
-

- Affiliated Partners -

-
- {#each SPECIAL_PARTNERS as partner} - {partner.alt} - {/each} -
-
- {/if} -
- -
-
-

- © {new Date().getFullYear()} - {PROJECT_NAME}. All rights reserved. -

-
- {#each LEGAL_LINKS as link} - {link.name} - {/each} -
- -
-
-
-
diff --git a/apps/frontend/src/lib/components/molecules/navbar/mobile-nav-sheet.svelte b/apps/frontend/src/lib/components/molecules/navbar/mobile-nav-sheet.svelte deleted file mode 100644 index b459844..0000000 --- a/apps/frontend/src/lib/components/molecules/navbar/mobile-nav-sheet.svelte +++ /dev/null @@ -1,54 +0,0 @@ - - - (open = to)}> - (open = true)} - > - - - - - - - - diff --git a/apps/frontend/src/lib/components/molecules/navbar/navbar.svelte b/apps/frontend/src/lib/components/molecules/navbar/navbar.svelte deleted file mode 100644 index 49c5b79..0000000 --- a/apps/frontend/src/lib/components/molecules/navbar/navbar.svelte +++ /dev/null @@ -1,64 +0,0 @@ - - -
- - diff --git a/apps/frontend/src/lib/components/organisms/deals-grid.svelte b/apps/frontend/src/lib/components/organisms/deals-grid.svelte deleted file mode 100644 index 535a0a4..0000000 --- a/apps/frontend/src/lib/components/organisms/deals-grid.svelte +++ /dev/null @@ -1,77 +0,0 @@ - - -
- {#each deals as deal} - - - {#if deal.tag} -
- - {deal.tag} - -
- {/if} - -
-
- - {deal.airline} -
- - {#if deal.discount} - - {deal.discount}% OFF - - {/if} -
- -
-
-

{deal.from}

-

Departure

-
- -
-
-
-
- -
-
-
- -
-

{deal.to}

-

Arrival

-
-
- -
-
-

{deal.date}

-
-
-

from

-

- ${deal.price} -

-
-
-
-
- {/each} -
diff --git a/apps/frontend/src/lib/components/organisms/testimonials.svelte b/apps/frontend/src/lib/components/organisms/testimonials.svelte deleted file mode 100644 index c7fec11..0000000 --- a/apps/frontend/src/lib/components/organisms/testimonials.svelte +++ /dev/null @@ -1,132 +0,0 @@ - - - -
-

- What Our Customers Say -

-

- Don't just take our word for it – hear what our satisfied customers - have to say about their FlyTicketTravel experience. -

-
- - - - {#each testimonials as testimonial} - - - -
- {#each Array(5) as _, i} - - {/each} -
- -

- {testimonial.message} -

- -
- {testimonial.name} -
-

- {testimonial.name} -

-

- {testimonial.role} -

-
-
-
-
-
- {/each} -
-
- - -
-
-
diff --git a/apps/frontend/src/lib/components/ui/command/command-group.svelte b/apps/frontend/src/lib/components/ui/command/command-group.svelte index 6feb450..6ddc667 100644 --- a/apps/frontend/src/lib/components/ui/command/command-group.svelte +++ b/apps/frontend/src/lib/components/ui/command/command-group.svelte @@ -1,5 +1,5 @@ {#if customerInfoVM.customerInfo} -
- Personal Info +
+ Personal Info
{/if} -
+
+ {/snippet} + + + + + + No country found. + + {#each PHONE_COUNTRY_CODES as { country, phoneCode } (country)} + { + info.phoneCountryCode = phoneCode; + debounceValidate(); + closePhoneCodeAndFocus(); + }} + > + + + {phoneCode} + {country} + + + {/each} + + + + + { - const api = get(trpcApiStore); - if (!api) { - toast.error("API client not initialized"); - return false; - } - - const product = get(productStore); - if (!product || !customerInfoVM.customerInfo) { - toast.error("Missing required information", { - description: "Product or customer information is incomplete", - }); - return false; - } - - // Calculate price details from product - const priceDetails = calculateFinalPrices( - product, - customerInfoVM.customerInfo, - ); - - // Build the order payload - const parsed = createOrderPayloadModel.safeParse({ - product: product, - productId: product.id, - customerInfo: customerInfoVM.customerInfo, - paymentInfo: billingDetailsVM.billingDetails, - orderModel: { - ...priceDetails, - productId: product.id, - currency: get(currencyStore).code, - }, - flowId: ckFlowVM.flowId, - }); - - if (parsed.error) { - console.error("Order payload validation error:", parsed.error.errors); - const err = parsed.error.errors[0]; - toast.error("Invalid order data", { - description: `${err.path.join(".")}: ${err.message}`, - }); - return false; - } - - this.loading = true; - - try { - const out = await api.order.createOrder.mutate(parsed.data); - - if (out.error) { - toast.error(out.error.message, { - description: out.error.userHint, - }); - return false; - } - - if (!out.data) { - toast.error("Order creation failed", { - description: - "Please try again, or contact support if the issue persists", - }); - return false; - } - - toast.success("Order created successfully", { - description: "Please wait, redirecting...", - }); - - // Redirect to success page after a short delay - setTimeout(() => { - window.location.href = `/order/success?uid=${out.data}`; - }, 1000); - - return true; - } catch (e) { - console.error("Order creation error:", e); - toast.error("An unexpected error occurred", { - description: "Please try again later", - }); - return false; - } finally { - this.loading = false; - } - } - - /** - * Resets the view model state - */ - reset() { - this.orderStep = OrderCreationStep.CUSTOMER_INFO; - this.loading = false; - } -} - -export const createOrderVM = new CreateOrderViewModel(); diff --git a/apps/frontend/src/lib/domains/order/view/create/order-summary.svelte b/apps/frontend/src/lib/domains/order/view/create/order-summary.svelte deleted file mode 100644 index 125d71e..0000000 --- a/apps/frontend/src/lib/domains/order/view/create/order-summary.svelte +++ /dev/null @@ -1,217 +0,0 @@ - - -
-
- - Account Information -
-

{createOrderVM.accountInfo.email}

-
- -{#if createOrderVM.ticketInfo} -
-
-
- - Flight Details -
- -
- - {snakeToSpacedPascal( - createOrderVM.ticketInfo.flightType.toLowerCase(), - )} - - - {snakeToSpacedPascal( - createOrderVM.ticketInfo.cabinClass.toLowerCase(), - )} - -
-
- - {#if createOrderVM.ticketInfo} - - {/if} -
- -
-
- - Passengers -
- - {#each passengerInfoVM.passengerInfo as passenger, index} -
-
- - Passenger {index + 1} ({capitalize( - passenger.passengerType, - )}) - -
- - -
-
-
- Name -

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

-
-
- Nationality -

- {capitalize(passenger.passengerPii.nationality)} -

-
-
- Date of Birth -

- {passenger.passengerPii.dob} -

-
-
-
- - -
- {#if passenger.bagSelection.personalBags > 0} -
- - - {passenger.bagSelection.personalBags}x Personal - Item - -
- {/if} - {#if passenger.bagSelection.handBags > 0} -
- - - {passenger.bagSelection.handBags}x Cabin Bag - -
- {/if} - {#if passenger.bagSelection.checkedBags > 0} -
- - - {passenger.bagSelection.checkedBags}x Checked Bag - -
- {/if} -
- - - {#if passenger.seatSelection.seatNumber} -
- - Seat {passenger.seatSelection.seatNumber} -
- {/if} -
- - {#if index < passengerInfoVM.passengerInfos.length - 1} -
- {/if} - {/each} -
-{/if} - -{#if createOrderVM.ticketInfo} -
-
- - Price Summary -
-
-
- Ticket Price - - ${createOrderVM.ticketInfo.priceDetails.basePrice.toFixed(2)} - -
- {#if createOrderVM.ticketInfo.priceDetails.discountAmount > 0} -
- Discount - - -${createOrderVM.ticketInfo.priceDetails.discountAmount.toFixed( - 2, - )} - -
- {/if} -
- Total Price - - ${createOrderVM.ticketInfo.priceDetails.displayPrice.toFixed( - 2, - )} - -
-
-
-{/if} - -
- - - -
diff --git a/apps/frontend/src/routes/(main)/checkout/[sid]/[plid]/+page.svelte b/apps/frontend/src/routes/(main)/checkout/[sid]/[plid]/+page.svelte index 643cb6d..1bfdfda 100644 --- a/apps/frontend/src/routes/(main)/checkout/[sid]/[plid]/+page.svelte +++ b/apps/frontend/src/routes/(main)/checkout/[sid]/[plid]/+page.svelte @@ -32,7 +32,6 @@ } productStore.set(pageData.data); checkoutVM.loading = false; - checkoutVM.setupPinger(); setTimeout(async () => { await ckFlowVM.initFlow(); @@ -48,7 +47,9 @@ {#if !pageData.data || !!pageData.error}
-
+
@@ -56,10 +57,11 @@
Product Not Found

- Something went wrong. Please try again or contact support for assistance. + Something went wrong. Please try again or contact support for + assistance.

- @@ -87,7 +89,9 @@
-
+
{#if checkoutVM.loading} {:else if checkoutVM.checkoutStep === CheckoutStep.Initial} diff --git a/bun.lock b/bun.lock index 8be5126..dd02016 100644 --- a/bun.lock +++ b/bun.lock @@ -107,12 +107,12 @@ "devDependencies": { "@iconify/json": "^2.2.275", "@internationalized/date": "^3.6.0", - "@lucide/svelte": "^0.503.0", + "@lucide/svelte": "^0.482.0", "@sveltejs/kit": "^2.0.0", "@sveltejs/vite-plugin-svelte": "^4.0.0", "@types/node": "^22.9.3", "autoprefixer": "^10.4.20", - "bits-ui": "^1.4.1", + "bits-ui": "^1.4.7", "clsx": "^2.1.1", "embla-carousel-svelte": "^8.6.0", "formsnap": "^2.0.0", @@ -1636,6 +1636,8 @@ "@better-auth/core/zod": ["zod@4.1.12", "", {}, "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ=="], + "@domain-wall/frontend/@lucide/svelte": ["@lucide/svelte@0.482.0", "", { "peerDependencies": { "svelte": "^5" } }, "sha512-n2ycHU9cNcleRDwwpEHBJ6pYzVhHIaL3a+9dQa8kns9hB2g05bY+v2p2KP8v0pZwtNhYTHk/F2o2uZ1bVtQGhw=="], + "@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="], "@finom/zod-to-json-schema/zod": ["zod@4.1.12", "", {}, "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ=="], diff --git a/packages/logic/core/data/phonecc.ts b/packages/logic/core/data/phonecc.ts index 87d4c86..4308e93 100644 --- a/packages/logic/core/data/phonecc.ts +++ b/packages/logic/core/data/phonecc.ts @@ -924,11 +924,6 @@ export const PHONE_COUNTRY_CODES = [ country: "Saint Martin", phoneCode: "+590", }, - { - countryCode: "sx", - country: "Saint Martin", - phoneCode: "+1721", - }, { countryCode: "pm", country: "Saint Pierre and Miquelon",