Release v2.6.3: support page with SBP/card/crypto donate placeholders and admin settings.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -52,6 +52,9 @@
|
||||
<a href="/settings" class="nav-link">{{ icon('settings') }} {{ _('nav_settings') }}</a>
|
||||
{% endif %}
|
||||
<a href="/my" class="nav-link">{{ icon('layers') }} {{ _('nav_connections') }}</a>
|
||||
{% if donate_enabled %}
|
||||
<a href="/support" class="nav-link nav-link-support">{{ icon('heart') }} {{ _('nav_support') }}</a>
|
||||
{% endif %}
|
||||
</nav>
|
||||
|
||||
<div class="nav-user">
|
||||
|
||||
+49
-1
@@ -650,6 +650,41 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- BLOCK: Support / Donate (admin) -->
|
||||
{% set donate_cfg = settings.get('donate') or {} %}
|
||||
<div class="card" style="margin-top: var(--space-lg);">
|
||||
<h3 class="card-title" style="margin-bottom: var(--space-lg);">{{ icon('heart') }} {{ _('donate_settings_title') }}</h3>
|
||||
<p class="form-hint" style="margin-top: 0; margin-bottom: var(--space-md);">{{ _('donate_settings_hint') }}</p>
|
||||
<div class="form-group">
|
||||
<label class="form-label" style="display:flex; align-items:center; gap:8px; cursor:pointer;">
|
||||
<input type="checkbox" id="donate_enabled" {% if donate_cfg.get('enabled', True) %}checked{% endif %}>
|
||||
{{ _('donate_enabled') }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">{{ _('donate_intro') }}</label>
|
||||
<textarea class="form-textarea" id="donate_intro" rows="2" placeholder="{{ _('support_intro') }}">{{ donate_cfg.get('intro') or '' }}</textarea>
|
||||
</div>
|
||||
{% for key, label_key in [('sbp', 'donate_sbp'), ('card', 'donate_card'), ('crypto', 'donate_crypto')] %}
|
||||
{% set method = donate_cfg.get(key) or {} %}
|
||||
<div style="border:1px solid var(--border-color); border-radius:var(--radius-md); padding:var(--space-md); margin-bottom:var(--space-md);">
|
||||
<label class="form-label" style="display:flex; align-items:center; gap:8px; cursor:pointer; margin-bottom:var(--space-sm);">
|
||||
<input type="checkbox" id="donate_{{ key }}_enabled" {% if method.get('enabled', True) %}checked{% endif %}>
|
||||
{{ _(label_key) }}
|
||||
</label>
|
||||
<div class="form-group" style="margin-bottom:var(--space-sm);">
|
||||
<label class="form-label">{{ _('donate_method_title') }}</label>
|
||||
<input type="text" class="form-input" id="donate_{{ key }}_title" value="{{ method.get('title') or '' }}" placeholder="{{ _(label_key) }}">
|
||||
</div>
|
||||
<div class="form-group" style="margin-bottom:0;">
|
||||
<label class="form-label">{{ _('donate_method_details') }}</label>
|
||||
<textarea class="form-textarea" id="donate_{{ key }}_details" rows="3" placeholder="{{ _('donate_details_placeholder') }}">{{ method.get('details') or '' }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<p class="form-hint">{{ _('donate_preview_hint') }} <a href="/support" target="_blank" rel="noopener">/support</a></p>
|
||||
</div>
|
||||
|
||||
<!-- BLOCK: About & Updates -->
|
||||
<div class="card" style="margin-top: var(--space-lg);">
|
||||
<h3 class="card-title" style="margin-bottom: var(--space-lg);">ℹ️ {{ _('about_title') }}</h3>
|
||||
@@ -1478,11 +1513,24 @@
|
||||
create_xui_panel_id: document.getElementById('guest_create_xui_panel')?.value || '',
|
||||
};
|
||||
|
||||
const donateMethod = (key) => ({
|
||||
enabled: document.getElementById(`donate_${key}_enabled`).checked,
|
||||
title: document.getElementById(`donate_${key}_title`).value.trim(),
|
||||
details: document.getElementById(`donate_${key}_details`).value.trim(),
|
||||
});
|
||||
const donate = {
|
||||
enabled: document.getElementById('donate_enabled').checked,
|
||||
intro: document.getElementById('donate_intro').value.trim(),
|
||||
sbp: donateMethod('sbp'),
|
||||
card: donateMethod('card'),
|
||||
crypto: donateMethod('crypto'),
|
||||
};
|
||||
|
||||
try {
|
||||
const res = await fetch('/api/settings/save', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ appearance, sync, captcha, telegram, ssl, guest })
|
||||
body: JSON.stringify({ appearance, sync, captcha, telegram, ssl, guest, donate })
|
||||
});
|
||||
if (!res.ok) throw new Error(_('error'));
|
||||
showToast(_('settings_saved'), 'success');
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
{% extends "base.html" %}
|
||||
{% from "macros/icons.html" import icon %}
|
||||
|
||||
{% block title_extra %} — {{ _('nav_support') }}{% endblock %}
|
||||
|
||||
{% block head_extra %}
|
||||
<style>
|
||||
.support-hero {
|
||||
text-align: center;
|
||||
padding: var(--space-xl) var(--space-md);
|
||||
margin-bottom: var(--space-xl);
|
||||
}
|
||||
.support-hero-icon {
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: var(--space-md);
|
||||
color: var(--accent);
|
||||
}
|
||||
.support-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
|
||||
gap: var(--space-lg);
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.support-card {
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: var(--space-lg);
|
||||
background: var(--bg-secondary);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-md);
|
||||
}
|
||||
.support-card-title {
|
||||
font-weight: 700;
|
||||
font-size: 1.05rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-sm);
|
||||
}
|
||||
.support-details {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||
font-size: 0.85rem;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
padding: var(--space-sm);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--bg-primary);
|
||||
border: 1px dashed var(--border-color);
|
||||
min-height: 4rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
.support-details.has-content {
|
||||
color: var(--text-primary);
|
||||
border-style: solid;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="support-hero">
|
||||
<div class="support-hero-icon">{{ icon('heart') }}</div>
|
||||
<h1 class="section-title" style="justify-content: center; margin-bottom: var(--space-sm);">
|
||||
{{ _('support_title') }}
|
||||
</h1>
|
||||
<p class="text-muted" style="max-width: 560px; margin: 0 auto; line-height: 1.5;">
|
||||
{% if donate.get('intro') %}
|
||||
{{ donate.intro }}
|
||||
{% else %}
|
||||
{{ _('support_intro') }}
|
||||
{% endif %}
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<div class="support-grid">
|
||||
{% set methods = [
|
||||
('sbp', _('donate_sbp'), '💳'),
|
||||
('card', _('donate_card'), '🏦'),
|
||||
('crypto', _('donate_crypto'), '₿'),
|
||||
] %}
|
||||
{% for key, default_title, emoji in methods %}
|
||||
{% set method = donate.get(key) or {} %}
|
||||
{% if method.get('enabled', True) %}
|
||||
<div class="card support-card">
|
||||
<div class="support-card-title">
|
||||
<span aria-hidden="true">{{ emoji }}</span>
|
||||
<span>{{ method.get('title') or default_title }}</span>
|
||||
</div>
|
||||
{% set details = (method.get('details') or '').strip() %}
|
||||
<div class="support-details{% if details %} has-content{% endif %}" id="donate-{{ key }}-details">{{ details or _('donate_details_soon') }}</div>
|
||||
{% if details %}
|
||||
<button type="button" class="btn btn-secondary btn-sm" onclick="copyToClipboard(document.getElementById('donate-{{ key }}-details').textContent)">
|
||||
{{ icon('copy') }} {{ _('copy') }}
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<p class="text-muted text-sm" style="text-align: center; margin-top: var(--space-xl); max-width: 520px; margin-left: auto; margin-right: auto;">
|
||||
{{ _('support_footer') }}
|
||||
</p>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user