feat: add UI templates for device and queue management

- Add devices management template
- Add conflicts resolution template
- Add queue management template
- Update admin templates with navigation links
This commit is contained in:
john-okeefe committed 2026-01-31 18:25:29 -05:00
1 parent 5a7144b98e
commit 707df26453
10 files changed
+988 -6

No files matched your search

+48
View File
@@ -0,0 +1,48 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.977
package templates
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
func Conflicts(user User) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!doctype html><html lang=\"en\"><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><title>Sync Conflicts - Bookmann</title><script src=\"/static/htmx.min.js\"></script><script src=\"/static/toast.js\"></script><link href=\"/static/style.css\" rel=\"stylesheet\"><script src=\"/static/theme.js\"></script><style>\n .conflict-card {\n border-left: 4px solid #f59e0b;\n }\n .conflict-resolved {\n border-left-color: #10b981;\n opacity: 0.7;\n }\n .progress-bar {\n height: 8px;\n border-radius: 4px;\n background-color: var(--border);\n overflow: hidden;\n }\n .progress-fill {\n height: 100%;\n background-color: var(--accent);\n transition: width 0.3s ease;\n }\n .device-badge {\n display: inline-block;\n padding: 2px 8px;\n border-radius: 4px;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n }\n .device-koreader { background-color: #3b82f6; color: white; }\n .device-kobo { background-color: #8b5cf6; color: white; }\n .device-web { background-color: #10b981; color: white; }\n .device-mobile { background-color: #f59e0b; color: white; }\n </style></head><body class=\"theme-{ user.Theme }\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = Header(user, "/conflicts").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<div class=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8\"><div class=\"mb-8\"><div class=\"flex justify-between items-center\"><div><h1 class=\"text-3xl font-bold\" style=\"color: var(--text-primary)\">Sync Conflicts</h1><p style=\"color: var(--text-secondary)\">Resolve conflicts when reading progress differs across devices</p></div><div class=\"flex space-x-3\"><select id=\"status-filter\" onchange=\"filterConflicts()\" class=\"px-4 py-2 border rounded-lg\" style=\"background-color: var(--bg-secondary); color: var(--text-primary); border-color: var(--border);\"><option value=\"unresolved\">Unresolved Only</option> <option value=\"all\">All Conflicts</option> <option value=\"resolved\">Resolved Only</option></select> <button onclick=\"dismissAllResolved()\" class=\"btn-secondary px-4 py-2 rounded-lg\">Dismiss All Resolved</button></div></div><div id=\"stats-bar\" class=\"mt-4 flex space-x-6 text-sm\"><span style=\"color: var(--text-secondary)\">Total: <strong id=\"total-count\" style=\"color: var(--text-primary)\">0</strong></span> <span style=\"color: var(--text-secondary)\">Unresolved: <strong id=\"unresolved-count\" style=\"color: #f59e0b;\">0</strong></span> <span style=\"color: var(--text-secondary)\">Resolved: <strong id=\"resolved-count\" style=\"color: #10b981;\">0</strong></span></div></div><div id=\"loading\" class=\"text-center py-16\" style=\"color: var(--text-secondary)\"><div class=\"loading-spinner mx-auto mb-4\"></div><p>Loading conflicts...</p></div><div id=\"empty-state\" class=\"hidden text-center py-16\" style=\"color: var(--text-secondary)\"><div class=\"text-6xl mb-4\">✅</div><h3 class=\"text-xl font-semibold mb-2\" style=\"color: var(--text-primary)\">No Conflicts</h3><p>Your devices are in sync! No conflicts to resolve.</p></div><div id=\"conflicts-list\" class=\"space-y-6 hidden\"></div><div id=\"conflict-modal\" class=\"hidden fixed inset-0 z-50 flex items-center justify-center overflow-y-auto\" style=\"background-color: rgba(0, 0, 0, 0.7);\"><div class=\"card rounded-lg p-6 w-full max-w-4xl mx-4 my-8\" style=\"background-color: var(--bg-secondary); border-color: var(--border);\"><div class=\"flex justify-between items-center mb-6\"><div><h2 class=\"text-2xl font-bold\" style=\"color: var(--text-primary)\">Resolve Conflict</h2><p class=\"text-sm\" id=\"conflict-book-title\" style=\"color: var(--text-secondary);\">Book Title</p></div><button onclick=\"hideConflictModal()\" class=\"p-2 hover:opacity-80 rounded-lg\" style=\"color: var(--text-primary); background-color: var(--bg-primary);\">X</button></div><form id=\"conflict-resolution-form\" onsubmit=\"handleResolveConflict(event)\"><input type=\"hidden\" id=\"conflict-id\"><div id=\"conflict-details\" class=\"mb-6\"></div><div class=\"card p-4 rounded-lg border mb-6\" style=\"background-color: var(--bg-primary); border-color: var(--border);\"><h3 class=\"font-semibold mb-4\" style=\"color: var(--text-primary)\">Resolution Options</h3><div class=\"space-y-3 mb-6\"><label class=\"flex items-center p-3 border rounded-lg cursor-pointer hover:opacity-80\" style=\"border-color: var(--border);\"><input type=\"radio\" name=\"winner\" value=\"koreader\" class=\"mr-3\" required><div class=\"flex-1\"><span class=\"font-semibold\" style=\"color: var(--text-primary);\">Keep KOReader Progress</span><p class=\"text-xs\" style=\"color: var(--text-secondary);\">Use progress from your KOReader device</p></div></label> <label class=\"flex items-center p-3 border rounded-lg cursor-pointer hover:opacity-80\" style=\"border-color: var(--border);\"><input type=\"radio\" name=\"winner\" value=\"kobo\" class=\"mr-3\"><div class=\"flex-1\"><span class=\"font-semibold\" style=\"color: var(--text-primary);\">Keep Kobo Progress</span><p class=\"text-xs\" style=\"color: var(--text-secondary);\">Use progress from your Kobo device</p></div></label> <label class=\"flex items-center p-3 border rounded-lg cursor-pointer hover:opacity-80\" style=\"border-color: var(--border);\"><input type=\"radio\" name=\"winner\" value=\"web\" class=\"mr-3\"><div class=\"flex-1\"><span class=\"font-semibold\" style=\"color: var(--text-primary);\">Keep Web Progress</span><p class=\"text-xs\" style=\"color: var(--text-secondary);\">Use progress from the web interface</p></div></label> <label class=\"flex items-center p-3 border rounded-lg cursor-pointer hover:opacity-80\" style=\"border-color: var(--border);\"><input type=\"radio\" name=\"winner\" value=\"manual\" class=\"mr-3\"><div class=\"flex-1\"><span class=\"font-semibold\" style=\"color: var(--text-primary);\">Manual Override</span><p class=\"text-xs\" style=\"color: var(--text-secondary);\">Specify a custom progress value</p></div></label></div><div id=\"manual-override\" class=\"hidden mb-4 p-4 border rounded-lg\" style=\"background-color: var(--bg-secondary); border-color: var(--border);\"><h4 class=\"font-semibold mb-3\" style=\"color: var(--textLine truncated
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate