PageSourceSearch

https://www.hamienet.com/assets/CreditManager-wYOeDl1g.js

js hamienet.com collected 2026-09-24 19:00:28 UTC 27,352 bytes, 412 lines download raw bytes

1import { r as reactExports, j as jsxRuntimeExports } from "./vendor-react-Bay9Y7Lb.js";
2import { a as api } from "./index-D0Bmh-UX.js";
3import { a as getAllActionCosts, c as creditRules } from "./credit-rules-8PgauDOD.js";
4import { D as DevDebugInfo } from "./AdminShared-P_hor2hS.js";
5import { a$ as Shield, bQ as ExternalLink, b5 as Trash2, bn as Globe, ce as Plus, cL as RefreshCw, cc as Save } from "./vendor-dVNaLJx_.js";
6import "./vendor-i18n-R-bMoYp_.js";
7const CATEGORY_SHORTCUTS = {
8  "Downloads": "/",
9  "MIDI Conversion": "/converter",
10  "Studio & Processing": "/audio-digitizer"
11};
12const CreditManager = ({ activeRole }) => {
13  var _a, _b;
14  const [config, setConfig] = reactExports.useState(null);
15  const [filterConfig, setFilterConfig] = reactExports.useState(null);
16  const [activeTab, setActiveTab] = reactExports.useState("credits");
17  const [activeCategory, setActiveCategory] = reactExports.useState("");
18  const [loading, setLoading] = reactExports.useState(true);
19  const [saving, setSaving] = reactExports.useState(false);
20  reactExports.useEffect(() => {
21    Promise.all([loadConfig(), loadFilterConfig()]);
22  }, []);
23  const loadFilterConfig = async () => {
24    const saved = await api.getSystemSetting("content_filter_config");
25    if (saved) setFilterConfig(saved.value);
26  };
27  const loadConfig = async () => {
28    setLoading(true);
29    const saved = await api.getSystemSetting("credit_config");
30    if (saved && saved.value) {
31      let currentConfig = { ...saved.value };
32      if (!Array.isArray(currentConfig.allotments)) {
33        currentConfig.allotments = [];
34      }
35      if (!currentConfig.allotments.some((a) => a.id === "guest")) {
36        const guestTier = {
37          id: "guest",
38          name: "Guest (Unregistered)",
39          base: 15,
40          trustTiers: []
41        };
42        currentConfig.allotments = [guestTier, ...currentConfig.allotments];
43        console.log("[CreditManager] Auto-repaired missing Guest tier.");
44      }
45      setConfig(currentConfig);
46    }
47    setLoading(false);
48  };
49  const handleSave = async () => {
50    setSaving(true);
51    try {
52      await Promise.all([
53        api.saveSystemSetting("credit_config", config),
54        api.saveSystemSetting("content_filter_config", filterConfig)
55      ]);
56      alert("Configuration saved successfully.");
57    } catch (e) {
58      alert("Failed to save configuration.");
59    } finally {
60      setSaving(false);
61    }
62  };
63  const updateTierField = (tierIndex, field, value) => {
64    setConfig((prev) => {
65      const newAllotments = [...prev.allotments];
66      newAllotments[tierIndex] = { ...newAllotments[tierIndex], [field]: value };
67      return { ...prev, allotments: newAllotments };
68    });
69  };
70  const updateBonusTierField = (tierIndex, bonusIndex, field, value) => {
71    setConfig((prev) => {
72      const newAllotments = [...prev.allotments];
73      const newTrustTiers = [...newAllotments[tierIndex].trustTiers];
74      newTrustTiers[bonusIndex] = { ...newTrustTiers[bonusIndex], [field]: value };
75      newAllotments[tierIndex] = { ...newAllotments[tierIndex], trustTiers: newTrustTiers };
76      return { ...prev, allotments: newAllotments };
77    });
78  };
79  const handleAddLevel = () => {
80    const newLevel = {
81      id: `new-level-${Date.now()}`,
82      name: "New Level",
83      base: 0,
84      trustTiers: []
85    };
86    setConfig((prev) => ({ ...prev, allotments: [...prev.allotments, newLevel] }));
87  };
88  const handleRemoveLevel = (index) => {
89    if (!confirm("Are you sure you want to remove this entire user level?")) return;
90    setConfig((prev) => ({ ...prev, allotments: prev.allotments.filter((_, i) => i !== index) }));
91  };
92  const handleAddBonusTier = (tierIndex) => {
93    const newTrustTiers = [...config.allotments[tierIndex].trustTiers, { score: 0, credits: 0 }];
94    updateTierField(tierIndex, "trustTiers", newTrustTiers);
95  };
96  const handleRemoveBonusTier = (tierIndex, bonusIndex) => {
97    if (!confirm("Remove this bonus tier?")) return;
98    const newTrustTiers = config.allotments[tierIndex].trustTiers.filter((_, i) => i !== bonusIndex);
99    updateTierField(tierIndex, "trustTiers", newTrustTiers);
100  };
101  const handleInitialize = async () => {
102    const defaultCosts = {};
103    Object.entries(creditRules.actions).forEach(([key, action]) => {
104      defaultCosts[key] = action.defaultCost;
105    });
106    const defaultConfig = {
107      costs: defaultCosts,
108      allotments: [
109        {
110          id: "guest",
111          name: "Guest (Unregistered)",
112          base: 15,
113          trustTiers: []
114        },
115        {
116          id: "free",
117          name: "Free Membership",
118          base: 25,
119          trustTiers: [
120            { score: 50, credits: 25 },
121            { score: 90, credits: 50 }
122          ]
123        },
124        {
125          id: "gold",
126          name: "Gold Supporter",
127          base: 250,
128          trustTiers: []
129        },
130        {
131          id: "premium",
132          name: "Premium Supporter",
133          base: 1e3,
134          trustTiers: []
135        },
136        {
137          id: "admin",
138          name: "Administrator",
139          base: 9999,
140          trustTiers: []
141        }
142      ]
143    };
144    setConfig(defaultConfig);
145  };
146  if (loading) return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-10 text-center text-zinc-500", children: "Loading Configuration..." });
147  if (!config) return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-10 text-center space-y-4", children: [
148    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-red-500 font-bold", children: "Error: Credit Configuration not found in database." }),
149    /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-500 text-sm", children: "The credit system has not been initialized yet." }),
150    /* @__PURE__ */ jsxRuntimeExports.jsx(
151      "button",
152      {
153        onClick: handleInitialize,
154        className: "px-6 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg font-bold text-sm shadow-lg shadow-indigo-500/20 transition-all",
155        children: "Initialize Default Settings"
156      }
157    )
158  ] });
159  const allCosts = getAllActionCosts(config);
160  const categories = Array.from(new Set(allCosts.map((a) => a.category)));
161  if (categories.length > 0 && !activeCategory) {
162    setActiveCategory(categories[0]);
163  }
164  const currentShortcut = CATEGORY_SHORTCUTS[activeCategory];
165  const activeCategoryCosts = allCosts.filter((action) => action.category === activeCategory);
166  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-6 max-w-5xl mx-auto animate-in fade-in", children: [
167    /* @__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: [
168      /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center justify-between mb-6 pb-6 border-b border-zinc-100 dark:border-zinc-800", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [
169        /* @__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(Shield, { size: 24 }) }),
170        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
171          /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-xl font-bold text-zinc-900 dark:text-white", children: "Usage & Quotas" }),
172          /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-zinc-500", children: "Control download limits, credit allotments, and content usage policies." })
173        ] })
174      ] }) }),
175      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border-b border-zinc-200 dark:border-zinc-800 flex items-center gap-6 mb-6", children: [
176        /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => setActiveTab("credits"), className: `pb-2 text-sm font-bold border-b-2 transition-all ${activeTab === "credits" ? "border-indigo-500 text-indigo-600 dark:text-indigo-400" : "border-transparent text-zinc-500"}`, children: "Allotments & Costs" }),
177        /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => setActiveTab("policy"), className: `pb-2 text-sm font-bold border-b-2 transition-all ${activeTab === "policy" ? "border-indigo-500 text-indigo-600 dark:text-indigo-400" : "border-transparent text-zinc-500"}`, children: "Content Usage Policy" })
178      ] }),
179      activeTab === "credits" ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
180        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mb-6", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 bg-zinc-50 dark:bg-zinc-800/50 rounded-xl border border-zinc-200 dark:border-zinc-800", children: [
181          /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "text-sm font-bold text-zinc-700 dark:text-zinc-300 mb-3 flex items-center justify-between", children: [
182            /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Action Costs" }),
183            currentShortcut && /* @__PURE__ */ jsxRuntimeExports.jsxs(
184              "a",
185              {
186                href: currentShortcut,
187                target: "_blank",
188                rel: "noopener noreferrer",
189                className: "text-xs text-indigo-600 dark:text-indigo-400 hover:underline flex items-center gap-1 font-semibold",
190                children: [
191                  "Launch App ",
192                  /* @__PURE__ */ jsxRuntimeExports.jsx(ExternalLink, { size: 12 })
193                ]
194              }
195            )
196          ] }),
197          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-4", children: [
198            /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-1/3 flex flex-col gap-1 border-r border-zinc-200 dark:border-zinc-800 pr-3", children: categories.map((cat) => /* @__PURE__ */ jsxRuntimeExports.jsx(
199              "button",
200              {
201                type: "button",
202                onClick: () => setActiveCategory(cat),
203                className: `w-full text-left px-3 py-1.5 rounded-lg text-xs font-semibold transition-all ${activeCategory === cat ? "bg-indigo-500 text-white shadow-sm" : "text-zinc-650 hover:bg-zinc-100 dark:text-zinc-400 dark:hover:bg-zinc-800/60"}`,
204                children: cat
205              },
206              cat
207            )) }),
208            /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 space-y-2 max-h-[250px] overflow-y-auto pr-1", children: activeCategoryCosts.map((action) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between gap-4 py-1 border-b border-zinc-150 dark:border-zinc-850/60 last:border-0", children: [
209              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0", children: [
210                /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs font-semibold text-zinc-755 dark:text-zinc-250 truncate", children: action.label }),
211                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[9px] font-mono text-zinc-450", children: action.key })
212              ] }),
213              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5 flex-shrink-0", children: [
214                /* @__PURE__ */ jsxRuntimeExports.jsx(
215                  "input",
216                  {
217                    type: "number",
218                    value: action.currentCost,
219                    onChange: (e) => setConfig((prev) => ({
220                      ...prev,
221                      costs: { ...(prev == null ? void 0 : prev.costs) || {}, [action.key]: Number(e.target.value) }
222                    })),
223                    className: "w-16 bg-white dark:bg-zinc-900 border border-zinc-300 dark:border-zinc-700 rounded-md py-1 px-1.5 text-xs outline-none focus:ring-1 focus:ring-indigo-500 text-zinc-900 dark:text-white text-right font-medium"
224                  }
225                ),
226                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-zinc-400 dark:text-zinc-500", children: "cr" })
227              ] })
228            ] }, action.key)) })
229          ] })
230        ] }) }),
231        /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-lg font-bold text-zinc-900 dark:text-white border-t border-zinc-200 dark:border-zinc-800 pt-6 mb-4", children: "User Level Allotments" }),
232        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-6", children: config.allotments.map((tier, tierIndex) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 bg-white dark:bg-zinc-950 rounded-lg border border-zinc-200 dark:border-zinc-800 space-y-4 relative", children: [
233          tier.id !== "guest" && /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => handleRemoveLevel(tierIndex), className: "absolute top-3 right-3 p-1.5 text-red-500 hover:bg-red-100 dark:hover:bg-red-900/30 rounded-full", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Trash2, { size: 14 }) }),
234          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-4", children: [
235            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
236              /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-xs font-bold text-zinc-500", children: "Level ID (Key)" }),
237              /* @__PURE__ */ jsxRuntimeExports.jsx(
238                "input",
239                {
240                  type: "text",
241                  value: tier.id,
242                  onChange: (e) => updateTierField(tierIndex, "id", e.target.value.toLowerCase().replace(/\s+/g, "-")),
243                  readOnly: !tier.id.startsWith("new-level"),
244                  className: "w-full mt-1 bg-zinc-100 dark:bg-zinc-800 border border-zinc-300 dark:border-zinc-700 rounded-md p-2 text-sm font-mono text-zinc-900 dark:text-white outline-none focus:ring-2 focus:ring-indigo-500 read-only:opacity-60"
245                }
246              )
247            ] }),
248            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
249              /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-xs font-bold text-zinc-500", children: "Level Name (Default)" }),
250              /* @__PURE__ */ jsxRuntimeExports.jsx(
251                "input",
252                {
253                  type: "text",
254                  value: tier.name,
255                  onChange: (e) => updateTierField(tierIndex, "name", e.target.value),
256                  className: "w-full mt-1 bg-zinc-100 dark:bg-zinc-800 border border-zinc-300 dark:border-zinc-700 rounded-md p-2 text-sm text-zinc-900 dark:text-white outline-none focus:ring-2 focus:ring-indigo-500"
257                }
258              )
259            ] }),
260            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
261              /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-xs font-bold text-zinc-500", children: tier.id === "guest" ? "Max Daily Downloads (IP-based)" : "Base Credits" }),
262              /* @__PURE__ */ jsxRuntimeExports.jsx(
263                "input",
264                {
265                  type: "number",
266                  value: tier.base,
267                  onChange: (e) => updateTierField(tierIndex, "base", Number(e.target.value)),
268                  className: "w-full mt-1 bg-zinc-100 dark:bg-zinc-800 border border-zinc-300 dark:border-zinc-700 rounded-md p-2 text-sm text-zinc-900 dark:text-white outline-none focus:ring-2 focus:ring-indigo-500"
269                }
270              )
271            ] })
272          ] }),
273          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-2 bg-blue-50 dark:bg-blue-900/10 rounded-lg text-xs text-blue-700 dark:text-blue-300 flex items-center gap-2 mt-2", children: [
274            /* @__PURE__ */ jsxRuntimeExports.jsx(Globe, { size: 14 }),
275            /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
276              "Translations for Level Names are managed in the ",
277              /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Translation Manager" }),
278              " using the key: ",
279              /* @__PURE__ */ jsxRuntimeExports.jsxs("code", { className: "font-mono bg-blue-100 dark:bg-blue-900/20 px-1 rounded", children: [
280                "creditTiers.",
281                tier.id
282              ] })
283            ] })
284          ] }),
285          tier.id !== "guest" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
286            /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "text-xs font-bold text-zinc-500 flex items-center gap-1 mb-2", children: [
287              /* @__PURE__ */ jsxRuntimeExports.jsx(Shield, { size: 12 }),
288              " Trust Score Bonuses"
289            ] }),
290            /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-2", children: tier.trustTiers.map((bonus, bonusIndex) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-sm bg-zinc-50 dark:bg-zinc-900 p-2 rounded", children: [
291              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-700 dark:text-zinc-300", children: "If score ≥" }),
292              /* @__PURE__ */ jsxRuntimeExports.jsx("input", { type: "number", value: bonus.score, onChange: (e) => updateBonusTierField(tierIndex, bonusIndex, "score", Number(e.target.value)), className: "w-16 p-1 rounded border border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-800 text-center text-zinc-900 dark:text-white outline-none focus:ring-1 focus:ring-indigo-500" }),
293              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-700 dark:text-zinc-300", children: ", grant" }),
294              /* @__PURE__ */ jsxRuntimeExports.jsx("input", { type: "number", value: bonus.credits, onChange: (e) => updateBonusTierField(tierIndex, bonusIndex, "credits", Number(e.target.value)), className: "w-20 p-1 rounded border border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-800 text-center text-zinc-900 dark:text-white outline-none focus:ring-1 focus:ring-indigo-500" }),
295              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-700 dark:text-zinc-300", children: "credits." }),
296              /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => handleRemoveBonusTier(tierIndex, bonusIndex), className: "p-1 text-red-500 hover:bg-red-100 dark:hover:bg-red-900/20 rounded-full transition-colors ml-auto", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Trash2, { size: 14 }) })
297            ] }, bonusIndex)) }),
298            /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { onClick: () => handleAddBonusTier(tierIndex), className: "mt-3 flex items-center gap-1 text-xs font-bold text-indigo-600 hover:text-indigo-500 transition-colors", children: [
299              /* @__PURE__ */ jsxRuntimeExports.jsx(Plus, { size: 12 }),
300              " Add Bonus Tier"
301            ] })
302          ] })
303        ] }, tier.id)) }),
304        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-6 border-t border-zinc-200 dark:border-zinc-800 pt-6", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { onClick: handleAddLevel, className: "w-full flex items-center justify-center gap-2 py-3 border-2 border-dashed border-zinc-300 dark:border-zinc-700 rounded-lg text-sm font-bold text-zinc-500 hover:border-indigo-500 hover:text-indigo-500 transition-colors", children: [
305          /* @__PURE__ */ jsxRuntimeExports.jsx(Plus, { size: 16 }),
306          " Add New User Level"
307        ] }) })
308      ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-6", children: [
309        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: [
310          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between p-4 bg-zinc-50 dark:bg-zinc-800/50 rounded-lg border border-zinc-200 dark:border-zinc-800", children: [
311            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
312              /* @__PURE__ */ jsxRuntimeExports.jsx("h4", { className: "text-sm font-bold text-zinc-900 dark:text-white", children: "Strict Enforcement" }),
313              /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-500", children: "Block uploads containing prohibited content." })
314            ] }),
315            /* @__PURE__ */ jsxRuntimeExports.jsx(
316              "button",
317              {
318                onClick: () => setFilterConfig({ ...filterConfig, strictMode: !filterConfig.strictMode }),
319                className: `w-10 h-5 rounded-full transition-colors relative ${filterConfig.strictMode ? "bg-indigo-600" : "bg-zinc-300 dark:bg-zinc-700"}`,
320                children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `absolute top-0.5 w-4 h-4 bg-white rounded-full transition-all ${filterConfig.strictMode ? "left-5.5" : "left-0.5"}` })
321              }
322            )
323          ] }),
324          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between p-4 bg-zinc-50 dark:bg-zinc-800/50 rounded-lg border border-zinc-200 dark:border-zinc-800", children: [
325            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
326              /* @__PURE__ */ jsxRuntimeExports.jsx("h4", { className: "text-sm font-bold text-zinc-900 dark:text-white", children: "Auto-Strip HTML" }),
327              /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-500", children: "Remove HTML tags from all text fields." })
328            ] }),
329            /* @__PURE__ */ jsxRuntimeExports.jsx(
330              "button",
331              {
332                onClick: () => setFilterConfig({ ...filterConfig, stripHtml: !filterConfig.stripHtml }),
333                className: `w-10 h-5 rounded-full transition-colors relative ${filterConfig.stripHtml ? "bg-indigo-600" : "bg-zinc-300 dark:bg-zinc-700"}`,
334                children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `absolute top-0.5 w-4 h-4 bg-white rounded-full transition-all ${filterConfig.stripHtml ? "left-5.5" : "left-0.5"}` })
335              }
336            )
337          ] })
338        ] }),
339        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-6", children: [
340          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
341            /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block font-bold text-sm text-zinc-900 dark:text-white mb-2", children: "Prohibited Words" }),
342            /* @__PURE__ */ jsxRuntimeExports.jsx(
343              "textarea",
344              {
345                className: "w-full h-48 bg-zinc-50 dark:bg-zinc-900 border border-zinc-300 dark:border-zinc-700 rounded-lg p-3 text-sm text-zinc-800 dark:text-zinc-200 font-mono",
346                value: Array.isArray(filterConfig.badWords) ? filterConfig.badWords.join(", ") : "",
347                onChange: (e) => setFilterConfig({ ...filterConfig, badWords: e.target.value.split(",").map((s) => s.trim()).filter(Boolean) })
348              }
349            )
350          ] }),
351          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
352            /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block font-bold text-sm text-zinc-900 dark:text-white mb-2", children: "Forbidden Domains" }),
353            /* @__PURE__ */ jsxRuntimeExports.jsx(
354              "textarea",
355              {
356                className: "w-full h-48 bg-zinc-50 dark:bg-zinc-900 border border-zinc-300 dark:border-zinc-700 rounded-lg p-3 text-sm text-zinc-800 dark:text-zinc-200 font-mono",
357                value: Array.isArray(filterConfig.forbiddenDomains) ? filterConfig.forbiddenDomains.join("\n") : "",
358                onChange: (e) => setFilterConfig({ ...filterConfig, forbiddenDomains: e.target.value.split("\n").map((s) => s.trim()).filter(Boolean) })
359              }
360            )
361          ] })
362        ] }),
363        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 bg-zinc-50 dark:bg-zinc-800/50 rounded-lg border border-zinc-200 dark:border-zinc-800 space-y-4", children: [
364          /* @__PURE__ */ jsxRuntimeExports.jsx("h4", { className: "text-sm font-bold text-zinc-900 dark:text-white", children: "Guest Upload Restrictions" }),
365          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
366            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
367              /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-[10px] font-bold text-zinc-500 uppercase", children: "Max Uploads / 24h" }),
368              /* @__PURE__ */ jsxRuntimeExports.jsx(
369                "input",
370                {
371                  type: "number",
372                  className: "w-full mt-1 bg-white dark:bg-zinc-900 border border-zinc-300 dark:border-zinc-700 rounded-md p-2 text-sm",
373                  value: ((_a = filterConfig == null ? void 0 : filterConfig.guestLimits) == null ? void 0 : _a.maxUploadsPerDay) || 3,
374                  onChange: (e) => setFilterConfig({ ...filterConfig, guestLimits: { ...filterConfig.guestLimits, maxUploadsPerDay: parseInt(e.target.value) } })
375                }
376              )
377            ] }),
378            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
379              /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-[10px] font-bold text-zinc-500 uppercase", children: "Max File Size (KB)" }),
380              /* @__PURE__ */ jsxRuntimeExports.jsx(
381                "input",
382                {
383                  type: "number",
384                  className: "w-full mt-1 bg-white dark:bg-zinc-900 border border-zinc-300 dark:border-zinc-700 rounded-md p-2 text-sm",
385                  value: ((_b = filterConfig == null ? void 0 : filterConfig.guestLimits) == null ? void 0 : _b.maxFileSizeKB) || 500,
386                  onChange: (e) => setFilterConfig({ ...filterConfig, guestLimits: { ...filterConfig.guestLimits, maxFileSizeKB: parseInt(e.target.value) } })
387                }
388              )
389            ] })
390          ] })
391        ] })
392      ] }),
393      /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-8 flex justify-end", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
394        "button",
395        {
396          onClick: handleSave,
397          disabled: saving,
398          className: "px-8 py-3 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg font-bold shadow-lg shadow-indigo-500/20 disabled:opacity-50 transition-all flex items-center gap-2",
399          children: [
400            saving ? /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "animate-spin", size: 18 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Save, { size: 18 }),
401            "Save All Changes"
402          ]
403        }
404      ) })
405    ] }),
406    /* @__PURE__ */ jsxRuntimeExports.jsx(DevDebugInfo, { table: "SystemSetting", keyName: "credit_config", value: config, isSuperAdmin: activeRole == null ? void 0 : activeRole.isSuperAdmin }),
407    /* @__PURE__ */ jsxRuntimeExports.jsx(DevDebugInfo, { table: "SystemSetting", keyName: "content_filter_config", value: filterConfig, isSuperAdmin: activeRole == null ? void 0 : activeRole.isSuperAdmin })
408  ] });
409};
410export {
411  CreditManager as default
412};

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.