@ezcoder.dev/sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/analytics/index.d.ts +18 -0
  2. package/dist/analytics/index.js +76 -0
  3. package/dist/analytics/index.js.map +1 -0
  4. package/dist/animation/index.d.ts +172 -0
  5. package/dist/animation/index.js +81 -0
  6. package/dist/animation/index.js.map +1 -0
  7. package/dist/auth/index.d.ts +80 -0
  8. package/dist/auth/index.js +463 -0
  9. package/dist/auth/index.js.map +1 -0
  10. package/dist/chunk-5XIZHBKE.js +372 -0
  11. package/dist/chunk-5XIZHBKE.js.map +1 -0
  12. package/dist/chunk-G7XDUN3Z.js +141 -0
  13. package/dist/chunk-G7XDUN3Z.js.map +1 -0
  14. package/dist/chunk-YNDCD53D.js +212 -0
  15. package/dist/chunk-YNDCD53D.js.map +1 -0
  16. package/dist/cms/index.d.ts +44 -0
  17. package/dist/cms/index.js +106 -0
  18. package/dist/cms/index.js.map +1 -0
  19. package/dist/errors/index.d.ts +20 -0
  20. package/dist/errors/index.js +61 -0
  21. package/dist/errors/index.js.map +1 -0
  22. package/dist/index.d.ts +30 -0
  23. package/dist/index.js +21 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/notifications/index.d.ts +30 -0
  26. package/dist/notifications/index.js +191 -0
  27. package/dist/notifications/index.js.map +1 -0
  28. package/dist/payments/index.d.ts +89 -0
  29. package/dist/payments/index.js +408 -0
  30. package/dist/payments/index.js.map +1 -0
  31. package/dist/roles/index.d.ts +37 -0
  32. package/dist/roles/index.js +120 -0
  33. package/dist/roles/index.js.map +1 -0
  34. package/dist/seo/index.d.ts +39 -0
  35. package/dist/seo/index.js +89 -0
  36. package/dist/seo/index.js.map +1 -0
  37. package/dist/server/index.d.ts +72 -0
  38. package/dist/server/index.js +191 -0
  39. package/dist/server/index.js.map +1 -0
  40. package/dist/storage/index.d.ts +52 -0
  41. package/dist/storage/index.js +212 -0
  42. package/dist/storage/index.js.map +1 -0
  43. package/dist/types-DtY5lp3P.d.ts +90 -0
  44. package/package.json +105 -0
@@ -0,0 +1,408 @@
1
+ import {
2
+ AuthContext
3
+ } from "../chunk-YNDCD53D.js";
4
+ import {
5
+ ezcoder
6
+ } from "../chunk-5XIZHBKE.js";
7
+ import {
8
+ features,
9
+ supabase
10
+ } from "../chunk-G7XDUN3Z.js";
11
+
12
+ // src/payments/useSubscription.ts
13
+ import { useState, useEffect, useCallback, useContext } from "react";
14
+ var TIER_LEVELS = {
15
+ free: 0,
16
+ starter: 1,
17
+ creator: 2,
18
+ pro: 3,
19
+ business: 4,
20
+ enterprise: 5
21
+ };
22
+ function useSubscription() {
23
+ const auth = useContext(AuthContext);
24
+ const [subscription, setSubscription] = useState(null);
25
+ const [loading, setLoading] = useState(true);
26
+ const fetchSubscription = useCallback(async () => {
27
+ if (!auth?.profile) {
28
+ setSubscription(null);
29
+ setLoading(false);
30
+ return;
31
+ }
32
+ const sub = {
33
+ customerId: auth.profile.stripe_customer_id || null,
34
+ tier: auth.profile.subscription_tier || "free",
35
+ status: auth.profile.subscription_status || null,
36
+ currentPeriodEnd: auth.profile.subscription_period_end || null
37
+ };
38
+ setSubscription(sub);
39
+ setLoading(false);
40
+ }, [auth?.profile]);
41
+ useEffect(() => {
42
+ fetchSubscription();
43
+ }, [fetchSubscription]);
44
+ useEffect(() => {
45
+ if (!features.auth || !auth?.user?.id) return;
46
+ const channel = supabase.channel(`subscription_${auth.user.id}`).on(
47
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
48
+ "postgres_changes",
49
+ {
50
+ event: "UPDATE",
51
+ schema: "public",
52
+ table: "user_profiles",
53
+ filter: `id=eq.${auth.user.id}`
54
+ },
55
+ () => {
56
+ auth.refetchProfile(auth.user.id);
57
+ }
58
+ ).subscribe();
59
+ return () => {
60
+ supabase.removeChannel(channel);
61
+ };
62
+ }, [auth?.user?.id, auth]);
63
+ const tier = subscription?.tier || "free";
64
+ const status = subscription?.status || null;
65
+ const isActive = !status || status === "active" || status === "trialing";
66
+ const tierLevel = TIER_LEVELS[tier] ?? 0;
67
+ return {
68
+ subscription,
69
+ tier,
70
+ status,
71
+ isActive,
72
+ isPro: tierLevel >= TIER_LEVELS.pro,
73
+ isBusiness: tierLevel >= TIER_LEVELS.business,
74
+ isEnterprise: tierLevel >= TIER_LEVELS.enterprise,
75
+ canAccess: (requiredTier) => tierLevel >= (TIER_LEVELS[requiredTier] ?? 0),
76
+ loading: loading || (auth?.loading ?? false),
77
+ isConfigured: features.payments,
78
+ user: auth?.user ?? null,
79
+ profile: auth?.profile ?? null,
80
+ isAuthenticated: Boolean(auth?.user),
81
+ refetch: fetchSubscription
82
+ };
83
+ }
84
+
85
+ // src/payments/useCustomerAccess.ts
86
+ import { useState as useState2, useEffect as useEffect2, useCallback as useCallback2 } from "react";
87
+ function useCustomerAccess() {
88
+ const [loading, setLoading] = useState2(true);
89
+ const [hasAccess, setHasAccess] = useState2(false);
90
+ const [customer, setCustomer] = useState2(null);
91
+ const [subscription, setSubscription] = useState2(null);
92
+ const [error, setError] = useState2(null);
93
+ const checkAccess = useCallback2(async () => {
94
+ setLoading(true);
95
+ setError(null);
96
+ try {
97
+ const customerId = typeof localStorage !== "undefined" ? localStorage.getItem("stripeCustomerId") : null;
98
+ const email = typeof localStorage !== "undefined" ? localStorage.getItem("customerEmail") : null;
99
+ if (!customerId && !email) {
100
+ setHasAccess(false);
101
+ setLoading(false);
102
+ return;
103
+ }
104
+ const result = await ezcoder.stripe.getCustomerStatus({ customerId: customerId || void 0, email: email || void 0 });
105
+ if (result.success) {
106
+ setHasAccess(Boolean(result.hasAccess));
107
+ setCustomer(result.customer || null);
108
+ setSubscription(result.subscription || null);
109
+ } else {
110
+ setHasAccess(false);
111
+ setError(result.error || "Failed to check access");
112
+ }
113
+ } catch (err) {
114
+ setError(err instanceof Error ? err.message : "Unknown error");
115
+ setHasAccess(false);
116
+ } finally {
117
+ setLoading(false);
118
+ }
119
+ }, []);
120
+ useEffect2(() => {
121
+ checkAccess();
122
+ }, [checkAccess]);
123
+ const logout = useCallback2(() => {
124
+ if (typeof localStorage !== "undefined") {
125
+ localStorage.removeItem("stripeCustomerId");
126
+ localStorage.removeItem("customerEmail");
127
+ }
128
+ setHasAccess(false);
129
+ setCustomer(null);
130
+ setSubscription(null);
131
+ }, []);
132
+ return { loading, hasAccess, customer, subscription, error, refresh: checkAccess, logout };
133
+ }
134
+
135
+ // src/payments/BuyButton.tsx
136
+ import { useState as useState3 } from "react";
137
+ import { jsx, jsxs } from "react/jsx-runtime";
138
+ function BuyButton({
139
+ priceId,
140
+ productName,
141
+ className = "",
142
+ children,
143
+ disabled = false,
144
+ customerEmail,
145
+ quantity,
146
+ successUrl,
147
+ cancelUrl
148
+ }) {
149
+ const [loading, setLoading] = useState3(false);
150
+ const [error, setError] = useState3(null);
151
+ const handleClick = async () => {
152
+ setError(null);
153
+ setLoading(true);
154
+ try {
155
+ const result = await ezcoder.stripe.createCheckout(priceId, {
156
+ customerEmail,
157
+ quantity,
158
+ successUrl,
159
+ cancelUrl,
160
+ redirect: true
161
+ });
162
+ if (!result.success) {
163
+ setError(result.error || "Checkout failed");
164
+ }
165
+ } catch (err) {
166
+ setError(err instanceof Error ? err.message : "Checkout failed");
167
+ } finally {
168
+ setLoading(false);
169
+ }
170
+ };
171
+ return /* @__PURE__ */ jsxs("div", { children: [
172
+ /* @__PURE__ */ jsx(
173
+ "button",
174
+ {
175
+ onClick: handleClick,
176
+ disabled: disabled || loading,
177
+ className,
178
+ style: !className ? {
179
+ padding: "10px 20px",
180
+ backgroundColor: disabled || loading ? "#9ca3af" : "#3b82f6",
181
+ color: "white",
182
+ border: "none",
183
+ borderRadius: "6px",
184
+ fontSize: "14px",
185
+ fontWeight: 500,
186
+ cursor: disabled || loading ? "not-allowed" : "pointer"
187
+ } : void 0,
188
+ children: loading ? "Processing..." : children || `Buy ${productName || "Now"}`
189
+ }
190
+ ),
191
+ error && /* @__PURE__ */ jsx("p", { style: { color: "#dc2626", fontSize: "12px", marginTop: "4px" }, children: error })
192
+ ] });
193
+ }
194
+
195
+ // src/payments/PricingTable.tsx
196
+ import { useState as useState4 } from "react";
197
+ import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
198
+ var DEFAULT_PLANS = [
199
+ {
200
+ name: "Starter",
201
+ description: "For individuals getting started",
202
+ monthlyPrice: 0,
203
+ features: ["Basic features", "Community support"],
204
+ cta: "Get Started"
205
+ },
206
+ {
207
+ name: "Creator",
208
+ description: "For growing projects",
209
+ monthlyPrice: 25,
210
+ yearlyPrice: 270,
211
+ features: ["All Starter features", "Priority support", "Advanced analytics"],
212
+ highlighted: true,
213
+ cta: "Subscribe"
214
+ },
215
+ {
216
+ name: "Business",
217
+ description: "For teams and businesses",
218
+ monthlyPrice: 50,
219
+ yearlyPrice: 540,
220
+ features: ["All Creator features", "Team collaboration", "Custom integrations"],
221
+ cta: "Subscribe"
222
+ },
223
+ {
224
+ name: "Enterprise",
225
+ description: "For large organizations",
226
+ monthlyPrice: -1,
227
+ features: ["All Business features", "Dedicated support", "SLA guarantee", "Custom contracts"],
228
+ cta: "Contact Sales"
229
+ }
230
+ ];
231
+ function PricingTable({ plans = DEFAULT_PLANS, className = "", customerEmail }) {
232
+ const [yearly, setYearly] = useState4(false);
233
+ return /* @__PURE__ */ jsxs2("div", { className, children: [
234
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex", justifyContent: "center", marginBottom: "32px", gap: "8px", alignItems: "center" }, children: [
235
+ /* @__PURE__ */ jsx2("span", { style: { fontSize: "14px", color: !yearly ? "#111" : "#6b7280", fontWeight: !yearly ? 600 : 400 }, children: "Monthly" }),
236
+ /* @__PURE__ */ jsx2(
237
+ "button",
238
+ {
239
+ onClick: () => setYearly(!yearly),
240
+ style: {
241
+ width: "44px",
242
+ height: "24px",
243
+ borderRadius: "12px",
244
+ backgroundColor: yearly ? "#3b82f6" : "#d1d5db",
245
+ border: "none",
246
+ cursor: "pointer",
247
+ position: "relative"
248
+ },
249
+ children: /* @__PURE__ */ jsx2("span", { style: {
250
+ width: "18px",
251
+ height: "18px",
252
+ borderRadius: "50%",
253
+ backgroundColor: "white",
254
+ position: "absolute",
255
+ top: "3px",
256
+ left: yearly ? "23px" : "3px",
257
+ transition: "left 0.2s"
258
+ } })
259
+ }
260
+ ),
261
+ /* @__PURE__ */ jsxs2("span", { style: { fontSize: "14px", color: yearly ? "#111" : "#6b7280", fontWeight: yearly ? 600 : 400 }, children: [
262
+ "Yearly ",
263
+ /* @__PURE__ */ jsx2("span", { style: { color: "#059669", fontSize: "12px" }, children: "Save 10%" })
264
+ ] })
265
+ ] }),
266
+ /* @__PURE__ */ jsx2("div", { style: { display: "grid", gridTemplateColumns: `repeat(${Math.min(plans.length, 4)}, 1fr)`, gap: "24px", maxWidth: "1200px", margin: "0 auto" }, children: plans.map((plan) => {
267
+ const price = yearly && plan.yearlyPrice !== void 0 ? plan.yearlyPrice / 12 : plan.monthlyPrice;
268
+ const priceId = yearly ? plan.yearlyPriceId : plan.monthlyPriceId;
269
+ return /* @__PURE__ */ jsxs2(
270
+ "div",
271
+ {
272
+ style: {
273
+ border: plan.highlighted ? "2px solid #3b82f6" : "1px solid #e5e7eb",
274
+ borderRadius: "12px",
275
+ padding: "24px",
276
+ backgroundColor: plan.highlighted ? "#eff6ff" : "white"
277
+ },
278
+ children: [
279
+ /* @__PURE__ */ jsx2("h3", { style: { fontSize: "1.25rem", fontWeight: 600, marginBottom: "4px" }, children: plan.name }),
280
+ /* @__PURE__ */ jsx2("p", { style: { color: "#6b7280", fontSize: "14px", marginBottom: "16px" }, children: plan.description }),
281
+ /* @__PURE__ */ jsx2("div", { style: { marginBottom: "24px" }, children: plan.monthlyPrice === 0 ? /* @__PURE__ */ jsx2("span", { style: { fontSize: "2rem", fontWeight: 700 }, children: "Free" }) : plan.monthlyPrice === -1 ? /* @__PURE__ */ jsx2("span", { style: { fontSize: "1.5rem", fontWeight: 600 }, children: "Custom" }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
282
+ /* @__PURE__ */ jsxs2("span", { style: { fontSize: "2rem", fontWeight: 700 }, children: [
283
+ "$",
284
+ Math.round(price)
285
+ ] }),
286
+ /* @__PURE__ */ jsx2("span", { style: { color: "#6b7280", fontSize: "14px" }, children: "/mo" })
287
+ ] }) }),
288
+ /* @__PURE__ */ jsx2("ul", { style: { listStyle: "none", padding: 0, marginBottom: "24px" }, children: plan.features.map((feature) => /* @__PURE__ */ jsxs2("li", { style: { display: "flex", alignItems: "center", gap: "8px", marginBottom: "8px", fontSize: "14px" }, children: [
289
+ /* @__PURE__ */ jsx2("span", { style: { color: "#059669" }, children: "\u2713" }),
290
+ " ",
291
+ feature
292
+ ] }, feature)) }),
293
+ priceId ? /* @__PURE__ */ jsx2(BuyButton, { priceId, productName: plan.name, customerEmail, children: plan.cta || "Subscribe" }) : plan.monthlyPrice === 0 ? /* @__PURE__ */ jsx2("button", { style: {
294
+ width: "100%",
295
+ padding: "10px",
296
+ border: "1px solid #d1d5db",
297
+ borderRadius: "6px",
298
+ background: "white",
299
+ cursor: "pointer",
300
+ fontSize: "14px"
301
+ }, children: plan.cta || "Get Started" }) : /* @__PURE__ */ jsx2("button", { style: {
302
+ width: "100%",
303
+ padding: "10px",
304
+ border: "1px solid #d1d5db",
305
+ borderRadius: "6px",
306
+ background: "white",
307
+ cursor: "pointer",
308
+ fontSize: "14px"
309
+ }, children: plan.cta || "Contact Sales" })
310
+ ]
311
+ },
312
+ plan.name
313
+ );
314
+ }) })
315
+ ] });
316
+ }
317
+
318
+ // src/payments/ManageSubscriptionButton.tsx
319
+ import { useState as useState5 } from "react";
320
+ import { jsx as jsx3 } from "react/jsx-runtime";
321
+ function ManageSubscriptionButton({ children, className = "", customerId }) {
322
+ const [loading, setLoading] = useState5(false);
323
+ const handleClick = async () => {
324
+ const id = customerId || (typeof localStorage !== "undefined" ? localStorage.getItem("stripeCustomerId") : null);
325
+ if (!id) return;
326
+ setLoading(true);
327
+ await ezcoder.stripe.createPortalSession(id, { redirect: true });
328
+ setLoading(false);
329
+ };
330
+ return /* @__PURE__ */ jsx3(
331
+ "button",
332
+ {
333
+ onClick: handleClick,
334
+ disabled: loading,
335
+ className,
336
+ style: !className ? {
337
+ padding: "8px 16px",
338
+ border: "1px solid #d1d5db",
339
+ borderRadius: "6px",
340
+ background: "white",
341
+ cursor: loading ? "not-allowed" : "pointer",
342
+ fontSize: "14px"
343
+ } : void 0,
344
+ children: loading ? "Loading..." : children || "Manage Subscription"
345
+ }
346
+ );
347
+ }
348
+
349
+ // src/payments/SubscriptionManager.tsx
350
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
351
+ var STATUS_COLORS = {
352
+ active: "#059669",
353
+ trialing: "#3b82f6",
354
+ past_due: "#d97706",
355
+ canceled: "#dc2626",
356
+ unpaid: "#dc2626"
357
+ };
358
+ function SubscriptionManager({ className = "" }) {
359
+ const { subscription, tier, status, isActive, loading } = useSubscription();
360
+ if (loading) {
361
+ return /* @__PURE__ */ jsx4("div", { style: { padding: "20px", color: "#6b7280" }, children: "Loading subscription..." });
362
+ }
363
+ return /* @__PURE__ */ jsxs3("div", { className, style: { border: "1px solid #e5e7eb", borderRadius: "12px", padding: "24px" }, children: [
364
+ /* @__PURE__ */ jsx4("h3", { style: { fontSize: "1.125rem", fontWeight: 600, marginBottom: "16px" }, children: "Subscription" }),
365
+ /* @__PURE__ */ jsxs3("div", { style: { display: "grid", gap: "12px", marginBottom: "24px" }, children: [
366
+ /* @__PURE__ */ jsxs3("div", { style: { display: "flex", justifyContent: "space-between" }, children: [
367
+ /* @__PURE__ */ jsx4("span", { style: { color: "#6b7280" }, children: "Plan" }),
368
+ /* @__PURE__ */ jsx4("span", { style: { fontWeight: 500, textTransform: "capitalize" }, children: tier })
369
+ ] }),
370
+ status && /* @__PURE__ */ jsxs3("div", { style: { display: "flex", justifyContent: "space-between" }, children: [
371
+ /* @__PURE__ */ jsx4("span", { style: { color: "#6b7280" }, children: "Status" }),
372
+ /* @__PURE__ */ jsx4("span", { style: {
373
+ fontWeight: 500,
374
+ color: STATUS_COLORS[status] || "#6b7280",
375
+ textTransform: "capitalize"
376
+ }, children: status === "past_due" ? "Past Due" : status })
377
+ ] }),
378
+ subscription?.currentPeriodEnd && /* @__PURE__ */ jsxs3("div", { style: { display: "flex", justifyContent: "space-between" }, children: [
379
+ /* @__PURE__ */ jsx4("span", { style: { color: "#6b7280" }, children: isActive ? "Renews" : "Expires" }),
380
+ /* @__PURE__ */ jsx4("span", { children: new Date(subscription.currentPeriodEnd).toLocaleDateString() })
381
+ ] })
382
+ ] }),
383
+ subscription?.customerId && /* @__PURE__ */ jsx4(ManageSubscriptionButton, { customerId: subscription.customerId })
384
+ ] });
385
+ }
386
+
387
+ // src/payments/ProtectedContent.tsx
388
+ import { Fragment as Fragment2, jsx as jsx5 } from "react/jsx-runtime";
389
+ function ProtectedContent({ children, fallback, loadingComponent }) {
390
+ const { loading, hasAccess } = useCustomerAccess();
391
+ if (loading) {
392
+ return /* @__PURE__ */ jsx5(Fragment2, { children: loadingComponent || /* @__PURE__ */ jsx5("div", { style: { padding: "20px", textAlign: "center", color: "#6b7280" }, children: "Loading..." }) });
393
+ }
394
+ if (!hasAccess) {
395
+ return /* @__PURE__ */ jsx5(Fragment2, { children: fallback || /* @__PURE__ */ jsx5("div", { style: { padding: "20px", textAlign: "center", color: "#6b7280" }, children: "This content requires a subscription." }) });
396
+ }
397
+ return /* @__PURE__ */ jsx5(Fragment2, { children });
398
+ }
399
+ export {
400
+ BuyButton,
401
+ ManageSubscriptionButton,
402
+ PricingTable,
403
+ ProtectedContent,
404
+ SubscriptionManager,
405
+ useCustomerAccess,
406
+ useSubscription
407
+ };
408
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/payments/useSubscription.ts","../../src/payments/useCustomerAccess.ts","../../src/payments/BuyButton.tsx","../../src/payments/PricingTable.tsx","../../src/payments/ManageSubscriptionButton.tsx","../../src/payments/SubscriptionManager.tsx","../../src/payments/ProtectedContent.tsx"],"sourcesContent":["import { useState, useEffect, useCallback, useContext } from 'react';\nimport { AuthContext } from '../auth/AuthProvider';\nimport { supabase } from '../core/supabase';\nimport { features } from '../core/config';\nimport type { SubscriptionTier, SubscriptionStatus } from '../core/types';\n\ninterface SubscriptionState {\n customerId: string | null;\n tier: SubscriptionTier;\n status: SubscriptionStatus | null;\n currentPeriodEnd: string | null;\n}\n\ninterface UseSubscriptionReturn {\n subscription: SubscriptionState | null;\n tier: SubscriptionTier;\n status: SubscriptionStatus | null;\n isActive: boolean;\n isPro: boolean;\n isBusiness: boolean;\n isEnterprise: boolean;\n canAccess: (requiredTier: SubscriptionTier) => boolean;\n loading: boolean;\n isConfigured: boolean;\n user: unknown;\n profile: unknown;\n isAuthenticated: boolean;\n refetch: () => Promise<void>;\n}\n\nconst TIER_LEVELS: Record<string, number> = {\n free: 0, starter: 1, creator: 2, pro: 3, business: 4, enterprise: 5,\n};\n\nexport function useSubscription(): UseSubscriptionReturn {\n const auth = useContext(AuthContext);\n const [subscription, setSubscription] = useState<SubscriptionState | null>(null);\n const [loading, setLoading] = useState(true);\n\n const fetchSubscription = useCallback(async () => {\n if (!auth?.profile) {\n setSubscription(null);\n setLoading(false);\n return;\n }\n\n const sub: SubscriptionState = {\n customerId: auth.profile.stripe_customer_id || null,\n tier: (auth.profile.subscription_tier as SubscriptionTier) || 'free',\n status: (auth.profile.subscription_status as SubscriptionStatus) || null,\n currentPeriodEnd: auth.profile.subscription_period_end || null,\n };\n\n setSubscription(sub);\n setLoading(false);\n }, [auth?.profile]);\n\n useEffect(() => {\n fetchSubscription();\n }, [fetchSubscription]);\n\n useEffect(() => {\n if (!features.auth || !auth?.user?.id) return;\n\n const channel = supabase\n .channel(`subscription_${auth.user.id}`)\n .on(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n 'postgres_changes' as any,\n {\n event: 'UPDATE',\n schema: 'public',\n table: 'user_profiles',\n filter: `id=eq.${auth.user.id}`,\n },\n () => {\n auth.refetchProfile(auth.user!.id);\n }\n )\n .subscribe();\n\n return () => {\n supabase.removeChannel(channel);\n };\n }, [auth?.user?.id, auth]);\n\n const tier = subscription?.tier || 'free';\n const status = subscription?.status || null;\n const isActive = !status || status === 'active' || status === 'trialing';\n const tierLevel = TIER_LEVELS[tier] ?? 0;\n\n return {\n subscription,\n tier,\n status,\n isActive,\n isPro: tierLevel >= TIER_LEVELS.pro,\n isBusiness: tierLevel >= TIER_LEVELS.business,\n isEnterprise: tierLevel >= TIER_LEVELS.enterprise,\n canAccess: (requiredTier: SubscriptionTier) => tierLevel >= (TIER_LEVELS[requiredTier] ?? 0),\n loading: loading || (auth?.loading ?? false),\n isConfigured: features.payments,\n user: auth?.user ?? null,\n profile: auth?.profile ?? null,\n isAuthenticated: Boolean(auth?.user),\n refetch: fetchSubscription,\n };\n}\n","import { useState, useEffect, useCallback } from 'react';\nimport { ezcoder } from '../core/platform';\n\ninterface CustomerAccessReturn {\n loading: boolean;\n hasAccess: boolean;\n customer: Record<string, unknown> | null;\n subscription: Record<string, unknown> | null;\n error: string | null;\n refresh: () => Promise<void>;\n logout: () => void;\n}\n\nexport function useCustomerAccess(): CustomerAccessReturn {\n const [loading, setLoading] = useState(true);\n const [hasAccess, setHasAccess] = useState(false);\n const [customer, setCustomer] = useState<Record<string, unknown> | null>(null);\n const [subscription, setSubscription] = useState<Record<string, unknown> | null>(null);\n const [error, setError] = useState<string | null>(null);\n\n const checkAccess = useCallback(async () => {\n setLoading(true);\n setError(null);\n\n try {\n const customerId = typeof localStorage !== 'undefined'\n ? localStorage.getItem('stripeCustomerId')\n : null;\n const email = typeof localStorage !== 'undefined'\n ? localStorage.getItem('customerEmail')\n : null;\n\n if (!customerId && !email) {\n setHasAccess(false);\n setLoading(false);\n return;\n }\n\n const result = await ezcoder.stripe.getCustomerStatus({ customerId: customerId || undefined, email: email || undefined });\n\n if (result.success) {\n setHasAccess(Boolean(result.hasAccess));\n setCustomer((result.customer as Record<string, unknown>) || null);\n setSubscription((result.subscription as Record<string, unknown>) || null);\n } else {\n setHasAccess(false);\n setError(result.error || 'Failed to check access');\n }\n } catch (err: unknown) {\n setError(err instanceof Error ? err.message : 'Unknown error');\n setHasAccess(false);\n } finally {\n setLoading(false);\n }\n }, []);\n\n useEffect(() => {\n checkAccess();\n }, [checkAccess]);\n\n const logout = useCallback(() => {\n if (typeof localStorage !== 'undefined') {\n localStorage.removeItem('stripeCustomerId');\n localStorage.removeItem('customerEmail');\n }\n setHasAccess(false);\n setCustomer(null);\n setSubscription(null);\n }, []);\n\n return { loading, hasAccess, customer, subscription, error, refresh: checkAccess, logout };\n}\n","import { useState } from 'react';\nimport { ezcoder } from '../core/platform';\n\ninterface BuyButtonProps {\n priceId: string;\n productName?: string;\n className?: string;\n children?: React.ReactNode;\n disabled?: boolean;\n customerEmail?: string;\n quantity?: number;\n successUrl?: string;\n cancelUrl?: string;\n}\n\nexport function BuyButton({\n priceId,\n productName,\n className = '',\n children,\n disabled = false,\n customerEmail,\n quantity,\n successUrl,\n cancelUrl,\n}: BuyButtonProps) {\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const handleClick = async () => {\n setError(null);\n setLoading(true);\n\n try {\n const result = await ezcoder.stripe.createCheckout(priceId, {\n customerEmail,\n quantity,\n successUrl,\n cancelUrl,\n redirect: true,\n });\n\n if (!result.success) {\n setError(result.error || 'Checkout failed');\n }\n } catch (err: unknown) {\n setError(err instanceof Error ? err.message : 'Checkout failed');\n } finally {\n setLoading(false);\n }\n };\n\n return (\n <div>\n <button\n onClick={handleClick}\n disabled={disabled || loading}\n className={className}\n style={!className ? {\n padding: '10px 20px',\n backgroundColor: disabled || loading ? '#9ca3af' : '#3b82f6',\n color: 'white',\n border: 'none',\n borderRadius: '6px',\n fontSize: '14px',\n fontWeight: 500,\n cursor: disabled || loading ? 'not-allowed' : 'pointer',\n } : undefined}\n >\n {loading ? 'Processing...' : children || `Buy ${productName || 'Now'}`}\n </button>\n {error && (\n <p style={{ color: '#dc2626', fontSize: '12px', marginTop: '4px' }}>{error}</p>\n )}\n </div>\n );\n}\n","import { useState } from 'react';\nimport { BuyButton } from './BuyButton';\n\ninterface PricingPlan {\n name: string;\n description: string;\n monthlyPriceId?: string;\n yearlyPriceId?: string;\n monthlyPrice: number;\n yearlyPrice?: number;\n features: string[];\n highlighted?: boolean;\n cta?: string;\n}\n\ninterface PricingTableProps {\n plans?: PricingPlan[];\n className?: string;\n customerEmail?: string;\n}\n\nconst DEFAULT_PLANS: PricingPlan[] = [\n {\n name: 'Starter',\n description: 'For individuals getting started',\n monthlyPrice: 0,\n features: ['Basic features', 'Community support'],\n cta: 'Get Started',\n },\n {\n name: 'Creator',\n description: 'For growing projects',\n monthlyPrice: 25,\n yearlyPrice: 270,\n features: ['All Starter features', 'Priority support', 'Advanced analytics'],\n highlighted: true,\n cta: 'Subscribe',\n },\n {\n name: 'Business',\n description: 'For teams and businesses',\n monthlyPrice: 50,\n yearlyPrice: 540,\n features: ['All Creator features', 'Team collaboration', 'Custom integrations'],\n cta: 'Subscribe',\n },\n {\n name: 'Enterprise',\n description: 'For large organizations',\n monthlyPrice: -1,\n features: ['All Business features', 'Dedicated support', 'SLA guarantee', 'Custom contracts'],\n cta: 'Contact Sales',\n },\n];\n\nexport function PricingTable({ plans = DEFAULT_PLANS, className = '', customerEmail }: PricingTableProps) {\n const [yearly, setYearly] = useState(false);\n\n return (\n <div className={className}>\n <div style={{ display: 'flex', justifyContent: 'center', marginBottom: '32px', gap: '8px', alignItems: 'center' }}>\n <span style={{ fontSize: '14px', color: !yearly ? '#111' : '#6b7280', fontWeight: !yearly ? 600 : 400 }}>Monthly</span>\n <button\n onClick={() => setYearly(!yearly)}\n style={{\n width: '44px', height: '24px', borderRadius: '12px',\n backgroundColor: yearly ? '#3b82f6' : '#d1d5db',\n border: 'none', cursor: 'pointer', position: 'relative',\n }}\n >\n <span style={{\n width: '18px', height: '18px', borderRadius: '50%', backgroundColor: 'white',\n position: 'absolute', top: '3px', left: yearly ? '23px' : '3px',\n transition: 'left 0.2s',\n }} />\n </button>\n <span style={{ fontSize: '14px', color: yearly ? '#111' : '#6b7280', fontWeight: yearly ? 600 : 400 }}>\n Yearly <span style={{ color: '#059669', fontSize: '12px' }}>Save 10%</span>\n </span>\n </div>\n\n <div style={{ display: 'grid', gridTemplateColumns: `repeat(${Math.min(plans.length, 4)}, 1fr)`, gap: '24px', maxWidth: '1200px', margin: '0 auto' }}>\n {plans.map((plan) => {\n const price = yearly && plan.yearlyPrice !== undefined ? plan.yearlyPrice / 12 : plan.monthlyPrice;\n const priceId = yearly ? plan.yearlyPriceId : plan.monthlyPriceId;\n\n return (\n <div\n key={plan.name}\n style={{\n border: plan.highlighted ? '2px solid #3b82f6' : '1px solid #e5e7eb',\n borderRadius: '12px', padding: '24px',\n backgroundColor: plan.highlighted ? '#eff6ff' : 'white',\n }}\n >\n <h3 style={{ fontSize: '1.25rem', fontWeight: 600, marginBottom: '4px' }}>{plan.name}</h3>\n <p style={{ color: '#6b7280', fontSize: '14px', marginBottom: '16px' }}>{plan.description}</p>\n\n <div style={{ marginBottom: '24px' }}>\n {plan.monthlyPrice === 0 ? (\n <span style={{ fontSize: '2rem', fontWeight: 700 }}>Free</span>\n ) : plan.monthlyPrice === -1 ? (\n <span style={{ fontSize: '1.5rem', fontWeight: 600 }}>Custom</span>\n ) : (\n <>\n <span style={{ fontSize: '2rem', fontWeight: 700 }}>${Math.round(price)}</span>\n <span style={{ color: '#6b7280', fontSize: '14px' }}>/mo</span>\n </>\n )}\n </div>\n\n <ul style={{ listStyle: 'none', padding: 0, marginBottom: '24px' }}>\n {plan.features.map((feature) => (\n <li key={feature} style={{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '8px', fontSize: '14px' }}>\n <span style={{ color: '#059669' }}>✓</span> {feature}\n </li>\n ))}\n </ul>\n\n {priceId ? (\n <BuyButton priceId={priceId} productName={plan.name} customerEmail={customerEmail}>\n {plan.cta || 'Subscribe'}\n </BuyButton>\n ) : plan.monthlyPrice === 0 ? (\n <button style={{\n width: '100%', padding: '10px', border: '1px solid #d1d5db',\n borderRadius: '6px', background: 'white', cursor: 'pointer', fontSize: '14px',\n }}>\n {plan.cta || 'Get Started'}\n </button>\n ) : (\n <button style={{\n width: '100%', padding: '10px', border: '1px solid #d1d5db',\n borderRadius: '6px', background: 'white', cursor: 'pointer', fontSize: '14px',\n }}>\n {plan.cta || 'Contact Sales'}\n </button>\n )}\n </div>\n );\n })}\n </div>\n </div>\n );\n}\n","import { useState } from 'react';\nimport { ezcoder } from '../core/platform';\n\ninterface ManageSubscriptionButtonProps {\n children?: React.ReactNode;\n className?: string;\n customerId?: string;\n}\n\nexport function ManageSubscriptionButton({ children, className = '', customerId }: ManageSubscriptionButtonProps) {\n const [loading, setLoading] = useState(false);\n\n const handleClick = async () => {\n const id = customerId || (typeof localStorage !== 'undefined' ? localStorage.getItem('stripeCustomerId') : null);\n if (!id) return;\n\n setLoading(true);\n await ezcoder.stripe.createPortalSession(id, { redirect: true });\n setLoading(false);\n };\n\n return (\n <button\n onClick={handleClick}\n disabled={loading}\n className={className}\n style={!className ? {\n padding: '8px 16px',\n border: '1px solid #d1d5db',\n borderRadius: '6px',\n background: 'white',\n cursor: loading ? 'not-allowed' : 'pointer',\n fontSize: '14px',\n } : undefined}\n >\n {loading ? 'Loading...' : children || 'Manage Subscription'}\n </button>\n );\n}\n","import { useSubscription } from './useSubscription';\nimport { ManageSubscriptionButton } from './ManageSubscriptionButton';\n\ninterface SubscriptionManagerProps {\n className?: string;\n}\n\nconst STATUS_COLORS: Record<string, string> = {\n active: '#059669',\n trialing: '#3b82f6',\n past_due: '#d97706',\n canceled: '#dc2626',\n unpaid: '#dc2626',\n};\n\nexport function SubscriptionManager({ className = '' }: SubscriptionManagerProps) {\n const { subscription, tier, status, isActive, loading } = useSubscription();\n\n if (loading) {\n return <div style={{ padding: '20px', color: '#6b7280' }}>Loading subscription...</div>;\n }\n\n return (\n <div className={className} style={{ border: '1px solid #e5e7eb', borderRadius: '12px', padding: '24px' }}>\n <h3 style={{ fontSize: '1.125rem', fontWeight: 600, marginBottom: '16px' }}>Subscription</h3>\n\n <div style={{ display: 'grid', gap: '12px', marginBottom: '24px' }}>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}>\n <span style={{ color: '#6b7280' }}>Plan</span>\n <span style={{ fontWeight: 500, textTransform: 'capitalize' }}>{tier}</span>\n </div>\n\n {status && (\n <div style={{ display: 'flex', justifyContent: 'space-between' }}>\n <span style={{ color: '#6b7280' }}>Status</span>\n <span style={{\n fontWeight: 500,\n color: STATUS_COLORS[status] || '#6b7280',\n textTransform: 'capitalize',\n }}>\n {status === 'past_due' ? 'Past Due' : status}\n </span>\n </div>\n )}\n\n {subscription?.currentPeriodEnd && (\n <div style={{ display: 'flex', justifyContent: 'space-between' }}>\n <span style={{ color: '#6b7280' }}>{isActive ? 'Renews' : 'Expires'}</span>\n <span>{new Date(subscription.currentPeriodEnd).toLocaleDateString()}</span>\n </div>\n )}\n </div>\n\n {subscription?.customerId && (\n <ManageSubscriptionButton customerId={subscription.customerId} />\n )}\n </div>\n );\n}\n","import { useCustomerAccess } from './useCustomerAccess';\n\ninterface ProtectedContentProps {\n children: React.ReactNode;\n fallback?: React.ReactNode;\n loadingComponent?: React.ReactNode;\n}\n\nexport function ProtectedContent({ children, fallback, loadingComponent }: ProtectedContentProps) {\n const { loading, hasAccess } = useCustomerAccess();\n\n if (loading) {\n return <>{loadingComponent || <div style={{ padding: '20px', textAlign: 'center', color: '#6b7280' }}>Loading...</div>}</>;\n }\n\n if (!hasAccess) {\n return <>{fallback || <div style={{ padding: '20px', textAlign: 'center', color: '#6b7280' }}>This content requires a subscription.</div>}</>;\n }\n\n return <>{children}</>;\n}\n"],"mappings":"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","names":["useState","useEffect","useCallback","useState","useCallback","useEffect","useState","useState","useState","jsx","jsxs","useState","useState","jsx","useState","jsx","jsxs","Fragment","jsx"]}
@@ -0,0 +1,37 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ interface UseRolesReturn {
4
+ roles: string[];
5
+ permissions: string[];
6
+ accessibleRoutes: string[];
7
+ loading: boolean;
8
+ error: string | null;
9
+ hasRole: (name: string) => boolean;
10
+ hasAnyRole: (names: string[]) => boolean;
11
+ hasAllRoles: (names: string[]) => boolean;
12
+ hasPermission: (perm: string) => boolean;
13
+ hasAnyPermission: (perms: string[]) => boolean;
14
+ hasAllPermissions: (perms: string[]) => boolean;
15
+ canAccessRoute: (path: string) => boolean;
16
+ refreshRoles: () => Promise<void>;
17
+ }
18
+ declare function useRoles(): UseRolesReturn;
19
+
20
+ interface RoleGateProps {
21
+ children: React.ReactNode;
22
+ roles?: string[];
23
+ permissions?: string[];
24
+ requireAll?: boolean;
25
+ fallback?: React.ReactNode;
26
+ loadingFallback?: React.ReactNode;
27
+ }
28
+ interface RouteGateProps {
29
+ children: React.ReactNode;
30
+ route: string;
31
+ fallback?: React.ReactNode;
32
+ loadingFallback?: React.ReactNode;
33
+ }
34
+ declare function RoleGate({ children, roles: requiredRoles, permissions: requiredPerms, requireAll, fallback, loadingFallback, }: RoleGateProps): react_jsx_runtime.JSX.Element;
35
+ declare function RouteGate({ children, route, fallback, loadingFallback }: RouteGateProps): react_jsx_runtime.JSX.Element;
36
+
37
+ export { RoleGate, RouteGate, useRoles };
@@ -0,0 +1,120 @@
1
+ import {
2
+ AuthContext
3
+ } from "../chunk-YNDCD53D.js";
4
+ import "../chunk-5XIZHBKE.js";
5
+ import {
6
+ features,
7
+ supabase
8
+ } from "../chunk-G7XDUN3Z.js";
9
+
10
+ // src/roles/useRoles.ts
11
+ import { useState, useEffect, useCallback, useContext } from "react";
12
+ function useRoles() {
13
+ const auth = useContext(AuthContext);
14
+ const [roles, setRoles] = useState([]);
15
+ const [permissions, setPermissions] = useState([]);
16
+ const [accessibleRoutes, setAccessibleRoutes] = useState([]);
17
+ const [loading, setLoading] = useState(true);
18
+ const [error, setError] = useState(null);
19
+ const fetchRoles = useCallback(async () => {
20
+ if (!auth?.user?.id || !features.auth) {
21
+ setRoles([]);
22
+ setPermissions([]);
23
+ setAccessibleRoutes([]);
24
+ setLoading(false);
25
+ return;
26
+ }
27
+ try {
28
+ const { data, error: rpcError } = await supabase.rpc("get_user_roles", {
29
+ user_uuid: auth.user.id
30
+ });
31
+ if (rpcError) {
32
+ setError(rpcError.message);
33
+ setLoading(false);
34
+ return;
35
+ }
36
+ const roleData = data || [];
37
+ const allRoles = roleData.map((r) => r.role_name);
38
+ const allPerms = [...new Set(roleData.flatMap((r) => r.permissions || []))];
39
+ const allRoutes = [...new Set(roleData.flatMap((r) => r.can_access_routes || []))];
40
+ setRoles(allRoles);
41
+ setPermissions(allPerms);
42
+ setAccessibleRoutes(allRoutes);
43
+ setError(null);
44
+ } catch (err) {
45
+ setError(err instanceof Error ? err.message : "Failed to fetch roles");
46
+ } finally {
47
+ setLoading(false);
48
+ }
49
+ }, [auth?.user?.id]);
50
+ useEffect(() => {
51
+ fetchRoles();
52
+ }, [fetchRoles]);
53
+ const hasRole = useCallback((name) => roles.includes(name), [roles]);
54
+ const hasAnyRole = useCallback((names) => names.some((n) => roles.includes(n)), [roles]);
55
+ const hasAllRoles = useCallback((names) => names.every((n) => roles.includes(n)), [roles]);
56
+ const hasPermission = useCallback((perm) => permissions.includes(perm), [permissions]);
57
+ const hasAnyPermission = useCallback((perms) => perms.some((p) => permissions.includes(p)), [permissions]);
58
+ const hasAllPermissions = useCallback((perms) => perms.every((p) => permissions.includes(p)), [permissions]);
59
+ const canAccessRoute = useCallback((path) => {
60
+ return accessibleRoutes.some((route) => {
61
+ if (route.endsWith("/*")) {
62
+ return path.startsWith(route.slice(0, -2));
63
+ }
64
+ return route === path;
65
+ });
66
+ }, [accessibleRoutes]);
67
+ return {
68
+ roles,
69
+ permissions,
70
+ accessibleRoutes,
71
+ loading,
72
+ error,
73
+ hasRole,
74
+ hasAnyRole,
75
+ hasAllRoles,
76
+ hasPermission,
77
+ hasAnyPermission,
78
+ hasAllPermissions,
79
+ canAccessRoute,
80
+ refreshRoles: fetchRoles
81
+ };
82
+ }
83
+
84
+ // src/roles/RoleGate.tsx
85
+ import { Fragment, jsx } from "react/jsx-runtime";
86
+ function RoleGate({
87
+ children,
88
+ roles: requiredRoles,
89
+ permissions: requiredPerms,
90
+ requireAll = false,
91
+ fallback = null,
92
+ loadingFallback = null
93
+ }) {
94
+ const { hasRole, hasAnyRole, hasAllRoles, hasPermission, hasAnyPermission, hasAllPermissions, loading } = useRoles();
95
+ if (loading) return /* @__PURE__ */ jsx(Fragment, { children: loadingFallback });
96
+ let hasAccess = true;
97
+ if (requiredRoles?.length) {
98
+ hasAccess = requireAll ? hasAllRoles(requiredRoles) : hasAnyRole(requiredRoles);
99
+ }
100
+ if (hasAccess && requiredPerms?.length) {
101
+ hasAccess = requireAll ? hasAllPermissions(requiredPerms) : hasAnyPermission(requiredPerms);
102
+ }
103
+ if (!hasAccess && !requiredRoles?.length && !requiredPerms?.length) {
104
+ hasAccess = true;
105
+ }
106
+ void hasRole;
107
+ void hasPermission;
108
+ return hasAccess ? /* @__PURE__ */ jsx(Fragment, { children }) : /* @__PURE__ */ jsx(Fragment, { children: fallback });
109
+ }
110
+ function RouteGate({ children, route, fallback = null, loadingFallback = null }) {
111
+ const { canAccessRoute, loading } = useRoles();
112
+ if (loading) return /* @__PURE__ */ jsx(Fragment, { children: loadingFallback });
113
+ return canAccessRoute(route) ? /* @__PURE__ */ jsx(Fragment, { children }) : /* @__PURE__ */ jsx(Fragment, { children: fallback });
114
+ }
115
+ export {
116
+ RoleGate,
117
+ RouteGate,
118
+ useRoles
119
+ };
120
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/roles/useRoles.ts","../../src/roles/RoleGate.tsx"],"sourcesContent":["import { useState, useEffect, useCallback, useContext } from 'react';\nimport { supabase } from '../core/supabase';\nimport { features } from '../core/config';\nimport { AuthContext } from '../auth/AuthProvider';\n\ninterface RoleData {\n role_name: string;\n display_name: string;\n permissions: string[];\n can_access_routes: string[];\n}\n\ninterface UseRolesReturn {\n roles: string[];\n permissions: string[];\n accessibleRoutes: string[];\n loading: boolean;\n error: string | null;\n hasRole: (name: string) => boolean;\n hasAnyRole: (names: string[]) => boolean;\n hasAllRoles: (names: string[]) => boolean;\n hasPermission: (perm: string) => boolean;\n hasAnyPermission: (perms: string[]) => boolean;\n hasAllPermissions: (perms: string[]) => boolean;\n canAccessRoute: (path: string) => boolean;\n refreshRoles: () => Promise<void>;\n}\n\nexport function useRoles(): UseRolesReturn {\n const auth = useContext(AuthContext);\n const [roles, setRoles] = useState<string[]>([]);\n const [permissions, setPermissions] = useState<string[]>([]);\n const [accessibleRoutes, setAccessibleRoutes] = useState<string[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n const fetchRoles = useCallback(async () => {\n if (!auth?.user?.id || !features.auth) {\n setRoles([]);\n setPermissions([]);\n setAccessibleRoutes([]);\n setLoading(false);\n return;\n }\n\n try {\n const { data, error: rpcError } = await supabase.rpc('get_user_roles', {\n user_uuid: auth.user.id,\n });\n\n if (rpcError) {\n setError(rpcError.message);\n setLoading(false);\n return;\n }\n\n const roleData = (data || []) as RoleData[];\n const allRoles = roleData.map((r) => r.role_name);\n const allPerms = [...new Set(roleData.flatMap((r) => r.permissions || []))];\n const allRoutes = [...new Set(roleData.flatMap((r) => r.can_access_routes || []))];\n\n setRoles(allRoles);\n setPermissions(allPerms);\n setAccessibleRoutes(allRoutes);\n setError(null);\n } catch (err: unknown) {\n setError(err instanceof Error ? err.message : 'Failed to fetch roles');\n } finally {\n setLoading(false);\n }\n }, [auth?.user?.id]);\n\n useEffect(() => {\n fetchRoles();\n }, [fetchRoles]);\n\n const hasRole = useCallback((name: string) => roles.includes(name), [roles]);\n const hasAnyRole = useCallback((names: string[]) => names.some((n) => roles.includes(n)), [roles]);\n const hasAllRoles = useCallback((names: string[]) => names.every((n) => roles.includes(n)), [roles]);\n const hasPermission = useCallback((perm: string) => permissions.includes(perm), [permissions]);\n const hasAnyPermission = useCallback((perms: string[]) => perms.some((p) => permissions.includes(p)), [permissions]);\n const hasAllPermissions = useCallback((perms: string[]) => perms.every((p) => permissions.includes(p)), [permissions]);\n\n const canAccessRoute = useCallback((path: string) => {\n return accessibleRoutes.some((route) => {\n if (route.endsWith('/*')) {\n return path.startsWith(route.slice(0, -2));\n }\n return route === path;\n });\n }, [accessibleRoutes]);\n\n return {\n roles, permissions, accessibleRoutes, loading, error,\n hasRole, hasAnyRole, hasAllRoles, hasPermission, hasAnyPermission, hasAllPermissions,\n canAccessRoute, refreshRoles: fetchRoles,\n };\n}\n","import { useRoles } from './useRoles';\n\ninterface RoleGateProps {\n children: React.ReactNode;\n roles?: string[];\n permissions?: string[];\n requireAll?: boolean;\n fallback?: React.ReactNode;\n loadingFallback?: React.ReactNode;\n}\n\ninterface RouteGateProps {\n children: React.ReactNode;\n route: string;\n fallback?: React.ReactNode;\n loadingFallback?: React.ReactNode;\n}\n\nexport function RoleGate({\n children,\n roles: requiredRoles,\n permissions: requiredPerms,\n requireAll = false,\n fallback = null,\n loadingFallback = null,\n}: RoleGateProps) {\n const { hasRole, hasAnyRole, hasAllRoles, hasPermission, hasAnyPermission, hasAllPermissions, loading } = useRoles();\n\n if (loading) return <>{loadingFallback}</>;\n\n let hasAccess = true;\n\n if (requiredRoles?.length) {\n hasAccess = requireAll ? hasAllRoles(requiredRoles) : hasAnyRole(requiredRoles);\n }\n\n if (hasAccess && requiredPerms?.length) {\n hasAccess = requireAll ? hasAllPermissions(requiredPerms) : hasAnyPermission(requiredPerms);\n }\n\n if (!hasAccess && !requiredRoles?.length && !requiredPerms?.length) {\n hasAccess = true;\n }\n\n // Use individual checks to satisfy linter\n void hasRole;\n void hasPermission;\n\n return hasAccess ? <>{children}</> : <>{fallback}</>;\n}\n\nexport function RouteGate({ children, route, fallback = null, loadingFallback = null }: RouteGateProps) {\n const { canAccessRoute, loading } = useRoles();\n\n if (loading) return <>{loadingFallback}</>;\n\n return canAccessRoute(route) ? <>{children}</> : <>{fallback}</>;\n}\n"],"mappings":"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","names":[]}