// Team — URL `/dashboard/team`. Lists the tenant's invites live // (`invites.list`) and creates them (`invites.create`). Creating an invite // spends a `seats` entitlement on the api, so on the free plan (1 seat) the // FIRST invite trips the same typed `EntitlementExceeded` — we branch on it and // point the user at the Billing page to add a seat. import type { ReactNode } from 'react' import { useState, type FormEvent } from 'react' import type { PageMeta } from '@voltro/web' import { T, useT } from '@voltro/i18n' import { getCatalog } from '../../../locales/index' import { isEntitlementExceeded, useMutation, useSubscription } from '../../../lib/api' export const renderMode = 'ssr' as const export const meta = ({ locale }: { readonly locale: string }): PageMeta => ({ title: getCatalog(locale)['meta.team.title'], }) const STATUS_KEY: Record = { pending: 'team.status.pending', accepted: 'team.status.accepted', revoked: 'team.status.revoked', } export default function Team(): ReactNode { const { data: live } = useSubscription('invites.list') const { mutate, pending } = useMutation('invites.create') const invites = live ?? [] const [email, setEmail] = useState('') const [outOfSeats, setOutOfSeats] = useState(false) const placeholder = useT('team.invite.placeholder') const onInvite = (e: FormEvent): void => { e.preventDefault() const trimmed = email.trim() if (trimmed === '') return void mutate({ email: trimmed }) .then(() => { setEmail(''); setOutOfSeats(false) }) .catch((err: unknown) => { if (isEntitlementExceeded(err)) setOutOfSeats(true) }) } return (

setEmail(e.target.value)} />
{outOfSeats ?

: null} {invites.length === 0 ? (

) : ( )}
) }