- Rename categoryEmoji to categoryIcon in ItemCard, CandidateCard, ThreadCard, ItemPicker - Import and render LucideIcon at appropriate sizes (36px placeholder, 14-16px badges) - Update hook interfaces to match server API (categoryIcon instead of categoryEmoji) - Rename iconData.ts to iconData.tsx (contains JSX) - Update useCategories mutation type to use icon instead of emoji Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
250 lines
9.8 KiB
TypeScript
250 lines
9.8 KiB
TypeScript
import { icons } from "lucide-react";
|
|
|
|
// --- Emoji to Lucide icon mapping (for migration/fallback) ---
|
|
|
|
export const EMOJI_TO_ICON_MAP: Record<string, string> = {
|
|
"\u{1F4E6}": "package",
|
|
"\u{1F3D5}\uFE0F": "tent",
|
|
"\u{26FA}": "tent",
|
|
"\u{1F6B2}": "bike",
|
|
"\u{1F4F7}": "camera",
|
|
"\u{1F392}": "backpack",
|
|
"\u{1F455}": "shirt",
|
|
"\u{1F527}": "wrench",
|
|
"\u{1F373}": "cooking-pot",
|
|
"\u{1F3AE}": "gamepad-2",
|
|
"\u{1F4BB}": "laptop",
|
|
"\u{1F3D4}\uFE0F": "mountain-snow",
|
|
"\u{26F0}\uFE0F": "mountain",
|
|
"\u{1F3D6}\uFE0F": "umbrella-off",
|
|
"\u{1F9ED}": "compass",
|
|
"\u{1F526}": "flashlight",
|
|
"\u{1F50B}": "battery",
|
|
"\u{1F4F1}": "smartphone",
|
|
"\u{1F3A7}": "headphones",
|
|
"\u{1F9E4}": "hand",
|
|
"\u{1F9E3}": "scarf",
|
|
"\u{1F45F}": "footprints",
|
|
"\u{1F97E}": "footprints",
|
|
"\u{1F9E2}": "hard-hat",
|
|
"\u{1F576}\uFE0F": "glasses",
|
|
"\u{1F52A}": "pocket-knife",
|
|
"\u{1FA93}": "axe",
|
|
"\u{1F4A1}": "lightbulb",
|
|
"\u{2699}\uFE0F": "cog",
|
|
"\u{1F3C6}": "trophy",
|
|
"\u{1F3AF}": "target",
|
|
"\u{2728}": "sparkles",
|
|
};
|
|
|
|
// --- Icon groups for the picker ---
|
|
|
|
export interface IconEntry {
|
|
name: string;
|
|
keywords: string[];
|
|
}
|
|
|
|
export interface IconGroup {
|
|
name: string;
|
|
icon: string;
|
|
icons: IconEntry[];
|
|
}
|
|
|
|
export const iconGroups: IconGroup[] = [
|
|
{
|
|
name: "Outdoor",
|
|
icon: "tent",
|
|
icons: [
|
|
{ name: "tent", keywords: ["camp", "shelter", "outdoor"] },
|
|
{ name: "campfire", keywords: ["fire", "camp", "warmth"] },
|
|
{ name: "mountain", keywords: ["peak", "hike", "climb"] },
|
|
{ name: "mountain-snow", keywords: ["peak", "snow", "alpine", "winter"] },
|
|
{ name: "compass", keywords: ["navigate", "direction", "orientation"] },
|
|
{ name: "map", keywords: ["navigate", "trail", "route"] },
|
|
{ name: "map-pin", keywords: ["location", "waypoint", "marker"] },
|
|
{ name: "binoculars", keywords: ["view", "watch", "birding", "optics"] },
|
|
{ name: "tree-pine", keywords: ["forest", "nature", "woods"] },
|
|
{ name: "trees", keywords: ["forest", "nature", "woods"] },
|
|
{ name: "sun", keywords: ["weather", "bright", "day"] },
|
|
{ name: "cloud-rain", keywords: ["weather", "rain", "storm"] },
|
|
{ name: "snowflake", keywords: ["winter", "cold", "snow"] },
|
|
{ name: "wind", keywords: ["weather", "breeze", "gust"] },
|
|
{ name: "flame", keywords: ["fire", "heat", "burn"] },
|
|
{ name: "leaf", keywords: ["nature", "plant", "green"] },
|
|
{ name: "flower-2", keywords: ["nature", "plant", "bloom"] },
|
|
{ name: "sunrise", keywords: ["morning", "dawn", "sky"] },
|
|
{ name: "sunset", keywords: ["evening", "dusk", "sky"] },
|
|
{ name: "moon", keywords: ["night", "lunar", "dark"] },
|
|
{ name: "star", keywords: ["night", "sky", "favorite"] },
|
|
{ name: "thermometer", keywords: ["temperature", "weather", "heat"] },
|
|
],
|
|
},
|
|
{
|
|
name: "Travel",
|
|
icon: "backpack",
|
|
icons: [
|
|
{ name: "backpack", keywords: ["bag", "pack", "hike", "carry"] },
|
|
{ name: "luggage", keywords: ["bag", "suitcase", "travel"] },
|
|
{ name: "plane", keywords: ["flight", "air", "travel"] },
|
|
{ name: "car", keywords: ["drive", "vehicle", "road"] },
|
|
{ name: "bike", keywords: ["cycle", "bicycle", "ride"] },
|
|
{ name: "ship", keywords: ["boat", "water", "sail"] },
|
|
{ name: "train-front", keywords: ["rail", "transit", "transport"] },
|
|
{ name: "map-pinned", keywords: ["location", "destination", "pinned"] },
|
|
{ name: "globe", keywords: ["world", "earth", "international"] },
|
|
{ name: "ticket", keywords: ["pass", "admission", "entry"] },
|
|
{ name: "route", keywords: ["path", "trail", "direction"] },
|
|
{ name: "navigation", keywords: ["direction", "arrow", "gps"] },
|
|
{ name: "milestone", keywords: ["marker", "progress", "distance"] },
|
|
{ name: "fuel", keywords: ["gas", "petrol", "energy"] },
|
|
{ name: "parking-meter", keywords: ["park", "meter", "time"] },
|
|
],
|
|
},
|
|
{
|
|
name: "Sports",
|
|
icon: "dumbbell",
|
|
icons: [
|
|
{ name: "dumbbell", keywords: ["gym", "weight", "exercise", "fitness"] },
|
|
{ name: "trophy", keywords: ["win", "award", "competition"] },
|
|
{ name: "medal", keywords: ["award", "prize", "achievement"] },
|
|
{ name: "timer", keywords: ["stopwatch", "time", "race"] },
|
|
{ name: "heart-pulse", keywords: ["health", "cardio", "fitness"] },
|
|
{ name: "footprints", keywords: ["walk", "hike", "track", "shoes"] },
|
|
{ name: "gauge", keywords: ["speed", "meter", "performance"] },
|
|
{ name: "target", keywords: ["aim", "goal", "archery"] },
|
|
{ name: "flag", keywords: ["finish", "race", "mark"] },
|
|
{ name: "swords", keywords: ["fight", "fencing", "combat"] },
|
|
{ name: "shield", keywords: ["protect", "defense", "guard"] },
|
|
{ name: "zap", keywords: ["energy", "power", "fast", "lightning"] },
|
|
],
|
|
},
|
|
{
|
|
name: "Electronics",
|
|
icon: "laptop",
|
|
icons: [
|
|
{ name: "laptop", keywords: ["computer", "notebook", "pc"] },
|
|
{ name: "smartphone", keywords: ["phone", "mobile", "cell"] },
|
|
{ name: "tablet-smartphone", keywords: ["tablet", "device", "screen"] },
|
|
{ name: "headphones", keywords: ["audio", "music", "listen"] },
|
|
{ name: "camera", keywords: ["photo", "picture", "lens"] },
|
|
{ name: "battery", keywords: ["power", "charge", "energy"] },
|
|
{ name: "bluetooth", keywords: ["wireless", "connect", "pair"] },
|
|
{ name: "wifi", keywords: ["internet", "wireless", "connect"] },
|
|
{ name: "usb", keywords: ["cable", "connect", "port"] },
|
|
{ name: "monitor", keywords: ["screen", "display", "desktop"] },
|
|
{ name: "keyboard", keywords: ["type", "input", "keys"] },
|
|
{ name: "mouse", keywords: ["click", "pointer", "input"] },
|
|
{ name: "gamepad-2", keywords: ["game", "controller", "play", "sim"] },
|
|
{ name: "speaker", keywords: ["audio", "sound", "music"] },
|
|
{ name: "radio", keywords: ["communication", "broadcast", "signal"] },
|
|
{ name: "tv", keywords: ["television", "screen", "monitor"] },
|
|
{ name: "plug", keywords: ["power", "electric", "connect"] },
|
|
{ name: "cable", keywords: ["wire", "connect", "cord"] },
|
|
{ name: "cpu", keywords: ["processor", "chip", "computing"] },
|
|
{ name: "hard-drive", keywords: ["storage", "disk", "data"] },
|
|
],
|
|
},
|
|
{
|
|
name: "Clothing",
|
|
icon: "shirt",
|
|
icons: [
|
|
{ name: "shirt", keywords: ["clothing", "top", "apparel"] },
|
|
{ name: "glasses", keywords: ["eyewear", "sunglasses", "vision"] },
|
|
{ name: "watch", keywords: ["time", "wrist", "accessory"] },
|
|
{ name: "gem", keywords: ["jewelry", "precious", "accessory"] },
|
|
{ name: "scissors", keywords: ["cut", "tailor", "craft"] },
|
|
{ name: "ruler", keywords: ["measure", "length", "size"] },
|
|
{ name: "palette", keywords: ["color", "art", "design"] },
|
|
],
|
|
},
|
|
{
|
|
name: "Cooking",
|
|
icon: "cooking-pot",
|
|
icons: [
|
|
{ name: "cooking-pot", keywords: ["pot", "cook", "kitchen", "stove"] },
|
|
{ name: "utensils", keywords: ["fork", "knife", "eating", "cutlery"] },
|
|
{ name: "cup-soda", keywords: ["drink", "beverage", "cup"] },
|
|
{ name: "coffee", keywords: ["drink", "hot", "brew", "mug"] },
|
|
{ name: "beef", keywords: ["meat", "food", "protein"] },
|
|
{ name: "fish", keywords: ["seafood", "food", "protein"] },
|
|
{ name: "apple", keywords: ["fruit", "food", "snack"] },
|
|
{ name: "wheat", keywords: ["grain", "food", "bread"] },
|
|
{ name: "flame-kindling", keywords: ["fire", "stove", "cook"] },
|
|
{ name: "refrigerator", keywords: ["cold", "store", "cool"] },
|
|
{ name: "microwave", keywords: ["heat", "cook", "kitchen"] },
|
|
],
|
|
},
|
|
{
|
|
name: "Tools",
|
|
icon: "wrench",
|
|
icons: [
|
|
{ name: "wrench", keywords: ["fix", "repair", "tool"] },
|
|
{ name: "hammer", keywords: ["build", "nail", "tool"] },
|
|
{ name: "screwdriver", keywords: ["fix", "screw", "tool"] },
|
|
{ name: "drill", keywords: ["bore", "hole", "tool"] },
|
|
{ name: "ruler", keywords: ["measure", "length", "tool"] },
|
|
{ name: "flashlight", keywords: ["light", "torch", "dark"] },
|
|
{ name: "pocket-knife", keywords: ["blade", "cut", "multi-tool"] },
|
|
{ name: "axe", keywords: ["chop", "wood", "hatchet"] },
|
|
{ name: "shovel", keywords: ["dig", "earth", "garden"] },
|
|
{ name: "paintbrush", keywords: ["paint", "art", "coat"] },
|
|
{ name: "scissors", keywords: ["cut", "trim", "snip"] },
|
|
{ name: "cog", keywords: ["gear", "settings", "mechanical"] },
|
|
{ name: "nut", keywords: ["bolt", "fastener", "hardware"] },
|
|
],
|
|
},
|
|
{
|
|
name: "General",
|
|
icon: "package",
|
|
icons: [
|
|
{ name: "package", keywords: ["box", "parcel", "container", "default"] },
|
|
{ name: "box", keywords: ["container", "storage", "crate"] },
|
|
{ name: "tag", keywords: ["label", "price", "mark"] },
|
|
{ name: "bookmark", keywords: ["save", "favorite", "mark"] },
|
|
{ name: "archive", keywords: ["store", "old", "save"] },
|
|
{ name: "folder", keywords: ["organize", "file", "directory"] },
|
|
{ name: "grid-3x3", keywords: ["grid", "layout", "table"] },
|
|
{ name: "list", keywords: ["items", "bullet", "todo"] },
|
|
{ name: "layers", keywords: ["stack", "overlap", "level"] },
|
|
{ name: "circle-dot", keywords: ["dot", "center", "radio"] },
|
|
{ name: "square", keywords: ["shape", "box", "block"] },
|
|
{ name: "hexagon", keywords: ["shape", "six", "polygon"] },
|
|
{ name: "triangle", keywords: ["shape", "warning", "three"] },
|
|
{ name: "heart", keywords: ["love", "favorite", "like"] },
|
|
{ name: "star", keywords: ["favorite", "rate", "highlight"] },
|
|
{ name: "plus", keywords: ["add", "new", "create"] },
|
|
{ name: "check", keywords: ["done", "complete", "yes"] },
|
|
{ name: "x", keywords: ["close", "remove", "delete"] },
|
|
],
|
|
},
|
|
];
|
|
|
|
// --- LucideIcon render component ---
|
|
|
|
function toPascalCase(str: string): string {
|
|
return str
|
|
.split("-")
|
|
.map((s) => s.charAt(0).toUpperCase() + s.slice(1))
|
|
.join("");
|
|
}
|
|
|
|
interface LucideIconProps {
|
|
name: string;
|
|
size?: number;
|
|
className?: string;
|
|
}
|
|
|
|
export function LucideIcon({
|
|
name,
|
|
size = 20,
|
|
className = "",
|
|
}: LucideIconProps) {
|
|
const pascalName = toPascalCase(name);
|
|
const IconComponent = icons[pascalName as keyof typeof icons];
|
|
if (!IconComponent) {
|
|
const FallbackIcon = icons.Package;
|
|
return <FallbackIcon size={size} className={className} />;
|
|
}
|
|
return <IconComponent size={size} className={className} />;
|
|
}
|