ModuleItem.js
539 lines
| 1 | /** |
| 2 | * External Dependencies |
| 3 | */ |
| 4 | import { |
| 5 | Badge, |
| 6 | Box, |
| 7 | Checkbox, |
| 8 | Heading, |
| 9 | Image, |
| 10 | Stack, |
| 11 | Text, |
| 12 | useToast, |
| 13 | Link, |
| 14 | Button, |
| 15 | Divider, |
| 16 | HStack, |
| 17 | Switch, |
| 18 | IconButton, |
| 19 | Modal, |
| 20 | Tooltip, |
| 21 | ModalCloseButton, |
| 22 | ModalContent, |
| 23 | ModalOverlay, |
| 24 | ModalHeader, |
| 25 | Spinner, |
| 26 | useDisclosure, |
| 27 | Icon, |
| 28 | } from "@chakra-ui/react"; |
| 29 | import { SettingsIcon, WarningIcon } from "@chakra-ui/icons"; |
| 30 | import { __ } from "@wordpress/i18n"; |
| 31 | import React, { useState, useEffect, useContext } from "react"; |
| 32 | import YouTubePlayer from 'react-player/youtube'; |
| 33 | import { FaInfoCircle, FaPlayCircle } from 'react-icons/fa'; |
| 34 | |
| 35 | /** |
| 36 | * Internal Dependencies |
| 37 | */ |
| 38 | import { activateModule, deactivateModule } from "./modules-api"; |
| 39 | import DashboardContext from "./../../../context/DashboardContext"; |
| 40 | import { actionTypes } from "./../../../reducers/DashboardReducer"; |
| 41 | import { FreeModules } from "../../../Constants/Products"; |
| 42 | |
| 43 | const ModuleItem = (props) => { |
| 44 | /* global _EVF_DASHBOARD_ */ |
| 45 | const { assetsURL, liveDemoURL, isPro, licensePlan, adminURL, upgradeURL } = |
| 46 | typeof _EVF_DASHBOARD_ !== "undefined" && _EVF_DASHBOARD_; |
| 47 | const [{ upgradeModal }, dispatch] = useContext(DashboardContext); |
| 48 | const [requirementFulfilled, setRequirementFulfilled] = useState(false); |
| 49 | const [licenseActivated, setLicenseActivated] = useState(false); |
| 50 | const [moduleEnabled, setModuleEnabled] = useState(false); |
| 51 | |
| 52 | const [showPlayVideoButton, setShowPlayVideoButton] = useState(false); |
| 53 | const [thumbnailVideoPlaying, setThumbnailVideoPlaying] = useState(false); |
| 54 | |
| 55 | const [thumbnailVideoLoading, setThumbnailVideoLoading] = useState(true); |
| 56 | const { isOpen, onOpen, onClose } = useDisclosure(); |
| 57 | const [isAddonActivating, setAddonActivated] = useState(false); |
| 58 | |
| 59 | const { |
| 60 | data, |
| 61 | isChecked, |
| 62 | onCheckedChange, |
| 63 | isPerformingBulkAction, |
| 64 | selectedModuleData, |
| 65 | |
| 66 | } = props; |
| 67 | const toast = useToast(); |
| 68 | const { |
| 69 | title, |
| 70 | name, |
| 71 | excerpt, |
| 72 | slug, |
| 73 | image, |
| 74 | plan, |
| 75 | link, |
| 76 | status, |
| 77 | required_plan, |
| 78 | type, |
| 79 | demo_video_url, |
| 80 | setting_url |
| 81 | } = data; |
| 82 | const [moduleStatus, setModuleStatus] = useState(status); |
| 83 | const [isPerformingAction, setIsPerformingAction] = useState(false); |
| 84 | const [moduleSettingsURL, setModuleSettingsURL] = useState(''); |
| 85 | |
| 86 | const handleModuleAction = () => { |
| 87 | setAddonActivated(true); |
| 88 | setIsPerformingAction(true); |
| 89 | |
| 90 | if (moduleEnabled) { |
| 91 | if ( |
| 92 | moduleStatus === "inactive" || |
| 93 | moduleStatus === "not-installed" |
| 94 | ) { |
| 95 | activateModule(slug, name, type) |
| 96 | .then((data) => { |
| 97 | |
| 98 | if (data.success) { |
| 99 | toast({ |
| 100 | title: data.message, |
| 101 | status: "success", |
| 102 | duration: 3000, |
| 103 | }); |
| 104 | // window.location.reload(); |
| 105 | setAddonActivated(false); |
| 106 | setModuleStatus("active"); |
| 107 | } else { |
| 108 | toast({ |
| 109 | title: data.message, |
| 110 | status: "error", |
| 111 | duration: 3000, |
| 112 | }); |
| 113 | setAddonActivated(false); |
| 114 | setModuleStatus("not-installed"); |
| 115 | } |
| 116 | }) |
| 117 | .catch((e) => { |
| 118 | toast({ |
| 119 | title: e.message, |
| 120 | status: "error", |
| 121 | duration: 3000, |
| 122 | }); |
| 123 | setModuleStatus("not-installed"); |
| 124 | }) |
| 125 | .finally(() => { |
| 126 | setIsPerformingAction(false); |
| 127 | setAddonActivated(false); |
| 128 | }); |
| 129 | } else { |
| 130 | deactivateModule(slug, type) |
| 131 | .then((data) => { |
| 132 | if (data.success) { |
| 133 | toast({ |
| 134 | title: data.message, |
| 135 | status: "success", |
| 136 | duration: 3000, |
| 137 | }); |
| 138 | // window.location.reload(); |
| 139 | setModuleStatus("inactive"); |
| 140 | } else { |
| 141 | toast({ |
| 142 | title: data.message, |
| 143 | status: "error", |
| 144 | duration: 3000, |
| 145 | }); |
| 146 | setModuleStatus("active"); |
| 147 | } |
| 148 | }) |
| 149 | .finally(() => { |
| 150 | setAddonActivated(false); |
| 151 | setIsPerformingAction(false); |
| 152 | }); |
| 153 | } |
| 154 | } else { |
| 155 | const upgradeModalRef = { ...upgradeModal }; |
| 156 | upgradeModalRef.enable = true; |
| 157 | // Handle Pro Upgrade notice |
| 158 | dispatch({ |
| 159 | type: actionTypes.GET_UPGRADE_MODAL, |
| 160 | upgradeModal: upgradeModalRef, |
| 161 | }); |
| 162 | } |
| 163 | }; |
| 164 | |
| 165 | useEffect(() => { |
| 166 | setModuleStatus(data.status); |
| 167 | |
| 168 | if (!upgradeModal.enable) { |
| 169 | setIsPerformingAction(false); |
| 170 | } |
| 171 | |
| 172 | if (isPro) { |
| 173 | setModuleEnabled(true); |
| 174 | if (licensePlan) { |
| 175 | const requiredPlan = licensePlan; |
| 176 | |
| 177 | if (data.plan && data.plan.includes(requiredPlan.trim())) { |
| 178 | setRequirementFulfilled(true); |
| 179 | } else { |
| 180 | setModuleEnabled(false); |
| 181 | } |
| 182 | setLicenseActivated(true); |
| 183 | } else { |
| 184 | setLicenseActivated(false); |
| 185 | setModuleEnabled(false); |
| 186 | if(FreeModules.includes(data.slug)){ |
| 187 | setModuleEnabled(true); |
| 188 | }else{ |
| 189 | setModuleEnabled(false); |
| 190 | } |
| 191 | } |
| 192 | } else { |
| 193 | if(FreeModules.includes(data.slug)){ |
| 194 | setModuleEnabled(true); |
| 195 | }else{ |
| 196 | setModuleEnabled(false); |
| 197 | } |
| 198 | } |
| 199 | }, [data, upgradeModal]); |
| 200 | |
| 201 | useEffect(() => { |
| 202 | if (thumbnailVideoPlaying) { |
| 203 | setShowPlayVideoButton(false); |
| 204 | } |
| 205 | }, [thumbnailVideoPlaying]); |
| 206 | |
| 207 | const handleBoxClick = () => { |
| 208 | const upgradeModalRef = { ...upgradeModal }; |
| 209 | upgradeModalRef.moduleType = data.type; |
| 210 | upgradeModalRef.moduleName = data.name; |
| 211 | |
| 212 | if (!isPro) { |
| 213 | const plan_upgrade_url = upgradeURL + '&utm_source=dashboard-all-feature&utm_medium=dashboard-upgrade-plan' |
| 214 | window.open(plan_upgrade_url,'_blank'); |
| 215 | } else if (isPro && !licenseActivated) { |
| 216 | upgradeModalRef.type = "license"; |
| 217 | upgradeModalRef.enable = true; |
| 218 | } else if (isPro && licenseActivated && !requirementFulfilled) { |
| 219 | upgradeModalRef.type = "requirement"; |
| 220 | upgradeModalRef.enable = true; |
| 221 | } else { |
| 222 | upgradeModalRef.enable = false; |
| 223 | } |
| 224 | |
| 225 | dispatch({ |
| 226 | type: actionTypes.GET_UPGRADE_MODAL, |
| 227 | upgradeModal: upgradeModalRef, |
| 228 | }); |
| 229 | }; |
| 230 | |
| 231 | const handleModuleSettingsURL = () => { |
| 232 | var settingsURL = adminURL + setting_url |
| 233 | window.open(settingsURL, '_blank'); |
| 234 | } |
| 235 | |
| 236 | return ( |
| 237 | <Box |
| 238 | overflow="hidden" |
| 239 | boxShadow="none" |
| 240 | border="1px" |
| 241 | borderRadius="base" |
| 242 | borderColor="gray.100" |
| 243 | display="flex" |
| 244 | flexDir="column" |
| 245 | bg="white" |
| 246 | > |
| 247 | <Box |
| 248 | p="0" |
| 249 | flex="1 1 0%" |
| 250 | position="relative" |
| 251 | overflow="visible" |
| 252 | opacity={moduleEnabled ? 1 : 0.7} |
| 253 | > |
| 254 | |
| 255 | <Box |
| 256 | position="relative" |
| 257 | borderTopRightRadius="sm" |
| 258 | borderTopLeftRadius="sm" |
| 259 | overflow="hidden" |
| 260 | height={"178px"} |
| 261 | onMouseLeave={() => demo_video_url && setShowPlayVideoButton(false)} |
| 262 | > |
| 263 | |
| 264 | {((demo_video_url && !thumbnailVideoPlaying) || !demo_video_url) && ( |
| 265 | <Image |
| 266 | src={assetsURL + image} |
| 267 | borderTopRightRadius="sm" |
| 268 | borderTopLeftRadius="sm" |
| 269 | w="full" |
| 270 | height={"178px"} |
| 271 | onMouseOver={() => |
| 272 | {if (demo_video_url) { |
| 273 | setShowPlayVideoButton(true); |
| 274 | } |
| 275 | } |
| 276 | } |
| 277 | /> |
| 278 | )} |
| 279 | |
| 280 | |
| 281 | {thumbnailVideoPlaying && ( |
| 282 | <Modal isOpen={true} onClose={() => setThumbnailVideoPlaying(false)} size="3xl"> |
| 283 | <ModalOverlay /> |
| 284 | <ModalContent px={4} pb={4}> |
| 285 | <ModalHeader textAlign="center">{title}</ModalHeader> |
| 286 | <ModalCloseButton/> |
| 287 | <YouTubePlayer |
| 288 | url={'https://www.youtube.com/embed/'+demo_video_url} |
| 289 | playing={true} |
| 290 | width={'100%'} |
| 291 | controls |
| 292 | onReady={() => setThumbnailVideoLoading(false)} |
| 293 | onBufferEnd={() => setThumbnailVideoLoading(false)} |
| 294 | /> |
| 295 | |
| 296 | {thumbnailVideoLoading && ( |
| 297 | <Box |
| 298 | position={'absolute'} |
| 299 | top={'50%'} |
| 300 | left={'50%'} |
| 301 | transform={'translate(-50%, -50%)'} |
| 302 | > |
| 303 | <Spinner size={'lg'} /> |
| 304 | </Box> |
| 305 | )} |
| 306 | </ModalContent> |
| 307 | </Modal> |
| 308 | )} |
| 309 | |
| 310 | {showPlayVideoButton && ( |
| 311 | <Box |
| 312 | pos="absolute" |
| 313 | top={0} |
| 314 | left={0} |
| 315 | right={0} |
| 316 | bottom={0} |
| 317 | bg="black" |
| 318 | opacity={0.7} |
| 319 | display="flex" |
| 320 | alignItems="center" |
| 321 | justifyContent="center" |
| 322 | borderTopStartRadius={10} |
| 323 | borderTopEndRadius={10} |
| 324 | > |
| 325 | <Tooltip label={__('Play Video', 'everest-forms')}> |
| 326 | <span> |
| 327 | <FaPlayCircle |
| 328 | color="white" |
| 329 | size={50} |
| 330 | cursor={'pointer'} |
| 331 | onClick={() => { |
| 332 | setThumbnailVideoPlaying(true); |
| 333 | setThumbnailVideoLoading(true); |
| 334 | }} |
| 335 | /> |
| 336 | </span> |
| 337 | </Tooltip> |
| 338 | </Box> |
| 339 | )} |
| 340 | |
| 341 | { |
| 342 | data.dependent_status === 'inactive' && ( |
| 343 | <Box |
| 344 | pos="absolute" |
| 345 | left={0} |
| 346 | bottom={0} |
| 347 | bg="rgba(0, 0, 0, 0.7)" |
| 348 | padding={"8px 20px"} |
| 349 | display="flex" |
| 350 | justifyContent="center" |
| 351 | backdropFilter="blur(5px)" |
| 352 | width={'100%'} |
| 353 | > |
| 354 | <Image src={_EVF_DASHBOARD_.alert_icon} w={'5'} h={'5'}/> |
| 355 | <Text |
| 356 | color="white" |
| 357 | fontWeight={600} |
| 358 | fontSize={'14px'} |
| 359 | lineHeight={'21px'} |
| 360 | marginLeft="10px" |
| 361 | |
| 362 | > |
| 363 | Activate { data.required_plugin } plugin to use this addon. |
| 364 | </Text> |
| 365 | </Box> |
| 366 | ) |
| 367 | |
| 368 | } |
| 369 | |
| 370 | </Box> |
| 371 | <Badge |
| 372 | backgroundColor="black" |
| 373 | color="white" |
| 374 | position="absolute" |
| 375 | top="0" |
| 376 | right="0" |
| 377 | textTransform="none" |
| 378 | fontSize="12px" |
| 379 | fontWeight="500" |
| 380 | p="5px" |
| 381 | m="5px" |
| 382 | > |
| 383 | {data.required_plan ? FreeModules.includes(data.slug) ? 'Free' : data.required_plan : "Pro"} |
| 384 | </Badge> |
| 385 | <Box p="6"> |
| 386 | <Stack direction="column" spacing="4"> |
| 387 | <Stack |
| 388 | direction="row" |
| 389 | align="center" |
| 390 | justify="space-between" |
| 391 | > |
| 392 | <Heading |
| 393 | fontSize="sm" |
| 394 | fontWeight="semibold" |
| 395 | color="gray.700" |
| 396 | > |
| 397 | <Checkbox |
| 398 | isChecked={isChecked} |
| 399 | onChange={(e) => { |
| 400 | moduleEnabled |
| 401 | ? onCheckedChange( |
| 402 | slug, |
| 403 | e.target.checked |
| 404 | ) |
| 405 | : handleBoxClick(); |
| 406 | }} |
| 407 | opacity={ data.dependent_status === 'inactive' ? 0.5 : 1 } |
| 408 | > |
| 409 | {title} |
| 410 | </Checkbox> |
| 411 | </Heading> |
| 412 | { |
| 413 | data.is_dependent && ( |
| 414 | <Tooltip |
| 415 | showArrow |
| 416 | label={sprintf( |
| 417 | __( |
| 418 | "Requires %s", |
| 419 | "everest-forms" |
| 420 | ), |
| 421 | data.required_plugin |
| 422 | )} |
| 423 | > |
| 424 | <Box |
| 425 | cursor="pointer" |
| 426 | opacity={ data.status === 'active' ? 0.5 : 1 } |
| 427 | > |
| 428 | <svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg"> |
| 429 | <path d="M12.5 17C12.7833 17 13.021 16.904 13.213 16.712C13.405 16.52 13.5007 16.2827 13.5 16V12C13.5 11.7167 13.404 11.4793 13.212 11.288C13.02 11.0967 12.7827 11.0007 12.5 11C12.2173 10.9993 11.98 11.0953 11.788 11.288C11.596 11.4807 11.5 11.718 11.5 12V16C11.5 16.2833 11.596 16.521 11.788 16.713C11.98 16.905 12.2173 17.0007 12.5 17ZM12.5 9C12.7833 9 13.021 8.904 13.213 8.712C13.405 8.52 13.5007 8.28267 13.5 8C13.4993 7.71733 13.4033 7.48 13.212 7.288C13.0207 7.096 12.7833 7 12.5 7C12.2167 7 11.9793 7.096 11.788 7.288C11.5967 7.48 11.5007 7.71733 11.5 8C11.4993 8.28267 11.5953 8.52033 11.788 8.713C11.9807 8.90567 12.218 9.00133 12.5 9ZM12.5 22C11.1167 22 9.81667 21.7373 8.6 21.212C7.38334 20.6867 6.325 19.9743 5.425 19.075C4.525 18.1757 3.81267 17.1173 3.288 15.9C2.76333 14.6827 2.50067 13.3827 2.5 12C2.49933 10.6173 2.762 9.31733 3.288 8.1C3.814 6.88267 4.52633 5.82433 5.425 4.925C6.32367 4.02567 7.382 3.31333 8.6 2.788C9.818 2.26267 11.118 2 12.5 2C13.882 2 15.182 2.26267 16.4 2.788C17.618 3.31333 18.6763 4.02567 19.575 4.925C20.4737 5.82433 21.1863 6.88267 21.713 8.1C22.2397 9.31733 22.502 10.6173 22.5 12C22.498 13.3827 22.2353 14.6827 21.712 15.9C21.1887 17.1173 20.4763 18.1757 19.575 19.075C18.6737 19.9743 17.6153 20.687 16.4 21.213C15.1847 21.739 13.8847 22.0013 12.5 22Z" fill="#ECC94B"/> |
| 430 | </svg> |
| 431 | </Box> |
| 432 | </Tooltip> |
| 433 | ) |
| 434 | } |
| 435 | </Stack> |
| 436 | |
| 437 | <Text |
| 438 | fontWeight="400" |
| 439 | fontSize="14px" |
| 440 | color="gray.500" |
| 441 | textAlign="left" |
| 442 | > |
| 443 | {excerpt} |
| 444 | </Text> |
| 445 | </Stack> |
| 446 | </Box> |
| 447 | </Box> |
| 448 | |
| 449 | <Divider color="gray.300" /> |
| 450 | <Box |
| 451 | px="4" |
| 452 | py="5" |
| 453 | justifyContent="space-between" |
| 454 | alignItems="center" |
| 455 | display="flex" |
| 456 | > |
| 457 | <HStack align="center" flexDirection={"column"} alignItems={"unset"} gap={"0"}> |
| 458 | <Link |
| 459 | href={link} |
| 460 | fontSize="xs" |
| 461 | color="gray.500" |
| 462 | textDecoration="underline" |
| 463 | isExternal |
| 464 | > |
| 465 | {__("Documentation", "everest-forms")} |
| 466 | </Link> |
| 467 | <Link |
| 468 | href={liveDemoURL} |
| 469 | fontSize="xs" |
| 470 | color="gray.500" |
| 471 | textDecoration="underline" |
| 472 | isExternal |
| 473 | > |
| 474 | {__("Live Demo", "everest-forms")} |
| 475 | </Link> |
| 476 | </HStack> |
| 477 | |
| 478 | {moduleEnabled && ( |
| 479 | ((setting_url !== "" && moduleStatus === "active") && ( |
| 480 | <IconButton |
| 481 | size='sm' |
| 482 | icon={<SettingsIcon />} |
| 483 | onClick={handleModuleSettingsURL} |
| 484 | /> |
| 485 | )) |
| 486 | )} |
| 487 | |
| 488 | {moduleEnabled && ( |
| 489 | <> |
| 490 | {isAddonActivating ? ( |
| 491 | <Spinner |
| 492 | speed='0.50s' |
| 493 | emptyColor='gray.200' |
| 494 | color='blue.500' |
| 495 | size='md' |
| 496 | /> |
| 497 | ) : ( |
| 498 | <Switch |
| 499 | isChecked={moduleStatus === 'active'} |
| 500 | onChange={moduleEnabled ? handleModuleAction : handleBoxClick} |
| 501 | colorScheme="green" |
| 502 | /> |
| 503 | )} |
| 504 | </> |
| 505 | )} |
| 506 | |
| 507 | |
| 508 | {(!moduleEnabled) &&( |
| 509 | <Button |
| 510 | colorScheme={"primary"} |
| 511 | size="sm" |
| 512 | fontSize="xs" |
| 513 | borderRadius="base" |
| 514 | fontWeight="semibold" |
| 515 | _hover={{ |
| 516 | color: "white", |
| 517 | textDecoration: "none", |
| 518 | }} |
| 519 | _focus={{ |
| 520 | color: "white", |
| 521 | textDecoration: "none", |
| 522 | }} |
| 523 | onClick={moduleEnabled ? handleModuleAction : handleBoxClick} |
| 524 | isLoading={ |
| 525 | isPerformingAction || |
| 526 | (selectedModuleData.hasOwnProperty(slug) && |
| 527 | isPerformingBulkAction) |
| 528 | } |
| 529 | > |
| 530 | {__("Upgrade Plan", "everest-forms")} |
| 531 | </Button> |
| 532 | )} |
| 533 | </Box> |
| 534 | </Box> |
| 535 | ); |
| 536 | }; |
| 537 | |
| 538 | export default ModuleItem; |
| 539 |