1import { r as reactExports, j as jsxRuntimeExports } from "./vendor-react-Bay9Y7Lb.js"; 2import { a as api } from "./index-D0Bmh-UX.js"; 3import { D as DevDebugInfo } from "./AdminShared-P_hor2hS.js"; 4import { bf as Search, cL as RefreshCw, cc as Save, df as SlidersVertical, bH as Hash, cT as Type, ce as Plus, aW as X } from "./vendor-dVNaLJx_.js"; 5import "./vendor-i18n-R-bMoYp_.js"; 6const DEFAULT_CONFIG = { 7 provider: "database", 8 weights: { title: 2, artist: 1.5, tags: 1, description: 0.5 }, 9 boostedTerms: [], 10 ignoredTerms: [], 11 synonyms: {} 12}; 13const SearchSettings = ({ activeRole }) => { 14 const [config, setConfig] = reactExports.useState(DEFAULT_CONFIG); 15 const [loading, setLoading] = reactExports.useState(true); 16 const [saving, setSaving] = reactExports.useState(false); 17 const [newSynonym, setNewSynonym] = reactExports.useState({ key: "", value: "" }); 18 reactExports.useEffect(() => { 19 loadConfig(); 20 }, []); 21 const loadConfig = async () => { 22 setLoading(true); 23 const saved = await api.getSystemSetting("search_config"); 24 if (saved && saved.value) { 25 setConfig({ ...DEFAULT_CONFIG, ...saved.value }); 26 } 27 setLoading(false); 28 }; 29 const handleSave = async () => { 30 setSaving(true); 31 try { 32 await api.saveSystemSetting("search_config", config); 33 alert("Search configuration saved."); 34 } catch (e) { 35 alert("Failed to save configuration."); 36 } finally { 37 setSaving(false); 38 } 39 }; 40 const handleWeightChange = (field, value) => { 41 setConfig((prev) => ({ 42 ...prev, 43 weights: { ...prev.weights, [field]: value } 44 })); 45 }; 46 const handleArrayChange = (field, value) => { 47 setConfig((prev) => ({ 48 ...prev, 49 [field]: value.split(",").map((s) => s.trim()).filter(Boolean) 50 })); 51 }; 52 const addSynonym = () => { 53 if (newSynonym.key && newSynonym.value) { 54 setConfig((prev) => ({ 55 ...prev, 56 synonyms: { ...prev.synonyms, [newSynonym.key]: newSynonym.value } 57 })); 58 setNewSynonym({ key: "", value: "" }); 59 } 60 }; 61 const removeSynonym = (key) => { 62 const next = { ...config.synonyms }; 63 delete next[key]; 64 setConfig((prev) => ({ ...prev, synonyms: next })); 65 }; 66 if (loading) return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-10 text-center text-zinc-500", children: "Loading Configuration..." }); 67 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-6 max-w-4xl mx-auto animate-in fade-in", children: [ 68 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-xl p-6 shadow-sm", children: [
69 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between mb-6 pb-6 border-b border-zinc-100 dark:border-zinc-800", children: [ 70 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [ 71 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-12 h-12 bg-indigo-50 dark:bg-indigo-900/20 rounded-xl flex items-center justify-center text-indigo-600 dark:text-indigo-400", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Search, { size: 24 }) }), 72 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 73 /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-xl font-bold text-zinc-900 dark:text-white", children: "Search Intelligence" }), 74 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-zinc-500", children: "Configure search ranking, weights, and synonyms." }) 75 ] }) 76 ] }), 77 /* @__PURE__ */ jsxRuntimeExports.jsxs( 78 "button", 79 { 80 onClick: handleSave, 81 disabled: saving, 82 className: "px-6 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg font-bold text-sm flex items-center gap-2 shadow-lg shadow-indigo-500/20 disabled:opacity-50", 83 children: [
84 saving ? /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "animate-spin", size: 16 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Save, { size: 16 }), 85 "Save Changes" 86 ] 87 } 88 ) 89 ] }), 90 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-8", children: [ 91 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-6", children: [ 92 /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "font-bold text-sm text-zinc-900 dark:text-white flex items-center gap-2", children: [ 93 /* @__PURE__ */ jsxRuntimeExports.jsx(SlidersVertical, { size: 16, className: "text-indigo-500" }), 94 " Field Relevance Weights" 95 ] }), 96 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "bg-zinc-50 dark:bg-zinc-800/50 p-4 rounded-xl border border-zinc-200 dark:border-zinc-800 space-y-4", children: Object.keys(config.weights).map((field) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
97 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between text-xs font-bold text-zinc-600 dark:text-zinc-400 mb-2 uppercase", children: [ 98 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: field }), 99 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-indigo-600 dark:text-indigo-400", children: [ 100 config.weights[field], 101 "x" 102 ] }) 103 ] }), 104 /* @__PURE__ */ jsxRuntimeExports.jsx( 105 "input", 106 { 107 type: "range", 108 min: "0", 109 max: "10", 110 step: "0.1", 111 value: config.weights[field], 112 onChange: (e) => handleWeightChange(field, parseFloat(e.target.value)), 113 className: "w-full accent-indigo-600 h-2 bg-zinc-200 dark:bg-zinc-700 rounded-lg appearance-none cursor-pointer" 114 } 115 ) 116 ] }, field)) }) 117 ] }), 118 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-6", children: [
119 /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "font-bold text-sm text-zinc-900 dark:text-white flex items-center gap-2", children: [ 120 /* @__PURE__ */ jsxRuntimeExports.jsx(Hash, { size: 16, className: "text-emerald-500" }), 121 " Term Handling" 122 ] }), 123 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 124 /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-500 uppercase mb-2", children: "Boosted Terms (Higher Ranking)" }), 125 /* @__PURE__ */ jsxRuntimeExports.jsx( 126 "textarea", 127 { 128 value: config.boostedTerms.join(", "), 129 onChange: (e) => handleArrayChange("boostedTerms", e.target.value), 130 className: "w-full h-20 bg-white dark:bg-zinc-950 border border-zinc-200 dark:border-zinc-800 rounded-lg p-3 text-sm focus:border-indigo-500 outline-none resize-none", 131 placeholder: "e.g. exclusive, featured, premium" 132 } 133 ) 134 ] }),
135 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 136 /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-500 uppercase mb-2", children: "Ignored Terms (Stop Words)" }), 137 /* @__PURE__ */ jsxRuntimeExports.jsx( 138 "textarea", 139 { 140 value: config.ignoredTerms.join(", "), 141 onChange: (e) => handleArrayChange("ignoredTerms", e.target.value), 142 className: "w-full h-20 bg-white dark:bg-zinc-950 border border-zinc-200 dark:border-zinc-800 rounded-lg p-3 text-sm focus:border-indigo-500 outline-none resize-none", 143 placeholder: "e.g. the, a, an, midi" 144 } 145 ) 146 ] }) 147 ] }) 148 ] }), 149 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-8 pt-8 border-t border-zinc-100 dark:border-zinc-800", children: [ 150 /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "font-bold text-sm text-zinc-900 dark:text-white flex items-center gap-2 mb-4", children: [
151 /* @__PURE__ */ jsxRuntimeExports.jsx(Type, { size: 16, className: "text-amber-500" }), 152 " Synonym Dictionary" 153 ] }), 154 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-50 dark:bg-zinc-800/50 p-4 rounded-xl border border-zinc-200 dark:border-zinc-800", children: [ 155 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2 mb-4", children: [ 156 /* @__PURE__ */ jsxRuntimeExports.jsx( 157 "input", 158 { 159 value: newSynonym.key, 160 onChange: (e) => setNewSynonym({ ...newSynonym, key: e.target.value }), 161 placeholder: "Search Term (e.g. ff7)", 162 className: "flex-1 bg-white dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-700 rounded-lg px-3 py-2 text-sm outline-none focus:border-indigo-500" 163 } 164 ), 165 /* @__PURE__ */ jsxRuntimeExports.jsx( 166 "input", 167 { 168 value: newSynonym.value, 169 onChange: (e) => setNewSynonym({ ...newSynonym, value: e.target.value }), 170 placeholder: "Maps to (e.g. Final Fantasy VII)", 171 className: "flex-[2] bg-white dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-700 rounded-lg px-3 py-2 text-sm outline-none focus:border-indigo-500" 172 } 173 ),
174 /* @__PURE__ */ jsxRuntimeExports.jsx( 175 "button", 176 { 177 onClick: addSynonym, 178 disabled: !newSynonym.key || !newSynonym.value, 179 className: "px-4 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 disabled:opacity-50", 180 children: /* @__PURE__ */ jsxRuntimeExports.jsx(Plus, { size: 18 }) 181 } 182 ) 183 ] }), 184 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [ 185 Object.entries(config.synonyms).length === 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-center text-zinc-500 text-sm py-4", children: "No synonyms defined." }), 186 Object.entries(config.synonyms).map(([key, val]) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between p-3 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-700 rounded-lg group", children: [ 187 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 text-sm", children: [
188 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-bold text-indigo-600 dark:text-indigo-400", children: key }), 189 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-400", children: "â" }), 190 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-700 dark:text-zinc-300", children: val }) 191 ] }), 192 /* @__PURE__ */ jsxRuntimeExports.jsx( 193 "button", 194 { 195 onClick: () => removeSynonym(key), 196 className: "text-zinc-400 hover:text-red-500 opacity-0 group-hover:opacity-100 transition-opacity", 197 children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { size: 16 }) 198 } 199 ) 200 ] }, key)) 201 ] }) 202 ] }) 203 ] }) 204 ] }), 205 /* @__PURE__ */ jsxRuntimeExports.jsx(DevDebugInfo, { table: "SystemSetting", keyName: "search_config", value: config, isSuperAdmin: activeRole == null ? void 0 : activeRole.isSuperAdmin }) 206 ] }); 207}; 208export { 209 SearchSettings as default 210};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.