Files
LogisicsApp_Client/src/validations/order.validation.ts
2026-06-24 18:01:01 +03:00

167 lines
6.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import * as yup from "yup";
// ── Saudi mobile regex (05xxxxxxxx) ─────────────────────────────────────────
const SAUDI_PHONE_RE = /^(05\d{8}|009665\d{8}|\+9665\d{8})$/;
// ── Address sub-schemas (mirrors backend orderAddressSchema) ─────────────────
// Delivery address — location.coordinates required by the backend (MongoDB GeoJSON)
const deliveryAddressSchema = yup.object({
details: yup.object({
city: yup.string().trim().optional(),
state: yup.string().trim().optional(),
district: yup.string().trim().optional(),
street: yup.string().trim().optional(),
buildingNo: yup.string().trim().optional(),
unitNo: yup.string().trim().optional(),
zipCode: yup.string().trim().optional(),
}).optional(),
location: yup.object({
coordinates: yup
.array()
.of(yup.number().required())
.length(2, "الإحداثيات يجب أن تكون [خط الطول, خط العرض]")
.required("إحداثيات موقع التسليم مطلوبة"),
}).required("موقع التسليم مطلوب"),
}).optional();
// Pickup address — coordinates optional (user may supply an existing ID instead)
const pickupAddressSchema = yup.object({
details: yup.object({
city: yup.string().trim().optional(),
state: yup.string().trim().optional(),
district: yup.string().trim().optional(),
street: yup.string().trim().optional(),
buildingNo: yup.string().trim().optional(),
unitNo: yup.string().trim().optional(),
zipCode: yup.string().trim().optional(),
}).optional(),
location: yup.object({
coordinates: yup
.array()
.of(yup.number().required())
.length(2, "الإحداثيات يجب أن تكون [خط الطول, خط العرض]")
.optional(),
}).optional(),
}).optional();
// ── Base shape shared between create & update ────────────────────────────────
// Optional financial / shipment detail fields live here so both schemas
// inherit them without repetition.
const baseShape = {
// ── Optional: shipment details ──
type: yup.string().nullable().optional(),
quantity: yup
.number()
.typeError("الكمية يجب أن تكون رقمًا")
.min(1, "الكمية يجب أن تكون ١ أو أكثر")
.optional(),
weight: yup
.number()
.typeError("الوزن يجب أن يكون رقمًا")
.min(0, "الوزن لا يمكن أن يكون سالبًا")
.nullable()
.optional(),
// ── Optional: financial ──
subTotal: yup
.number()
.typeError("الإجمالي الفرعي يجب أن يكون رقمًا")
.min(0, "القيمة لا يمكن أن تكون سالبة")
.nullable()
.optional(),
vatRate: yup
.number()
.typeError("نسبة الضريبة يجب أن تكون رقمًا")
.min(0, "النسبة لا يمكن أن تكون سالبة")
.max(100, "النسبة لا يمكن أن تتجاوز ١٠٠%")
.nullable()
.optional(),
paymentMethod: yup
.mixed<"Cash" | "Card" | "Prepaid">()
.oneOf(["Cash", "Card", "Prepaid"], "طريقة الدفع غير صالحة")
.nullable()
.optional(),
};
// ── Create schema ─────────────────────────────────────────────────────────────
// The five fields mandated by the task spec are required here.
// deliveryAddressId / pickupAddressId intentionally excluded — the backend
// generates address records from deliveryAddressData / pickupAddressData.
export const createOrderSchema = yup.object({
// ── Required ──
tripId: yup
.string()
.required("الرحلة مطلوبة")
.min(1, "الرحلة مطلوبة"),
clientId: yup
.string()
.required("العميل مطلوب")
.min(1, "العميل مطلوب"),
shipmentNumber: yup
.string()
.required("رقم الشحنة مطلوب")
.min(2, "رقم الشحنة قصير جدًا"),
recipientName: yup
.string()
.required("اسم المستلم مطلوب")
.min(2, "اسم المستلم قصير جدًا"),
recipientPhone: yup
.string()
.required("رقم جوال المستلم مطلوب")
.matches(SAUDI_PHONE_RE, "أدخل رقم جوال سعودي صحيح (05xxxxxxxx)"),
// ── Optional: addresses ──
// deliveryAddress: always a new object (backend creates in MongoDB); coordinates required
deliveryAddress: deliveryAddressSchema,
// pickupAddress: new object OR existing ID — both optional; coordinates optional
pickupAddress: pickupAddressSchema,
pickupAddressId: yup.string().optional(),
// ── Optional inherited ──
...baseShape,
});
// ── Update schema ─────────────────────────────────────────────────────────────
// All fields are optional on update — the backend applies partial patches.
export const updateOrderSchema = yup.object({
tripId: yup.string().nullable().optional(),
clientId: yup.string().nullable().optional(),
shipmentNumber: yup.string().min(2, "رقم الشحنة قصير جدًا").optional(),
recipientName: yup.string().min(2, "اسم المستلم قصير جدًا").optional(),
recipientPhone: yup
.string()
.matches(SAUDI_PHONE_RE, "أدخل رقم جوال سعودي صحيح (05xxxxxxxx)")
.optional(),
paymentStatus: yup
.mixed<"Pending" | "Paid" | "Failed" | "Refunded">()
.oneOf(["Pending", "Paid", "Failed", "Refunded"], "حالة الدفع غير صالحة")
.nullable()
.optional(),
// ── Optional: addresses (delivery keeps coordinates required; pickup stays optional) ──
deliveryAddress: deliveryAddressSchema,
pickupAddress: pickupAddressSchema,
pickupAddressId: yup.string().optional(),
// ── Optional inherited ──
...baseShape,
});
// ── SchemaErrors — typed error bag consumed by OrderFormModal ─────────────────
export type CreateOrderSchemaErrors = Partial<
Record<keyof yup.InferType<typeof createOrderSchema>, string>
>;
export type UpdateOrderSchemaErrors = Partial<
Record<keyof yup.InferType<typeof updateOrderSchema>, string>
>;
// Union used inside the modal so one type covers both modes
export type OrderSchemaErrors = CreateOrderSchemaErrors & UpdateOrderSchemaErrors;