Remove the Settings cog and its Preferences menu from the sidebar footer
CI / full-gate (push) Successful in 2m37s
CI / full-gate (push) Successful in 2m37s
This commit is contained in:
@@ -101,6 +101,7 @@ test("the public landing at / is ungated and links to sign in + register", async
|
||||
await expect(page.locator(".landing")).toBeVisible();
|
||||
// the same app shell every page renders — the menu shows even signed out (permission-filtered).
|
||||
await expect(page.locator(".sidebar")).toBeVisible();
|
||||
await expect(page.locator('.side-footer use[href="#i-gear"]')).toHaveCount(0); // no settings cog to offer a signed-out visitor
|
||||
await expect(page.getByRole("link", { name: "Log in" })).toHaveAttribute("href", "/login");
|
||||
await expect(page.getByRole("link", { name: "Create account" })).toHaveAttribute("href", "/registration");
|
||||
await shot(page, "live-05-public-landing");
|
||||
|
||||
@@ -282,7 +282,7 @@ span.nav-self { cursor: default; } /* static / non-clickable */
|
||||
outline: 2px solid var(--focus); outline-offset: 1px;
|
||||
}
|
||||
|
||||
/* profile / settings row */
|
||||
/* profile row */
|
||||
.footer-actions { display: flex; align-items: center; gap: 4px; }
|
||||
.profile {
|
||||
display: flex; align-items: center; gap: 9px; flex: 1 1 auto;
|
||||
|
||||
@@ -151,9 +151,7 @@ const messages = {
|
||||
"shell.guest": "Guest",
|
||||
"shell.mainNav": "Main navigation",
|
||||
"shell.openMenu": "Open menu",
|
||||
"shell.preferences": "Preferences",
|
||||
"shell.profile": "Profile",
|
||||
"shell.settings": "Settings",
|
||||
"shell.sidebar": "Primary",
|
||||
"shell.signedInAs": "Signed in as {{name}}",
|
||||
"shell.signIn": "Sign in",
|
||||
|
||||
@@ -137,9 +137,7 @@ const messages: CoreMessages = {
|
||||
"shell.guest": "Gäst",
|
||||
"shell.mainNav": "Huvudmeny",
|
||||
"shell.openMenu": "Öppna menyn",
|
||||
"shell.preferences": "Inställningar",
|
||||
"shell.profile": "Profil",
|
||||
"shell.settings": "Inställningar",
|
||||
"shell.sidebar": "Primär",
|
||||
"shell.signedInAs": "Inloggad som {{name}}",
|
||||
"shell.signIn": "Logga in",
|
||||
|
||||
@@ -16,7 +16,6 @@ export const ICON_NAMES: Record<string, string> = {
|
||||
"i-copy": "copy",
|
||||
"i-download": "download",
|
||||
"i-edit": "pencil",
|
||||
"i-gear": "settings",
|
||||
"i-globe": "globe",
|
||||
"i-grid": "layout-grid",
|
||||
"i-kebab": "ellipsis-vertical",
|
||||
|
||||
@@ -40,6 +40,9 @@ test("app shell renders sidebar, topbar and the content slot", async () => {
|
||||
assert.match(html, /<form class="menu-item-form" method="post" action="\/logout">/);
|
||||
assert.match(html, /<input type="hidden" name="_csrf" value="tok\.sig" \/>/);
|
||||
|
||||
// The footer carries the profile and the language picker only — no settings/preferences menu.
|
||||
assert.doesNotMatch(html, /i-gear|Preferences/);
|
||||
|
||||
// Branding, document title, and the inlined icon sprite (so <use> resolves).
|
||||
assert.match(html, /Acme Console/);
|
||||
assert.match(html, /<title>People<\/title>/);
|
||||
@@ -51,6 +54,7 @@ test("app shell offers Sign in (not Sign out) to an anonymous visitor — so a p
|
||||
const html = await render({ title: "Overview", brand: { name: "Acme" }, nav: "", body: "x" }); // no user, no signInHref → default
|
||||
assert.match(html, /href="\/login"[^>]*>[\s\S]*?Sign in/); // a path to sign in (default target)
|
||||
assert.doesNotMatch(html, /action="\/logout"/); // a guest has no session to end
|
||||
assert.doesNotMatch(html, /i-gear|Preferences/); // nor any settings menu to open
|
||||
|
||||
// When chrome supplies signInHref (the current page as return_to), the link carries it.
|
||||
const withReturn = await render({ title: "Overview", brand: { name: "Acme" }, nav: "", body: "x", signInHref: "/login?return_to=%2Fscheduling" });
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
## Unfinnished work
|
||||
|
||||
- [ ] There is a "Settings" in the bottom left (a little cog) showing a "Preferences" in a little menu when clicked. That is not in any spec, it exists when not even logged in and erh. Just remove.
|
||||
- [ ] On the first page there is a button saying "Log in" and in the bottom left corner another button says "Sign in". Use a uniform language.
|
||||
- [ ] When logged in, there is a "profile" link in the little box when I've clicked my username in the bottom left corner. There is no profile, so the link is dead. Remove it.
|
||||
- [ ] The little menues, like when choosing language or clicking my username, they do not dissapear when clicking outside them, I must click the original trigger or choose something. See if there are more modern ways of handling this with HTML and CSS. I think there is a modal-thing or something?
|
||||
@@ -46,6 +45,7 @@ Prioritized. Overall verdict: architecture is sound (contract-first plugin API,
|
||||
- [x] The human developer understands the security model in the auth in this project. (Two README sections. [Users, groups & permissions](README.md#users-groups--permissions) carries the weight: the entity model, a worked graph, a per-route can/cannot walkthrough, and the trap that a per-row grant never widens a coarse gate — placed before Building plugins because a manifest's `permission:` gate is unreadable without it. [Security model](README.md#security-model) is deliberately short, only the facts a deployment gets wrong without them: the private network as the *only* guard on the Ory APIs, signed-not-encrypted claims, the 30-day Kratos session behind the ~10m JWT, and non-instant offboarding. The first attempt answered the *threat* model instead — a 12-row attack/defense table — which was the wrong question and mostly restated code readable at its source; cut. Also corrected the hardening checklist: `REQUIRE_SECURE_SECRETS` guards only `CSRF_SECRET`, so the committed Kratos/Hydra/Postgres/demo-admin secrets are now listed in "What you must supply". The mandatory-`exp` guard gained a test in `src/auth/jwt-middleware.test.ts`.)
|
||||
- [x] Add i18n support. (Catalogs are TS modules per locale — `src/i18n/locales/<tag>.ts` for the host, `plugins/<id>/i18n/<tag>.ts` for a plugin, looked up plugin-first then core; en-US + sv-SE ship. A request is served by `?locale=sv-SE` → `Accept-Language` → `en-US`, exact on a full tag but a lone language takes the first regional catalog; no cookie — when the URL asked, the host carries `?locale` onto the links it renders and `ctx.localeHref()` does it for a plugin's. `ctx.t(key, vars)` plus `t`/`locale`/`locales`/`localeHref`/`dir` merged into every view (any include depth); `{{var}}` interpolation, plurals via `Intl.PluralRules`, an unknown key renders as itself — which is what makes a nav label either a key or plain text. Every catalog is checked against its set's en-US at boot (keys, kind, plural categories) and a mismatch stops startup. Kratos' own flow text is mapped by its numeric id (only ids verified against the live stack; its generic trait-label id is deliberately unmapped, field labels key on the input name instead). Zero-JS language picker in the shell + the auth/consent pages, `<html lang dir>` from the locale. Core, both example plugins and their views translated; unit tests + `e2e-tests/language.spec.ts` in the visual gate; documented in README → Languages, decisions in AGENTS.md.)
|
||||
- [x] Settle the identity-vs-user vocabulary. (Plainpages says **user** everywhere — Keto namespace `User`, subjects `user:<kratos-id>`, `ctx.user`. Ory calls the record an "identity", but its own docs say it uses that term interchangeably with "users"/"accounts", so this is house style rather than a renamed concept, and "user" is the word readers know (Nielsen heuristic #2). README → Auth carries one note recording the mapping; the only place Ory's spelling survives is the `Identity` DTO in `src/auth/kratos-admin.ts`, which mirrors the Kratos wire shape. Recorded in AGENTS.md.)
|
||||
- [x] There is a "Settings" in the bottom left (a little cog) showing a "Preferences" in a little menu when clicked. That is not in any spec, it exists when not even logged in and erh. Just remove. (Dropped from the sidebar footer in `views/partials/shell.ejs`, which now carries the profile menu — or Sign in when anonymous — plus the language picker. The `shell.settings`/`shell.preferences` catalog keys went with it in both locales, as did the then-unreferenced `i-gear` icon: `ICON_NAMES` is by definition the icons the UI references, so `views/partials/icons.ejs` was regenerated from it. Kratos' own `/settings` account flow is a different thing and is untouched. Covered by `src/ui/shell.test.ts` signed-in and anonymous, plus the public-landing case in `e2e-tests/visual.spec.ts`.)
|
||||
|
||||
### Architectural review findings (2026-07-02)
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
<symbol id="i-copy" viewBox="0 0 24 24"><rect width="14" height="14" x="8" y="8" rx="2" ry="2" /><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" /></symbol>
|
||||
<symbol id="i-download" viewBox="0 0 24 24"><path d="M12 15V3" /><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /><path d="m7 10 5 5 5-5" /></symbol>
|
||||
<symbol id="i-edit" viewBox="0 0 24 24"><path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z" /><path d="m15 5 4 4" /></symbol>
|
||||
<symbol id="i-gear" viewBox="0 0 24 24"><path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915" /><circle cx="12" cy="12" r="3" /></symbol>
|
||||
<symbol id="i-globe" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10" /><path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20" /><path d="M2 12h20" /></symbol>
|
||||
<symbol id="i-grid" viewBox="0 0 24 24"><rect width="7" height="7" x="3" y="3" rx="1" /><rect width="7" height="7" x="14" y="3" rx="1" /><rect width="7" height="7" x="14" y="14" rx="1" /><rect width="7" height="7" x="3" y="14" rx="1" /></symbol>
|
||||
<symbol id="i-kebab" viewBox="0 0 24 24"><circle cx="12" cy="12" r="1" /><circle cx="12" cy="5" r="1" /><circle cx="12" cy="19" r="1" /></symbol>
|
||||
|
||||
@@ -84,12 +84,6 @@
|
||||
<% } %>
|
||||
|
||||
<%- include("locale-switch") %>
|
||||
|
||||
<%- include("menu", {
|
||||
up: true,
|
||||
trigger: { class: "btn icon-btn", label: t("shell.settings"), html: '<svg class="ico"><use href="#i-gear"/></svg>' },
|
||||
items: [{ head: t("shell.settings") }, { label: t("shell.preferences"), icon: "i-gear" }],
|
||||
}) %>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
Reference in New Issue
Block a user