auth-yes/ui/components/admin/drawers/InviteDrawer.tsx
Tyler Gillispie 2d25e33515 wip(ui): preserve in-flight admin drawers and scripts extraction
- Preserves extracted components: AppDrawer, InviteDrawer, RoleEditorDrawer
- Preserves extracted SSR JSX scripts: AdminAppsScript, AdminInvitesScript, AdminRolesScript
- Parks Phase 2 task spec in tasks/wip/
- Checkpoint preserved AS IS from session 3615485303186036917
2026-08-26 10:37:57 -07:00

188 lines
6.8 KiB
XML

import { AdminModal } from "../AdminModal.tsx";
export const InviteDrawer = ({ apps }: { apps: any[] }) => {
return (
<AdminModal
id="createInviteModal"
title="Generate User Onboarding Token"
onClose="toggleCreateInviteForm()"
>
<p style="color: var(--text-secondary); font-size: 0.9rem; margin: 0 0 1.25rem 0;">
Configure time bounds, usage capacity, role assignments, and initial
account activation status.
</p>
<form id="createInviteForm" onsubmit="handleCreateInvite(event)">
{/* Row 1: Type & Target App */}
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 1rem; margin-bottom: 1rem;">
<div>
<label style="display: block; font-weight: 600; margin-bottom: 0.35rem; font-size: 0.85rem; color: var(--text-secondary);">
Token Provisioning Type *
</label>
<select
id="inviteType"
onchange="handleInviteTypeChange()"
style="width: 100%;"
>
<option value="site_scoped">
Type 2: Site-Scoped Token (Pre-Authorized for App)
</option>
<option value="global_admin">
Type 1: Global Admin Token (Full System Access)
</option>
<option value="open_pending">
Type 3: General Open Token (Unassigned Access)
</option>
</select>
</div>
<div id="appSelectContainer">
<label style="display: block; font-weight: 600; margin-bottom: 0.35rem; font-size: 0.85rem; color: var(--text-secondary);">
Target Application *
</label>
<select
id="inviteAppId"
onchange="updateInviteRoleOptions()"
style="width: 100%;"
>
{apps.map((app) => (
<option value={app.id}>
{app.name} ({app.spiffe_id})
</option>
))}
</select>
</div>
</div>
{/* Row 2: Usage Limits & Assigned Role */}
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem; margin-bottom: 1rem;">
<div>
<label style="display: block; font-weight: 600; margin-bottom: 0.35rem; font-size: 0.85rem; color: var(--text-secondary);">
Usage Capacity *
</label>
<select
id="inviteUsageType"
onchange="handleUsageTypeChange()"
style="width: 100%;"
>
<option value="single">
Single-Use (1 Person - Max Security)
</option>
<option value="limited">
Limited Multi-Use (Cap at N People)
</option>
<option value="unlimited">
Unlimited Time-Bound (Campaign / Beta)
</option>
</select>
</div>
<div id="maxUsesContainer" style="display: none;">
<label style="display: block; font-weight: 600; margin-bottom: 0.35rem; font-size: 0.85rem; color: var(--text-secondary);">
Max Registrations *
</label>
<input
type="number"
id="inviteMaxUses"
value="5"
min="2"
max="1000"
style="width: 100%;"
/>
</div>
<div id="roleSelectContainer">
<label style="display: block; font-weight: 600; margin-bottom: 0.35rem; font-size: 0.85rem; color: var(--text-secondary);">
Assigned Role *
</label>
<select
id="inviteRole"
style="width: 100%;"
>
{/* Dynamically populated */}
</select>
</div>
</div>
{/* Row 3: Expiration, Custom Code, Activation Toggle */}
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 1rem; margin-bottom: 1.25rem; align-items: flex-end;">
<div>
<label style="display: block; font-weight: 600; margin-bottom: 0.35rem; font-size: 0.85rem; color: var(--text-secondary);">
Expires In (Days)
</label>
<input
type="number"
id="inviteExpiresInDays"
value="7"
min="1"
max="30"
style="width: 100%;"
/>
</div>
<div>
<label style="display: block; font-weight: 600; margin-bottom: 0.35rem; font-size: 0.85rem; color: var(--text-secondary);">
Custom Code (Optional)
</label>
<input
type="text"
id="inviteCustomCode"
placeholder="Leave blank to auto-generate"
style="width: 100%;"
/>
</div>
<div style="padding-bottom: 0.5rem;">
<label style="display: flex; align-items: center; gap: 0.5rem; font-size: 0.85rem; font-weight: 600; cursor: pointer; color: var(--text-primary);">
<input
type="checkbox"
id="inviteAutoActivate"
checked
style="width: 18px; height: 18px;"
/>
Auto-Activate Account
</label>
</div>
</div>
<div style="display: flex; gap: 0.75rem;">
<button type="submit" class="btn-primary" style="min-height: 42px;">
Create Invite Token
</button>
<button
type="button"
class="btn-outline"
onclick="toggleCreateInviteForm()"
style="min-height: 42px;"
>
Cancel
</button>
</div>
</form>
<div
id="generated-token-banner"
style="display: none; margin-top: 1.25rem; padding: 1rem; background: var(--success-bg); border: 1px solid var(--success-border); border-radius: var(--radius-md);"
>
<strong style="color: var(--success-text);">
Token Created Successfully!
</strong>
<div style="margin-top: 0.5rem; display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap;">
<code
id="generatedTokenUrl"
style="padding: 0.5rem 0.75rem; background: var(--surface-card); border: 1px solid var(--border-subtle); border-radius: var(--radius-sm); font-size: 0.85rem; flex: 1; min-width: 220px; word-break: break-all; font-family: monospace; color: var(--primary);"
>
</code>
<button
type="button"
class="btn-primary"
onclick="copyGeneratedTokenUrl()"
>
Copy Link
</button>
</div>
</div>
</AdminModal>
);
};