Initial Filamenteka setup - Bambu Lab filament tracker
- React + TypeScript frontend with automatic color coding - Confluence API integration for data sync - AWS Amplify deployment with Terraform - Support for all Bambu Lab filament colors including gradients
This commit is contained in:
25
src/components/ColorCell.tsx
Normal file
25
src/components/ColorCell.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import React from 'react';
|
||||
import { getFilamentColor, getColorStyle, getContrastColor } from '../data/bambuLabColors';
|
||||
|
||||
interface ColorCellProps {
|
||||
colorName: string;
|
||||
}
|
||||
|
||||
export const ColorCell: React.FC<ColorCellProps> = ({ colorName }) => {
|
||||
const colorMapping = getFilamentColor(colorName);
|
||||
const style = getColorStyle(colorMapping);
|
||||
const textColor = Array.isArray(colorMapping.hex)
|
||||
? '#000000'
|
||||
: getContrastColor(colorMapping.hex);
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<div
|
||||
className="w-6 h-6 rounded border border-gray-300"
|
||||
style={style}
|
||||
title={Array.isArray(colorMapping.hex) ? colorMapping.hex.join(' - ') : colorMapping.hex}
|
||||
/>
|
||||
<span style={{ color: textColor }}>{colorName}</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
147
src/components/FilamentTable.tsx
Normal file
147
src/components/FilamentTable.tsx
Normal file
@@ -0,0 +1,147 @@
|
||||
import React, { useState, useMemo } from 'react';
|
||||
import { Filament } from '../types/filament';
|
||||
import { ColorCell } from './ColorCell';
|
||||
import { getFilamentColor, getColorStyle } from '../data/bambuLabColors';
|
||||
|
||||
interface FilamentTableProps {
|
||||
filaments: Filament[];
|
||||
loading?: boolean;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export const FilamentTable: React.FC<FilamentTableProps> = ({ filaments, loading, error }) => {
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [sortField, setSortField] = useState<keyof Filament>('boja');
|
||||
const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('asc');
|
||||
|
||||
const filteredAndSortedFilaments = useMemo(() => {
|
||||
let filtered = filaments.filter(filament =>
|
||||
Object.values(filament).some(value =>
|
||||
value.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
)
|
||||
);
|
||||
|
||||
filtered.sort((a, b) => {
|
||||
const aValue = a[sortField];
|
||||
const bValue = b[sortField];
|
||||
|
||||
if (sortOrder === 'asc') {
|
||||
return aValue.localeCompare(bValue);
|
||||
} else {
|
||||
return bValue.localeCompare(aValue);
|
||||
}
|
||||
});
|
||||
|
||||
return filtered;
|
||||
}, [filaments, searchTerm, sortField, sortOrder]);
|
||||
|
||||
const handleSort = (field: keyof Filament) => {
|
||||
if (sortField === field) {
|
||||
setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc');
|
||||
} else {
|
||||
setSortField(field);
|
||||
setSortOrder('asc');
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex justify-center items-center h-64">
|
||||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-gray-900"></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded">
|
||||
Error: {error}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div className="mb-4">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search filaments..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="overflow-x-auto">
|
||||
<table className="min-w-full bg-white border border-gray-300">
|
||||
<thead>
|
||||
<tr className="bg-gray-100">
|
||||
<th
|
||||
className="px-4 py-2 border-b cursor-pointer hover:bg-gray-200"
|
||||
onClick={() => handleSort('brand')}
|
||||
>
|
||||
Brand {sortField === 'brand' && (sortOrder === 'asc' ? '↑' : '↓')}
|
||||
</th>
|
||||
<th
|
||||
className="px-4 py-2 border-b cursor-pointer hover:bg-gray-200"
|
||||
onClick={() => handleSort('tip')}
|
||||
>
|
||||
Tip {sortField === 'tip' && (sortOrder === 'asc' ? '↑' : '↓')}
|
||||
</th>
|
||||
<th
|
||||
className="px-4 py-2 border-b cursor-pointer hover:bg-gray-200"
|
||||
onClick={() => handleSort('finish')}
|
||||
>
|
||||
Finish {sortField === 'finish' && (sortOrder === 'asc' ? '↑' : '↓')}
|
||||
</th>
|
||||
<th
|
||||
className="px-4 py-2 border-b cursor-pointer hover:bg-gray-200"
|
||||
onClick={() => handleSort('boja')}
|
||||
>
|
||||
Boja {sortField === 'boja' && (sortOrder === 'asc' ? '↑' : '↓')}
|
||||
</th>
|
||||
<th className="px-4 py-2 border-b">Refill</th>
|
||||
<th className="px-4 py-2 border-b">Vakum</th>
|
||||
<th className="px-4 py-2 border-b">Otvoreno</th>
|
||||
<th className="px-4 py-2 border-b">Količina</th>
|
||||
<th className="px-4 py-2 border-b">Cena</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{filteredAndSortedFilaments.map((filament, index) => {
|
||||
const colorMapping = getFilamentColor(filament.boja);
|
||||
const rowStyle = getColorStyle(colorMapping);
|
||||
|
||||
return (
|
||||
<tr
|
||||
key={index}
|
||||
className="hover:opacity-90 transition-opacity"
|
||||
style={{
|
||||
...rowStyle,
|
||||
opacity: 0.8
|
||||
}}
|
||||
>
|
||||
<td className="px-4 py-2 border-b">{filament.brand}</td>
|
||||
<td className="px-4 py-2 border-b">{filament.tip}</td>
|
||||
<td className="px-4 py-2 border-b">{filament.finish}</td>
|
||||
<td className="px-4 py-2 border-b">
|
||||
<ColorCell colorName={filament.boja} />
|
||||
</td>
|
||||
<td className="px-4 py-2 border-b">{filament.refill}</td>
|
||||
<td className="px-4 py-2 border-b">{filament.vakum}</td>
|
||||
<td className="px-4 py-2 border-b">{filament.otvoreno}</td>
|
||||
<td className="px-4 py-2 border-b">{filament.kolicina}</td>
|
||||
<td className="px-4 py-2 border-b">{filament.cena}</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 text-sm text-gray-600">
|
||||
Showing {filteredAndSortedFilaments.length} of {filaments.length} filaments
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user