Phase 2 Admin Drawers & Scripts execution: - Extract `AppDrawer`, `InviteDrawer`, `RoleEditorDrawer`, and `GrantDrawer`. - Extract `AdminAppsScript`, `AdminInvitesScript`, `AdminRolesScript`, and `AdminUserDetailsScript`. - Hook extracted components into their respective pages. - Format `AdminRolesPage.tsx` and all modified files using `deno fmt`. Co-authored-by: mrteye <1945243+mrteye@users.noreply.github.com>
188 lines
6.8 KiB
XML
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>
|
|
);
|
|
};
|