export type LimitWarningTone = 'warning' | 'danger'; export type LimitWarning = { id: string; scope: 'photos' | 'guests' | 'gallery'; tone: LimitWarningTone; message: string; }; export type LimitUsageSummary = { limit: number | null; used: number; remaining: number | null; percentage: number | null; state: 'ok' | 'warning' | 'limit_reached' | 'unlimited'; threshold_reached: number | null; next_threshold: number | null; thresholds: number[]; } | null; export type GallerySummary = { state: 'ok' | 'warning' | 'expired' | 'unlimited'; expires_at: string | null; days_remaining: number | null; warning_thresholds: number[]; warning_triggered: number | null; warning_sent_at: string | null; expired_notified_at: string | null; } | null; export type EventLimitSummary = { photos: LimitUsageSummary; guests: LimitUsageSummary; gallery: GallerySummary; can_upload_photos: boolean; can_add_guests: boolean; } | null | undefined; type TranslateFn = (key: string, options?: Record) => string; function hasRemaining(summary: LimitUsageSummary): summary is LimitUsageSummary & { remaining: number; limit: number } { return Boolean(summary) && typeof summary?.remaining === 'number' && typeof summary?.limit === 'number'; } export function buildLimitWarnings(limits: EventLimitSummary, t: TranslateFn): LimitWarning[] { if (!limits) { return []; } const warnings: LimitWarning[] = []; if (limits.photos) { if (limits.photos.state === 'limit_reached') { warnings.push({ id: 'photos-limit', scope: 'photos', tone: 'danger', message: t('photosBlocked'), }); } else if (limits.photos.state === 'warning' && hasRemaining(limits.photos)) { warnings.push({ id: 'photos-warning', scope: 'photos', tone: 'warning', message: t('photosWarning', { remaining: limits.photos.remaining, limit: limits.photos.limit, }), }); } } if (limits.guests) { if (limits.guests.state === 'limit_reached') { warnings.push({ id: 'guests-limit', scope: 'guests', tone: 'danger', message: t('guestsBlocked'), }); } else if (limits.guests.state === 'warning' && hasRemaining(limits.guests)) { warnings.push({ id: 'guests-warning', scope: 'guests', tone: 'warning', message: t('guestsWarning', { remaining: limits.guests.remaining, limit: limits.guests.limit, }), }); } } if (limits.gallery) { if (limits.gallery.state === 'expired') { warnings.push({ id: 'gallery-expired', scope: 'gallery', tone: 'danger', message: t('galleryExpired'), }); } else if (limits.gallery.state === 'warning') { const days = limits.gallery.days_remaining ?? 0; const safeDays = Math.max(0, days); const key = safeDays === 1 ? 'galleryWarningDay' : 'galleryWarningDays'; warnings.push({ id: 'gallery-warning', scope: 'gallery', tone: 'warning', message: t(key, { days: safeDays }), }); } } return warnings; }