mirror of
https://github.com/anten-ka/gotelegram_pro.git
synced 2026-05-19 12:16:01 +00:00
v2.5.0: replace keys table with responsive cards
This commit is contained in:
@@ -1138,12 +1138,12 @@ function renderUserTraffic() {
|
||||
}
|
||||
|
||||
function renderUsers() {
|
||||
const tbody = $("#usersTable");
|
||||
const container = $("#usersTable");
|
||||
if (!state.users.length) {
|
||||
tbody.innerHTML = `<tr><td colspan="6" class="empty-cell">${escapeHtml(t("noKeys"))}</td></tr>`;
|
||||
container.innerHTML = `<div class="empty empty-cell">${escapeHtml(t("noKeys"))}</div>`;
|
||||
return;
|
||||
}
|
||||
tbody.innerHTML = state.users.map((user) => {
|
||||
container.innerHTML = state.users.map((user) => {
|
||||
const pending = state.pendingUsers.has(user.name);
|
||||
const selected = user.name === state.userTrafficUser;
|
||||
const traffic = user.traffic || {};
|
||||
@@ -1151,13 +1151,12 @@ function renderUsers() {
|
||||
const activeIps = Number(traffic.active_unique_ips) || 0;
|
||||
const maxUniqueIps = Number.isFinite(Number(user.max_unique_ips)) ? Math.max(0, Number(user.max_unique_ips)) : 0;
|
||||
return `
|
||||
<tr class="${user.enabled ? "" : "disabled-row"} ${pending ? "pending-row" : ""} ${selected ? "selected-row" : ""}" data-select-user-traffic="${escapeAttr(user.name)}" aria-selected="${selected ? "true" : "false"}">
|
||||
<td data-label="${escapeAttr(t("tableUser"))}">
|
||||
<article class="key-card ${user.enabled ? "" : "disabled-row"} ${pending ? "pending-row" : ""} ${selected ? "selected-row" : ""}" data-select-user-traffic="${escapeAttr(user.name)}" aria-selected="${selected ? "true" : "false"}">
|
||||
<div class="key-card-user">
|
||||
<span class="field-label">${escapeHtml(t("tableUser"))}</span>
|
||||
<button class="key-name-button" type="button" data-user-traffic="${escapeAttr(user.name)}">
|
||||
<strong>${escapeHtml(user.name)}</strong>${user.main ? ` <small>${escapeHtml(t("main"))}</small>` : ""}
|
||||
</button>
|
||||
</td>
|
||||
<td data-label="${escapeAttr(t("tableStatus"))}">
|
||||
<div class="status-control">
|
||||
<label class="switch" title="${escapeAttr(user.main ? t("main") : (user.enabled ? t("disableKey") : t("enableKey")))}">
|
||||
<input type="checkbox" data-toggle-user="${escapeAttr(user.name)}" ${user.enabled ? "checked" : ""} ${user.main || pending ? "disabled" : ""}>
|
||||
@@ -1165,15 +1164,20 @@ function renderUsers() {
|
||||
</label>
|
||||
<strong class="${user.enabled ? "state-on" : "state-off"}">${escapeHtml(pending ? t("applying") : (user.enabled ? t("enabled") : t("disabled")))}</strong>
|
||||
</div>
|
||||
</td>
|
||||
<td data-label="${escapeAttr(t("tableSecret"))}"><code title="${escapeAttr(user.secret)}">${escapeHtml(user.secret)}</code></td>
|
||||
<td data-label="${escapeAttr(t("tableLink"))}">
|
||||
</div>
|
||||
<div class="key-card-secret">
|
||||
<span class="field-label">${escapeHtml(t("tableSecret"))}</span>
|
||||
<code title="${escapeAttr(user.secret)}">${escapeHtml(user.secret)}</code>
|
||||
</div>
|
||||
<div class="key-card-links">
|
||||
<span class="field-label">${escapeHtml(t("tableLink"))}</span>
|
||||
<div class="mini-actions">
|
||||
<button class="soft" data-copy="${escapeAttr(user.link)}" ${user.enabled ? "" : "disabled"}>${escapeHtml(t("copyLink"))}</button>
|
||||
<button class="soft" data-user-qr="${escapeAttr(user.name)}">${escapeHtml(t("showQr"))}</button>
|
||||
</div>
|
||||
</td>
|
||||
<td data-label="${escapeAttr(t("tableTraffic"))}">
|
||||
</div>
|
||||
<div class="key-card-traffic">
|
||||
<span class="field-label">${escapeHtml(t("tableTraffic"))}</span>
|
||||
<div class="traffic-cell">
|
||||
<div class="traffic-main">
|
||||
<span>
|
||||
@@ -1188,14 +1192,15 @@ function renderUsers() {
|
||||
<button class="soft" type="submit" data-ip-limit-save="${escapeAttr(user.name)}">${escapeHtml(t("saveIpLimit"))}</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
<td data-label="${escapeAttr(t("tableActions"))}">
|
||||
</div>
|
||||
<div class="key-card-actions">
|
||||
<span class="field-label">${escapeHtml(t("tableActions"))}</span>
|
||||
<div class="action-buttons">
|
||||
<button class="soft" data-copy="${escapeAttr(user.secret)}">${escapeHtml(t("copySecret"))}</button>
|
||||
<button class="danger" data-delete="${escapeAttr(user.name)}" ${user.main ? "disabled" : ""}>${escapeHtml(t("delete"))}</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</div>
|
||||
</article>
|
||||
`; }).join("");
|
||||
}
|
||||
|
||||
|
||||
@@ -194,20 +194,8 @@
|
||||
<button type="submit" data-i18n="addKey">Add key</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="table-wrap">
|
||||
<table class="keys-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-i18n="tableUser">User</th>
|
||||
<th data-i18n="tableStatus">Status</th>
|
||||
<th data-i18n="tableSecret">Secret</th>
|
||||
<th data-i18n="tableLink">Link</th>
|
||||
<th data-i18n="tableTraffic">Traffic</th>
|
||||
<th data-i18n="tableActions">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="usersTable"></tbody>
|
||||
</table>
|
||||
<div class="keys-wrap">
|
||||
<div class="keys-list" id="usersTable" aria-live="polite"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel user-traffic-panel" id="userTrafficPanel">
|
||||
@@ -400,6 +388,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="/app.js?v=2.5.0-admin16" type="module"></script>
|
||||
<script src="/app.js?v=2.5.0-admin17" type="module"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -783,6 +783,79 @@ h2 {
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.keys-wrap {
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
||||
.keys-list {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.key-card {
|
||||
display: grid;
|
||||
grid-template-columns:
|
||||
minmax(150px, .85fr)
|
||||
minmax(260px, 1.35fr)
|
||||
minmax(200px, 1fr)
|
||||
minmax(260px, 1.1fr)
|
||||
minmax(210px, .9fr);
|
||||
grid-template-areas: "user secret links traffic actions";
|
||||
gap: 12px;
|
||||
align-items: stretch;
|
||||
min-width: 0;
|
||||
padding: 14px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
background: var(--panel);
|
||||
cursor: pointer;
|
||||
transition: background .16s ease, box-shadow .16s ease, border-color .16s ease;
|
||||
}
|
||||
|
||||
.key-card:hover {
|
||||
background: color-mix(in srgb, var(--blue) 7%, var(--panel));
|
||||
}
|
||||
|
||||
.key-card.selected-row {
|
||||
border-color: color-mix(in srgb, var(--blue) 34%, var(--line));
|
||||
background: color-mix(in srgb, var(--blue) 10%, var(--panel));
|
||||
box-shadow: inset 4px 0 0 var(--blue);
|
||||
}
|
||||
|
||||
.key-card-user,
|
||||
.key-card-secret,
|
||||
.key-card-links,
|
||||
.key-card-traffic,
|
||||
.key-card-actions {
|
||||
display: grid;
|
||||
align-content: center;
|
||||
gap: 8px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.key-card-user { grid-area: user; }
|
||||
.key-card-secret { grid-area: secret; }
|
||||
.key-card-links { grid-area: links; }
|
||||
.key-card-traffic { grid-area: traffic; }
|
||||
.key-card-actions { grid-area: actions; }
|
||||
|
||||
.key-card-secret code {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
overflow-wrap: anywhere;
|
||||
word-break: break-word;
|
||||
white-space: normal;
|
||||
font-size: 13px;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.field-label {
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
font-weight: 800;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
min-width: 720px;
|
||||
@@ -863,6 +936,10 @@ td small {
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.key-name-button small {
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -899,6 +976,24 @@ td small {
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.key-card .mini-actions,
|
||||
.key-card .action-buttons {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
align-content: center;
|
||||
align-items: stretch;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.key-card .mini-actions button,
|
||||
.key-card .action-buttons button {
|
||||
width: 100%;
|
||||
min-height: 44px;
|
||||
white-space: normal;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.traffic-cell {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
@@ -1310,6 +1405,14 @@ td small {
|
||||
}
|
||||
|
||||
@media (max-width: 1280px) {
|
||||
.key-card {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
grid-template-areas:
|
||||
"user traffic"
|
||||
"secret links"
|
||||
"actions actions";
|
||||
}
|
||||
|
||||
.service-grid {
|
||||
grid-template-columns: repeat(3, minmax(150px, 1fr));
|
||||
}
|
||||
@@ -1488,6 +1591,16 @@ td small {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.key-card {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-areas:
|
||||
"user"
|
||||
"secret"
|
||||
"links"
|
||||
"traffic"
|
||||
"actions";
|
||||
}
|
||||
|
||||
.ip-limit-control {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user