Step 1-2 (audit + plan): classified 35 project markdown files into Core/Architecture/ADR/Temporary-audit/Sprint-report/Generated-review/ Duplicate/Obsolete/Historical. Agent-tooling files (.agents/skills/**, .superpowers/**, docs/context/**, CLAUDE.md/GEMINI.md/AGENTS.md/ .github/copilot-instructions.md) explicitly out of scope — intentional per-tool duplication, not documentation debt. Step 3 (merge, no information lost): - docs/PROJECT.md -> docs/PROJECT_INDEX.md, rewritten as the single entry point: system overview, living-doc index, archive pointer, current status, and a critical-finding callout up top. - docs/backend/BACKEND-INTEGRATION.md -> docs/BACKEND_API.md, docs/backend/REMAINING-BACKEND-WORK.md -> docs/BACKEND_API_REMAINING_WORK.md (also folded in a legitimate uncommitted status update that had been sitting unstaged all session: categories marked DONE, order-creation endpoint noted done). - RELEASE-NOTES.md merged into CHANGELOG.md (was a near-duplicate of the same release content in friendlier prose), then deleted. - KNOWN-ISSUES.md: added item 13 (see below) and item 14 (missing canDeactivate on admin/products edit, from the archived PROJECT-STATE audit, re-verified still true); added a correction note to Fixed item 7. - All cross-references to renamed/moved files fixed across every kept doc (grep+sed pass, then verified with a link-existence check across all 58 in-scope markdown files -> 0 broken links). Step 4 (archive, nothing deleted without merging first): created docs/archive/, moved 19 files there (3 root sprint reports, 1 platform report, SPRINT-PLAN.md, and 14 one-off audit/review/report docs). Added correction headers to the 3 archived docs whose conclusions were affected by the finding below, rather than silently leaving them misleading. Step 5: docs/PROJECT_INDEX.md rewritten per the mission brief - someone opening the repo should understand the whole system from it. IMPORTANT FINDING (surfaced during this audit, not the mission's primary goal but too significant to bury): pages/category/*, pages/search/*, pages/item-detail/*, pages/info/**, pages/legal/** (40+ files) are entirely unrouted dead code - app.routes.ts's cmsContentRoutes is a literal empty array, and category/search/product routes redirect to CatalogContainerComponent/ ProductDetailsContainerComponent, not these files. Confirmed against app.routes.ts directly and cross-checked against FRONTEND.md's own routing description. This means several fixes from earlier this cycle (RC-Premium-01, RC STORE-01) and the dead-code cleanup sprint's conclusion that these files were live were all wrong - documented as KNOWN-ISSUES.md item 13, flagged at the top of PROJECT_INDEX.md, and noted on the 3 archived docs whose conclusions it affects. No application code was changed to fix this (out of scope per this session's 'documentation only' constraint) - it needs a wire-it-up-or- delete-it decision first. Verification: tsc --noEmit clean, npm run build green, all markdown links across 58 in-scope files resolve (checked programmatically). No application/Angular/backend code modified. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
111 lines
12 KiB
Markdown
111 lines
12 KiB
Markdown
# RC UI Sprint — Icon & Visual Language Audit
|
||
|
||
Full-application audit and consolidation onto **one icon library** (Lucide, via
|
||
`@lucide/angular`) across storefront, Marketplace Builder, and backoffice. Before this
|
||
pass the app mixed three icon systems in the same screens: **PrimeIcons** (`pi pi-*`
|
||
CSS classes), **dozens of hand-rolled inline `<svg>` blocks** (often the same icon
|
||
redrawn from scratch at different sizes/colors), and **bare text glyphs** (`♥`, `⇄`,
|
||
`«`, `»`) standing in for icons entirely outside any icon system.
|
||
|
||
## Foundation
|
||
|
||
- Added `@lucide/angular` (the maintained package; the unscoped `lucide-angular` is
|
||
deprecated upstream — installed the correct one directly).
|
||
- One shared entry point for every icon: `app-icon` (`src/app/shared/ui/icon/`),
|
||
backed by a canonical `name → Lucide icon` registry (`icon-registry.ts`). Default
|
||
size 20px, default stroke-width 2 — the same visual weight everywhere unless a
|
||
call site explicitly overrides it for a specific context (e.g. a 48px empty-state
|
||
illustration vs. a 14px inline chevron).
|
||
- `app-icon` is decorative (`aria-hidden`) by default. An `ariaLabel` input exists for
|
||
the rare case an icon carries meaning with no adjacent text; in practice, the label
|
||
almost always belongs on the surrounding interactive element instead.
|
||
|
||
## Old icon → New icon → Reason
|
||
|
||
| Area | Old | New | Reason |
|
||
|---|---|---|---|
|
||
| Admin dashboard | `pi-file-edit`, `pi-heart`, `pi-clock`, `pi-question-circle`, `pi-check-circle`, `pi-save`, `pi-upload`, `pi-book`, `pi-bolt`, `pi-flag`, `pi-megaphone`, `pi-circle` (data-driven via `icon: 'pi-*'` fields) | `edit`, `heart`, `clock`, `help`, `checkCircle`, `save`, `upload`, `book`, `zap`, `flag`, `megaphone`, `circle` | PrimeIcons → Lucide; icon fields retyped `AppIconName` so an unmapped name is a compile error, not a silently blank icon |
|
||
| Backoffice shell (sidebar nav + topbar) | `pi-home`, `pi-box`, `pi-tags`, `pi-shopping-cart`, `pi-user`, `pi-credit-card`, `pi-star`, `pi-chart-bar`, `pi-sitemap`, `pi-users`, `pi-cog`, `pi-desktop`, `pi-chart-line`, `pi-sign-out`, `pi-bars`, `pi-search`, `pi-upload`, `pi-building`, `pi-bell` | `home`, `package`, `tags`, `cart`, `user`, `creditCard`, `star`, `chartBar`, `network`, `users`, `settings`, `monitor`, `chartLine`, `logOut`, `menu`, `search`, `upload`, `building`, `bell` | PrimeIcons → Lucide |
|
||
| Marketplace Builder (overview, nav, sections, HTML editor toolbar) | `pi-arrow-left/right-circle/up/down`, `pi-check-circle`, `pi-circle`, `pi-circle-fill`, `pi-question-circle`, `pi-desktop`, `pi-shop`, `pi-image`, `pi-th-large`, `pi-box`, `pi-images`, `pi-history`, `pi-megaphone`, `pi-verified`, `pi-code`, `pi-globe`, `pi-sliders-h`, `pi-compass`, `pi-copy`, `pi-trash`, `pi-list`, `pi-link`, `pi-table`, `pi-minus`, `pi-video`, **`pi-bars` reused for drag handles** | `arrowLeft/Right/Up/Down`, `checkCircle`, `circle`, `help`, `monitor`, `store`, `image`, `layoutGrid`, `package`, `images`, `history`, `megaphone`, `verified`, `code`, `globe`, `slidersHorizontal`, `compass`, `copy`, `trash`, `list`, `link`, `table`, `minus`, `video`, **new dedicated `grip` icon** | PrimeIcons → Lucide. `pi-bars` had been reused for both the hamburger menu **and** every drag-to-reorder handle — two unrelated meanings sharing one icon, exactly the "duplicated icon, different meaning" pattern the audit brief calls out. Added `GripVertical` as a distinct drag-handle icon. |
|
||
| Header (storefront) | Two hand-drawn magnifying-glass SVGs (`#576463` desktop / `#1e3c38` mobile — same icon, two different hardcoded colors), `♥`/`⇄` **text glyphs** for wishlist/compare, hand-drawn cart SVG, hand-drawn home/catalog mobile-menu icons, 3× duplicated inline chevron SVG | `search` (color via `currentColor`), `heart`, `scale`, `cart`, `home`, `layoutGrid`, `chevronRight` | Consolidates one icon drawn twice with two different colors into one; replaces bare text characters (`♥`/`⇄`) that weren't part of any icon system at all |
|
||
| Search page | Same magnifying-glass path hand-duplicated **4 times** (input icon, empty-query, no-results, no-query states) at 3 sizes/3 colors | `search` ×4, color preserved per state via a CSS `color` property on the wrapper | Textbook duplication — one icon, four copy-pasted SVGs |
|
||
| Cart | Trash/X/plus/minus inline SVGs, standalone `EmptyCartIconComponent` (an 80px duplicate of the same cart glyph, only used once), chat-bubble "login gate" icon, duplicated close-X icon (2 modals), duplicated refresh icon (QR expired/error) | `trash`, `x`, `plus`, `minus`, `cart` (inline, component deleted), `lock`, `x`, `refresh` | Removed a whole component that existed only to duplicate an icon already available; unified the "login required" icon with telegram-login's identical icon (same concept, was drawn twice) |
|
||
| Language/region selectors | 3× duplicated chevron SVG (language dropdown, currency dropdown, region dropdown — identical path, copy-pasted), map-pin SVG, crosshair "locate" SVG, globe SVG | `chevronDown` ×3, `mapPin`, `locate`, `globe` | Same chevron redrawn three times in two components |
|
||
| Items carousel, category/subcategories, item-detail | Hand-drawn star (hardcoded `#497671` fill), hand-drawn cart icon (×3 separate redraws across 3 files), no-image/package/grid empty-state illustrations, check/X status icons, thumbs-up/down vote icons, dynamic fill/stroke rating stars | `star` (with new `color` input + `.dx-star--filled` CSS class for the solid/outline toggle), `cart`, `image`/`package`/`layoutGrid`, `check`/`x`, `thumbsUp`/`thumbsDown` | The cart icon alone had been hand-drawn from scratch in 5 different files across this audit (header, items-carousel, subcategories, item-detail, cart) — now one icon, one registry entry |
|
||
| Shared `app-select` | Browser-native `<select>` dropdown indicator (renders differently per browser, no relation to the icon system) | `chevronDown` (native indicator hidden via `appearance: none`) | "Selects: replace browser default indicators" — this component is used by nearly every admin form, so the fix applies everywhere at once |
|
||
| Shared `app-pagination` | `«` / `»` HTML entities | `chevronLeft` / `chevronRight` | "Pagination: use proper chevrons" |
|
||
| Every native `<details>`/`<summary>` expander (7 call sites: admin product form ×4, page editor, builder widget panel) | Browser-default disclosure triangle (differs Chrome/Firefox/Safari) | Lucide `ChevronDown` path drawn via one global CSS rule (`details > summary::after`), rotated 180° on `[open]` | Covers all 7 expanders with a single CSS change rather than touching each template — no shared class existed to hang a component-based fix on |
|
||
|
||
## Consistency improvements
|
||
|
||
- **One icon family everywhere.** Zero PrimeIcons (`pi-*`) class usages remain
|
||
anywhere in the app (verified by full-repo sweep after each batch). PrimeIcons
|
||
(`primeicons` package) is still installed as a PrimeNG peer dependency but no
|
||
longer used directly for any icon in app code.
|
||
- **One default size/stroke-width.** `app-icon` defaults to 20px / stroke-width 2;
|
||
every call site that deviates does so for a legible, deliberate reason (48px empty-
|
||
state illustration, 14px inline chevron), not arbitrary per-instance sizing.
|
||
- **Color via `currentColor`, not hardcoded hex.** Every hand-rolled SVG that baked a
|
||
specific hex into its `fill`/`stroke` attribute now inherits color from its
|
||
surrounding CSS `color`, so hover/active/disabled states that already change text
|
||
color also correctly recolor the icon — previously several icons ignored those
|
||
states entirely because their color was hardcoded in the SVG markup.
|
||
- **Duplicated icons resolved to one instance:**
|
||
- Magnifying glass: was hand-drawn independently in header (×2), search page (×4).
|
||
Now one `search` icon everywhere.
|
||
- Shopping cart: was hand-drawn independently in header, items-carousel,
|
||
subcategories, item-detail, and cart (via the now-deleted
|
||
`EmptyCartIconComponent`). Now one `cart` icon everywhere.
|
||
- Chevron/dropdown arrow: was hand-drawn independently in language-selector (×2),
|
||
region-selector, and the builder mobile nav (×3). Now one `chevronDown`/
|
||
`chevronRight` everywhere.
|
||
- "Login required" icon (chat-bubble shape): was drawn identically in both
|
||
telegram-login and cart's login gate. Now one `lock` icon in both, matching the
|
||
actual semantic ("authentication required") better than a chat bubble did.
|
||
- **Bare text glyphs replaced with real icons.** `♥` and `⇄` in the header were plain
|
||
Unicode characters, not part of any icon system, inconsistent stroke weight and
|
||
optical size versus every other icon on the same toolbar. Now `heart` and `scale`.
|
||
- **Corrected a genuine meaning collision**, not just a style one: PrimeIcons'
|
||
`pi-bars` (hamburger lines) was reused in the Marketplace Builder for both the
|
||
mobile menu toggle *and* every drag-to-reorder handle. A user scanning the builder
|
||
UI would see the same glyph mean "open navigation" in the header and "drag this
|
||
row" in a list — added a dedicated `grip` icon (`GripVertical`) so the two concepts
|
||
are now visually distinct.
|
||
- **Icon-only buttons audited for accessible names.** Swept every button whose only
|
||
content is an icon; found three relying on `title` alone (not reliably announced by
|
||
screen readers) or nothing at all (region-selector detect-location, carousel
|
||
add-to-cart, subcategories add-to-cart) and added `aria-label` to all three.
|
||
- **Decorative icons intentionally kept as custom SVG (not migrated):**
|
||
- The **Telegram brand logo** (cart, telegram-login) — a brand mark, not a generic
|
||
icon; replacing it with a generic Lucide icon would misrepresent the brand.
|
||
- **`layout-switcher`'s grid-pattern preview icons** — these show the actual layout
|
||
being selected (2-column, compact grid, list, etc.) as a literal visual preview,
|
||
not a stand-in for a word. Lucide has no equivalent "this exact grid pattern"
|
||
icon set; redrawing them as generic layout icons would lose the preview function.
|
||
|
||
## Remaining issues / backlog
|
||
|
||
- **No sortable table columns exist anywhere in the app** to add sort-direction
|
||
icons to (every admin list table has static, non-interactive `<th>` labels). Adding
|
||
actual column-sort interactivity would be new functionality, out of scope for an
|
||
icon/visual-language pass — flagged here rather than invented.
|
||
- **Bundle size**: `app-icon` renders each Lucide icon as its own standalone Angular
|
||
component (the current `@lucide/angular` API — no tree-shakeable "icon font" or
|
||
sprite sheet). The initial bundle grew from the prior build's already-over-budget
|
||
~585 KB over the 700 KB target to ~760 KB over, mostly from icon components now
|
||
bundled eagerly in the admin shell/dashboard/builder (loaded on every admin route).
|
||
Worth a follow-up pass to lazy-load icon-heavy admin sections if bundle size becomes
|
||
a concrete problem.
|
||
- **No `docs/DESIGN.md` exists** in this repo (confirmed across all three RC sprints
|
||
this session). The `impeccable` design-quality hook flagged pre-existing font-size/
|
||
radius/color values against a document that doesn't exist — none of those findings
|
||
were introduced by this pass; they're pre-existing values in files this pass
|
||
touched for unrelated reasons (colors, radii, spacing untouched).
|
||
- **PrimeIcons (`primeicons` npm package) is still installed** — it's a transitive
|
||
dependency PrimeNG components may rely on internally (calendar, carousel nav
|
||
arrows rendered by `p-carousel`, etc.), so it wasn't removed from `package.json`.
|
||
No app code imports `pi-*` classes directly anymore, but a future pass could audit
|
||
whether PrimeNG's own internal icon usage (e.g. `p-carousel`'s built-in prev/next
|
||
arrows) should also be re-skinned to match, or left as PrimeNG's own visual
|
||
language since those are framework-owned, not hand-authored.
|