Apply the new design-system primitives across the remaining pages so the whole app shares one visual language: - Auth/entry: redesigned index, login, register (centered card layout, SVG icons, semantic tokens) - Admin: sidebar nav with icons, cohesive admin dashboard/users/library/ processing-issues/settings (tables use border-line + hover states; scan/websocket attributes preserved) - Secondary: analytics (stat cards), devices, progress, conflicts, queue, profile + form/modal all migrated to .card/.btn/.input primitives and SVG action icons - Docs + API explorer + setup wizard + collection/rules/modals + unlinked books + book-detail modals: consistent chrome, modal scrims use --surface-overlay, close buttons use icon-btn - reader.templ and error.templ intentionally left unchanged
49 lines
15 KiB
Go
49 lines
15 KiB
Go
// Code generated by templ - DO NOT EDIT.
|
||
|
||
// templ: version: v0.3.1020
|
||
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 Setup() 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>Setup - Bookhoard</title><script src=\"/static/htmx.min.js\"></script><script type=\"module\" src=\"/static/main.js\"></script><link href=\"/static/style.css\" rel=\"stylesheet\"></head><body class=\"theme-tokyo-night min-h-screen\" x-data=\"setupWizard\" x-init=\"initSetup()\"><div class=\"container mx-auto px-4 py-8 max-w-2xl\"><div class=\"text-center mb-8\"><h1 class=\"text-2xl font-bold tracking-tight text-content mb-2\">Welcome to Bookhoard</h1><p class=\"text-lg text-content-muted\">Let's get your library set up.</p></div><!-- Step indicator --><div class=\"flex items-center justify-center mb-8 gap-2\"><template x-for=\"n in 4\" :key=\"n\"><div class=\"flex items-center gap-2\"><div class=\"w-10 h-10 rounded-full flex items-center justify-center font-bold text-sm transition-all\" :class=\"currentStep >= n ? 'bg-[var(--accent)] text-[var(--bg-primary)]' : 'bg-[var(--bg-secondary)] text-[var(--text-secondary)] border border-[var(--border)]'\"><span x-text=\"currentStep > n ? '✓' : n\"></span></div><div x-text=\"['Admin', 'Libraries', 'Folders', 'Scan'][n-1]\" class=\"text-sm hidden sm:inline\" :style=\"currentStep >= n ? 'color: var(--text-primary)' : 'color: var(--text-secondary)'\"></div><div x-show=\"n < 4\" class=\"w-8 h-0.5 mx-1\" :class=\"currentStep > n ? 'bg-[var(--accent)]' : 'bg-[var(--border)]'\"></div></div></template></div><!-- Error display --><div x-show=\"errorMsg\" x-cloak class=\"mb-4 p-4 rounded-lg border border-red-500/50 bg-red-500/10 text-red-400 text-sm\"><span x-text=\"errorMsg\"></span></div><!-- ==================== STEP 1: Create Admin ==================== --><div x-show=\"currentStep === 1\" x-cloak class=\"card p-6\"><h2 class=\"text-2xl font-bold tracking-tight text-content mb-2\">Create Administrator Account</h2><p class=\"mb-6 text-content-muted\">This first account will have full admin privileges.</p><form @submit.prevent=\"submitAdmin()\"><div class=\"mb-6 p-4 rounded-lg border border-line bg-surface\"><label class=\"block text-xs font-semibold uppercase tracking-wide text-content-muted mb-1\">Server URL (Base URL)</label> <input type=\"url\" x-model=\"admin.baseUrl\" placeholder=\"https://books.example.com\" class=\"input w-full mt-2\" required><p class=\"text-sm mt-2 text-content-muted\">The public URL of your Bookhoard instance. Used for device sync, OPDS feed, and API endpoints.</p></div><div class=\"mb-4\"><label class=\"block text-xs font-semibold uppercase tracking-wide text-content-muted mb-1\">Email</label> <input type=\"email\" id=\"email\" x-model=\"admin.email\" class=\"input w-full\" required></div><div class=\"mb-4\"><label class=\"block text-xs font-semibold uppercase tracking-wide text-content-muted mb-1\">Username</label> <input type=\"text\" x-model=\"admin.username\" id=\"username\" class=\"input w-full\" required></div><div class=\"grid grid-cols-2 gap-4 mb-4\"><div><label class=\"block text-xs font-semibold uppercase tracking-wide text-content-muted mb-1\">First Name</label> <input type=\"text\" x-model=\"admin.firstName\" placeholder=\"Optional\" class=\"input w-full\"></div><div><label class=\"block text-xs font-semibold uppercase tracking-wide text-content-muted mb-1\">Last Name</label> <input type=\"text\" x-model=\"admin.lastName\" placeholder=\"Optional\" class=\"input w-full\"></div></div><div id=\"password-requirements\" class=\"mb-4 p-4 rounded-lg border border-line bg-surface text-sm\"><p class=\"font-semibold mb-2 text-content\">Password Requirements:</p><ul class=\"space-y-1 text-content-muted\"><li id=\"req-length\" class=\"flex items-center gap-2\"><span class=\"requirement-icon\">○</span> At least 8 characters</li><li id=\"req-upper\" class=\"flex items-center gap-2\"><span class=\"requirement-icon\">○</span> One uppercase letter</li><li id=\"req-lower\" class=\"flex items-center gap-2\"><span class=\"requirement-icon\">○</span> One lowercase letter</li><li id=\"req-number\" class=\"flex items-center gap-2\"><span class=\"requirement-icon\">○</span> One number</li><li id=\"req-special\" class=\"flex items-center gap-2\"><span class=\"requirement-icon\">○</span> One special character</li><li id=\"req-match\" class=\"flex items-center gap-2\"><span class=\"requirement-icon\">○</span> Passwords match</li></ul></div><div class=\"mb-4\"><label class=\"block text-xs font-semibold uppercase tracking-wide text-content-muted mb-1\">Password</label> <input type=\"password\" x-model=\"admin.password\" id=\"password\" class=\"input w-full\" required></div><div class=\"mb-6\"><label class=\"block text-xs font-semibold uppercase tracking-wide text-content-muted mb-1\">Confirm Password</label> <input type=\"password\" x-model=\"admin.confirmPassword\" id=\"confirm-password\" class=\"input w-full\" required></div><button type=\"submit\" id=\"register-btn\" class=\"btn btn-primary w-full opacity-50 cursor-not-allowed\" :disabled=\"loading\" x-text=\"loading ? 'Creating...' : 'Create Admin Account'\"></button></form></div><!-- ==================== STEP 2: Create Libraries ==================== --><div x-show=\"currentStep === 2\" x-cloak class=\"card p-6\"><h2 class=\"text-2xl font-bold tracking-tight text-content mb-2\">Create Libraries</h2><p class=\"mb-6 text-content-muted\">Add one or more media libraries. You can always add more later.</p><!-- Created libraries list --><div x-show=\"libraries.length > 0\" x-cloak class=\"mb-6 space-y-2\"><template x-for=\"(lib, idx) in libraries\" :key=\"lib.id\"><div class=\"p-3 rounded-lg border border-line bg-surface flex items-center justify-between\"><div class=\"flex items-center gap-3\"><span class=\"text-xl\" x-text=\"lib.type === 'ebooks' ? '📚' : lib.type === 'comics' ? '📖' : '🗾'\"></span><div><p class=\"font-medium text-content\" x-text=\"lib.name\"></p><p class=\"text-xs capitalize text-content-muted\" x-text=\"lib.type\"></p></div></div><button type=\"button\" @click=\"removeLibrary(idx)\" class=\"text-xs text-danger hover:underline\">Remove</button></div></template></div><!-- Add library form --><form @submit.prevent=\"addLibrary()\" class=\"space-y-4 p-4 rounded-lg border border-line bg-surface\"><div><label class=\"block text-xs font-semibold uppercase tracking-wide text-content-muted mb-1\">Library Name</label> <input type=\"text\" x-model=\"newLibrary.name\" placeholder=\"My Ebook Library\" class=\"input w-full\" required></div><div><label class=\"block text-xs font-semibold uppercase tracking-wide text-content-muted mb-1\">Library Type</label> <select x-model=\"newLibrary.type\" class=\"input w-full\"><option value=\"ebooks\">📚 Ebooks</option> <option value=\"comics\">📖 Comics</option> <option value=\"manga\">🗾 Manga</option></select></div><div><label class=\"block text-xs font-semibold uppercase tracking-wide text-content-muted mb-1\">Description (optional)</label> <input type=\"text\" x-model=\"newLibrary.description\" placeholder=\"Optional\" class=\"input w-full\"></div><button type=\"submit\" class=\"btn btn-secondary w-full\" :disabled=\"loading || !newLibrary.name.trim()\" x-text=\"loading ? 'Creating...' : '+ Add This Library'\"></button></form><div class=\"flex justify-between mt-6\"><button type=\"button\" @click=\"currentStep = 1\" class=\"btn btn-secondary\">Back</button> <button type=\"button\" @click=\"currentStep = 3\" class=\"btn btn-primary\"><span x-text=\"libraries.length > 0 ? 'Next: Configure Folders' : 'Skip – Continue'\"></span></button></div></div><!-- ==================== STEP 3: Configure Folders ==================== --><div x-show=\"currentStep === 3\" x-cloak class=\"card p-6\"><h2 class=\"text-2xl font-bold tracking-tight text-content mb-2\">Configure Library Folders</h2><p class=\"mb-6 text-content-muted\">Add the filesystem folders where your media files are stored.</p><div x-show=\"libraries.length === 0\" x-cloak class=\"text-center py-8 text-content-muted\"><p>No libraries created. You can add folders later from the admin panel.</p></div><div class=\"space-y-6\"><template x-for=\"lib in libraries\" :key=\"lib.id\"><div class=\"p-4 rounded-lg border border-line bg-surface\"><div class=\"flex items-center gap-2 mb-3\"><span class=\"text-xl\" x-text=\"lib.type === 'ebooks' ? '📚' : lib.type === 'comics' ? '📖' : '🗾'\"></span><h4 class=\"font-semibold text-content\" x-text=\"lib.name\"></h4></div><!-- Existing folders for this library --><div class=\"space-y-1 mb-3\" x-show=\"lib.folders && lib.folders.length > 0\"><template x-for=\"(folder, fIdx) in (lib.folders || [])\" :key=\"folder\"><div class=\"flex items-center justify-between p-2 rounded text-sm border border-line bg-surface-raised\"><span class=\"text-content\" x-text=\"folder\"></span> <button type=\"button\" @click=\"removeFolder(lib.id, folder, fIdx)\" class=\"text-xs text-danger hover:underline\">Remove</button></div></template></div><!-- Add folder --><div class=\"flex gap-2\"><input type=\"text\" :placeholder=\"'/path/to/' + lib.type\" :id=\"'folderInput-' + lib.id\" class=\"input flex-1 text-sm\"> <button type=\"button\" @click=\"showFolderBrowser('folderInput-' + lib.id)\" class=\"btn btn-secondary text-xs\">Browse</button> <button type=\"button\" @click=\"addFolderToLibrary(lib)\" class=\"btn btn-primary text-xs\">Add</button></div></div></template></div><div class=\"flex justify-between mt-6\"><button type=\"button\" @click=\"currentStep = 2\" class=\"btn btn-secondary\">Back</button> <button type=\"button\" @click=\"currentStep = 4\" class=\"btn btn-primary\">Next: Scan</button></div></div><!-- ==================== STEP 4: Scan & Finish ==================== --><div x-show=\"currentStep === 4\" x-cloak class=\"card p-6\"><h2 class=\"text-2xl font-bold tracking-tight text-content mb-2\">Scan & Finish</h2><p class=\"mb-6 text-content-muted\">Review your configuration and start the initial scan.</p><!-- Summary --><div class=\"space-y-3 mb-6\"><div class=\"p-4 rounded-lg border border-line bg-surface\"><p class=\"text-sm font-medium mb-1 text-content-muted\">Server URL</p><p class=\"text-content\" x-text=\"admin.baseUrl\"></p></div><div class=\"p-4 rounded-lg border border-line bg-surface\"><p class=\"text-sm font-medium mb-1 text-content-muted\">Administrator</p><p class=\"text-content\" x-text=\"admin.username + ' (' + admin.email + ')'\"></p></div><div class=\"p-4 rounded-lg border border-line bg-surface\"><p class=\"text-sm font-medium mb-2 text-content-muted\">Libraries (<span x-text=\"libraries.length\"></span>)</p><template x-for=\"lib in libraries\" :key=\"lib.id\"><div class=\"flex items-start justify-between text-sm py-1\"><div><span class=\"text-content\" x-text=\"lib.name\"></span> <span class=\"ml-2 text-xs px-2 py-0.5 rounded bg-brand text-surface\" x-text=\"lib.type\"></span></div><div class=\"text-right text-content-muted\"><span x-text=\"(lib.folders || []).length + ' folder(s)'\"></span></div></div></template></div></div><!-- Scan progress --><div x-show=\"scanStarted\" x-cloak class=\"mb-6 space-y-3\"><h3 class=\"font-semibold text-content\">Scan Progress</h3><div id=\"library-progress-list\" class=\"space-y-3\"><template x-for=\"job in scanJobs\" :key=\"job.jobId\"><div class=\"p-3 rounded border border-line bg-surface\"><div class=\"flex justify-between items-center mb-2\"><span class=\"font-medium text-content\" x-text=\"job.libraryName\"></span> <span class=\"text-sm text-content-muted\" x-text=\"job.statusText\"></span></div><div class=\"w-full rounded-full h-2 bg-surface-raised\"><div class=\"h-2 rounded-full transition-all duration-500\" :style=\"'width: ' + job.progress + '%; background-color: var(--accent);'\"></div></div></div></template></div><div x-show=\"scanComplete\" x-cloak class=\"p-4 rounded-lg border border-green-500/50 bg-green-500/10 text-green-400 text-sm\">Initial scan complete! Your libraries are ready to use.</div></div><div class=\"flex justify-between mt-6\"><button type=\"button\" @click=\"currentStep = 3\" class=\"btn btn-secondary\" :disabled=\"loading\">Back</button><div class=\"flex gap-3\"><button x-show=\"!scanStarted\" x-cloak type=\"button\" @click=\"startScan()\" class=\"btn btn-primary\" :disabled=\"loading\">Start Scan</button> <button x-show=\"scanStarted && !scanComplete\" x-cloak type=\"button\" @click=\"finishSetup()\" class=\"btn btn-secondary\" :disabled=\"loading\">Skip to Dashboard</button> <button x-show=\"scanComplete\" x-cloak type=\"button\" @click=\"finishSetup()\" class=\"btn btn-primary\" :disabled=\"loading\" x-text=\"loading ? 'Finishing...' : 'Go to Dashboard'\"></button></div></div></div></div><!-- Folder Browser Modal (reused from admin library) --><div id=\"folder-browser-modal\" class=\"hidden fixed inset-0 z-50 flex items-center justify-center\" style=\"background-color: var(--surface-overlay);\"><div class=\"card p-6 w-full max-w-md mx-4\"><div class=\"flex justify-between items-center mb-4\"><h2 class=\"text-xl font-bold text-content\">Browse Folders</h2><button type=\"button\" @click=\"hideFolderBrowser()\" class=\"icon-btn\">")
|
||
if templ_7745c5c3_Err != nil {
|
||
return templ_7745c5c3_Err
|
||
}
|
||
templ_7745c5c3_Err = Icon("close", "h-5 w-5").Render(ctx, templ_7745c5c3_Buffer)
|
||
if templ_7745c5c3_Err != nil {
|
||
return templ_7745c5c3_Err
|
||
}
|
||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "</button></div><div id=\"folder-browser-content\"><!-- Directory listings rendered by Alpine --></div></div></div></body></html>")
|
||
if templ_7745c5c3_Err != nil {
|
||
return templ_7745c5c3_Err
|
||
}
|
||
return nil
|
||
})
|
||
}
|
||
|
||
var _ = templruntime.GeneratedTemplate
|