Skip to content

Commit 286abb3

Browse files
committed
polish mods list add and edit chrome
1 parent 282eabf commit 286abb3

5 files changed

Lines changed: 135 additions & 41 deletions

File tree

web/pages/admin.edit.mod.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@
1212

1313
global $userbank, $theme;
1414

15-
new \Sbpp\View\AdminTabs([], $userbank, $theme);
16-
1715
require_once __DIR__ . '/_admin_edit_helpers.php';
1816

1917
$modId = isset($_GET['id']) ? (int) $_GET['id'] : 0;

web/themes/default/css/theme.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2130,6 +2130,11 @@ details.queue-row > summary > .row-actions {
21302130
.admins-list-card:last-child { border-bottom: none; }
21312131
.admins-list-card__body { padding: 0.75rem 1rem 0.25rem; }
21322132

2133+
.mods-list-cards { display: none; }
2134+
.mods-list-card { border-bottom: 1px solid var(--border); }
2135+
.mods-list-card:last-child { border-bottom: none; }
2136+
.mods-list-card__body { padding: 0.75rem 1rem 0.25rem; }
2137+
21332138
/* ---- Responsive ---- */
21342139
[data-mobile-menu] { display: none; }
21352140
@media (max-width: 1024px) {
@@ -2148,6 +2153,7 @@ details.queue-row > summary > .row-actions {
21482153
dance as `.ban-cards` — hidden at desktop, block at mobile. */
21492154
.log-cards { display: block; }
21502155
.admins-list-cards { display: block; }
2156+
.mods-list-cards { display: block; }
21512157
/* #1181: filter chip rows wrap onto multiple lines on mobile
21522158
instead of horizontal-scrolling, so every chip is reachable
21532159
without a swipe. The .scroll-x desktop affordance is the
@@ -2159,6 +2165,7 @@ details.queue-row > summary > .row-actions {
21592165
.ban-cards { display: none; }
21602166
.log-cards { display: none; }
21612167
.admins-list-cards { display: none; }
2168+
.mods-list-cards { display: none; }
21622169
}
21632170

21642171
/* ---- Utility classes used by templates ---- */

web/themes/default/page_admin_edit_mod.tpl

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,21 +27,26 @@
2727
template variable — no MooTools-era `$('enabled').checked = …`
2828
re-paint script.
2929
*}
30-
<div class="page-section">
30+
<div class="page-section" data-testid="editmod-section" style="max-width:48rem">
31+
<div class="mb-6">
32+
{* nofilter: name is htmlspecialchars'd on store in admin.edit.mod.php;
33+
auto-escaping here would double-encode (#1108 / #1113). *}
34+
<h1 style="font-size:var(--fs-xl);font-weight:600;margin:0" data-testid="editmod-title">
35+
Edit mod · {$name nofilter}
36+
</h1>
37+
<p class="text-sm text-muted m-0 mt-2">
38+
Update the configuration for this game mod.
39+
</p>
40+
</div>
41+
3142
<form method="post"
3243
action=""
3344
enctype="multipart/form-data"
3445
autocomplete="off"
3546
data-testid="editmod-form">
3647
{csrf_field}
3748
<div class="card">
38-
<div class="card__header">
39-
<div>
40-
<h3>Edit Mod</h3>
41-
<p>Update the configuration for this game mod.</p>
42-
</div>
43-
</div>
44-
<div class="card__body space-y-4" style="max-width:42rem">
49+
<div class="card__body space-y-4">
4550
<input type="hidden" name="insert_type" value="add">
4651

4752
{* nofilter: mod metadata is htmlspecialchars(strip_tags($_POST[…]))'d in admin.edit.mod.php before INSERT/UPDATE, so values pulled back out of `:prefix_mods` are already entity-encoded; auto-escaping the value attribute would double-encode (#1113 audit). The id="icon_hid" element is the channel the popup uploader writes into via window.opener.icon(). *}

web/themes/default/page_admin_mods_add.tpl

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,23 @@
4242
without depending on element ids. The icon-callback hidden input is
4343
`#icon_hid` to mirror the edit-mod template.
4444
*}
45-
<div class="page-section">
45+
<div class="page-section" data-testid="addmod-section" style="max-width:48rem">
4646
{if NOT $permission_add}
4747
<div class="card">
4848
<div class="card__body">
4949
<p class="text-muted">Access denied.</p>
5050
</div>
5151
</div>
5252
{else}
53+
<div class="mb-6">
54+
<h1 style="font-size:var(--fs-xl);font-weight:600;margin:0" data-testid="addmod-title">
55+
Add mod
56+
</h1>
57+
<p class="text-sm text-muted m-0 mt-2">
58+
Configure a new game mod that can be assigned to bans and servers.
59+
</p>
60+
</div>
61+
5362
<form method="post"
5463
action=""
5564
enctype="multipart/form-data"
@@ -58,13 +67,7 @@
5867
data-testid="addmod-form">
5968
{csrf_field}
6069
<div class="card">
61-
<div class="card__header">
62-
<div>
63-
<h3>Add Mod</h3>
64-
<p>Configure a new game mod that can be assigned to bans and servers.</p>
65-
</div>
66-
</div>
67-
<div class="card__body space-y-4" style="max-width:42rem">
70+
<div class="card__body space-y-4">
6871
{* #1402: the legacy `<input id="fromsub">` hidden was a vestigial
6972
reference to the v1.x ProcessMod() flow; the new submit handler
7073
has no equivalent. Replaced with `#icon_hid` so the upload-icon

web/themes/default/page_admin_mods_list.tpl

Lines changed: 104 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,20 @@
4343
</div>
4444
</div>
4545
{else}
46-
<div class="card">
47-
<div class="card__header">
48-
<div>
49-
<h3>Server Mods</h3>
50-
<p><span data-testid="mod-count">{$mod_count}</span> configured</p>
51-
</div>
52-
</div>
46+
<div class="mb-4">
47+
<h1 style="font-size:var(--fs-xl);font-weight:600;margin:0" data-testid="mods-list-title">
48+
Mods
49+
<span class="text-faint" style="font-weight:400;margin-left:0.375rem" data-testid="mod-count">({$mod_count})</span>
50+
</h1>
51+
<p class="text-sm text-muted m-0 mt-2">
52+
Game mods that can be assigned to bans and servers.
53+
</p>
54+
</div>
55+
56+
<div class="card" style="overflow:hidden">
5357
{if $mod_count > 0}
54-
<table class="table" data-testid="mods-table">
58+
<div class="table-scroll">
59+
<table class="table table--compact" data-testid="mods-table">
5560
<thead>
5661
<tr>
5762
<th style="width:40%">Name</th>
@@ -67,11 +72,11 @@
6772
{foreach from=$mod_list item=mod}
6873
<tr id="mid_{$mod.mid}" data-testid="mod-row" data-id="{$mod.mid}">
6974
<td>
70-
<div class="flex items-center gap-3">
75+
<div class="flex items-center gap-2">
7176
<img src="images/games/{$mod.icon}"
7277
alt=""
73-
width="20"
74-
height="20"
78+
width="18"
79+
height="18"
7580
loading="lazy"
7681
onerror="this.style.visibility='hidden'">
7782
<span class="font-medium">{$mod.name}</span>
@@ -87,12 +92,20 @@
8792
{/if}
8893
</td>
8994
{if $permission_editmods || $permission_deletemods}
90-
<td style="text-align:right">
91-
<div class="flex justify-end gap-2">
95+
<td class="col-actions" style="text-align:right">
96+
{* Icon-only row actions matching banlist / admins list:
97+
Lucide icon + `data-tooltip` + `aria-label` inside
98+
`.row-actions--icons`. Keep `data-testid` / `data-action`
99+
/ `data-fallback-href` wiring unchanged. *}
100+
<div class="row-actions row-actions--icons">
92101
{if $permission_editmods}
93-
<a class="btn btn--ghost btn--sm"
102+
<a class="btn btn--ghost btn--icon btn--sm"
94103
href="index.php?p=admin&c=mods&o=edit&id={$mod.mid|escape:'url'}"
95-
data-testid="editmod-link">Edit</a>
104+
data-testid="editmod-link"
105+
data-tooltip="Edit"
106+
aria-label="Edit mod {$mod.name|escape}">
107+
<i data-lucide="pencil" style="width:14px;height:14px"></i>
108+
</a>
96109
{/if}
97110
{if $permission_deletemods}
98111
{* #1397: data-action wires the delete button to the inline
@@ -110,14 +123,17 @@
110123
beyond the bug; the fallback is a graceful degradation
111124
for the rare case where the JSON dispatcher itself is
112125
missing (e.g. third-party theme that stripped api.js). *}
113-
<button class="btn btn--ghost btn--sm"
126+
<button class="btn btn--ghost btn--icon btn--sm"
114127
type="button"
115128
data-action="mod-delete"
116129
data-mid="{$mod.mid}"
117130
data-name="{$mod.name|escape}"
118131
data-fallback-href="index.php?p=admin&amp;c=mods"
119132
data-testid="deletemod-btn"
120-
aria-label="Delete mod {$mod.name|escape}">Delete</button>
133+
data-tooltip="Delete"
134+
aria-label="Delete mod {$mod.name|escape}">
135+
<i data-lucide="trash-2" style="width:14px;height:14px;color:var(--danger)"></i>
136+
</button>
121137
{/if}
122138
</div>
123139
</td>
@@ -126,6 +142,65 @@
126142
{/foreach}
127143
</tbody>
128144
</table>
145+
</div>
146+
147+
{* Mobile cards — paired surface for the global
148+
`@media (max-width: 768px) { .table { display: none } }`
149+
rule. Same display dance as `.admins-list-cards`. *}
150+
<div class="mods-list-cards" data-testid="mods-list-cards">
151+
{foreach from=$mod_list item=mod}
152+
<div class="mods-list-card" data-testid="mods-list-card" data-id="{$mod.mid}">
153+
<div class="mods-list-card__body flex items-center gap-3">
154+
<img src="images/games/{$mod.icon}"
155+
alt=""
156+
width="28"
157+
height="28"
158+
loading="lazy"
159+
onerror="this.style.visibility='hidden'">
160+
<div style="flex:1;min-width:0">
161+
<div class="font-medium text-sm truncate">{$mod.name}</div>
162+
<div class="text-xs text-muted truncate" style="margin-top:0.125rem">
163+
<span class="font-mono">{$mod.modfolder}</span>
164+
· SU {$mod.steam_universe}
165+
</div>
166+
<div style="margin-top:0.35rem">
167+
{if $mod.enabled}
168+
<span class="pill pill--online">Enabled</span>
169+
{else}
170+
<span class="pill pill--offline">Disabled</span>
171+
{/if}
172+
</div>
173+
</div>
174+
</div>
175+
{if $permission_editmods || $permission_deletemods}
176+
<div class="row-actions row-actions--icons ban-card__actions">
177+
{if $permission_editmods}
178+
<a class="btn btn--ghost btn--icon btn--sm"
179+
href="index.php?p=admin&amp;c=mods&amp;o=edit&amp;id={$mod.mid|escape:'url'}"
180+
data-testid="editmod-link-mobile"
181+
data-tooltip="Edit"
182+
aria-label="Edit mod {$mod.name|escape}">
183+
<i data-lucide="pencil" style="width:14px;height:14px"></i>
184+
</a>
185+
{/if}
186+
{if $permission_deletemods}
187+
<button class="btn btn--ghost btn--icon btn--sm"
188+
type="button"
189+
data-action="mod-delete"
190+
data-mid="{$mod.mid}"
191+
data-name="{$mod.name|escape}"
192+
data-fallback-href="index.php?p=admin&amp;c=mods"
193+
data-testid="deletemod-btn-mobile"
194+
data-tooltip="Delete"
195+
aria-label="Delete mod {$mod.name|escape}">
196+
<i data-lucide="trash-2" style="width:14px;height:14px;color:var(--danger)"></i>
197+
</button>
198+
{/if}
199+
</div>
200+
{/if}
201+
</div>
202+
{/foreach}
203+
</div>
129204
{else}
130205
<div class="card__body">
131206
<p class="text-muted">No mods configured yet.</p>
@@ -258,10 +333,13 @@
258333

259334
/**
260335
* @param {string} mid
261-
* @returns {Element|null}
336+
* @returns {NodeListOf<Element>}
262337
*/
263-
function rowForMid(mid) {
264-
return document.querySelector('[data-testid="mod-row"][data-id="' + mid + '"]');
338+
function rowsForMid(mid) {
339+
return document.querySelectorAll(
340+
'[data-testid="mod-row"][data-id="' + mid + '"],'
341+
+ '[data-testid="mods-list-card"][data-id="' + mid + '"]'
342+
);
265343
}
266344

267345
/**
@@ -276,7 +354,7 @@
276354
if (!el) return;
277355
var n = Number((el.textContent || '').replace(/[^0-9]/g, ''));
278356
if (!Number.isFinite(n) || n <= 0) return;
279-
el.textContent = String(n - 1);
357+
el.textContent = '(' + (n - 1).toLocaleString() + ')';
280358
}
281359

282360
/** @returns {HTMLDialogElement|null} */
@@ -397,8 +475,11 @@
397475
toast('error', 'Delete failed', msg);
398476
return;
399477
}
400-
var row = rowForMid(ctx.mid);
401-
if (row && row.parentNode) row.parentNode.removeChild(row);
478+
var rows = rowsForMid(ctx.mid);
479+
for (var i = 0; i < rows.length; i++) {
480+
var row = rows[i];
481+
if (row && row.parentNode) row.parentNode.removeChild(row);
482+
}
402483
decrementCount();
403484
closeDeleteDialog();
404485
toast('success', 'Mod deleted', ctx.name + ' has been removed.');

0 commit comments

Comments
 (0)