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 / composables / useIntegrations.ts

useIntegrations.ts in The Innovative Form Builder – IvyForms 1.1.1, at frontend/src/composables/useIntegrations.ts

195 lines 6.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { computed, ref } from 'vue'
2 import { getIntegrations, type Integration, type integrationType } from '@/constants/integrations'
3 import { useProFeatures } from '@/composables/useProFeatures'
4 import { useProFeatureUpgrade } from '@/composables/useProFeatureUpgrade'
5 import { useSettingsStore } from '@/stores/useSettingsStore'
6
7 /**
8 * Integrations Composable
9 *
10 * Manages integration status and dynamically updates types based on:
11 * - Pro installation status
12 * - Current license plan
13 * - Connection/activation state
14 */
15
16 interface IntegrationState {
17 [key: string]: {
18 enabled: boolean
19 connected: boolean
20 }
21 }
22
23 export function useIntegrations() {
24 const pro = useProFeatures()
25 const settingsStore = useSettingsStore()
26 const { isProInstalled: checkProInstalled, isProLicenseActive: checkProLicenseActive } =
27 useProFeatureUpgrade()
28 const integrationState = ref<IntegrationState>({})
29
30 /**
31 * Load integration states from settings store
32 */
33 const loadIntegrationStates = async () => {
34 // Load from the settings store's integrationsSettings
35 const storeSettings = settingsStore.integrationsSettings || {}
36
37 // Transform to match our IntegrationState interface
38 const transformed: IntegrationState = {}
39 for (const [key, value] of Object.entries(storeSettings)) {
40 if (value && typeof value === 'object') {
41 transformed[key] = {
42 enabled: (value as { enabled?: boolean }).enabled || false,
43 connected: (value as { connected?: boolean }).connected || false,
44 }
45 }
46 }
47
48 integrationState.value = transformed
49 }
50
51 /**
52 * Check if Pro is installed and meets minimum plan requirement
53 * Also checks if license is active
54 */
55 const meetsRequirement = (requiredPlan?: string): boolean => {
56 if (!requiredPlan) return true
57
58 // First check: Is Pro installed and license active?
59 if (!checkProInstalled() || !checkProLicenseActive()) {
60 return false
61 }
62
63 // Wait for Pro features to be loaded
64 if (!pro.loaded.value) {
65 return false
66 }
67
68 const currentPlan = pro.plan.value
69 const isProInstalledCheck =
70 currentPlan !== 'lite' && currentPlan !== 'error' && currentPlan !== 'unauthorized'
71
72 if (!isProInstalledCheck) return false
73
74 const planHierarchy = ['essentials', 'growth', 'agency']
75 const currentIndex = planHierarchy.indexOf(currentPlan)
76 const requiredIndex = planHierarchy.indexOf(requiredPlan)
77
78 return currentIndex >= requiredIndex && currentIndex !== -1
79 }
80
81 /**
82 * Determine integration type dynamically
83 */
84 const getIntegrationType = (integration: Integration): integrationType => {
85 const state = integrationState.value[integration.value] || { enabled: false, connected: false }
86
87 // Check if this is a Pro integration (plan is not 'lite')
88 const isProIntegration = integration.searchCategory !== 'lite'
89
90 if (isProIntegration) {
91 const proInstalled = checkProInstalled()
92 const licenseActive = checkProLicenseActive()
93
94 // CASE 1: Pro not installed at all
95 // All Pro features show upgrade badge
96 if (!proInstalled) {
97 return 'upgrade' // Show upgrade with plan badge, disable toggle
98 }
99
100 // CASE 2: Pro installed but license not active
101 // Check if the user's stored plan tier covers the required plan.
102 // If yes → show "Activate License" (proDisabled); they have the right plan, just need a key.
103 // If no → show "Upgrade" (upgrade); they need to purchase a higher tier.
104 if (!licenseActive) {
105 const planHierarchy = ['essentials', 'growth', 'agency']
106 const storedPlanIndex = planHierarchy.indexOf(pro.installedPlan.value)
107 const requiredPlanIndex = planHierarchy.indexOf(integration.requiredPlan || 'essentials')
108
109 if (storedPlanIndex !== -1 && storedPlanIndex >= requiredPlanIndex) {
110 return 'proDisabled' // Right plan tier — just needs an active license
111 }
112 return 'upgrade' // Needs a higher plan tier
113 }
114
115 // CASE 3: Pro installed and licensed - check plan hierarchy
116 // Plan hierarchy: essentials < growth < agency
117 if (!meetsRequirement(integration.requiredPlan)) {
118 // Licensed but insufficient plan
119 // Examples:
120 // - Essentials user viewing Growth feature → show 'upgrade' with "GROWTH" badge
121 // - Essentials user viewing Agency feature → show 'upgrade' with "AGENCY" badge
122 // - Growth user viewing Agency feature → show 'upgrade' with "AGENCY" badge
123 return 'upgrade' // Show upgrade with required plan badge, disable toggle
124 }
125
126 // CASE 4: Pro installed, licensed, and plan is sufficient
127 // User has the required plan or higher - show normal functionality
128 // Now check if integration is enabled/connected
129
130 // If not enabled, return 'proDisabled'
131 if (!state.enabled) {
132 return 'proDisabled'
133 }
134
135 // If enabled but not connected, return 'proDisabled'
136 if (!state.connected) {
137 return 'proDisabled'
138 }
139
140 // Enabled and connected
141 return 'proActive'
142 }
143
144 // Lite integrations
145 // PRIORITY 1: Check if plugin is installed (connected)
146 // If installed, ALWAYS show "Learn More" regardless of toggle state
147 if (state.connected) {
148 return 'active'
149 }
150
151 // PRIORITY 2: Plugin is NOT installed
152 return 'disabled'
153 }
154
155 /**
156 * Get all integrations with dynamic types
157 * Now async - loads from backend registry
158 */
159 const integrations = ref<Integration[]>([])
160 const integrationsLoaded = ref(false)
161
162 const loadIntegrations = async () => {
163 if (integrationsLoaded.value) {
164 return integrations.value
165 }
166
167 // Call getIntegrations() to get filtered list from backend at runtime
168 const loaded = await getIntegrations()
169 integrations.value = loaded.map((integration) => ({
170 ...integration,
171 type: getIntegrationType(integration),
172 }))
173
174 integrationsLoaded.value = true
175 return integrations.value
176 }
177
178 /**
179 * Update integration state
180 */
181 const updateIntegrationState = (name: string, enabled: boolean, connected = false) => {
182 integrationState.value[name] = { enabled, connected }
183 }
184
185 return {
186 integrations: computed(() => integrations.value),
187 integrationsLoaded: computed(() => integrationsLoaded.value),
188 integrationState,
189 loadIntegrations,
190 loadIntegrationStates,
191 updateIntegrationState,
192 getIntegrationType,
193 }
194 }
195