Replace emoji UI icons with a shared SVG icon system.

Add an icons sprite and helpers so nav, actions, and protocol cards use consistent stroke icons.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
orohi
2026-07-26 04:58:06 +03:00
co-authored by Cursor
parent 30ae4d476c
commit 16d14a7256
12 changed files with 398 additions and 149 deletions
+15 -14
View File
@@ -1,4 +1,5 @@
{% extends "base.html" %}
{% from "macros/icons.html" import icon %}
{% block title_extra %} — {{ _('users_title') }}{% endblock %}
@@ -7,19 +8,19 @@
<div class="flex items-center justify-between"
style="margin-bottom: var(--space-lg); gap: var(--space-md); flex-wrap: wrap;">
<h1 class="section-title" style="margin-bottom:0; flex-shrink: 0;">
<span class="icon">👥</span>
<span class="icon">{{ icon('users') }}</span>
{{ _('users_title') }}
</h1>
<div style="flex: 1; min-width: 250px; max-width: 500px; position: relative;">
<input type="text" class="form-input" id="userSearch" placeholder="{{ _('search_placeholder') }}"
oninput="handleSearch(this.value)" style="padding-left: 40px;">
<span style="position: absolute; left: 14px; top: 50%; transform: translateY(-50%); opacity: 0.5;">🔍</span>
<span style="position: absolute; left: 14px; top: 50%; transform: translateY(-50%); color: var(--text-muted); display:flex;">{{ icon('search') }}</span>
</div>
{% if current_user.role == 'admin' %}
<button class="btn btn-primary" onclick="openModal('addUserModal')" style="flex-shrink: 0;">
<span></span> {{ _('add_user') }}
{{ icon('plus') }} {{ _('add_user') }}
</button>
{% endif %}
</div>
@@ -45,7 +46,7 @@
</div>
<div id="usersEmpty" class="empty-state hidden">
<div class="empty-icon">👤</div>
<div class="empty-icon">{{ icon('user') }}</div>
<div class="empty-title">{{ _('nothing_found') }}</div>
<div class="empty-desc">{{ _('search_empty_desc') }}</div>
</div>
@@ -496,7 +497,7 @@
${u.enabled ? '' : `<span class="badge badge-danger" style="font-size:0.65rem;">${_('disabled')}</span>`}
</div>
<div class="client-meta">
<span style="cursor:pointer; color:var(--primary); text-decoration: underline;" onclick="viewUserConnections('${u.id}', '${u.username.replace(/'/g, "\\'")}')">🔗 ${u.connections_count} ${_('connections')}</span>
<span style="cursor:pointer; color:var(--primary); text-decoration: underline; display:inline-flex; align-items:center; gap:4px;" onclick="viewUserConnections('${u.id}', '${u.username.replace(/'/g, "\\'")}')">${uiIcon('link')} ${u.connections_count} ${_('connections')}</span>
${u.created_at ? `<span>📅 ${u.created_at.substring(0, 10)}</span>` : ''}
${u.telegramId ? `<span>💬 ${u.telegramId}</span>` : ''}
${u.email ? `<span>📧 ${u.email}</span>` : ''}
@@ -542,22 +543,22 @@
{% if current_user.role == 'admin' %}
<button class="btn btn-secondary btn-sm btn-icon"
onclick="openAddConnectionForUser('${u.id}', '${u.username}')"
title="${_('add_connection')}">🔗</button>
title="${_('add_connection')}">${uiIcon('link')}</button>
<button class="btn btn-secondary btn-sm btn-icon"
onclick='openEditUser({"id": "${u.id}", "username": "${u.username}", "tg": "${u.telegramId || ""}", "email": "${u.email || ""}", "desc": "${u.description || ""}", "limit": "${u.traffic_limit ? (u.traffic_limit / Math.pow(1024, 3)).toFixed(2) : 0}", "strategy": "${u.traffic_reset_strategy || 'never'}", "expiration": "${u.expiration_date || ''}"})'
title="${_('edit')}">✏️</button>
title="${_('edit')}">${uiIcon('pencil')}</button>
<button class="btn btn-secondary btn-sm btn-icon" onclick="openShareModal(this.dataset)"
data-id="${u.id}" data-username="${u.username}" data-token="${u.share_token || ''}"
data-enabled="${u.share_enabled ? 'true' : 'false'}"
data-haspassword="${u.has_share_password ? 'true' : 'false'}"
title="${_('share_access')}">📤</button>
title="${_('share_access')}">${uiIcon('share')}</button>
${u.id !== "{{ current_user.id }}" ? `
<button class="btn btn-secondary btn-sm btn-icon" onclick="toggleUser(this.dataset)"
data-id="${u.id}" data-enable="${u.enabled ? 'false' : 'true'}"
title="${u.enabled ? _('stop_btn') : _('start_btn')}">
${u.enabled ? '🔵' : '⚫'}
${u.enabled ? uiIcon('pause') : uiIcon('play')}
</button>
<button class="btn btn-danger btn-sm btn-icon" onclick="deleteUser('${u.id}')" title="${_('delete')}">🗑</button>
<button class="btn btn-danger btn-sm btn-icon" onclick="deleteUser('${u.id}')" title="${_('delete')}">${uiIcon('trash')}</button>
` : ''}
{% endif %}
</div>
@@ -860,17 +861,17 @@
listEl.innerHTML += `
<div class="client-item">
<div class="client-info">
<div class="client-avatar">🔗</div>
<div class="client-avatar">${uiIcon('link')}</div>
<div>
<div class="client-name">${c.name || 'Connection'}</div>
<div class="client-meta">
<span>🖥 ${c.server_name || ''}</span>
<span style="display:inline-flex;align-items:center;gap:4px;">${uiIcon('server')} ${c.server_name || ''}</span>
<span>${c.protocol === 'awg' ? 'AmneziaWG' : (c.protocol === 'awg2' ? 'AmneziaWG 2.0' : (c.protocol === 'awg_legacy' ? 'AWG Legacy' : (c.protocol === 'xray' ? 'Xray' : c.protocol.toUpperCase())))}</span>
</div>
</div>
</div>
<div class="client-actions">
<button class="btn btn-secondary btn-sm btn-icon" onclick="showUserConnectionConfig(${c.server_id}, '${c.client_id}', '${c.protocol}', '${(c.name || 'VPN').replace(/'/g, "\\'")}')" title="${_('config')}">📄</button>
<button class="btn btn-secondary btn-sm btn-icon" onclick="showUserConnectionConfig(${c.server_id}, '${c.client_id}', '${c.protocol}', '${(c.name || 'VPN').replace(/'/g, "\\'")}')" title="${_('config')}">${uiIcon('file')}</button>
</div>
</div>
`;
@@ -949,7 +950,7 @@
<div class="flex gap-sm">
<input class="form-input" type="text" id="shareLink" readonly
style="background: var(--bg-primary);">
<button class="btn btn-secondary" onclick="copyShareLink()">📋</button>
<button class="btn btn-secondary btn-icon" onclick="copyShareLink()" title="{{ _('copy') }}">{{ icon('copy') }}</button>
</div>
</div>
</div>