Modules.js
427 lines
| 1 | import React, { useContext, useEffect, useState, useCallback } from "react"; |
| 2 | import { |
| 3 | Box, |
| 4 | Container, |
| 5 | Stack, |
| 6 | Select, |
| 7 | Tabs, |
| 8 | Tab, |
| 9 | TabList, |
| 10 | TabPanels, |
| 11 | TabPanel, |
| 12 | Button, |
| 13 | InputGroup, |
| 14 | InputLeftElement, |
| 15 | Input, |
| 16 | FormControl, |
| 17 | useToast, |
| 18 | Text |
| 19 | } from "@chakra-ui/react"; |
| 20 | import { __ } from "@wordpress/i18n"; |
| 21 | import { |
| 22 | getAllModules, |
| 23 | bulkActivateModules, |
| 24 | bulkDeactivateModules, |
| 25 | } from "./components/modules-api"; |
| 26 | import ModuleBody from "./components/ModuleBody"; |
| 27 | import AddonsSkeleton from "./../../skeleton/AddonsSkeleton/AddonsSkeleton"; |
| 28 | import { Search } from "./../../components/Icon/Icon"; |
| 29 | import dashboardReducer, { |
| 30 | actionTypes, |
| 31 | } from "./../../reducers/DashboardReducer"; |
| 32 | import DashboardContext from "./../../context/DashboardContext"; |
| 33 | import { debounce } from "lodash"; |
| 34 | |
| 35 | const Modules = () => { |
| 36 | const toast = useToast(); |
| 37 | const [modules, setModules] = useState([]); |
| 38 | const [originalModules, setOriginalModules] = useState([]); |
| 39 | const [error, setError] = useState(null); |
| 40 | const [selectedModuleData, setSelectedModuleData] = useState(""); |
| 41 | const [isSearching, setIsSearching] = useState(false); |
| 42 | const [tabIndex, setTabIndex] = useState(0); |
| 43 | const [isPerformingBulkAction, setIsPerformingBulkAction] = useState(false); |
| 44 | const [bulkAction, setBulkAction] = useState(""); |
| 45 | const [modulesLoaded, setModulesLoaded] = useState(false); |
| 46 | const [{ allModules }, dispatch] = useContext(DashboardContext); |
| 47 | const [searchItem, setSearchItem] = useState(''); |
| 48 | const [noItemFound, setNoItemFound] = useState(false); |
| 49 | |
| 50 | const fetchModules = useCallback(() => { |
| 51 | getAllModules() |
| 52 | .then((data) => { |
| 53 | if (data.success) { |
| 54 | dispatch({ |
| 55 | type: actionTypes.GET_ALL_MODULES, |
| 56 | allModules: data.modules_lists, |
| 57 | }); |
| 58 | |
| 59 | setOriginalModules(data.modules_lists); |
| 60 | filterModules(data.modules_lists); |
| 61 | setModulesLoaded(true); |
| 62 | } |
| 63 | }) |
| 64 | .catch((error) => { |
| 65 | setError(error.message); |
| 66 | }); |
| 67 | }, [dispatch, tabIndex]); |
| 68 | |
| 69 | const filterModules = (modules) => { |
| 70 | let filteredModules = modules; |
| 71 | |
| 72 | if (tabIndex === 1) { |
| 73 | filteredModules = modules.filter(module => module.type === "feature"); |
| 74 | } else if (tabIndex === 2) { |
| 75 | filteredModules = modules.filter(module => module.type === "addon"); |
| 76 | } |
| 77 | |
| 78 | setModules(filteredModules); |
| 79 | setModulesLoaded(true); |
| 80 | }; |
| 81 | |
| 82 | useEffect(() => { |
| 83 | fetchModules(); |
| 84 | }, [fetchModules]); |
| 85 | |
| 86 | useEffect(() => { |
| 87 | if (error !== null) { |
| 88 | toast({ |
| 89 | title: error, |
| 90 | status: "error", |
| 91 | duration: 3000, |
| 92 | }); |
| 93 | } |
| 94 | }, [error]); |
| 95 | |
| 96 | useEffect(() => { |
| 97 | filterModules(originalModules); |
| 98 | }, [tabIndex, originalModules]); |
| 99 | |
| 100 | const handleBulkActions = () => { |
| 101 | setIsPerformingBulkAction(true); |
| 102 | |
| 103 | const actionFunction = bulkAction === "activate" ? bulkActivateModules : bulkDeactivateModules; |
| 104 | |
| 105 | actionFunction(selectedModuleData) |
| 106 | .then((data) => { |
| 107 | toast({ |
| 108 | title: data.message, |
| 109 | status: data.success ? "success" : "error", |
| 110 | duration: 3000, |
| 111 | }); |
| 112 | }) |
| 113 | .catch((e) => { |
| 114 | toast({ |
| 115 | title: e.message, |
| 116 | status: "error", |
| 117 | duration: 3000, |
| 118 | }); |
| 119 | }) |
| 120 | .finally(() => { |
| 121 | setIsPerformingBulkAction(false); |
| 122 | setSelectedModuleData({}); |
| 123 | fetchModules(); |
| 124 | }); |
| 125 | }; |
| 126 | |
| 127 | const debounceSearch = debounce((val) => { |
| 128 | setIsSearching(true); |
| 129 | |
| 130 | if (!val) { |
| 131 | filterModules(originalModules); |
| 132 | setIsSearching(false); |
| 133 | return; |
| 134 | } |
| 135 | |
| 136 | let searchedData = []; |
| 137 | |
| 138 | if (tabIndex === 1) { |
| 139 | searchedData = originalModules.filter( |
| 140 | (module) => module.type === "feature" && module.title.toLowerCase().includes(val.toLowerCase()) |
| 141 | ); |
| 142 | } else if (tabIndex === 2) { |
| 143 | searchedData = originalModules.filter( |
| 144 | (module) => module.type === "addon" && module.title.toLowerCase().includes(val.toLowerCase()) |
| 145 | ); |
| 146 | } else { |
| 147 | searchedData = originalModules.filter((module) => module.title.toLowerCase().includes(val.toLowerCase())); |
| 148 | } |
| 149 | |
| 150 | if (searchedData.length > 0) { |
| 151 | setModules(searchedData); |
| 152 | setModulesLoaded(true); |
| 153 | setNoItemFound(false); |
| 154 | } else { |
| 155 | setModules([]); |
| 156 | setModulesLoaded(false); |
| 157 | setNoItemFound(true); |
| 158 | } |
| 159 | |
| 160 | setIsSearching(false); |
| 161 | }, 800); |
| 162 | |
| 163 | const handleSearchInputChange = (e) => { |
| 164 | const val = e.target.value; |
| 165 | setSearchItem(val); |
| 166 | debounceSearch(val); |
| 167 | }; |
| 168 | |
| 169 | const parseDate = (dateString) => { |
| 170 | const [day, month, year] = dateString.split("/").map(Number); |
| 171 | return new Date(year, month - 1, day); |
| 172 | }; |
| 173 | |
| 174 | const handleSorterChange = (sortType, data, setData) => { |
| 175 | switch (sortType) { |
| 176 | case "newest": |
| 177 | setData( |
| 178 | [...data].sort( |
| 179 | (firstAddonInContext, secondAddonInContext) => |
| 180 | parseDate(secondAddonInContext.released_date) - |
| 181 | parseDate(firstAddonInContext.released_date) |
| 182 | ) |
| 183 | ); |
| 184 | break; |
| 185 | case "oldest": |
| 186 | setData( |
| 187 | [...data].sort( |
| 188 | (firstAddonInContext, secondAddonInContext) => |
| 189 | parseDate(firstAddonInContext.released_date) - |
| 190 | parseDate(secondAddonInContext.released_date) |
| 191 | ) |
| 192 | ); |
| 193 | break; |
| 194 | case "asc": |
| 195 | setData( |
| 196 | [...data].sort( |
| 197 | (firstAddonInContext, secondAddonInContext) => |
| 198 | firstAddonInContext.title.localeCompare( |
| 199 | secondAddonInContext.title |
| 200 | ) |
| 201 | ) |
| 202 | ); |
| 203 | break; |
| 204 | case "desc": |
| 205 | setData( |
| 206 | [...data].sort( |
| 207 | (firstAddonInContext, secondAddonInContext) => secondAddonInContext.title.localeCompare(firstAddonInContext.title) |
| 208 | ) |
| 209 | ); |
| 210 | break; |
| 211 | default: |
| 212 | const sortedData = [...data].sort((firstAddonInContext, secondAddonInContext) => { |
| 213 | if ('popular_rank' in firstAddonInContext && 'popular_rank' in secondAddonInContext) { |
| 214 | return firstAddonInContext.popular_rank - secondAddonInContext.popular_rank; |
| 215 | } else if ('popular_rank' in firstAddonInContext) { |
| 216 | return -1; |
| 217 | } else if ('popular_rank' in secondAddonInContext) { |
| 218 | return 1; |
| 219 | } else { |
| 220 | return 0; |
| 221 | } |
| 222 | }); |
| 223 | setData(sortedData); |
| 224 | } |
| 225 | }; |
| 226 | |
| 227 | return ( |
| 228 | <Box top="var(--wp-admin--admin-bar--height, 0)" zIndex={1}> |
| 229 | <Container maxW="container.xl"> |
| 230 | <Stack |
| 231 | direction="row" |
| 232 | minH="70px" |
| 233 | justify="space-between" |
| 234 | px="6" |
| 235 | > |
| 236 | <Stack direction="row" align="center" gap="5"> |
| 237 | <Select |
| 238 | display="inline-flex" |
| 239 | alignItems="center" |
| 240 | size="md" |
| 241 | bg="#DFDFE0" |
| 242 | onChange={(e) => { |
| 243 | handleSorterChange( |
| 244 | e.target.value, |
| 245 | modules, |
| 246 | setModules |
| 247 | ); |
| 248 | }} |
| 249 | border="1px solid #DFDFE0 !important" |
| 250 | borderRadius="4px !important" |
| 251 | icon="" |
| 252 | width="fit-content" |
| 253 | > |
| 254 | <option value="default"> |
| 255 | {__("Popular", "everest-forms")} |
| 256 | </option> |
| 257 | <option value="newest"> |
| 258 | {__("Newest", "everest-forms")} |
| 259 | </option> |
| 260 | <option value="oldest"> |
| 261 | {__("Oldest", "everest-forms")} |
| 262 | </option> |
| 263 | <option value="asc"> |
| 264 | {__("Ascending", "everest-forms")} |
| 265 | </option> |
| 266 | <option value="desc"> |
| 267 | {__("Descending", "everest-forms")} |
| 268 | </option> |
| 269 | </Select> |
| 270 | |
| 271 | <Tabs |
| 272 | index={tabIndex} |
| 273 | onChange={(index) => { |
| 274 | setTabIndex(index); |
| 275 | }} |
| 276 | > |
| 277 | <TabList> |
| 278 | <Tab |
| 279 | onClick={() => handleSearchInputChange({ target: { value: searchItem } })} |
| 280 | > |
| 281 | {__("All Modules", "everest-forms")} |
| 282 | </Tab> |
| 283 | <Tab |
| 284 | onClick={() => handleSearchInputChange({ target: { value: searchItem } })} |
| 285 | > |
| 286 | {__("Features", "everest-forms")} |
| 287 | </Tab> |
| 288 | <Tab |
| 289 | onClick={() => handleSearchInputChange({ target: { value: searchItem } })} |
| 290 | > |
| 291 | {__("Addons", "everest-forms")} |
| 292 | </Tab> |
| 293 | </TabList> |
| 294 | </Tabs> |
| 295 | |
| 296 | <Box display="flex" gap="8px"> |
| 297 | <Select |
| 298 | display="inline-flex" |
| 299 | alignItems="center" |
| 300 | size="md" |
| 301 | bg="#DFDFE0" |
| 302 | placeholder={__( |
| 303 | "Bulk Actions", |
| 304 | "everest-forms" |
| 305 | )} |
| 306 | onChange={(e) => setBulkAction(e.target.value)} |
| 307 | icon="" |
| 308 | width="fit-content" |
| 309 | border="1px solid #DFDFE0 !important" |
| 310 | borderRadius="4px !important" |
| 311 | > |
| 312 | <option value="activate"> |
| 313 | {__("Activate", "everest-forms")} |
| 314 | </option> |
| 315 | <option value="deactivate"> |
| 316 | {__("Deactivate", "everest-forms")} |
| 317 | </option> |
| 318 | </Select> |
| 319 | |
| 320 | <Button |
| 321 | fontSize="14px" |
| 322 | variant="outline" |
| 323 | fontWeight="normal" |
| 324 | color="gray.600" |
| 325 | borderRadius="base" |
| 326 | border="1px solid #DFDFE0 !important" |
| 327 | textDecor="none !important" |
| 328 | padding="6px 12px" |
| 329 | onClick={handleBulkActions} |
| 330 | isLoading={isPerformingBulkAction} |
| 331 | > |
| 332 | {__("Apply", "everest-forms")} |
| 333 | </Button> |
| 334 | </Box> |
| 335 | </Stack> |
| 336 | <Stack direction="row" align="center" gap="7"> |
| 337 | <FormControl> |
| 338 | <InputGroup> |
| 339 | <InputLeftElement |
| 340 | pointerEvents="none" |
| 341 | top="2px" |
| 342 | > |
| 343 | <Search h="5" w="5" color="gray.300" /> |
| 344 | </InputLeftElement> |
| 345 | <Input |
| 346 | type="text" |
| 347 | placeholder={__( |
| 348 | "Search...", |
| 349 | "everest-forms" |
| 350 | )} |
| 351 | paddingLeft="32px !important" |
| 352 | value={searchItem} onChange={handleSearchInputChange} |
| 353 | /> |
| 354 | </InputGroup> |
| 355 | </FormControl> |
| 356 | </Stack> |
| 357 | </Stack> |
| 358 | </Container> |
| 359 | <Container maxW="container.xl"> |
| 360 | { |
| 361 | isSearching ? ( |
| 362 | <AddonsSkeleton /> |
| 363 | ) : ( |
| 364 | noItemFound ? ( |
| 365 | <Text |
| 366 | align="center" |
| 367 | fontSize="1.2rem" |
| 368 | bg="red" |
| 369 | color="white" |
| 370 | p={4} |
| 371 | m="5rem auto 0" |
| 372 | width="60%" |
| 373 | borderRadius="10px" |
| 374 | fontWeight="bold" |
| 375 | > |
| 376 | {(() => { |
| 377 | switch (tabIndex) { |
| 378 | case 1: |
| 379 | return __("Sorry, No features found", "everest-forms"); |
| 380 | case 2: |
| 381 | return __("Sorry, No addons found", "everest-forms"); |
| 382 | default: |
| 383 | return __("Sorry, No modules found", "everest-forms"); |
| 384 | } |
| 385 | })()} |
| 386 | </Text> |
| 387 | ) : ( |
| 388 | <Box> |
| 389 | <Tabs index={tabIndex}> |
| 390 | <TabPanels> |
| 391 | <TabPanel> |
| 392 | <ModuleBody |
| 393 | isPerformingBulkAction={isPerformingBulkAction} |
| 394 | filteredAddons={modules} |
| 395 | setSelectedModuleData={setSelectedModuleData} |
| 396 | selectedModuleData={selectedModuleData} |
| 397 | /> |
| 398 | </TabPanel> |
| 399 | <TabPanel> |
| 400 | <ModuleBody |
| 401 | isPerformingBulkAction={isPerformingBulkAction} |
| 402 | filteredAddons={modules} |
| 403 | setSelectedModuleData={setSelectedModuleData} |
| 404 | selectedModuleData={selectedModuleData} |
| 405 | /> |
| 406 | </TabPanel> |
| 407 | <TabPanel> |
| 408 | <ModuleBody |
| 409 | isPerformingBulkAction={isPerformingBulkAction} |
| 410 | filteredAddons={modules} |
| 411 | setSelectedModuleData={setSelectedModuleData} |
| 412 | selectedModuleData={selectedModuleData} |
| 413 | /> |
| 414 | </TabPanel> |
| 415 | </TabPanels> |
| 416 | </Tabs> |
| 417 | </Box> |
| 418 | ) |
| 419 | ) |
| 420 | } |
| 421 | </Container> |
| 422 | </Box> |
| 423 | ); |
| 424 | }; |
| 425 | |
| 426 | export default Modules; |
| 427 |