feat: image variants settings screens

This commit is contained in:
fsylvain committed 2026-10-08 13:57:41 +02:00
1 parent f1d56ef50e
commit f567866161
42 files changed
+7192 -11

No files matched your search

+1 -1
View File
@@ -70,7 +70,7 @@ Responsive 400 px card from sm up, with its top 12 vh from the top. Below sm , t
### DashboardShell
The sidebar has seven destinations for owner and admin (Home, Articles, Media, Users, Activity, Integration, Settings; regular users get Home, Articles, Media, Activity), and the user menu sits at its foot. The top bar carries the breadcrumbs and page title. Collapsing the sidebar leaves a 56 px icon rail. Below lg , the sidebar becomes a slideover.
The sidebar has seven destinations for owner and admin (Home, Articles, Media, Users, Activity, Integration, Settings; regular users get Home, Articles, Media, Activity), and the user menu sits at its foot. For owner and admin, Settings expands to a Media group with Variants and Cache (see "Sidebar entry (1l)" in [image-variants](screens/image-variants.md)); it is built with the `children` of UNavigationMenu, and the same group appears in the mobile drawer. The top bar carries the breadcrumbs and page title. Collapsing the sidebar leaves a 56 px icon rail. Below lg , the sidebar becomes a slideover.
RenewCMS
+2
View File
@@ -27,6 +27,8 @@ A global `router.beforeEach` guard reads `meta.auth`. On the first navigation it
| `/invite/accept` | AuthShell | public | AcceptInvitePage | GET /v1/auth/invite?c=* · POST /v1/auth/invite/accept* |
| `/:pathMatch(.*)*` | either | any | NotFoundPage | none |
The sidebar reaches `/settings/media/variants` and `/settings/media/cache` through the Media group under Settings (owner and admin only). See [image-variants](screens/image-variants.md), frame 1l.
`/media` and `/article/:id/edit` also gain image variants: GET /v1/media/{id}/variants · POST|DELETE /v1/media/{id}/variants/{name} and the `variants` and `sort=total_size` query parameters on GET /v1/media/ (proposed). See [image-variants](screens/image-variants.md).
Auth values: `public` needs no session, `session` needs a logged-in account (verified or not), `verified` needs a verified account, `admin` needs an admin or owner account, `any` applies to redirects and the catch-all.
+3
View File
@@ -31,6 +31,9 @@ Admin-only page under Settings → Media, with two tabs: Variants and Cache. Des
| 1i | Delete confirm | Delete in the row menu. |
| 1j | Delete in progress | Delete was confirmed. The button shows a spinner. |
| 1k | Request reference | Mobile scrolled to the reference card. On desktop it is visible below the table. |
| 1l | Entry: sidebar | From any page, Settings expands in the sidebar to Media: Variants and Cache. Admin only. On mobile the menu button opens it as a drawer. |
**Sidebar entry (1l).** For owner and admin, the Settings item has a chevron (right when closed, down when open) and expands to a group: a muted uppercase label "Media" (11 px, semibold, 0.04em tracking), then "Variants" (layers icon) and "Cache" (database icon). The group is indented 19 px with a 1 px left border; its items are 32 px high (44 px in the mobile drawer) with 14 px icons. The group is open on every `/settings` route. When a subpage is current, its item takes the sand fill and Settings keeps only the highlighted text and medium weight. On `/settings` itself Settings is the current item. Other roles see no chevron and no group. Cache stays inert until its screen exists.
### Copy: Variants list
@@ -113,6 +113,7 @@ class Component extends DCLogic {
st('1h', 'Purge confirm', 'Purge cache in the row menu.', l({}, { modal: 'purgeVariant' })),
st('1i', 'Delete confirm', 'Delete in the row menu.', l({}, { modal: 'deleteVariant' })),
st('1j', 'Delete in progress', 'Delete was confirmed. The button shows a spinner.', l({}, { modal: 'deleteVariant', loadingModal: true })),
st('1l', 'Entry: sidebar', 'From any page, Settings expands in the sidebar to Media: Variants and Cache. Admin only. On mobile the menu button opens it as a drawer.', { kind: 'media', ...MB, sideOpen: true, ml: {}, drawer: true }),
st('1k', 'Request reference', 'Mobile scrolled to the reference card. On desktop it is visible below the table.', l({}), { scroll: 900 })
],
tables: [tb('Copy: Variants list', [
+10 -4
View File
@@ -86,10 +86,15 @@ class Component extends DCLogic {
// ---------- shell
const NAV = [['home', 'house', 'Home'], ['articles', 'file-text', 'Articles'], ['media', 'images', 'Media'], ['users', 'users', 'Users'], ['integration', 'plug', 'Integration'], ['settings', 'settings', 'Settings']]
const nav = s.role === 'user' ? NAV.filter(n => ['home', 'articles', 'media'].includes(n[0])) : NAV
const side = D('width:224px;flex-shrink:0;background:var(--ui-bg);border-right:1px solid var(--ui-border);display:flex;flex-direction:column;padding:12px;gap:2px',
D('display:flex;align-items:center;gap:10px;height:40px;padding:0 6px;margin-bottom:10px', mark(26), S('font-weight:600;font-size:14px;color:var(--ui-text-highlighted)', 'RenewCMS')),
...nav.map(([k, ic, t]) => D('display:flex;align-items:center;gap:10px;height:32px;padding:0 10px;background:' + (k === s.nav ? 'var(--rc-sand)' : 'transparent') + ';color:' + (k === s.nav ? 'var(--rc-on-sand)' : 'var(--ui-text-toned)') + ';font-weight:' + (k === s.nav ? 500 : 400), I(ic), t)),
D('flex:1'), D('display:flex;align-items:center;gap:10px;height:40px;padding:0 6px;color:var(--ui-text-highlighted)', S('width:26px;height:26px;flex-shrink:0;background:var(--rc-sand);color:var(--rc-on-sand);display:flex;align-items:center;justify-content:center;font:600 11px \'IBM Plex Sans\',sans-serif', 'MA'), S('flex:1;font-weight:500', 'Marta Alves'), I('chevrons-up-down', 14)))
const Side = dr => {
const open = s.nav === 'settings' || s.sideOpen, act = s.nav === 'settings' ? (s.crumbs || []).slice(-1)[0] : null, h = dr ? 44 : 32
const row = (ic, t, on, pl, tail, hi) => D('display:flex;align-items:center;gap:10px;height:' + h + 'px;padding:0 10px 0 ' + pl + 'px;background:' + (on ? 'var(--rc-sand)' : 'transparent') + ';color:' + (on ? 'var(--rc-on-sand)' : hi ? 'var(--ui-text-highlighted)' : 'var(--ui-text-toned)') + ';font-weight:' + (on || hi ? 500 : 400), I(ic, pl > 10 ? 14 : undefined), S('flex:1', t), tail)
return D('width:' + (dr ? '100%' : '224px') + ';height:' + (dr ? '100%' : 'auto') + ';flex-shrink:0;background:var(--ui-bg);' + (dr ? 'box-shadow:var(--rc-shadow-modal);' : 'border-right:1px solid var(--ui-border);') + 'display:flex;flex-direction:column;padding:12px;gap:2px',
D('display:flex;align-items:center;gap:10px;height:40px;padding:0 6px;margin-bottom:10px', mark(26), S('flex:1;font-weight:600;font-size:14px;color:var(--ui-text-highlighted)', 'RenewCMS'), dr && I('x', 20)),
...nav.flatMap(([k, ic, t]) => k !== 'settings' ? [row(ic, t, k === s.nav, 10)] : [row(ic, t, false, 10, I(open ? 'chevron-down' : 'chevron-right', 14), open), open && D('display:flex;flex-direction:column;gap:2px;margin-left:19px;border-left:1px solid var(--ui-border)', S('padding:6px 10px 2px 12px;font-size:11px;line-height:16px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;color:var(--ui-text-muted)', 'Media'), row('layers', 'Variants', act === 'Variants', 12), row('database', 'Cache', act === 'Cache', 12))]),
D('flex:1'), D('display:flex;align-items:center;gap:10px;height:40px;padding:0 6px;color:var(--ui-text-highlighted)', S('width:26px;height:26px;flex-shrink:0;background:var(--rc-sand);color:var(--rc-on-sand);display:flex;align-items:center;justify-content:center;font:600 11px \'IBM Plex Sans\',sans-serif', 'MA'), S('flex:1;font-weight:500', 'Marta Alves'), I('chevrons-up-down', 14)))
}
const side = Side(false)
const crumbs = s.crumbs || []
const topbar = m
? D('height:56px;flex-shrink:0;border-bottom:1px solid var(--ui-border);background:var(--ui-bg);display:flex;align-items:center;padding:0 6px;gap:4px;color:var(--ui-text-highlighted)', S('width:44px;height:44px;display:flex;align-items:center;justify-content:center', I(s.back ? 'arrow-left' : 'menu', 20)), S('flex:1;font-weight:600;font-size:16px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap', s.mTitle || crumbs[crumbs.length - 1]))
@@ -319,6 +324,7 @@ class Component extends DCLogic {
k === 'edit' && (m ? D('position:absolute;inset:0;z-index:40;display:flex', EditPanel()) : D('position:absolute;inset:0;z-index:40;display:flex;justify-content:flex-end;background:var(--rc-overlay)', EditPanel())),
k === 'detail' && (DT.preview ? [DetailPanel(), PrevDlg()] : DetailPanel()),
k === 'editor' && ED.picker && Picker(),
m && s.drawer && D('position:absolute;inset:0;z-index:60;display:flex;background:var(--rc-overlay)', D('width:300px;max-width:85%;height:100%', Side(true))),
s.modal && Modal(), s.toasts && Toasts())
return { view: root }
}