PluginProbe
The Innovative Form Builder – IvyForms / 1.1.1
The Innovative Form Builder – IvyForms v1.1.1
1.5.1 1.5 1.4.1 1.4 trunk 0.1.2 0.2 0.2.1 0.3 0.3.1 0.4 0.5 0.6 0.6.1 0.6.1-backup 0.6.1.1 0.7 0.8 0.8.1 0.8.2 0.9 0.9.1 1.0 1.1 1.1.1 All 27 releases
ivyforms / frontend / src / constants / integrations.ts

integrations.ts in The Innovative Form Builder – IvyForms 1.1.1, at frontend/src/constants/integrations.ts

176 lines 5.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { useApiClient } from '@/composables/useApiClient'
2
3 /**
4 * Backend integration configuration interface
5 */
6 interface BackendIntegrationConfig {
7 slug: string
8 label: string
9 component: string
10 icon?: string
11 description?: string
12 category?: string
13 requiresAuth?: boolean
14 hasGlobalSettings?: boolean
15 hasFormSettings?: boolean
16 plan?: string
17 settingsSchema?: Record<string, unknown>
18 learnMoreUrl?: string
19 }
20
21 /**
22 * Backend-registered integrations cache
23 * Populated dynamically from the IntegrationRegistry via REST API
24 */
25 let integrationsCache: Integration[] | null = null
26 let integrationsFetchPromise: Promise<Integration[]> | null = null
27
28 /**
29 * Fetch integrations from backend registry
30 * Uses caching to avoid multiple API calls
31 */
32 async function fetchBackendIntegrations(): Promise<Integration[]> {
33 // Return cached value if available
34 if (integrationsCache) {
35 return integrationsCache
36 }
37
38 // Return existing promise if fetch is in progress
39 if (integrationsFetchPromise) {
40 return integrationsFetchPromise
41 }
42
43 // Start new fetch
44 integrationsFetchPromise = (async () => {
45 try {
46 const { request } = useApiClient()
47 const response = await request<{
48 success: boolean
49 data: Record<string, BackendIntegrationConfig>
50 }>('integrations')
51
52 // The API controller returns {success: true, data: {wpdatatables: {...}, mailchimp: {...}}}
53 // useApiClient wraps it in {message: 'OK', data: {success: true, data: {...}}}
54 // So we need to unwrap twice: response.data.data.data
55 const wrappedData = response.data?.data // {success: true, data: {...}}
56 const backendData = wrappedData?.data as Record<string, BackendIntegrationConfig> // {wpdatatables: {...}, mailchimp: {...}}
57
58 if (!backendData || typeof backendData !== 'object') {
59 throw new Error('Invalid response format')
60 }
61
62 // Transform backend format to frontend format
63 const transformed: Integration[] = Object.entries(backendData).map(([slug, config]) => ({
64 name: config.label, // Translation key from backend (e.g., 'wpdatatables_label')
65 value: slug, // Use slug as value
66 image: config.icon || slug,
67 description: config.description || '', // Translation key from backend (e.g., 'wpdatatables_description')
68 category: config.category || 'Other',
69 type: config.plan === 'lite' ? 'disabled' : 'soon', // Default type, will be updated by useIntegrations
70 searchCategory: config.plan || 'lite',
71 requiredPlan: config.plan !== 'lite' ? config.plan : undefined,
72 component: config.component,
73 learnMoreUrl: config.learnMoreUrl || '',
74 hasGlobalSettings: config.hasGlobalSettings, // Pass through from backend
75 hasFormSettings: config.hasFormSettings, // Pass through from backend
76 }))
77
78 integrationsCache = transformed
79 return transformed
80 } catch (error) {
81 console.error('Failed to load integrations from backend:', error)
82
83 // Fallback to hardcoded defaults
84 const fallback: Integration[] = [
85 {
86 name: 'wpdatatables',
87 value: 'wpdatatables',
88 image: 'wpdatatables',
89 description: 'wpdt_integration_description',
90 category: 'Data Management',
91 type: 'disabled',
92 searchCategory: 'lite',
93 },
94 ]
95
96 integrationsCache = fallback
97 return fallback
98 } finally {
99 integrationsFetchPromise = null
100 }
101 })()
102
103 return integrationsFetchPromise
104 }
105
106 /**
107 * Get integrations with filters applied
108 * This function applies filters at runtime, allowing Pro to modify integrations
109 *
110 * NOTE: This is now async and should be used with await or in a component setup
111 */
112 export async function getIntegrations(): Promise<Integration[]> {
113 let result = await fetchBackendIntegrations()
114
115 // Allow Pro to extend/filter integrations via dedicated hooks
116 if (typeof window !== 'undefined') {
117 const w = window as Window & {
118 IvyFormsPro?: {
119 integrationsHooks?: Array<(integrations: Integration[]) => Integration[]>
120 }
121 IvyForms?: {
122 hooks?: {
123 applyFilters?: (hookName: string, value: Integration[]) => Integration[]
124 }
125 }
126 }
127
128 // Check Pro-specific hooks first (direct hooks from Pro plugin)
129 const proHooks = w.IvyFormsPro?.integrationsHooks || []
130 for (const hook of proHooks) {
131 result = hook(result)
132 }
133
134 // Also support general filters if defined
135 if (w.IvyForms?.hooks?.applyFilters) {
136 result = w.IvyForms.hooks.applyFilters('ivyforms/integrations/list', result)
137 }
138 }
139
140 return result
141 }
142
143 /**
144 * Clear integrations cache
145 * Useful for forcing a refresh after new integrations are registered
146 */
147 export function clearIntegrationsCache(): void {
148 integrationsCache = null
149 integrationsFetchPromise = null
150 }
151
152 // Remove legacy export - integrations are now loaded dynamically
153
154 export type integrationType =
155 | 'active'
156 | 'disabled'
157 | 'upgrade'
158 | 'proActive'
159 | 'proDisabled'
160 | 'soon'
161
162 export interface Integration {
163 name: string
164 value: string
165 image?: string
166 description: string
167 category: string
168 type: integrationType
169 searchCategory: string
170 requiredPlan?: string // Optional: minimum plan required for Pro integrations
171 component?: string // Component name for settings UI
172 learnMoreUrl?: string // Optional: documentation/learn more URL
173 hasGlobalSettings?: boolean // Optional: whether integration has global settings page (default: true)
174 hasFormSettings?: boolean // Optional: whether integration has per-form settings in form builder (default: true)
175 }
176