ui(step-5): update roles/runtime-roles pages to reference layout

- roles/create: navy submit button, proper form inputs, data-table
- roles/templates: table-card, navy Create button
- runtime-roles/index: fix oversized heading, data-table, navy colors
- runtime-roles/new: white header shell, proper form styling

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Ashwin Kumar 2026-03-24 05:26:39 +01:00
parent 3ffed6c813
commit 3b98609cb5
4 changed files with 249 additions and 234 deletions

View file

@ -93,127 +93,131 @@ export default function CreateInternalRolePage() {
return (
<AdminShell>
<div class="mb-6 flex items-start justify-between gap-4">
<div>
<h1 class="text-2xl font-bold text-gray-900">Create Internal Role</h1>
<p class="mt-1 text-sm text-gray-500">Create a new internal role and choose what it can access.</p>
<div class="flex flex-col -mx-6 -mt-6 min-h-full">
<div class="bg-white border-b border-gray-200 px-6 py-4">
<h1 class="text-xl font-semibold text-gray-900">Create Internal Role</h1>
<p class="text-sm text-gray-500 mt-0.5">Create a new internal role and choose what it can access.</p>
</div>
<A class="inline-flex items-center rounded-lg border border-gray-200 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 transition-colors" href="/admin/roles">Back to Roles</A>
</div>
<nav class="hidden" aria-label="Role Management Navigation">
<A class="hidden" href="/admin/roles">Internal Roles</A>
<A class="hidden" href="/admin/runtime-roles">External Runtime Roles</A>
<A class="hidden" href="/admin/onboarding-schemas">Onboarding Schemas</A>
</nav>
<div class="flex-1 p-6">
<nav class="hidden" aria-label="Role Management Navigation">
<A class="hidden" href="/admin/roles">Internal Roles</A>
<A class="hidden" href="/admin/runtime-roles">External Runtime Roles</A>
<A class="hidden" href="/admin/onboarding-schemas">Onboarding Schemas</A>
</nav>
<Show when={error()}>
<div class="mb-4 rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700">{error()}</div>
</Show>
<Show when={error()}>
<div class="mb-4 rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700">{error()}</div>
</Show>
{/* Role Details */}
<div class="mb-6 rounded-xl border border-gray-200 bg-white p-6 shadow-sm">
<h3>Role Basics</h3>
<p>Start by giving this role a clear name.</p>
<div class="mt-4 grid grid-cols-1 gap-4 sm:grid-cols-2">
<div class="field">
<label>Role Name <span style="color:#ef4444">*</span></label>
<input
value={roleName()}
onInput={(e) => setRoleName(e.currentTarget.value)}
placeholder="e.g. Customer Support Rep"
/>
{/* Role Details */}
<div class="mb-6 rounded-xl border border-gray-200 bg-white p-6 shadow-sm">
<h3>Role Basics</h3>
<p>Start by giving this role a clear name.</p>
<div class="mt-4 grid grid-cols-1 gap-4 sm:grid-cols-2">
<div class="field">
<label class="mb-1.5 block text-sm font-medium text-gray-700">Role Name <span class="text-red-500">*</span></label>
<input
class="w-full rounded-lg border border-gray-200 px-3 py-2 text-sm outline-none focus:border-[#0a1d37] focus:ring-1 focus:ring-[#0a1d37]"
value={roleName()}
onInput={(e) => setRoleName(e.currentTarget.value)}
placeholder="e.g. Customer Support Rep"
/>
</div>
<div class="field">
<label class="mb-1.5 block text-sm font-medium text-gray-700">Description</label>
<input
class="w-full rounded-lg border border-gray-200 px-3 py-2 text-sm outline-none focus:border-[#0a1d37] focus:ring-1 focus:ring-[#0a1d37]"
value={description()}
onInput={(e) => setDescription(e.currentTarget.value)}
placeholder="Short description of this role"
/>
</div>
</div>
</div>
<div class="field">
<label>Description</label>
<input
value={description()}
onInput={(e) => setDescription(e.currentTarget.value)}
placeholder="Short description of this role"
/>
</div>
</div>
</div>
{/* Module Access */}
<div class="mb-6 rounded-xl border border-gray-200 bg-white p-6 shadow-sm">
<h3>Area Access</h3>
<p>Select which areas this role can access. You can set permissions for selected areas below.</p>
<Show when={permissions.loading}>
<p class="notice">Loading available areas...</p>
</Show>
<Show when={!permissions.loading && allModules().length > 0}>
<div class="mt-3 flex flex-wrap gap-2">
{allModules().map((mod) => (
<button
type="button"
class={`flex cursor-pointer items-center gap-1.5 rounded-full border border-gray-200 bg-white px-3 py-1.5 text-xs font-medium text-gray-600 hover:border-gray-300 ${assignedModules().includes(mod) ? 'selected' : ''}`}
onClick={() => toggleModule(mod)}
>
<span style={`width:14px;height:14px;border-radius:3px;border:2px solid ${assignedModules().includes(mod) ? '#c2410c' : '#cbd5e1'};background:${assignedModules().includes(mod) ? '#c2410c' : '#fff'};flex-shrink:0;display:inline-block`} />
{mod}
</button>
))}
{/* Module Access */}
<div class="mb-6 rounded-xl border border-gray-200 bg-white p-6 shadow-sm">
<h3>Area Access</h3>
<p>Select which areas this role can access. You can set permissions for selected areas below.</p>
<Show when={permissions.loading}>
<p class="notice">Loading available areas...</p>
</Show>
<Show when={!permissions.loading && allModules().length > 0}>
<div class="mt-3 flex flex-wrap gap-2">
{allModules().map((mod) => (
<button
type="button"
class={`flex cursor-pointer items-center gap-1.5 rounded-full border border-gray-200 bg-white px-3 py-1.5 text-xs font-medium text-gray-600 hover:border-gray-300 ${assignedModules().includes(mod) ? 'selected' : ''}`}
onClick={() => toggleModule(mod)}
>
<span class={`inline-block w-3.5 h-3.5 rounded-[3px] border-2 flex-shrink-0 ${assignedModules().includes(mod) ? 'border-[#0a1d37] bg-[#0a1d37]' : 'border-slate-300 bg-white'}`} />
{mod}
</button>
))}
</div>
</Show>
<Show when={!permissions.loading && allModules().length === 0}>
<p class="notice">No areas available.</p>
</Show>
</div>
</Show>
<Show when={!permissions.loading && allModules().length === 0}>
<p class="notice">No areas available.</p>
</Show>
</div>
{/* Permission Table */}
<Show when={assignedModules().length > 0}>
<div class="mb-6 rounded-xl border border-gray-200 bg-white p-6 shadow-sm">
<h3>Permissions</h3>
<p>Choose what this role can do in each selected area.</p>
<div class="overflow-x-auto">
<table class="w-full text-sm">
<thead>
<tr>
<th style="width:45%">Area</th>
<th style="width:11%">No Access</th>
<th style="width:11%">Read</th>
<th style="width:11%">Create</th>
<th style="width:11%">Update</th>
<th style="width:11%">Delete</th>
</tr>
</thead>
<tbody>
{assignedModules().sort().map((mod) => {
const perms = permsByModule()[mod] || [];
const actionMap: Record<string, string> = {};
perms.forEach((p) => { actionMap[p.action] = p.id; });
const hasRead = !!actionMap['Read'] && permissionIds().includes(actionMap['Read']);
const hasCreate = !!actionMap['Create'] && permissionIds().includes(actionMap['Create']);
const hasUpdate = !!actionMap['Update'] && permissionIds().includes(actionMap['Update']);
const hasDelete = !!actionMap['Delete'] && permissionIds().includes(actionMap['Delete']);
const noAccess = !hasRead && !hasCreate && !hasUpdate && !hasDelete;
return (
{/* Permission Table */}
<Show when={assignedModules().length > 0}>
<div class="mb-6 rounded-xl border border-gray-200 bg-white p-6 shadow-sm">
<h3>Permissions</h3>
<p>Choose what this role can do in each selected area.</p>
<div class="table-card overflow-x-auto">
<table data-table class="w-full text-sm">
<thead>
<tr>
<td style="font-weight:500">{mod}</td>
<td><input type="checkbox" checked={noAccess} disabled aria-label={`${mod} no access`} /></td>
<td>{actionMap['Read'] ? <input type="checkbox" checked={hasRead} onChange={() => togglePermission(actionMap['Read'])} aria-label={`${mod} read`} /> : <span style="color:#cbd5e1"></span>}</td>
<td>{actionMap['Create'] ? <input type="checkbox" checked={hasCreate} onChange={() => togglePermission(actionMap['Create'])} aria-label={`${mod} create`} /> : <span style="color:#cbd5e1"></span>}</td>
<td>{actionMap['Update'] ? <input type="checkbox" checked={hasUpdate} onChange={() => togglePermission(actionMap['Update'])} aria-label={`${mod} update`} /> : <span style="color:#cbd5e1"></span>}</td>
<td>{actionMap['Delete'] ? <input type="checkbox" checked={hasDelete} onChange={() => togglePermission(actionMap['Delete'])} aria-label={`${mod} delete`} /> : <span style="color:#cbd5e1"></span>}</td>
<th style="width:45%">Area</th>
<th style="width:11%">No Access</th>
<th style="width:11%">Read</th>
<th style="width:11%">Create</th>
<th style="width:11%">Update</th>
<th style="width:11%">Delete</th>
</tr>
);
})}
</tbody>
</table>
</thead>
<tbody>
{assignedModules().sort().map((mod) => {
const perms = permsByModule()[mod] || [];
const actionMap: Record<string, string> = {};
perms.forEach((p) => { actionMap[p.action] = p.id; });
const hasRead = !!actionMap['Read'] && permissionIds().includes(actionMap['Read']);
const hasCreate = !!actionMap['Create'] && permissionIds().includes(actionMap['Create']);
const hasUpdate = !!actionMap['Update'] && permissionIds().includes(actionMap['Update']);
const hasDelete = !!actionMap['Delete'] && permissionIds().includes(actionMap['Delete']);
const noAccess = !hasRead && !hasCreate && !hasUpdate && !hasDelete;
return (
<tr class="hover:bg-slate-50">
<td class="font-semibold text-slate-900">{mod}</td>
<td><input type="checkbox" checked={noAccess} disabled aria-label={`${mod} no access`} /></td>
<td>{actionMap['Read'] ? <input type="checkbox" checked={hasRead} onChange={() => togglePermission(actionMap['Read'])} aria-label={`${mod} read`} /> : <span class="text-slate-300"></span>}</td>
<td>{actionMap['Create'] ? <input type="checkbox" checked={hasCreate} onChange={() => togglePermission(actionMap['Create'])} aria-label={`${mod} create`} /> : <span class="text-slate-300"></span>}</td>
<td>{actionMap['Update'] ? <input type="checkbox" checked={hasUpdate} onChange={() => togglePermission(actionMap['Update'])} aria-label={`${mod} update`} /> : <span class="text-slate-300"></span>}</td>
<td>{actionMap['Delete'] ? <input type="checkbox" checked={hasDelete} onChange={() => togglePermission(actionMap['Delete'])} aria-label={`${mod} delete`} /> : <span class="text-slate-300"></span>}</td>
</tr>
);
})}
</tbody>
</table>
</div>
</div>
</Show>
{/* Save */}
<div class="flex justify-end gap-3 mt-2">
<A class="rounded-lg border border-gray-200 px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 transition-colors" href="/admin/roles">Cancel</A>
<button
class="rounded-lg bg-[#0a1d37] px-6 py-2 text-sm font-medium text-white hover:bg-[#0f2a4e] transition-colors disabled:opacity-60"
onClick={handleSave}
disabled={saving() || !roleName().trim()}
>
{saving() ? 'Creating...' : 'Create Role'}
</button>
</div>
</div>
</Show>
{/* Save */}
<div style="display:flex;justify-content:flex-end;margin-top:8px">
<button
class="inline-flex items-center rounded-lg bg-[#fd6216] px-4 py-2 text-sm font-semibold text-white hover:bg-orange-600 transition-colors"
onClick={handleSave}
disabled={saving() || !roleName().trim()}
>
{saving() ? 'Creating...' : 'Create Role'}
</button>
</div>
</AdminShell>
);

View file

@ -36,57 +36,63 @@ export default function RoleTemplatesPage() {
return (
<AdminShell>
<div class="mb-6 flex items-start justify-between gap-4">
<div>
<h1 class="text-2xl font-bold text-gray-900">Role Templates</h1>
<p class="mt-1 text-sm text-gray-500">Starter role presets for faster internal role creation and cloning.</p>
<div class="flex flex-col -mx-6 -mt-6 min-h-full">
<div class="bg-white border-b border-gray-200 px-6 py-4">
<div class="flex items-center justify-between">
<div>
<h1 class="text-xl font-semibold text-gray-900">Role Templates</h1>
<p class="text-sm text-gray-500 mt-0.5">Starter role presets for faster internal role creation and cloning.</p>
</div>
<A class="inline-flex items-center rounded-lg bg-[#0a1d37] px-4 py-2 text-sm font-semibold text-white hover:bg-[#0f2a4e] transition-colors" href="/admin/roles/create">Create Internal Role</A>
</div>
</div>
<A class="inline-flex items-center rounded-lg bg-[#fd6216] px-4 py-2 text-sm font-semibold text-white hover:bg-orange-600 transition-colors" href="/admin/roles/create">Create Internal Role</A>
</div>
<section class="rounded-xl border border-gray-200 bg-white shadow-sm" style="padding:0;overflow:hidden">
<div style="display:flex;align-items:center;justify-content:space-between;padding:14px 18px;border-bottom:1px solid #e2e8f0">
<h2 style="margin:0;font-size:16px">Available Templates</h2>
<span style="font-size:12px;color:#64748b">{count()} template{count() !== 1 ? 's' : ''}</span>
<div class="flex-1 p-6">
<div class="table-card">
<div class="flex items-center justify-between px-4 py-3 border-b border-gray-200">
<h2 class="text-sm font-semibold text-gray-900">Available Templates</h2>
<span class="text-xs text-slate-500">{count()} template{count() !== 1 ? 's' : ''}</span>
</div>
<div class="overflow-x-auto">
<table data-table class="w-full text-sm">
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th>Code</th>
<th class="text-right">Actions</th>
</tr>
</thead>
<tbody>
<Show when={templates.loading}>
<tr><td colspan="4" class="text-center px-8 py-8 text-slate-500">Loading templates...</td></tr>
</Show>
<Show when={!templates.loading && count() === 0}>
<tr><td colspan="4" class="text-center px-8 py-8 text-slate-400">No templates available yet.</td></tr>
</Show>
<Show when={!templates.loading && count() > 0}>
<For each={templates()}>
{(item) => (
<tr class="hover:bg-slate-50">
<td class="font-semibold text-slate-900">{item.name}</td>
<td class="text-slate-500">{item.description || '—'}</td>
<td class="text-slate-500 font-mono">{item.code || '—'}</td>
<td>
<div class="flex items-center justify-end gap-1">
<A class="inline-flex items-center rounded-lg border border-gray-200 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 transition-colors" href={`/admin/roles/${item.id}`}>View</A>
<A class="inline-flex items-center rounded-lg border border-gray-200 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 transition-colors" href="/admin/roles/create">Use As Base</A>
</div>
</td>
</tr>
)}
</For>
</Show>
</tbody>
</table>
</div>
</div>
</div>
<div class="overflow-x-auto">
<table class="w-full text-sm">
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th>Code</th>
<th class="text-right">Actions</th>
</tr>
</thead>
<tbody>
<Show when={templates.loading}>
<tr><td colspan="4" style="text-align:center;padding:32px;color:#64748b">Loading templates...</td></tr>
</Show>
<Show when={!templates.loading && count() === 0}>
<tr><td colspan="4" style="text-align:center;padding:32px;color:#94a3b8">No templates available yet.</td></tr>
</Show>
<Show when={!templates.loading && count() > 0}>
<For each={templates()}>
{(item) => (
<tr>
<td style="font-weight:600;color:#0f172a">{item.name}</td>
<td style="color:#475569">{item.description || '—'}</td>
<td style="color:#64748b;font-family:ui-monospace,SFMono-Regular,Menlo,monospace">{item.code || '—'}</td>
<td>
<div class="flex items-center justify-end gap-1">
<A class="inline-flex items-center rounded-lg border border-gray-200 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 transition-colors" href={`/admin/roles/${item.id}`}>View</A>
<A class="inline-flex items-center rounded-lg border border-gray-200 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 transition-colors" href="/admin/roles/create">Use As Base</A>
</div>
</td>
</tr>
)}
</For>
</Show>
</tbody>
</table>
</div>
</section>
</div>
</AdminShell>
);
}

View file

@ -76,67 +76,71 @@ export default function RuntimeRolesPage() {
return (
<AdminShell>
<div class="mb-8">
<h1 class="text-[52px] font-extrabold tracking-tight text-[#071b3d] sm:text-[36px] lg:text-[52px]">Roles Management</h1>
<p class="mt-2 text-[17px] text-[#4b5563]">Configure and maintain external system roles and access privileges.</p>
</div>
<section class="overflow-hidden rounded-[22px] border border-[#d8dbe5] bg-white shadow-[0_14px_28px_-20px_rgba(15,23,42,0.35)]">
<div class="overflow-x-auto">
<table class="w-full min-w-[860px] text-sm">
<thead>
<tr class="bg-[#071b3d] text-white">
<th class="px-8 py-5 text-left text-[14px] font-semibold uppercase tracking-[0.08em]">ID</th>
<th class="px-8 py-5 text-left text-[14px] font-semibold uppercase tracking-[0.08em]">Name</th>
<th class="px-8 py-5 text-left text-[14px] font-semibold uppercase tracking-[0.08em]">Issue Type</th>
<th class="px-8 py-5 text-center text-[14px] font-semibold uppercase tracking-[0.08em]">Edit</th>
<th class="px-8 py-5 text-center text-[14px] font-semibold uppercase tracking-[0.08em]">Delete</th>
</tr>
</thead>
<tbody>
<Show when={roles.loading}>
<tr><td colspan="5" style="text-align:center;padding:32px;color:#64748b">Loading external roles...</td></tr>
</Show>
<Show when={!roles.loading && roles.error}>
<tr><td colspan="5" style="text-align:center;padding:32px;color:#b91c1c">Failed to load external roles. Is the backend running?</td></tr>
</Show>
<Show when={!roles.loading && !roles.error && roles()?.length === 0}>
<tr><td colspan="5" style="text-align:center;padding:32px;color:#94a3b8">No external roles configured yet.</td></tr>
</Show>
<Show when={!roles.loading && !roles.error && (roles()?.length ?? 0) > 0}>
{roles()!.map((role) => (
<tr class={`border-b border-[#e4e7ef] text-[17px] ${selectedRoleKey() === role.roleKey.toLowerCase() ? 'bg-[#fff7f2]' : ''}`}>
<td class="px-8 py-7 font-medium text-[#364152]">{role.roleKey || role.id?.slice(0, 6).toUpperCase()}</td>
<td class="px-8 py-7 font-semibold text-[#0f172a]">{role.displayName}</td>
<td class="px-8 py-7">
<A class="inline-flex items-center gap-2 font-semibold text-[#fd6216] hover:text-orange-700" href={`/admin/runtime-roles/${encodeURIComponent(role.roleKey)}`} target="_blank" rel="noreferrer">
<span>View</span>
<span class="text-[18px]"></span>
</A>
</td>
<td class="px-8 py-7 text-center">
<A class="inline-flex h-9 w-9 items-center justify-center rounded-md text-[#071b3d] hover:bg-slate-100" href={`/admin/runtime-roles/${encodeURIComponent(role.roleKey)}`} title="Edit External Role"></A>
</td>
<td class="px-8 py-7 text-center">
<button class="inline-flex h-9 w-9 items-center justify-center rounded-md text-[#c81e1e] hover:bg-red-50" title="Delete External Role" aria-label={`Delete ${role.displayName}`}>🗑</button>
</td>
</tr>
))}
</Show>
</tbody>
</table>
<div class="flex flex-col -mx-6 -mt-6 min-h-full">
<div class="bg-white border-b border-gray-200 px-6 py-4">
<h1 class="text-xl font-semibold text-gray-900">External Role Management</h1>
<p class="text-sm text-gray-500 mt-0.5">Configure and maintain external system roles and access privileges.</p>
</div>
<div class="flex items-center justify-between border-t border-[#e4e7ef] px-8 py-5">
<p class="text-[14px] font-semibold uppercase tracking-[0.1em] text-[#485163]">Showing 1 to 5 of {(roles()?.length || 0) || 5} entries</p>
<div class="flex items-center gap-2">
<button class="h-11 min-w-11 rounded-xl border border-[#d4d8e2] bg-white px-3 text-[#111827]">{'<'}</button>
<button class="h-11 min-w-11 rounded-xl bg-[#fd6216] px-3 font-bold text-white">1</button>
<button class="h-11 min-w-11 rounded-xl border border-[#d4d8e2] bg-white px-3 font-bold text-[#111827]">2</button>
<button class="h-11 min-w-11 rounded-xl border border-[#d4d8e2] bg-white px-3 font-bold text-[#111827]">3</button>
<button class="h-11 min-w-11 rounded-xl border border-[#d4d8e2] bg-white px-3 text-[#111827]">{'>'}</button>
<div class="flex-1 p-6">
<div class="table-card">
<div class="overflow-x-auto">
<table data-table class="w-full min-w-[860px] text-sm">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Issue Type</th>
<th class="text-center">Edit</th>
<th class="text-center">Delete</th>
</tr>
</thead>
<tbody>
<Show when={roles.loading}>
<tr><td colspan="5" class="text-center px-8 py-8 text-slate-500">Loading external roles...</td></tr>
</Show>
<Show when={!roles.loading && roles.error}>
<tr><td colspan="5" class="text-center px-8 py-8 text-red-600">Failed to load external roles. Is the backend running?</td></tr>
</Show>
<Show when={!roles.loading && !roles.error && roles()?.length === 0}>
<tr><td colspan="5" class="text-center px-8 py-8 text-slate-400">No external roles configured yet.</td></tr>
</Show>
<Show when={!roles.loading && !roles.error && (roles()?.length ?? 0) > 0}>
{roles()!.map((role) => (
<tr class={`hover:bg-slate-50 ${selectedRoleKey() === role.roleKey.toLowerCase() ? 'bg-slate-50' : ''}`}>
<td class="text-slate-500">{role.roleKey || role.id?.slice(0, 6).toUpperCase()}</td>
<td class="font-semibold text-slate-900">{role.displayName}</td>
<td class="text-slate-500">
<A class="inline-flex items-center gap-1 font-medium text-[#0a1d37] hover:text-[#0f2a4e]" href={`/admin/runtime-roles/${encodeURIComponent(role.roleKey)}`} target="_blank" rel="noreferrer">
<span>View</span>
<span></span>
</A>
</td>
<td class="text-center">
<A class="inline-flex h-8 w-8 items-center justify-center rounded-md text-slate-600 hover:bg-slate-100" href={`/admin/runtime-roles/${encodeURIComponent(role.roleKey)}`} title="Edit External Role"></A>
</td>
<td class="text-center">
<button class="inline-flex h-8 w-8 items-center justify-center rounded-md text-red-600 hover:bg-red-50" title="Delete External Role" aria-label={`Delete ${role.displayName}`}>🗑</button>
</td>
</tr>
))}
</Show>
</tbody>
</table>
</div>
<div class="flex items-center justify-between border-t border-gray-200 px-6 py-4">
<p class="text-sm text-slate-500">Showing 1 to 5 of {(roles()?.length || 0) || 5} entries</p>
<div class="flex items-center gap-2">
<button class="h-9 min-w-9 rounded-lg border border-gray-200 bg-white px-3 text-sm text-gray-700 hover:bg-gray-50">{'<'}</button>
<button class="h-9 min-w-9 rounded-lg bg-[#0a1d37] px-3 text-sm font-medium text-white">1</button>
<button class="h-9 min-w-9 rounded-lg border border-gray-200 bg-white px-3 text-sm font-medium text-gray-700 hover:bg-gray-50">2</button>
<button class="h-9 min-w-9 rounded-lg border border-gray-200 bg-white px-3 text-sm font-medium text-gray-700 hover:bg-gray-50">3</button>
<button class="h-9 min-w-9 rounded-lg border border-gray-200 bg-white px-3 text-sm text-gray-700 hover:bg-gray-50">{'>'}</button>
</div>
</div>
</div>
</div>
</section>
</div>
</AdminShell>
);
}

View file

@ -84,29 +84,30 @@ export default function CreateExternalRolePage() {
return (
<AdminShell>
<div class="mb-6 flex items-start justify-between gap-4">
<div>
<h1 class="text-2xl font-bold text-gray-900">Create External Role</h1>
<p class="mt-1 text-sm text-gray-500">
<div class="flex flex-col -mx-6 -mt-6 min-h-full">
<div class="bg-white border-b border-gray-200 px-6 py-4">
<h1 class="text-xl font-semibold text-gray-900">Create External Role</h1>
<p class="text-sm text-gray-500 mt-0.5">
Create a new external role and choose what it can access in the app.
</p>
</div>
<A class="inline-flex items-center rounded-lg border border-gray-200 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 transition-colors" href="/admin/runtime-roles">Back to External Roles</A>
<div class="flex-1 p-6">
<ExternalRoleTabs />
<Show when={error()}>
<div class="mb-4 rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700">{error()}</div>
</Show>
<ExternalRoleForm
initialValue={defaultConfig()}
onboardingSchemaOptions={onboardingSchemas() || []}
saving={saving()}
submitLabel="Create External Role"
onSubmit={handleSubmit}
/>
</div>
</div>
<ExternalRoleTabs />
<Show when={error()}>
<div class="mb-4 rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700">{error()}</div>
</Show>
<ExternalRoleForm
initialValue={defaultConfig()}
onboardingSchemaOptions={onboardingSchemas() || []}
saving={saving()}
submitLabel="Create External Role"
onSubmit={handleSubmit}
/>
</AdminShell>
);
}