238 lines
11 KiB
HTML
238 lines
11 KiB
HTML
{{define "content"}}
|
|
<div class="row row-deck row-cards">
|
|
<!-- Theme Settings -->
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3 class="card-title"><i class="ti ti-palette"></i> Appearance</h3>
|
|
</div>
|
|
<div class="card-body">
|
|
<form action="/settings/theme" method="post">
|
|
<div class="row align-items-end">
|
|
<div class="col-auto">
|
|
<label class="form-label">Theme</label>
|
|
<select name="theme" class="form-select" style="width: 280px;">
|
|
<optgroup label="🌊 Ocean (Standard)">
|
|
<option value="ocean-auto" {{if or (not .User) (eq .User.Theme "") (eq .User.Theme "auto") (eq .User.Theme "ocean-auto")}}selected{{end}}>Ocean (System)</option>
|
|
<option value="ocean-light" {{if or (and .User (eq .User.Theme "ocean-light")) (and .User (eq .User.Theme "light"))}}selected{{end}}>Ocean Light</option>
|
|
<option value="ocean-dark" {{if or (and .User (eq .User.Theme "ocean-dark")) (and .User (eq .User.Theme "dark"))}}selected{{end}}>Ocean Dark</option>
|
|
</optgroup>
|
|
<optgroup label="🌲 Forest">
|
|
<option value="forest-auto" {{if and .User (eq .User.Theme "forest-auto")}}selected{{end}}>Forest (System)</option>
|
|
<option value="forest-light" {{if and .User (eq .User.Theme "forest-light")}}selected{{end}}>Forest Light</option>
|
|
<option value="forest-dark" {{if and .User (eq .User.Theme "forest-dark")}}selected{{end}}>Forest Dark</option>
|
|
</optgroup>
|
|
<optgroup label="🌅 Sunset">
|
|
<option value="sunset-auto" {{if and .User (eq .User.Theme "sunset-auto")}}selected{{end}}>Sunset (System)</option>
|
|
<option value="sunset-light" {{if and .User (eq .User.Theme "sunset-light")}}selected{{end}}>Sunset Light</option>
|
|
<option value="sunset-dark" {{if and .User (eq .User.Theme "sunset-dark")}}selected{{end}}>Sunset Dark</option>
|
|
</optgroup>
|
|
<optgroup label="🌹 Rose">
|
|
<option value="rose-auto" {{if and .User (eq .User.Theme "rose-auto")}}selected{{end}}>Rose (System)</option>
|
|
<option value="rose-light" {{if and .User (eq .User.Theme "rose-light")}}selected{{end}}>Rose Light</option>
|
|
<option value="rose-dark" {{if and .User (eq .User.Theme "rose-dark")}}selected{{end}}>Rose Dark</option>
|
|
</optgroup>
|
|
<optgroup label="❄️ Nord">
|
|
<option value="nord-auto" {{if and .User (eq .User.Theme "nord-auto")}}selected{{end}}>Nord (System)</option>
|
|
<option value="nord-light" {{if and .User (eq .User.Theme "nord-light")}}selected{{end}}>Nord Light</option>
|
|
<option value="nord-dark" {{if and .User (eq .User.Theme "nord-dark")}}selected{{end}}>Nord Dark</option>
|
|
</optgroup>
|
|
</select>
|
|
</div>
|
|
<div class="col-auto">
|
|
<button type="submit" class="btn btn-primary">
|
|
<i class="ti ti-device-floppy"></i> Save
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Profile Picture -->
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3 class="card-title"><i class="ti ti-camera"></i> Profile Picture</h3>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row align-items-center">
|
|
<div class="col-auto">
|
|
<span class="avatar avatar-xl rounded-circle bg-primary-lt" id="avatar-preview-container">
|
|
{{with .User}}
|
|
{{if .AvatarBase64}}
|
|
<img src="/avatar/{{.ID}}" id="avatar-preview" style="width:100%;height:100%;object-fit:cover;border-radius:50%;" alt="Avatar">
|
|
{{else}}
|
|
<i class="ti ti-user" style="font-size: 2.5rem;" id="avatar-placeholder"></i>
|
|
{{end}}
|
|
{{end}}
|
|
</span>
|
|
</div>
|
|
<div class="col">
|
|
<form action="/settings/avatar" method="post" enctype="multipart/form-data">
|
|
<div class="mb-2">
|
|
<input type="file" name="avatar" class="form-control" accept="image/png,image/jpeg,image/gif,image/webp" onchange="previewAvatar(this)">
|
|
<small class="form-hint">Max. 2 MB. PNG, JPG, GIF or WebP.</small>
|
|
</div>
|
|
<div class="d-flex gap-2">
|
|
<button type="submit" class="btn btn-primary btn-sm">
|
|
<i class="ti ti-upload"></i> Upload
|
|
</button>
|
|
</div>
|
|
</form>
|
|
{{with .User}}
|
|
{{if .AvatarBase64}}
|
|
<form action="/settings/avatar" method="post" class="mt-2">
|
|
<input type="hidden" name="remove_avatar" value="1">
|
|
<button type="submit" class="btn btn-outline-danger btn-sm">
|
|
<i class="ti ti-trash"></i> Remove Picture
|
|
</button>
|
|
</form>
|
|
{{end}}
|
|
{{end}}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Personal Settings -->
|
|
<div class="col-lg-6">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3 class="card-title"><i class="ti ti-lock"></i> Change Password</h3>
|
|
</div>
|
|
<div class="card-body">
|
|
<form action="/settings" method="post">
|
|
<div class="mb-3">
|
|
<label class="form-label required">Current Password</label>
|
|
<input type="password" name="current_password" class="form-control" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label required">New Password</label>
|
|
<input type="password" name="new_password" class="form-control" required minlength="{{if .PasswordPolicy}}{{.PasswordPolicy.MinLength}}{{else}}8{{end}}">
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label required">Confirm New Password</label>
|
|
<input type="password" name="confirm_password" class="form-control" required minlength="{{if .PasswordPolicy}}{{.PasswordPolicy.MinLength}}{{else}}8{{end}}">
|
|
</div>
|
|
{{if .PasswordPolicy}}
|
|
<div class="mb-3">
|
|
<small class="form-hint">
|
|
Password requirements: min. {{.PasswordPolicy.MinLength}} characters{{if .PasswordPolicy.RequireUpper}}, uppercase{{end}}{{if .PasswordPolicy.RequireLower}}, lowercase{{end}}{{if .PasswordPolicy.RequireDigit}}, digit{{end}}{{if .PasswordPolicy.RequireSpecial}}, special char{{end}}.
|
|
</small>
|
|
</div>
|
|
{{end}}
|
|
<div class="form-footer">
|
|
<button type="submit" class="btn btn-primary">
|
|
<i class="ti ti-lock"></i> Change Password
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- MFA Settings -->
|
|
<div class="col-lg-6">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3 class="card-title"><i class="ti ti-shield-check"></i> Two-Factor Authentication (MFA)</h3>
|
|
</div>
|
|
<div class="card-body">
|
|
{{with .User}}
|
|
{{if .MFAEnabled}}
|
|
<div class="alert alert-success">
|
|
<div class="d-flex">
|
|
<div><i class="ti ti-shield-check icon alert-icon"></i></div>
|
|
<div>
|
|
<h4 class="alert-title">MFA is enabled</h4>
|
|
<div class="text-secondary">Your account is protected with two-factor authentication.</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{{if not $.MFARequired}}
|
|
<form action="/settings/mfa/disable" method="post" onsubmit="return confirm('Are you sure you want to disable MFA? This will reduce your account security.')">
|
|
<button type="submit" class="btn btn-outline-danger">
|
|
<i class="ti ti-shield-off"></i> Disable MFA
|
|
</button>
|
|
</form>
|
|
{{else}}
|
|
<div class="text-secondary">
|
|
<i class="ti ti-info-circle"></i> MFA is enforced by your administrator and cannot be disabled.
|
|
</div>
|
|
{{end}}
|
|
{{else}}
|
|
<div class="alert alert-warning">
|
|
<div class="d-flex">
|
|
<div><i class="ti ti-alert-triangle icon alert-icon"></i></div>
|
|
<div>
|
|
<h4 class="alert-title">MFA is not enabled</h4>
|
|
<div class="text-secondary">Add an extra layer of security to your account.{{if $.MFARequired}} <strong>MFA is required by your administrator.</strong>{{end}}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<a href="/settings/mfa/setup" class="btn btn-primary">
|
|
<i class="ti ti-shield-check"></i> Enable MFA
|
|
</a>
|
|
{{end}}
|
|
{{end}}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Email Notifications -->
|
|
{{if .EmailEnabled}}
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3 class="card-title"><i class="ti ti-mail"></i> Email Notifications</h3>
|
|
</div>
|
|
<div class="card-body">
|
|
{{with .User}}
|
|
<p class="text-secondary mb-3">
|
|
Receive email notifications for certain events. Notifications are sent to <strong>{{.Email}}</strong>.
|
|
</p>
|
|
<form action="/settings/email/notify" method="post">
|
|
<div class="mb-3">
|
|
<label class="form-check form-switch">
|
|
<input type="hidden" name="email_notify_login" value="0">
|
|
<input class="form-check-input" type="checkbox" name="email_notify_login" value="1" {{if .EmailNotifyLogin}}checked{{end}} onchange="this.form.submit()">
|
|
<span class="form-check-label">Login notification</span>
|
|
<span class="form-check-description">Send an email every time someone logs into your account.</span>
|
|
</label>
|
|
</div>
|
|
</form>
|
|
{{end}}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{{end}}
|
|
|
|
</div>
|
|
<script>
|
|
function previewAvatar(input) {
|
|
if (!input.files || !input.files[0]) return;
|
|
var reader = new FileReader();
|
|
reader.onload = function(e) {
|
|
var container = document.getElementById('avatar-preview-container');
|
|
var existing = document.getElementById('avatar-preview');
|
|
var placeholder = document.getElementById('avatar-placeholder');
|
|
if (placeholder) placeholder.style.display = 'none';
|
|
if (existing) {
|
|
existing.src = e.target.result;
|
|
} else {
|
|
var img = document.createElement('img');
|
|
img.id = 'avatar-preview';
|
|
img.src = e.target.result;
|
|
img.style.cssText = 'width:100%;height:100%;object-fit:cover;border-radius:50%;';
|
|
img.alt = 'Avatar';
|
|
container.appendChild(img);
|
|
}
|
|
};
|
|
reader.readAsDataURL(input.files[0]);
|
|
}
|
|
</script>
|
|
{{end}}
|