export type DeliveryInvitationAction = | "create_delivery_invitation" | "send_delivery_offer" | "send_delivery_reminder" | "request_new_link" | "confirm_delivery_choice" | "transfer_to_logistics" | "mark_paid_storage" | "mark_delivered"; export type DeliveryInvitationPublicState = | "awaiting_choice" | "opened" | "reminder_sent" | "transferred_to_logistics" | "paid_storage" | "delivered" | "agreed" | "default"; export const DEFAULT_AVAILABLE_SLOTS = ["Первая половина дня", "Вторая половина дня"]; export const getOrderUpdateForDeliveryInvitationAction = (action: DeliveryInvitationAction) => { switch (action) { case "create_delivery_invitation": case "send_delivery_offer": case "send_delivery_reminder": case "request_new_link": return { status: "Ожидает ответа клиента", deliveryAgreementStatus: "Отправлено клиенту", }; case "confirm_delivery_choice": return { status: "Доставка согласована", deliveryAgreementStatus: "Подтверждено клиентом", }; case "transfer_to_logistics": return { status: "Передан логисту", deliveryAgreementStatus: "Нет ответа", }; case "mark_paid_storage": return { status: "Платное хранение", deliveryAgreementStatus: "Нет ответа", }; case "mark_delivered": return { status: "Доставлен", deliveryAgreementStatus: "Подтверждено клиентом", }; default: return null; } }; export const getClientInvitationStateFromOrderStatus = ( status: string, ): DeliveryInvitationPublicState => { switch (status) { case "Ожидает ответа клиента": return "awaiting_choice"; case "Ожидает согласования доставки": return "opened"; case "Напоминание отправлено": case "Переход отправлен": return "reminder_sent"; case "Передан логисту": return "transferred_to_logistics"; case "Платное хранение": return "paid_storage"; case "Доставлен": return "delivered"; case "Доставка согласована": return "agreed"; default: return "default"; } }; export const isActiveInvitationState = (state: DeliveryInvitationPublicState) => state === "awaiting_choice" || state === "opened" || state === "reminder_sent"; export const generateInvitationToken = () => crypto.randomUUID().replaceAll("-", ""); export const hashInvitationToken = async (token: string) => { const bytes = new TextEncoder().encode(token); const digest = await crypto.subtle.digest("SHA-256", bytes); return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join(""); }; export const normalizeAvailableSlots = (availableSlots?: string[] | null) => { const slots = availableSlots?.map((slot) => slot.trim()).filter(Boolean) || []; return slots.length > 0 ? Array.from(new Set(slots)) : [...DEFAULT_AVAILABLE_SLOTS]; }; export const resolvePublicAppUrl = ( request: Request, fallbackEnv?: string, ) => { const origin = request.headers.get("origin") || request.headers.get("referer") || ""; const envValue = fallbackEnv || (typeof Deno !== "undefined" ? Deno.env.get("PUBLIC_APP_URL") || Deno.env.get("APP_PUBLIC_URL") : ""); return (envValue || origin || "").replace(/\/$/, ""); }; export const buildInvitationUrl = (baseUrl: string, token: string) => `${baseUrl.replace(/\/$/, "")}/delivery/${token}`;