export type RegistrationEvent = { id: string; user_id: string; event: string; actor: string; note: string; applicant_message: string; mail_status: string; created_at: string; policy_changes?: string[]; } const eventLabels: Record = { approve: 'Account approved', reject: 'Registration rejected', reopen: 'Registration reopened for review', note: 'Internal note added', registration_observed: 'Registration recorded', verification_link_confirmed: 'Email address verified', verification_admin_override: 'Email marked verified by an administrator', verification_resend_requested: 'Verification email requested', verification_resend_failed: 'Verification email could not be sent', access_policy_changed: 'Access settings updated', } const legacyPolicyLabels: Record = { enabled: 'Account access check', enableForRegistration: 'WordPress registration check', enableForWooCommerceRegistration: 'WooCommerce registration check', verificationMode: 'Verification method', skipRoles: 'Roles exempt from verification', registrationFormMode: 'Registration form handling', } const mailLabels: Record = { queued: 'Notification email queued', sending: 'Notification email is being sent', accepted: 'Notification email submitted for sending. Delivery is not confirmed.', failed: 'Notification email could not be sent', skipped: 'Notification email was not sent', } function validTimezone(timezone: string) { if (/^[+-](?:0\d|1[0-4]):[0-5]\d$/.test(timezone)) return timezone try { new Intl.DateTimeFormat(undefined, { timeZone: timezone || 'UTC' }); return timezone || 'UTC' } catch { return 'UTC' } } export function activityTimezoneLabel(timezone: string) { const zone = validTimezone(timezone) return /^[+-]/.test(zone) ? /[1-9]/.test(zone) ? `UTC${zone}` : 'UTC' : zone } export function formatActivityDate(value: string, timezone = 'UTC') { const iso = value.replace(' ', 'T') const date = new Date(/(?:Z|[+-]\d{2}:\d{2})$/i.test(iso) ? iso : `${iso}Z`) if (Number.isNaN(date.getTime())) return 'Date unavailable' const zone = validTimezone(timezone) // WordPress can return a fixed offset instead of an IANA name. Support it // without relying on newer browsers accepting offsets in Intl.timeZone. const offset = zone.match(/^([+-])(\d{2}):(\d{2})$/) if (offset) date.setUTCMinutes(date.getUTCMinutes() + (offset[1] === '+' ? 1 : -1) * (Number(offset[2]) * 60 + Number(offset[3]))) return date.toLocaleString(undefined, { timeZone: offset ? 'UTC' : zone, dateStyle: 'medium', timeStyle: 'short' }) } export function policyChanges(item: RegistrationEvent) { if (item.event !== 'access_policy_changed') return [] if (Array.isArray(item.policy_changes)) return item.policy_changes.filter(change => typeof change === 'string' && change.trim()) // Backward compatible with an older Pro provider: never expose option keys. const keys = item.note.split(',').map(key => key.trim()) return keys.every(key => legacyPolicyLabels[key]) ? keys.map(key => `${legacyPolicyLabels[key]} changed`) : ['Account access settings changed.'] } export function ActivityHistory({ items, timezone }: { items: RegistrationEvent[]; timezone: string }) { if (!items.length) return

No activity yet. New registrations, account decisions and access setting changes will appear here.

return
    {items.map(item => { const changes = policyChanges(item) return
  1. {eventLabels[item.event] || 'Registration activity recorded'}

    By {item.actor || 'System'}{Number(item.user_id) > 0 && ` ยท Account #${item.user_id}`}

    {changes.length > 0 &&
      {changes.map((change, index) =>
    • {change}
    • )}
    } {item.note && item.event !== 'access_policy_changed' &&

    Internal note: {item.note}

    } {item.applicant_message &&

    Message to applicant: {item.applicant_message}

    } {item.mail_status && item.mail_status !== 'none' &&

    {mailLabels[item.mail_status] || 'Notification email status unavailable'}

    }
  2. })}
}