- Added an availability confirmation step in the checkout process for internet orders, ensuring users verify service eligibility before proceeding to payment. - Updated the CheckoutWizard component to conditionally include the new AvailabilityStep based on the order type. - Refactored AddressStep to navigate to the AvailabilityStep for internet orders, improving user flow. - Enhanced the checkout store to track internet availability requests and updated the state management for better handling of checkout steps. - Updated relevant schemas and documentation to reflect the new checkout flow and requirements.
163 lines
5.3 KiB
TypeScript
163 lines
5.3 KiB
TypeScript
/**
|
|
* Checkout Domain - Schemas
|
|
*
|
|
* Zod validation schemas for unified checkout flow.
|
|
* Supports both authenticated and guest checkout.
|
|
*/
|
|
|
|
import { z } from "zod";
|
|
import {
|
|
emailSchema,
|
|
passwordSchema,
|
|
nameSchema,
|
|
phoneSchema,
|
|
genderEnum,
|
|
} from "../common/schema.js";
|
|
import { addressFormSchema } from "../customer/schema.js";
|
|
|
|
// ============================================================================
|
|
// ISO Date Schema
|
|
// ============================================================================
|
|
|
|
const isoDateOnlySchema = z
|
|
.string()
|
|
.regex(/^\d{4}-\d{2}-\d{2}$/, "Enter a valid date (YYYY-MM-DD)")
|
|
.refine(value => !Number.isNaN(Date.parse(value)), "Enter a valid date (YYYY-MM-DD)");
|
|
|
|
// ============================================================================
|
|
// Order Type Schema
|
|
// ============================================================================
|
|
|
|
export const orderTypeSchema = z.enum(["INTERNET", "SIM", "VPN"]);
|
|
|
|
// ============================================================================
|
|
// Price Breakdown Schema
|
|
// ============================================================================
|
|
|
|
export const priceBreakdownItemSchema = z.object({
|
|
label: z.string(),
|
|
sku: z.string().optional(),
|
|
monthlyPrice: z.number().optional(),
|
|
oneTimePrice: z.number().optional(),
|
|
quantity: z.number().optional().default(1),
|
|
});
|
|
|
|
// ============================================================================
|
|
// Cart Item Schema
|
|
// ============================================================================
|
|
|
|
export const cartItemSchema = z.object({
|
|
orderType: orderTypeSchema,
|
|
planSku: z.string().min(1, "Plan SKU is required"),
|
|
planName: z.string().min(1, "Plan name is required"),
|
|
addonSkus: z.array(z.string()).default([]),
|
|
configuration: z.record(z.string(), z.unknown()).default({}),
|
|
pricing: z.object({
|
|
monthlyTotal: z.number().nonnegative(),
|
|
oneTimeTotal: z.number().nonnegative(),
|
|
breakdown: z.array(priceBreakdownItemSchema).default([]),
|
|
}),
|
|
});
|
|
|
|
// ============================================================================
|
|
// Guest Info Schema
|
|
// ============================================================================
|
|
|
|
export const guestInfoSchema = z.object({
|
|
email: emailSchema,
|
|
firstName: nameSchema,
|
|
lastName: nameSchema,
|
|
phone: phoneSchema,
|
|
phoneCountryCode: z.string().min(1, "Phone country code is required"),
|
|
password: passwordSchema,
|
|
dateOfBirth: isoDateOnlySchema.optional(),
|
|
gender: genderEnum.optional(),
|
|
});
|
|
|
|
// ============================================================================
|
|
// Checkout Register Request Schema
|
|
// ============================================================================
|
|
|
|
export const checkoutRegisterRequestSchema = z.object({
|
|
email: emailSchema,
|
|
firstName: nameSchema,
|
|
lastName: nameSchema,
|
|
phone: phoneSchema,
|
|
phoneCountryCode: z.string().min(1, "Phone country code is required"),
|
|
password: passwordSchema,
|
|
address: addressFormSchema,
|
|
dateOfBirth: isoDateOnlySchema.optional(),
|
|
gender: genderEnum.optional(),
|
|
acceptTerms: z.literal(true, { message: "You must accept the terms and conditions" }),
|
|
marketingConsent: z.boolean().optional(),
|
|
});
|
|
|
|
// ============================================================================
|
|
// Checkout Register Response Schema
|
|
// ============================================================================
|
|
|
|
export const checkoutRegisterResponseSchema = z.object({
|
|
success: z.boolean(),
|
|
user: z.object({
|
|
id: z.string(),
|
|
email: z.string(),
|
|
firstname: z.string(),
|
|
lastname: z.string(),
|
|
}),
|
|
session: z.object({
|
|
expiresAt: z.string(),
|
|
refreshExpiresAt: z.string(),
|
|
}),
|
|
sfAccountNumber: z.string().optional(),
|
|
});
|
|
|
|
// ============================================================================
|
|
// Checkout Step Schema
|
|
// ============================================================================
|
|
|
|
export const checkoutStepSchema = z.enum([
|
|
"account",
|
|
"address",
|
|
"availability",
|
|
"payment",
|
|
"review",
|
|
]);
|
|
|
|
// ============================================================================
|
|
// Checkout State Schema (for Zustand store)
|
|
// ============================================================================
|
|
|
|
export const checkoutStateSchema = z.object({
|
|
// Cart data
|
|
cartItem: cartItemSchema.nullable(),
|
|
|
|
// Guest info (pre-registration)
|
|
guestInfo: guestInfoSchema.partial().nullable(),
|
|
|
|
// Address
|
|
address: addressFormSchema.nullable(),
|
|
|
|
// Registration state
|
|
registrationComplete: z.boolean(),
|
|
userId: z.string().nullable(),
|
|
|
|
// Payment state
|
|
paymentMethodVerified: z.boolean(),
|
|
|
|
// Checkout step
|
|
currentStep: checkoutStepSchema,
|
|
});
|
|
|
|
// ============================================================================
|
|
// Inferred Types
|
|
// ============================================================================
|
|
|
|
export type OrderType = z.infer<typeof orderTypeSchema>;
|
|
export type PriceBreakdownItem = z.infer<typeof priceBreakdownItemSchema>;
|
|
export type CartItem = z.infer<typeof cartItemSchema>;
|
|
export type GuestInfo = z.infer<typeof guestInfoSchema>;
|
|
export type CheckoutRegisterRequest = z.infer<typeof checkoutRegisterRequestSchema>;
|
|
export type CheckoutRegisterResponse = z.infer<typeof checkoutRegisterResponseSchema>;
|
|
export type CheckoutStep = z.infer<typeof checkoutStepSchema>;
|
|
export type CheckoutState = z.infer<typeof checkoutStateSchema>;
|