Audit-driven sweep: replaced 168 hand-written inline <svg> icons across 77
files with the central Phosphor (react-icons/pi) components from
@/components/icons, preserving each icon's size classes and colour (dynamic
stroke colours via the parent's currentColor, RefreshIcon's spinning state,
conditional rotate-180 chevrons).
Verified: tsc + vite build + eslint clean; an adversarial diff review of all
changed files found the replacements correct (70 files clean, 0 blockers).
Remaining inline <svg> dropped from 262 to ~95 — the survivors are legitimate
non-icons (brand/provider logos, loading spinners & framer-motion animations,
charts/sparklines, background decoration) plus a few ambiguous glyphs.
Audit P2: admin pages prioritise density but cabinet also runs in the
Telegram Mini App on phones. Small button targets (~24-32px) work for
mouse precision but fail mobile thumbs. Apply targeted bumps that keep
desktop density unchanged.
src/styles/globals.css
• .btn — add min-h-[44px] on mobile, sm:min-h-0 on desktop.
Cascades to every .btn-primary / .btn-secondary / .btn-ghost /
.btn-danger consumer (76 + 48 + N usages app-wide). One change,
everywhere.
• .btn-icon — same logic (min-h/min-w 44px mobile). Drops the
duplicate definition that appeared further down the file.
components/admin/userDetail/TicketsTab.tsx — mobile-critical chat
• Back button h-8 w-8 → h-10 w-10 sm:h-8 sm:w-8 (40px / 32px).
Adds aria-label since it had none.
• Status pills px-2.5 py-1 → +min-h-[36px] py-1.5 sm:min-h-0
sm:py-1 (36px / 26px). Mis-tap penalty on mobile is high (it
flips ticket status).
• Reply send button — add min-h/min-w 44px mobile + aria-label.
Why not blanket bump py-1.5 → py-2.5 across all 40+ admin pages:
visual density patterns were iteratively designed and a global sweep
would regress them. Per-page review can target specific buttons that
deserve mobile touch floor without breaking density elsewhere.
icon-only buttons not using .btn-icon (raw <button><svg/></button>)
left as-is — they're typically in compact toolbars where visual
density is the point. Per-page review under /impeccable adapt
target=<page> if mobile flow analysis flags one.
Pulls the tickets tab into components/admin/userDetail/TicketsTab.tsx:
- ticketsQuery (list)
- selectedTicketId / selectedTicket state + detail loader
- reply form state (replyText, replySending)
- status-change action handler + actionLoading
- auto-scroll messagesEndRef
- JSX is split into ChatView + TicketsList + EmptyState helpers
inside the new file (file stays under 400 lines despite the
split-view UI)
Parent now passes only userId + formatDate. Imports for adminApi,
MessageMediaGrid, linkifyText, AdminTicket / AdminTicketDetail types
all drop from AdminUserDetail.tsx — tickets are the only consumers.
Net to AdminUserDetail.tsx: down to 2395 lines (-295). Same DOM,
same event handlers, same queryKey ['admin-user-tickets', userId].