auth-yes/dump_roles.txt
google-labs-jules[bot] 4c7d8d4af7 feat(ui): decompose admin pages into reusable AdminTable and AdminModal components
Refactored AdminInvitesPage, AdminUserDetailsPage, AdminRolesPage, and AdminAppsPage to use the new pure Hono SSR JSX stateless components.
Fixed missing import definitions in AdminRolesPage.
Moved task file to complete state.

Co-authored-by: mrteye <1945243+mrteye@users.noreply.github.com>
2026-08-26 00:34:29 +00:00

133 lines
5.1 KiB
Plaintext

onclick="closeRoleDrawer()"
style="min-height: 42px;"
>
Cancel
</button>
</div>
</form>
</AdminModal>
<div class="card">
{/* Instant Search and Scope Filters */}
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.25rem; flex-wrap: wrap; gap: 0.75rem;">
<div style="display: flex; gap: 0.75rem; align-items: center; flex-wrap: wrap; flex: 1;">
{/* Search */}
<div style="position: relative; min-width: 200px; max-width: 300px; width: 100%;">
<input
type="text"
id="roleSearchInput"
placeholder="Search roles..."
oninput="filterRoles()"
style="width: 100%; padding: 0.45rem 0.85rem 0.45rem 2.1rem; font-size: 0.85rem;"
/>
<svg
width="15"
height="15"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
style="position: absolute; left: 0.7rem; top: 50%; transform: translateY(-50%); color: var(--text-muted); pointer-events: none;"
>
<circle cx="11" cy="11" r="8"></circle>
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
</svg>
</div>
{/* Scope dropdown */}
<div style="display: flex; gap: 0.4rem; align-items: center;">
<label style="font-weight: 600; font-size: 0.85rem; color: var(--text-secondary); white-space: nowrap;">
Scope:
</label>
<select
id="filterScopeSelect"
onchange="filterRoles()"
style="padding: 0.4rem 0.75rem; font-size: 0.85rem;"
>
<option value="all">All Roles</option>
<option value="global">Global (Shared) Only</option>
{apps.map((app) => (
<option value={app.id}>
{app.name} Only
</option>
))}
</select>
</div>
</div>
<span
id="roleCountDisplay"
style="font-size: 0.85rem; color: var(--text-muted);"
>
Showing {roles.length} roles
</span>
</div>
{/* Desktop Table View (≥ 768px) */}
<div class="table-container desktop-only" style="display: none;">
<table id="rolesTable">
<thead>
<tr>
<th>Role Identifier</th>
<th>Scope</th>
<th>Description</th>
<th>Created</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{roles.length === 0
? (
<tr>
<td
colSpan={5}
style="text-align: center; color: var(--text-muted); padding: 2rem;"
>
No roles found.
</td>
</tr>
)
: (
roles.map((r) => {
const isGlobal = !r.app_id;
const isCoreAdmin = isGlobal && r.name === "admin";
return (
<tr
key={r.id}
class="role-row"
data-app-id={r.app_id || "global"}
data-search={`${r.name} ${r.description || ""} ${
isGlobal ? "global" : r.app_name || ""
}`.toLowerCase()}
>
<td>
<strong style="font-family: monospace; font-size: 0.95rem; color: var(--text-primary);">
{r.name}
</strong>
</td>
<td>
{isGlobal
? (
<span class="badge badge-info">
Global (Shared)
</span>
)
: (
<span class="badge badge-warning">
{r.app_name || "App-Specific"}
</span>
)}
</td>
<td style="color: var(--text-secondary); font-size: 0.85rem;">
{r.description || "-"}
</td>
<td style="font-size: 0.85rem; color: var(--text-secondary);">
{new Date(r.created_at).toLocaleDateString()}
</td>
<td>
{!isCoreAdmin
? (
<div style="display: flex; gap: 0.35rem;">
<button