- {/* Main Content - Full Screen */}
-
+
+
+ {/* Main Content */}
+
);
@@ -710,7 +649,7 @@ function FilamentForm({
finish: filament.finish || (filament.id ? '' : 'Basic'), // Default to Basic for new filaments
boja: filament.boja || '',
boja_hex: filament.boja_hex || '',
- refill: isSpoolOnly(filament.finish, filament.tip) ? 0 : (filament.refill || 0), // Store as number
+ refill: isSpoolOnly(filament.finish, filament.tip, filament.boja) ? 0 : (filament.refill || 0), // Store as number
spulna: isRefillOnly(filament.boja || '', filament.finish, filament.tip) ? 0 : (filament.spulna || 0), // Store as number
kolicina: filament.kolicina || 0, // Default to 0, stored as number
cena: '', // Price is now determined by color selection
@@ -787,9 +726,9 @@ function FilamentForm({
});
} else if (name === 'tip') {
// If changing filament type, reset finish if it's not compatible
- const newTypeFinishes = FINISH_OPTIONS_BY_TYPE[value] || [];
+ const newTypeFinishes = getFinishesForMaterial(value);
const resetFinish = !newTypeFinishes.includes(formData.finish);
- const spoolOnly = isSpoolOnly(formData.finish, value);
+ const spoolOnly = isSpoolOnly(formData.finish, value, formData.boja);
// If changing to PPA with CF finish and current color is not black, reset color
const needsColorReset = value === 'PPA' && formData.finish === 'CF' && formData.boja.toLowerCase() !== 'black';
setFormData({
@@ -810,7 +749,7 @@ function FilamentForm({
} else if (name === 'finish') {
// If changing to Translucent finish, enable spool option and disable refill
const refillOnly = isRefillOnly(formData.boja, value, formData.tip);
- const spoolOnly = isSpoolOnly(value, formData.tip);
+ const spoolOnly = isSpoolOnly(value, formData.tip, formData.boja);
// If changing to PPA CF and current color is not black, reset color
const needsColorReset = formData.tip === 'PPA' && value === 'CF' && formData.boja.toLowerCase() !== 'black';
setFormData({
@@ -887,17 +826,9 @@ function FilamentForm({
className="custom-select w-full 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"
>
-
-
-
-
-
-
-
-
-
-
-
+ {getMaterialOptions().map(mat => (
+
+ ))}
@@ -911,7 +842,7 @@ function FilamentForm({
className="custom-select w-full 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"
>
- {(FINISH_OPTIONS_BY_TYPE[formData.tip] || []).map(finish => (
+ {getFinishesForMaterial(formData.tip).map(finish => (
))}
@@ -991,9 +922,9 @@ function FilamentForm({
min="0"
step="1"
placeholder="3499"
- disabled={isSpoolOnly(formData.finish, formData.tip)}
+ disabled={isSpoolOnly(formData.finish, formData.tip, formData.boja)}
className={`w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md ${
- isSpoolOnly(formData.finish, formData.tip)
+ isSpoolOnly(formData.finish, formData.tip, formData.boja)
? 'bg-gray-100 dark:bg-gray-600 cursor-not-allowed'
: 'bg-white dark:bg-gray-700'
} text-green-600 dark:text-green-400 font-bold focus:outline-none focus:ring-2 focus:ring-green-500`}
@@ -1025,7 +956,7 @@ function FilamentForm({
@@ -1037,9 +968,9 @@ function FilamentForm({
min="0"
step="1"
placeholder="0"
- disabled={isSpoolOnly(formData.finish, formData.tip)}
+ disabled={isSpoolOnly(formData.finish, formData.tip, formData.boja)}
className={`w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md ${
- isSpoolOnly(formData.finish, formData.tip)
+ isSpoolOnly(formData.finish, formData.tip, formData.boja)
? 'bg-gray-100 dark:bg-gray-600 cursor-not-allowed'
: 'bg-white dark:bg-gray-700'
} text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500`}
diff --git a/app/upadaj/analytics/page.tsx b/app/upadaj/analytics/page.tsx
index 0703c18..ad778f7 100644
--- a/app/upadaj/analytics/page.tsx
+++ b/app/upadaj/analytics/page.tsx
@@ -5,6 +5,7 @@ import { useRouter } from 'next/navigation';
import { analyticsService } from '@/src/services/api';
import type { AnalyticsOverview, TopSeller, RevenueDataPoint, InventoryAlert, TypeBreakdown } from '@/src/types/sales';
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, BarChart, Bar, PieChart, Pie, Cell, Legend } from 'recharts';
+import { AdminSidebar } from '@/src/components/AdminSidebar';
type Period = '7d' | '30d' | '90d' | '6m' | '1y' | 'all';
@@ -127,20 +128,7 @@ export default function AnalyticsDashboard() {
return (
- {/* Sidebar */}
-
+
{/* Main Content */}
diff --git a/app/upadaj/colors/page.tsx b/app/upadaj/colors/page.tsx
index 28c9bad..d129211 100644
--- a/app/upadaj/colors/page.tsx
+++ b/app/upadaj/colors/page.tsx
@@ -5,6 +5,7 @@ import { useRouter } from 'next/navigation';
import { colorService } from '@/src/services/api';
import { bambuLabColors, getColorHex } from '@/src/data/bambuLabColorsComplete';
import { BulkPriceEditor } from '@/src/components/BulkPriceEditor';
+import { AdminSidebar } from '@/src/components/AdminSidebar';
import '@/src/styles/select.css';
interface Color {
@@ -188,20 +189,7 @@ export default function ColorsManagement() {
return (
- {/* Sidebar */}
-
+
{/* Main Content */}
diff --git a/app/upadaj/customers/page.tsx b/app/upadaj/customers/page.tsx
index 22533b8..7e34341 100644
--- a/app/upadaj/customers/page.tsx
+++ b/app/upadaj/customers/page.tsx
@@ -4,6 +4,7 @@ import { useState, useEffect, useCallback } from 'react';
import { useRouter } from 'next/navigation';
import { customerService } from '@/src/services/api';
import { Customer, Sale } from '@/src/types/sales';
+import { AdminSidebar } from '@/src/components/AdminSidebar';
interface CustomerWithSales extends Customer {
sales?: Sale[];
@@ -172,50 +173,7 @@ export default function CustomersManagement() {
return (
- {/* Sidebar */}
-
+
{/* Main Content */}
diff --git a/app/upadaj/page.tsx b/app/upadaj/page.tsx
index 610601a..35065c0 100644
--- a/app/upadaj/page.tsx
+++ b/app/upadaj/page.tsx
@@ -12,6 +12,15 @@ export default function AdminLogin() {
const [error, setError] = useState('');
const [loading, setLoading] = useState(false);
+ // Redirect to dashboard if already authenticated
+ useEffect(() => {
+ const token = localStorage.getItem('authToken');
+ const expiry = localStorage.getItem('tokenExpiry');
+ if (token && expiry && Date.now() < parseInt(expiry)) {
+ window.location.href = '/dashboard';
+ }
+ }, []);
+
// Set dark mode by default
useEffect(() => {
document.documentElement.classList.add('dark');
diff --git a/app/upadaj/requests/page.tsx b/app/upadaj/requests/page.tsx
index b933c00..f8032ec 100644
--- a/app/upadaj/requests/page.tsx
+++ b/app/upadaj/requests/page.tsx
@@ -4,6 +4,7 @@ import React, { useState, useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { colorRequestService } from '@/src/services/api';
import Link from 'next/link';
+import { AdminSidebar } from '@/src/components/AdminSidebar';
interface ColorRequest {
id: string;
@@ -120,16 +121,11 @@ export default function ColorRequestsAdmin() {
return (
-
+
+
+
Zahtevi za Boje
-
- Inventar
- Boje
- Prodaja
- Kupci
- Analitika
-
{error && (
@@ -347,6 +343,7 @@ export default function ColorRequestsAdmin() {
+
);
diff --git a/app/upadaj/sales/page.tsx b/app/upadaj/sales/page.tsx
index 76a127d..afedb7e 100644
--- a/app/upadaj/sales/page.tsx
+++ b/app/upadaj/sales/page.tsx
@@ -4,6 +4,7 @@ import { useState, useEffect, useCallback, useRef } from 'react';
import { saleService, customerService, filamentService, colorService } from '@/src/services/api';
import { Customer, Sale, SaleItem, CreateSaleRequest } from '@/src/types/sales';
import { Filament } from '@/src/types/filament';
+import { AdminSidebar } from '@/src/components/AdminSidebar';
interface LineItem {
filament_id: string;
@@ -120,50 +121,7 @@ export default function SalesPage() {
return (
- {/* Sidebar */}
-
+
{/* Main Content */}
diff --git a/src/components/AdminSidebar.tsx b/src/components/AdminSidebar.tsx
new file mode 100644
index 0000000..fe423ab
--- /dev/null
+++ b/src/components/AdminSidebar.tsx
@@ -0,0 +1,42 @@
+'use client'
+
+import { usePathname } from 'next/navigation'
+
+const navItems = [
+ { href: '/dashboard', label: 'Filamenti' },
+ { href: '/upadaj/colors', label: 'Boje' },
+ { href: '/upadaj/requests', label: 'Zahtevi za boje' },
+ { href: '/upadaj/sales', label: 'Prodaja' },
+ { href: '/upadaj/customers', label: 'Kupci' },
+ { href: '/upadaj/analytics', label: 'Analitika' },
+]
+
+export function AdminSidebar() {
+ const pathname = usePathname()
+
+ return (
+
+
+
Admin Panel
+
+
+
+ )
+}
diff --git a/src/components/EnhancedFilters.tsx b/src/components/EnhancedFilters.tsx
index e427171..308c00e 100644
--- a/src/components/EnhancedFilters.tsx
+++ b/src/components/EnhancedFilters.tsx
@@ -1,6 +1,8 @@
-import React from 'react';
+import React, { useMemo } from 'react';
import '@/src/styles/select.css';
import { trackEvent } from './MatomoAnalytics';
+import { getFinishesForMaterial, getAllFinishes, getMaterialOptions } from '@/src/data/bambuLabCatalog';
+import { Filament } from '@/src/types/filament';
interface EnhancedFiltersProps {
filters: {
@@ -8,22 +10,74 @@ interface EnhancedFiltersProps {
finish: string;
color: string;
};
- onFilterChange: (filters: any) => void;
+ onFilterChange: (filters: { material: string; finish: string; color: string }) => void;
uniqueValues: {
materials: string[];
finishes: string[];
colors: string[];
};
+ inventoryFilaments?: Filament[];
}
-export const EnhancedFilters: React.FC
= ({
- filters,
- onFilterChange,
- uniqueValues
+export const EnhancedFilters: React.FC = ({
+ filters,
+ onFilterChange,
+ uniqueValues,
+ inventoryFilaments = []
}) => {
- // Check if any filters are active
const hasActiveFilters = filters.material || filters.finish || filters.color;
+ // Catalog-aware material list (static from catalog)
+ const materialOptions = useMemo(() => getMaterialOptions(), []);
+
+ // Finish options: conditional on selected material
+ const finishOptions = useMemo(() => {
+ const inStock = inventoryFilaments.filter(f => f.kolicina > 0);
+ if (filters.material) {
+ // Show finishes from catalog for this material, but only if they exist in inventory
+ const catalogFinishes = getFinishesForMaterial(filters.material);
+ const inventoryFinishes = new Set(
+ inStock.filter(f => f.tip === filters.material).map(f => f.finish)
+ );
+ return catalogFinishes.filter(f => inventoryFinishes.has(f));
+ }
+ // No material selected: show all finishes from inventory
+ if (inStock.length > 0) {
+ return [...new Set(inStock.map(f => f.finish))].sort();
+ }
+ return getAllFinishes();
+ }, [filters.material, inventoryFilaments]);
+
+ // Color options: conditional on selected material + finish
+ const colorOptions = useMemo(() => {
+ const inStock = inventoryFilaments.filter(f => f.kolicina > 0);
+ let filtered = inStock;
+ if (filters.material) {
+ filtered = filtered.filter(f => f.tip === filters.material);
+ }
+ if (filters.finish) {
+ filtered = filtered.filter(f => f.finish === filters.finish);
+ }
+ return [...new Set(filtered.map(f => f.boja))].sort();
+ }, [filters.material, filters.finish, inventoryFilaments]);
+
+ const handleMaterialChange = (value: string) => {
+ // Reset finish and color when material changes
+ onFilterChange({ material: value, finish: '', color: '' });
+ trackEvent('Filter', 'Material', value || 'All');
+ };
+
+ const handleFinishChange = (value: string) => {
+ // Reset color when finish changes
+ onFilterChange({ ...filters, finish: value, color: '' });
+ trackEvent('Filter', 'Finish', value || 'All');
+ };
+
+ const handleColorChange = (value: string) => {
+ onFilterChange({ ...filters, color: value });
+ trackEvent('Filter', 'Color', value || 'All');
+ };
+
return (
{/* Filters Grid */}
@@ -35,26 +89,15 @@ export const EnhancedFilters: React.FC = ({
@@ -65,35 +108,15 @@ export const EnhancedFilters: React.FC = ({
@@ -104,16 +127,13 @@ export const EnhancedFilters: React.FC
= ({
@@ -141,4 +161,4 @@ export const EnhancedFilters: React.FC = ({
)}
);
-};
\ No newline at end of file
+};
diff --git a/src/components/FilamentTableV2.tsx b/src/components/FilamentTableV2.tsx
index 5c0303f..4bad8f2 100644
--- a/src/components/FilamentTableV2.tsx
+++ b/src/components/FilamentTableV2.tsx
@@ -141,10 +141,11 @@ const FilamentTableV2: React.FC
= ({ filaments }) => {
{/* Enhanced Filters */}
-
diff --git a/src/data/bambuLabCatalog.ts b/src/data/bambuLabCatalog.ts
new file mode 100644
index 0000000..e563463
--- /dev/null
+++ b/src/data/bambuLabCatalog.ts
@@ -0,0 +1,407 @@
+// Master Bambu Lab product catalog — single source of truth
+// Material → Finish → Color[] with refill/spool availability
+
+export interface CatalogColorEntry {
+ name: string;
+ refill: boolean;
+ spool: boolean;
+}
+
+export interface CatalogFinish {
+ colors: CatalogColorEntry[];
+}
+
+export type BambuLabCatalog = Record
>;
+
+export const BAMBU_LAB_CATALOG: BambuLabCatalog = {
+ PLA: {
+ Basic: {
+ colors: [
+ { name: 'Jade White', refill: true, spool: true },
+ { name: 'Black', refill: true, spool: true },
+ { name: 'Red', refill: true, spool: true },
+ { name: 'Bambu Green', refill: true, spool: true },
+ { name: 'Blue', refill: true, spool: true },
+ { name: 'Scarlet Red', refill: true, spool: true },
+ { name: 'Lemon Yellow', refill: true, spool: true },
+ { name: 'Cyan', refill: true, spool: true },
+ { name: 'Sakura Pink', refill: true, spool: true },
+ { name: 'Cobalt Blue', refill: true, spool: true },
+ { name: 'Mistletoe Green', refill: true, spool: true },
+ { name: 'Dark Red', refill: true, spool: true },
+ { name: 'Hot Pink', refill: true, spool: true },
+ { name: 'Lavender', refill: true, spool: true },
+ { name: 'Light Blue', refill: true, spool: true },
+ { name: 'Sky Blue', refill: true, spool: true },
+ { name: 'Sunflower Yellow', refill: true, spool: true },
+ { name: 'Pumpkin Orange', refill: true, spool: true },
+ { name: 'Lime', refill: true, spool: true },
+ { name: 'Blue Grey', refill: true, spool: false },
+ { name: 'Beige', refill: true, spool: false },
+ { name: 'Light Gray', refill: true, spool: false },
+ { name: 'Yellow', refill: true, spool: false },
+ { name: 'Orange', refill: true, spool: false },
+ { name: 'Gold', refill: true, spool: false },
+ { name: 'Bright Green', refill: true, spool: false },
+ { name: 'Pink', refill: true, spool: false },
+ { name: 'Magenta', refill: true, spool: false },
+ { name: 'Maroon Red', refill: true, spool: false },
+ { name: 'Purple', refill: true, spool: false },
+ { name: 'Turquoise', refill: true, spool: false },
+ { name: 'Brown', refill: true, spool: false },
+ { name: 'Bronze', refill: true, spool: false },
+ { name: 'Silver', refill: true, spool: false },
+ { name: 'Dark Gray', refill: true, spool: false },
+ ],
+ },
+ 'Basic Gradient': {
+ colors: [
+ { name: 'Neon City', refill: false, spool: true },
+ { name: 'Midnight Blaze', refill: false, spool: true },
+ { name: 'South Beach', refill: false, spool: true },
+ { name: 'Arctic Whisper', refill: false, spool: true },
+ { name: 'Cotton Candy Cloud', refill: false, spool: true },
+ { name: 'Ocean to Meadow', refill: false, spool: true },
+ { name: 'Solar Breeze', refill: false, spool: true },
+ { name: 'Velvet Eclipse', refill: false, spool: true },
+ { name: 'Dawn Radiance', refill: false, spool: true },
+ { name: 'Dusk Glare', refill: false, spool: true },
+ { name: 'Blueberry Bubblegum', refill: false, spool: true },
+ { name: 'Blue Hawaii', refill: false, spool: true },
+ { name: 'Gilded Rose', refill: false, spool: true },
+ { name: 'Pink Citrus', refill: false, spool: true },
+ { name: 'Mint Lime', refill: false, spool: true },
+ ],
+ },
+ Matte: {
+ colors: [
+ { name: 'Matte Ivory White', refill: true, spool: false },
+ { name: 'Matte Charcoal', refill: true, spool: false },
+ { name: 'Matte Scarlet Red', refill: true, spool: false },
+ { name: 'Matte Marine Blue', refill: true, spool: false },
+ { name: 'Matte Mandarin Orange', refill: true, spool: false },
+ { name: 'Matte Ash Gray', refill: true, spool: false },
+ { name: 'Matte Desert Tan', refill: true, spool: false },
+ { name: 'Matte Nardo Gray', refill: true, spool: false },
+ { name: 'Matte Apple Green', refill: true, spool: false },
+ { name: 'Matte Bone White', refill: true, spool: false },
+ { name: 'Matte Caramel', refill: true, spool: false },
+ { name: 'Matte Dark Blue', refill: true, spool: false },
+ { name: 'Matte Dark Brown', refill: true, spool: false },
+ { name: 'Matte Dark Chocolate', refill: true, spool: false },
+ { name: 'Matte Dark Green', refill: true, spool: false },
+ { name: 'Matte Dark Red', refill: true, spool: false },
+ { name: 'Matte Grass Green', refill: true, spool: false },
+ { name: 'Matte Ice Blue', refill: true, spool: false },
+ { name: 'Matte Lemon Yellow', refill: true, spool: false },
+ { name: 'Matte Lilac Purple', refill: true, spool: false },
+ { name: 'Matte Plum', refill: true, spool: false },
+ { name: 'Matte Sakura Pink', refill: true, spool: false },
+ { name: 'Matte Sky Blue', refill: true, spool: false },
+ { name: 'Matte Latte Brown', refill: true, spool: false },
+ { name: 'Matte Terracotta', refill: true, spool: false },
+ ],
+ },
+ 'Silk+': {
+ colors: [
+ { name: 'Candy Green', refill: false, spool: true },
+ { name: 'Candy Red', refill: false, spool: true },
+ { name: 'Mint', refill: false, spool: true },
+ { name: 'Titan Gray', refill: false, spool: true },
+ { name: 'Rose Gold', refill: false, spool: true },
+ { name: 'Champagne', refill: false, spool: true },
+ { name: 'Baby Blue', refill: false, spool: true },
+ ],
+ },
+ '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 },
+ ],
+ },
+ Metal: {
+ colors: [
+ { name: 'Iron Gray Metallic', refill: false, spool: true },
+ { name: 'Iridium Gold Metallic', refill: false, spool: true },
+ { name: 'Cobalt Blue Metallic', refill: false, spool: true },
+ { name: 'Copper Brown Metallic', refill: false, spool: true },
+ { name: 'Oxide Green Metallic', refill: false, spool: true },
+ ],
+ },
+ Sparkle: {
+ colors: [
+ { name: 'Onyx Black Sparkle', refill: false, spool: true },
+ { name: 'Classic Gold Sparkle', refill: false, spool: true },
+ { name: 'Crimson Red Sparkle', refill: false, spool: true },
+ { name: 'Royal Purple Sparkle', refill: false, spool: true },
+ { name: 'Slate Gray Sparkle', refill: false, spool: true },
+ { name: 'Alpine Green Sparkle', refill: false, spool: true },
+ ],
+ },
+ Galaxy: {
+ colors: [
+ { name: 'Nebulae', refill: true, spool: true },
+ ],
+ },
+ Marble: {
+ colors: [
+ { name: 'White Marble', refill: false, spool: true },
+ { name: 'Red Granite', refill: false, spool: true },
+ ],
+ },
+ Glow: {
+ colors: [
+ { name: 'Glow Blue', refill: false, spool: true },
+ { name: 'Glow Green', refill: false, spool: true },
+ { name: 'Glow Orange', refill: false, spool: true },
+ { name: 'Glow Pink', refill: false, spool: true },
+ { name: 'Glow Yellow', refill: false, spool: true },
+ ],
+ },
+ Wood: {
+ colors: [
+ { name: 'Ochre Yellow', refill: false, spool: true },
+ { name: 'White Oak', refill: false, spool: true },
+ { name: 'Clay Brown', refill: false, spool: true },
+ ],
+ },
+ 'Tough+': {
+ colors: [
+ { name: 'Black', refill: true, spool: true },
+ ],
+ },
+ CF: {
+ colors: [
+ { name: 'Black', refill: false, spool: true },
+ { name: 'Burgundy Red', refill: false, spool: true },
+ { name: 'Jeans Blue', refill: false, spool: true },
+ { name: 'Lava Gray', refill: false, spool: true },
+ { name: 'Matcha Green', refill: false, spool: true },
+ { name: 'Royal Blue', refill: false, spool: true },
+ { name: 'Iris Purple', refill: false, spool: true },
+ ],
+ },
+ },
+ PETG: {
+ HF: {
+ colors: [
+ { name: 'Jade White', refill: true, spool: true },
+ { name: 'Black', refill: true, spool: true },
+ { name: 'Red', refill: true, spool: true },
+ { name: 'Green', refill: true, spool: true },
+ { name: 'Blue', refill: true, spool: true },
+ { name: 'Gray', refill: true, spool: true },
+ { name: 'Orange', refill: true, spool: true },
+ { name: 'Cream', refill: true, spool: true },
+ { name: 'Forest Green', refill: true, spool: true },
+ { name: 'Lake Blue', refill: true, spool: true },
+ { name: 'Lime Green', refill: true, spool: true },
+ { name: 'Peanut Brown', refill: true, spool: true },
+ ],
+ },
+ Translucent: {
+ colors: [
+ { name: 'Clear', refill: false, spool: true },
+ { name: 'Translucent Gray', refill: false, spool: true },
+ { name: 'Translucent Brown', refill: false, spool: true },
+ { name: 'Translucent Purple', refill: false, spool: true },
+ { name: 'Translucent Orange', refill: false, spool: true },
+ { name: 'Translucent Olive', refill: false, spool: true },
+ { name: 'Translucent Pink', refill: false, spool: true },
+ { name: 'Translucent Light Blue', refill: false, spool: true },
+ { name: 'Translucent Tea', refill: false, spool: true },
+ ],
+ },
+ CF: {
+ colors: [
+ { name: 'Black', refill: false, spool: true },
+ { name: 'Brick Red', refill: false, spool: true },
+ { name: 'Indigo Blue', refill: false, spool: true },
+ { name: 'Malachite Green', refill: false, spool: true },
+ { name: 'Titan Gray', refill: false, spool: true },
+ { name: 'Violet Purple', refill: false, spool: true },
+ ],
+ },
+ },
+ ABS: {
+ Basic: {
+ colors: [
+ { name: 'ABS Azure', refill: true, spool: true },
+ { name: 'ABS Black', refill: true, spool: true },
+ { name: 'ABS Blue', refill: true, spool: true },
+ { name: 'ABS Olive', refill: true, spool: true },
+ { name: 'ABS Tangerine Yellow', refill: true, spool: true },
+ { name: 'ABS Navy Blue', refill: true, spool: true },
+ { name: 'ABS Orange', refill: true, spool: true },
+ { name: 'ABS Bambu Green', refill: true, spool: true },
+ { name: 'ABS Red', refill: true, spool: true },
+ { name: 'ABS White', refill: true, spool: true },
+ { name: 'ABS Silver', refill: true, spool: true },
+ ],
+ },
+ GF: {
+ colors: [
+ { name: 'ABS GF Yellow', refill: true, spool: false },
+ { name: 'ABS GF Orange', refill: true, spool: false },
+ ],
+ },
+ },
+ TPU: {
+ '85A': {
+ colors: [
+ { name: 'Black', refill: false, spool: true },
+ { name: 'White', refill: false, spool: true },
+ { name: 'Flesh', refill: false, spool: true },
+ { name: 'Light Cyan', refill: false, spool: true },
+ { name: 'Neon Orange', refill: false, spool: true },
+ ],
+ },
+ '90A': {
+ colors: [
+ { name: 'Black', refill: false, spool: true },
+ { name: 'White', refill: false, spool: true },
+ { name: 'Red', refill: false, spool: true },
+ { name: 'Blaze', refill: false, spool: true },
+ { name: 'Frozen', refill: false, spool: true },
+ { name: 'Grape Jelly', refill: false, spool: true },
+ { name: 'Crystal Blue', refill: false, spool: true },
+ { name: 'Quicksilver', refill: false, spool: true },
+ { name: 'Cocoa Brown', refill: false, spool: true },
+ ],
+ },
+ '95A HF': {
+ colors: [
+ { name: 'Black', refill: true, spool: false },
+ { name: 'White', refill: true, spool: false },
+ { name: 'TPU 95A HF Yellow', refill: true, spool: false },
+ ],
+ },
+ },
+ ASA: {
+ Basic: {
+ colors: [
+ { name: 'Black', refill: true, spool: true },
+ { name: 'Blue', refill: true, spool: true },
+ { name: 'Gray', refill: true, spool: true },
+ { name: 'Green', refill: true, spool: true },
+ { name: 'Red', refill: true, spool: true },
+ { name: 'White', refill: true, spool: true },
+ ],
+ },
+ CF: {
+ colors: [
+ { name: 'Black', refill: false, spool: true },
+ ],
+ },
+ Aero: {
+ colors: [
+ { name: 'White', refill: false, spool: true },
+ ],
+ },
+ },
+ PA6: {
+ GF: {
+ colors: [
+ { name: 'Black', refill: false, spool: true },
+ ],
+ },
+ },
+ PAHT: {
+ CF: {
+ colors: [
+ { name: 'Black', refill: false, spool: true },
+ ],
+ },
+ },
+ PC: {
+ Basic: {
+ colors: [
+ { name: 'Clear Black', refill: false, spool: true },
+ { name: 'Transparent', refill: false, spool: true },
+ ],
+ },
+ FR: {
+ colors: [
+ { name: 'Black', refill: false, spool: true },
+ ],
+ },
+ },
+ PET: {
+ CF: {
+ colors: [
+ { name: 'Black', refill: false, spool: true },
+ ],
+ },
+ },
+ PPA: {
+ CF: {
+ colors: [
+ { name: 'Black', refill: false, spool: true },
+ ],
+ },
+ },
+ PPS: {
+ CF: {
+ colors: [
+ { name: 'Black', refill: false, spool: true },
+ ],
+ },
+ },
+};
+
+// Helper functions
+
+export function getMaterialOptions(): string[] {
+ return Object.keys(BAMBU_LAB_CATALOG).sort();
+}
+
+export function getFinishesForMaterial(material: string): string[] {
+ const materialData = BAMBU_LAB_CATALOG[material];
+ if (!materialData) return [];
+ return Object.keys(materialData).sort();
+}
+
+export function getAllFinishes(): string[] {
+ const finishes = new Set();
+ for (const material of Object.values(BAMBU_LAB_CATALOG)) {
+ for (const finish of Object.keys(material)) {
+ finishes.add(finish);
+ }
+ }
+ return [...finishes].sort();
+}
+
+export function getColorsForMaterialFinish(material: string, finish: string): CatalogColorEntry[] {
+ return BAMBU_LAB_CATALOG[material]?.[finish]?.colors ?? [];
+}
+
+export function getColorsForMaterial(material: string): CatalogColorEntry[] {
+ const materialData = BAMBU_LAB_CATALOG[material];
+ if (!materialData) return [];
+ const seen = new Set();
+ const result: CatalogColorEntry[] = [];
+ for (const finish of Object.values(materialData)) {
+ for (const color of finish.colors) {
+ if (!seen.has(color.name)) {
+ seen.add(color.name);
+ result.push(color);
+ }
+ }
+ }
+ return result.sort((a, b) => a.name.localeCompare(b.name));
+}
+
+export function catalogIsSpoolOnly(material: string, finish: string, color: string): boolean {
+ const entry = BAMBU_LAB_CATALOG[material]?.[finish]?.colors.find(c => c.name === color);
+ return entry ? (entry.spool && !entry.refill) : false;
+}
+
+export function catalogIsRefillOnly(material: string, finish: string, color: string): boolean {
+ const entry = BAMBU_LAB_CATALOG[material]?.[finish]?.colors.find(c => c.name === color);
+ return entry ? (entry.refill && !entry.spool) : false;
+}
+
+export function getFinishOptionsForType(type: string): string[] {
+ return getFinishesForMaterial(type);
+}
diff --git a/src/data/bambuLabColors.ts b/src/data/bambuLabColors.ts
index 51d00be..55b96aa 100644
--- a/src/data/bambuLabColors.ts
+++ b/src/data/bambuLabColors.ts
@@ -147,11 +147,8 @@ export const bambuLabColors: Record = {
'Yellow': { hex: '#F4EE2A' },
// ABS Colors
- // ABS GF Colors
'ABS GF Yellow': { hex: '#FDD835' },
'ABS GF Orange': { hex: '#F48438' },
-
- // ABS Colors
'ABS Azure': { hex: '#489FDF' },
'ABS Olive': { hex: '#748C45' },
'ABS Blue': { hex: '#0A2989' },
@@ -164,7 +161,7 @@ export const bambuLabColors: Record = {
'ABS Black': { hex: '#000000' },
'ABS Silver': { hex: '#A6A9AA' },
- // Translucent Colors
+ // PETG Translucent Colors
'Translucent Gray': { hex: '#B8B8B8' },
'Translucent Brown': { hex: '#C89A74' },
'Translucent Purple': { hex: '#C5A8D8' },
@@ -174,7 +171,15 @@ export const bambuLabColors: Record = {
'Translucent Light Blue': { hex: '#A8D8F0' },
'Translucent Tea': { hex: '#D9C7A8' },
- // PLA Matte - New Colors (2025)
+ // PLA Matte Colors
+ 'Matte Ivory White': { hex: '#FFFFF0' },
+ 'Matte Charcoal': { hex: '#333333' },
+ 'Matte Scarlet Red': { hex: '#DE4343' },
+ 'Matte Marine Blue': { hex: '#0078BF' },
+ 'Matte Mandarin Orange': { hex: '#F99963' },
+ 'Matte Ash Gray': { hex: '#9B9EA0' },
+ 'Matte Desert Tan': { hex: '#E8DBB7' },
+ 'Matte Nardo Gray': { hex: '#747474' },
'Matte Apple Green': { hex: '#C6E188' },
'Matte Bone White': { hex: '#C8C5B6' },
'Matte Caramel': { hex: '#A4845C' },
@@ -185,7 +190,6 @@ export const bambuLabColors: Record = {
'Matte Dark Red': { hex: '#BB3D43' },
'Matte Grass Green': { hex: '#7CB342' },
'Matte Ice Blue': { hex: '#A3D8E1' },
- 'Matte Ivory': { hex: '#FFFFF0' },
'Matte Lemon Yellow': { hex: '#F7D959' },
'Matte Lilac Purple': { hex: '#AE96D4' },
'Matte Plum': { hex: '#851A52' },
@@ -199,7 +203,11 @@ export const bambuLabColors: Record = {
'Silk Phantom Blue': { hex: ['#00629B', '#000000'], isGradient: true },
'Silk Mystic Magenta': { hex: ['#720062', '#3A913F'], isGradient: true },
- // TPU 95A HF Colors
+ // TPU Colors
+ 'Flesh': { hex: '#E8C4A2' },
+ 'Grape Jelly': { hex: '#6B2D75' },
+ 'Crystal Blue': { hex: '#5BC0EB' },
+ 'Quicksilver': { hex: '#A6A9AA' },
'TPU 95A HF Yellow': { hex: '#F3E600' },
// Default fallback
diff --git a/src/data/bambuLabColorsComplete.ts b/src/data/bambuLabColorsComplete.ts
index d1e41ca..b992aaa 100644
--- a/src/data/bambuLabColorsComplete.ts
+++ b/src/data/bambuLabColorsComplete.ts
@@ -1,6 +1,12 @@
// Complete Bambu Lab color database with hex codes
-export const bambuLabColors = {
- // Basic Colors
+// Re-exports catalog-aligned color groupings
+
+import { BAMBU_LAB_CATALOG } from './bambuLabCatalog';
+
+// Flat hex lookup (for backwards compatibility)
+export const bambuLabColors: Record = {
+ // PLA Basic
+ "Jade White": "#FFFFFF",
"Black": "#000000",
"White": "#FFFFFF",
"Red": "#E53935",
@@ -8,39 +14,64 @@ export const bambuLabColors = {
"Green": "#43A047",
"Yellow": "#FDD835",
"Orange": "#FB8C00",
- "Purple": "#8E24AA",
- "Pink": "#EC407A",
- "Grey": "#757575",
- "Brown": "#6D4C41",
- "Light Blue": "#64B5F6",
- "Light Green": "#81C784",
- "Mint Green": "#4DB6AC",
- "Lime Green": "#C0CA33",
- "Sky Blue": "#81D4FA",
- "Navy Blue": "#283593",
- "Magenta": "#E91E63",
- "Violet": "#7B1FA2",
- "Beige": "#F5DEB3",
- "Ivory": "#FFFFF0",
-
- // Matte Colors
- "Matte Black": "#212121",
- "Matte White": "#FAFAFA",
- "Matte Red": "#C62828",
- "Matte Blue": "#1565C0",
- "Matte Green": "#2E7D32",
- "Matte Yellow": "#F9A825",
- "Matte Orange": "#EF6C00",
- "Matte Purple": "#6A1B9A",
- "Matte Pink": "#D81B60",
- "Matte Grey": "#616161",
- "Matte Brown": "#4E342E",
- "Matte Mint": "#26A69A",
- "Matte Lime": "#9E9D24",
- "Matte Navy": "#1A237E",
- "Matte Coral": "#FF5252",
+ "Purple": "#5E43B7",
+ "Pink": "#F55A74",
+ "Gray": "#8E9089",
+ "Brown": "#9D432C",
+ "Light Blue": "#61B0FF",
+ "Light Gray": "#D0D2D4",
+ "Sky Blue": "#73B2E5",
+ "Navy Blue": "#0C2340",
+ "Magenta": "#EC008C",
+ "Beige": "#F7E6DE",
+ "Bambu Green": "#00AE42",
+ "Scarlet Red": "#DE4343",
+ "Lemon Yellow": "#F7D959",
+ "Cyan": "#0086D6",
+ "Sakura Pink": "#E8AFCF",
+ "Cobalt Blue": "#0055B8",
+ "Mistletoe Green": "#3F8E43",
+ "Dark Red": "#BB3D43",
+ "Hot Pink": "#F5547D",
+ "Lavender": "#B5AAD5",
+ "Sunflower Yellow": "#FEC601",
+ "Pumpkin Orange": "#FF8E16",
+ "Lime": "#C5ED48",
+ "Blue Grey": "#5B6579",
+ "Gold": "#E4BD68",
+ "Bright Green": "#BDCF00",
+ "Maroon Red": "#0A2989",
+ "Turquoise": "#00B1B7",
+ "Bronze": "#847D48",
+ "Silver": "#A6A9AA",
+ "Dark Gray": "#555555",
- // Matte Colors - New 2025
+ // PLA Basic Gradient
+ "Neon City": "#0047BB",
+ "Midnight Blaze": "#0047BB",
+ "South Beach": "#468791",
+ "Arctic Whisper": "#ECF7F8",
+ "Cotton Candy Cloud": "#E9E2EC",
+ "Ocean to Meadow": "#A1E4CA",
+ "Solar Breeze": "#F3D9D5",
+ "Velvet Eclipse": "#000000",
+ "Dawn Radiance": "#C472A1",
+ "Dusk Glare": "#F6B790",
+ "Blueberry Bubblegum": "#BADCF4",
+ "Blue Hawaii": "#739FE6",
+ "Gilded Rose": "#ED982C",
+ "Pink Citrus": "#F8C4BC",
+ "Mint Lime": "#BAF382",
+
+ // PLA Matte
+ "Matte Ivory White": "#FFFFF0",
+ "Matte Charcoal": "#333333",
+ "Matte Scarlet Red": "#DE4343",
+ "Matte Marine Blue": "#0078BF",
+ "Matte Mandarin Orange": "#F99963",
+ "Matte Ash Gray": "#9B9EA0",
+ "Matte Desert Tan": "#E8DBB7",
+ "Matte Nardo Gray": "#747474",
"Matte Apple Green": "#C6E188",
"Matte Bone White": "#C8C5B6",
"Matte Caramel": "#A4845C",
@@ -49,7 +80,7 @@ export const bambuLabColors = {
"Matte Dark Chocolate": "#4A3729",
"Matte Dark Green": "#68724D",
"Matte Dark Red": "#BB3D43",
- "Matte Grass Green": "#61C680",
+ "Matte Grass Green": "#7CB342",
"Matte Ice Blue": "#A3D8E1",
"Matte Lemon Yellow": "#F7D959",
"Matte Lilac Purple": "#AE96D4",
@@ -58,132 +89,147 @@ export const bambuLabColors = {
"Matte Sky Blue": "#73B2E5",
"Matte Latte Brown": "#D3B7A7",
"Matte Terracotta": "#A25A37",
-
- // Silk Colors
- "Silk White": "#FEFEFE",
- "Silk Black": "#0A0A0A",
- "Silk Red": "#F44336",
- "Silk Blue": "#2196F3",
- "Silk Green": "#4CAF50",
- "Silk Gold": "#FFD54F",
- "Silk Silver": "#CFD8DC",
- "Silk Purple": "#9C27B0",
- "Silk Pink": "#F06292",
- "Silk Orange": "#FF9800",
- "Silk Bronze": "#A1887F",
- "Silk Copper": "#BF6F3F",
- "Silk Jade": "#00897B",
- "Silk Rose Gold": "#E8A09A",
- "Silk Pearl": "#F8F8FF",
- "Silk Ruby": "#E91E63",
- "Silk Sapphire": "#1976D2",
- "Silk Emerald": "#00695C",
- // Silk Multi-Color
+ // PLA Silk+
+ "Candy Green": "#408619",
+ "Candy Red": "#BB3A2E",
+ "Mint": "#A5DAB7",
+ "Titan Gray": "#606367",
+ "Rose Gold": "#B29593",
+ "Champagne": "#EBD0B1",
+ "Baby Blue": "#AEC3ED",
+
+ // PLA Silk Multi-Color
"Silk Aurora Purple": "#7F3696",
"Silk Phantom Blue": "#00629B",
"Silk Mystic Magenta": "#720062",
-
- // Metal Colors
- "Metal Grey": "#9E9E9E",
- "Metal Silver": "#B0BEC5",
- "Metal Gold": "#D4AF37",
- "Metal Copper": "#B87333",
- "Metal Bronze": "#CD7F32",
-
- // Sparkle Colors
- "Sparkle Red": "#EF5350",
- "Sparkle Blue": "#42A5F5",
- "Sparkle Green": "#66BB6A",
- "Sparkle Purple": "#AB47BC",
- "Sparkle Gold": "#FFCA28",
- "Sparkle Silver": "#E0E0E0",
-
- // Glow Colors
- "Glow in the Dark Green": "#C8E6C9",
- "Glow in the Dark Blue": "#BBDEFB",
-
- // Transparent Colors
- "Clear": "#FFFFFF",
- "Transparent Red": "#EF5350",
- "Transparent Blue": "#42A5F5",
- "Transparent Green": "#66BB6A",
- "Transparent Yellow": "#FFEE58",
- "Transparent Orange": "#FFA726",
- "Transparent Purple": "#AB47BC",
-
- // Support Materials
- "Natural": "#F5F5DC",
- "Support White": "#F5F5F5",
- "Support G": "#90CAF9",
- // Metal Colors (PLA)
+ // PLA Metal
"Iron Gray Metallic": "#6B6C6F",
+ "Iridium Gold Metallic": "#B39B84",
+ "Cobalt Blue Metallic": "#39699E",
+ "Copper Brown Metallic": "#AA6443",
+ "Oxide Green Metallic": "#1D7C6A",
- // ABS GF Colors
+ // PLA Sparkle
+ "Onyx Black Sparkle": "#2D2B28",
+ "Classic Gold Sparkle": "#E4BD68",
+ "Crimson Red Sparkle": "#792B36",
+ "Royal Purple Sparkle": "#483D8B",
+ "Slate Gray Sparkle": "#8E9089",
+ "Alpine Green Sparkle": "#3F5443",
+
+ // PLA Galaxy
+ "Nebulae": "#424379",
+
+ // PLA Marble
+ "White Marble": "#F7F3F0",
+ "Red Granite": "#AD4E38",
+
+ // PLA Glow
+ "Glow Blue": "#7AC0E9",
+ "Glow Green": "#A1FFAC",
+ "Glow Orange": "#FF9D5B",
+ "Glow Pink": "#F17B8F",
+ "Glow Yellow": "#F8FF80",
+
+ // PLA Wood
+ "Ochre Yellow": "#BC8B39",
+ "White Oak": "#D2CCA2",
+ "Clay Brown": "#8E621A",
+
+ // PLA CF
+ "Burgundy Red": "#951E23",
+ "Jeans Blue": "#6E88BC",
+ "Lava Gray": "#4D5054",
+ "Matcha Green": "#5C9748",
+ "Royal Blue": "#2842AD",
+ "Iris Purple": "#69398E",
+
+ // PETG HF
+ "Cream": "#F3E0B8",
+ "Forest Green": "#415520",
+ "Lake Blue": "#4672E4",
+ "Lime Green": "#8EE43D",
+ "Peanut Brown": "#7E5A1F",
+
+ // PETG Translucent
+ "Clear": "#FAFAFA",
+ "Translucent Gray": "#B8B8B8",
+ "Translucent Brown": "#C89A74",
+ "Translucent Purple": "#C5A8D8",
+ "Translucent Orange": "#FFB380",
+ "Translucent Olive": "#A4B885",
+ "Translucent Pink": "#F9B8D0",
+ "Translucent Light Blue": "#A8D8F0",
+ "Translucent Tea": "#D9C7A8",
+
+ // PETG CF
+ "Brick Red": "#9F332A",
+ "Indigo Blue": "#324585",
+ "Malachite Green": "#16B08E",
+ "Violet Purple": "#583061",
+
+ // ABS
+ "ABS Azure": "#489FDF",
+ "ABS Black": "#000000",
+ "ABS Blue": "#0A2989",
+ "ABS Olive": "#748C45",
+ "ABS Tangerine Yellow": "#FFC72C",
+ "ABS Navy Blue": "#0C2340",
+ "ABS Orange": "#FF6A13",
+ "ABS Bambu Green": "#00AE42",
+ "ABS Red": "#C12E1F",
+ "ABS White": "#FFFFFF",
+ "ABS Silver": "#A6A9AA",
"ABS GF Yellow": "#FDD835",
"ABS GF Orange": "#F48438",
- // TPU 95A HF Colors
+ // TPU
+ "Flesh": "#E8C4A2",
+ "Light Cyan": "#B9E3DF",
+ "Neon Orange": "#F68A1B",
+ "Blaze": "#E78390",
+ "Frozen": "#A6DEF3",
+ "Grape Jelly": "#6B2D75",
+ "Crystal Blue": "#5BC0EB",
+ "Quicksilver": "#A6A9AA",
+ "Cocoa Brown": "#6F5034",
"TPU 95A HF Yellow": "#F3E600",
- // Wood Colors
- "Ochre Yellow": "#BC8B39",
- "White Oak": "#D2CCA2",
- "Clay Brown": "#8E621A"
+ // PC
+ "Clear Black": "#5A5161",
+ "Transparent": "#FFFFFF",
};
-// Colors grouped by finish type for easier selection
-export const colorsByFinish = {
- "Basic": [
- "Black", "White", "Red", "Blue", "Green", "Yellow", "Orange",
- "Purple", "Pink", "Grey", "Brown", "Light Blue", "Light Green",
- "Mint Green", "Lime Green", "Sky Blue", "Navy Blue", "Magenta",
- "Violet", "Beige", "Ivory"
- ],
- "Matte": [
- "Matte Black", "Matte White", "Matte Red", "Matte Blue", "Matte Green",
- "Matte Yellow", "Matte Orange", "Matte Purple", "Matte Pink", "Matte Grey",
- "Matte Brown", "Matte Mint", "Matte Lime", "Matte Navy", "Matte Coral",
- "Matte Apple Green", "Matte Bone White", "Matte Caramel", "Matte Dark Blue",
- "Matte Dark Brown", "Matte Dark Chocolate", "Matte Dark Green", "Matte Dark Red",
- "Matte Grass Green", "Matte Ice Blue", "Matte Lemon Yellow", "Matte Lilac Purple",
- "Matte Latte Brown", "Matte Plum", "Matte Sakura Pink", "Matte Sky Blue", "Matte Terracotta"
- ],
- "Silk": [
- "Silk White", "Silk Black", "Silk Red", "Silk Blue", "Silk Green",
- "Silk Gold", "Silk Silver", "Silk Purple", "Silk Pink", "Silk Orange",
- "Silk Bronze", "Silk Copper", "Silk Jade", "Silk Rose Gold", "Silk Pearl",
- "Silk Ruby", "Silk Sapphire", "Silk Emerald"
- ],
- "Metal": [
- "Metal Grey", "Metal Silver", "Metal Gold", "Metal Copper", "Metal Bronze"
- ],
- "Sparkle": [
- "Sparkle Red", "Sparkle Blue", "Sparkle Green", "Sparkle Purple",
- "Sparkle Gold", "Sparkle Silver"
- ],
- "Glow": [
- "Glow in the Dark Green", "Glow in the Dark Blue"
- ],
- "Transparent": [
- "Clear", "Transparent Red", "Transparent Blue", "Transparent Green",
- "Transparent Yellow", "Transparent Orange", "Transparent Purple"
- ],
- "Support": [
- "Natural", "Support White", "Support G"
- ],
- "Wood": [
- "Ochre Yellow", "White Oak", "Clay Brown"
- ]
-};
+// Colors grouped by finish type — derived from catalog
+export const colorsByFinish: Record = {};
+
+// Build colorsByFinish from catalog
+for (const [, finishes] of Object.entries(BAMBU_LAB_CATALOG)) {
+ for (const [finishName, finishData] of Object.entries(finishes)) {
+ if (!colorsByFinish[finishName]) {
+ colorsByFinish[finishName] = [];
+ }
+ for (const color of finishData.colors) {
+ if (!colorsByFinish[finishName].includes(color.name)) {
+ colorsByFinish[finishName].push(color.name);
+ }
+ }
+ }
+}
+
+// Sort each finish's colors
+for (const finish of Object.keys(colorsByFinish)) {
+ colorsByFinish[finish].sort();
+}
// Function to get hex code for a color
export function getColorHex(colorName: string): string {
- return bambuLabColors[colorName as keyof typeof bambuLabColors] || "#000000";
+ return bambuLabColors[colorName] || "#000000";
}
// Function to get colors for a specific finish
export function getColorsForFinish(finish: string): string[] {
- return colorsByFinish[finish as keyof typeof colorsByFinish] || [];
-}
\ No newline at end of file
+ return colorsByFinish[finish] || [];
+}