1import { r as reactExports, j as jsxRuntimeExports } from "./vendor-react-Bay9Y7Lb.js"; 2import { a as api } from "./index-w5lXls15.js"; 3import { D as DevDebugInfo } from "./AdminShared-BP_vxG_A.js"; 4import { bj as CreditCard, cM as RefreshCw, cd as Save, fg as ToggleRight, fh as ToggleLeft, cF as Key, bz as EyeOff, bA as Eye, bq as Coins, bi as Sparkles } from "./vendor-D0P4XgNj.js"; 5import "./vendor-i18n-BC7ZMXWQ.js"; 6const SubscriptionSettings = ({ activeRole }) => { 7 const [config, setConfig] = reactExports.useState({ 8 enabled: false, 9 stripeSecretKey: "", 10 stripeWebhookSecret: "", 11 goldPriceIdMonthly: "", 12 goldPriceIdAnnual: "", 13 goldCreditsFixed: 250, 14 premiumPriceIdMonthly: "", 15 premiumPriceIdAnnual: "", 16 premiumCreditsFixed: 1e3, 17 priceIdCreditPack: "", 18 creditPackPrice: 4.99, 19 creditPackSize: 500 20 }); 21 const [loading, setLoading] = reactExports.useState(true); 22 const [saving, setSaving] = reactExports.useState(false); 23 const [showSecretKey, setShowSecretKey] = reactExports.useState(false); 24 const [showWebhookSecret, setShowWebhookSecret] = reactExports.useState(false); 25 reactExports.useEffect(() => { 26 loadConfig(); 27 }, []); 28 const loadConfig = async () => { 29 setLoading(true); 30 try { 31 const saved = await api.getSystemSetting("subscription_config"); 32 if (saved && saved.value) { 33 const parsed = typeof saved.value === "string" ? JSON.parse(saved.value) : saved.value; 34 setConfig((prev) => ({ 35 ...prev, 36 ...parsed 37 })); 38 } 39 } catch (e) { 40 console.error("[SubSettings] Failed to load config:", e); 41 } finally { 42 setLoading(false); 43 } 44 }; 45 const handleSave = async () => { 46 setSaving(true); 47 try { 48 await api.saveSystemSetting("subscription_config", config); 49 alert("Supporter Membership Configuration saved successfully."); 50 } catch (e) { 51 alert("Failed to save configuration: " + (e instanceof Error ? e.message : String(e))); 52 } finally { 53 setSaving(false); 54 } 55 }; 56 const handleChange = (field, value) => { 57 setConfig((prev) => ({ 58 ...prev, 59 ...field.endsWith("Fixed") || field === "creditPackSize" ? { [field]: parseInt(value) || 0 } : field === "creditPackPrice" ? { [field]: parseFloat(value) || 0 } : { [field]: value } 60 })); 61 }; 62 if (loading) { 63 return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-8 text-center text-zinc-500 font-mono animate-pulse", children: "Loading Membership Engine Config..." }); 64 } 65 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-6 max-w-4xl animate-in fade-in duration-300 pb-16", children: [ 66 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center bg-zinc-900 border border-zinc-800 p-6 rounded-2xl shadow-sm shrink-0", children: [
67 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1", children: [ 68 /* @__PURE__ */ jsxRuntimeExports.jsxs("h2", { className: "text-xl font-bold text-white flex items-center gap-2", children: [ 69 /* @__PURE__ */ jsxRuntimeExports.jsx(CreditCard, { className: "text-indigo-500" }), 70 " Supporter Membership Settings" 71 ] }), 72 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-400", children: "Configure Stripe integrations, supporter tiers (Gold & Premium), daily credits, and global pre-launch switches." }) 73 ] }), 74 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [ 75 /* @__PURE__ */ jsxRuntimeExports.jsx( 76 "button", 77 { 78 onClick: loadConfig, 79 className: "p-2 bg-zinc-800 hover:bg-zinc-700 text-zinc-400 hover:text-white rounded-lg transition-colors border-0", 80 title: "Reload configuration", 81 children: /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { size: 16 }) 82 } 83 ),
84 /* @__PURE__ */ jsxRuntimeExports.jsxs( 85 "button", 86 { 87 onClick: handleSave, 88 disabled: saving, 89 className: "flex items-center gap-2 px-4 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg text-sm font-bold shadow-md hover:shadow-indigo-500/20 active:scale-95 transition-all border-0 cursor-pointer", 90 children: [ 91 saving ? /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { size: 16, className: "animate-spin" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Save, { size: 16 }), 92 "Save Settings" 93 ] 94 } 95 ) 96 ] }) 97 ] }), 98 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 p-6 rounded-2xl shadow-sm flex items-center justify-between", children: [ 99 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1 flex-1 pr-6", children: [ 100 /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "text-md font-bold text-zinc-900 dark:text-white flex items-center gap-1.5", children: [ 101 "Membership System Launch State",
102 !config.enabled && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[9px] font-bold bg-amber-500/10 text-amber-500 px-1.5 py-0.5 rounded uppercase", children: "Pre-Launch (OFF)" }), 103 config.enabled && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[9px] font-bold bg-emerald-500/10 text-emerald-500 px-1.5 py-0.5 rounded uppercase", children: "Live (ON)" }) 104 ] }), 105 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-500 dark:text-zinc-400 leading-relaxed", children: "When disabled, checkout routes will return 403 and the public Billing page will show a pre-launch overlay. Turn this ON once you configure the Stripe keys and Price IDs below." }) 106 ] }), 107 /* @__PURE__ */ jsxRuntimeExports.jsx( 108 "button", 109 { 110 onClick: () => handleChange("enabled", !config.enabled), 111 className: "border-0 bg-transparent cursor-pointer text-indigo-600 dark:text-indigo-400 p-1 hover:scale-105 transition-transform", 112 children: config.enabled ? /* @__PURE__ */ jsxRuntimeExports.jsx(ToggleRight, { className: "w-14 h-10 text-indigo-500" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ToggleLeft, { className: "w-14 h-10 text-zinc-400 dark:text-zinc-600" }) 113 } 114 ) 115 ] }), 116 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-6", children: [
117 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 p-6 rounded-2xl shadow-sm space-y-4", children: [ 118 /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "font-bold text-zinc-900 dark:text-white text-sm flex items-center gap-2 border-b border-zinc-100 dark:border-zinc-800 pb-3", children: [ 119 /* @__PURE__ */ jsxRuntimeExports.jsx(Key, { size: 16, className: "text-indigo-500" }), 120 " Stripe API Keys" 121 ] }), 122 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1.5", children: [ 123 /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-xs font-bold text-zinc-500 dark:text-zinc-400", children: "Stripe Secret Key (sk_test_...)" }), 124 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative", children: [ 125 /* @__PURE__ */ jsxRuntimeExports.jsx( 126 "input", 127 { 128 type: showSecretKey ? "text" : "password", 129 value: config.stripeSecretKey, 130 onChange: (e) => handleChange("stripeSecretKey", e.target.value), 131 className: "w-full text-sm rounded-lg border border-zinc-200 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-950 p-2.5 pr-10 text-zinc-800 dark:text-zinc-100 focus:outline-none focus:border-indigo-500", 132 placeholder: "sk_live_..." 133 } 134 ),
135 /* @__PURE__ */ jsxRuntimeExports.jsx( 136 "button", 137 { 138 type: "button", 139 onClick: () => setShowSecretKey(!showSecretKey), 140 className: "absolute right-3 top-1/2 -translate-y-1/2 text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-200 bg-transparent border-0 cursor-pointer", 141 children: showSecretKey ? /* @__PURE__ */ jsxRuntimeExports.jsx(EyeOff, { size: 16 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Eye, { size: 16 }) 142 } 143 ) 144 ] }) 145 ] }), 146 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1.5", children: [ 147 /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-xs font-bold text-zinc-500 dark:text-zinc-400", children: "Stripe Webhook Secret (whsec_...)" }), 148 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative", children: [ 149 /* @__PURE__ */ jsxRuntimeExports.jsx( 150 "input", 151 { 152 type: showWebhookSecret ? "text" : "password", 153 value: config.stripeWebhookSecret, 154 onChange: (e) => handleChange("stripeWebhookSecret", e.target.value), 155 className: "w-full text-sm rounded-lg border border-zinc-200 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-950 p-2.5 pr-10 text-zinc-800 dark:text-zinc-100 focus:outline-none focus:border-indigo-500", 156 placeholder: "whsec_..." 157 } 158 ),
159 /* @__PURE__ */ jsxRuntimeExports.jsx( 160 "button", 161 { 162 type: "button", 163 onClick: () => setShowWebhookSecret(!showWebhookSecret), 164 className: "absolute right-3 top-1/2 -translate-y-1/2 text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-200 bg-transparent border-0 cursor-pointer", 165 children: showWebhookSecret ? /* @__PURE__ */ jsxRuntimeExports.jsx(EyeOff, { size: 16 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Eye, { size: 16 }) 166 } 167 ) 168 ] }) 169 ] }), 170 /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-[10px] text-zinc-400 leading-relaxed", children: [ 171 "To receive live payment callbacks, configure Stripe Webhooks pointing to ", 172 /* @__PURE__ */ jsxRuntimeExports.jsx("code", { children: "https://yourdomain.com/api/payments/webhook" }), 173 " and subscribe to ", 174 /* @__PURE__ */ jsxRuntimeExports.jsx("code", { children: "checkout.session.completed" }), 175 ", ",
176 /* @__PURE__ */ jsxRuntimeExports.jsx("code", { children: "customer.subscription.updated" }), 177 ", and ", 178 /* @__PURE__ */ jsxRuntimeExports.jsx("code", { children: "customer.subscription.deleted" }), 179 " events." 180 ] }) 181 ] }), 182 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 p-6 rounded-2xl shadow-sm space-y-4", children: [ 183 /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "font-bold text-zinc-900 dark:text-white text-sm flex items-center gap-2 border-b border-zinc-100 dark:border-zinc-800 pb-3", children: [ 184 /* @__PURE__ */ jsxRuntimeExports.jsx(Coins, { size: 16, className: "text-amber-500" }), 185 " Credit Allowances & Packs" 186 ] }), 187 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-2 gap-4", children: [ 188 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1.5", children: [ 189 /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-xs font-bold text-zinc-500 dark:text-zinc-400", children: "Gold Daily Credits" }),
190 /* @__PURE__ */ jsxRuntimeExports.jsx( 191 "input", 192 { 193 type: "number", 194 value: config.goldCreditsFixed, 195 onChange: (e) => handleChange("goldCreditsFixed", e.target.value), 196 className: "w-full text-sm rounded-lg border border-zinc-200 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-950 p-2.5 text-zinc-800 dark:text-zinc-100 focus:outline-none focus:border-indigo-500" 197 } 198 ) 199 ] }), 200 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1.5", children: [ 201 /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-xs font-bold text-zinc-500 dark:text-zinc-400", children: "Premium Daily Credits" }), 202 /* @__PURE__ */ jsxRuntimeExports.jsx( 203 "input", 204 { 205 type: "number", 206 value: config.premiumCreditsFixed, 207 onChange: (e) => handleChange("premiumCreditsFixed", e.target.value), 208 className: "w-full text-sm rounded-lg border border-zinc-200 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-950 p-2.5 text-zinc-800 dark:text-zinc-100 focus:outline-none focus:border-indigo-500" 209 } 210 ) 211 ] }) 212 ] }),
213 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border-t border-zinc-100 dark:border-zinc-800/80 pt-4 grid grid-cols-2 gap-4", children: [ 214 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1.5", children: [ 215 /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-xs font-bold text-zinc-500 dark:text-zinc-400", children: "Credit Pack Size" }), 216 /* @__PURE__ */ jsxRuntimeExports.jsx( 217 "input", 218 { 219 type: "number", 220 value: config.creditPackSize, 221 onChange: (e) => handleChange("creditPackSize", e.target.value), 222 className: "w-full text-sm rounded-lg border border-zinc-200 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-950 p-2.5 text-zinc-800 dark:text-zinc-100 focus:outline-none focus:border-indigo-500" 223 } 224 ) 225 ] }), 226 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1.5", children: [ 227 /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-xs font-bold text-zinc-500 dark:text-zinc-400", children: "Credit Pack Price ($)" }),
228 /* @__PURE__ */ jsxRuntimeExports.jsx( 229 "input", 230 { 231 type: "number", 232 step: "0.01", 233 value: config.creditPackPrice, 234 onChange: (e) => handleChange("creditPackPrice", e.target.value), 235 className: "w-full text-sm rounded-lg border border-zinc-200 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-950 p-2.5 text-zinc-800 dark:text-zinc-100 focus:outline-none focus:border-indigo-500" 236 } 237 ) 238 ] }) 239 ] }) 240 ] }) 241 ] }), 242 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 p-6 rounded-2xl shadow-sm space-y-6", children: [ 243 /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "font-bold text-zinc-900 dark:text-white text-sm flex items-center gap-2 border-b border-zinc-100 dark:border-zinc-800 pb-3", children: [ 244 /* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { size: 16, className: "text-indigo-500" }), 245 " Stripe Price IDs Mapping" 246 ] }),
247 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [ 248 /* @__PURE__ */ jsxRuntimeExports.jsx("h4", { className: "text-xs font-black text-amber-600 dark:text-amber-400 uppercase tracking-wider", children: "Gold Supporter Pricing" }), 249 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: [ 250 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1.5", children: [ 251 /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-xs font-bold text-zinc-500 dark:text-zinc-400", children: "Gold Monthly Price ID (price_...)" }), 252 /* @__PURE__ */ jsxRuntimeExports.jsx( 253 "input", 254 { 255 type: "text", 256 value: config.goldPriceIdMonthly, 257 onChange: (e) => handleChange("goldPriceIdMonthly", e.target.value), 258 className: "w-full text-sm rounded-lg border border-zinc-200 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-950 p-2.5 text-zinc-800 dark:text-zinc-100 focus:outline-none focus:border-indigo-500 font-mono", 259 placeholder: "price_1O..." 260 } 261 ) 262 ] }),
263 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1.5", children: [ 264 /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-xs font-bold text-zinc-500 dark:text-zinc-400", children: "Gold Annual Price ID (price_...)" }), 265 /* @__PURE__ */ jsxRuntimeExports.jsx( 266 "input", 267 { 268 type: "text", 269 value: config.goldPriceIdAnnual, 270 onChange: (e) => handleChange("goldPriceIdAnnual", e.target.value), 271 className: "w-full text-sm rounded-lg border border-zinc-200 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-950 p-2.5 text-zinc-800 dark:text-zinc-100 focus:outline-none focus:border-indigo-500 font-mono", 272 placeholder: "price_1O..." 273 } 274 ) 275 ] }) 276 ] }) 277 ] }), 278 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4 border-t border-zinc-100 dark:border-zinc-800/80 pt-4", children: [ 279 /* @__PURE__ */ jsxRuntimeExports.jsx("h4", { className: "text-xs font-black text-indigo-600 dark:text-indigo-400 uppercase tracking-wider", children: "Premium Supporter Pricing" }),
280 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: [ 281 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1.5", children: [ 282 /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-xs font-bold text-zinc-500 dark:text-zinc-400", children: "Premium Monthly Price ID (price_...)" }), 283 /* @__PURE__ */ jsxRuntimeExports.jsx( 284 "input", 285 { 286 type: "text", 287 value: config.premiumPriceIdMonthly, 288 onChange: (e) => handleChange("premiumPriceIdMonthly", e.target.value), 289 className: "w-full text-sm rounded-lg border border-zinc-200 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-950 p-2.5 text-zinc-800 dark:text-zinc-100 focus:outline-none focus:border-indigo-500 font-mono", 290 placeholder: "price_1O..." 291 } 292 ) 293 ] }), 294 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1.5", children: [
295 /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-xs font-bold text-zinc-500 dark:text-zinc-400", children: "Premium Annual Price ID (price_...)" }), 296 /* @__PURE__ */ jsxRuntimeExports.jsx( 297 "input", 298 { 299 type: "text", 300 value: config.premiumPriceIdAnnual, 301 onChange: (e) => handleChange("premiumPriceIdAnnual", e.target.value), 302 className: "w-full text-sm rounded-lg border border-zinc-200 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-950 p-2.5 text-zinc-800 dark:text-zinc-100 focus:outline-none focus:border-indigo-500 font-mono", 303 placeholder: "price_1O..." 304 } 305 ) 306 ] }) 307 ] }) 308 ] }), 309 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4 border-t border-zinc-100 dark:border-zinc-800/80 pt-4", children: [ 310 /* @__PURE__ */ jsxRuntimeExports.jsx("h4", { className: "text-xs font-black text-emerald-600 dark:text-emerald-400 uppercase tracking-wider", children: "Credit Pack Pricing" }),
311 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1.5", children: [ 312 /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-xs font-bold text-zinc-500 dark:text-zinc-400", children: "Credit Pack Price ID (price_...)" }), 313 /* @__PURE__ */ jsxRuntimeExports.jsx( 314 "input", 315 { 316 type: "text", 317 value: config.priceIdCreditPack, 318 onChange: (e) => handleChange("priceIdCreditPack", e.target.value), 319 className: "w-full text-sm rounded-lg border border-zinc-200 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-950 p-2.5 text-zinc-800 dark:text-zinc-100 focus:outline-none focus:border-indigo-500 font-mono", 320 placeholder: "price_1O..." 321 } 322 ) 323 ] }) }) 324 ] }) 325 ] }), 326 /* @__PURE__ */ jsxRuntimeExports.jsx(DevDebugInfo, { table: "SystemSetting", keyName: "subscription_config", value: config, isSuperAdmin: activeRole == null ? void 0 : activeRole.isSuperAdmin }) 327 ] }); 328}; 329export { 330 SubscriptionSettings as default 331};
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.