Files
shuffle-cracked/frontend/src/components/AppGrid.jsx
T
2026-05-07 18:24:32 +05:30

2144 lines
74 KiB
React

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, Collapse } 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,
CollectionsOutlined,
CookieSharp,
} from "@mui/icons-material";
import { toast } from "react-toastify"
import ClearIcon from '@mui/icons-material/Clear';
import Box from '@mui/material/Box';
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 { useDebouncedCallback } from "../utils/useDebouncedCallback.jsx";
import aa from "search-insights";
import { useLocation } from 'react-router-dom';
import "./FilterCSS.css";
import {
Zoom,
Grid,
Paper,
TextField,
ButtonBase,
InputAdornment,
Typography,
Button,
Tooltip,
} from "@mui/material";
const searchClient = algoliasearch(
"JNSS5CFDZZ",
"c8f882473ff42d41158430be09ec2b4e"
);
//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 [deactivatedIndexes, setDeactivatedIndexes] = 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 noImage = "/public/no_image.png";
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, searchQuery, setSearchQuery }) => {
var defaultSearch = "";
//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());
};
const debouncedRefine = useDebouncedCallback((value) => refine(value), 300);
return (
<form noValidate action="" role="search">
<TextField
value={searchQuery}
fullWidth
style={{
backgroundColor: theme.palette.inputColor,
marginTop: 20,
marginLeft: 10,
marginRight: 12,
width: 693,
borderRadius: 8,
fontSize: 16,
}}
InputProps={{
style: {
color: "white",
fontSize: "1em",
height: 50,
width: 693,
borderRadius: 8,
},
startAdornment: (
<InputAdornment position="start">
<SearchIcon style={{ marginLeft: 5 }} />
</InputAdornment>
),
endAdornment: (
<InputAdornment position="end">
{searchQuery.length > 0 && (
<ClearIcon
style={{
color: "white",
cursor: "pointer",
marginRight: 10
}}
onClick={() => {
setSearchQuery('')
removeQuery("q");
refine('')
}}
/>
)}
<button
type="button"
onClick={handleSearch}
style={{
backgroundImage:
"linear-gradient(to right, rgb(248, 106, 62), rgb(243, 64, 121))",
color: "white",
border: "none",
padding: "10px 20px",
width: 100,
height: 35,
borderRadius: 17.5,
cursor: "pointer",
}}
>
Search
</button>
</InputAdornment>
),
}}
autoComplete="off"
color="primary"
placeholder="Search more than 2500 Apps"
id="shuffle_search_field"
onChange={(event) => {
const value = event.currentTarget.value;
setSearchQuery(value);
removeQuery("q");
debouncedRefine(value);
}}
onKeyDown={(event) => {
if(event.key === "Enter") {
event.preventDefault();
}
}}
limit={5}
/>
{/*isSearchStalled ? 'My search is stalled' : ''*/}
</form>
);
};
const [currTab, setCurrTab] = useState(0);
const location = useLocation();
useEffect(() => {
const queryParams = new URLSearchParams(location.search);
const tabParam = queryParams.get('tab');
if (tabParam === 'org_apps') {
setCurrTab(1);
} else if (tabParam === 'my_apps') {
setCurrTab(2);
} else {
setCurrTab(0);
}
}, [location.search]);
const handleTabChange = (event, newTab) => {
setCurrTab(newTab);
const newQueryParam = newTab === 0 ? 'all_apps' : newTab === 1 ? 'org_apps' : 'my_apps';
const queryParams = new URLSearchParams(location.search);
queryParams.set('tab', newQueryParam);
queryParams.delete('q');
window.history.replaceState({}, '', `${location.pathname}?${queryParams.toString()}`);
};
const [isLoggedIn, setIsLoggedIn] = useState(false);
const [isLoading, setIsLoading] = useState(true)
// Component to fetch all public app from the algolia.
const Hits = ({
hits,
insights,
setIsAnyAppActivated,
searchQuery
}) => {
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;
}
};
//check user login and get user info.
const [allActivatedAppIds, setAllActivatedAppIds] = useState(null);
const [userdata, setUserdata] = useState([]);
useEffect(() => {
var baseurl = globalUrl;
fetch(baseurl + "/api/v1/me", {
credentials: "include",
headers: {
'Content-Type': 'application/json',
},
})
.then(response => response.json())
.then(responseJson => {
if (responseJson?.success) {
setUserdata(responseJson);
setAllActivatedAppIds(responseJson.active_apps)
setIsLoggedIn(true);
} else {
setIsLoggedIn(false);
}
setIsLoading(false)
})
.catch(error => {
console.log("Failed login check: ", error);
});
}, [currTab]);
//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();
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;
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',
};
const [showNoAppFound, setShowNoAppFound] = useState(false);
//show some delay to show the "App Not Found." so it doesn't not show while changing tab.
useEffect(() => {
const timer = setTimeout(() => {
setShowNoAppFound(true);
}, 1000);
return () => clearTimeout(timer);
}, []);
return (
<div>
{!isLoading ? (
<div>
{hits.length === 0 && searchQuery.length >= 0 && showNoAppFound ? (
<Typography variant="body1" style={{ marginTop: '30%' }}>No Apps Found</Typography>
) : (
<Grid item spacing={2} justifyContent="flex-start">
<div
style={{
gap: 16,
marginTop: 16,
display: "flex",
flexWrap: "wrap",
justifyContent: "flex-start",
marginLeft: 24,
overflowY: "auto",
overflowX: "hidden",
maxHeight: 570,
scrollbarWidth: "thin",
scrollbarColor: "#494949 #2f2f2f",
}}
>
{hits?.map((data, index) => {
const appUrl =
isCloud === true ?
`/apps/${data.objectID}`
: `apps/${data.objectID}`;
if (data.name === "" && data.id === "") {
return null
}
return (
<Zoom
key={index}
in={true}
style={{
transitionDelay: `${workflowDelay}ms`,
}}
>
<Grid>
<a
href={appUrl}
rel="noopener noreferrer"
target="_blank"
style={{
textDecoration: "none",
color: "#f85a3e",
}}
>
<Paper
elevation={0}
style={{ paperStyle, backgroundColor: mouseHoverIndex === index ? "#2F2F2F" : "rgba(26, 26, 26, 1)" }}
onMouseEnter={() => {
setMouseHoverIndex(index);
}}
onMouseLeave={() => {
setMouseHoverIndex(-1);
}}
>
<button
style={{
borderRadius: 8,
fontSize: 16,
display: "flex",
alignItems: "flex-start",
padding: 0,
border: 'none',
width: 339,
height: 96,
cursor: 'pointer',
color: "rgba(26, 26, 26, 1)",
backgroundColor: 'transparent',
}}
>
<img
id={`image_${index}`}
alt={data.name}
src={data.image_url ? data.image_url : "/images/no_image.png"}
onError={(e) => {
// Replace the image with the default image
const foundImage = document.getElementById(`image_${index}`)
if (foundImage !== undefined && foundImage !== null) {
foundImage.src = theme.palette.defaultImage
data.image_url = theme.palette.defaultImage
}
}}
style={{
width: 80,
height: 80,
borderRadius: 8,
margin: 8
}}
/>
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
gap: 4,
overflow: "hidden",
margin: "12px 0 12px 0",
marginLeft: 8,
fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)",
}}
>
<div
style={{
display: 'flex',
flexDirection: "row",
overflow: "hidden",
gap: 8,
textOverflow: "ellipsis",
whiteSpace: "nowrap",
color: '#F1F1F1'
}}
>
{(allActivatedAppIds && allActivatedAppIds.includes(data.objectID)) && <Box sx={{ width: 8, height: 8, backgroundColor: "#02CB70", borderRadius: '50%' }} />}
{normalizedString(data.name)}
</div>
<div
style={{
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
color: "rgba(158, 158, 158, 1)",
marginTop: 5
}}
>
{data.categories !== null
? normalizedString(data.categories).join(", ")
: "NA"}
</div>
<div
style={{
display: "flex",
justifyContent: 'space-between',
width: 230,
textAlign: 'start',
color: "rgba(158, 158, 158, 1)",
}}
>
<div style={{ marginBottom: 15, }}>
{mouseHoverIndex === index && isCloud ? (
<div>
{data.tags && (
<Tooltip
title={data.tags.join(", ")}
placement="bottom"
componentsProps={{
tooltip: {
sx: {
backgroundColor: "rgba(33, 33, 33, 1)",
color: "rgba(241, 241, 241, 1)",
width: "auto",
height: "auto",
fontSize: 16,
border: "1px solid rgba(73, 73, 73, 1)",
}
}
}}
>
<span>
{data?.tags?.slice(0, 1)?.map((tag, tagIndex) => (
<span key={tagIndex}>
{normalizedString(tag)}
{tagIndex < 1 ? ", " : ""}
</span>
))}
</span>
</Tooltip>
)}
</div>
) : (
<div style={{ width: 230, textOverflow: "ellipsis", overflow: 'hidden', whiteSpace: 'nowrap', }}>
{data.tags &&
data?.tags?.map((tag, tagIndex) => (
<span key={tagIndex}>
{normalizedString(tag)}
{tagIndex < data.tags.length - 1 ? ", " : ""}
</span>
))}
</div>
)}
</div>
<div style={{ position: 'relative', bottom: 5 }}>
{mouseHoverIndex === index && isCloud && (
<div>
{allActivatedAppIds && allActivatedAppIds.includes(data.objectID) ? (
<Button style={{
width: 102,
height: 35,
borderRadius: 200,
backgroundColor: "rgba(73, 73, 73, 1)",
color: "rgba(241, 241, 241, 1)",
textTransform: "none",
}}
onClick={(event) => {
handleActivateButton(event, data, "deactivate");
}}>
Deactivate
</Button>
) : (
<Button
style={{
width: 102,
height: 35,
borderRadius: 200,
backgroundColor: "rgba(242, 101, 59, 1)",
color: "rgba(255, 255, 255, 1)",
textTransform: "none",
}}
onClick={(event) => {
handleActivateButton(event, data, "activate");
}}
>
Activate
</Button>
)}
</div>
)}
</div>
</div>
</div>
</button>
</Paper>
</a>
</Grid>
</Zoom>
);
})
}
</div>
</Grid >
)}
</div>
) : (
<div><Box sx={{ position: 'absolute', top: '30%', left: '50%', }}> <CircularProgress /></Box></div>
)}
</div>
);
};
var workflowDelay = -50;
const CustomClearRefinements = connectStateResults(({ searchResults, ...rest }) => {
const hasFilters = searchResults && searchResults.nbHits !== searchResults.nbSortedHits;
return <ClearRefinements translations={{ reset: <span style={{ textDecoration: 'underline' }}>Clear All</span> }} {...rest} disabled={!hasFilters} />;
});
//Component to Filter all apps base on category
const FilterAllAppsByCategory = () => {
const [isCategoreListExpanded, setIsCategoreListExpanded] = useState(true);
const toggleRefinementList = () => {
setIsCategoreListExpanded((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 (
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
cursor: "pointer",
width: "100%",
}}
>
<Button
style={categoryButtonStyling}
onClick={toggleRefinementList}
>
Category
{isCategoreListExpanded ? (
<ExpandLessIcon style={{ marginLeft: "auto" }} />
) : (
<ExpandMoreIcon style={{ marginLeft: "auto" }} />
)}
</Button>
<Collapse in={isCategoreListExpanded}>
<div style={{ display: 'flex', flexDirection: 'column' }}>
<RefinementList attribute="categories" />
<CustomClearRefinements />
</div>
</Collapse>
</div>
);
};
//Component to filter all apps base on Action label
const FilterByActionLabel = () => {
const [isActionLabelExpanded, setIsActionLabelExpanded] = 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 (
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
marginTop: 20,
width: "100%",
}}
>
<Button
style={actionLabelButtonStyling}
onClick={toogleActionLabel}
>
Labels
{isActionLabelExpanded ? (
<ExpandLessIcon style={{ marginLeft: "auto" }} />
) : (
<ExpandMoreIcon style={{ marginLeft: "auto" }} />
)}
</Button>
<Collapse in={isActionLabelExpanded}>
<div style={{ display: 'flex', flexDirection: 'column' }}>
<RefinementList attribute="action_labels" />
<CustomClearRefinements />
</div>
</Collapse>
</div>
);
};
//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 (
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
marginTop: 20,
width: "100%",
}}
>
<Button
style={createdWithButtonStyling}
onClick={toogleCreatedWith}
>
Created With
{isCreatedWithExpanded ? <ExpandLessIcon style={{ marginLeft: "auto" }} /> : <ExpandMoreIcon style={{ marginLeft: 'auto' }} />}
</Button>
<Collapse in={isCreatedWithExpanded}>
<div style={{ display: 'flex', flexDirection: 'column' }}>
<RefinementList attribute="generated" transformItems={transformRefinementListItems} />
<CustomClearRefinements />
</div>
</Collapse>
</div>
);
};
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 (
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
marginTop: 20,
width: "100%",
}}
>
<Button
style={createdByButtonStyling}
onClick={toogleCreatedBy}
disabled={true}
>
Created By
{isCreatedByExpanded ? (
<ExpandLessIcon style={{ marginLeft: "auto" }} />
) : (
<ExpandMoreIcon style={{ marginLeft: "auto" }} />
)}
</Button>
{isCreatedByExpanded && (
<>
{/* <InstantSearch searchClient={searchClient} indexName="creators"> */}
<RefinementList attribute="creator" />
{/* </InstantSearch> */}
<button
style={{
cursor: "pointer",
color: "rgb(248, 103, 67)",
border: "none",
backgroundColor: "transparent",
fontSize: 16,
}}
>
Clear All
</button>
</>
)}
</div>
);
};
const FilterForAllApps = () => {
return (
<div
style={{
display: "flex",
flexDirection: "column",
width: 200,
alignItems: "flex-start",
marginRight: 16,
}}
>
<Typography variant="h5" style={{ marginBottom: 30, marginTop: 30, fontWeight: "400", fontSize: 24, fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)", }}>
Filter By
</Typography>
<FilterAllAppsByCategory />
<FilterByActionLabel />
<FilterByCreatedWith />
<FilterCreatedBy />
</div>
);
};
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 }) => {
var [searchQuery, setSearchQuery] = useState("");
return (
<div
style={{
width: "100%",
position: "relative",
height: "100%",
}}
>
<CustomSearchBox searchQuery={searchQuery} setSearchQuery={setSearchQuery} />
<CustomHits
setIsAnyAppActivated={setIsAnyAppActivated}
hitsPerPage={5}
searchQuery={searchQuery}
/>
</div>
);
};
//Search box for the orgs and users apps
const SearchBoxForOrgAndUserApp = ({ searchQuery, setSearchQuery }) => {
return (
<form noValidate action="" role="search">
<TextField
value={searchQuery}
fullWidth
style={{
backgroundColor: theme.palette.inputColor,
marginTop: 20,
marginLeft: 10,
marginRight: 12,
width: 693,
borderRadius: 8,
fontSize: 16,
}}
InputProps={{
style: {
color: "white",
fontSize: "1em",
height: 50,
width: 693,
borderRadius: 8,
},
startAdornment: (
<InputAdornment position="start">
<SearchIcon style={{ marginLeft: 5 }} />
</InputAdornment>
),
endAdornment: (
<InputAdornment position="end">
{searchQuery.length > 0 && (
<ClearIcon
style={{
color: "white",
cursor: "pointer",
marginRight: 10
}}
onClick={() => {
setSearchQuery('');
}}
/>
)}
<button
type="button"
// onClick={handleSearch}
style={{
backgroundImage:
"linear-gradient(to right, rgb(248, 106, 62), rgb(243, 64, 121))",
color: "white",
border: "none",
padding: "10px 20px",
width: 100,
height: 35,
borderRadius: 17.5,
cursor: "pointer",
}}
>
Search
</button>
</InputAdornment>
),
}}
autoComplete="off"
color="primary"
placeholder="Search your Activated or self-built apps"
id="shuffle_search_field"
onChange={(event) => {
setSearchQuery(event.currentTarget.value);
}}
onKeyDown={(event) => {
if(event.key === "Enter") {
event.preventDefault();
}
}}
limit={5}
/>
{/*isSearchStalled ? 'My search is stalled' : ''*/}
</form>
)
}
const [userApps, setUserApps] = useState([]);
const [orgApps, setOrgApps] = useState([]);
useEffect(() => {
if (currTab === 2) {
const baseUrl = globalUrl;
const userAppsUrl = `${baseUrl}/api/v1/users/me/apps`;
fetch(userAppsUrl, {
method: "GET",
credentials: "include",
headers: {
"Content-Type": "application/json",
},
})
.then((response) => response.json())
.then((data) => {
setUserApps(data);
setIsLoading(false);
})
.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) => {
setOrgApps(data);
setIsLoading(false)
})
.catch((err) => {
console.error("Error fetching apps:", err);
});
}
}, [currTab]);
//Component to display category List for User and Orgs app
const FilterUsersAndOrgsAppByCategory = ({ selectedCategoryForUsersAndOgsApps, setselectedCategoryForUsersAndOgsApps }) => {
const [isCategoreListExpanded, setIsCategoryListExpanded] = useState(true);
const toogleCategoryList = () => {
setIsCategoryListExpanded((prevState) => !prevState);
};
const [appsToFilter, setAppsToFilter] = useState([]);
useEffect(() => {
if (currTab === 1) {
setAppsToFilter(orgApps)
}
if (currTab === 2) {
setAppsToFilter(userApps)
}
})
//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(appsToFilter)) {
appsToFilter.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 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)",
marginBottom: isCategoreListExpanded && 10
}
return (
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
cursor: "pointer",
width: "100%",
}}
>
<Button
style={categorysButtonStyling}
onClick={toogleCategoryList}
>
Category
{isCategoreListExpanded ? (
<ExpandLessIcon style={{ marginLeft: "auto" }} />
) : (
<ExpandMoreIcon style={{ marginLeft: "auto" }} />
)}
</Button>
<div>
{!isLoading && (
<Collapse in={isCategoreListExpanded}>
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start', width: '100%' }}>
{topCategories?.map((data, index) => (
<Button
key={data.category}
style={{
border: "none",
background: "none",
padding: 0,
color: "#F1F1F1",
textTransform: 'none'
}}
onClick={() => handleCheckboxChange(data.category)}
>
<input
id={`checkbox-${data.category}`}
type="checkbox"
className="ais-RefinementList-checkbox"
checked={selectedCategoryForUsersAndOgsApps.includes(data.category)}
onChange={() => handleCheckboxChange(data.category)}
style={{ marginRight: 5 }}
/>
<span className="ais-RefinementList-labelText" style={{ marginTop: 3, marginLeft: 3, fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)" }}>{data.category}</span>
</Button>
))}
<Button
style={{
marginTop: 11,
textDecoration: 'underline',
textTransform: 'none',
backgroundColor: 'transparent',
fontSize: 16,
justifyContent: 'flex-start',
}}
onClick={handleClearFilter}
disableElevation
disableRipple
>
Clear All
</Button>
</div>
</Collapse>
)}
</div>
</div>
);
};
const FilterUsersAndOrgsAppByActionLabel = ({ selectedTagsForUserAndOrgApps, setSelectedTagsForUserAndOrgApps }) => {
const [isActionLabelExpanded, setIsActionLabelExpanded] = useState(false);
const toggleActionLabel = () => {
setIsActionLabelExpanded((prevState) => !prevState);
};
const [appsToFilter, setAppsToFilter] = useState([]);
useEffect(() => {
if (currTab === 1) {
setAppsToFilter(orgApps)
}
if (currTab === 2) {
setAppsToFilter(userApps)
}
})
//Find top 9 tags from the database
const findTopTags = () => {
const tagCountMap = {};
if (Array.isArray(appsToFilter)) {
appsToFilter.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 topTags = findTopTags();
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',
width: '100%',
height: 30,
flexDirection: 'row',
textTransform: 'none',
fontFamily: 'var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)',
marginBottom: isActionLabelExpanded && 15
};
return (
<div
style={{
marginTop: 20,
display: 'flex',
flexDirection: 'column',
alignItems: 'flex-start',
cursor: 'pointer',
width: '100%',
}}
>
<Button onClick={toggleActionLabel} style={actionLabelButtonStyling}>
Labels
{isActionLabelExpanded ? <ExpandLessIcon style={{ marginLeft: 'auto' }} /> : <ExpandMoreIcon style={{ marginLeft: 'auto' }} />}
</Button>
<Collapse in={isActionLabelExpanded}>
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start', width: '100%', }}>
{topTags && topTags.length > 0 && topTags?.map((data, index) => (
<Button
key={index}
onClick={() => handleCheckboxChange(index)}
style={{
marginBottom: 5,
border: "none",
background: "none",
padding: 0,
color: "#F1F1F1",
textTransform: 'none',
gap: 5
}}
>
<input
id={`checkbox-${index}`}
type="checkbox"
checked={selectedTagsForUserAndOrgApps.includes(data.tag)}
className="ais-RefinementList-checkbox"
onChange={() => handleCheckboxChange(index)}
style={{ marginRight: 5 }}
/>
<span className="ais-RefinementList-labelText">{data.tag}</span>
</Button>
))}
<Button
onClick={handleClearFilter}
style={{
marginTop: 11,
textDecoration: 'underline',
textTransform: 'none',
backgroundColor: 'transparent',
fontSize: 16,
justifyContent: 'flex-start',
}}
disableElevation
disableRipple
>
Clear All
</Button>
</div>
</Collapse>
</div>
);
};
const FilterUsersAndOrgsAppByCreatedWith = ({ selectedOptionOfCreatedWith, setSelectedOptionOfCreatedWith }) => {
const [isCreatedWithExpanded, setIsCreatedWithExpanded] = useState(false);
const toogleCreatedWith = () => {
setIsCreatedWithExpanded((prevState) => !prevState);
};
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 (
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
marginTop: 20,
width: "100%",
fontWeight: 400
}}
>
<Button
style={createdWithButtonStyling}
onClick={toogleCreatedWith}
>
Created With
{isCreatedWithExpanded ? <ExpandLessIcon style={{ marginLeft: "auto" }} /> : <ExpandMoreIcon style={{ marginLeft: 'auto' }} />}
</Button>
<Collapse in={isCreatedWithExpanded}>
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start', width: '100%' }}>
{AppCreatedWithOptions?.map((data, index) => (
<Button
style={{
display: "inline-flex",
flexDirection: "row",
alignItems: "center",
cursor: "pointer",
marginBottom: 5,
border: "none",
background: "none",
padding: 0,
color: "#F1F1F1",
textTransform: 'none'
}}
key={index}
onClick={() => handleCheckboxChange(index)}
>
<input
id={`checkbox-${index}`}
type="checkbox"
className="ais-RefinementList-checkbox"
checked={selectedOptionOfCreatedWith.includes(data)}
onChange={() => handleCheckboxChange(index)}
style={{ marginRight: 5 }}
/>
<span style={{ fontSize: 16, marginLeft: 5, fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)" }}>{data}</span>
</Button>
))}
<Button
style={{
marginTop: 11,
fontSize: 16,
textAlign: 'left',
textDecoration: 'underline',
textTransform: 'none',
height: 30,
justifyContent: 'left',
backgroundColor: 'transparent'
}}
onClick={handleClearFilter}
disableElevation
disableRipple
>
Clear All
</Button>
</div>
</Collapse>
</div>
);
};
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 (
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
marginTop: 20,
width: "100%",
}}
>
<Button
style={createdByButtonStyling}
onClick={toogleCreatedBy}
disabled={true}
>
Created By
{isCreatedByExpanded ? (
<ExpandLessIcon style={{ marginLeft: "auto" }} />
) : (
<ExpandMoreIcon style={{ marginLeft: "auto" }} />
)}
</Button>
{isCreatedByExpanded && (
<>
<RefinementList attribute="creator" />
<Button
style={{
marginTop: 11,
textAlign: 'left',
textDecoration: 'underline',
textTransform: 'none',
height: 30,
justifyContent: 'left',
backgroundColor: 'transparent'
}}
// onClick={handleClearFilter}
>
Clear All
</Button>
</>
)}
</div>
);
};
const FilterForUserAndOrgApps = ({ setselectedCategoryForUsersAndOgsApps, setSelectedTagsForUserAndOrgApps, setSelectedOptionOfCreatedWith, selectedCategoryForUsersAndOgsApps, selectedTagsForUserAndOrgApps, selectedOptionOfCreatedWith }) => {
return (
<div
style={{
width: 200,
marginRight: 16
}}
>
{isLoggedIn === true && (
<div style={{
display: "flex",
flexDirection: "column",
width: 200,
alignItems: "flex-start",
marginRight: 16,
}}>
<Typography variant="h5" style={{ marginBottom: 30, marginTop: 30, fontWeight: "400", fontSize: 24 }}>
Filter By
</Typography>
<FilterUsersAndOrgsAppByCategory selectedCategoryForUsersAndOgsApps={selectedCategoryForUsersAndOgsApps} setselectedCategoryForUsersAndOgsApps={setselectedCategoryForUsersAndOgsApps} />
<FilterUsersAndOrgsAppByActionLabel selectedTagsForUserAndOrgApps={selectedTagsForUserAndOrgApps} setSelectedTagsForUserAndOrgApps={setSelectedTagsForUserAndOrgApps} />
<FilterUsersAndOrgsAppByCreatedWith selectedOptionOfCreatedWith={selectedOptionOfCreatedWith} setSelectedOptionOfCreatedWith={setSelectedOptionOfCreatedWith} />
<FilterUsersAndOrgsAppCreatedBy />
</div>
)}
</div>
)
}
useEffect(() => {
if (currTab === 1 || currTab === 2) {
setIsLoading(true);
}
}, [currTab])
//Component to fetch all apps created by user and Org
const UserAndOrgApps = ({ selectedCategoryForUsersAndOgsApps, selectedTagsForUserAndOrgApps, selectedOptionOfCreatedWith, setselectedCategoryForUsersAndOgsApps, setSelectedTagsForUserAndOrgApps, setSelectedOptionOfCreatedWith }) => {
const [searchQuery, setSearchQuery] = useState("");
const [appsToShow, setAppsToShow] = useState([]);
useEffect(() => {
if (currTab === 1) {
setAppsToShow(orgApps)
}
if (currTab === 2) {
setAppsToShow(userApps)
}
}, [currTab])
useEffect(() => {
setselectedCategoryForUsersAndOgsApps([]);
setSelectedTagsForUserAndOrgApps([]);
setSelectedOptionOfCreatedWith([]);
}, [currTab])
//Search app base on app name, category and tag
const filteredUserAppdata = Array.isArray(appsToShow) ? appsToShow.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 [showNoAppFound, setShowNoAppFound] = useState(false);
//show some delay to show the "App Not Found." so it doesn't not show while changing tab.
useEffect(() => {
const timer = setTimeout(() => {
setShowNoAppFound(true);
}, 1000);
return () => clearTimeout(timer);
}, []);
return (
<div>
{isLoading ? (
<Box sx={{ position: 'absolute', top: '30%', left: '50%', }}> <CircularProgress /> </Box>
) : (
<div>
{isLoggedIn ? (
<div>
<SearchBoxForOrgAndUserApp searchQuery={searchQuery} setSearchQuery={setSearchQuery} />
{((filteredUserAppdata.length === 0 && showNoAppFound) && isLoggedIn && !isLoading) ? (
<Typography wait={1000} variant="body1" style={{ marginTop: '30%' }}>No App Found</Typography>
) : (
<div
style={{
maxWidth: 741,
maxHeight: 570,
}}
>
<div>
<Grid>
<div
style={{
gap: 16,
marginTop: 16,
display: "flex",
flexWrap: "wrap",
justifyContent: "flex-start",
marginLeft: 24,
overflowY: "auto",
overflowX: "hidden",
maxHeight: 570,
scrollbarWidth: "thin",
scrollbarColor: "#494949 #2f2f2f",
maxHeight: 570,
}}
>
{filteredUserAppdata?.map((data, index) => {
const isMouseOverOnCloudIcon = false;
const xs = 12;
const rowHandler = 12;
const searchClient = {};
const userdata = {};
const paperStyle = {
backgroundColor: mouseHoverIndex === index ? "rgba(26, 26, 26, 1)" : "#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 === true ?
`/apps/${data.id}`
: `https://shuffler.io/apps/${data.id}`;
if (data.name === "" && data.id === "") {
return null
}
return (
<Zoom
key={index}
in={true}
style={{
transitionDelay: `${workflowDelay}ms`,
}}
>
<Grid rowSpacing={1} item xs={xs} key={index}>
<a
href={appUrl}
rel="noopener noreferrer"
style={{
textDecoration: "none",
color: "#f85a3e",
}}
>
<Paper
elevation={0}
style={paperStyle}
onMouseOver={() => {
setMouseHoverIndex(index);
}}
onMouseOut={() => {
setMouseHoverIndex(-1);
}}
>
<ButtonBase
style={{
borderRadius: 8,
fontSize: 16,
overflow: "hidden",
display: "flex",
alignItems: "flex-start",
width: '100%',
backgroundColor: mouseHoverIndex === index ? "#2F2F2F" : "#1A1A1A",
}}
>
<img
alt={data.name}
src={data.small_image || data.large_image ? (data.small_image || data.large_image) : "/images/no_image.png"}
style={{
width: 80,
height: 80,
borderRadius: 12,
margin: 8,
}}
/>
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
width: 339,
gap: 8,
fontWeight: '400',
overflow: "hidden",
margin: "12px 0 12px 0",
fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)",
}}
>
<div
style={{
display: 'flex',
flexDirection: 'row',
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
marginLeft: 8,
gap: 8
}}
>
{normalizedString(data.name)}
</div>
<div
style={{
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
marginLeft: 8,
color: "rgba(158, 158, 158, 1)"
}}
>
{data.categories !== null
? normalizedString(data.categories).join(", ")
: "NA"}
</div>
<div
style={{
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
width: 230,
textAlign: 'start',
marginLeft: 8,
color: "rgba(158, 158, 158, 1)",
display: "flex",
}}
>
<div style={{minWidth: 120, overflow: "hidden", }}>
{data.generated !== true ?
<div>
{data?.tags &&
data?.tags?.slice(0,2)?.map((tag, tagIndex) => (
<span key={tagIndex}>
{normalizedString(tag)}
{tagIndex < data.tags.length - 1 ? ", " : ""}
</span>
))
}
</div>
: null}
</div>
{currTab === 1 && !deactivatedIndexes.includes(index) && mouseHoverIndex === index && data.generated === true ?
<Button style={{
marginLeft: 15,
width: 102,
height: 35,
borderRadius: 200,
backgroundColor: "rgba(73, 73, 73, 1)",
color: "rgba(241, 241, 241, 1)",
textTransform: "none",
}}
onClick={(event) => {
//deactivatedIndexes.push(index)
//setDeactivatedIndexes(deactivatedIndexes)
event.preventDefault();
event.stopPropagation();
//handleActivateButton(event, data, "deactivate");
// FIXME: Put this in a function lol
const url = `${globalUrl}/api/v1/apps/${data.id}/deactivate`;
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 Deactivated Successfully. Reload UI to see updated changes.")
//const updatedIds = allActivatedAppIds.filter(id => id !== data.objectID);
//setAllActivatedAppIds(updatedIds);
}
})
.catch(error => {
console.log("app error: ", error.toString());
})
}}>
Deactivate
</Button>
: null}
</div>
{/* )} */}
</div>
</ButtonBase>
</Paper>
</a>
</Grid>
</Zoom>
);
})
}
</div>
</Grid>
</div>
</div>
)}
</div>
) : (
<Typography variant="body" style={{ position: "relative", top: 40 }}>Please <a href="/login" rel="noopener noreferrer" style={{ color: "rgba(255, 132, 68, 1)", textDecoration: "underline" }}>login</a> to your account first to view {`${currTab === 1 ? "Organization" : "My"}`} Apps.<br />
Or <a href="/register" rel="noopener noreferrer" style={{ color: "rgba(255, 132, 68, 1)", textDecoration: "underline" }}>signup</a> to create a new account.</Typography>
)}
</div>
)}
</div>
);
};
const AppTab = ({ selectedCategoryForUsersAndOgsApps, selectedTagsForUserAndOrgApps, selectedOptionOfCreatedWith, setselectedCategoryForUsersAndOgsApps, setSelectedTagsForUserAndOrgApps, setSelectedOptionOfCreatedWith }) => {
const [isAnyAppActivated, setIsAnyAppActivated] = useState(false);
return (
<div style={{ display: "flex", justifyContent: "center" }}>
<div style={boxStyle}>
<Tabs
style={{ marginTop: 15, display: "flex", width: "100%", borderBottom: '1px solid rgba(73, 73, 73, 1)', height: 44 }}
value={currTab}
onChange={handleTabChange}
scrollButtons="off"
>
<Tab
label="All Apps"
sx={{
color: currTab === 0 ? "#F86743" : "inherit",
height: 44,
fontSize: 16,
textTransform: 'none',
flex: 1,
fontWeight: 400,
paddingBottom: 3,
marginLeft: 3,
fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)",
}}
></Tab>
<Tab
label=<div style={{ display: 'flex', flexDirection: "row", gap: 8, whiteSpace: "nowrap", }}>{isAnyAppActivated && <Box sx={{ width: 8, height: 8, backgroundColor: "#02CB70", borderRadius: '50%', }} />}<span>Organization Apps</span></div>
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)",
}}
></Tab>
<Tab
label="My Apps"
sx={{
color: currTab === 2 ? "#F86743" : "inherit",
height: 44,
fontSize: 16,
textTransform: 'none',
flex: 1,
fontWeight: 400,
paddingBottom: 3,
marginRight: 3,
fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)",
}}
></Tab>
</Tabs>
{currTab === 0 ? (
<AllApps setIsAnyAppActivated={setIsAnyAppActivated} />
) : currTab === 1 || currTab === 2 ? (
<UserAndOrgApps selectedCategoryForUsersAndOgsApps={selectedCategoryForUsersAndOgsApps} selectedTagsForUserAndOrgApps={selectedTagsForUserAndOrgApps} selectedOptionOfCreatedWith={selectedOptionOfCreatedWith} setselectedCategoryForUsersAndOgsApps={setselectedCategoryForUsersAndOgsApps} setSelectedTagsForUserAndOrgApps={setSelectedTagsForUserAndOrgApps} setSelectedOptionOfCreatedWith={setSelectedOptionOfCreatedWith} />
) : null}
</div>
</div>
);
};
const CustomSearchBox = connectSearchBox(SearchBox);
const CustomHits = connectHits(Hits);
const DisplayAllAppsTab = () => {
const [selectedCategoryForUsersAndOgsApps, setselectedCategoryForUsersAndOgsApps] = useState([]);
const [selectedTagsForUserAndOrgApps, setSelectedTagsForUserAndOrgApps] = useState([]);
const [selectedOptionOfCreatedWith, setSelectedOptionOfCreatedWith] = useState([]);
return (
<div>
<InstantSearch searchClient={searchClient} indexName="appsearch">
<div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'center', paddingRight: 215 }}>
{currTab === 0 ? (
<FilterForAllApps />
) : (
<FilterForUserAndOrgApps
setselectedCategoryForUsersAndOgsApps={setselectedCategoryForUsersAndOgsApps}
setSelectedTagsForUserAndOrgApps={setSelectedTagsForUserAndOrgApps}
setSelectedOptionOfCreatedWith={setSelectedOptionOfCreatedWith}
selectedCategoryForUsersAndOgsApps={selectedCategoryForUsersAndOgsApps}
selectedTagsForUserAndOrgApps={selectedTagsForUserAndOrgApps}
selectedOptionOfCreatedWith={selectedOptionOfCreatedWith}
/>
)}
<AppTab
selectedCategoryForUsersAndOgsApps={selectedCategoryForUsersAndOgsApps}
selectedTagsForUserAndOrgApps={selectedTagsForUserAndOrgApps}
selectedOptionOfCreatedWith={selectedOptionOfCreatedWith}
setselectedCategoryForUsersAndOgsApps={setselectedCategoryForUsersAndOgsApps}
setSelectedTagsForUserAndOrgApps={setSelectedTagsForUserAndOrgApps}
setSelectedOptionOfCreatedWith={setSelectedOptionOfCreatedWith}
/>
</div>
<Configure clickAnalytics />
</InstantSearch>
</div>
);
};
return (
<div
style={{
width: "100%",
textAlign: "center",
position: "relative",
display: "flex",
}}
>
<div
style={{
height: "100%",
width: "100%",
}}
>
<DisplayAllAppsTab />
{showSuggestion === true ? (
<div
style={{
paddingTop: 0,
maxWidth: isMobile ? "100%" : "60%",
margin: "auto",
}}
>
<Typography variant="h6" style={{ color: "white", marginTop: 50 }}>
Can't find what you're looking for?
</Typography>
<div
style={{
flex: "1",
display: "flex",
flexDirection: "row",
textAlign: "center",
}}
>
<TextField
required
style={{
flex: "1",
marginRight: 15,
backgroundColor: theme.palette.inputColor,
}}
InputProps={{
style: {
color: "#ffffff",
},
}}
color="primary"
fullWidth={true}
placeholder="Email (optional)"
type="email"
id="email-handler"
autoComplete="email"
margin="normal"
variant="outlined"
onChange={(e) => setFormMail(e.target.value)}
/>
<TextField
required
style={{ flex: "1", backgroundColor: theme.palette.inputColor }}
InputProps={{
style: {
color: "#ffffff",
},
}}
color="primary"
fullWidth={true}
placeholder="What apps do you want to see?"
type=""
id="standard-required"
margin="normal"
variant="outlined"
autoComplete="off"
onChange={(e) => setMessage(e.target.value)}
/>
</div>
<Button
variant="contained"
color="primary"
style={buttonStyle}
disabled={message.length === 0}
onClick={() => {
submitContact(formMail, message);
}}
>
Submit
</Button>
<Typography style={{ color: "white" }} variant="body2">
{formMessage}
</Typography>
</div>
) : null}
</div>
</div>
);
};
export default AppGrid;