import React, { useEffect, useState, useRef } from "react"; import theme from "../theme.jsx"; import ReactGA from "react-ga4"; import { Link } from "react-router-dom"; import { removeQuery } from "../components/ScrollToTop.jsx"; import { useMemo } from "react"; import { Tabs, Tab } from "@mui/material"; import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; import ExpandLessIcon from "@mui/icons-material/ExpandLess"; import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon, } from "@mui/icons-material"; import { toast } from "react-toastify" import ClearIcon from '@mui/icons-material/Clear'; import Box from '@mui/material/Box'; import noImage from "../no_image.png" import CircularProgress from '@mui/material/CircularProgress'; import algoliasearch from "algoliasearch/lite"; import { InstantSearch, Configure, connectSearchBox, connectHits, connectHitInsights, RefinementList, ClearRefinements, connectStateResults } from "react-instantsearch-dom"; import aa from "search-insights"; import "./FilterCSS.css"; import { Zoom, Grid, Paper, TextField, ButtonBase, InputAdornment, Typography, Button, Tooltip, } from "@mui/material"; const searchClient = algoliasearch( "JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240" ); //const searchClient = algoliasearch("L55H18ZINA", "a19be455e7e75ee8f20a93d26b9fc6d6") const AppGrid = (props) => { const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, isHeader, } = props; const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows; const xs = parsedXs === undefined || parsedXs === null ? (isMobile ? 6 : 3) : parsedXs; const [formMail, setFormMail] = React.useState(""); const [message, setMessage] = React.useState(""); const [formMessage, setFormMessage] = React.useState(""); const buttonStyle = { borderRadius: 30, height: 50, width: 220, margin: isMobile ? "15px auto 15px auto" : 20, fontSize: 18, }; const innerColor = "rgba(255,255,255,0.65)"; const borderRadius = 3; window.title = "Shuffle | Apps | Find and integrate any app"; const submitContact = (email, message) => { const data = { firstname: "", lastname: "", title: "", companyname: "", email: email, phone: "", message: message, }; const errorMessage = "Something went wrong. Please contact frikky@shuffler.io directly."; fetch(globalUrl + "/api/v1/contact", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(data), }) .then((response) => response.json()) .then((response) => { if (response.success === true) { setFormMessage(response.reason); //toast("Thanks for submitting!") } else { setFormMessage(errorMessage); } setFormMail(""); setMessage(""); }) .catch((error) => { setFormMessage(errorMessage); console.log(error); }); }; const SearchBox = ({ currentRefinement, refine, isSearchStalled }) => { var defaultSearch = ""; var [searchQuery, setSearchQuery] = useState(""); //useEffect(() => { if ( window !== undefined && window.location !== undefined && window.location.search !== undefined && window.location.search !== null ) { const urlSearchParams = new URLSearchParams(window.location.search); const params = Object.fromEntries(urlSearchParams.entries()); const foundQuery = params["q"]; if (foundQuery !== null && foundQuery !== undefined) { console.log("Got query: ", foundQuery); refine(foundQuery); defaultSearch = foundQuery; searchQuery = foundQuery } } //}, []) const handleSearch = () => { refine(searchQuery.trim()); }; return (
), endAdornment: ( {searchQuery.length > 0 && ( { setSearchQuery('') removeQuery("q"); refine('') }} /> )} ), }} autoComplete="off" color="primary" placeholder="Find Apps" id="shuffle_search_field" onChange={(event) => { setSearchQuery(event.currentTarget.value); removeQuery("q"); refine(event.currentTarget.value); }} limit={5} /> {/*isSearchStalled ? 'My search is stalled' : ''*/} ); }; const [currTab, setCurrTab] = useState(0); const handleTabChange = (event, newValue) => { setCurrTab(newValue); }; const [isLoggedIn, setIsLoggedIn] = useState(false); const [userInfo, setUserInfo] = useState([]); useEffect(() => { var baseurl = globalUrl; fetch(baseurl + "/api/v1/getinfo", { credentials: "include", headers: { 'Content-Type': 'application/json', }, }) .then(response => response.json()) .then(responseJson => { if (responseJson.success) { setIsLoggedIn(true); setUserInfo(responseJson); } }) .catch(error => { console.log("Failed login check: ", error); }); }, []); //Component to fetch all app from the algolia const Hits = ({ hits, insights, setIsAnyAppActivated }) => { const [mouseHoverIndex, setMouseHoverIndex] = useState(-1); var counted = 0; const [hoverEffect, setHoverEffect] = useState(-1); const normalizedString = (name) => { if (typeof name === 'string') { return name.replace(/_/g, ' '); } else { return name; } }; const [allActivatedAppIds, setAllActivatedAppIds] = useState(() => { const storedApps = isLoggedIn && localStorage.getItem('allActivatedAppIds'); return storedApps ? JSON.parse(storedApps) : userInfo.active_apps; }); const [isAppActivated, setIsAppActivated] = useState(false); const [isActivateAppSuccess, setIsActivateAppSuccess] = useState(false); //Function for activation and deactivation of app const handleActivateButton = (event, data, type) => { event.preventDefault(); if (!isLoggedIn) { toast.error("Please log in to your account to activate the app.") return; } if (type === "activate") { toast.success(`The ${normalizedString(data.name)} app is activating. Please wait...`); } if (type === "deactivate") { toast.success(`The ${normalizedString(data.name)} app is deactivating. Please wait...`); } const baseURL = globalUrl; const url = `${baseURL}/api/v1/apps/${data.objectID}/${type}`; fetch(url, { method: 'GET', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, credentials: "include", }) .then((response) => response.json()) .then((responseJson) => { if (responseJson.success === false) { toast.error(responseJson.reason); } else { toast.success(`App ${type}d Successfully!`); if (type === 'activate') { setAllActivatedAppIds(prev => [...prev, data.objectID]); setIsAnyAppActivated(true); } if (type === 'deactivate') { const updatedIds = allActivatedAppIds.filter(id => id !== data.objectID); setAllActivatedAppIds(updatedIds); } setIsActivateAppSuccess(prev => !prev); } }) .catch(error => { console.log("app error: ", error.toString()); }); } useEffect(() => { isLoggedIn && localStorage.setItem('allActivatedAppIds', JSON.stringify(allActivatedAppIds)); }, [allActivatedAppIds]); const memoizedHits = useMemo(() => { return hits.map((data, index) => { let workflowDelay = 0; const isHeader = true; const paperStyle = { color: "rgba(241, 241, 241, 1)", padding: isHeader ? null : 15, cursor: "pointer", maxWidth: 339, maxHeight: 96, borderRadius: 8, transition: 'background-color 0.3s ease', backgroundColor: "rgba(26, 26, 26, 1)", }; const appUrl = isCloud ? `/apps/${data.objectID}?queryID=${data.__queryID}` : `https://shuffler.io/apps/${data.objectID}?queryID=${data.__queryID}`; //check if appExist in userInfo.active_app or not. return ( { setMouseHoverIndex(index); }} onMouseOut={() => { setMouseHoverIndex(-1); }} > ) : ( )} )} ); }); }, [hits, mouseHoverIndex, isActivateAppSuccess, allActivatedAppIds]); return (
{memoizedHits}
); }; var workflowDelay = -50; const CustomClearRefinements = connectStateResults(({ searchResults, ...rest }) => { const hasFilters = searchResults && searchResults.nbHits !== searchResults.nbSortedHits; return Clear All }} {...rest} disabled={!hasFilters} />; }); //Component to Filter all apps base on category const FilterAllAppsByCategory = () => { const [isRefinementListExpanded, setIsRefinementListExpanded] = useState(true); const toggleRefinementList = () => { setIsRefinementListExpanded((prevState) => !prevState); }; const categoryButtonStyling = { cursor: "pointer", color: "white", border: "none", backgroundColor: "transparent", fontSize: 16, display: "flex", width: "100%", height: 30, flexDirection: "row", textTransform: 'none', fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)" } return (
{isRefinementListExpanded && ( <> )}
); }; //Component to filter all apps base on Action label const FilterByActionLabel = () => { const [isActionLabelExpanded, setIsActionLabelExpanded] = useState(false); useState(false); const toogleActionLabel = () => { setIsActionLabelExpanded((prevState) => !prevState); }; const actionLabelButtonStyling = { cursor: "pointer", color: "white", border: "none", backgroundColor: "transparent", fontSize: 16, display: "flex", flexDirection: "row", width: "100%", height: 30, textTransform: 'none', fontWeight: 400 } return (
{isActionLabelExpanded && ( <> )}
); }; //component to filter all apps base on the created with like 'App Editor' or 'Python' const FilterByCreatedWith = () => { const [isCreatedWithExpanded, setIsCreatedWithExpanded] = useState(false); const toogleCreatedWith = () => { setIsCreatedWithExpanded((prevState) => !prevState); }; const transformRefinementListItems = items => items.map(item => ({ ...item, label: item.label === 'true' ? 'App Editor' : 'Python', })); const createdWithButtonStyling = { cursor: "pointer", color: "white", border: "none", backgroundColor: "transparent", fontSize: 16, display: "flex", flexDirection: "row", alignItems: "center", width: "100%", height: 30, textTransform: 'none', fontWeight: 400 } return (
{isCreatedWithExpanded && ( <> )}
); }; const FilterCreatedBy = () => { const [isCreatedByExpanded, setIscreatedByExpanded] = useState(false); useState(false); const toogleCreatedBy = () => { setIscreatedByExpanded((prevState) => !prevState); }; const createdByButtonStyling = { cursor: "pointer", color: "white", border: "none", backgroundColor: "transparent", fontSize: 16, display: "flex", flexDirection: "row", alignItems: "center", whiteSpace: "nowrap", width: "100%", height: 30, textTransform: 'none', opacity: '0.5' } return (
{isCreatedByExpanded && ( <> {/* */} {/* */} )}
); }; const FilterApps = () => { return (
Filter By
); }; const boxStyle = { color: "white", flex: "1", marginLeft: isHeader ? null : 10, marginRight: isHeader ? null : 10, paddingLeft: isHeader ? null : 30, paddingRight: isHeader ? null : 30, paddingBottom: isHeader ? null : 30, display: "flex", flexDirection: "column", overflowX: "visible", backgroundColor: "rgba(33, 33, 33, 1)", borderRadius: 16, marginTop: 24, width: 741, height: 741, }; //Component to display all apps. const AllApps = ({ setIsAnyAppActivated }) => { return (
); }; //Search box for the orgs and users apps const SearchBoxForOrgsAndUsersApp = ({ searchQuery, setSearchQuery }) => { return (
), endAdornment: ( {searchQuery.length > 0 && ( setSearchQuery('')} /> )} ), }} autoComplete="off" color="primary" placeholder="Find Apps" id="shuffle_search_field" onChange={(event) => { setSearchQuery(event.currentTarget.value); }} limit={5} /> {/*isSearchStalled ? 'My search is stalled' : ''*/} ) } const [selectedCategoryForUsersAndOgsApps, setselectedCategoryForUsersAndOgsApps] = useState([]); const [selectedTagsForUserAndOrgApps, setSelectedTagsForUserAndOrgApps] = useState([]); const [isCategoreListExpanded, setIsCategoryListExpanded] = useState(true); const toogleCategoryList = () => { setIsCategoryListExpanded((prevState) => !prevState); }; //Component to display category List for User and Orgs app const FilterUsersAndOrgsAppByCategory = ({ userAndOrgsApp }) => { //Display top 9 category from the database const findTopCategories = () => { const categoryCountMap = {}; // Check if userAndOrgsApp is an array before iterating over it and Find top 10 Category from the apps if (Array.isArray(userAndOrgsApp)) { userAndOrgsApp.forEach((app) => { const categories = app.categories; if (categories && categories.length > 0) { categories.forEach((category) => { categoryCountMap[category] = (categoryCountMap[category] || 0) + 1; }); } }); const categoryArray = Object.keys(categoryCountMap).map((category) => ({ category, count: categoryCountMap[category], })); categoryArray.sort((a, b) => b.count - a.count); const topCategories = categoryArray.slice(0, 9); return topCategories; } }; const topCategories = findTopCategories(); const handleCheckboxChange = (category) => { if (selectedCategoryForUsersAndOgsApps.includes(category)) { setselectedCategoryForUsersAndOgsApps(selectedCategoryForUsersAndOgsApps.filter((item) => item !== category)); } else { setselectedCategoryForUsersAndOgsApps([...selectedCategoryForUsersAndOgsApps, category]); } }; const handleClearFilter = () => { setselectedCategoryForUsersAndOgsApps([]); }; const categorysButtonStyling = { cursor: "pointer", color: "white", border: "none", backgroundColor: "transparent", fontSize: 16, display: "flex", width: "100%", height: 30, flexDirection: "row", textTransform: 'none', fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)" } return (
{isCategoreListExpanded && topCategories && topCategories.length > 0 && (
{topCategories.map((data, index) => ( ))}
)}
); }; const [isActionLabelExpanded, setIsActionLabelExpanded] = useState(false); const toogleActionLabel = () => { setIsActionLabelExpanded((prevState) => !prevState); }; const FilterUsersAndOrgsAppByActionLabel = ({ userAndOrgsApp }) => { const findTopTags = () => { const tagCountMap = {}; // Check if userAndOrgsApp is an array before iterating over it and Find top 10 tags from the apps if (Array.isArray(userAndOrgsApp)) { userAndOrgsApp.forEach((app) => { const tags = app.tags; if (tags && tags.length > 0) { tags.forEach((tag) => { tagCountMap[tag] = (tagCountMap[tag] || 0) + 1; }); } }); } const tagArray = Object.keys(tagCountMap).map((tag) => ({ tag, count: tagCountMap[tag], })); tagArray.sort((a, b) => b.count - a.count); const topTags = tagArray.slice(0, 9); return topTags; }; const topTags = findTopTags(); const [selectedCategories, setSelectedCategories] = useState([]); const handleCheckboxChange = (index) => { const category = topTags[index].tag; const updatedCheckboxStates = [...selectedTagsForUserAndOrgApps]; if (updatedCheckboxStates.includes(category)) { setSelectedTagsForUserAndOrgApps(updatedCheckboxStates.filter((item) => item !== category)); } else { setSelectedTagsForUserAndOrgApps([...updatedCheckboxStates, category]); } }; const handleClearFilter = () => { setSelectedTagsForUserAndOrgApps([]); }; const actionLabelButtonStyling = { cursor: "pointer", color: "white", border: "none", backgroundColor: "transparent", fontSize: 16, display: "flex", flexDirection: "row", width: "100%", height: 30, textTransform: 'none', marginBottom: isActionLabelExpanded && 16, fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)" } return (
{isActionLabelExpanded && topTags && topTags.length > 0 && ( <> {topTags.map((data, index) => ( ))} )}
); }; const [selectedOptionOfCreatedWith, setSelectedOptionOfCreatedWith] = useState([]); const [isCreatedWithExpanded, setIsCreatedWithExpanded] = useState(false); const toogleCreatedWith = () => { setIsCreatedWithExpanded((prevState) => !prevState); }; const FilterUsersAndOrgsAppByCreatedWith = () => { const AppCreatedWithOptions = ['App Editor', 'Python'] const handleCheckboxChange = (index) => { const category = AppCreatedWithOptions[index]; const updatedCheckboxStates = [...selectedOptionOfCreatedWith]; if (updatedCheckboxStates.includes(category)) { setSelectedOptionOfCreatedWith(updatedCheckboxStates.filter((item) => item !== category)); } else { setSelectedOptionOfCreatedWith([...updatedCheckboxStates, category]); } }; const handleClearFilter = () => { setSelectedOptionOfCreatedWith([]); }; const createdWithButtonStyling = { cursor: "pointer", color: "white", border: "none", backgroundColor: "transparent", fontSize: 16, display: "flex", flexDirection: "row", alignItems: "center", width: "100%", height: 30, textTransform: 'none', marginBottom: isCreatedWithExpanded && 16, fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)" } return (
{isCreatedWithExpanded && ( <> {AppCreatedWithOptions.map((data, index) => ( ))} )}
); }; const FilterUsersAndOrgsAppCreatedBy = () => { const [isCreatedByExpanded, setIscreatedByExpanded] = useState(false); useState(false); const toogleCreatedBy = () => { setIscreatedByExpanded((prevState) => !prevState); }; const [isButtonDisable, setIsButtonDisable] = useState(true) const createdByButtonStyling = { cursor: "pointer", color: "white", border: "none", backgroundColor: isButtonDisable ? '#3c3c3c. ' : "transparent", fontSize: 16, display: "flex", flexDirection: "row", alignItems: "center", whiteSpace: "nowrap", width: "100%", height: 30, textTransform: 'none', opacity: '0.5' } return (
{isCreatedByExpanded && ( <> )}
); }; const FilterUserAndOrgApps = () => { const [userAndOrgsApp, setUserAndOrgsApp] = useState([]); useEffect(() => { if (currTab === 2) { const baseUrl = globalUrl; const userAppsUrl = `${baseUrl}/api/v1/users/apps`; fetch(userAppsUrl, { method: "GET", credentials: "include", headers: { "Content-Type": "application/json", }, }) .then((response) => response.json()) .then((data) => { setUserAndOrgsApp(data); }) .catch((err) => { console.error("Error fetching user apps:", err); }); } else if (currTab === 1) { const baseUrl = globalUrl; const appsUrl = `${baseUrl}/api/v1/apps`; fetch(appsUrl, { method: "GET", credentials: "include", headers: { "Content-Type": "application/json", }, }) .then((response) => response.json()) .then((data) => { setUserAndOrgsApp(data); }) .catch((err) => { console.error("Error fetching apps:", err); }); } }, [currTab]); return (
{isLoggedIn === true && (
Filter By
)}
) } const [isLoading, setIsLoading] = useState(false) useEffect(() => { if (currTab) { setselectedCategoryForUsersAndOgsApps([]); setSelectedTagsForUserAndOrgApps([]); setSelectedOptionOfCreatedWith([]); setIsCategoryListExpanded(true); setIsActionLabelExpanded(false); setIsCreatedWithExpanded(false); } if (currTab === 1 || currTab === 2) { setIsLoading(true); } }, [currTab]) //Component to fetch all apps created by user and Org const UserAndOrgApps = () => { const [searchQuery, setSearchQuery] = useState(""); const [userAndOrgAppData, setUserAndOrgAppData] = useState([]) const allActivatedAppIdsString = localStorage.getItem('allActivatedAppIds'); const allActivatedAppIds = allActivatedAppIdsString ? JSON.parse(allActivatedAppIdsString) : []; const latestActivatedAppId = allActivatedAppIds.length > 0 ? allActivatedAppIds[allActivatedAppIds.length - 1] : null; useEffect(() => { if (currTab === 2 && isLoggedIn != undefined && isLoggedIn != null && isLoggedIn === true) { const baseUrl = globalUrl; const URL = `${baseUrl}/api/v1/users/apps`; fetch(URL, { method: "GET", credentials: "include", headers: { "Content-Type": "application/json", }, }) .then((response) => { return response.json(); }) .then((data) => { setUserAndOrgAppData(data) setIsLoading(false) }) .catch((err) => { console.error("Error fetching user apps:", err); }); } else if (currTab === 1 && isLoggedIn != undefined && isLoggedIn != null && isLoggedIn === true) { const baseUrl = globalUrl; const URL = `${baseUrl}/api/v1/apps`; fetch(URL, { method: "GET", credentials: "include", headers: { "Content-Type": "application/json", }, }) .then((response) => { return response.json(); }) .then((data) => { setUserAndOrgAppData(data) setIsLoading(false); }) .catch((err) => { console.error("Error fetching user apps:", err); }); } }, []) //Search app base on app name, category and tag const filteredUserAppdata = Array.isArray(userAndOrgAppData) ? userAndOrgAppData.filter((app) => { const matchesSearchQuery = ( searchQuery === "" || app.name.toLowerCase().includes(searchQuery.toLowerCase()) || (app.tags && app.tags.some(tag => tag.toLowerCase().includes(searchQuery.toLowerCase()) )) || (app.categories && app.categories.some((category) => category.toLowerCase().includes(searchQuery.toLowerCase()) )) ); const matchesSelectedCategories = ( selectedCategoryForUsersAndOgsApps.length === 0 || (app.categories && app.categories.some(category => selectedCategoryForUsersAndOgsApps.includes(category) )) ); const matchesSelectedTags = ( selectedTagsForUserAndOrgApps.length === 0 || (app.tags && selectedTagsForUserAndOrgApps.some(tag => app.tags.includes(tag) )) ); const matchesSelectedOption = ( selectedOptionOfCreatedWith.length === 0 || selectedOptionOfCreatedWith.includes('App Editor') && app.generated === true || selectedOptionOfCreatedWith.includes('Python') && app.generated === false ); return matchesSearchQuery && matchesSelectedCategories && matchesSelectedTags && matchesSelectedOption; }) : []; const [mouseHoverIndex, setMouseHoverIndex] = useState(-1); var counted = 0; const memoizedHits = useMemo(() => { return filteredUserAppdata.map((data, index) => { const isMouseOverOnCloudIcon = false; const xs = 12; const rowHandler = 12; const searchClient = {}; const userdata = {}; const paperStyle = { backgroundColor: "#1A1A1A", color: "rgba(241, 241, 241, 1)", padding: isHeader ? null : 15, cursor: "pointer", position: "relative", width: 339, height: 96, borderRadius: 8, }; var parsedname = ""; for (var key = 0; key < data.name.length; key++) { var character = data.name.charAt(key); if (character === character.toUpperCase()) { if ( data.name.charAt(key + 1) !== undefined && data.name.charAt(key + 1) === data.name.charAt(key + 1).toUpperCase() ) { } else { parsedname += " "; } } parsedname += character; } parsedname = ( parsedname.charAt(0).toUpperCase() + parsedname.substring(1) ).replaceAll("_", " "); const normalizedString = (name) => { if (typeof name === 'string') { return name.replace(/_/g, ' '); } else { return name; } }; const appUrl = isCloud === false ? `/apps/${data.id}` : `https://shuffler.io/apps/${data.id}`; return ( { setMouseHoverIndex(index); }} onMouseOut={() => { setMouseHoverIndex(-1); }} > {data.name}
{normalizedString(data.name)}
{data.categories !== null ? normalizedString(data.categories).join(", ") : "NA"}
{data.tags && data.tags.map((tag, tagIndex) => ( {normalizedString(tag)} {tagIndex < data.tags.length - 1 ? ", " : ""} ))}
{/* )} */}
); }); }, [filteredUserAppdata, latestActivatedAppId]); return (
{isLoggedIn ? (
{isLoading ? : (
{memoizedHits}
)}
) : (
Please login to your account first to view {`${currTab === 1 ? "Organization" : "My"}`} Apps.
Or signup to create a new account.
)}
); }; const AppTab = () => { const [isAnyAppActivated, setIsAnyAppActivated] = useState(false); return (
{isAnyAppActivated && }Organization Apps
sx={{ color: currTab === 1 ? "#F86743" : "inherit", border: 'none', height: 44, fontSize: 16, flex: 1, textTransform: 'none', fontWeight: 400, paddingBottom: 3, fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)", }} > {currTab === 0 ? ( ) : currTab === 1 || currTab === 2 ? ( ) : null}
); }; const CustomSearchBox = connectSearchBox(SearchBox); const CustomHits = connectHits(Hits); return (
{/*
*/}
{currTab === 0 ? : }
{/* */}
{showSuggestion === true ? (
Can't find what you're looking for?
setFormMail(e.target.value)} /> setMessage(e.target.value)} />
{formMessage}
) : null}
); }; export default AppGrid;