/** * Mirrors backend `event-edit-policy.ts` for host/admin edit UI. * Keep in sync when changing the lock matrix. */ export const EVENT_SENSITIVE_FIELDS = [ 'startsAt', 'endsAt', 'provinceId', 'cityId', 'address', 'lat', 'lng', 'isFree', 'price', 'cancellationFeePercent', 'cancellationFeePercent12To24Hours', 'cancellationFeePercentMoreThan24Hours', 'genderRestriction', 'ageRestriction', ] as const export type EventSensitiveField = (typeof EVENT_SENSITIVE_FIELDS)[number] export interface EventEditPolicy { canEdit: boolean hasActiveBookings: boolean minCapacity: number lockedFields: ReadonlySet /** When true, behavioral settings (discoverability, etc.) cannot change. */ lockBehavioralSettings: boolean } const TERMINAL = new Set(['cancelled', 'completed']) const LIVE = new Set(['published', 'full']) export function isTerminalEventStatus(status: string): boolean { return TERMINAL.has(status) } export function isLiveEventStatus(status: string): boolean { return LIVE.has(status) } /** * Slug cannot change on terminal events. * Live slug changes go through pending revisions (applied on admin approve). */ export function isEventSlugLocked(status: string): boolean { return TERMINAL.has(status) } /** * Event is locked for mutations when it reached a terminal status OR its * scheduled end time has passed (covers early complete vs clock end). */ export function isEventEnded(status: string, endsAt: string | Date, now: Date = new Date()): boolean { if (isTerminalEventStatus(status)) return true return new Date(endsAt).getTime() <= now.getTime() } export function resolveEventEditPolicy(status: string, bookedCount: number, reservedCapacity = 0): EventEditPolicy { const occupied = bookedCount + reservedCapacity const minCapacity = Math.max(1, occupied) if (isTerminalEventStatus(status)) { return { canEdit: false, hasActiveBookings: bookedCount > 0, minCapacity, lockedFields: new Set(EVENT_SENSITIVE_FIELDS), lockBehavioralSettings: true, } } if (status === 'draft' || !LIVE.has(status)) { return { canEdit: true, hasActiveBookings: false, minCapacity, lockedFields: new Set(), lockBehavioralSettings: false, } } const hasActiveBookings = bookedCount > 0 return { canEdit: true, hasActiveBookings, minCapacity, lockedFields: hasActiveBookings ? new Set(EVENT_SENSITIVE_FIELDS) : new Set(), lockBehavioralSettings: hasActiveBookings, } } export function isFieldLocked(policy: EventEditPolicy, field: EventSensitiveField): boolean { return policy.lockedFields.has(field) }