From 86335b067bd1ea45c3c59a6889e91554e4098ca2 Mon Sep 17 00:00:00 2001 From: wilfordgrimley <2397930+WilfordGrimley@users.noreply.github.com> Date: Wed, 22 Jul 2026 02:07:08 +0000 Subject: [PATCH] Redesign navbar + add three-tier footer, cut nav to five surfaces Nav shrinks to Editor (-> /display)/What's That Card?/Wiki + Sources + compact auth dropdown, goes full-width, and drops What's New?/Explore/My Decks/Download/Contributions/Guide (absorbed or moved to the footer). New Footer.tsx carries Contributions/Wiki/Sources, About/Privacy/Terms, and the GitHub/Reddit/Discord + chilli_axe credit links. AuthWidget's signed-in state becomes a monogram-avatar user menu instead of a wrapping text pair. Adds a genuine Terms of Use section to /about with its own anchor, and relocates the cloud download-queue counter from the navbar to the two export surfaces that actually enqueue downloads (FinishFooter + print.tsx). Co-Authored-By: Claude Fable 5 --- docs/features/homepage-panel.md | 13 +- docs/user-guide.md | 24 +- .../@googleworkspace/drive-picker-react.js | 25 ++ .../src/features/display/FinishFooter.tsx | 32 ++- .../src/features/download/DownloadManager.tsx | 1 + .../src/features/moderation/AuthWidget.tsx | 101 +++++++-- frontend/src/features/ui/Footer.test.tsx | 72 +++++- frontend/src/features/ui/Footer.tsx | 213 ++++++++++++++---- frontend/src/features/ui/Navbar.test.tsx | 155 +++++++++++++ frontend/src/features/ui/Navbar.tsx | 205 ++++++----------- frontend/src/pages/about.tsx | 36 +++ frontend/src/pages/print.tsx | 24 +- frontend/tests/DisplayFinishFooter.spec.ts | 22 +- frontend/tests/DisplayPage.spec.ts | 50 ++-- frontend/tests/ModerationQueue.spec.ts | 3 + frontend/tests/Navbar.spec.ts | 5 + frontend/tests/SavedDecks.spec.ts | 38 ++-- frontend/tests/SelectVersionSection.spec.ts | 2 +- frontend/tests/UnsavedWorkGuard.spec.ts | 2 +- .../tests/perf/display-scroll.bench.spec.ts | 2 +- frontend/tests/test-utils.ts | 10 +- 21 files changed, 777 insertions(+), 258 deletions(-) create mode 100644 frontend/__mocks__/@googleworkspace/drive-picker-react.js create mode 100644 frontend/src/features/ui/Navbar.test.tsx diff --git a/docs/features/homepage-panel.md b/docs/features/homepage-panel.md index 853917bde..ebcbbe5da 100644 --- a/docs/features/homepage-panel.md +++ b/docs/features/homepage-panel.md @@ -18,10 +18,15 @@ otherwise never discover — the community printing-identification game The whole panel returns `null` without a remote backend configured (`useRemoteBackendConfigured`) — the exact same condition `Navbar.tsx` -already uses to decide whether to show its own `/whatsthat` and `My Decks` nav links. Promoting a CTA to a route that would 404/no-op for a -Local-Folder-only visitor is worse than not showing it; this keeps the -panel's own gating identical to the nav's, rather than inventing a -second, possibly-drifting rule for the same underlying condition. +uses to gate its own `/whatsthat` link. Promoting a CTA to a route that +would 404/no-op for a Local-Folder-only visitor is worse than not showing +it; this keeps the panel's own gating identical to the nav's, rather than +inventing a second, possibly-drifting rule for the same underlying +condition. (Nav+footer redesign, 2026-07-22: `My Decks` no longer has a +nav-bar link of its own at all - cut from the nav regardless of backend +state, see `docs/user-guide.md` - so this panel's own `/myDecks` CTA is +now one of `My Decks`'s only two discovery paths, alongside a direct +`/myDecks` visit; it no longer merely _duplicates_ a nav link.) ## The two CTA cards diff --git a/docs/user-guide.md b/docs/user-guide.md index 70e0a15a1..2d959a86d 100644 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -10,7 +10,10 @@ it. See [`documentation-process.md`](documentation-process.md). How to search the catalog, read search results, and pick a printing for a slot in your decklist. -The **Display** page's search bar is dual-mode: an **Add / Browse** +The **Editor** page (the redesigned `/display` route - nav+footer redesign, +2026-07-22, renamed the nav link from "Display (beta)" to plain "Editor"; +the classic `/editor` page is still reachable directly by URL but no longer +has a nav link of its own) search bar is dual-mode: an **Add / Browse** toggle next to the input switches between adding cards to your project (the usual decklist-line paste/search box) and browsing the whole catalog without touching your project — browsing renders a grid of @@ -44,7 +47,7 @@ questions scroll underneath it. How to arrange your chosen cards into pages, set bleed/DPI/paper size, and export a PDF ready to print and cut. -The **Display** page's Page Setup section defaults to Letter landscape, +The **Editor** page's Page Setup section defaults to Letter landscape, 3.175mm bleed, and a **Margin profile** picker (Borderless / Bordered / Rear-feed) calibrated against an Epson ET-8500/8550 printer — Borderless is the default and the only profile that fits full bleed on a 4-across @@ -59,10 +62,13 @@ strip-cutting while rows keep a gap that suits a die cutter. A **Link**/**Linked** toggle next to it locks the two axes to move together when you'd rather set one value for both. -Once your sheet is ready, the **Display** page's Finish footer has two +Once your sheet is ready, the **Editor** page's Finish footer has two equal-weight buttons: **Save Deck** (or **Sign in to Save**, if you're not signed in) and **Print / Export →**, plus the existing **Export** -dropdown for lightweight XML/card-image/decklist exports. Your project is +dropdown for lightweight XML/card-image/decklist exports and a small +cloud-download counter beside it (nav+footer redesign, 2026-07-22 - this +used to sit in the top navbar; it now lives next to the exports it counts, +here and again on the Print page). Your project is also quietly backed up to this browser as you work — a small "Draft backed up locally" note under the buttons confirms it — and pressing **Print / Export →** while signed in with unsaved changes offers to save @@ -72,14 +78,16 @@ browser's memory and you don't want to lose your work if it struggles. ## Saving and re-using a project Local-folder and Google Drive options for coming back to a project -later. If you're signed in and have saved decks already, the **Display** +later. If you're signed in and have saved decks already, the **Editor** page's empty-project landing screen lists them directly so you can jump -back into one without a trip to **My Decks** first. +back into one without a trip to **My Decks** first (**My Decks** itself +has no nav-bar link since the 2026-07-22 nav redesign - reach it from this +landing screen, the homepage's own CTA, or `/myDecks` directly). ## Saved decks, export, and the standalone decrypt tool -Signed in with Discord? The editor and display page's Save button -persists your deck to your account, and the **My Decks** page lists +Signed in with Discord? The classic editor and the Editor page's Save +button persists your deck to your account, and the **My Decks** page lists everything you've saved, decrypted right there in your browser — the server only ever stores encrypted, opaque bytes it can't read. diff --git a/frontend/__mocks__/@googleworkspace/drive-picker-react.js b/frontend/__mocks__/@googleworkspace/drive-picker-react.js new file mode 100644 index 000000000..94422c414 --- /dev/null +++ b/frontend/__mocks__/@googleworkspace/drive-picker-react.js @@ -0,0 +1,25 @@ +// Jest manual mock (node_modules mock, auto-applied per +// https://jestjs.io/docs/manual-mocks#mocking-node-modules) for +// @googleworkspace/drive-picker-react - an ESM-only package ("type": "module", no "require" +// export condition) that jest/jsdom can't resolve as-installed on this machine. It's only ever +// referenced as JSX components (DrivePicker/DrivePickerDocsView are custom-element wrappers +// with no interactive behavior worth exercising here) - Footer.tsx became the first jest test +// to render BackendConfig's component tree (via its own "Sources" button, see Footer.tsx's own +// module comment) since GoogleDriveBackendConfig.tsx statically imports GoogleDrivePicker.tsx, +// which statically imports this package - a plain no-op stub is enough for that tree to mount +// without error; nothing here asserts on Google Drive picker behavior itself. +const React = require("react"); + +function DrivePicker({ children }) { + return React.createElement( + "div", + { "data-testid": "mock-drive-picker" }, + children + ); +} + +function DrivePickerDocsView() { + return null; +} + +module.exports = { DrivePicker, DrivePickerDocsView }; diff --git a/frontend/src/features/display/FinishFooter.tsx b/frontend/src/features/display/FinishFooter.tsx index 164b87876..d14b4d30d 100644 --- a/frontend/src/features/display/FinishFooter.tsx +++ b/frontend/src/features/display/FinishFooter.tsx @@ -25,6 +25,10 @@ import React, { useEffect, useState } from "react"; import Button from "react-bootstrap/Button"; import { useAppSelector } from "@/common/types"; +import { + DownloadManager, + OpenDownloadManagerButton, +} from "@/features/download/DownloadManager"; import { DisplayExportMenu } from "@/features/export/DisplayExportMenu"; import { useSaveDeckFlow } from "@/features/savedDecks/useSaveDeckFlow"; import { useGetWhoamiQuery } from "@/store/api"; @@ -49,6 +53,17 @@ export function FinishFooter({ const backendURL = useAppSelector(selectRemoteBackendURL); const whoami = useGetWhoamiQuery(); + // Nav+footer redesign (2026-07-22, N10) - the cloud download-queue counter/manager used to + // live in the global navbar (OpenDownloadManagerButton + DownloadManager), cut from there + // per the redesign since it only ever counted in-browser export downloads (XML/card + // images/decklist/PDF), never the abandoned desktop tool. This is one of its two new mounts + // (the other is pages/print.tsx, which owns the memory-heavy PDF/desktop-tool downloads this + // footer's own DisplayExportMenu deliberately excludes) - both read the same global + // fileDownloadsSlice, so either one shows every download regardless of where it started; + // mounting in both closes the gap where a download enqueued on the other page wouldn't be + // visible without navigating back. + const [showDownloadManager, setShowDownloadManager] = useState(false); + // window isn't available during the static export build - resolved client-only, mirroring // AuthWidget.tsx's own identical pattern for the exact same `?next=` round-trip. const [currentHref, setCurrentHref] = useState(null); @@ -97,10 +112,19 @@ export function FinishFooter({ Print / Export → - {/* Issue #241 (design doc §5's export-beyond-PDF row) - XML/Card Images/Decklist, unchanged - and unforked; the ONLY export surface this footer still owns directly, per D9's own - "memory-heavy operations move OUT" line. */} - +
+ {/* Issue #241 (design doc §5's export-beyond-PDF row) - XML/Card Images/Decklist, + unchanged and unforked; the ONLY export surface this footer still owns directly, per + D9's own "memory-heavy operations move OUT" line. */} + + setShowDownloadManager(true)} + /> +
+ setShowDownloadManager(false)} + /> {hasBackedUpThisSession && (