From 19e0c24f9a894d5ce50e31cba6ab062b7bd3d775 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Falc=C3=B3n?= Date: Tue, 14 Jul 2026 22:24:52 +0200 Subject: [PATCH] fix(categories): fall back to gray when category color is unknown (#675) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Problem `getCategoryColorClasses(color)` in `resources/js/types/category.ts` returned `colorMap[color]` directly. When a category's `color` is **not** one of the known `CATEGORY_COLORS` (a legacy value, or any color the frontend map doesn't cover), the lookup is `undefined`. Consumers then read `.bg` off it — e.g. the transaction categorizer command palette: ```tsx const colorClasses = getCategoryColorClasses(category.color); // ... className={cn(..., colorClasses.bg)} // 💥 Cannot read properties of undefined (reading 'bg') ``` This threw `TypeError: Cannot read properties of undefined (reading 'bg')` and crashed the categorize-transactions view. **Sentry:** `PHP-LARAVEL-4H` (frontend, error/high) — first seen 2026-07-14, in `use-categorize-transactions` chunk, `getCategoryColorClasses` → `Array.map` over categories. ## Fix Mirror the guard that already exists in the sibling `getCategoryChartColor` (which does `?? 'var(--color-gray-500)'`): fall back to the gray classes when the color isn't in the map. ```diff - return colorMap[color]; + return colorMap[color] ?? colorMap.gray; ``` One line, behavior-preserving for all valid colors, and it turns a crash into a neutral gray swatch for the rare out-of-enum color. ## Test Added two cases to `resources/js/types/category.test.ts`: - a known color still returns its own classes, - an unknown color falls back to gray instead of returning `undefined`. Ran locally (minimal vitest config): 4/4 pass. 🤖 Found and fixed autonomously via the Sentry monitoring loop. --- resources/js/types/category.test.ts | 22 +++++++++++++++++++++- resources/js/types/category.ts | 2 +- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/resources/js/types/category.test.ts b/resources/js/types/category.test.ts index 50a7682c..37cf27ab 100644 --- a/resources/js/types/category.test.ts +++ b/resources/js/types/category.test.ts @@ -1,6 +1,10 @@ import { setTranslations } from '@/utils/i18n'; import { afterEach, describe, expect, it } from 'vitest'; -import { getCategoryTypeLabel } from './category'; +import { + type CategoryColor, + getCategoryColorClasses, + getCategoryTypeLabel, +} from './category'; describe('getCategoryTypeLabel', () => { afterEach(() => { @@ -25,3 +29,19 @@ describe('getCategoryTypeLabel', () => { expect(getCategoryTypeLabel('transfer')).toBe('Transfer'); }); }); + +describe('getCategoryColorClasses', () => { + it('returns the classes for a known color', () => { + expect(getCategoryColorClasses('blue')).toEqual({ + bg: 'bg-blue-100 dark:bg-blue-700', + text: 'text-blue-700 dark:text-blue-100', + }); + }); + + it('falls back to gray for an unknown color instead of returning undefined', () => { + const classes = getCategoryColorClasses('chartreuse' as CategoryColor); + + expect(classes).toEqual(getCategoryColorClasses('gray')); + expect(classes.bg).toBeDefined(); + }); +}); diff --git a/resources/js/types/category.ts b/resources/js/types/category.ts index 3347e3d8..46f8e1de 100644 --- a/resources/js/types/category.ts +++ b/resources/js/types/category.ts @@ -235,7 +235,7 @@ export function getCategoryColorClasses(color: CategoryColor): { }, }; - return colorMap[color]; + return colorMap[color] ?? colorMap.gray; } export function getCategoryChartColor(color: CategoryColor): string {