export type EventAvailabilityInput = { status?: string | null; registration_start_date?: string | null; registration_end_date?: string | null; start_time?: string | null; end_time?: string | null; capacity?: number | null; registration_count?: number | null; }; export function getEventPath(event: { id: number; slug?: string | null }) { const segment = event.slug?.trim() || String(event.id); return `/events/${encodeURIComponent(segment)}`; } export type EventRegistrationState = | "open" | "not_started" | "closed" | "full" | "unavailable"; function timestamp(value?: string | null) { if (!value) return null; const parsed = new Date(value).getTime(); return Number.isFinite(parsed) ? parsed : null; } export function getEventRegistrationState( event: EventAvailabilityInput, now = Date.now(), ): { state: EventRegistrationState; remaining: number | null } { if (event.status !== "published") { return { state: "unavailable", remaining: null }; } const endsAt = timestamp(event.registration_end_date); if (endsAt !== null && now > endsAt) { return { state: "closed", remaining: getRemainingCapacity(event) }; } const startsAt = timestamp(event.registration_start_date); if (startsAt !== null && now < startsAt) { return { state: "not_started", remaining: getRemainingCapacity(event) }; } const remaining = getRemainingCapacity(event); if (remaining !== null && remaining <= 0) { return { state: "full", remaining: 0 }; } return { state: "open", remaining }; } function getRemainingCapacity(event: EventAvailabilityInput) { if (event.capacity == null || !Number.isFinite(Number(event.capacity))) { return null; } const used = Number.isFinite(Number(event.registration_count)) ? Number(event.registration_count) : 0; return Math.max(0, Number(event.capacity) - used); } export function getEventCardState( event: EventAvailabilityInput, now = Date.now(), ): "past" | "full" | "upcoming" { const endsAt = timestamp(event.end_time); if (event.status === "completed" || (endsAt !== null && endsAt < now)) { return "past"; } if (getEventRegistrationState(event, now).state === "full") { return "full"; } return "upcoming"; } export function getEventBrowseLabel(event: EventAvailabilityInput, now = Date.now()) { if (getEventCardState(event, now) === "past") return "برگزار شده"; if (getEventRegistrationState(event, now).state === "open") return "درحال برگزاری"; const startsAt = timestamp(event.start_time); if (startsAt !== null && startsAt <= now) return "درحال برگزاری"; return "به‌زودی"; } export function getEventRegistrationMessage(event: EventAvailabilityInput, now = Date.now()) { if (getEventCardState(event, now) === "past") return "این رویداد برگزار شده است"; const registration = getEventRegistrationState(event, now); if (registration.state === "not_started") return "ثبت‌نام هنوز آغاز نشده است"; if (registration.state === "closed") return "زمان ثبت‌نام به پایان رسیده است"; if (registration.state === "full") return "ظرفیت ثبت‌نام تکمیل شده است"; if (registration.state === "unavailable") return "امکان ثبت‌نام وجود ندارد"; if (registration.remaining === null) return "ثبت‌نام در حال انجام است"; return `${registration.remaining.toLocaleString("fa-IR")} نفر ظرفیت باقی‌مانده`; } export type RegistrationErrorKind = | "already_registered" | "not_started" | "closed" | "full" | "unknown"; function normalizeApiMessage(message: unknown) { return String(message ?? "") .replace(/[يى]/g, "ی") .replace(/\u200c/g, " ") .replace(/\s+/g, " ") .trim() .toLocaleLowerCase(); } export function classifyRegistrationError(message: unknown): RegistrationErrorKind { const normalized = normalizeApiMessage(message); if (normalized.includes("already registered") || normalized.includes("قبلا در این ایونت ثبت نام کرده اید")) { return "already_registered"; } if (normalized.includes("زمان ثبت نام هنوز آغاز نشده") || normalized.includes("registration has not started")) { return "not_started"; } if (normalized.includes("مهلت ثبت نام به پایان رسیده") || normalized.includes("registration period has ended")) { return "closed"; } if (normalized.includes("ظرفیت شرکت کنندگان تکمیل است") || normalized.includes("event is full")) { return "full"; } return "unknown"; } export type PaymentOutcome = "paid" | "pending" | "failed" | "canceled" | "unknown"; export function getPaymentOutcome(status: unknown): PaymentOutcome { const normalized = normalizeApiMessage(status); if (["success", "paid", "ok", "پرداخت شده", "تایید شده"].includes(normalized)) { return "paid"; } if (["pending", "initiated", "init", "در انتظار", "در حال پردازش"].includes(normalized)) { return "pending"; } if (["canceled", "cancelled", "لغو شده"].includes(normalized)) { return "canceled"; } if (["failed", "failure", "ناموفق"].includes(normalized)) { return "failed"; } return "unknown"; } export type RegistrationOutcome = "confirmed" | "pending" | "canceled" | "unknown"; export function getRegistrationOutcome(status: unknown): RegistrationOutcome { const normalized = normalizeApiMessage(status); if (["confirmed", "attended", "approved", "تایید شده"].includes(normalized)) { return "confirmed"; } if (["pending", "در انتظار"].includes(normalized)) { return "pending"; } if (["canceled", "cancelled", "لغو شده"].includes(normalized)) { return "canceled"; } return "unknown"; }