88 lines
3.8 KiB
Markdown
88 lines
3.8 KiB
Markdown
# F-071 — Architecture: Editable emoji and color for categories
|
|
|
|
## Problem
|
|
|
|
The frontend uses **hardcoded** emoji/color maps keyed by category slug:
|
|
- `project/frontend/src/components/home/CategoriesGrid.tsx` — `icons` map + `colors` array
|
|
- `project/frontend/src/app/categories/page.tsx` — `icons` map + `colors` array
|
|
|
|
The admin category editor (`project/apps/admin/src/app/(dashboard)/categories/page.tsx`)
|
|
shows a generic 📁 folder icon for parent categories and has **no** emoji/color fields.
|
|
New parent categories therefore show the folder instead of an identifying emoji.
|
|
|
|
## Design
|
|
|
|
### 1. Database schema (migration 030)
|
|
|
|
```sql
|
|
ALTER TABLE categories_categories
|
|
ADD COLUMN emoji VARCHAR(10) NULL DEFAULT NULL,
|
|
ADD COLUMN color TEXT NULL DEFAULT NULL;
|
|
```
|
|
|
|
- `emoji VARCHAR(10)`: a single emoji unicode character (with potential variation selectors). Nullable so existing categories are unaffected.
|
|
- `color TEXT`: stores a Tailwind gradient class string, e.g. `from-[#70ad47] to-[#40916C]`. Nullable. Storing the class string keeps it simple and consistent with the existing `categories/page.tsx` `colors` array format.
|
|
|
|
Both columns are nullable with NULL default → zero-downtime, no data migration needed for existing rows.
|
|
|
|
### 2. Backend domain
|
|
|
|
`project/src/modules/categories/domain/category.ts`:
|
|
- Add `emoji?: string | null` and `color?: string | null` to `Category`, `NewCategory`, `CategoryPatch`.
|
|
|
|
### 3. Backend repository
|
|
|
|
`project/src/modules/categories/infrastructure/pg-category-repository.ts`:
|
|
- Add `emoji: string | null` and `color: string | null` to `CategoryRow`.
|
|
- Add `['emoji', 'emoji']` and `['color', 'color']` to `UPDATABLE`.
|
|
- Add emoji/color to the INSERT statement.
|
|
- Map emoji/color in `toCategory()`.
|
|
|
|
### 4. Backend API routes
|
|
|
|
`project/src/modules/categories/api/categories.routes.ts`:
|
|
- Add `emoji: z.string().max(10).optional().nullable()` and `color: z.string().max(200).optional().nullable()` to `newCategorySchema`.
|
|
- Add `emoji` and `color` to `serializeCategory()` output.
|
|
|
|
### 5. Frontend types
|
|
|
|
`project/frontend/src/types/api.ts`:
|
|
- Add `emoji?: string` and `color?: string` to the `Category` interface.
|
|
|
|
### 6. Frontend — CategoriesGrid + CategoriesPage
|
|
|
|
- Replace hardcoded `icons[cat.slug] ?? '📦'` with `cat.emoji ?? fallbackIcons[cat.slug] ?? '📦'`.
|
|
- Replace hardcoded `colors[i % colors.length]` gradient with `cat.color ?? colors[i % colors.length]` — keeps existing visual behavior for categories without stored color.
|
|
|
|
### 7. Admin — types + form
|
|
|
|
`project/apps/admin/src/types/index.ts`:
|
|
- Add `emoji?: string` and `color?: string` to `Category`.
|
|
|
|
`project/apps/admin/src/app/(dashboard)/categories/page.tsx`:
|
|
- Add `emoji` and `color` to `FormState`.
|
|
- Add emoji input (text) and color input (text input for Tailwind class, with placeholder examples) to the form UI.
|
|
- Include emoji/color in the save payload.
|
|
- In `CategoryRow`, replace the hardcoded 📁 with `cat.emoji ?? '📁'`.
|
|
|
|
### 8. Storefront
|
|
|
|
`project/storefront/src/lib/api.ts`:
|
|
- Add `emoji?: string | null` and `color?: string | null` to `CategoryDto`.
|
|
|
|
`project/storefront/src/app/categoria/[slug]/page.tsx`:
|
|
- Optionally render the emoji in the header if present.
|
|
|
|
## Data flow
|
|
|
|
```
|
|
Admin form → PATCH /api/categories/:id → backend serializes → DB stores
|
|
GET /categories/tree → backend returns emoji+color → frontend renders
|
|
```
|
|
|
|
## Risks
|
|
|
|
- **Low**: All new columns are nullable; existing categories keep working.
|
|
- The `color` field stores a CSS class string, not a sanitized color value. Admin input is validated by `z.string().max(200)`. A malicious class value is a cosmetic (not security) concern — it's rendered via `className` which React sanitizes against script injection.
|
|
- Backward compatibility: `emoji` and `color` default to `null` → UI falls back to hardcoded maps.
|