CreateFormCTA.tsx
2 weeks ago
CreateWithAI.tsx
2 weeks ago
Main.tsx
2 weeks ago
PluginStatus.tsx
2 weeks ago
Sidebar.tsx
2 weeks ago
TemplateList.tsx
2 weeks ago
TemplatesSkeleton.tsx
4 months ago
PluginStatus.tsx
245 lines
| 1 | import React, { useEffect, useState } from "react"; |
| 2 | import apiFetch from "@wordpress/api-fetch"; |
| 3 | import { |
| 4 | Box, |
| 5 | Button, |
| 6 | Flex, |
| 7 | HStack, |
| 8 | Icon, |
| 9 | Spinner, |
| 10 | Text, |
| 11 | VStack, |
| 12 | useToast, |
| 13 | } from "@chakra-ui/react"; |
| 14 | import { FiCheck, FiDownload, FiZap } from "react-icons/fi"; |
| 15 | import { templatesScriptData } from "../utils/global"; |
| 16 | import { __, sprintf } from '@wordpress/i18n'; |
| 17 | |
| 18 | interface PluginStatusProps { |
| 19 | requiredPlugins: { key: string; value: string }[]; |
| 20 | onActivateAndContinue: () => void; |
| 21 | } |
| 22 | |
| 23 | interface PluginStatusResponse { |
| 24 | success: boolean; |
| 25 | plugin_status: Record<string, string>; |
| 26 | message?: string; |
| 27 | } |
| 28 | |
| 29 | const { restURL, security } = templatesScriptData; |
| 30 | |
| 31 | const StatusBadge: React.FC<{ status: string | undefined }> = ({ status }) => { |
| 32 | if (!status) { |
| 33 | return <Spinner size="xs" color="#7545BB" thickness="2px" />; |
| 34 | } |
| 35 | if (status === 'active') { |
| 36 | return ( |
| 37 | <HStack spacing="5px"> |
| 38 | <Box w="6px" h="6px" borderRadius="full" bg="#22c55e" /> |
| 39 | <Text fontSize="12px" fontWeight="500" color="#16a34a" margin="0"> |
| 40 | {__('Active', 'everest-forms')} |
| 41 | </Text> |
| 42 | </HStack> |
| 43 | ); |
| 44 | } |
| 45 | if (status === 'inactive') { |
| 46 | return ( |
| 47 | <HStack spacing="5px"> |
| 48 | <Box w="6px" h="6px" borderRadius="full" bg="#f59e0b" /> |
| 49 | <Text fontSize="12px" fontWeight="500" color="#d97706" margin="0"> |
| 50 | {__('Inactive', 'everest-forms')} |
| 51 | </Text> |
| 52 | </HStack> |
| 53 | ); |
| 54 | } |
| 55 | if (status === 'not-installed') { |
| 56 | return ( |
| 57 | <HStack spacing="5px"> |
| 58 | <Box w="6px" h="6px" borderRadius="full" bg="#94a3b8" /> |
| 59 | <Text fontSize="12px" fontWeight="500" color="#64748b" margin="0"> |
| 60 | {__('Not installed', 'everest-forms')} |
| 61 | </Text> |
| 62 | </HStack> |
| 63 | ); |
| 64 | } |
| 65 | if (status === 'error') { |
| 66 | return ( |
| 67 | <HStack spacing="5px"> |
| 68 | <Box w="6px" h="6px" borderRadius="full" bg="#ef4444" /> |
| 69 | <Text fontSize="12px" fontWeight="500" color="#dc2626" margin="0"> |
| 70 | {__('Error', 'everest-forms')} |
| 71 | </Text> |
| 72 | </HStack> |
| 73 | ); |
| 74 | } |
| 75 | return null; |
| 76 | }; |
| 77 | |
| 78 | const PluginStatus: React.FC<PluginStatusProps> = ({ |
| 79 | requiredPlugins, |
| 80 | onActivateAndContinue, |
| 81 | }) => { |
| 82 | const [pluginStatuses, setPluginStatuses] = useState<Record<string, string>>({}); |
| 83 | const [loading, setLoading] = useState(false); |
| 84 | const [installInProgress, setInstallInProgress] = useState(false); |
| 85 | const [buttonState, setButtonState] = useState<'idle' | 'install' | 'activate' | 'continue'>('idle'); |
| 86 | const toast = useToast(); |
| 87 | |
| 88 | useEffect(() => { |
| 89 | const fetchPluginStatus = async () => { |
| 90 | try { |
| 91 | const response = await apiFetch<PluginStatusResponse>({ |
| 92 | path: `${restURL}everest-forms/v1/plugin/status`, |
| 93 | method: "GET", |
| 94 | headers: { "X-WP-Nonce": security }, |
| 95 | }); |
| 96 | if (response.success) { |
| 97 | setPluginStatuses(response.plugin_status); |
| 98 | deriveButtonState(response.plugin_status); |
| 99 | } |
| 100 | } catch { |
| 101 | toast({ |
| 102 | title: __("Error", "everest-forms"), |
| 103 | description: __("Unable to check plugin status.", "everest-forms"), |
| 104 | status: "error", position: "bottom-right", duration: 5000, isClosable: true, variant: "subtle", |
| 105 | }); |
| 106 | } |
| 107 | }; |
| 108 | fetchPluginStatus(); |
| 109 | }, []); |
| 110 | |
| 111 | const deriveButtonState = (statuses: Record<string, string>) => { |
| 112 | const allActive = requiredPlugins.every(p => statuses[p.key] === 'active'); |
| 113 | const anyNotInst = requiredPlugins.some(p => statuses[p.key] === 'not-installed'); |
| 114 | const anyInactive = requiredPlugins.some(p => statuses[p.key] === 'inactive'); |
| 115 | |
| 116 | if (allActive) setButtonState('continue'); |
| 117 | else if (anyNotInst) setButtonState('install'); |
| 118 | else if (anyInactive) setButtonState('activate'); |
| 119 | else setButtonState('continue'); |
| 120 | }; |
| 121 | |
| 122 | const handleButtonClick = async () => { |
| 123 | if (buttonState === 'continue') { |
| 124 | onActivateAndContinue(); |
| 125 | return; |
| 126 | } |
| 127 | |
| 128 | setLoading(true); |
| 129 | setInstallInProgress(true); |
| 130 | |
| 131 | let finalMessage = ""; |
| 132 | for (const plugin of requiredPlugins) { |
| 133 | if (pluginStatuses[plugin.key] === 'active') continue; |
| 134 | try { |
| 135 | const response = (await apiFetch({ |
| 136 | path: `${restURL}everest-forms/v1/plugin/activate`, |
| 137 | method: "POST", |
| 138 | body: JSON.stringify({ |
| 139 | moduleData: [{ name: plugin.value, slug: plugin.key, type: "addon" }], |
| 140 | }), |
| 141 | headers: { "Content-Type": "application/json", "X-WP-Nonce": security }, |
| 142 | })) as PluginStatusResponse; |
| 143 | |
| 144 | if (response.success) { |
| 145 | setPluginStatuses(prev => ({ ...prev, [plugin.key]: 'active' })); |
| 146 | finalMessage = response.message || __("Plugin activated successfully.", "everest-forms"); |
| 147 | } else { |
| 148 | setPluginStatuses(prev => ({ ...prev, [plugin.key]: 'error' })); |
| 149 | finalMessage = response.message || sprintf(__("Failed to activate: %s.", "everest-forms"), plugin.value); |
| 150 | } |
| 151 | } catch { |
| 152 | setPluginStatuses(prev => ({ ...prev, [plugin.key]: 'error' })); |
| 153 | finalMessage = sprintf(__("Unable to activate %s.", "everest-forms"), plugin.value); |
| 154 | } |
| 155 | } |
| 156 | |
| 157 | setLoading(false); |
| 158 | setInstallInProgress(false); |
| 159 | setButtonState('continue'); |
| 160 | |
| 161 | toast({ |
| 162 | title: __("Success", "everest-forms"), |
| 163 | description: finalMessage, |
| 164 | status: "success", position: "bottom-right", duration: 5000, isClosable: true, variant: "subtle", |
| 165 | }); |
| 166 | }; |
| 167 | |
| 168 | const buttonConfig = { |
| 169 | install: { label: __('Install & Activate', 'everest-forms'), icon: FiDownload }, |
| 170 | activate: { label: __('Activate', 'everest-forms'), icon: FiZap }, |
| 171 | continue: { label: __('Continue', 'everest-forms'), icon: FiCheck }, |
| 172 | idle: { label: __('Continue', 'everest-forms'), icon: FiCheck }, |
| 173 | }[buttonState]; |
| 174 | |
| 175 | if (requiredPlugins.length === 0) return null; |
| 176 | |
| 177 | return ( |
| 178 | <VStack align="stretch" spacing="0"> |
| 179 | {/* Addon rows */} |
| 180 | <VStack align="stretch" spacing="2px" mb="20px"> |
| 181 | {requiredPlugins.map((plugin, i) => ( |
| 182 | <Flex |
| 183 | key={plugin.key} |
| 184 | align="center" |
| 185 | justify="space-between" |
| 186 | px="14px" |
| 187 | py="12px" |
| 188 | bg={i % 2 === 0 ? '#fafafa' : 'white'} |
| 189 | border="1px solid #f1f5f9" |
| 190 | borderRadius={ |
| 191 | i === 0 |
| 192 | ? '10px 10px 0 0' |
| 193 | : i === requiredPlugins.length - 1 |
| 194 | ? '0 0 10px 10px' |
| 195 | : '0' |
| 196 | } |
| 197 | borderTop={i > 0 ? 'none' : undefined} |
| 198 | > |
| 199 | <Text fontSize="14px" fontWeight="500" color="#374151" margin="0"> |
| 200 | {plugin.value} |
| 201 | </Text> |
| 202 | <StatusBadge status={pluginStatuses[plugin.key]} /> |
| 203 | </Flex> |
| 204 | ))} |
| 205 | </VStack> |
| 206 | |
| 207 | {/* Action button */} |
| 208 | {buttonState !== 'idle' && ( |
| 209 | <Box |
| 210 | as="button" |
| 211 | display="inline-flex" |
| 212 | alignItems="center" |
| 213 | justifyContent="center" |
| 214 | gap="8px" |
| 215 | alignSelf="flex-end" |
| 216 | h="40px" |
| 217 | px="20px" |
| 218 | borderRadius="8px" |
| 219 | bg={buttonState === 'continue' ? '#22c55e' : '#7545BB'} |
| 220 | color="white" |
| 221 | fontSize="14px" |
| 222 | fontWeight="500" |
| 223 | border="none" |
| 224 | cursor={loading || installInProgress ? 'not-allowed' : 'pointer'} |
| 225 | opacity={loading || installInProgress ? 0.75 : 1} |
| 226 | onClick={!loading && !installInProgress ? handleButtonClick : undefined} |
| 227 | transition="background 0.2s, opacity 0.2s" |
| 228 | _hover={{ bg: buttonState === 'continue' ? '#16a34a' : '#6a3daa' }} |
| 229 | > |
| 230 | {loading ? ( |
| 231 | <Spinner size="xs" color="white" thickness="2px" /> |
| 232 | ) : ( |
| 233 | <Icon as={buttonConfig.icon} boxSize="4" /> |
| 234 | )} |
| 235 | <Text margin="0" color="white"> |
| 236 | {loading ? __('Processing…', 'everest-forms') : buttonConfig.label} |
| 237 | </Text> |
| 238 | </Box> |
| 239 | )} |
| 240 | </VStack> |
| 241 | ); |
| 242 | }; |
| 243 | |
| 244 | export default PluginStatus; |
| 245 |