Compare commits
2 Commits
deploy-202
...
deploy-202
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d55d0e5e02 | ||
|
|
cff73c1381 |
10
CLAUDE.md
10
CLAUDE.md
@@ -48,14 +48,14 @@ scripts/deploy-frontend.sh # Manual frontend deploy to S3/CloudFront
|
|||||||
|
|
||||||
### Frontend (Next.js App Router)
|
### Frontend (Next.js App Router)
|
||||||
- `/app/page.tsx` — Public filament inventory table (home page)
|
- `/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/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/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)
|
- `/src/data/bambuLabColorsComplete.ts` — Extended color database grouped by finish type (used by filters)
|
||||||
|
|
||||||
### API (`/api/server.js`)
|
### API (`/api/server.js`)
|
||||||
Single-file Express server running on EC2 (port 80). All routes inline.
|
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)
|
- Auth: JWT tokens with 24h expiry, single admin user (password from `ADMIN_PASSWORD` env var)
|
||||||
|
|
||||||
### Database (PostgreSQL on RDS)
|
### Database (PostgreSQL on RDS)
|
||||||
@@ -64,13 +64,15 @@ Key schemas and constraints:
|
|||||||
```
|
```
|
||||||
filaments: id, tip (material), finish, boja (color), refill, spulna, kolicina, cena, sale_*
|
filaments: id, tip (material), finish, boja (color), refill, spulna, kolicina, cena, sale_*
|
||||||
colors: id, name, hex, cena_refill (default 3499), cena_spulna (default 3999)
|
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
|
color_requests: id, color_name, message, contact_name, contact_phone, status
|
||||||
```
|
```
|
||||||
|
|
||||||
**Critical constraints:**
|
**Critical constraints:**
|
||||||
- `filaments.boja` → FK to `colors.name` (ON UPDATE CASCADE) — colors must exist before filaments reference them
|
- `filaments.boja` → FK to `colors.name` (ON UPDATE CASCADE) — colors must exist before filaments reference them
|
||||||
- `kolicina = refill + spulna` — enforced by check constraint
|
- `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
|
## 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
|
3. Optionally add to `src/data/bambuLabColorsComplete.ts` finish groups
|
||||||
|
|
||||||
### API Communication
|
### 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
|
- Auth token stored in localStorage with 24h expiry
|
||||||
- Cache busting on filament fetches via timestamp query param
|
- Cache busting on filament fetches via timestamp query param
|
||||||
|
|
||||||
|
|||||||
@@ -33,9 +33,9 @@ describe('Bambu Lab Colors Data', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have Unknown fallback color', () => {
|
it('should return fallback for unknown colors', () => {
|
||||||
expect(bambuLabColors).toHaveProperty('Unknown');
|
const result = getFilamentColor('NonExistentColor');
|
||||||
expect(bambuLabColors.Unknown.hex).toBe('#CCCCCC');
|
expect(result.hex).toBe('#CCCCCC');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -21,9 +21,9 @@ describe('Bambu Lab Colors Complete Data', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should have silk colors', () => {
|
it('should have silk colors', () => {
|
||||||
expect(bambuLabColors).toHaveProperty('Silk Aurora Purple');
|
expect(bambuLabColors).toHaveProperty('Aurora Purple');
|
||||||
expect(bambuLabColors).toHaveProperty('Silk Phantom Blue');
|
expect(bambuLabColors).toHaveProperty('Phantom Blue');
|
||||||
expect(bambuLabColors).toHaveProperty('Silk Mystic Magenta');
|
expect(bambuLabColors).toHaveProperty('Mystic Magenta');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have valid hex colors', () => {
|
it('should have valid hex colors', () => {
|
||||||
|
|||||||
@@ -370,10 +370,24 @@ export default function ColorsManagement() {
|
|||||||
|
|
||||||
{/* Edit Modal */}
|
{/* Edit Modal */}
|
||||||
{editingColor && (
|
{editingColor && (
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
|
<div
|
||||||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-xl max-w-md w-full max-h-[90vh] overflow-auto">
|
className="fixed inset-0 bg-black/60 backdrop-blur-sm flex items-center justify-center z-50 p-4"
|
||||||
|
onClick={() => setEditingColor(null)}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="bg-white dark:bg-gray-800 rounded-xl shadow-2xl border border-gray-200 dark:border-gray-700 max-w-md w-full max-h-[90vh] overflow-auto"
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
<div className="flex justify-between items-center p-6 pb-0">
|
||||||
|
<h3 className="text-xl font-bold text-gray-900 dark:text-white">Izmeni boju</h3>
|
||||||
|
<button
|
||||||
|
onClick={() => setEditingColor(null)}
|
||||||
|
className="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 text-2xl leading-none"
|
||||||
|
>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<div className="p-6">
|
<div className="p-6">
|
||||||
<h3 className="text-xl font-bold mb-4 text-gray-900 dark:text-white">Izmeni boju</h3>
|
|
||||||
<ColorForm
|
<ColorForm
|
||||||
color={editingColor}
|
color={editingColor}
|
||||||
onSave={(color) => {
|
onSave={(color) => {
|
||||||
@@ -477,7 +491,7 @@ function ColorForm({
|
|||||||
readOnly={isBambuLabColor}
|
readOnly={isBambuLabColor}
|
||||||
pattern="^#[0-9A-Fa-f]{6}$"
|
pattern="^#[0-9A-Fa-f]{6}$"
|
||||||
placeholder="#000000"
|
placeholder="#000000"
|
||||||
className={`flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500 ${isBambuLabColor ? 'bg-gray-100 dark:bg-gray-900 cursor-not-allowed' : ''}`}
|
className={`flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500 ${isBambuLabColor ? 'bg-gray-100 dark:bg-gray-900 cursor-not-allowed' : 'bg-white dark:bg-gray-700'}`}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{isBambuLabColor && (
|
{isBambuLabColor && (
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ export default function CustomersManagement() {
|
|||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
const [darkMode, setDarkMode] = useState(false);
|
const [darkMode, setDarkMode] = useState(false);
|
||||||
const [mounted, setMounted] = useState(false);
|
const [mounted, setMounted] = useState(false);
|
||||||
|
const [showAddModal, setShowAddModal] = useState(false);
|
||||||
|
const [addForm, setAddForm] = useState({ name: '', phone: '', city: '', notes: '' });
|
||||||
|
const [addError, setAddError] = useState('');
|
||||||
|
const [addSaving, setAddSaving] = useState(false);
|
||||||
|
|
||||||
// Initialize dark mode
|
// Initialize dark mode
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -139,6 +143,35 @@ export default function CustomersManagement() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleAddCustomer = async () => {
|
||||||
|
if (!addForm.name.trim()) {
|
||||||
|
setAddError('Ime je obavezno');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setAddSaving(true);
|
||||||
|
setAddError('');
|
||||||
|
try {
|
||||||
|
await customerService.create({
|
||||||
|
name: addForm.name.trim(),
|
||||||
|
phone: addForm.phone.trim() || undefined,
|
||||||
|
city: addForm.city.trim() || undefined,
|
||||||
|
notes: addForm.notes.trim() || undefined,
|
||||||
|
});
|
||||||
|
setShowAddModal(false);
|
||||||
|
setAddForm({ name: '', phone: '', city: '', notes: '' });
|
||||||
|
await fetchCustomers();
|
||||||
|
} catch (err: unknown) {
|
||||||
|
const error = err as { response?: { data?: { code?: string } } };
|
||||||
|
if (error.response?.data?.code === '23505') {
|
||||||
|
setAddError('Kupac sa ovim brojem telefona vec postoji');
|
||||||
|
} else {
|
||||||
|
setAddError('Greska pri dodavanju kupca');
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
setAddSaving(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleLogout = () => {
|
const handleLogout = () => {
|
||||||
localStorage.removeItem('authToken');
|
localStorage.removeItem('authToken');
|
||||||
localStorage.removeItem('tokenExpiry');
|
localStorage.removeItem('tokenExpiry');
|
||||||
@@ -230,8 +263,8 @@ export default function CustomersManagement() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Search bar */}
|
{/* Search bar + Add button */}
|
||||||
<div className="mb-6">
|
<div className="mb-6 flex items-center gap-4">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Pretrazi kupce..."
|
placeholder="Pretrazi kupce..."
|
||||||
@@ -239,6 +272,12 @@ export default function CustomersManagement() {
|
|||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
onChange={(e) => setSearchTerm(e.target.value)}
|
||||||
className="w-full max-w-md px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white placeholder-gray-400 dark:placeholder-gray-500 focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
className="w-full max-w-md px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white placeholder-gray-400 dark:placeholder-gray-500 focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||||
/>
|
/>
|
||||||
|
<button
|
||||||
|
onClick={() => setShowAddModal(true)}
|
||||||
|
className="px-4 py-2 bg-green-500 text-white rounded-lg hover:bg-green-600 whitespace-nowrap font-medium"
|
||||||
|
>
|
||||||
|
+ Dodaj kupca
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Customer count */}
|
{/* Customer count */}
|
||||||
@@ -488,6 +527,108 @@ export default function CustomersManagement() {
|
|||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Add Customer Modal */}
|
||||||
|
{showAddModal && (
|
||||||
|
<div
|
||||||
|
className="fixed inset-0 bg-black/50 flex items-center justify-center z-50"
|
||||||
|
onClick={() => setShowAddModal(false)}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="bg-white dark:bg-gray-800 rounded-lg shadow-xl w-full max-w-md mx-4 p-6"
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
<div className="flex justify-between items-center mb-4">
|
||||||
|
<h2 className="text-xl font-bold text-gray-900 dark:text-white">
|
||||||
|
Dodaj kupca
|
||||||
|
</h2>
|
||||||
|
<button
|
||||||
|
onClick={() => setShowAddModal(false)}
|
||||||
|
className="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 text-2xl leading-none"
|
||||||
|
>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{addError && (
|
||||||
|
<div className="mb-4 p-3 bg-red-50 dark:bg-red-900/20 text-red-800 dark:text-red-400 rounded text-sm">
|
||||||
|
{addError}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||||
|
Ime *
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={addForm.name}
|
||||||
|
onChange={(e) => setAddForm({ ...addForm, name: e.target.value })}
|
||||||
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-green-500 focus:border-transparent"
|
||||||
|
placeholder="Ime i prezime"
|
||||||
|
autoFocus
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||||
|
Telefon
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={addForm.phone}
|
||||||
|
onChange={(e) => setAddForm({ ...addForm, phone: e.target.value })}
|
||||||
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-green-500 focus:border-transparent"
|
||||||
|
placeholder="Broj telefona"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||||
|
Grad
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={addForm.city}
|
||||||
|
onChange={(e) => setAddForm({ ...addForm, city: e.target.value })}
|
||||||
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-green-500 focus:border-transparent"
|
||||||
|
placeholder="Grad"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||||
|
Napomena
|
||||||
|
</label>
|
||||||
|
<textarea
|
||||||
|
value={addForm.notes}
|
||||||
|
onChange={(e) => setAddForm({ ...addForm, notes: e.target.value })}
|
||||||
|
rows={3}
|
||||||
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-green-500 focus:border-transparent"
|
||||||
|
placeholder="npr. stampa figurice, obicno crna..."
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-end gap-3 mt-6">
|
||||||
|
<button
|
||||||
|
onClick={() => setShowAddModal(false)}
|
||||||
|
className="px-4 py-2 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-300 dark:hover:bg-gray-600"
|
||||||
|
>
|
||||||
|
Otkazi
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={handleAddCustomer}
|
||||||
|
disabled={addSaving}
|
||||||
|
className="px-4 py-2 bg-green-500 text-white rounded-lg hover:bg-green-600 disabled:opacity-50 font-medium"
|
||||||
|
>
|
||||||
|
{addSaving ? 'Cuvanje...' : 'Dodaj'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
100
database/migrations/023_cleanup_fake_colors_add_missing.sql
Normal file
100
database/migrations/023_cleanup_fake_colors_add_missing.sql
Normal file
@@ -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;
|
||||||
@@ -115,9 +115,9 @@ export const BAMBU_LAB_CATALOG: BambuLabCatalog = {
|
|||||||
},
|
},
|
||||||
'Silk Multi-Color': {
|
'Silk Multi-Color': {
|
||||||
colors: [
|
colors: [
|
||||||
{ name: 'Silk Aurora Purple', refill: false, spool: true },
|
{ name: 'Aurora Purple', refill: false, spool: true },
|
||||||
{ name: 'Silk Phantom Blue', refill: false, spool: true },
|
{ name: 'Phantom Blue', refill: false, spool: true },
|
||||||
{ name: 'Silk Mystic Magenta', refill: false, spool: true },
|
{ name: 'Mystic Magenta', refill: false, spool: true },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
Metal: {
|
Metal: {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export const bambuLabColors: Record<string, ColorMapping> = {
|
|||||||
'Apple Green': { hex: '#C6E188' },
|
'Apple Green': { hex: '#C6E188' },
|
||||||
'Arctic Whisper': { hex: '#ECF7F8' },
|
'Arctic Whisper': { hex: '#ECF7F8' },
|
||||||
'Ash Grey': { hex: '#9B9EA0' },
|
'Ash Grey': { hex: '#9B9EA0' },
|
||||||
'Aurora Purple': { hex: '#285BB7' },
|
'Aurora Purple': { hex: ['#7F3696', '#006EC9'], isGradient: true },
|
||||||
'Azure': { hex: '#489FDF' },
|
'Azure': { hex: '#489FDF' },
|
||||||
'Baby Blue': { hex: '#AEC3ED' },
|
'Baby Blue': { hex: '#AEC3ED' },
|
||||||
'Bambu Green': { hex: '#00AE42' },
|
'Bambu Green': { hex: '#00AE42' },
|
||||||
@@ -73,8 +73,6 @@ export const bambuLabColors: Record<string, ColorMapping> = {
|
|||||||
'Iridium Gold Metallic': { hex: '#B39B84' },
|
'Iridium Gold Metallic': { hex: '#B39B84' },
|
||||||
'Iris Purple': { hex: '#69398E' },
|
'Iris Purple': { hex: '#69398E' },
|
||||||
'Iron Gray Metallic': { hex: '#6B6C6F' },
|
'Iron Gray Metallic': { hex: '#6B6C6F' },
|
||||||
'IronGray Metallic': { hex: '#6B6C6F' },
|
|
||||||
'Ivory White': { hex: '#FFFFFF' },
|
|
||||||
'Jade White': { hex: '#FFFFFF' },
|
'Jade White': { hex: '#FFFFFF' },
|
||||||
'Jeans Blue': { hex: '#6E88BC' },
|
'Jeans Blue': { hex: '#6E88BC' },
|
||||||
'Lake Blue': { hex: '#4672E4' },
|
'Lake Blue': { hex: '#4672E4' },
|
||||||
@@ -103,7 +101,6 @@ export const bambuLabColors: Record<string, ColorMapping> = {
|
|||||||
'Nardo Gray': { hex: '#747474' },
|
'Nardo Gray': { hex: '#747474' },
|
||||||
'Navy Blue': { hex: '#0C2340' },
|
'Navy Blue': { hex: '#0C2340' },
|
||||||
'Nebulae': { hex: '#424379' },
|
'Nebulae': { hex: '#424379' },
|
||||||
'Nebulane': { hex: '#424379' },
|
|
||||||
'Neon City': { hex: '#0047BB' },
|
'Neon City': { hex: '#0047BB' },
|
||||||
'Neon Green': { hex: '#ABFF1E' },
|
'Neon Green': { hex: '#ABFF1E' },
|
||||||
'Neon Orange': { hex: '#F68A1B' },
|
'Neon Orange': { hex: '#F68A1B' },
|
||||||
@@ -147,8 +144,6 @@ export const bambuLabColors: Record<string, ColorMapping> = {
|
|||||||
'Yellow': { hex: '#F4EE2A' },
|
'Yellow': { hex: '#F4EE2A' },
|
||||||
|
|
||||||
// ABS Colors
|
// ABS Colors
|
||||||
'ABS GF Yellow': { hex: '#FDD835' },
|
|
||||||
'ABS GF Orange': { hex: '#F48438' },
|
|
||||||
'ABS Azure': { hex: '#489FDF' },
|
'ABS Azure': { hex: '#489FDF' },
|
||||||
'ABS Olive': { hex: '#748C45' },
|
'ABS Olive': { hex: '#748C45' },
|
||||||
'ABS Blue': { hex: '#0A2989' },
|
'ABS Blue': { hex: '#0A2989' },
|
||||||
@@ -169,7 +164,7 @@ export const bambuLabColors: Record<string, ColorMapping> = {
|
|||||||
'Translucent Olive': { hex: '#A4B885' },
|
'Translucent Olive': { hex: '#A4B885' },
|
||||||
'Translucent Pink': { hex: '#F9B8D0' },
|
'Translucent Pink': { hex: '#F9B8D0' },
|
||||||
'Translucent Light Blue': { hex: '#A8D8F0' },
|
'Translucent Light Blue': { hex: '#A8D8F0' },
|
||||||
'Translucent Tea': { hex: '#D9C7A8' },
|
'Translucent Teal': { hex: '#008080' },
|
||||||
|
|
||||||
// PLA Matte Colors
|
// PLA Matte Colors
|
||||||
'Matte Ivory White': { hex: '#FFFFF0' },
|
'Matte Ivory White': { hex: '#FFFFF0' },
|
||||||
@@ -199,19 +194,14 @@ export const bambuLabColors: Record<string, ColorMapping> = {
|
|||||||
'Matte Terracotta': { hex: '#A25A37' },
|
'Matte Terracotta': { hex: '#A25A37' },
|
||||||
|
|
||||||
// PLA Silk Multi-Color
|
// PLA Silk Multi-Color
|
||||||
'Silk Aurora Purple': { hex: ['#7F3696', '#006EC9'], isGradient: true },
|
'Phantom Blue': { hex: ['#00629B', '#000000'], isGradient: true },
|
||||||
'Silk Phantom Blue': { hex: ['#00629B', '#000000'], isGradient: true },
|
'Mystic Magenta': { hex: ['#720062', '#3A913F'], isGradient: true },
|
||||||
'Silk Mystic Magenta': { hex: ['#720062', '#3A913F'], isGradient: true },
|
|
||||||
|
|
||||||
// TPU Colors
|
// TPU Colors
|
||||||
'Flesh': { hex: '#E8C4A2' },
|
'Flesh': { hex: '#E8C4A2' },
|
||||||
'Grape Jelly': { hex: '#6B2D75' },
|
'Grape Jelly': { hex: '#6B2D75' },
|
||||||
'Crystal Blue': { hex: '#5BC0EB' },
|
'Crystal Blue': { hex: '#5BC0EB' },
|
||||||
'Quicksilver': { hex: '#A6A9AA' },
|
'Quicksilver': { hex: '#A6A9AA' }
|
||||||
'TPU 95A HF Yellow': { hex: '#F3E600' },
|
|
||||||
|
|
||||||
// Default fallback
|
|
||||||
'Unknown': { hex: '#CCCCCC' }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getFilamentColor(colorName: string): ColorMapping {
|
export function getFilamentColor(colorName: string): ColorMapping {
|
||||||
@@ -240,7 +230,7 @@ export function getFilamentColor(colorName: string): ColorMapping {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return default unknown color
|
// Return default unknown color
|
||||||
return bambuLabColors['Unknown'];
|
return { hex: '#CCCCCC' };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getColorStyle(colorMapping: ColorMapping): React.CSSProperties {
|
export function getColorStyle(colorMapping: ColorMapping): React.CSSProperties {
|
||||||
|
|||||||
@@ -100,9 +100,9 @@ export const bambuLabColors: Record<string, string> = {
|
|||||||
"Baby Blue": "#AEC3ED",
|
"Baby Blue": "#AEC3ED",
|
||||||
|
|
||||||
// PLA Silk Multi-Color
|
// PLA Silk Multi-Color
|
||||||
"Silk Aurora Purple": "#7F3696",
|
"Aurora Purple": "#7F3696",
|
||||||
"Silk Phantom Blue": "#00629B",
|
"Phantom Blue": "#00629B",
|
||||||
"Silk Mystic Magenta": "#720062",
|
"Mystic Magenta": "#720062",
|
||||||
|
|
||||||
// PLA Metal
|
// PLA Metal
|
||||||
"Iron Gray Metallic": "#6B6C6F",
|
"Iron Gray Metallic": "#6B6C6F",
|
||||||
@@ -162,7 +162,7 @@ export const bambuLabColors: Record<string, string> = {
|
|||||||
"Translucent Olive": "#A4B885",
|
"Translucent Olive": "#A4B885",
|
||||||
"Translucent Pink": "#F9B8D0",
|
"Translucent Pink": "#F9B8D0",
|
||||||
"Translucent Light Blue": "#A8D8F0",
|
"Translucent Light Blue": "#A8D8F0",
|
||||||
"Translucent Tea": "#D9C7A8",
|
"Translucent Teal": "#008080",
|
||||||
|
|
||||||
// PETG CF
|
// PETG CF
|
||||||
"Brick Red": "#9F332A",
|
"Brick Red": "#9F332A",
|
||||||
|
|||||||
Reference in New Issue
Block a user