import React, { useState, useEffect, useContext, useCallback, memo, useMemo, useRef } from "react"; import theme from "../theme.jsx"; import { isMobile } from "react-device-detect"; import { useLocation, useNavigate } from "react-router-dom"; import { TextField, Button, Typography, MenuItem, Select, Tabs, Tab, Zoom, Grid, Paper, ButtonBase, Tooltip, Box, CircularProgress, Checkbox, Skeleton, Dialog, DialogTitle, DialogContent, DialogActions, IconButton, } from "@mui/material"; import { Context } from "../context/ContextApi.jsx"; import { Add as AddIcon, Edit as EditIcon, Search as SearchIcon, Clear as ClearIcon, Close as CloseIcon, Cached as CachedIcon, CloudDownload as CloudDownloadIcon, ForkRight as ForkRightIcon, } from "@mui/icons-material"; import InputAdornment from '@mui/material/InputAdornment'; import { ClearRefinements, connectHits, connectSearchBox, connectStateResults, InstantSearch, RefinementList, connectRefinementList, Configure } from "react-instantsearch-dom"; import { removeQuery } from "../components/ScrollToTop.jsx"; import { toast } from "react-toastify"; import algoliasearch from "algoliasearch/lite"; import { debounce } from "lodash"; import AppSelection from "../components/AppSelection.jsx"; import AppModal from "../components/AppModal.jsx"; import AppCreationModal from "../components/AppCreationModal.jsx"; const searchClient = algoliasearch( "JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240" ); // AppCard Component const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl, deactivatedIndexes, currTab, handleAppClick, leftSideBarOpenByClick, userdata, fetchApps, appsToShow, setAppsToShow, setUserApps, }) => { const navigate = useNavigate(); const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io" || window.location.host === "localhost:3000"; //const appUrl = isCloud ? `/apps/${data.id}` : `https://shuffler.io/apps/${data.id}`; const appUrl = `/apps/${data.id}` var canEditApp = userdata?.support || userdata?.id === data?.owner || (userdata?.admin === "true" && userdata?.active_org?.id === data?.reference_org) || data?.contributors?.includes(userdata?.id) const paperStyle = { backgroundColor: mouseHoverIndex === index ? "rgba(26, 26, 26, 1)" : "#212121", color: "rgba(241, 241, 241, 1)", cursor: "pointer", fontFamily: theme?.typography?.fontFamily, // position: "relative", width: "100%", height: 96, borderRadius: 8, boxShadow: "0px 0px 10px 0px rgba(0, 0, 0, 0.1)", marginBottom: 20, transition: "width 0.3s ease", } return ( setMouseHoverIndex(index)} onMouseOut={() => setMouseHoverIndex(-1)} > { handleAppClick(data); }} > {data.name}
{data.name.replace(/_/g, ' ').replace(/\b\w/g, char => char.toUpperCase())}
{data.categories ? data.categories.join(", ") : "NA"}
{data.generated !== true && data.tags && data.tags.slice(0, 2).map((tag, tagIndex) => ( {tag} {tagIndex < data.tags.length - 1 ? ", " : ""} ))}
{/* Deactivate button */} {(currTab === 0 || currTab === 1) && !deactivatedIndexes.includes(index) && mouseHoverIndex === index && data.generated === true && (
{ canEditApp ? ( ) : ( ) }
)}
); }; // Component to fetch all public app from the algolia. const Hits = ({ userdata, hits, handleAppClick, setIsAnyAppActivated, searchQuery, globalUrl, isLoggedIn, currTab, leftSideBarOpenByClick }) => { const [hoverEffect, setHoverEffect] = useState(-1); const [allActivatedAppIds, setAllActivatedAppIds] = useState([]); const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; const [deactivatedIndexes, setDeactivatedIndexes] = React.useState([]); const [isLoading, setIsLoading] = useState(false) useEffect(() => { var baseurl = globalUrl; setIsLoading(true) fetch(baseurl + "/api/v1/me", { credentials: "include", headers: { 'Content-Type': 'application/json', }, }) .then(response => response.json()) .then(responseJson => { if (responseJson.success) { setAllActivatedAppIds(responseJson.active_apps) } setIsLoading(false) }) .catch(error => { console.log("Failed login check: ", error); }); }, [currTab]); const normalizedString = (name) => { if (typeof name === 'string') { return name.replace(/_/g, ' '); } else { return name; } }; useEffect(() => { if (userdata && userdata.active_apps) { setAllActivatedAppIds(userdata.active_apps); } }, [currTab, window.location]); //Function for activation and deactivation of app const handleActivateButton = (event, data, type) => { //use prevent default so it will stop redirection to the app page event.preventDefault(); event.stopPropagation(); 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); } } }) .catch(error => { console.log("app error: ", error.toString()); }); } let workflowDelay = 0; const isHeader = true; return (
{!isLoading ? (
{hits?.length === 0 && searchQuery.length >= 0 ? (
No apps found matching your search criteria Try adjusting your search terms or filters to find what you're looking for
) : (
{hits?.map((data, index) => { const appUrl = isCloud ? `/apps/${data.objectID}?queryID=${data.__queryID}` : `https://shuffler.io/apps/${data.objectID}?queryID=${data.__queryID}`; return ( { setHoverEffect(index); }} onMouseLeave={() => { setHoverEffect(-1); }} > { handleAppClick(data); }} > {data.name}
{(allActivatedAppIds && allActivatedAppIds.includes(data.objectID)) && }
{normalizedString(data.name)}
{data.categories !== null ? normalizedString(data.categories).join(", ") : "NA"}
{hoverEffect === index && isCloud ? (
{data.tags && ( {data.tags.slice(0, 1).map((tag, tagIndex) => ( {normalizedString(tag)} {tagIndex < 1 ? ", " : ""} ))} )}
) : (
{data.tags && data.tags.map((tag, tagIndex) => ( {normalizedString(tag)} {tagIndex < data.tags.length - 1 ? ", " : ""} ))}
)}
{hoverEffect === index && (
{allActivatedAppIds && allActivatedAppIds?.includes(data.objectID) ? ( ) : ( )}
)}
); })}
)}
) : ( ) }
); } // Custom SearchBox Component const SearchBox = ({ refine, searchQuery, setSearchQuery }) => { const inputRef = useRef(null); const [localQuery, setLocalQuery] = useState(searchQuery); const location = useLocation(); // Initialize search when component mounts or when switching to Discover tab useEffect(() => { if (searchQuery) { setLocalQuery(searchQuery); refine(searchQuery); // This will trigger the Algolia search } }, [searchQuery, refine]); // Debounced function to refine search const debouncedRefine = useRef( debounce((value) => { setSearchQuery(value); removeQuery("q"); refine(value); }, 300) ).current; useEffect(() => { const urlSearchParams = new URLSearchParams(window.location.search); const params = Object.fromEntries(urlSearchParams.entries()); const foundQuery = params["q"]; if (foundQuery) { setLocalQuery(foundQuery); debouncedRefine(foundQuery); } }, [debouncedRefine]); useEffect(() => { if (searchQuery === "") { return; } if (inputRef.current) { inputRef.current.focus(); } }, [searchQuery]); const handleChange = (event) => { const value = event.target.value; setLocalQuery(value); debouncedRefine(value); }; return ( { if (event.key === "Enter") { event.preventDefault(); } }} style={{ borderRadius: 4, height: 45, fontFamily: theme?.typography?.fontFamily, flex: 1 }} InputProps={{ style: { borderRadius: 4, height: 45 }, endAdornment: ( {localQuery?.length === 0 ? : ( { setLocalQuery(''); debouncedRefine(''); const queryParams = new URLSearchParams(location.search); queryParams.delete('q'); window.history.replaceState({}, '', `${location.pathname}?${queryParams.toString()}`); }} /> )} ), }} /> ); }; const CustomSearchBox = connectSearchBox(SearchBox); const CustomHits = connectHits(Hits); // Custom Category Dropdown Component const CategoryDropdown = ({ items, currentRefinement, refine }) => { const handleChange = (event) => { const value = event.target.value; refine(value); }; return (
{currentRefinement.length > 0 && ( refine([])} /> )}
); }; const CustomCategoryDropdown = connectRefinementList(CategoryDropdown); // Custom Label Dropdown Component const LabelDropdown = ({ items, currentRefinement, refine }) => { const handleChange = (event) => { const value = event.target.value; refine(value); }; return (
{currentRefinement.length > 0 && ( refine([])} /> )}
); }; const CustomLabelDropdown = connectRefinementList(LabelDropdown); // New filter function const filterApps = (apps, searchQuery, selectedCategory, selectedLabel) => { if (!Array.isArray(apps)) return []; return apps.filter((app) => { const matchesSearchQuery = ( searchQuery === "" || // If searchQuery is empty, match all apps 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 = ( selectedCategory.length === 0 || // If no category is selected, match all apps (app.categories && app.categories.some(category => selectedCategory.includes(category) )) ); const matchesSelectedTags = ( selectedLabel.length === 0 || // If no label is selected, match all apps (app.tags && app.tags.some(tag => selectedLabel.includes(tag) )) ); return matchesSearchQuery && matchesSelectedCategories && matchesSelectedTags; }); }; const LoginPrompt = () => { const navigate = useNavigate(); return (
Log in to see your organization's apps
) }; // Add this new component for the app skeleton const AppSkeleton = () => { return (
); }; // Replace the loading sections in the main component with this const LoadingGrid = () => { return (
{[...Array(7)].map((_, index) => ( ))}
); }; // Main Apps Component const Apps2 = (props) => { const { globalUrl, isLoaded, serverside, userdata, isLoggedIn, checkLogin } = props; let navigate = useNavigate(); const { leftSideBarOpenByClick } = useContext(Context); const location = useLocation(); const [searchQuery, setSearchQuery] = useState(""); const [selectedCategory, setSelectedCategory] = useState([]); const [selectedLabel, setSelectedLabel] = useState([]); const [currTab, setCurrTab] = useState(0); const [categories, setCategories] = useState([]); const [labels, setLabels] = useState([]); const [isLoading, setIsLoading] = useState(false); const [userApps, setUserApps] = useState([]); const [orgApps, setOrgApps] = useState([]); const [appsToShow, setAppsToShow] = useState([]); const [deactivatedIndexes, setDeactivatedIndexes] = React.useState([]); const [IsAnyAppActivated, setIsAnyAppActivated] = useState(false); const [mouseHoverIndex, setMouseHoverIndex] = useState(-1); var counted = 0; const [showNoAppFound, setShowNoAppFound] = useState(false); const [openModal, setOpenModal] = useState(false); const [selectedApp, setSelectedApp] = useState(null); const [appFramework, setAppFramework] = useState(undefined); const [defaultSearch, setDefaultSearch] = useState(""); const [apps, setApps] = useState([]); const [filteredApps, setFilteredApps] = useState([]); const [appSearchLoading, setAppSearchLoading] = useState(false); const [creatorProfile, setCreatorProfile] = useState({}); const [openApi, setOpenApi] = React.useState(""); const [loadAppsModalOpen, setLoadAppsModalOpen] = useState(false); const [downloadBranch, setDownloadBranch] = useState("master"); const [field1, setField1] = useState(""); const [field2, setField2] = useState(""); const [validation, setValidation] = useState(null); const [createAppModalOpen, setCreateAppModalOpen] = useState(false); const baseRepository = "https://github.com/frikky/shuffle-apps"; const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io" ? true : false; // Set the current tab based on the query parameter useEffect(() => { const queryParams = new URLSearchParams(location.search); const tabParam = queryParams.get('tab'); if (tabParam !== null && tabParam !== undefined) { if (tabParam === 'org_apps') { setCurrTab(0); } else if (tabParam === 'my_apps') { setCurrTab(1); } else { setCurrTab(2); } } }, [location.search]); useEffect(() => { const getFramework = () => { fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", { method: "GET", headers: { "Content-Type": "application/json", Accept: "application/json", }, credentials: "include", }) .then((response) => { if (response.status !== 200) { console.log("Status not 200 for framework!"); } return response.json(); }) .then((responseJson) => { if (responseJson.success === false) { setAppFramework({}) if (responseJson.reason !== undefined) { //toast("Failed loading: " + responseJson.reason) } else { //toast("Failed to load framework for your org.") } } else { setAppFramework(responseJson) } }) .catch((error) => { console.log("err in framework: ", error.toString()); }) } getFramework(); }, []); // Fetch apps based on the current tab : 0 -> org_apps, 1 -> my_apps, 2 -> all_apps const fetchApps = async () => { const baseUrl = globalUrl; let url; setIsLoading(true); const userId = userdata?.id; if (currTab === 1 && userId) { url = `${baseUrl}/api/v1/users/${userId}/apps`; } else if (currTab === 0) { url = `${baseUrl}/api/v1/apps`; } try { const response = await fetch(url, { method: "GET", credentials: "include", headers: { "Content-Type": "application/json", }, }); const data = await response.json(); if (currTab === 1) { setAppsToShow(data); setUserApps(data); } else if (currTab === 0) { setAppsToShow(data); setOrgApps(data); // For testing the empty state // setAppsToShow([]); // setOrgApps([]); } setIsLoading(false); } catch (err) { console.error("Error fetching apps:", err); setIsLoading(false); } }; useEffect(() => { // Only fetch if we have required data if (globalUrl && (currTab === 0 || (currTab === 1 && userdata?.id))) { fetchApps(); } }, [currTab, globalUrl, userdata?.id]); // Remove location.search dependency // useEffect(() => { // // setSearchQuery(""); // setSelectedCategory([]); // setSelectedLabel([]); // }, [currTab]) // Find top categories and tags based on the current tab useEffect(() => { if (currTab === 0 || currTab === 1) { setCategories(findTopCategories()); setLabels(findTopTags()); } }, [currTab, appsToShow]) const getUserProfile = (username) => { if (serverside === true || !isCloud) { setCreatorProfile({}) return; } fetch(`${globalUrl}/api/v1/users/creators/${username}`, { method: "GET", headers: { "Content-Type": "application/json", Accept: "application/json", }, credentials: "include", }) .then((response) => { if (response.status !== 200) { console.log("Status not 200 for WORKFLOW EXECUTION :O!"); } return response.json(); }) .then((responseJson) => { if (responseJson.success !== false) { console.log("creator profile: ", responseJson) setCreatorProfile(responseJson); } else { setCreatorProfile({}) } }) .catch((error) => { console.log(error); setCreatorProfile({}) }); }; useEffect(() => { if (serverside) { return null; } }, [serverside]); const getApps = () => { // Get apps from localstorage var storageApps = [] try { const appstorage = localStorage.getItem("apps") storageApps = JSON.parse(appstorage) if (storageApps === null || storageApps === undefined || storageApps.length === 0) { storageApps = [] } else { setAppsToShow(storageApps) setOrgApps(storageApps) setApps(storageApps) // setFilteredApps(storageApps) // setAppSearchLoading(false) } } catch (e) { //console.log("Failed to get apps from localstorage: ", e) } setIsLoading(true); fetch(globalUrl + "/api/v1/apps", { method: "GET", headers: { "Content-Type": "application/json", Accept: "application/json", }, credentials: "include", }) .then((response) => { setIsLoading(false); if (response.status !== 200) { console.log("Status not 200 for apps :O!"); //if (isCloud) { // window.location.pathname = "/search"; //} } return response.json(); }) .then((responseJson) => { var privateapps = []; var valid = []; var invalid = []; for (var key in responseJson) { const app = responseJson[key]; if (app.categories !== undefined && app.categories !== null && app?.categories.includes("Eradication")) { app.categories = ["EDR"] } if (app.is_valid && !(!app.activated && app.generated)) { privateapps.push(app); } else if ( app.private_id !== undefined && app.private_id.length > 0 ) { valid.push(app); } else { invalid.push(app); } } privateapps.push(...valid); privateapps.push(...invalid); console.log("privateapps: setting apps ", privateapps) setAppsToShow(privateapps); setOrgApps(privateapps); setApps(privateapps); // setFilteredApps(privateapps); if (privateapps.length > 0) { if (selectedApp.id === undefined || selectedApp.id === null) { if (privateapps[0].owner !== undefined && privateapps[0].owner !== null) { getUserProfile(privateapps[0].owner); } // setContact(privateapps[0].contact_info) // setSelectedApp(privateapps[0]); // setSharingConfiguration(privateapps[0].sharing === true ? "public" : "you") } // if ( // privateapps[0].actions !== null && // privateapps[0].actions.length > 0 // ) { // setSelectedAction(privateapps[0].actions[0]); // } else { // setSelectedAction({}); // } } if (privateapps.length > 0 && storageApps.length === 0) { try { localStorage.setItem("apps", JSON.stringify(privateapps)) } catch (e) { console.log("Failed to set apps in localstorage: ", e) } } //setTimeout(() => { // setFirstLoad(false) //}, 5000) }) .catch((error) => { toast(error.toString()); setIsLoading(false); }); }; // Locally hotloads app from folder const hotloadApps = () => { toast("Hotloading apps from location in .env"); setIsLoading(true); fetch(globalUrl + "/api/v1/apps/run_hotload", { method: "POST", mode: "cors", headers: { Accept: "application/json", }, credentials: "include", }) .then((response) => { setIsLoading(false); if (response.status === 200) { //toast("Hotloaded apps!") getApps(); } return response.json(); }) .then((responseJson) => { if (responseJson.success === true) { toast("Successfully finished hotload"); } else { console.log("failed hotload: ", responseJson) // toast(`Failed hotload: ${responseJson.reason}`); //(responseJson.reason !== undefined && responseJson.reason.length > 0) { } }) .catch((error) => { toast(`Failed hotload: ${error.toString()}`); }); }; // Load data e.g. from github const getSpecificApps = (url, forceUpdate) => { setValidation(true); setIsLoading(true); //start() const parsedData = { url: url, branch: downloadBranch || "master", }; if (field1.length > 0) { parsedData["field_1"] = field1; } if (field2.length > 0) { parsedData["field_2"] = field2; } parsedData["force_update"] = forceUpdate; toast("Getting specific apps from your URL."); var cors = "cors"; fetch(globalUrl + "/api/v1/apps/get_existing", { method: "POST", mode: "cors", headers: { Accept: "application/json", }, body: JSON.stringify(parsedData), credentials: "include", }) .then((response) => { if (response.status === 200) { toast("Loaded existing apps!"); } //stop() setIsLoading(false); setValidation(false); return response.json(); }) .then((responseJson) => { console.log("DATA: ", responseJson); if (responseJson.reason !== undefined) { toast("Failed loading: " + responseJson.reason); } }) .catch((error) => { console.log("ERROR: ", error.toString()); //toast(error.toString()); //stop() setIsLoading(false); setValidation(false); }); }; const handleGithubValidation = (forceUpdate) => { getSpecificApps(openApi, forceUpdate); setLoadAppsModalOpen(false); }; const appsModalLoad = loadAppsModalOpen ? ( { setOpenApi(""); setLoadAppsModalOpen(false); setField1(""); setField2(""); }} maxWidth="md" fullWidth PaperProps={{ sx: { borderRadius: 2, border: "1px solid #494949", minWidth: '440px', fontFamily: theme?.typography?.fontFamily, backgroundColor: "#212121", zIndex: 1000, '& .MuiDialogContent-root': { backgroundColor: "#212121", }, '& .MuiDialogTitle-root': { backgroundColor: "#212121", }, '& .MuiTypography-root': { fontFamily: theme?.typography?.fontFamily, }, '& .MuiButton-root': { fontFamily: theme?.typography?.fontFamily, }, } }} > Load from Github Repository { setOpenApi(""); setLoadAppsModalOpen(false); setField1(""); setField2(""); }} sx={{ color: 'rgba(255, 255, 255, 0.7)', '&:hover': { bgcolor: 'rgba(255, 255, 255, 0.1)' } }} > Repository (supported: github, gitlab, bitbucket) setOpenApi(e.target.value)} sx={{ mb: 3, '& .MuiOutlinedInput-root': { color: 'white', height: '50px', '& fieldset': { borderColor: 'rgba(255, 255, 255, 0.23)', }, }, }} /> Branch (default value is "master") setDownloadBranch(e.target.value)} placeholder="master" sx={{ mb: 3, '& .MuiOutlinedInput-root': { color: 'white', height: '50px', '& fieldset': { borderColor: 'rgba(255, 255, 255, 0.23)', }, }, }} /> Authentication (optional - private repos etc)
setField1(e.target.value)} sx={{ '& .MuiOutlinedInput-root': { color: 'white', height: '50px', '& fieldset': { borderColor: 'rgba(255, 255, 255, 0.23)', }, }, }} /> setField2(e.target.value)} sx={{ '& .MuiOutlinedInput-root': { color: 'white', height: '50px', '& fieldset': { borderColor: 'rgba(255, 255, 255, 0.23)', }, }, }} />
{!isCloud && ( )}
) : null; const findTopCategories = () => { const categoryCountMap = {}; const apps = currTab === 1 ? userApps : orgApps; // Check if userAndOrgsApp is an array before iterating over it and Find top 10 Category from the apps if (Array.isArray(apps)) { apps.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, 7); return topCategories; } }; const findTopTags = () => { const tagCountMap = {}; const apps = currTab === 1 ? userApps : orgApps; if (Array.isArray(apps)) { apps.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, 8); return topTags; }; const handleCreateApp = (e) => { e.preventDefault(); setCreateAppModalOpen(true); // setOpenModal(true); }; useEffect(() => { const apps = currTab === 1 ? userApps : orgApps; const filteredUserAppdata = filterApps(apps, searchQuery, selectedCategory, selectedLabel); setAppsToShow(filteredUserAppdata); }, [searchQuery, selectedCategory, selectedLabel, currTab]); const handleTabChange = (event, newTab) => { setCurrTab(newTab); // Apply filters immediately when changing tabs if (newTab === 0) { const filteredOrgApps = filterApps(orgApps, searchQuery, selectedCategory, selectedLabel); setAppsToShow(filteredOrgApps); } else if (newTab === 1) { const filteredUserApps = filterApps(userApps, searchQuery, selectedCategory, selectedLabel); setAppsToShow(filteredUserApps); } // Update URL query params based on tab index const tabMapping = { 0: 'org_apps', 1: 'my_apps', 2: 'all_apps' }; const queryParams = new URLSearchParams(location.search); queryParams.set('tab', tabMapping[newTab]); // Maintain search query in URL regardless of tab if (searchQuery) { queryParams.set('q', searchQuery); } else { queryParams.delete('q'); } navigate(`${location.pathname}?${queryParams.toString()}`); }; // Update useEffect for filtering without URL manipulation useEffect(() => { if (currTab === 2) return; // Skip for "Discover Apps" tab as it uses Algolia const apps = currTab === 1 ? userApps : orgApps; const filteredApps = filterApps(apps, searchQuery, selectedCategory, selectedLabel); setAppsToShow(filteredApps); }, [searchQuery, selectedCategory, selectedLabel, currTab, userApps, orgApps]); // Add URL update only when search is performed const handleSearchChange = (event) => { const newSearchQuery = event.target.value; setSearchQuery(newSearchQuery); // Update URL only when user performs search const queryParams = new URLSearchParams(location.search); if (newSearchQuery) { queryParams.set('q', newSearchQuery); } else { queryParams.delete('q'); } window.history.replaceState({}, '', `${location.pathname}?${queryParams.toString()}`); }; const boxStyle = { color: "white", display: "flex", flexDirection: "column", height: "100%", width: "100%", margin: "auto", maxWidth: "70%", fontFamily: theme?.typography?.fontFamily, // padding: '20px 380px', }; const handleCategoryChange = (event) => { const value = event.target.value; setSelectedCategory(value); }; const handleLabelChange = (event) => { const value = event.target.value; setSelectedLabel(value); }; const handleAppClick = (app) => { setSelectedApp(app); setOpenModal(true); } const handleAppModalClose = () => { setOpenModal(false) } const tabStyle = { textTransform: 'none', marginRight: 20, fontFamily: theme?.typography?.fontFamily, fontSize: 16, borderBottom: "5px solid transparent", minHeight: "48px", padding: "12px 16px", } const tabActive = { borderBottom: "5px solid #FF8544", borderRadius: "2px", color: "#FF8544" } return (
setCreateAppModalOpen(false)} theme={theme} globalUrl={globalUrl} isCloud={isCloud} /> {appsModalLoad}
{currTab === 0 ? "Org" : currTab === 1 ? "Your" : "Discover"} Apps {isCloud ? null : ( {userdata === undefined || userdata === null || isLoading ? null : ( )} {userdata === undefined || userdata === null || userdata.admin === "false" ? null : } )}
handleTabChange(event, newTab)} style={{ fontFamily: theme?.typography?.fontFamily, fontSize: 16, marginBottom: "-2px" }} TabIndicatorProps={{ style: { display: 'none' } }} >
{(currTab === 0 || currTab === 1) ? ( { if (event.key === "Enter") { event.preventDefault(); } }} limit={5} InputProps={{ style: { borderRadius: 4, height: 45 }, endAdornment: ( {searchQuery.length === 0 ? : ( { setSearchQuery("") const queryParams = new URLSearchParams(location.search); queryParams.delete('q'); window.history.replaceState({}, '', `${location.pathname}?${queryParams.toString()}`); }} /> )} ), }} /> ) : ( )}
{currTab === 2 ? ( ) : ( <> {selectedCategory.length > 0 && ( setSelectedCategory([])} /> )} )}
{currTab === 2 ? ( ) : ( <> {selectedLabel.length > 0 && ( setSelectedLabel([])} /> )} )}
{ currTab === 0 && (
{isLoading ? ( ) : ( isLoggedIn && !isLoading ? ( <> {appsToShow?.length > 0 && appsToShow !== undefined && !isLoading ? (
{appsToShow.map((data, index) => ( ))}
) : (
)} ) : ( ) )}
) } { currTab === 1 && (
{isLoading ? ( ) : ( isLoggedIn && !isLoading ? ( <> {appsToShow?.length > 0 && appsToShow !== undefined ? (
{appsToShow.map((data, index) => ( ))}
) : (
)} ) : ( ) )}
) } { currTab === 2 && }
); }; export default Apps2;