Initial commit
This commit is contained in:
156
templates/settings.templ
Normal file
156
templates/settings.templ
Normal file
@@ -0,0 +1,156 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"veola/internal/models"
|
||||
)
|
||||
|
||||
type SettingsData struct {
|
||||
Page
|
||||
Values map[string]string
|
||||
IsAdmin bool
|
||||
Users []models.User
|
||||
TestNtfyOK string
|
||||
TestApifyOK string
|
||||
PasswordMsg string
|
||||
PasswordError string
|
||||
UserMsg string
|
||||
UserError string
|
||||
}
|
||||
|
||||
templ settingsBody(d SettingsData) {
|
||||
<div class="space-y-8 max-w-3xl">
|
||||
<h1 class="text-3xl font-semibold">Settings</h1>
|
||||
|
||||
<section class="v-card p-6">
|
||||
<h2 class="font-semibold mb-4">Apify and Ntfy</h2>
|
||||
<form method="post" action="/settings" class="space-y-4">
|
||||
@CSRFInput(d.CSRFToken)
|
||||
<div>
|
||||
<label class="v-label">Apify API Key</label>
|
||||
<input class="v-input font-mono" type="password" name="apify_api_key" value={ d.Values["apify_api_key"] }/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="v-label">Ntfy Base URL</label>
|
||||
<input class="v-input" name="ntfy_base_url" value={ d.Values["ntfy_base_url"] }/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="v-label">Ntfy Default Topic</label>
|
||||
<input class="v-input" name="ntfy_default_topic" value={ d.Values["ntfy_default_topic"] }/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="v-label">Ntfy Token</label>
|
||||
<input class="v-input font-mono" type="password" name="ntfy_token" value={ d.Values["ntfy_token"] } placeholder="tk_... (leave blank if ntfy is unauthenticated)"/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="v-label">Global Poll Interval (minutes)</label>
|
||||
<input class="v-input font-mono" name="global_poll_interval_minutes" type="number" value={ d.Values["global_poll_interval_minutes"] }/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="v-label">Match Confidence Threshold</label>
|
||||
<input class="v-input font-mono" name="match_confidence_threshold" type="number" min="0" max="1" step="0.05" value={ d.Values["match_confidence_threshold"] }/>
|
||||
</div>
|
||||
if !d.IsAdmin {
|
||||
<div class="v-muted text-sm">Read-only for non-admin users.</div>
|
||||
} else {
|
||||
<div class="flex items-center gap-3 pt-1">
|
||||
<button class="v-btn" type="submit">Save</button>
|
||||
<button class="v-btn-ghost" type="submit" formaction="/settings/test-ntfy">Test Ntfy</button>
|
||||
<button class="v-btn-ghost" type="submit" formaction="/settings/test-apify">Test Apify</button>
|
||||
</div>
|
||||
}
|
||||
</form>
|
||||
if d.TestNtfyOK != "" {
|
||||
<div class="v-flash mt-3">{ d.TestNtfyOK }</div>
|
||||
}
|
||||
if d.TestApifyOK != "" {
|
||||
<div class="v-flash mt-3">{ d.TestApifyOK }</div>
|
||||
}
|
||||
</section>
|
||||
|
||||
<section class="v-card p-6">
|
||||
<h2 class="font-semibold mb-4">Change Password</h2>
|
||||
if d.PasswordError != "" {
|
||||
<div class="v-flash-error">{ d.PasswordError }</div>
|
||||
}
|
||||
if d.PasswordMsg != "" {
|
||||
<div class="v-flash">{ d.PasswordMsg }</div>
|
||||
}
|
||||
<form method="post" action="/settings/password" class="space-y-4">
|
||||
@CSRFInput(d.CSRFToken)
|
||||
<div>
|
||||
<label class="v-label">Current Password</label>
|
||||
<input class="v-input" type="password" name="current_password"/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="v-label">New Password</label>
|
||||
<input class="v-input" type="password" name="new_password"/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="v-label">Confirm New Password</label>
|
||||
<input class="v-input" type="password" name="new_password_confirm"/>
|
||||
</div>
|
||||
<button class="v-btn" type="submit">Update Password</button>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
if d.IsAdmin {
|
||||
<section class="v-card p-6">
|
||||
<h2 class="font-semibold mb-4">Users</h2>
|
||||
if d.UserError != "" {
|
||||
<div class="v-flash-error">{ d.UserError }</div>
|
||||
}
|
||||
if d.UserMsg != "" {
|
||||
<div class="v-flash">{ d.UserMsg }</div>
|
||||
}
|
||||
<table class="v-table mb-4">
|
||||
<thead><tr><th>Username</th><th>Role</th><th>Created</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
for _, u := range d.Users {
|
||||
<tr>
|
||||
<td>{ u.Username }</td>
|
||||
<td class="v-muted">{ string(u.Role) }</td>
|
||||
<td class="v-muted text-sm">{ u.CreatedAt.Format("2006-01-02") }</td>
|
||||
<td class="text-right">
|
||||
<form class="inline" method="post" action={ templ.SafeURL(fmt.Sprintf("/users/%d/reset-password", u.ID)) }>
|
||||
<input type="hidden" name="csrf_token" value={ d.CSRFToken }/>
|
||||
<input type="password" class="v-input inline-block max-w-[140px]" name="new_password" placeholder="new password"/>
|
||||
<button class="v-btn-ghost" type="submit">Reset</button>
|
||||
</form>
|
||||
<form class="inline" method="post" action={ templ.SafeURL(fmt.Sprintf("/users/%d/delete", u.ID)) } onsubmit="return confirm('Remove user?')">
|
||||
<input type="hidden" name="csrf_token" value={ d.CSRFToken }/>
|
||||
<button class="v-btn-ghost" type="submit">Remove</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
<form method="post" action="/users" class="grid md:grid-cols-4 gap-3 items-end">
|
||||
<input type="hidden" name="csrf_token" value={ d.CSRFToken }/>
|
||||
<div>
|
||||
<label class="v-label">Username</label>
|
||||
<input class="v-input" name="username"/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="v-label">Role</label>
|
||||
<select class="v-select" name="role">
|
||||
<option value="user">user</option>
|
||||
<option value="admin">admin</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="v-label">Initial Password</label>
|
||||
<input class="v-input" type="password" name="password"/>
|
||||
</div>
|
||||
<button class="v-btn" type="submit">Add User</button>
|
||||
</form>
|
||||
</section>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
templ Settings(d SettingsData) {
|
||||
@Layout(d.Page, settingsBody(d))
|
||||
}
|
||||
Reference in New Issue
Block a user