admin page updates
This commit is contained in:
@@ -5,13 +5,20 @@ import (
|
||||
"git.haelnorr.com/h/oslstats/internal/db"
|
||||
)
|
||||
|
||||
templ RolesList(roles []*db.Role) {
|
||||
<div class="space-y-4">
|
||||
templ RolesList(roles *db.List[db.Role]) {
|
||||
<div
|
||||
id="roles-list-container"
|
||||
x-data={ fmt.Sprintf("{ page: %d, perPage: %d, order: '%s', orderBy: '%s' }",
|
||||
roles.PageOpts.Page,
|
||||
roles.PageOpts.PerPage,
|
||||
roles.PageOpts.Order,
|
||||
roles.PageOpts.OrderBy) }
|
||||
>
|
||||
<!-- Header with Create Button -->
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h1 class="text-2xl font-bold text-text">Role Management</h1>
|
||||
<button
|
||||
class="px-4 py-2 bg-blue text-mantle rounded-lg font-semibold hover:bg-sky transition"
|
||||
class="px-4 py-2 bg-blue text-mantle rounded-lg font-semibold hover:bg-sky transition hover:cursor-pointer"
|
||||
hx-get="/admin/roles/create"
|
||||
hx-target="#role-modal"
|
||||
hx-swap="innerHTML"
|
||||
@@ -20,29 +27,68 @@ templ RolesList(roles []*db.Role) {
|
||||
</button>
|
||||
</div>
|
||||
<!-- Roles Table -->
|
||||
<div class="bg-surface0 border border-surface1 rounded-lg overflow-hidden">
|
||||
<table class="w-full">
|
||||
<thead class="bg-surface1">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-sm font-semibold text-text">Name</th>
|
||||
<th class="px-6 py-3 text-left text-sm font-semibold text-text">Description</th>
|
||||
<th class="px-6 py-3 text-left text-sm font-semibold text-text">Type</th>
|
||||
<th class="px-6 py-3 text-right text-sm font-semibold text-text">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-surface1">
|
||||
if len(roles) == 0 {
|
||||
if len(roles.Items) == 0 {
|
||||
<div class="bg-mantle border border-surface1 rounded-lg p-8 text-center">
|
||||
<p class="text-subtext0 text-lg">No roles found</p>
|
||||
</div>
|
||||
} else {
|
||||
<div class="bg-surface0 border border-surface1 rounded-lg overflow-hidden">
|
||||
<table class="w-full">
|
||||
<thead class="bg-mantle border-b border-surface1">
|
||||
<tr>
|
||||
<td colspan="4" class="px-6 py-8 text-center text-subtext0">No roles found</td>
|
||||
@roleTableHeader("display_name", "Name", roles.PageOpts.OrderBy, string(roles.PageOpts.Order))
|
||||
<th class="px-6 py-3 text-left text-sm font-semibold text-text">Description</th>
|
||||
@roleTableHeader("is_system", "Type", roles.PageOpts.OrderBy, string(roles.PageOpts.Order))
|
||||
<th class="px-6 py-3 text-right text-sm font-semibold text-text">Actions</th>
|
||||
</tr>
|
||||
} else {
|
||||
for _, role := range roles {
|
||||
</thead>
|
||||
<tbody class="divide-y divide-surface1">
|
||||
for _, role := range roles.Items {
|
||||
@roleRow(role)
|
||||
}
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- Pagination controls -->
|
||||
<div class="mt-6 flex flex-col gap-4">
|
||||
<!-- Page info and per-page selector -->
|
||||
<div class="flex flex-col sm:flex-row justify-between items-center gap-4 text-sm text-subtext0">
|
||||
<div>
|
||||
if roles.Total > 0 {
|
||||
Showing { fmt.Sprintf("%d", roles.PageOpts.StartItem()) } - { fmt.Sprintf("%d", roles.PageOpts.EndItem(roles.Total)) } of { fmt.Sprintf("%d", roles.Total) } roles
|
||||
} else {
|
||||
No roles
|
||||
}
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<label for="roles-per-page-select">Per page:</label>
|
||||
<select
|
||||
id="roles-per-page-select"
|
||||
class="py-1 px-2 rounded-lg bg-surface0 border border-surface1 text-text focus:border-blue outline-none"
|
||||
x-model.number="perPage"
|
||||
@change="submitRolesPage(page, perPage, order, orderBy)"
|
||||
>
|
||||
<option value="10">10</option>
|
||||
<option value="25">25</option>
|
||||
<option value="50">50</option>
|
||||
<option value="100">100</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Pagination buttons -->
|
||||
if roles.Total > 0 && roles.PageOpts.TotalPages(roles.Total) > 1 {
|
||||
<div class="flex flex-wrap justify-center items-center gap-2">
|
||||
@paginationButton("First", "<<", fmt.Sprintf("submitRolesPage(1, perPage, order, orderBy)"), !roles.PageOpts.HasPrevPage())
|
||||
@paginationButton("Previous", "<", fmt.Sprintf("submitRolesPage(%d, perPage, order, orderBy)", roles.PageOpts.Page-1), !roles.PageOpts.HasPrevPage())
|
||||
for _, pageNum := range roles.PageOpts.GetPageRange(roles.Total, 7) {
|
||||
@pageNumberButton(pageNum, roles.PageOpts.Page, "submitRolesPage")
|
||||
}
|
||||
@paginationButton("Next", ">", fmt.Sprintf("submitRolesPage(%d, perPage, order, orderBy)", roles.PageOpts.Page+1), !roles.PageOpts.HasNextPage(roles.Total))
|
||||
@paginationButton("Last", ">>", fmt.Sprintf("submitRolesPage(%d, perPage, order, orderBy)", roles.PageOpts.TotalPages(roles.Total)), !roles.PageOpts.HasNextPage(roles.Total))
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<!-- Modal Container -->
|
||||
<div id="role-modal"></div>
|
||||
@@ -57,6 +103,27 @@ templ RolesList(roles []*db.Role) {
|
||||
</div>
|
||||
}
|
||||
|
||||
templ roleTableHeader(field string, label string, currentField string, currentOrder string) {
|
||||
{{
|
||||
isActive := currentField == field
|
||||
baseClasses := "px-6 py-3 text-left text-sm font-semibold text-text cursor-pointer select-none hover:text-blue transition-colors"
|
||||
arrow := ""
|
||||
if isActive {
|
||||
if currentOrder == "ASC" {
|
||||
arrow = " ↑"
|
||||
} else {
|
||||
arrow = " ↓"
|
||||
}
|
||||
}
|
||||
}}
|
||||
<th class={ baseClasses } @click={ fmt.Sprintf("sortRolesColumn('%s', order, orderBy)", field) }>
|
||||
{ label }
|
||||
if arrow != "" {
|
||||
<span class="text-blue">{ arrow }</span>
|
||||
}
|
||||
</th>
|
||||
}
|
||||
|
||||
templ roleRow(role *db.Role) {
|
||||
<tr class="hover:bg-surface1/50 transition">
|
||||
<td class="px-6 py-4 text-sm text-text font-semibold">{ role.DisplayName }</td>
|
||||
@@ -70,7 +137,7 @@ templ roleRow(role *db.Role) {
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm text-right">
|
||||
<button
|
||||
class="px-4 py-2 bg-blue text-mantle rounded-lg hover:bg-sky transition text-sm font-semibold"
|
||||
class="px-4 py-2 bg-blue text-mantle rounded-lg hover:bg-sky transition text-sm font-semibold hover:cursor-pointer"
|
||||
hx-get={ fmt.Sprintf("/admin/roles/%d/manage", role.ID) }
|
||||
hx-target="#role-modal"
|
||||
hx-swap="innerHTML"
|
||||
|
||||
Reference in New Issue
Block a user