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>
12 KiB
12 KiB
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 unscopedlucide-angularis deprecated upstream — installed the correct one directly). - One shared entry point for every icon:
app-icon(src/app/shared/ui/icon/), backed by a canonicalname → Lucide iconregistry (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-iconis decorative (aria-hidden) by default. AnariaLabelinput 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 (primeiconspackage) 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-icondefaults 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 itsfill/strokeattribute now inherits color from its surrounding CSScolor, 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
searchicon everywhere. - Shopping cart: was hand-drawn independently in header, items-carousel,
subcategories, item-detail, and cart (via the now-deleted
EmptyCartIconComponent). Now onecarticon everywhere. - Chevron/dropdown arrow: was hand-drawn independently in language-selector (×2),
region-selector, and the builder mobile nav (×3). Now one
chevronDown/chevronRighteverywhere. - "Login required" icon (chat-bubble shape): was drawn identically in both
telegram-login and cart's login gate. Now one
lockicon in both, matching the actual semantic ("authentication required") better than a chat bubble did.
- Magnifying glass: was hand-drawn independently in header (×2), search page (×4).
Now one
- 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. Nowheartandscale. - 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 dedicatedgripicon (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
titlealone (not reliably announced by screen readers) or nothing at all (region-selector detect-location, carousel add-to-cart, subcategories add-to-cart) and addedaria-labelto 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-iconrenders each Lucide icon as its own standalone Angular component (the current@lucide/angularAPI — 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.mdexists in this repo (confirmed across all three RC sprints this session). Theimpeccabledesign-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 (
primeiconsnpm package) is still installed — it's a transitive dependency PrimeNG components may rely on internally (calendar, carousel nav arrows rendered byp-carousel, etc.), so it wasn't removed frompackage.json. No app code importspi-*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.