diff --git a/CLAUDE.md b/CLAUDE.md index 0d768a8..7a087d3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -48,14 +48,14 @@ scripts/deploy-frontend.sh # Manual frontend deploy to S3/CloudFront ### Frontend (Next.js App Router) - `/app/page.tsx` — Public filament inventory table (home page) -- `/app/upadaj/` — Admin panel: login, `/dashboard` (filament CRUD), `/colors` (color management), `/requests` (customer color requests) +- `/app/upadaj/` — Admin panel: login, `/dashboard` (filament CRUD), `/colors` (color management), `/customers` (customer management), `/sales` (sales recording), `/requests` (customer color requests), `/analytics` (revenue/inventory dashboards) - `/src/services/api.ts` — Axios instance with auth interceptors (auto token injection, 401/403 redirect) - `/src/data/bambuLabColors.ts` — Primary color-to-hex mapping with gradient support (used by `ColorCell` component) - `/src/data/bambuLabColorsComplete.ts` — Extended color database grouped by finish type (used by filters) ### API (`/api/server.js`) Single-file Express server running on EC2 (port 80). All routes inline. -- Endpoints: `/api/login`, `/api/filaments`, `/api/colors`, `/api/sale/bulk`, `/api/color-requests` +- Endpoints: `/api/login`, `/api/filaments`, `/api/colors`, `/api/customers`, `/api/sales`, `/api/color-requests`, `/api/analytics/*` - Auth: JWT tokens with 24h expiry, single admin user (password from `ADMIN_PASSWORD` env var) ### Database (PostgreSQL on RDS) @@ -64,13 +64,15 @@ Key schemas and constraints: ``` filaments: id, tip (material), finish, boja (color), refill, spulna, kolicina, cena, sale_* colors: id, name, hex, cena_refill (default 3499), cena_spulna (default 3999) +customers: id, name, phone (unique), city, notes, created_at +sales: id, customer_id (FK→customers), total_amount, notes, created_at + sale_items join table color_requests: id, color_name, message, contact_name, contact_phone, status ``` **Critical constraints:** - `filaments.boja` → FK to `colors.name` (ON UPDATE CASCADE) — colors must exist before filaments reference them - `kolicina = refill + spulna` — enforced by check constraint -- Migrations in `/database/migrations/` with sequential numbering (currently up to `020_`) +- Migrations in `/database/migrations/` with sequential numbering (currently up to `023_`) ## Key Patterns @@ -86,7 +88,7 @@ color_requests: id, color_name, message, contact_name, contact_phone, status 3. Optionally add to `src/data/bambuLabColorsComplete.ts` finish groups ### API Communication -- Service modules in `src/services/api.ts`: `authService`, `colorService`, `filamentService`, `colorRequestService` +- Service modules in `src/services/api.ts`: `authService`, `colorService`, `filamentService`, `colorRequestService`, `customerService`, `saleService`, `analyticsService` - Auth token stored in localStorage with 24h expiry - Cache busting on filament fetches via timestamp query param diff --git a/__tests__/data/bambuLabColors.test.ts b/__tests__/data/bambuLabColors.test.ts index 2e2590b..036bc90 100644 --- a/__tests__/data/bambuLabColors.test.ts +++ b/__tests__/data/bambuLabColors.test.ts @@ -33,9 +33,9 @@ describe('Bambu Lab Colors Data', () => { }); }); - it('should have Unknown fallback color', () => { - expect(bambuLabColors).toHaveProperty('Unknown'); - expect(bambuLabColors.Unknown.hex).toBe('#CCCCCC'); + it('should return fallback for unknown colors', () => { + const result = getFilamentColor('NonExistentColor'); + expect(result.hex).toBe('#CCCCCC'); }); }); diff --git a/__tests__/data/bambuLabColorsComplete.test.ts b/__tests__/data/bambuLabColorsComplete.test.ts index 8c323c4..90207cf 100644 --- a/__tests__/data/bambuLabColorsComplete.test.ts +++ b/__tests__/data/bambuLabColorsComplete.test.ts @@ -21,9 +21,9 @@ describe('Bambu Lab Colors Complete Data', () => { }); it('should have silk colors', () => { - expect(bambuLabColors).toHaveProperty('Silk Aurora Purple'); - expect(bambuLabColors).toHaveProperty('Silk Phantom Blue'); - expect(bambuLabColors).toHaveProperty('Silk Mystic Magenta'); + expect(bambuLabColors).toHaveProperty('Aurora Purple'); + expect(bambuLabColors).toHaveProperty('Phantom Blue'); + expect(bambuLabColors).toHaveProperty('Mystic Magenta'); }); it('should have valid hex colors', () => { diff --git a/database/migrations/023_cleanup_fake_colors_add_missing.sql b/database/migrations/023_cleanup_fake_colors_add_missing.sql new file mode 100644 index 0000000..ad56f0d --- /dev/null +++ b/database/migrations/023_cleanup_fake_colors_add_missing.sql @@ -0,0 +1,100 @@ +-- Migration 023: Clean up fake colors and add missing real Bambu Lab colors +-- Source of truth: bambu_lab_filaments.numbers spreadsheet + +BEGIN; + +-- 1. Rename typo: IronGray Metallic → Iron Gray Metallic (FK cascades to filaments) +UPDATE colors SET name = 'Iron Gray Metallic' WHERE name = 'IronGray Metallic'; + +-- 2. Delete 67 fake colors not used by any filament +DELETE FROM colors WHERE name IN ( + 'Coton Candy Cloud', + 'Glow in the Dark Blue', + 'Glow in the Dark Green', + 'Grey', + 'Indingo Purple', + 'Ivory', + 'Ivory White', + 'Light Blue', + 'Light Green', + 'Matte Black', + 'Matte Blue', + 'Matte Brown', + 'Matte Coral', + 'Matte Green', + 'Matte Grey', + 'Matte Lime', + 'Matte Mint', + 'Matte Navy', + 'Matte Orange', + 'Matte Pink', + 'Matte Purple', + 'Matte White', + 'Matte Yellow', + 'Metal Bronze', + 'Metal Copper', + 'Metal Gold', + 'Metal Grey', + 'Metal Silver', + 'Mint Green', + 'Natural', + 'Navy Blue', + 'Nebulane', + 'Silk Black', + 'Silk Blue', + 'Silk Bronze', + 'Silk Copper', + 'Silk Emerald', + 'Silk Gold', + 'Silk Green', + 'Silk Jade', + 'Silk Orange', + 'Silk Pearl', + 'Silk Pink', + 'Silk Purple', + 'Silk Red', + 'Silk Rose Gold', + 'Silk Ruby', + 'Silk Sapphire', + 'Silk Silver', + 'Silk White', + 'Sky Blue', + 'Sparkle Blue', + 'Sparkle Gold', + 'Sparkle Green', + 'Sparkle Purple', + 'Sparkle Red', + 'Sparkle Silver', + 'Support G', + 'Support White', + 'Translucent Tea', + 'Transparent Blue', + 'Transparent Green', + 'Transparent Orange', + 'Transparent Purple', + 'Transparent Red', + 'Transparent Yellow', + 'Violet' +); + +-- 3. Add 17 missing real Bambu Lab colors from spreadsheet +INSERT INTO colors (name, hex) VALUES + ('Aurora Purple', '#285BB7'), + ('Candy Green', '#408619'), + ('Candy Red', '#BB3A2E'), + ('Crystal Blue', '#5BC0EB'), + ('Flesh', '#E8C4A2'), + ('Forest Green', '#415520'), + ('Grape Jelly', '#6B2D75'), + ('Lake Blue', '#4672E4'), + ('Lime', '#C5ED48'), + ('Mystic Magenta', '#720062'), + ('Nebulae', '#424379'), + ('Neon Green', '#ABFF1E'), + ('Phantom Blue', '#00629B'), + ('Quicksilver', '#A6A9AA'), + ('Rose Gold', '#B29593'), + ('Translucent Teal', '#008080') +ON CONFLICT (name) DO NOTHING; + +COMMIT; diff --git a/src/data/bambuLabCatalog.ts b/src/data/bambuLabCatalog.ts index e563463..4a48c2b 100644 --- a/src/data/bambuLabCatalog.ts +++ b/src/data/bambuLabCatalog.ts @@ -115,9 +115,9 @@ export const BAMBU_LAB_CATALOG: BambuLabCatalog = { }, 'Silk Multi-Color': { colors: [ - { name: 'Silk Aurora Purple', refill: false, spool: true }, - { name: 'Silk Phantom Blue', refill: false, spool: true }, - { name: 'Silk Mystic Magenta', refill: false, spool: true }, + { name: 'Aurora Purple', refill: false, spool: true }, + { name: 'Phantom Blue', refill: false, spool: true }, + { name: 'Mystic Magenta', refill: false, spool: true }, ], }, Metal: { diff --git a/src/data/bambuLabColors.ts b/src/data/bambuLabColors.ts index 55b96aa..9b09926 100644 --- a/src/data/bambuLabColors.ts +++ b/src/data/bambuLabColors.ts @@ -8,7 +8,7 @@ export const bambuLabColors: Record = { 'Apple Green': { hex: '#C6E188' }, 'Arctic Whisper': { hex: '#ECF7F8' }, 'Ash Grey': { hex: '#9B9EA0' }, - 'Aurora Purple': { hex: '#285BB7' }, + 'Aurora Purple': { hex: ['#7F3696', '#006EC9'], isGradient: true }, 'Azure': { hex: '#489FDF' }, 'Baby Blue': { hex: '#AEC3ED' }, 'Bambu Green': { hex: '#00AE42' }, @@ -73,8 +73,6 @@ export const bambuLabColors: Record = { 'Iridium Gold Metallic': { hex: '#B39B84' }, 'Iris Purple': { hex: '#69398E' }, 'Iron Gray Metallic': { hex: '#6B6C6F' }, - 'IronGray Metallic': { hex: '#6B6C6F' }, - 'Ivory White': { hex: '#FFFFFF' }, 'Jade White': { hex: '#FFFFFF' }, 'Jeans Blue': { hex: '#6E88BC' }, 'Lake Blue': { hex: '#4672E4' }, @@ -103,7 +101,6 @@ export const bambuLabColors: Record = { 'Nardo Gray': { hex: '#747474' }, 'Navy Blue': { hex: '#0C2340' }, 'Nebulae': { hex: '#424379' }, - 'Nebulane': { hex: '#424379' }, 'Neon City': { hex: '#0047BB' }, 'Neon Green': { hex: '#ABFF1E' }, 'Neon Orange': { hex: '#F68A1B' }, @@ -147,8 +144,6 @@ export const bambuLabColors: Record = { 'Yellow': { hex: '#F4EE2A' }, // ABS Colors - 'ABS GF Yellow': { hex: '#FDD835' }, - 'ABS GF Orange': { hex: '#F48438' }, 'ABS Azure': { hex: '#489FDF' }, 'ABS Olive': { hex: '#748C45' }, 'ABS Blue': { hex: '#0A2989' }, @@ -169,7 +164,7 @@ export const bambuLabColors: Record = { 'Translucent Olive': { hex: '#A4B885' }, 'Translucent Pink': { hex: '#F9B8D0' }, 'Translucent Light Blue': { hex: '#A8D8F0' }, - 'Translucent Tea': { hex: '#D9C7A8' }, + 'Translucent Teal': { hex: '#008080' }, // PLA Matte Colors 'Matte Ivory White': { hex: '#FFFFF0' }, @@ -199,19 +194,14 @@ export const bambuLabColors: Record = { 'Matte Terracotta': { hex: '#A25A37' }, // PLA Silk Multi-Color - 'Silk Aurora Purple': { hex: ['#7F3696', '#006EC9'], isGradient: true }, - 'Silk Phantom Blue': { hex: ['#00629B', '#000000'], isGradient: true }, - 'Silk Mystic Magenta': { hex: ['#720062', '#3A913F'], isGradient: true }, + 'Phantom Blue': { hex: ['#00629B', '#000000'], isGradient: true }, + 'Mystic Magenta': { hex: ['#720062', '#3A913F'], isGradient: true }, // TPU Colors 'Flesh': { hex: '#E8C4A2' }, 'Grape Jelly': { hex: '#6B2D75' }, 'Crystal Blue': { hex: '#5BC0EB' }, - 'Quicksilver': { hex: '#A6A9AA' }, - 'TPU 95A HF Yellow': { hex: '#F3E600' }, - - // Default fallback - 'Unknown': { hex: '#CCCCCC' } + 'Quicksilver': { hex: '#A6A9AA' } }; export function getFilamentColor(colorName: string): ColorMapping { @@ -240,7 +230,7 @@ export function getFilamentColor(colorName: string): ColorMapping { } // Return default unknown color - return bambuLabColors['Unknown']; + return { hex: '#CCCCCC' }; } export function getColorStyle(colorMapping: ColorMapping): React.CSSProperties { diff --git a/src/data/bambuLabColorsComplete.ts b/src/data/bambuLabColorsComplete.ts index b992aaa..0fbc48d 100644 --- a/src/data/bambuLabColorsComplete.ts +++ b/src/data/bambuLabColorsComplete.ts @@ -100,9 +100,9 @@ export const bambuLabColors: Record = { "Baby Blue": "#AEC3ED", // PLA Silk Multi-Color - "Silk Aurora Purple": "#7F3696", - "Silk Phantom Blue": "#00629B", - "Silk Mystic Magenta": "#720062", + "Aurora Purple": "#7F3696", + "Phantom Blue": "#00629B", + "Mystic Magenta": "#720062", // PLA Metal "Iron Gray Metallic": "#6B6C6F", @@ -162,7 +162,7 @@ export const bambuLabColors: Record = { "Translucent Olive": "#A4B885", "Translucent Pink": "#F9B8D0", "Translucent Light Blue": "#A8D8F0", - "Translucent Tea": "#D9C7A8", + "Translucent Teal": "#008080", // PETG CF "Brick Red": "#9F332A",