diff --git a/src/app/admin/classes/page.tsx b/src/app/admin/classes/page.tsx index 326bbec..f32c29f 100644 --- a/src/app/admin/classes/page.tsx +++ b/src/app/admin/classes/page.tsx @@ -1,13 +1,127 @@ "use client"; +import { useState, useRef, useEffect, useMemo } from "react"; import { SectionEditor } from "../_components/SectionEditor"; import { InputField, TextareaField } from "../_components/FormField"; import { ArrayEditor } from "../_components/ArrayEditor"; +import { icons, type LucideIcon } from "lucide-react"; -const ICON_OPTIONS = [ - "sparkles", "flame", "wind", "zap", "star", "monitor", - "heart", "music", "dumbbell", "trophy", -]; +// PascalCase "HeartPulse" → kebab "heart-pulse" +function toKebab(name: string) { + return name.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase(); +} + +// All icons as { key: kebab-name, Icon: component, label: PascalCase } +const ALL_ICONS = Object.entries(icons).map(([name, Icon]) => ({ + key: toKebab(name), + Icon: Icon as LucideIcon, + label: name, +})); + +const ICON_BY_KEY = Object.fromEntries(ALL_ICONS.map((i) => [i.key, i])); + +function IconPicker({ + value, + onChange, +}: { + value: string; + onChange: (v: string) => void; +}) { + const [open, setOpen] = useState(false); + const [search, setSearch] = useState(""); + const ref = useRef(null); + const inputRef = useRef(null); + const selected = ICON_BY_KEY[value]; + + useEffect(() => { + if (!open) return; + function handle(e: MouseEvent) { + if (ref.current && !ref.current.contains(e.target as Node)) { + setOpen(false); + setSearch(""); + } + } + document.addEventListener("mousedown", handle); + return () => document.removeEventListener("mousedown", handle); + }, [open]); + + const filtered = useMemo(() => { + if (!search) return ALL_ICONS.slice(0, 60); + const q = search.toLowerCase(); + return ALL_ICONS.filter((i) => i.label.toLowerCase().includes(q)).slice(0, 60); + }, [search]); + + const SelectedIcon = selected?.Icon; + + return ( +
+ + + + {open && ( +
+
+ setSearch(e.target.value)} + placeholder="Поиск иконки... (flame, heart, star...)" + className="w-full rounded-md border border-white/10 bg-neutral-900 px-3 py-1.5 text-sm text-white outline-none focus:border-gold/50 placeholder:text-neutral-600" + /> +
+
+ {filtered.length === 0 ? ( +
Ничего не найдено
+ ) : ( +
+ {filtered.map(({ key, Icon, label }) => ( + + ))} +
+ )} +
+
+ )} +
+ ); +} const COLOR_SWATCHES: { value: string; bg: string }[] = [ { value: "rose", bg: "bg-rose-500" }, @@ -63,24 +177,10 @@ export default function ClassesEditorPage() { value={item.name} onChange={(v) => updateItem({ ...item, name: v })} /> -
- - -
+ updateItem({ ...item, icon: v })} + />