'use client' import { Badge } from '@/components/ui/badge' import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs' import { certificationStatus, CERT_STATUS_LABEL, type CertStatus, } from '@/lib/certifications-logic' import { SIGNUP_STATUS_LABEL, SESSION_STATUS_LABEL } from '@/lib/classes-logic' import { RESERVATION_STATUS_LABEL } from '@/lib/equipment-logic' import { presenceStatus } from '@/lib/presence-logic' import { DuesCard } from '@/components/billing/dues-card' import { ProfileForm, type ProfileInitial } from './profile-form' import { CancelAction } from './cancel-action' import { cancelMySignup, cancelReservation } from '@/lib/actions' const STATUS_VARIANT: Record = { active: 'default', expiring_soon: 'outline', expired: 'outline', revoked: 'outline', } export type Grant = { id: string granted_at: string expires_at: string | null revoked_at: string | null revoked_reason: string | null note: string | null certifications: { name: string; description: string | null; validity_months: number | null } | null } export type ClassSignup = { id: string status: string attended: boolean signed_up_at: string class_sessions: | { id: string; starts_at: string; ends_at: string | null; location: string | null; status: string; classes: { title: string } | { title: string }[] | null } | { id: string; starts_at: string; ends_at: string | null; location: string | null; status: string; classes: { title: string } | { title: string }[] | null }[] | null } export type Reservation = { id: string starts_at: string ends_at: string status: string notes: string | null equipment: { name: string; location: string | null } | { name: string; location: string | null }[] | null } export type MyCard = { id: string; card_type: string; label: string | null; is_active: boolean; last4: string } export type MyVisit = { id: string; checked_in_at: string; checked_out_at: string | null; is_host: boolean; check_in_note: string | null; check_out_note: string | null } export type MyNotif = { id: string; type: string; subject: string; status: string; createdAt: string; sentAt: string | null } export type MyPayment = { id: string; amount: number; currency: string; platform: string; description: string | null; status: string; date: string } export type HeldPermission = { code: string; label: string } export type Billing = { status: string | null; currentPeriodEnd: string | null; hasCustomer: boolean } | null type Props = { profile: ProfileInitial grants: Grant[] held: HeldPermission[] billing: Billing payments: MyPayment[] notifs: MyNotif[] classSignups: ClassSignup[] reservations: Reservation[] myCards: MyCard[] myVisits: MyVisit[] } const sectionH = 'font-mono text-[10px] uppercase tracking-widest text-muted-foreground mb-3' export function MePortalClient({ profile, grants, held, billing, payments, notifs, classSignups, reservations, myCards, myVisits, }: Props) { const sessionOf = (cs: ClassSignup) => Array.isArray(cs.class_sessions) ? cs.class_sessions[0] : cs.class_sessions const equipOf = (r: Reservation) => (Array.isArray(r.equipment) ? r.equipment[0] : r.equipment) const titleOf = (cs: ClassSignup) => { const sess = sessionOf(cs) const c = sess ? (Array.isArray(sess.classes) ? sess.classes[0] : sess.classes) : null return c?.title ?? 'Class' } return (
Profile Membership Activity

Profile

My permissions

{held.length === 0 ? (

You have the standard member access for this space. No extra permissions are granted to your role.

) : (
    {held.map(p => (
  • {p.label}
  • ))}
)}

Permissions are set per role by a space admin. This is a read-only view.

Dues

My payments

{payments.length === 0 ? (

No payments recorded for you yet.

) : (
    {payments.map(p => (
  • {p.currency} {p.amount.toFixed(2)}

    {new Date(p.date).toLocaleDateString()} · {p.platform} {p.description ? ` · ${p.description}` : ''}

    {p.status}
  • ))}
)}

Certifications

{grants.length === 0 ? (

You have no certifications yet. An instructor can award one to you.

) : (
    {grants.map(g => { const status = certificationStatus(g) return (
  • {g.certifications?.name ?? 'Certification'} {CERT_STATUS_LABEL[status]}
    {g.certifications?.description && (

    {g.certifications.description}

    )}

    granted {new Date(g.granted_at).toLocaleDateString()} {g.expires_at ? ` · expires ${new Date(g.expires_at).toLocaleDateString()}` : ' · no expiry'} {g.revoked_at ? ` · revoked ${new Date(g.revoked_at).toLocaleDateString()}` : ''}

    {g.revoked_at && g.revoked_reason && (

    Reason: {g.revoked_reason}

    )}
  • ) })}
)}

Notifications

{notifs.length === 0 ? (

No notifications yet. Dues receipts and payment alerts will show up here.

) : (
    {notifs.map(n => (
  • {n.subject}

    {new Date(n.createdAt).toLocaleString()} {n.sentAt ? ` · sent ${new Date(n.sentAt).toLocaleDateString()}` : ''}

    {n.status === 'sent' ? 'Sent' : n.status === 'failed' ? 'Failed' : 'Queued'}
  • ))}
)}

My classes

{classSignups.length === 0 ? (

You have not signed up for any classes. Browse what’s on at{' '} Classes.

) : (
    {classSignups.map(cs => { const sess = sessionOf(cs) return (
  • {titleOf(cs)} {SIGNUP_STATUS_LABEL[cs.status] ?? cs.status} {cs.attended && Attended}
    {sess && (

    {new Date(sess.starts_at).toLocaleString()} {sess.location ? ` · ${sess.location}` : ''} {' · '} {SESSION_STATUS_LABEL[sess.status] ?? sess.status}

    )}
    {sess && (cs.status === 'registered' || cs.status === 'waitlisted') && ( cancelMySignup({ sessionId: sess.id })} /> )}
  • ) })}
)}

My equipment reservations

{reservations.length === 0 ? (

You have no reservations. Browse what you can reserve at{' '} Equipment.

) : (
    {reservations.map(r => { const eq = equipOf(r) return (
  • {eq?.name ?? 'Equipment'} {RESERVATION_STATUS_LABEL[r.status] ?? r.status}

    {new Date(r.starts_at).toLocaleString()} – {new Date(r.ends_at).toLocaleString()} {eq?.location ? ` · ${eq.location}` : ''}

    {r.notes &&

    {r.notes}

    }
    {r.status === 'reserved' && ( cancelReservation({ reservationId: r.id })} /> )}
  • ) })}
)}

My access cards

{myCards.length === 0 ? (

No access cards are registered to you. A door manager can add one.

) : (
    {myCards.map(c => (
  • •••• {c.last4} {c.card_type} {c.label && {c.label}}
    {c.is_active ? 'Active' : 'Inactive'}
  • ))}
)}

Only the last 4 digits are shown. The full card number is never displayed here.

My recent visits

{myVisits.length === 0 ? (

No visits yet. Check in from the dashboard when you are at the space.

) : (
    {myVisits.map(v => { const st = presenceStatus(v.checked_in_at, v.checked_out_at) return (
  • {new Date(v.checked_in_at).toLocaleString()} {v.is_host && Host} {st === 'present' && here now} {st === 'stale' && no checkout}
    {(v.check_in_note || v.check_out_note) && (

    {v.check_in_note ? `in: ${v.check_in_note}` : ''} {v.check_in_note && v.check_out_note ? ' · ' : ''} {v.check_out_note ? `out: ${v.check_out_note}` : ''}

    )}
    {v.checked_out_at ? `out ${new Date(v.checked_out_at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}` : 'open'}
  • ) })}
)}
) }