- Bulk-load recent price points per item and render a sparkline in the items list (new LoadRecentPriceHistory query avoids N+1). - Add retro.css visual layer and refreshed login/items/layout styling. - Swap the logo from webp to avif. - Pin htmx/Chart.js/Tailwind/templ versions in the Makefile with vendor / tools / update-deps targets; README documents the dependency-bump flow and the hardened systemd deploy. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
106 lines
3.3 KiB
Plaintext
106 lines
3.3 KiB
Plaintext
package templates
|
|
|
|
type LoginData struct {
|
|
Page
|
|
Error string
|
|
Username string
|
|
}
|
|
|
|
templ loginBody(d LoginData) {
|
|
<div class="min-h-screen grid md:grid-cols-2 v-auth-grid">
|
|
<div class="flex items-center justify-center p-8">
|
|
<div class="w-full max-w-sm">
|
|
<div class="flex items-center gap-2 mb-8 v-auth-wordmark">
|
|
<span class="text-3xl">🐝</span>
|
|
<span class="text-2xl font-semibold tracking-wide">Veola</span>
|
|
</div>
|
|
<div class="v-card v-auth-card p-7">
|
|
<h1 class="text-3xl font-semibold mb-2">Open the door.</h1>
|
|
<p class="v-muted mb-6">Sign in to continue.</p>
|
|
if d.Error != "" {
|
|
<div class="v-flash-error">{ d.Error }</div>
|
|
}
|
|
<form method="post" action="/login" class="space-y-4">
|
|
@CSRFInput(d.CSRFToken)
|
|
<div>
|
|
<label class="v-label">Username</label>
|
|
<input class="v-input" name="username" autocomplete="username" autofocus value={ d.Username }/>
|
|
</div>
|
|
<div>
|
|
<label class="v-label">Password</label>
|
|
<input class="v-input" type="password" name="password" autocomplete="current-password"/>
|
|
</div>
|
|
<button class="v-btn w-full justify-center" type="submit">Sign In</button>
|
|
</form>
|
|
</div>
|
|
<p class="v-auth-tagline">Track · Watch · Notice</p>
|
|
</div>
|
|
</div>
|
|
<div class="hidden md:flex flex-col items-center justify-center p-8 v-auth-portrait-col">
|
|
<div class="v-auth-portrait-halo">
|
|
<div class="v-veola-portrait max-w-md">
|
|
<img src="/static/img/veola.avif" alt="Veola"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
templ Login(d LoginData) {
|
|
@Bare(d.Page, loginBody(d))
|
|
}
|
|
|
|
type SetupData struct {
|
|
Page
|
|
Error string
|
|
Username string
|
|
}
|
|
|
|
templ setupBody(d SetupData) {
|
|
<div class="min-h-screen grid md:grid-cols-2 v-auth-grid">
|
|
<div class="flex items-center justify-center p-8">
|
|
<div class="w-full max-w-sm">
|
|
<div class="flex items-center gap-2 mb-8 v-auth-wordmark">
|
|
<span class="text-3xl">🐝</span>
|
|
<span class="text-2xl font-semibold tracking-wide">Veola</span>
|
|
</div>
|
|
<div class="v-card v-auth-card p-7">
|
|
<h1 class="text-3xl font-semibold mb-2">First time here.</h1>
|
|
<p class="v-muted mb-6">Create the admin account. Password must be at least 12 characters.</p>
|
|
if d.Error != "" {
|
|
<div class="v-flash-error">{ d.Error }</div>
|
|
}
|
|
<form method="post" action="/setup" class="space-y-4">
|
|
@CSRFInput(d.CSRFToken)
|
|
<div>
|
|
<label class="v-label">Username</label>
|
|
<input class="v-input" name="username" autofocus value={ d.Username }/>
|
|
</div>
|
|
<div>
|
|
<label class="v-label">Password</label>
|
|
<input class="v-input" type="password" name="password"/>
|
|
</div>
|
|
<div>
|
|
<label class="v-label">Confirm Password</label>
|
|
<input class="v-input" type="password" name="password_confirm"/>
|
|
</div>
|
|
<button class="v-btn w-full justify-center" type="submit">Create Admin</button>
|
|
</form>
|
|
</div>
|
|
<p class="v-auth-tagline">Track · Watch · Notice</p>
|
|
</div>
|
|
</div>
|
|
<div class="hidden md:flex flex-col items-center justify-center p-8 v-auth-portrait-col">
|
|
<div class="v-auth-portrait-halo">
|
|
<div class="v-veola-portrait max-w-md">
|
|
<img src="/static/img/veola.avif" alt="Veola"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
templ Setup(d SetupData) {
|
|
@Bare(d.Page, setupBody(d))
|
|
}
|