webweav.ing/ui/views/admin.templ

278 lines
8.1 KiB
Plaintext

package views
import (
"fmt"
"git.32bit.cafe/32bitcafe/guestbook/internal/forms"
"git.32bit.cafe/32bitcafe/guestbook/internal/models"
"slices"
"strings"
"time"
)
type AdminStat struct {
WebsiteCount int64
UserCount int64
CommentCount int64
}
templ adminBase(title string, data CommonData) {
<!DOCTYPE html>
<html lang="en">
<head>
<title>{ title } - webweav.ing</title>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta name="htmx-config" content={ `{"includeIndicatorStyles":false}` }/>
<link href="/static/css/style.css" rel="stylesheet"/>
<link href="/static/fontawesome/css/fontawesome.css" rel="stylesheet"/>
<link href="/static/fontawesome/css/solid.css" rel="stylesheet"/>
<script src="/static/js/htmx.min.js"></script>
</head>
<body>
<header>
<a href="/">Back to webweav.ing</a>
</header>
<main>
{ children... }
</main>
@commonFooter()
</body>
</html>
}
templ adminSidebar() {
<nav aria-label="Admin panel navigation">
<div>
<section>
<h3>Administration</h3>
<ul role="list">
<li><a href="/admin">Dashboard</a></li>
<li><a href="/admin/users">Users</a></li>
<li><a href="/admin/websites">Websites</a></li>
</ul>
</section>
</div>
</nav>
}
templ AdminPanelLandingView(title string, data CommonData, stats AdminStat) {
@adminBase(title, data) {
<div id="dashboard">
@adminSidebar()
<div>
<section class="admin-section">
<h1>{ title }</h1>
<p>Welcome to the admin panel</p>
</section>
<section class="admin-flex">
<section class="admin-section admin-info">
<h2>Users</h2>
<p>{ fmt.Sprintf("%d", stats.UserCount) }</p>
</section>
<section class="admin-section admin-info">
<h2>Websites</h2>
<p>{ fmt.Sprintf("%d", stats.WebsiteCount) }</p>
</section>
<section class="admin-section admin-info">
<h2>Comments</h2>
<p>{ fmt.Sprintf("%d", stats.CommentCount) }</p>
</section>
</section>
</div>
</div>
}
}
templ AdminPanelUsersView(title string, data CommonData, users []models.User, pageNum, pageSize, total int64) {
@adminBase(title, data) {
<div id="dashboard">
@adminSidebar()
<div>
<section class="admin-section">
<table>
<th>Username</th>
<th>Joined</th>
<th>Email</th>
for _, u := range users {
<tr>
{{ url := fmt.Sprintf("/admin/users/%s", shortIdToSlug(u.ShortId)) }}
<td><a href={ templ.URL(url) }>{ u.Username }</a></td>
<td>{ u.Created.Format(time.RFC3339) }</td>
<td>{ u.Email }</td>
</tr>
}
</table>
</section>
@pagination("/admin/users", pageNum, total, pageSize)
</div>
</div>
}
}
templ AdminPanelUserMgmtDetail(csrfToken string, user models.User) {
<div id="user-info">
<form>
<input type="hidden" name="csrf_token" value={ csrfToken }/>
<section>
<h3>User Info</h3>
<div>
<h5>Username</h5>
<p>{ user.Username }</p>
</div>
<div>
<h5>Email</h5>
<p>{ user.Email }</p>
</div>
<div>
<h5>Joined</h5>
<p>{ user.Created.Format(time.RFC3339) }</p>
</div>
</section>
<section>
<h3>Groups</h3>
<ul>
for _, g := range user.Groups {
<li>{ fmt.Sprintf("%s", getGroupName(g)) }</li>
}
</ul>
</section>
<section>
<h3>Actions</h3>
{{ getFormUrl := fmt.Sprintf("/admin/users/%s/edit", shortIdToSlug(user.ShortId)) }}
{{ putBanUrl := fmt.Sprintf("/admin/users/%s/ban", shortIdToSlug(user.ShortId)) }}
{{ putUnbanUrl := fmt.Sprintf("/admin/users/%s/unban", shortIdToSlug(user.ShortId)) }}
{{ deleteUrl := fmt.Sprintf("/admin/users/%s", shortIdToSlug(user.ShortId)) }}
<button type="button" hx-get={ getFormUrl } hx-target="#user-info">Edit</button>
if user.ID != 1 {
if user.Banned.IsZero() {
<button type="button" hx-put={ putBanUrl } hx-confirm="Are you sure you want to ban this user?" hx-target="#user-info" class="danger">Ban</button>
} else {
<button type="button" hx-put={ putUnbanUrl } hx-confirm="Are you sure you want to unban this user?" hx-target="#user-info">Unban</button>
}
<button type="button" hx-delete={ deleteUrl } hx-confirm="Are you sure you want to delete this user? This is irreversible" class="danger">Delete</button>
}
</section>
</form>
</div>
}
templ AdminPanelUserMgmtView(title string, data CommonData, user models.User) {
@adminBase(title, data) {
<div id="dashboard">
@adminSidebar()
@AdminPanelUserMgmtDetail(data.CSRFToken, user)
</div>
}
}
templ AdminPanelUserMgmtEditForm(csrfToken string, form forms.AdminUserMgmtForm, user models.User, groups []models.UserGroupId) {
<div id="user-info">
<form>
<input type="hidden" name="csrf_token" value={ csrfToken }/>
<fieldset>
<h3>User Info</h3>
<div>
<h5>Username</h5>
<input type="text" name="admin_username" id="username" value={ form.Username } required/>
</div>
<div>
<h5>Email</h5>
<input type="text" name="admin_useremail" id="useremail" value={ form.Email } required/>
</div>
<div>
<h5>Joined</h5>
<p>{ user.Created.Format(time.RFC3339) }</p>
</div>
</fieldset>
<section>
<fieldset>
<h3>Groups</h3>
{{ isAdmin := slices.Contains(user.Groups, models.AdminGroup) }}
<input type="checkbox" name="admin_usergroup_admin" id="usergroup_admin" checked?={ isAdmin } disabled?={ user.ID == 1 }/>
<label for="usergroup_admin">Administrator</label>
<input type="checkbox" name="admin_usergroup_user" id="usergroup_user" checked disabled/>
<label for="usergroup_user">User</label>
</fieldset>
</section>
<section>
<h3>Actions</h3>
{{ putFormUrl := fmt.Sprintf("/admin/users/%s/edit", shortIdToSlug(user.ShortId)) }}
{{ getDetailUrl := fmt.Sprintf("/admin/users/%s/detail", shortIdToSlug(user.ShortId)) }}
<button type="button" hx-put={ putFormUrl } hx-target="#user-info">Save</button>
<button type="reset" hx-get={ getDetailUrl } hx-target="#user-info">Cancel</button>
</section>
</form>
</div>
}
templ AdminPanelAllWebsitesView(title string, data CommonData, websites []models.Website, pageNum, pageCount, total int64) {
@adminBase(title, data) {
<div id="dashboard">
@adminSidebar()
<div>
<section class="admin-section">
<table>
<th>Site Name</th>
<th>Owner</th>
<th>URL</th>
<th>Created</th>
<th>Guestbook</th>
for _, w := range websites {
<tr>
{{ detailUrl := fmt.Sprintf("/admin/websites/%s", shortIdToSlug(w.ShortId)) }}
<td><a href={ templ.SafeURL(detailUrl) }>{ w.Name }</a></td>
<td>{ w.AuthorName }</td>
<td><a href={ templ.SafeURL(w.Url.String()) }>{ w.Url.String() }</a></td>
<td>{ w.Created.Format(time.RFC1123) }</td>
{{ gbUrl := fmt.Sprintf("/websites/%s/guestbook", shortIdToSlug(w.ShortId)) }}
<td><a href={ templ.SafeURL(gbUrl) }>View</a></td>
</tr>
}
</table>
</section>
@pagination("/admin/websites", pageNum, total, pageCount)
</div>
</div>
}
}
func truncateComment(s string, n int) string {
words := strings.Fields(s)
if len(words) < n {
return s
}
truncated := words[:n]
truncated = append(truncated, "...")
return strings.Join(truncated, " ")
}
templ AdminPanelWebsiteDetailView(title string, data CommonData, website models.Website, comments []models.GuestbookComment, pageNum, pageAmount, total int64) {
@adminBase(title, data) {
<div id="dashboard">
@adminSidebar()
<div>
<section class="admin-section">
<table>
<th>Author</th>
<th>Created</th>
<th>Email</th>
<th>Homepage</th>
<th>Comment</th>
for _, c := range comments {
<tr>
<td>{ c.AuthorName }</td>
<td>{ c.Created.Format(time.RFC1123) }</td>
<td>{ c.AuthorEmail }</td>
<td>{ c.AuthorSite }</td>
<td>{ truncateComment(c.CommentText, 15) }</td>
</tr>
}
</table>
</section>
{{ url := fmt.Sprintf("/admin/websites/%s", shortIdToSlug(website.ShortId)) }}
@pagination(url, pageNum, total, pageAmount)
</div>
</div>
}
}