Replace the cabinet's hand-written heroicons-style SVG icon components with
the Remnawave panel's own icon family — Phosphor via react-icons/pi (Duotone).
All icons now live in a single central barrel (src/components/icons:
index + extended-icons + editor-icons, 144 icons); every feature file imports
from it instead of redefining inline SVGs (~440 icon defs removed).
- Add react-icons dependency
- Desktop nav (AppShell) now uses the central Phosphor icons, removing a
stroke-vs-duotone inconsistency the partial migration introduced
- Icons with custom props (SortIcon's direction, expandable chevrons) kept as
thin Phosphor wrappers; the RemnawaveIcon brand logo and animated SVGs
(checkmarks, spinners) are intentionally left as-is
- Restore the original per-call-site icon sizes wherever the centralized
default differed (211 call sites)
Last and biggest tab — extracted via view-facade pattern (like Info).
JSX moves into components/admin/userDetail/SubscriptionTab.tsx;
state, queries, mutation handlers stay in parent because most are
shared with other tabs (panelInfo + devicesQuery used by Info,
inline-confirm armer used by destructive actions, etc.).
The new file is 1223 lines — over the soft cap, but cohesive: one
tab with multi-sub list, sub-detail view, traffic packages,
devices CRUD with inline rename, panel info/config, live traffic,
node usage, and request history table. Sub-extracting (Devices /
RequestHistory / PanelLinks) is the next step but not free —
each adds prop-passing boilerplate.
Wide prop interface (~50 props) but every prop is a single pass-
through line at the call site. No state moves; just JSX reroute.
Parent imports trimmed: DEVICE_ALIAS_MAX_LENGTH, getFlagEmoji,
createNumberInputHandler, getCountryFlag, PlusIcon, MinusIcon,
StatusBadge — all gone, subscription tab was their last consumer.
RefreshIcon kept (page header reload button still uses it).
Net to AdminUserDetail.tsx: down to 970 lines (-1022). Total
across the 7 tab extractions: 3820 → 970 (-2850, -75%).