chore: stage changes before history rewrite
This commit is contained in:
141
app/templates/admin.html
Normal file
141
app/templates/admin.html
Normal file
@@ -0,0 +1,141 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}{{ 'admin.title'|t(user.language) }} - {{ 'brand'|t(user.language) }}{% endblock %}
|
||||
{% block content %}
|
||||
<div class="section-header">
|
||||
<h1>{{ 'admin.title'|t(user.language) }}</h1>
|
||||
</div>
|
||||
|
||||
<div class="tabs" id="tabs">
|
||||
<button class="tab {% if active_tab != 'config' %}active{% endif %}" onclick="switchTab('invites')">{{ 'admin.tab_invites'|t(user.language) }}</button>
|
||||
<button class="tab {% if active_tab == 'config' %}active{% endif %}" onclick="switchTab('config')">{{ 'admin.tab_config'|t(user.language) }}</button>
|
||||
</div>
|
||||
|
||||
{% if success %}
|
||||
<div class="alert alert-success">{{ success }}</div>
|
||||
{% endif %}
|
||||
{% if error %}
|
||||
<div class="alert alert-error">{{ error }}</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- ═══ Tab: Invites ═══ -->
|
||||
<div id="tab-invites" class="tab-panel" {% if active_tab == 'config' %}style="display:none"{% endif %}>
|
||||
<div class="form-card" style="margin-bottom:1.5rem">
|
||||
<h3 style="margin-bottom:.75rem">{{ 'invites.create'|t(user.language) }}</h3>
|
||||
<form method="post" action="/admin/invites/add" style="display:flex;flex-direction:column;gap:.85rem">
|
||||
<div>
|
||||
<label style="margin-top:0">{{ 'invites.code_label'|t(user.language) }}</label>
|
||||
<input type="text" name="code" maxlength="8" placeholder="{{ 'invites.code_ph'|t(user.language) }}" style="text-transform:uppercase">
|
||||
</div>
|
||||
<div>
|
||||
<label style="margin-top:0">{{ 'invites.limit_type'|t(user.language) }}</label>
|
||||
<select name="limit_type" id="limit_type" onchange="toggleLimitFields()">
|
||||
<option value="unlimited">{{ 'invites.unlimited'|t(user.language) }}</option>
|
||||
<option value="uses">{{ 'invites.uses'|t(user.language) }}</option>
|
||||
<option value="expires">{{ 'invites.expires'|t(user.language) }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="max_uses_field" style="display:none;align-items:center;gap:.3rem">
|
||||
<label style="margin:0;font-size:.8rem;white-space:nowrap">{{ 'invites.times'|t(user.language) }}</label>
|
||||
<input type="number" name="max_uses" min="1" value="1" class="aif-input aif-short">
|
||||
</div>
|
||||
<div id="expires_field" style="display:none;align-items:center;gap:.3rem">
|
||||
<label style="margin:0;font-size:.8rem;white-space:nowrap">{{ 'invites.expires'|t(user.language) }}</label>
|
||||
<input type="datetime-local" name="expires_at" class="aif-input aif-date">
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-primary">{{ 'invites.create_btn'|t(user.language) }}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% if codes %}
|
||||
<div class="postcard-list">
|
||||
{% for c in codes %}
|
||||
<div class="postcard-row" style="flex-direction:column;align-items:stretch;gap:.75rem">
|
||||
<div style="display:flex;align-items:center;gap:1rem;flex-wrap:wrap">
|
||||
<strong style="font-family:monospace;font-size:1.1rem">{{ c.code }}</strong>
|
||||
<span style="font-size:.8rem;color:var(--text-muted)">{{ 'invites.creator'|t(user.language) }}: {{ c.creator.username if c.creator else '-' }}</span>
|
||||
<span style="font-size:.8rem;color:var(--text-muted)">{{ c.created_at|to_local|strftime('%Y-%m-%d %H:%M') }}</span>
|
||||
{% if c.max_uses is not none %}
|
||||
<span class="badge">{{ 'invites.used'|t(user.language) }} {{ c.use_count }} / {{ c.max_uses }}</span>
|
||||
{% else %}
|
||||
<span class="badge">{{ 'invites.used'|t(user.language) }} {{ c.use_count }} / ∞</span>
|
||||
{% endif %}
|
||||
{% if c.expires_at %}
|
||||
<span class="badge">{{ 'invites.expires_at'|t(user.language) }}: {{ c.expires_at|to_local|strftime('%Y-%m-%d %H:%M') }}</span>
|
||||
{% else %}
|
||||
<span class="badge">{{ 'invites.no_expire'|t(user.language) }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if c.registered_users %}
|
||||
<div style="font-size:.8rem;color:var(--text-muted)">{{ 'invites.registered_users'|t(user.language) }}: {{ c.registered_users|map(attribute='username')|join(', ') }}</div>
|
||||
{% endif %}
|
||||
<form method="post" action="/admin/invites/{{ c.id }}/edit" class="admin-invite-form" id="edit-form-{{ c.id }}">
|
||||
<div class="aif-field">
|
||||
<select name="limit_type" class="aif-select" onchange="toggleEditFields({{ c.id }}, this.value)">
|
||||
<option value="unlimited"{% if c.max_uses is none and not c.expires_at %} selected{% endif %}>{{ 'invites.unlimited'|t(user.language) }}</option>
|
||||
<option value="uses"{% if c.max_uses is not none %} selected{% endif %}>{{ 'invites.uses'|t(user.language) }}</option>
|
||||
<option value="expires"{% if c.expires_at %} selected{% endif %}>{{ 'invites.expires'|t(user.language) }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div style="display:{% if c.max_uses is not none %}inline-flex{% else %}none{% endif %};align-items:center;gap:.3rem;align-self:flex-end">
|
||||
<label style="margin:0;font-size:.8rem;white-space:nowrap">{{ 'invites.times'|t(user.language) }}</label>
|
||||
<input type="number" name="max_uses" value="{{ c.max_uses or 1 }}" min="1" class="aif-input aif-short">
|
||||
</div>
|
||||
<div style="display:{% if c.expires_at %}inline-flex{% else %}none{% endif %};align-items:center;gap:.3rem;align-self:flex-end">
|
||||
<label style="margin:0;font-size:.8rem;white-space:nowrap">{{ 'invites.expires'|t(user.language) }}</label>
|
||||
<input type="datetime-local" name="expires_at" value="{{ c.expires_at|to_local|strftime('%Y-%m-%dT%H:%M') if c.expires_at else '' }}" class="aif-input aif-date">
|
||||
</div>
|
||||
<div style="display:flex;gap:.4rem;align-self:flex-end">
|
||||
<button type="submit" class="btn btn-sm">{{ 'invites.save'|t(user.language) }}</button>
|
||||
<button type="button" class="btn btn-danger btn-sm" onclick="if(confirm('{{ 'invites.confirm_delete'|t(user.language) }}'))this.closest('form').nextElementSibling.submit()">{{ 'invites.delete'|t(user.language) }}</button>
|
||||
</div>
|
||||
</form>
|
||||
<form method="post" action="/admin/invites/{{ c.id }}/delete" style="display:none"></form>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="empty-state">
|
||||
<div class="empty-icon">🔑</div>
|
||||
<p>{{ 'invites.empty'|t(user.language) }}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- ═══ Tab: System Config ═══ -->
|
||||
<div id="tab-config" class="tab-panel" {% if active_tab != 'config' %}style="display:none"{% endif %}>
|
||||
<div class="form-card">
|
||||
<h3 style="margin-bottom:.75rem">{{ 'admin.config_title'|t(user.language) }}</h3>
|
||||
<form method="post" action="/admin/config">
|
||||
<label>{{ 'admin.copyright_label'|t(user.language) }}</label>
|
||||
<textarea name="copyright_text" rows="3" style="width:100%;resize:vertical">{{ config.get('copyright_text', '') }}</textarea>
|
||||
<p style="font-size:.8rem;color:var(--text-muted);margin-top:.25rem">{{ 'admin.copyright_hint'|t(user.language) }}</p>
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-primary">{{ 'admin.save_config'|t(user.language) }}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function switchTab(name) {
|
||||
document.querySelectorAll('.tab').forEach(function(t) { t.classList.remove('active'); });
|
||||
document.querySelectorAll('.tab-panel').forEach(function(p) { p.style.display = 'none'; });
|
||||
event.currentTarget.classList.add('active');
|
||||
document.getElementById('tab-' + name).style.display = '';
|
||||
}
|
||||
function toggleLimitFields() {
|
||||
var t = document.getElementById('limit_type').value;
|
||||
document.getElementById('max_uses_field').style.display = t === 'uses' ? 'inline-flex' : 'none';
|
||||
document.getElementById('expires_field').style.display = t === 'expires' ? 'inline-flex' : 'none';
|
||||
}
|
||||
function toggleEditFields(id, val) {
|
||||
var form = document.getElementById('edit-form-' + id);
|
||||
var num = form.querySelector('[name="max_uses"]').parentElement;
|
||||
var date = form.querySelector('[name="expires_at"]').parentElement;
|
||||
num.style.display = val === 'uses' ? 'inline-flex' : 'none';
|
||||
date.style.display = val === 'expires' ? 'inline-flex' : 'none';
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user