From 2964e3b6dc7b358b74648d254bc1a3bd6f0d91df Mon Sep 17 00:00:00 2001 From: Chris Sherwood Date: Tue, 6 Oct 2026 16:51:04 -0700 Subject: [PATCH] feat(custom-apps): add a gaming icon, and show a box for unknown icon names - Add IconDeviceGamepad2 to the DynamicIcon registry and offer it as "Gaming" in the Custom App icon dropdown. The 36-icon link tile grid (six by six) is deliberately left unchanged. - DynamicIcon rendered nothing for a name outside its registry, which a Custom App can carry when its icon is set through the API. It now falls back to IconBox (the console warning stays). Closes #1315 Co-Authored-By: Claude Opus 5.5 --- admin/constants/link_tile_icons.ts | 5 ++--- admin/inertia/components/CustomAppModal.tsx | 1 + admin/inertia/components/DynamicIcon.tsx | 6 ++++-- admin/inertia/lib/icons.ts | 2 ++ 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/admin/constants/link_tile_icons.ts b/admin/constants/link_tile_icons.ts index 9f4dbc9e..a4241959 100644 --- a/admin/constants/link_tile_icons.ts +++ b/admin/constants/link_tile_icons.ts @@ -3,9 +3,8 @@ * six across and six down. * * Every name here must exist in the `DynamicIcon` registry (`inertia/lib/icons.ts`). - * `DynamicIcon` renders nothing at all for a name it does not know, so an icon - * that is not in that registry produces a tile with a silent hole where its icon - * should be. Shared by the picker and the server-side validator so the two can + * `DynamicIcon` falls back to a generic box for a name it does not know, so an + * icon that is not in that registry produces a tile with the wrong icon. Shared by the picker and the server-side validator so the two can * never disagree about what is selectable. * * Chosen for "a thing on my network": storage, media, documents, tooling, diff --git a/admin/inertia/components/CustomAppModal.tsx b/admin/inertia/components/CustomAppModal.tsx index 77ada8f9..4ea9b278 100644 --- a/admin/inertia/components/CustomAppModal.tsx +++ b/admin/inertia/components/CustomAppModal.tsx @@ -67,6 +67,7 @@ const ICON_OPTIONS = [ { value: 'IconWorld', label: 'Web' }, { value: 'IconShieldLock', label: 'Security' }, { value: 'IconMovie', label: 'Media' }, + { value: 'IconDeviceGamepad2', label: 'Gaming' }, { value: 'IconBook', label: 'Book' }, { value: 'IconNotes', label: 'Notes' }, { value: 'IconCpu', label: 'Compute' }, diff --git a/admin/inertia/components/DynamicIcon.tsx b/admin/inertia/components/DynamicIcon.tsx index be5b16af..ac846907 100644 --- a/admin/inertia/components/DynamicIcon.tsx +++ b/admin/inertia/components/DynamicIcon.tsx @@ -13,11 +13,13 @@ interface DynamicIconProps { const DynamicIcon: React.FC = ({ icon, className, stroke, onClick }) => { if (!icon) return null - const Icon = icons[icon] + let Icon = icons[icon] + // A name outside the registry can still arrive from data (e.g. a Custom App icon set + // via the API). Show a generic box rather than an empty hole where the icon should be. if (!Icon) { console.warn(`Icon "${icon}" not found in icon map.`) - return null + Icon = icons.IconBox } return diff --git a/admin/inertia/lib/icons.ts b/admin/inertia/lib/icons.ts index ce58a2ec..5e576908 100644 --- a/admin/inertia/lib/icons.ts +++ b/admin/inertia/lib/icons.ts @@ -19,6 +19,7 @@ import { IconCopy, IconCpu, IconDatabase, + IconDeviceGamepad2, IconDownload, IconExternalLink, IconFileDescription, @@ -97,6 +98,7 @@ export const icons = { IconCopy, IconCpu, IconDatabase, + IconDeviceGamepad2, IconDownload, IconExternalLink, IconFileDescription,