@ezcoder.dev/sdk 1.0.0 → 1.1.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 (48) hide show
  1. package/dist/DatabaseProvider-DalP-KHC.d.ts +167 -0
  2. package/dist/analytics/index.d.ts +22 -1
  3. package/dist/analytics/index.js +78 -5
  4. package/dist/analytics/index.js.map +1 -1
  5. package/dist/animation/index.js.map +1 -1
  6. package/dist/auth/index.d.ts +1 -1
  7. package/dist/auth/index.js +3 -3
  8. package/dist/auth/index.js.map +1 -1
  9. package/dist/{chunk-G7XDUN3Z.js → chunk-2WG4O4J2.js} +24 -8
  10. package/dist/chunk-2WG4O4J2.js.map +1 -0
  11. package/dist/chunk-7VGYFCQC.js +640 -0
  12. package/dist/chunk-7VGYFCQC.js.map +1 -0
  13. package/dist/{chunk-YNDCD53D.js → chunk-AWU47M6N.js} +36 -12
  14. package/dist/chunk-AWU47M6N.js.map +1 -0
  15. package/dist/{chunk-5XIZHBKE.js → chunk-GPF4AYNG.js} +23 -6
  16. package/dist/chunk-GPF4AYNG.js.map +1 -0
  17. package/dist/cms/index.js +1 -1
  18. package/dist/cms/index.js.map +1 -1
  19. package/dist/cron/index.d.ts +15 -0
  20. package/dist/cron/index.js +49 -0
  21. package/dist/cron/index.js.map +1 -0
  22. package/dist/database/index.d.ts +37 -0
  23. package/dist/database/index.js +31 -0
  24. package/dist/database/index.js.map +1 -0
  25. package/dist/email/index.d.ts +11 -0
  26. package/dist/email/index.js +45 -0
  27. package/dist/email/index.js.map +1 -0
  28. package/dist/errors/index.js.map +1 -1
  29. package/dist/index.d.ts +9 -2
  30. package/dist/index.js +11 -3
  31. package/dist/notifications/index.d.ts +10 -1
  32. package/dist/notifications/index.js +25 -3
  33. package/dist/notifications/index.js.map +1 -1
  34. package/dist/payments/index.d.ts +11 -3
  35. package/dist/payments/index.js +98 -6
  36. package/dist/payments/index.js.map +1 -1
  37. package/dist/roles/index.js +3 -3
  38. package/dist/roles/index.js.map +1 -1
  39. package/dist/seo/index.js.map +1 -1
  40. package/dist/server/index.js.map +1 -1
  41. package/dist/storage/index.d.ts +1 -1
  42. package/dist/storage/index.js +15 -8
  43. package/dist/storage/index.js.map +1 -1
  44. package/dist/{types-DtY5lp3P.d.ts → types-1uP3V_pe.d.ts} +5 -0
  45. package/package.json +120 -105
  46. package/dist/chunk-5XIZHBKE.js.map +0 -1
  47. package/dist/chunk-G7XDUN3Z.js.map +0 -1
  48. package/dist/chunk-YNDCD53D.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/email/sendEmail.ts"],"sourcesContent":["import { supabase } from '../core/supabase';\r\nimport { env } from '../core/config';\r\n\r\ninterface SendEmailOptions {\r\n from?: string;\r\n}\r\n\r\ninterface SendEmailResult {\r\n success: boolean;\r\n id?: string;\r\n error?: string;\r\n}\r\n\r\nexport async function sendEmail(\r\n to: string,\r\n subject: string,\r\n html: string,\r\n options: SendEmailOptions = {},\r\n): Promise<SendEmailResult> {\r\n const apiUrl = env.EZCODER_API_URL;\r\n const projectId = env.EZC_PROJECT_ID;\r\n\r\n if (!apiUrl || !projectId) {\r\n return { success: false, error: 'EZCODER_API_URL and EZC_PROJECT_ID are required' };\r\n }\r\n\r\n const session = await supabase.auth.getSession();\r\n const token = session.data.session?.access_token;\r\n\r\n if (!token) {\r\n return { success: false, error: 'Authentication required' };\r\n }\r\n\r\n try {\r\n const res = await fetch(`${apiUrl}/api/project-email/send`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n Authorization: `Bearer ${token}`,\r\n },\r\n body: JSON.stringify({\r\n projectId,\r\n to,\r\n subject,\r\n html,\r\n from: options.from,\r\n }),\r\n });\r\n\r\n const data = await res.json();\r\n\r\n if (!res.ok) {\r\n return { success: false, error: data.error || `HTTP ${res.status}` };\r\n }\r\n\r\n return { success: true, id: data.id };\r\n } catch (err) {\r\n return { success: false, error: err instanceof Error ? err.message : 'Network error' };\r\n }\r\n}\r\n"],"mappings":";;;;;;AAaA,eAAsB,UACpB,IACA,SACA,MACA,UAA4B,CAAC,GACH;AAC1B,QAAM,SAAS,IAAI;AACnB,QAAM,YAAY,IAAI;AAEtB,MAAI,CAAC,UAAU,CAAC,WAAW;AACzB,WAAO,EAAE,SAAS,OAAO,OAAO,kDAAkD;AAAA,EACpF;AAEA,QAAM,UAAU,MAAM,SAAS,KAAK,WAAW;AAC/C,QAAM,QAAQ,QAAQ,KAAK,SAAS;AAEpC,MAAI,CAAC,OAAO;AACV,WAAO,EAAE,SAAS,OAAO,OAAO,0BAA0B;AAAA,EAC5D;AAEA,MAAI;AACF,UAAM,MAAM,MAAM,MAAM,GAAG,MAAM,2BAA2B;AAAA,MAC1D,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,eAAe,UAAU,KAAK;AAAA,MAChC;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,MAAM,QAAQ;AAAA,MAChB,CAAC;AAAA,IACH,CAAC;AAED,UAAM,OAAO,MAAM,IAAI,KAAK;AAE5B,QAAI,CAAC,IAAI,IAAI;AACX,aAAO,EAAE,SAAS,OAAO,OAAO,KAAK,SAAS,QAAQ,IAAI,MAAM,GAAG;AAAA,IACrE;AAEA,WAAO,EAAE,SAAS,MAAM,IAAI,KAAK,GAAG;AAAA,EACtC,SAAS,KAAK;AACZ,WAAO,EAAE,SAAS,OAAO,OAAO,eAAe,QAAQ,IAAI,UAAU,gBAAgB;AAAA,EACvF;AACF;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/errors/ErrorBoundary.tsx"],"sourcesContent":["import { Component } from 'react';\nimport type { ErrorInfo, ReactNode } from 'react';\n\ninterface ErrorBoundaryProps {\n children: ReactNode;\n fallback?: ReactNode | ((error: Error) => ReactNode);\n}\n\ninterface ErrorBoundaryState {\n hasError: boolean;\n error: Error | null;\n}\n\nexport class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {\n constructor(props: ErrorBoundaryProps) {\n super(props);\n this.state = { hasError: false, error: null };\n }\n\n static getDerivedStateFromError(error: Error): ErrorBoundaryState {\n return { hasError: true, error };\n }\n\n componentDidCatch(error: Error, errorInfo: ErrorInfo): void {\n void errorInfo;\n void error;\n }\n\n render() {\n if (this.state.hasError && this.state.error) {\n if (typeof this.props.fallback === 'function') {\n return this.props.fallback(this.state.error);\n }\n if (this.props.fallback) {\n return this.props.fallback;\n }\n\n return (\n <div style={{ padding: '40px', textAlign: 'center' }}>\n <h2 style={{ fontSize: '1.5rem', fontWeight: 600, color: '#dc2626', marginBottom: '8px' }}>\n Something went wrong\n </h2>\n <p style={{ color: '#6b7280', marginBottom: '16px' }}>\n {this.state.error.message}\n </p>\n <button\n onClick={() => this.setState({ hasError: false, error: null })}\n style={{\n padding: '8px 16px', backgroundColor: '#3b82f6', color: 'white',\n border: 'none', borderRadius: '6px', cursor: 'pointer', marginRight: '8px',\n }}\n >\n Try Again\n </button>\n <a href=\"/\" style={{ padding: '8px 16px', color: '#3b82f6', textDecoration: 'none' }}>\n Go Home\n </a>\n </div>\n );\n }\n\n return this.props.children;\n }\n}\n\nexport function NotFoundPage() {\n return (\n <div style={{ textAlign: 'center', padding: '80px 20px' }}>\n <h1 style={{ fontSize: '4rem', fontWeight: 700, color: '#d1d5db', marginBottom: '8px' }}>404</h1>\n <h2 style={{ fontSize: '1.5rem', fontWeight: 600, marginBottom: '8px' }}>Page Not Found</h2>\n <p style={{ color: '#6b7280', marginBottom: '24px' }}>The page you&apos;re looking for doesn&apos;t exist.</p>\n <a href=\"/\" style={{ color: '#3b82f6', textDecoration: 'none', fontWeight: 500 }}>Go Home</a>\n </div>\n );\n}\n"],"mappings":";AAAA,SAAS,iBAAiB;AAsClB,SACE,KADF;AAzBD,IAAM,gBAAN,cAA4B,UAAkD;AAAA,EACnF,YAAY,OAA2B;AACrC,UAAM,KAAK;AACX,SAAK,QAAQ,EAAE,UAAU,OAAO,OAAO,KAAK;AAAA,EAC9C;AAAA,EAEA,OAAO,yBAAyB,OAAkC;AAChE,WAAO,EAAE,UAAU,MAAM,MAAM;AAAA,EACjC;AAAA,EAEA,kBAAkB,OAAc,WAA4B;AAC1D,SAAK;AACL,SAAK;AAAA,EACP;AAAA,EAEA,SAAS;AACP,QAAI,KAAK,MAAM,YAAY,KAAK,MAAM,OAAO;AAC3C,UAAI,OAAO,KAAK,MAAM,aAAa,YAAY;AAC7C,eAAO,KAAK,MAAM,SAAS,KAAK,MAAM,KAAK;AAAA,MAC7C;AACA,UAAI,KAAK,MAAM,UAAU;AACvB,eAAO,KAAK,MAAM;AAAA,MACpB;AAEA,aACE,qBAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,WAAW,SAAS,GACjD;AAAA,4BAAC,QAAG,OAAO,EAAE,UAAU,UAAU,YAAY,KAAK,OAAO,WAAW,cAAc,MAAM,GAAG,kCAE3F;AAAA,QACA,oBAAC,OAAE,OAAO,EAAE,OAAO,WAAW,cAAc,OAAO,GAChD,eAAK,MAAM,MAAM,SACpB;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,SAAS,MAAM,KAAK,SAAS,EAAE,UAAU,OAAO,OAAO,KAAK,CAAC;AAAA,YAC7D,OAAO;AAAA,cACL,SAAS;AAAA,cAAY,iBAAiB;AAAA,cAAW,OAAO;AAAA,cACxD,QAAQ;AAAA,cAAQ,cAAc;AAAA,cAAO,QAAQ;AAAA,cAAW,aAAa;AAAA,YACvE;AAAA,YACD;AAAA;AAAA,QAED;AAAA,QACA,oBAAC,OAAE,MAAK,KAAI,OAAO,EAAE,SAAS,YAAY,OAAO,WAAW,gBAAgB,OAAO,GAAG,qBAEtF;AAAA,SACF;AAAA,IAEJ;AAEA,WAAO,KAAK,MAAM;AAAA,EACpB;AACF;AAEO,SAAS,eAAe;AAC7B,SACE,qBAAC,SAAI,OAAO,EAAE,WAAW,UAAU,SAAS,YAAY,GACtD;AAAA,wBAAC,QAAG,OAAO,EAAE,UAAU,QAAQ,YAAY,KAAK,OAAO,WAAW,cAAc,MAAM,GAAG,iBAAG;AAAA,IAC5F,oBAAC,QAAG,OAAO,EAAE,UAAU,UAAU,YAAY,KAAK,cAAc,MAAM,GAAG,4BAAc;AAAA,IACvF,oBAAC,OAAE,OAAO,EAAE,OAAO,WAAW,cAAc,OAAO,GAAG,wDAAoD;AAAA,IAC1G,oBAAC,OAAE,MAAK,KAAI,OAAO,EAAE,OAAO,WAAW,gBAAgB,QAAQ,YAAY,IAAI,GAAG,qBAAO;AAAA,KAC3F;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../src/errors/ErrorBoundary.tsx"],"sourcesContent":["import { Component } from 'react';\r\nimport type { ErrorInfo, ReactNode } from 'react';\r\n\r\ninterface ErrorBoundaryProps {\r\n children: ReactNode;\r\n fallback?: ReactNode | ((error: Error) => ReactNode);\r\n}\r\n\r\ninterface ErrorBoundaryState {\r\n hasError: boolean;\r\n error: Error | null;\r\n}\r\n\r\nexport class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {\r\n constructor(props: ErrorBoundaryProps) {\r\n super(props);\r\n this.state = { hasError: false, error: null };\r\n }\r\n\r\n static getDerivedStateFromError(error: Error): ErrorBoundaryState {\r\n return { hasError: true, error };\r\n }\r\n\r\n componentDidCatch(error: Error, errorInfo: ErrorInfo): void {\r\n void errorInfo;\r\n void error;\r\n }\r\n\r\n render() {\r\n if (this.state.hasError && this.state.error) {\r\n if (typeof this.props.fallback === 'function') {\r\n return this.props.fallback(this.state.error);\r\n }\r\n if (this.props.fallback) {\r\n return this.props.fallback;\r\n }\r\n\r\n return (\r\n <div style={{ padding: '40px', textAlign: 'center' }}>\r\n <h2 style={{ fontSize: '1.5rem', fontWeight: 600, color: '#dc2626', marginBottom: '8px' }}>\r\n Something went wrong\r\n </h2>\r\n <p style={{ color: '#6b7280', marginBottom: '16px' }}>\r\n {this.state.error.message}\r\n </p>\r\n <button\r\n onClick={() => this.setState({ hasError: false, error: null })}\r\n style={{\r\n padding: '8px 16px', backgroundColor: '#3b82f6', color: 'white',\r\n border: 'none', borderRadius: '6px', cursor: 'pointer', marginRight: '8px',\r\n }}\r\n >\r\n Try Again\r\n </button>\r\n <a href=\"/\" style={{ padding: '8px 16px', color: '#3b82f6', textDecoration: 'none' }}>\r\n Go Home\r\n </a>\r\n </div>\r\n );\r\n }\r\n\r\n return this.props.children;\r\n }\r\n}\r\n\r\nexport function NotFoundPage() {\r\n return (\r\n <div style={{ textAlign: 'center', padding: '80px 20px' }}>\r\n <h1 style={{ fontSize: '4rem', fontWeight: 700, color: '#d1d5db', marginBottom: '8px' }}>404</h1>\r\n <h2 style={{ fontSize: '1.5rem', fontWeight: 600, marginBottom: '8px' }}>Page Not Found</h2>\r\n <p style={{ color: '#6b7280', marginBottom: '24px' }}>The page you&apos;re looking for doesn&apos;t exist.</p>\r\n <a href=\"/\" style={{ color: '#3b82f6', textDecoration: 'none', fontWeight: 500 }}>Go Home</a>\r\n </div>\r\n );\r\n}\r\n"],"mappings":";AAAA,SAAS,iBAAiB;AAsClB,SACE,KADF;AAzBD,IAAM,gBAAN,cAA4B,UAAkD;AAAA,EACnF,YAAY,OAA2B;AACrC,UAAM,KAAK;AACX,SAAK,QAAQ,EAAE,UAAU,OAAO,OAAO,KAAK;AAAA,EAC9C;AAAA,EAEA,OAAO,yBAAyB,OAAkC;AAChE,WAAO,EAAE,UAAU,MAAM,MAAM;AAAA,EACjC;AAAA,EAEA,kBAAkB,OAAc,WAA4B;AAC1D,SAAK;AACL,SAAK;AAAA,EACP;AAAA,EAEA,SAAS;AACP,QAAI,KAAK,MAAM,YAAY,KAAK,MAAM,OAAO;AAC3C,UAAI,OAAO,KAAK,MAAM,aAAa,YAAY;AAC7C,eAAO,KAAK,MAAM,SAAS,KAAK,MAAM,KAAK;AAAA,MAC7C;AACA,UAAI,KAAK,MAAM,UAAU;AACvB,eAAO,KAAK,MAAM;AAAA,MACpB;AAEA,aACE,qBAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,WAAW,SAAS,GACjD;AAAA,4BAAC,QAAG,OAAO,EAAE,UAAU,UAAU,YAAY,KAAK,OAAO,WAAW,cAAc,MAAM,GAAG,kCAE3F;AAAA,QACA,oBAAC,OAAE,OAAO,EAAE,OAAO,WAAW,cAAc,OAAO,GAChD,eAAK,MAAM,MAAM,SACpB;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,SAAS,MAAM,KAAK,SAAS,EAAE,UAAU,OAAO,OAAO,KAAK,CAAC;AAAA,YAC7D,OAAO;AAAA,cACL,SAAS;AAAA,cAAY,iBAAiB;AAAA,cAAW,OAAO;AAAA,cACxD,QAAQ;AAAA,cAAQ,cAAc;AAAA,cAAO,QAAQ;AAAA,cAAW,aAAa;AAAA,YACvE;AAAA,YACD;AAAA;AAAA,QAED;AAAA,QACA,oBAAC,OAAE,MAAK,KAAI,OAAO,EAAE,SAAS,YAAY,OAAO,WAAW,gBAAgB,OAAO,GAAG,qBAEtF;AAAA,SACF;AAAA,IAEJ;AAEA,WAAO,KAAK,MAAM;AAAA,EACpB;AACF;AAEO,SAAS,eAAe;AAC7B,SACE,qBAAC,SAAI,OAAO,EAAE,WAAW,UAAU,SAAS,YAAY,GACtD;AAAA,wBAAC,QAAG,OAAO,EAAE,UAAU,QAAQ,YAAY,KAAK,OAAO,WAAW,cAAc,MAAM,GAAG,iBAAG;AAAA,IAC5F,oBAAC,QAAG,OAAO,EAAE,UAAU,UAAU,YAAY,KAAK,cAAc,MAAM,GAAG,4BAAc;AAAA,IACvF,oBAAC,OAAE,OAAO,EAAE,OAAO,WAAW,cAAc,OAAO,GAAG,wDAAoD;AAAA,IAC1G,oBAAC,OAAE,MAAK,KAAI,OAAO,EAAE,OAAO,WAAW,gBAAgB,QAAQ,YAAY,IAAI,GAAG,qBAAO;AAAA,KAC3F;AAEJ;","names":[]}
package/dist/index.d.ts CHANGED
@@ -1,16 +1,23 @@
1
1
  import { SupabaseClient } from '@supabase/supabase-js';
2
- import { E as EzcoderClient, A as AuthIntegration } from './types-DtY5lp3P.js';
3
- export { a as AnalyticsResult, b as AuthUser, C as CheckoutOptions, S as StorageFile, c as StorageResult, d as SubscriptionStatus, e as SubscriptionTier, U as UserProfile } from './types-DtY5lp3P.js';
2
+ import { E as EzcoderClient, A as AuthIntegration } from './types-1uP3V_pe.js';
3
+ export { a as AnalyticsResult, b as AuthUser, C as CheckoutOptions, S as StorageFile, c as StorageResult, d as SubscriptionStatus, e as SubscriptionTier, U as UserProfile } from './types-1uP3V_pe.js';
4
+ export { D as DatabaseClient, a as DatabaseProvider, u as useDatabase } from './DatabaseProvider-DalP-KHC.js';
5
+ import 'react/jsx-runtime';
6
+ import 'react';
4
7
 
5
8
  declare const env: {
6
9
  SUPABASE_URL: string;
7
10
  SUPABASE_ANON_KEY: string;
11
+ EZCODER_AUTH_URL: string;
12
+ EZCODER_AUTH_ANON_KEY: string;
8
13
  STRIPE_PUBLISHABLE_KEY: string;
9
14
  EZC_PROJECT_ID: string;
10
15
  EZCODER_API_URL: string;
11
16
  EZC_SECRET_KEY: string;
12
17
  S3_BUCKET: string;
13
18
  CLOUDINARY_URL: string;
19
+ STORAGE_BUCKET_PUBLIC: string;
20
+ STORAGE_BUCKET_PRIVATE: string;
14
21
  };
15
22
  declare const features: {
16
23
  auth: boolean;
package/dist/index.js CHANGED
@@ -1,21 +1,29 @@
1
+ import {
2
+ DatabaseClient,
3
+ DatabaseProvider,
4
+ useDatabase
5
+ } from "./chunk-7VGYFCQC.js";
1
6
  import {
2
7
  ezcoder,
3
8
  ezcoderAuthIntegration
4
- } from "./chunk-5XIZHBKE.js";
9
+ } from "./chunk-GPF4AYNG.js";
5
10
  import {
6
11
  env,
7
12
  features,
8
13
  isFeatureConfigured,
9
14
  isSupabaseConfigured,
10
15
  supabase
11
- } from "./chunk-G7XDUN3Z.js";
16
+ } from "./chunk-2WG4O4J2.js";
12
17
  export {
18
+ DatabaseClient,
19
+ DatabaseProvider,
13
20
  env,
14
21
  ezcoder,
15
22
  ezcoderAuthIntegration,
16
23
  features,
17
24
  isFeatureConfigured,
18
25
  isSupabaseConfigured,
19
- supabase
26
+ supabase,
27
+ useDatabase
20
28
  };
21
29
  //# sourceMappingURL=index.js.map
@@ -27,4 +27,13 @@ interface NotificationCenterProps {
27
27
  }
28
28
  declare function NotificationCenter({ className }: NotificationCenterProps): react_jsx_runtime.JSX.Element;
29
29
 
30
- export { NotificationCenter, useNotifications };
30
+ interface SendNotificationOptions {
31
+ type?: 'info' | 'success' | 'warning' | 'error' | 'system';
32
+ data?: Record<string, unknown>;
33
+ }
34
+ declare function sendNotification(userId: string, title: string, message: string, options?: SendNotificationOptions): Promise<{
35
+ success: boolean;
36
+ error?: string;
37
+ }>;
38
+
39
+ export { NotificationCenter, sendNotification, useNotifications };
@@ -1,11 +1,12 @@
1
1
  import {
2
2
  AuthContext
3
- } from "../chunk-YNDCD53D.js";
4
- import "../chunk-5XIZHBKE.js";
3
+ } from "../chunk-AWU47M6N.js";
4
+ import "../chunk-GPF4AYNG.js";
5
5
  import {
6
+ env,
6
7
  features,
7
8
  supabase
8
- } from "../chunk-G7XDUN3Z.js";
9
+ } from "../chunk-2WG4O4J2.js";
9
10
 
10
11
  // src/notifications/useNotifications.ts
11
12
  import { useState, useEffect, useCallback, useContext } from "react";
@@ -184,8 +185,29 @@ function NotificationCenter({ className = "" }) {
184
185
  ] })
185
186
  ] });
186
187
  }
188
+
189
+ // src/notifications/sendNotification.ts
190
+ async function sendNotification(userId, title, message, options = {}) {
191
+ const projectId = env.EZC_PROJECT_ID;
192
+ if (!projectId) {
193
+ return { success: false, error: "EZC_PROJECT_ID not configured" };
194
+ }
195
+ const { data, error } = await supabase.rpc("sdk_create_notification", {
196
+ p_project_id: projectId,
197
+ p_user_id: userId,
198
+ p_title: title,
199
+ p_message: message,
200
+ p_type: options.type || "info",
201
+ p_data: options.data || {}
202
+ });
203
+ if (error) {
204
+ return { success: false, error: error.message };
205
+ }
206
+ return data || { success: true };
207
+ }
187
208
  export {
188
209
  NotificationCenter,
210
+ sendNotification,
189
211
  useNotifications
190
212
  };
191
213
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/notifications/useNotifications.ts","../../src/notifications/NotificationCenter.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 Notification {\n id: string;\n type: string;\n title: string;\n message: string;\n data?: Record<string, unknown>;\n read: boolean;\n read_at?: string;\n created_at: string;\n}\n\nconst DEMO_NOTIFICATIONS: Notification[] = [\n {\n id: 'demo-1',\n type: 'info',\n title: 'Welcome!',\n message: 'Connect a database to enable real-time notifications.',\n read: false,\n created_at: new Date().toISOString(),\n },\n];\n\ninterface UseNotificationsReturn {\n notifications: Notification[];\n unreadCount: number;\n loading: boolean;\n isConfigured: boolean;\n markAsRead: (notificationId: string) => Promise<void>;\n markAllAsRead: () => Promise<void>;\n deleteNotification: (notificationId: string) => Promise<void>;\n refetch: () => Promise<void>;\n}\n\nexport function useNotifications(): UseNotificationsReturn {\n const auth = useContext(AuthContext);\n const [notifications, setNotifications] = useState<Notification[]>([]);\n const [loading, setLoading] = useState(true);\n\n const userId = auth?.user?.id;\n\n const fetchNotifications = useCallback(async () => {\n if (!features.auth || !userId) {\n setNotifications(DEMO_NOTIFICATIONS);\n setLoading(false);\n return;\n }\n\n try {\n const { data, error } = await supabase\n .from('notifications')\n .select('*')\n .eq('user_id', userId)\n .order('created_at', { ascending: false })\n .limit(50);\n\n if (error) {\n setNotifications(DEMO_NOTIFICATIONS);\n } else {\n setNotifications((data as Notification[]) || []);\n }\n } catch {\n setNotifications(DEMO_NOTIFICATIONS);\n } finally {\n setLoading(false);\n }\n }, [userId]);\n\n useEffect(() => {\n fetchNotifications();\n }, [fetchNotifications]);\n\n useEffect(() => {\n if (!features.auth || !userId) return;\n\n const channel = supabase\n .channel(`notifications_${userId}`)\n .on(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n 'postgres_changes' as any,\n {\n event: 'INSERT',\n schema: 'public',\n table: 'notifications',\n filter: `user_id=eq.${userId}`,\n },\n (payload: { new: Notification }) => {\n setNotifications((prev) => [payload.new, ...prev]);\n }\n )\n .subscribe();\n\n return () => {\n supabase.removeChannel(channel);\n };\n }, [userId]);\n\n const markAsRead = useCallback(async (notificationId: string) => {\n if (!features.auth) return;\n\n await supabase\n .from('notifications')\n .update({ read: true, read_at: new Date().toISOString() })\n .eq('id', notificationId);\n\n setNotifications((prev) =>\n prev.map((n) => (n.id === notificationId ? { ...n, read: true } : n))\n );\n }, []);\n\n const markAllAsRead = useCallback(async () => {\n if (!features.auth || !userId) return;\n\n await supabase\n .from('notifications')\n .update({ read: true, read_at: new Date().toISOString() })\n .eq('user_id', userId)\n .eq('read', false);\n\n setNotifications((prev) => prev.map((n) => ({ ...n, read: true })));\n }, [userId]);\n\n const deleteNotification = useCallback(async (notificationId: string) => {\n if (!features.auth) return;\n\n await supabase.from('notifications').delete().eq('id', notificationId);\n setNotifications((prev) => prev.filter((n) => n.id !== notificationId));\n }, []);\n\n const unreadCount = notifications.filter((n) => !n.read).length;\n\n return {\n notifications,\n unreadCount,\n loading,\n isConfigured: features.auth,\n markAsRead,\n markAllAsRead,\n deleteNotification,\n refetch: fetchNotifications,\n };\n}\n","import { useState } from 'react';\nimport { useNotifications } from './useNotifications';\n\ninterface NotificationCenterProps {\n className?: string;\n}\n\nexport function NotificationCenter({ className = '' }: NotificationCenterProps) {\n const { notifications, unreadCount, markAsRead, markAllAsRead, deleteNotification } = useNotifications();\n const [open, setOpen] = useState(false);\n\n return (\n <div className={className} style={{ position: 'relative', display: 'inline-block' }}>\n <button\n onClick={() => setOpen(!open)}\n style={{\n background: 'none', border: 'none', cursor: 'pointer',\n position: 'relative', padding: '8px', fontSize: '20px',\n }}\n >\n 🔔\n {unreadCount > 0 && (\n <span style={{\n position: 'absolute', top: '2px', right: '2px',\n backgroundColor: '#dc2626', color: 'white', fontSize: '10px',\n borderRadius: '50%', width: '18px', height: '18px',\n display: 'flex', alignItems: 'center', justifyContent: 'center',\n }}>\n {unreadCount > 9 ? '9+' : unreadCount}\n </span>\n )}\n </button>\n\n {open && (\n <div style={{\n position: 'absolute', right: 0, top: '100%', width: '320px',\n backgroundColor: 'white', border: '1px solid #e5e7eb',\n borderRadius: '8px', boxShadow: '0 4px 12px rgba(0,0,0,0.1)',\n zIndex: 50, maxHeight: '400px', overflow: 'auto',\n }}>\n <div style={{ padding: '12px 16px', borderBottom: '1px solid #e5e7eb', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>\n <span style={{ fontWeight: 600 }}>Notifications</span>\n {unreadCount > 0 && (\n <button onClick={() => markAllAsRead()} style={{ background: 'none', border: 'none', color: '#3b82f6', cursor: 'pointer', fontSize: '12px' }}>\n Mark all read\n </button>\n )}\n </div>\n\n {notifications.length === 0 ? (\n <div style={{ padding: '24px', textAlign: 'center', color: '#9ca3af' }}>\n No notifications\n </div>\n ) : (\n notifications.map((n) => (\n <div\n key={n.id}\n style={{\n padding: '12px 16px', borderBottom: '1px solid #f3f4f6',\n backgroundColor: n.read ? 'transparent' : '#eff6ff',\n cursor: 'pointer',\n }}\n onClick={() => !n.read && markAsRead(n.id)}\n >\n <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>\n <div>\n <p style={{ fontWeight: 500, fontSize: '14px', marginBottom: '2px' }}>{n.title}</p>\n <p style={{ color: '#6b7280', fontSize: '13px' }}>{n.message}</p>\n <p style={{ color: '#9ca3af', fontSize: '11px', marginTop: '4px' }}>\n {new Date(n.created_at).toLocaleDateString()}\n </p>\n </div>\n <button\n onClick={(e) => { e.stopPropagation(); deleteNotification(n.id); }}\n style={{ background: 'none', border: 'none', color: '#9ca3af', cursor: 'pointer', fontSize: '16px' }}\n >\n ×\n </button>\n </div>\n </div>\n ))\n )}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":["useState","useState"]}
1
+ {"version":3,"sources":["../../src/notifications/useNotifications.ts","../../src/notifications/NotificationCenter.tsx","../../src/notifications/sendNotification.ts"],"sourcesContent":["import { useState, useEffect, useCallback, useContext } from 'react';\r\nimport { supabase } from '../core/supabase';\r\nimport { features } from '../core/config';\r\nimport { AuthContext } from '../auth/AuthProvider';\r\n\r\ninterface Notification {\r\n id: string;\r\n type: string;\r\n title: string;\r\n message: string;\r\n data?: Record<string, unknown>;\r\n read: boolean;\r\n read_at?: string;\r\n created_at: string;\r\n}\r\n\r\nconst DEMO_NOTIFICATIONS: Notification[] = [\r\n {\r\n id: 'demo-1',\r\n type: 'info',\r\n title: 'Welcome!',\r\n message: 'Connect a database to enable real-time notifications.',\r\n read: false,\r\n created_at: new Date().toISOString(),\r\n },\r\n];\r\n\r\ninterface UseNotificationsReturn {\r\n notifications: Notification[];\r\n unreadCount: number;\r\n loading: boolean;\r\n isConfigured: boolean;\r\n markAsRead: (notificationId: string) => Promise<void>;\r\n markAllAsRead: () => Promise<void>;\r\n deleteNotification: (notificationId: string) => Promise<void>;\r\n refetch: () => Promise<void>;\r\n}\r\n\r\nexport function useNotifications(): UseNotificationsReturn {\r\n const auth = useContext(AuthContext);\r\n const [notifications, setNotifications] = useState<Notification[]>([]);\r\n const [loading, setLoading] = useState(true);\r\n\r\n const userId = auth?.user?.id;\r\n\r\n const fetchNotifications = useCallback(async () => {\r\n if (!features.auth || !userId) {\r\n setNotifications(DEMO_NOTIFICATIONS);\r\n setLoading(false);\r\n return;\r\n }\r\n\r\n try {\r\n const { data, error } = await supabase\r\n .from('notifications')\r\n .select('*')\r\n .eq('user_id', userId)\r\n .order('created_at', { ascending: false })\r\n .limit(50);\r\n\r\n if (error) {\r\n setNotifications(DEMO_NOTIFICATIONS);\r\n } else {\r\n setNotifications((data as Notification[]) || []);\r\n }\r\n } catch {\r\n setNotifications(DEMO_NOTIFICATIONS);\r\n } finally {\r\n setLoading(false);\r\n }\r\n }, [userId]);\r\n\r\n useEffect(() => {\r\n fetchNotifications();\r\n }, [fetchNotifications]);\r\n\r\n useEffect(() => {\r\n if (!features.auth || !userId) return;\r\n\r\n const channel = supabase\r\n .channel(`notifications_${userId}`)\r\n .on(\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n 'postgres_changes' as any,\r\n {\r\n event: 'INSERT',\r\n schema: 'public',\r\n table: 'notifications',\r\n filter: `user_id=eq.${userId}`,\r\n },\r\n (payload: { new: Notification }) => {\r\n setNotifications((prev) => [payload.new, ...prev]);\r\n }\r\n )\r\n .subscribe();\r\n\r\n return () => {\r\n supabase.removeChannel(channel);\r\n };\r\n }, [userId]);\r\n\r\n const markAsRead = useCallback(async (notificationId: string) => {\r\n if (!features.auth) return;\r\n\r\n await supabase\r\n .from('notifications')\r\n .update({ read: true, read_at: new Date().toISOString() })\r\n .eq('id', notificationId);\r\n\r\n setNotifications((prev) =>\r\n prev.map((n) => (n.id === notificationId ? { ...n, read: true } : n))\r\n );\r\n }, []);\r\n\r\n const markAllAsRead = useCallback(async () => {\r\n if (!features.auth || !userId) return;\r\n\r\n await supabase\r\n .from('notifications')\r\n .update({ read: true, read_at: new Date().toISOString() })\r\n .eq('user_id', userId)\r\n .eq('read', false);\r\n\r\n setNotifications((prev) => prev.map((n) => ({ ...n, read: true })));\r\n }, [userId]);\r\n\r\n const deleteNotification = useCallback(async (notificationId: string) => {\r\n if (!features.auth) return;\r\n\r\n await supabase.from('notifications').delete().eq('id', notificationId);\r\n setNotifications((prev) => prev.filter((n) => n.id !== notificationId));\r\n }, []);\r\n\r\n const unreadCount = notifications.filter((n) => !n.read).length;\r\n\r\n return {\r\n notifications,\r\n unreadCount,\r\n loading,\r\n isConfigured: features.auth,\r\n markAsRead,\r\n markAllAsRead,\r\n deleteNotification,\r\n refetch: fetchNotifications,\r\n };\r\n}\r\n","import { useState } from 'react';\r\nimport { useNotifications } from './useNotifications';\r\n\r\ninterface NotificationCenterProps {\r\n className?: string;\r\n}\r\n\r\nexport function NotificationCenter({ className = '' }: NotificationCenterProps) {\r\n const { notifications, unreadCount, markAsRead, markAllAsRead, deleteNotification } = useNotifications();\r\n const [open, setOpen] = useState(false);\r\n\r\n return (\r\n <div className={className} style={{ position: 'relative', display: 'inline-block' }}>\r\n <button\r\n onClick={() => setOpen(!open)}\r\n style={{\r\n background: 'none', border: 'none', cursor: 'pointer',\r\n position: 'relative', padding: '8px', fontSize: '20px',\r\n }}\r\n >\r\n 🔔\r\n {unreadCount > 0 && (\r\n <span style={{\r\n position: 'absolute', top: '2px', right: '2px',\r\n backgroundColor: '#dc2626', color: 'white', fontSize: '10px',\r\n borderRadius: '50%', width: '18px', height: '18px',\r\n display: 'flex', alignItems: 'center', justifyContent: 'center',\r\n }}>\r\n {unreadCount > 9 ? '9+' : unreadCount}\r\n </span>\r\n )}\r\n </button>\r\n\r\n {open && (\r\n <div style={{\r\n position: 'absolute', right: 0, top: '100%', width: '320px',\r\n backgroundColor: 'white', border: '1px solid #e5e7eb',\r\n borderRadius: '8px', boxShadow: '0 4px 12px rgba(0,0,0,0.1)',\r\n zIndex: 50, maxHeight: '400px', overflow: 'auto',\r\n }}>\r\n <div style={{ padding: '12px 16px', borderBottom: '1px solid #e5e7eb', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>\r\n <span style={{ fontWeight: 600 }}>Notifications</span>\r\n {unreadCount > 0 && (\r\n <button onClick={() => markAllAsRead()} style={{ background: 'none', border: 'none', color: '#3b82f6', cursor: 'pointer', fontSize: '12px' }}>\r\n Mark all read\r\n </button>\r\n )}\r\n </div>\r\n\r\n {notifications.length === 0 ? (\r\n <div style={{ padding: '24px', textAlign: 'center', color: '#9ca3af' }}>\r\n No notifications\r\n </div>\r\n ) : (\r\n notifications.map((n) => (\r\n <div\r\n key={n.id}\r\n style={{\r\n padding: '12px 16px', borderBottom: '1px solid #f3f4f6',\r\n backgroundColor: n.read ? 'transparent' : '#eff6ff',\r\n cursor: 'pointer',\r\n }}\r\n onClick={() => !n.read && markAsRead(n.id)}\r\n >\r\n <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>\r\n <div>\r\n <p style={{ fontWeight: 500, fontSize: '14px', marginBottom: '2px' }}>{n.title}</p>\r\n <p style={{ color: '#6b7280', fontSize: '13px' }}>{n.message}</p>\r\n <p style={{ color: '#9ca3af', fontSize: '11px', marginTop: '4px' }}>\r\n {new Date(n.created_at).toLocaleDateString()}\r\n </p>\r\n </div>\r\n <button\r\n onClick={(e) => { e.stopPropagation(); deleteNotification(n.id); }}\r\n style={{ background: 'none', border: 'none', color: '#9ca3af', cursor: 'pointer', fontSize: '16px' }}\r\n >\r\n ×\r\n </button>\r\n </div>\r\n </div>\r\n ))\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n );\r\n}\r\n","import { supabase } from '../core/supabase';\r\nimport { env } from '../core/config';\r\n\r\ninterface SendNotificationOptions {\r\n type?: 'info' | 'success' | 'warning' | 'error' | 'system';\r\n data?: Record<string, unknown>;\r\n}\r\n\r\nexport async function sendNotification(\r\n userId: string,\r\n title: string,\r\n message: string,\r\n options: SendNotificationOptions = {},\r\n): Promise<{ success: boolean; error?: string }> {\r\n const projectId = env.EZC_PROJECT_ID;\r\n if (!projectId) {\r\n return { success: false, error: 'EZC_PROJECT_ID not configured' };\r\n }\r\n\r\n const { data, error } = await supabase.rpc('sdk_create_notification', {\r\n p_project_id: projectId,\r\n p_user_id: userId,\r\n p_title: title,\r\n p_message: message,\r\n p_type: options.type || 'info',\r\n p_data: options.data || {},\r\n });\r\n\r\n if (error) {\r\n return { success: false, error: error.message };\r\n }\r\n\r\n return (data as { success: boolean; error?: string }) || { success: true };\r\n}\r\n"],"mappings":"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","names":["useState","useState"]}
@@ -1,4 +1,4 @@
1
- import { e as SubscriptionTier, d as SubscriptionStatus } from '../types-DtY5lp3P.js';
1
+ import { e as SubscriptionTier, d as SubscriptionStatus } from '../types-1uP3V_pe.js';
2
2
  import * as react_jsx_runtime from 'react/jsx-runtime';
3
3
 
4
4
  interface SubscriptionState {
@@ -83,7 +83,15 @@ interface ProtectedContentProps {
83
83
  children: React.ReactNode;
84
84
  fallback?: React.ReactNode;
85
85
  loadingComponent?: React.ReactNode;
86
+ requiredTier?: SubscriptionTier;
86
87
  }
87
- declare function ProtectedContent({ children, fallback, loadingComponent }: ProtectedContentProps): react_jsx_runtime.JSX.Element;
88
+ declare function ProtectedContent({ children, fallback, loadingComponent, requiredTier }: ProtectedContentProps): react_jsx_runtime.JSX.Element;
88
89
 
89
- export { BuyButton, ManageSubscriptionButton, PricingTable, ProtectedContent, SubscriptionManager, useCustomerAccess, useSubscription };
90
+ interface InvoiceHistoryProps {
91
+ customerId?: string;
92
+ limit?: number;
93
+ className?: string;
94
+ }
95
+ declare function InvoiceHistory({ customerId, limit, className }: InvoiceHistoryProps): react_jsx_runtime.JSX.Element;
96
+
97
+ export { BuyButton, InvoiceHistory, ManageSubscriptionButton, PricingTable, ProtectedContent, SubscriptionManager, useCustomerAccess, useSubscription };
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  AuthContext
3
- } from "../chunk-YNDCD53D.js";
3
+ } from "../chunk-AWU47M6N.js";
4
4
  import {
5
5
  ezcoder
6
- } from "../chunk-5XIZHBKE.js";
6
+ } from "../chunk-GPF4AYNG.js";
7
7
  import {
8
8
  features,
9
9
  supabase
10
- } from "../chunk-G7XDUN3Z.js";
10
+ } from "../chunk-2WG4O4J2.js";
11
11
 
12
12
  // src/payments/useSubscription.ts
13
13
  import { useState, useEffect, useCallback, useContext } from "react";
@@ -386,18 +386,110 @@ function SubscriptionManager({ className = "" }) {
386
386
 
387
387
  // src/payments/ProtectedContent.tsx
388
388
  import { Fragment as Fragment2, jsx as jsx5 } from "react/jsx-runtime";
389
- function ProtectedContent({ children, fallback, loadingComponent }) {
390
- const { loading, hasAccess } = useCustomerAccess();
389
+ function ProtectedContent({ children, fallback, loadingComponent, requiredTier }) {
390
+ const customerAccess = useCustomerAccess();
391
+ const subscription = useSubscription();
392
+ const loading = requiredTier ? subscription.loading : customerAccess.loading;
391
393
  if (loading) {
392
394
  return /* @__PURE__ */ jsx5(Fragment2, { children: loadingComponent || /* @__PURE__ */ jsx5("div", { style: { padding: "20px", textAlign: "center", color: "#6b7280" }, children: "Loading..." }) });
393
395
  }
396
+ const hasAccess = requiredTier ? subscription.isActive && subscription.canAccess(requiredTier) : customerAccess.hasAccess;
394
397
  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." }) });
398
+ return /* @__PURE__ */ jsx5(Fragment2, { children: fallback || /* @__PURE__ */ jsx5("div", { style: { padding: "20px", textAlign: "center", color: "#6b7280" }, children: requiredTier ? `This content requires a ${requiredTier} subscription or higher.` : "This content requires a subscription." }) });
396
399
  }
397
400
  return /* @__PURE__ */ jsx5(Fragment2, { children });
398
401
  }
402
+
403
+ // src/payments/InvoiceHistory.tsx
404
+ import { useState as useState6, useEffect as useEffect3, useCallback as useCallback3 } from "react";
405
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
406
+ function InvoiceHistory({ customerId, limit = 10, className = "" }) {
407
+ const [invoices, setInvoices] = useState6([]);
408
+ const [loading, setLoading] = useState6(true);
409
+ const [error, setError] = useState6(null);
410
+ const fetchInvoices = useCallback3(async () => {
411
+ const id = customerId || (typeof localStorage !== "undefined" ? localStorage.getItem("stripeCustomerId") : null);
412
+ if (!id) {
413
+ setLoading(false);
414
+ return;
415
+ }
416
+ setLoading(true);
417
+ setError(null);
418
+ try {
419
+ const result = await ezcoder.stripe.getInvoices(id, { limit });
420
+ if (result.success && result.invoices) {
421
+ setInvoices(result.invoices);
422
+ } else {
423
+ setError(result.error || "Failed to load invoices");
424
+ }
425
+ } catch (err) {
426
+ setError(err instanceof Error ? err.message : "Unknown error");
427
+ } finally {
428
+ setLoading(false);
429
+ }
430
+ }, [customerId, limit]);
431
+ useEffect3(() => {
432
+ fetchInvoices();
433
+ }, [fetchInvoices]);
434
+ if (loading) {
435
+ return /* @__PURE__ */ jsx6("div", { style: { padding: "20px", color: "#6b7280" }, children: "Loading invoices..." });
436
+ }
437
+ if (error) {
438
+ return /* @__PURE__ */ jsx6("div", { style: { padding: "20px", color: "#dc2626" }, children: error });
439
+ }
440
+ if (invoices.length === 0) {
441
+ return /* @__PURE__ */ jsx6("div", { style: { padding: "20px", color: "#6b7280" }, children: "No invoices found." });
442
+ }
443
+ const formatAmount = (amount, currency) => {
444
+ return new Intl.NumberFormat("en-US", { style: "currency", currency }).format(amount / 100);
445
+ };
446
+ const STATUS_COLORS2 = {
447
+ paid: "#059669",
448
+ open: "#3b82f6",
449
+ draft: "#6b7280",
450
+ void: "#9ca3af",
451
+ uncollectible: "#dc2626"
452
+ };
453
+ return /* @__PURE__ */ jsxs4("div", { className, style: { border: "1px solid #e5e7eb", borderRadius: "12px", overflow: "hidden" }, children: [
454
+ /* @__PURE__ */ jsx6("div", { style: { padding: "16px 24px", borderBottom: "1px solid #e5e7eb" }, children: /* @__PURE__ */ jsx6("h3", { style: { fontSize: "1.125rem", fontWeight: 600, margin: 0 }, children: "Invoice History" }) }),
455
+ /* @__PURE__ */ jsxs4("table", { style: { width: "100%", borderCollapse: "collapse", fontSize: "14px" }, children: [
456
+ /* @__PURE__ */ jsx6("thead", { children: /* @__PURE__ */ jsxs4("tr", { style: { borderBottom: "1px solid #e5e7eb", background: "#f9fafb" }, children: [
457
+ /* @__PURE__ */ jsx6("th", { style: { padding: "10px 16px", textAlign: "left", fontWeight: 500, color: "#6b7280" }, children: "Invoice" }),
458
+ /* @__PURE__ */ jsx6("th", { style: { padding: "10px 16px", textAlign: "left", fontWeight: 500, color: "#6b7280" }, children: "Date" }),
459
+ /* @__PURE__ */ jsx6("th", { style: { padding: "10px 16px", textAlign: "right", fontWeight: 500, color: "#6b7280" }, children: "Amount" }),
460
+ /* @__PURE__ */ jsx6("th", { style: { padding: "10px 16px", textAlign: "center", fontWeight: 500, color: "#6b7280" }, children: "Status" }),
461
+ /* @__PURE__ */ jsx6("th", { style: { padding: "10px 16px", textAlign: "right", fontWeight: 500, color: "#6b7280" } })
462
+ ] }) }),
463
+ /* @__PURE__ */ jsx6("tbody", { children: invoices.map((invoice) => /* @__PURE__ */ jsxs4("tr", { style: { borderBottom: "1px solid #f3f4f6" }, children: [
464
+ /* @__PURE__ */ jsx6("td", { style: { padding: "12px 16px" }, children: invoice.number || invoice.id.slice(0, 12) }),
465
+ /* @__PURE__ */ jsx6("td", { style: { padding: "12px 16px", color: "#6b7280" }, children: new Date(invoice.created * 1e3).toLocaleDateString() }),
466
+ /* @__PURE__ */ jsx6("td", { style: { padding: "12px 16px", textAlign: "right", fontWeight: 500 }, children: formatAmount(invoice.amount, invoice.currency) }),
467
+ /* @__PURE__ */ jsx6("td", { style: { padding: "12px 16px", textAlign: "center" }, children: /* @__PURE__ */ jsx6("span", { style: {
468
+ padding: "2px 8px",
469
+ borderRadius: "9999px",
470
+ fontSize: "12px",
471
+ fontWeight: 500,
472
+ color: STATUS_COLORS2[invoice.status] || "#6b7280",
473
+ background: `${STATUS_COLORS2[invoice.status] || "#6b7280"}15`,
474
+ textTransform: "capitalize"
475
+ }, children: invoice.status }) }),
476
+ /* @__PURE__ */ jsx6("td", { style: { padding: "12px 16px", textAlign: "right" }, children: invoice.pdfUrl && /* @__PURE__ */ jsx6(
477
+ "a",
478
+ {
479
+ href: invoice.pdfUrl,
480
+ target: "_blank",
481
+ rel: "noopener noreferrer",
482
+ style: { color: "#3b82f6", textDecoration: "none", fontSize: "13px" },
483
+ children: "PDF"
484
+ }
485
+ ) })
486
+ ] }, invoice.id)) })
487
+ ] })
488
+ ] });
489
+ }
399
490
  export {
400
491
  BuyButton,
492
+ InvoiceHistory,
401
493
  ManageSubscriptionButton,
402
494
  PricingTable,
403
495
  ProtectedContent,
@@ -1 +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"]}
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","../../src/payments/InvoiceHistory.tsx"],"sourcesContent":["import { useState, useEffect, useCallback, useContext } from 'react';\r\nimport { AuthContext } from '../auth/AuthProvider';\r\nimport { supabase } from '../core/supabase';\r\nimport { features } from '../core/config';\r\nimport type { SubscriptionTier, SubscriptionStatus } from '../core/types';\r\n\r\ninterface SubscriptionState {\r\n customerId: string | null;\r\n tier: SubscriptionTier;\r\n status: SubscriptionStatus | null;\r\n currentPeriodEnd: string | null;\r\n}\r\n\r\ninterface UseSubscriptionReturn {\r\n subscription: SubscriptionState | null;\r\n tier: SubscriptionTier;\r\n status: SubscriptionStatus | null;\r\n isActive: boolean;\r\n isPro: boolean;\r\n isBusiness: boolean;\r\n isEnterprise: boolean;\r\n canAccess: (requiredTier: SubscriptionTier) => boolean;\r\n loading: boolean;\r\n isConfigured: boolean;\r\n user: unknown;\r\n profile: unknown;\r\n isAuthenticated: boolean;\r\n refetch: () => Promise<void>;\r\n}\r\n\r\nconst TIER_LEVELS: Record<string, number> = {\r\n free: 0, starter: 1, creator: 2, pro: 3, business: 4, enterprise: 5,\r\n};\r\n\r\nexport function useSubscription(): UseSubscriptionReturn {\r\n const auth = useContext(AuthContext);\r\n const [subscription, setSubscription] = useState<SubscriptionState | null>(null);\r\n const [loading, setLoading] = useState(true);\r\n\r\n const fetchSubscription = useCallback(async () => {\r\n if (!auth?.profile) {\r\n setSubscription(null);\r\n setLoading(false);\r\n return;\r\n }\r\n\r\n const sub: SubscriptionState = {\r\n customerId: auth.profile.stripe_customer_id || null,\r\n tier: (auth.profile.subscription_tier as SubscriptionTier) || 'free',\r\n status: (auth.profile.subscription_status as SubscriptionStatus) || null,\r\n currentPeriodEnd: auth.profile.subscription_period_end || null,\r\n };\r\n\r\n setSubscription(sub);\r\n setLoading(false);\r\n }, [auth?.profile]);\r\n\r\n useEffect(() => {\r\n fetchSubscription();\r\n }, [fetchSubscription]);\r\n\r\n useEffect(() => {\r\n if (!features.auth || !auth?.user?.id) return;\r\n\r\n const channel = supabase\r\n .channel(`subscription_${auth.user.id}`)\r\n .on(\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n 'postgres_changes' as any,\r\n {\r\n event: 'UPDATE',\r\n schema: 'public',\r\n table: 'user_profiles',\r\n filter: `id=eq.${auth.user.id}`,\r\n },\r\n () => {\r\n auth.refetchProfile(auth.user!.id);\r\n }\r\n )\r\n .subscribe();\r\n\r\n return () => {\r\n supabase.removeChannel(channel);\r\n };\r\n }, [auth?.user?.id, auth]);\r\n\r\n const tier = subscription?.tier || 'free';\r\n const status = subscription?.status || null;\r\n const isActive = !status || status === 'active' || status === 'trialing';\r\n const tierLevel = TIER_LEVELS[tier] ?? 0;\r\n\r\n return {\r\n subscription,\r\n tier,\r\n status,\r\n isActive,\r\n isPro: tierLevel >= TIER_LEVELS.pro,\r\n isBusiness: tierLevel >= TIER_LEVELS.business,\r\n isEnterprise: tierLevel >= TIER_LEVELS.enterprise,\r\n canAccess: (requiredTier: SubscriptionTier) => tierLevel >= (TIER_LEVELS[requiredTier] ?? 0),\r\n loading: loading || (auth?.loading ?? false),\r\n isConfigured: features.payments,\r\n user: auth?.user ?? null,\r\n profile: auth?.profile ?? null,\r\n isAuthenticated: Boolean(auth?.user),\r\n refetch: fetchSubscription,\r\n };\r\n}\r\n","import { useState, useEffect, useCallback } from 'react';\r\nimport { ezcoder } from '../core/platform';\r\n\r\ninterface CustomerAccessReturn {\r\n loading: boolean;\r\n hasAccess: boolean;\r\n customer: Record<string, unknown> | null;\r\n subscription: Record<string, unknown> | null;\r\n error: string | null;\r\n refresh: () => Promise<void>;\r\n logout: () => void;\r\n}\r\n\r\nexport function useCustomerAccess(): CustomerAccessReturn {\r\n const [loading, setLoading] = useState(true);\r\n const [hasAccess, setHasAccess] = useState(false);\r\n const [customer, setCustomer] = useState<Record<string, unknown> | null>(null);\r\n const [subscription, setSubscription] = useState<Record<string, unknown> | null>(null);\r\n const [error, setError] = useState<string | null>(null);\r\n\r\n const checkAccess = useCallback(async () => {\r\n setLoading(true);\r\n setError(null);\r\n\r\n try {\r\n const customerId = typeof localStorage !== 'undefined'\r\n ? localStorage.getItem('stripeCustomerId')\r\n : null;\r\n const email = typeof localStorage !== 'undefined'\r\n ? localStorage.getItem('customerEmail')\r\n : null;\r\n\r\n if (!customerId && !email) {\r\n setHasAccess(false);\r\n setLoading(false);\r\n return;\r\n }\r\n\r\n const result = await ezcoder.stripe.getCustomerStatus({ customerId: customerId || undefined, email: email || undefined });\r\n\r\n if (result.success) {\r\n setHasAccess(Boolean(result.hasAccess));\r\n setCustomer((result.customer as Record<string, unknown>) || null);\r\n setSubscription((result.subscription as Record<string, unknown>) || null);\r\n } else {\r\n setHasAccess(false);\r\n setError(result.error || 'Failed to check access');\r\n }\r\n } catch (err: unknown) {\r\n setError(err instanceof Error ? err.message : 'Unknown error');\r\n setHasAccess(false);\r\n } finally {\r\n setLoading(false);\r\n }\r\n }, []);\r\n\r\n useEffect(() => {\r\n checkAccess();\r\n }, [checkAccess]);\r\n\r\n const logout = useCallback(() => {\r\n if (typeof localStorage !== 'undefined') {\r\n localStorage.removeItem('stripeCustomerId');\r\n localStorage.removeItem('customerEmail');\r\n }\r\n setHasAccess(false);\r\n setCustomer(null);\r\n setSubscription(null);\r\n }, []);\r\n\r\n return { loading, hasAccess, customer, subscription, error, refresh: checkAccess, logout };\r\n}\r\n","import { useState } from 'react';\r\nimport { ezcoder } from '../core/platform';\r\n\r\ninterface BuyButtonProps {\r\n priceId: string;\r\n productName?: string;\r\n className?: string;\r\n children?: React.ReactNode;\r\n disabled?: boolean;\r\n customerEmail?: string;\r\n quantity?: number;\r\n successUrl?: string;\r\n cancelUrl?: string;\r\n}\r\n\r\nexport function BuyButton({\r\n priceId,\r\n productName,\r\n className = '',\r\n children,\r\n disabled = false,\r\n customerEmail,\r\n quantity,\r\n successUrl,\r\n cancelUrl,\r\n}: BuyButtonProps) {\r\n const [loading, setLoading] = useState(false);\r\n const [error, setError] = useState<string | null>(null);\r\n\r\n const handleClick = async () => {\r\n setError(null);\r\n setLoading(true);\r\n\r\n try {\r\n const result = await ezcoder.stripe.createCheckout(priceId, {\r\n customerEmail,\r\n quantity,\r\n successUrl,\r\n cancelUrl,\r\n redirect: true,\r\n });\r\n\r\n if (!result.success) {\r\n setError(result.error || 'Checkout failed');\r\n }\r\n } catch (err: unknown) {\r\n setError(err instanceof Error ? err.message : 'Checkout failed');\r\n } finally {\r\n setLoading(false);\r\n }\r\n };\r\n\r\n return (\r\n <div>\r\n <button\r\n onClick={handleClick}\r\n disabled={disabled || loading}\r\n className={className}\r\n style={!className ? {\r\n padding: '10px 20px',\r\n backgroundColor: disabled || loading ? '#9ca3af' : '#3b82f6',\r\n color: 'white',\r\n border: 'none',\r\n borderRadius: '6px',\r\n fontSize: '14px',\r\n fontWeight: 500,\r\n cursor: disabled || loading ? 'not-allowed' : 'pointer',\r\n } : undefined}\r\n >\r\n {loading ? 'Processing...' : children || `Buy ${productName || 'Now'}`}\r\n </button>\r\n {error && (\r\n <p style={{ color: '#dc2626', fontSize: '12px', marginTop: '4px' }}>{error}</p>\r\n )}\r\n </div>\r\n );\r\n}\r\n","import { useState } from 'react';\r\nimport { BuyButton } from './BuyButton';\r\n\r\ninterface PricingPlan {\r\n name: string;\r\n description: string;\r\n monthlyPriceId?: string;\r\n yearlyPriceId?: string;\r\n monthlyPrice: number;\r\n yearlyPrice?: number;\r\n features: string[];\r\n highlighted?: boolean;\r\n cta?: string;\r\n}\r\n\r\ninterface PricingTableProps {\r\n plans?: PricingPlan[];\r\n className?: string;\r\n customerEmail?: string;\r\n}\r\n\r\nconst DEFAULT_PLANS: PricingPlan[] = [\r\n {\r\n name: 'Starter',\r\n description: 'For individuals getting started',\r\n monthlyPrice: 0,\r\n features: ['Basic features', 'Community support'],\r\n cta: 'Get Started',\r\n },\r\n {\r\n name: 'Creator',\r\n description: 'For growing projects',\r\n monthlyPrice: 25,\r\n yearlyPrice: 270,\r\n features: ['All Starter features', 'Priority support', 'Advanced analytics'],\r\n highlighted: true,\r\n cta: 'Subscribe',\r\n },\r\n {\r\n name: 'Business',\r\n description: 'For teams and businesses',\r\n monthlyPrice: 50,\r\n yearlyPrice: 540,\r\n features: ['All Creator features', 'Team collaboration', 'Custom integrations'],\r\n cta: 'Subscribe',\r\n },\r\n {\r\n name: 'Enterprise',\r\n description: 'For large organizations',\r\n monthlyPrice: -1,\r\n features: ['All Business features', 'Dedicated support', 'SLA guarantee', 'Custom contracts'],\r\n cta: 'Contact Sales',\r\n },\r\n];\r\n\r\nexport function PricingTable({ plans = DEFAULT_PLANS, className = '', customerEmail }: PricingTableProps) {\r\n const [yearly, setYearly] = useState(false);\r\n\r\n return (\r\n <div className={className}>\r\n <div style={{ display: 'flex', justifyContent: 'center', marginBottom: '32px', gap: '8px', alignItems: 'center' }}>\r\n <span style={{ fontSize: '14px', color: !yearly ? '#111' : '#6b7280', fontWeight: !yearly ? 600 : 400 }}>Monthly</span>\r\n <button\r\n onClick={() => setYearly(!yearly)}\r\n style={{\r\n width: '44px', height: '24px', borderRadius: '12px',\r\n backgroundColor: yearly ? '#3b82f6' : '#d1d5db',\r\n border: 'none', cursor: 'pointer', position: 'relative',\r\n }}\r\n >\r\n <span style={{\r\n width: '18px', height: '18px', borderRadius: '50%', backgroundColor: 'white',\r\n position: 'absolute', top: '3px', left: yearly ? '23px' : '3px',\r\n transition: 'left 0.2s',\r\n }} />\r\n </button>\r\n <span style={{ fontSize: '14px', color: yearly ? '#111' : '#6b7280', fontWeight: yearly ? 600 : 400 }}>\r\n Yearly <span style={{ color: '#059669', fontSize: '12px' }}>Save 10%</span>\r\n </span>\r\n </div>\r\n\r\n <div style={{ display: 'grid', gridTemplateColumns: `repeat(${Math.min(plans.length, 4)}, 1fr)`, gap: '24px', maxWidth: '1200px', margin: '0 auto' }}>\r\n {plans.map((plan) => {\r\n const price = yearly && plan.yearlyPrice !== undefined ? plan.yearlyPrice / 12 : plan.monthlyPrice;\r\n const priceId = yearly ? plan.yearlyPriceId : plan.monthlyPriceId;\r\n\r\n return (\r\n <div\r\n key={plan.name}\r\n style={{\r\n border: plan.highlighted ? '2px solid #3b82f6' : '1px solid #e5e7eb',\r\n borderRadius: '12px', padding: '24px',\r\n backgroundColor: plan.highlighted ? '#eff6ff' : 'white',\r\n }}\r\n >\r\n <h3 style={{ fontSize: '1.25rem', fontWeight: 600, marginBottom: '4px' }}>{plan.name}</h3>\r\n <p style={{ color: '#6b7280', fontSize: '14px', marginBottom: '16px' }}>{plan.description}</p>\r\n\r\n <div style={{ marginBottom: '24px' }}>\r\n {plan.monthlyPrice === 0 ? (\r\n <span style={{ fontSize: '2rem', fontWeight: 700 }}>Free</span>\r\n ) : plan.monthlyPrice === -1 ? (\r\n <span style={{ fontSize: '1.5rem', fontWeight: 600 }}>Custom</span>\r\n ) : (\r\n <>\r\n <span style={{ fontSize: '2rem', fontWeight: 700 }}>${Math.round(price)}</span>\r\n <span style={{ color: '#6b7280', fontSize: '14px' }}>/mo</span>\r\n </>\r\n )}\r\n </div>\r\n\r\n <ul style={{ listStyle: 'none', padding: 0, marginBottom: '24px' }}>\r\n {plan.features.map((feature) => (\r\n <li key={feature} style={{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '8px', fontSize: '14px' }}>\r\n <span style={{ color: '#059669' }}>✓</span> {feature}\r\n </li>\r\n ))}\r\n </ul>\r\n\r\n {priceId ? (\r\n <BuyButton priceId={priceId} productName={plan.name} customerEmail={customerEmail}>\r\n {plan.cta || 'Subscribe'}\r\n </BuyButton>\r\n ) : plan.monthlyPrice === 0 ? (\r\n <button style={{\r\n width: '100%', padding: '10px', border: '1px solid #d1d5db',\r\n borderRadius: '6px', background: 'white', cursor: 'pointer', fontSize: '14px',\r\n }}>\r\n {plan.cta || 'Get Started'}\r\n </button>\r\n ) : (\r\n <button style={{\r\n width: '100%', padding: '10px', border: '1px solid #d1d5db',\r\n borderRadius: '6px', background: 'white', cursor: 'pointer', fontSize: '14px',\r\n }}>\r\n {plan.cta || 'Contact Sales'}\r\n </button>\r\n )}\r\n </div>\r\n );\r\n })}\r\n </div>\r\n </div>\r\n );\r\n}\r\n","import { useState } from 'react';\r\nimport { ezcoder } from '../core/platform';\r\n\r\ninterface ManageSubscriptionButtonProps {\r\n children?: React.ReactNode;\r\n className?: string;\r\n customerId?: string;\r\n}\r\n\r\nexport function ManageSubscriptionButton({ children, className = '', customerId }: ManageSubscriptionButtonProps) {\r\n const [loading, setLoading] = useState(false);\r\n\r\n const handleClick = async () => {\r\n const id = customerId || (typeof localStorage !== 'undefined' ? localStorage.getItem('stripeCustomerId') : null);\r\n if (!id) return;\r\n\r\n setLoading(true);\r\n await ezcoder.stripe.createPortalSession(id, { redirect: true });\r\n setLoading(false);\r\n };\r\n\r\n return (\r\n <button\r\n onClick={handleClick}\r\n disabled={loading}\r\n className={className}\r\n style={!className ? {\r\n padding: '8px 16px',\r\n border: '1px solid #d1d5db',\r\n borderRadius: '6px',\r\n background: 'white',\r\n cursor: loading ? 'not-allowed' : 'pointer',\r\n fontSize: '14px',\r\n } : undefined}\r\n >\r\n {loading ? 'Loading...' : children || 'Manage Subscription'}\r\n </button>\r\n );\r\n}\r\n","import { useSubscription } from './useSubscription';\r\nimport { ManageSubscriptionButton } from './ManageSubscriptionButton';\r\n\r\ninterface SubscriptionManagerProps {\r\n className?: string;\r\n}\r\n\r\nconst STATUS_COLORS: Record<string, string> = {\r\n active: '#059669',\r\n trialing: '#3b82f6',\r\n past_due: '#d97706',\r\n canceled: '#dc2626',\r\n unpaid: '#dc2626',\r\n};\r\n\r\nexport function SubscriptionManager({ className = '' }: SubscriptionManagerProps) {\r\n const { subscription, tier, status, isActive, loading } = useSubscription();\r\n\r\n if (loading) {\r\n return <div style={{ padding: '20px', color: '#6b7280' }}>Loading subscription...</div>;\r\n }\r\n\r\n return (\r\n <div className={className} style={{ border: '1px solid #e5e7eb', borderRadius: '12px', padding: '24px' }}>\r\n <h3 style={{ fontSize: '1.125rem', fontWeight: 600, marginBottom: '16px' }}>Subscription</h3>\r\n\r\n <div style={{ display: 'grid', gap: '12px', marginBottom: '24px' }}>\r\n <div style={{ display: 'flex', justifyContent: 'space-between' }}>\r\n <span style={{ color: '#6b7280' }}>Plan</span>\r\n <span style={{ fontWeight: 500, textTransform: 'capitalize' }}>{tier}</span>\r\n </div>\r\n\r\n {status && (\r\n <div style={{ display: 'flex', justifyContent: 'space-between' }}>\r\n <span style={{ color: '#6b7280' }}>Status</span>\r\n <span style={{\r\n fontWeight: 500,\r\n color: STATUS_COLORS[status] || '#6b7280',\r\n textTransform: 'capitalize',\r\n }}>\r\n {status === 'past_due' ? 'Past Due' : status}\r\n </span>\r\n </div>\r\n )}\r\n\r\n {subscription?.currentPeriodEnd && (\r\n <div style={{ display: 'flex', justifyContent: 'space-between' }}>\r\n <span style={{ color: '#6b7280' }}>{isActive ? 'Renews' : 'Expires'}</span>\r\n <span>{new Date(subscription.currentPeriodEnd).toLocaleDateString()}</span>\r\n </div>\r\n )}\r\n </div>\r\n\r\n {subscription?.customerId && (\r\n <ManageSubscriptionButton customerId={subscription.customerId} />\r\n )}\r\n </div>\r\n );\r\n}\r\n","import { useCustomerAccess } from './useCustomerAccess';\r\nimport { useSubscription } from './useSubscription';\r\nimport type { SubscriptionTier } from '../core/types';\r\n\r\ninterface ProtectedContentProps {\r\n children: React.ReactNode;\r\n fallback?: React.ReactNode;\r\n loadingComponent?: React.ReactNode;\r\n requiredTier?: SubscriptionTier;\r\n}\r\n\r\nexport function ProtectedContent({ children, fallback, loadingComponent, requiredTier }: ProtectedContentProps) {\r\n const customerAccess = useCustomerAccess();\r\n const subscription = useSubscription();\r\n\r\n const loading = requiredTier ? subscription.loading : customerAccess.loading;\r\n\r\n if (loading) {\r\n return <>{loadingComponent || <div style={{ padding: '20px', textAlign: 'center', color: '#6b7280' }}>Loading...</div>}</>;\r\n }\r\n\r\n const hasAccess = requiredTier\r\n ? subscription.isActive && subscription.canAccess(requiredTier)\r\n : customerAccess.hasAccess;\r\n\r\n if (!hasAccess) {\r\n return <>{fallback || <div style={{ padding: '20px', textAlign: 'center', color: '#6b7280' }}>\r\n {requiredTier\r\n ? `This content requires a ${requiredTier} subscription or higher.`\r\n : 'This content requires a subscription.'}\r\n </div>}</>;\r\n }\r\n\r\n return <>{children}</>;\r\n}\r\n","import { useState, useEffect, useCallback } from 'react';\r\nimport { ezcoder } from '../core/platform';\r\n\r\ninterface Invoice {\r\n id: string;\r\n number: string | null;\r\n amount: number;\r\n currency: string;\r\n status: string;\r\n created: number;\r\n hostedUrl: string | null;\r\n pdfUrl: string | null;\r\n}\r\n\r\ninterface InvoiceHistoryProps {\r\n customerId?: string;\r\n limit?: number;\r\n className?: string;\r\n}\r\n\r\nexport function InvoiceHistory({ customerId, limit = 10, className = '' }: InvoiceHistoryProps) {\r\n const [invoices, setInvoices] = useState<Invoice[]>([]);\r\n const [loading, setLoading] = useState(true);\r\n const [error, setError] = useState<string | null>(null);\r\n\r\n const fetchInvoices = useCallback(async () => {\r\n const id = customerId || (typeof localStorage !== 'undefined' ? localStorage.getItem('stripeCustomerId') : null);\r\n if (!id) {\r\n setLoading(false);\r\n return;\r\n }\r\n\r\n setLoading(true);\r\n setError(null);\r\n\r\n try {\r\n const result = await ezcoder.stripe.getInvoices(id, { limit });\r\n if (result.success && result.invoices) {\r\n setInvoices(result.invoices as Invoice[]);\r\n } else {\r\n setError(result.error || 'Failed to load invoices');\r\n }\r\n } catch (err: unknown) {\r\n setError(err instanceof Error ? err.message : 'Unknown error');\r\n } finally {\r\n setLoading(false);\r\n }\r\n }, [customerId, limit]);\r\n\r\n useEffect(() => {\r\n fetchInvoices();\r\n }, [fetchInvoices]);\r\n\r\n if (loading) {\r\n return <div style={{ padding: '20px', color: '#6b7280' }}>Loading invoices...</div>;\r\n }\r\n\r\n if (error) {\r\n return <div style={{ padding: '20px', color: '#dc2626' }}>{error}</div>;\r\n }\r\n\r\n if (invoices.length === 0) {\r\n return <div style={{ padding: '20px', color: '#6b7280' }}>No invoices found.</div>;\r\n }\r\n\r\n const formatAmount = (amount: number, currency: string) => {\r\n return new Intl.NumberFormat('en-US', { style: 'currency', currency }).format(amount / 100);\r\n };\r\n\r\n const STATUS_COLORS: Record<string, string> = {\r\n paid: '#059669',\r\n open: '#3b82f6',\r\n draft: '#6b7280',\r\n void: '#9ca3af',\r\n uncollectible: '#dc2626',\r\n };\r\n\r\n return (\r\n <div className={className} style={{ border: '1px solid #e5e7eb', borderRadius: '12px', overflow: 'hidden' }}>\r\n <div style={{ padding: '16px 24px', borderBottom: '1px solid #e5e7eb' }}>\r\n <h3 style={{ fontSize: '1.125rem', fontWeight: 600, margin: 0 }}>Invoice History</h3>\r\n </div>\r\n <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: '14px' }}>\r\n <thead>\r\n <tr style={{ borderBottom: '1px solid #e5e7eb', background: '#f9fafb' }}>\r\n <th style={{ padding: '10px 16px', textAlign: 'left', fontWeight: 500, color: '#6b7280' }}>Invoice</th>\r\n <th style={{ padding: '10px 16px', textAlign: 'left', fontWeight: 500, color: '#6b7280' }}>Date</th>\r\n <th style={{ padding: '10px 16px', textAlign: 'right', fontWeight: 500, color: '#6b7280' }}>Amount</th>\r\n <th style={{ padding: '10px 16px', textAlign: 'center', fontWeight: 500, color: '#6b7280' }}>Status</th>\r\n <th style={{ padding: '10px 16px', textAlign: 'right', fontWeight: 500, color: '#6b7280' }}></th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n {invoices.map((invoice) => (\r\n <tr key={invoice.id} style={{ borderBottom: '1px solid #f3f4f6' }}>\r\n <td style={{ padding: '12px 16px' }}>{invoice.number || invoice.id.slice(0, 12)}</td>\r\n <td style={{ padding: '12px 16px', color: '#6b7280' }}>\r\n {new Date(invoice.created * 1000).toLocaleDateString()}\r\n </td>\r\n <td style={{ padding: '12px 16px', textAlign: 'right', fontWeight: 500 }}>\r\n {formatAmount(invoice.amount, invoice.currency)}\r\n </td>\r\n <td style={{ padding: '12px 16px', textAlign: 'center' }}>\r\n <span style={{\r\n padding: '2px 8px',\r\n borderRadius: '9999px',\r\n fontSize: '12px',\r\n fontWeight: 500,\r\n color: STATUS_COLORS[invoice.status] || '#6b7280',\r\n background: `${STATUS_COLORS[invoice.status] || '#6b7280'}15`,\r\n textTransform: 'capitalize',\r\n }}>\r\n {invoice.status}\r\n </span>\r\n </td>\r\n <td style={{ padding: '12px 16px', textAlign: 'right' }}>\r\n {invoice.pdfUrl && (\r\n <a\r\n href={invoice.pdfUrl}\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\"\r\n style={{ color: '#3b82f6', textDecoration: 'none', fontSize: '13px' }}\r\n >\r\n PDF\r\n </a>\r\n )}\r\n </td>\r\n </tr>\r\n ))}\r\n </tbody>\r\n </table>\r\n </div>\r\n );\r\n}\r\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","useState","useEffect","useCallback","jsx","jsxs","useState","useCallback","useEffect","STATUS_COLORS"]}
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  AuthContext
3
- } from "../chunk-YNDCD53D.js";
4
- import "../chunk-5XIZHBKE.js";
3
+ } from "../chunk-AWU47M6N.js";
4
+ import "../chunk-GPF4AYNG.js";
5
5
  import {
6
6
  features,
7
7
  supabase
8
- } from "../chunk-G7XDUN3Z.js";
8
+ } from "../chunk-2WG4O4J2.js";
9
9
 
10
10
  // src/roles/useRoles.ts
11
11
  import { useState, useEffect, useCallback, useContext } from "react";
@@ -1 +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":[]}
1
+ {"version":3,"sources":["../../src/roles/useRoles.ts","../../src/roles/RoleGate.tsx"],"sourcesContent":["import { useState, useEffect, useCallback, useContext } from 'react';\r\nimport { supabase } from '../core/supabase';\r\nimport { features } from '../core/config';\r\nimport { AuthContext } from '../auth/AuthProvider';\r\n\r\ninterface RoleData {\r\n role_name: string;\r\n display_name: string;\r\n permissions: string[];\r\n can_access_routes: string[];\r\n}\r\n\r\ninterface UseRolesReturn {\r\n roles: string[];\r\n permissions: string[];\r\n accessibleRoutes: string[];\r\n loading: boolean;\r\n error: string | null;\r\n hasRole: (name: string) => boolean;\r\n hasAnyRole: (names: string[]) => boolean;\r\n hasAllRoles: (names: string[]) => boolean;\r\n hasPermission: (perm: string) => boolean;\r\n hasAnyPermission: (perms: string[]) => boolean;\r\n hasAllPermissions: (perms: string[]) => boolean;\r\n canAccessRoute: (path: string) => boolean;\r\n refreshRoles: () => Promise<void>;\r\n}\r\n\r\nexport function useRoles(): UseRolesReturn {\r\n const auth = useContext(AuthContext);\r\n const [roles, setRoles] = useState<string[]>([]);\r\n const [permissions, setPermissions] = useState<string[]>([]);\r\n const [accessibleRoutes, setAccessibleRoutes] = useState<string[]>([]);\r\n const [loading, setLoading] = useState(true);\r\n const [error, setError] = useState<string | null>(null);\r\n\r\n const fetchRoles = useCallback(async () => {\r\n if (!auth?.user?.id || !features.auth) {\r\n setRoles([]);\r\n setPermissions([]);\r\n setAccessibleRoutes([]);\r\n setLoading(false);\r\n return;\r\n }\r\n\r\n try {\r\n const { data, error: rpcError } = await supabase.rpc('get_user_roles', {\r\n user_uuid: auth.user.id,\r\n });\r\n\r\n if (rpcError) {\r\n setError(rpcError.message);\r\n setLoading(false);\r\n return;\r\n }\r\n\r\n const roleData = (data || []) as RoleData[];\r\n const allRoles = roleData.map((r) => r.role_name);\r\n const allPerms = [...new Set(roleData.flatMap((r) => r.permissions || []))];\r\n const allRoutes = [...new Set(roleData.flatMap((r) => r.can_access_routes || []))];\r\n\r\n setRoles(allRoles);\r\n setPermissions(allPerms);\r\n setAccessibleRoutes(allRoutes);\r\n setError(null);\r\n } catch (err: unknown) {\r\n setError(err instanceof Error ? err.message : 'Failed to fetch roles');\r\n } finally {\r\n setLoading(false);\r\n }\r\n }, [auth?.user?.id]);\r\n\r\n useEffect(() => {\r\n fetchRoles();\r\n }, [fetchRoles]);\r\n\r\n const hasRole = useCallback((name: string) => roles.includes(name), [roles]);\r\n const hasAnyRole = useCallback((names: string[]) => names.some((n) => roles.includes(n)), [roles]);\r\n const hasAllRoles = useCallback((names: string[]) => names.every((n) => roles.includes(n)), [roles]);\r\n const hasPermission = useCallback((perm: string) => permissions.includes(perm), [permissions]);\r\n const hasAnyPermission = useCallback((perms: string[]) => perms.some((p) => permissions.includes(p)), [permissions]);\r\n const hasAllPermissions = useCallback((perms: string[]) => perms.every((p) => permissions.includes(p)), [permissions]);\r\n\r\n const canAccessRoute = useCallback((path: string) => {\r\n return accessibleRoutes.some((route) => {\r\n if (route.endsWith('/*')) {\r\n return path.startsWith(route.slice(0, -2));\r\n }\r\n return route === path;\r\n });\r\n }, [accessibleRoutes]);\r\n\r\n return {\r\n roles, permissions, accessibleRoutes, loading, error,\r\n hasRole, hasAnyRole, hasAllRoles, hasPermission, hasAnyPermission, hasAllPermissions,\r\n canAccessRoute, refreshRoles: fetchRoles,\r\n };\r\n}\r\n","import { useRoles } from './useRoles';\r\n\r\ninterface RoleGateProps {\r\n children: React.ReactNode;\r\n roles?: string[];\r\n permissions?: string[];\r\n requireAll?: boolean;\r\n fallback?: React.ReactNode;\r\n loadingFallback?: React.ReactNode;\r\n}\r\n\r\ninterface RouteGateProps {\r\n children: React.ReactNode;\r\n route: string;\r\n fallback?: React.ReactNode;\r\n loadingFallback?: React.ReactNode;\r\n}\r\n\r\nexport function RoleGate({\r\n children,\r\n roles: requiredRoles,\r\n permissions: requiredPerms,\r\n requireAll = false,\r\n fallback = null,\r\n loadingFallback = null,\r\n}: RoleGateProps) {\r\n const { hasRole, hasAnyRole, hasAllRoles, hasPermission, hasAnyPermission, hasAllPermissions, loading } = useRoles();\r\n\r\n if (loading) return <>{loadingFallback}</>;\r\n\r\n let hasAccess = true;\r\n\r\n if (requiredRoles?.length) {\r\n hasAccess = requireAll ? hasAllRoles(requiredRoles) : hasAnyRole(requiredRoles);\r\n }\r\n\r\n if (hasAccess && requiredPerms?.length) {\r\n hasAccess = requireAll ? hasAllPermissions(requiredPerms) : hasAnyPermission(requiredPerms);\r\n }\r\n\r\n if (!hasAccess && !requiredRoles?.length && !requiredPerms?.length) {\r\n hasAccess = true;\r\n }\r\n\r\n // Use individual checks to satisfy linter\r\n void hasRole;\r\n void hasPermission;\r\n\r\n return hasAccess ? <>{children}</> : <>{fallback}</>;\r\n}\r\n\r\nexport function RouteGate({ children, route, fallback = null, loadingFallback = null }: RouteGateProps) {\r\n const { canAccessRoute, loading } = useRoles();\r\n\r\n if (loading) return <>{loadingFallback}</>;\r\n\r\n return canAccessRoute(route) ? <>{children}</> : <>{fallback}</>;\r\n}\r\n"],"mappings":"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","names":[]}