PluginProbe
The Innovative Form Builder – IvyForms / 1.1
The Innovative Form Builder – IvyForms v1.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, at frontend/src/constants/integrations.ts

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