feat(operator): partner-style tenant provisioning wizard + admin invite
ci / tc_portal (push) Has been skipped
ci / changes (push) Successful in 4s
ci / tc_booking (push) Has been skipped
ci / tc_website (push) Has been skipped
ci / tc_platform_api (push) Successful in 22s
ci / tc_operator (push) Successful in 24s
ci / build_portal (push) Has been skipped
ci / build_booking (push) Has been skipped
ci / test_platform_api (push) Successful in 32s
ci / build_operator (push) Successful in 31s
ci / build_platform_api (push) Successful in 15s
ci / deploy (push) Successful in 41s
ci / tc_portal (push) Has been skipped
ci / changes (push) Successful in 4s
ci / tc_booking (push) Has been skipped
ci / tc_website (push) Has been skipped
ci / tc_platform_api (push) Successful in 22s
ci / tc_operator (push) Successful in 24s
ci / build_portal (push) Has been skipped
ci / build_booking (push) Has been skipped
ci / test_platform_api (push) Successful in 32s
ci / build_operator (push) Successful in 31s
ci / build_platform_api (push) Successful in 15s
ci / deploy (push) Successful in 41s
The minimal create modal silently dropped adminName/adminEmail — the invite only existed in the partner wizard's server path. Operator now gets the same 5-step wizard UX (organization, domain, first admin, plan with live price catalog, review) composed client-side: POST /tenants creates + provisions, then POST /users/invite-tenant-admin (new, operator-only — lives in UsersModule because UsersModule already imports TenantsModule and the reverse would be circular) runs the same inviteTenantAdmin flow the partner gets, and the result view hands over the single-use recovery link or temp password. Tenant detail page gains an Invite admin action for retries/successors. PLATFORM_TENANT_SLUG back to 'dezky' (the recreated company tenant) + config-rev bump to roll platform-api.
This commit is contained in:
@@ -81,6 +81,10 @@ const INTEGRATION_TONE = {
|
||||
const INTEGRATIONS = ['authentik', 'stalwart', 'ocis'] as const
|
||||
type IntegrationKey = (typeof INTEGRATIONS)[number]
|
||||
|
||||
// Invite (or re-invite) the tenant's admin — same flow the create wizard
|
||||
// runs; reusable whenever the first invite failed or someone new takes over.
|
||||
const inviteAdminOpen = ref(false)
|
||||
|
||||
// ── Danger-zone state ─────────────────────────────────────────────────────
|
||||
const dangerAction = ref<'suspend' | 'resume' | 'delete' | 'purge' | null>(null)
|
||||
const dangerBusy = ref(false)
|
||||
@@ -141,6 +145,10 @@ async function reconcile() {
|
||||
<template #actions>
|
||||
<Badge :tone="STATUS_TONE[tenant.status]" dot>{{ tenant.status }}</Badge>
|
||||
<Badge tone="neutral">{{ tenant.plan }}</Badge>
|
||||
<UiButton variant="secondary" @click="inviteAdminOpen = true">
|
||||
<template #leading><UiIcon name="plus" :size="13" /></template>
|
||||
Invite admin
|
||||
</UiButton>
|
||||
<UiButton variant="secondary" @click="impersonate.open(tenant)">
|
||||
<template #leading><UiIcon name="key" :size="13" /></template>
|
||||
Impersonate
|
||||
@@ -392,6 +400,14 @@ async function reconcile() {
|
||||
</p>
|
||||
<p v-if="dangerError" class="danger-err">{{ dangerError }}</p>
|
||||
</ConfirmDialog>
|
||||
|
||||
<InviteTenantAdminModal
|
||||
v-if="tenant"
|
||||
:open="inviteAdminOpen"
|
||||
:tenant-slug="tenant.slug"
|
||||
:tenant-name="tenant.name"
|
||||
@close="inviteAdminOpen = false"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user