Tabs are working except myApps and categories and labels are displayed

This commit is contained in:
monilprajapati
2024-11-13 22:34:56 +05:30
parent 4855b54937
commit 39e3218533
3 changed files with 816 additions and 421 deletions
+2 -19
View File
@@ -35,8 +35,8 @@ import (
"github.com/go-git/go-billy/v5/memfs" "github.com/go-git/go-billy/v5/memfs"
"github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/storage/memory"
gitProxy "github.com/go-git/go-git/v5/plumbing/transport" gitProxy "github.com/go-git/go-git/v5/plumbing/transport"
"github.com/go-git/go-git/v5/storage/memory"
// Random // Random
xj "github.com/basgys/goxml2json" xj "github.com/basgys/goxml2json"
@@ -257,7 +257,6 @@ type Hook struct {
Environment string `json:"environment" datastore:"environment"` Environment string `json:"environment" datastore:"environment"`
} }
func GetUsersHandler(w http.ResponseWriter, r *http.Request) { func GetUsersHandler(w http.ResponseWriter, r *http.Request) {
data := map[string]interface{}{ data := map[string]interface{}{
"id": "12345", "id": "12345",
@@ -559,7 +558,6 @@ func createNewUser(username, password, role, apikey string, org shuffle.OrgMini)
} }
} }
return nil return nil
} }
@@ -932,7 +930,6 @@ func handleInfo(resp http.ResponseWriter, request *http.Request) {
log.Printf("[DEBUG] Failed to get org during getinfo: %s", err) log.Printf("[DEBUG] Failed to get org during getinfo: %s", err)
} }
//if err == nil { //if err == nil {
if len(org.Id) > 0 { if len(org.Id) > 0 {
if userInfo.Role == "" { if userInfo.Role == "" {
@@ -1092,7 +1089,6 @@ type passwordReset struct {
Reference string `json:"reference"` Reference string `json:"reference"`
} }
func checkAdminLogin(resp http.ResponseWriter, request *http.Request) { func checkAdminLogin(resp http.ResponseWriter, request *http.Request) {
cors := shuffle.HandleCors(resp, request) cors := shuffle.HandleCors(resp, request)
if cors { if cors {
@@ -3338,7 +3334,6 @@ func buildSwaggerApp(resp http.ResponseWriter, body []byte, user shuffle.User, s
} }
} }
log.Printf("[DEBUG] Successfully built app %s (%s)", api.Name, api.ID) log.Printf("[DEBUG] Successfully built app %s (%s)", api.Name, api.ID)
if len(user.Id) > 0 { if len(user.Id) > 0 {
resp.WriteHeader(200) resp.WriteHeader(200)
@@ -3379,8 +3374,6 @@ func verifySwagger(resp http.ResponseWriter, request *http.Request) {
buildSwaggerApp(resp, body, user, false) buildSwaggerApp(resp, body, user, false)
} }
// Hotloads new apps from a folder // Hotloads new apps from a folder
func handleAppHotload(ctx context.Context, location string, forceUpdate bool) error { func handleAppHotload(ctx context.Context, location string, forceUpdate bool) error {
@@ -3727,7 +3720,6 @@ func remoteOrgJobController(org shuffle.Org, body []byte) error {
return nil return nil
} }
func remoteOrgJobHandler(org shuffle.Org, interval int) error { func remoteOrgJobHandler(org shuffle.Org, interval int) error {
// Check if it's 1 in 10 (10% chance random) // Check if it's 1 in 10 (10% chance random)
@@ -3777,7 +3769,6 @@ func remoteOrgJobHandler(org shuffle.Org, interval int) error {
backupJobData = []byte{} backupJobData = []byte{}
} }
syncUrl := fmt.Sprintf("%s/api/v1/cloud/sync", syncUrl) syncUrl := fmt.Sprintf("%s/api/v1/cloud/sync", syncUrl)
client := shuffle.GetExternalClient(syncUrl) client := shuffle.GetExternalClient(syncUrl)
req, err := http.NewRequest( req, err := http.NewRequest(
@@ -4340,7 +4331,6 @@ func runInitEs(ctx context.Context) {
log.Printf("[INFO] Skipping download of extra API samples as %d were found", len(workflowapps)) log.Printf("[INFO] Skipping download of extra API samples as %d were found", len(workflowapps))
} }
if os.Getenv("SHUFFLE_HEALTHCHECK_DISABLED") != "true" { if os.Getenv("SHUFFLE_HEALTHCHECK_DISABLED") != "true" {
healthcheckInterval := 30 healthcheckInterval := 30
log.Printf("[INFO] Starting healthcheck job every %d minute. Stats available on /api/v1/health/stats. Disable with SHUFFLE_HEALTHCHECK_DISABLED=true", healthcheckInterval) log.Printf("[INFO] Starting healthcheck job every %d minute. Stats available on /api/v1/health/stats. Disable with SHUFFLE_HEALTHCHECK_DISABLED=true", healthcheckInterval)
@@ -4369,7 +4359,6 @@ func runInitEs(ctx context.Context) {
log.Printf("[INFO] Finished INIT (ES)") log.Printf("[INFO] Finished INIT (ES)")
} }
func handleVerifyCloudsync(orgId string) (shuffle.SyncFeatures, error) { func handleVerifyCloudsync(orgId string) (shuffle.SyncFeatures, error) {
ctx := context.Background() ctx := context.Background()
org, err := shuffle.GetOrg(ctx, orgId) org, err := shuffle.GetOrg(ctx, orgId)
@@ -4948,8 +4937,6 @@ func makeWorkflowPublic(resp http.ResponseWriter, request *http.Request) {
resp.Write([]byte(fmt.Sprintf(`{"success": true}`))) resp.Write([]byte(fmt.Sprintf(`{"success": true}`)))
} }
func handleAppZipUpload(resp http.ResponseWriter, request *http.Request) { func handleAppZipUpload(resp http.ResponseWriter, request *http.Request) {
cors := shuffle.HandleCors(resp, request) cors := shuffle.HandleCors(resp, request)
if cors { if cors {
@@ -5008,8 +4995,6 @@ func handleAppZipUpload(resp http.ResponseWriter, request *http.Request) {
resp.Write([]byte("OK")) resp.Write([]byte("OK"))
} }
func initHandlers() { func initHandlers() {
var err error var err error
ctx := context.Background() ctx := context.Background()
@@ -5056,7 +5041,7 @@ func initHandlers() {
r.HandleFunc("/api/v1/users/register", handleRegister).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/users/register", handleRegister).Methods("POST", "OPTIONS")
r.HandleFunc("/api/v1/users/checkusers", checkAdminLogin).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/users/checkusers", checkAdminLogin).Methods("GET", "OPTIONS")
r.HandleFunc("/api/v1/users/getinfo", handleInfo).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/users/getinfo", handleInfo).Methods("GET", "OPTIONS")
r.HandleFunc("/api/v1/users/{userId}/apps", shuffle.HandleGetUserApps).Methods("GET", "OPTIONS")
r.HandleFunc("/api/v1/users/apps", shuffle.HandleGetUserApps).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/users/apps", shuffle.HandleGetUserApps).Methods("GET", "OPTIONS")
r.HandleFunc("/api/v1/users/generateapikey", shuffle.HandleApiGeneration).Methods("GET", "POST", "OPTIONS") r.HandleFunc("/api/v1/users/generateapikey", shuffle.HandleApiGeneration).Methods("GET", "POST", "OPTIONS")
r.HandleFunc("/api/v1/users/logout", shuffle.HandleLogout).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/users/logout", shuffle.HandleLogout).Methods("POST", "OPTIONS")
@@ -5244,7 +5229,6 @@ func initHandlers() {
r.HandleFunc("/api/v1/orgs/{orgId}/datastore", shuffle.HandleSetCacheKey).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/orgs/{orgId}/datastore", shuffle.HandleSetCacheKey).Methods("POST", "OPTIONS")
r.HandleFunc("/api/v1/orgs/{orgId}/datastore/{cache_key}", shuffle.HandleDeleteCacheKey).Methods("DELETE", "OPTIONS") r.HandleFunc("/api/v1/orgs/{orgId}/datastore/{cache_key}", shuffle.HandleDeleteCacheKey).Methods("DELETE", "OPTIONS")
// Docker orborus specific - downloads an image // Docker orborus specific - downloads an image
r.HandleFunc("/api/v1/get_docker_image", getDockerImage).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/get_docker_image", getDockerImage).Methods("POST", "OPTIONS")
r.HandleFunc("/api/v1/login_sso", shuffle.HandleSSO).Methods("GET", "POST", "OPTIONS") r.HandleFunc("/api/v1/login_sso", shuffle.HandleSSO).Methods("GET", "POST", "OPTIONS")
@@ -5274,7 +5258,6 @@ func initHandlers() {
r.HandleFunc("/api/v1/detections/{fileId}/{action}", shuffle.HandleToggleRule).Methods("PUT", "OPTIONS") r.HandleFunc("/api/v1/detections/{fileId}/{action}", shuffle.HandleToggleRule).Methods("PUT", "OPTIONS")
//r.HandleFunc("/api/v1/detections/siem/node_health", shuffle.HandleTenzirHealthUpdate).Methods("POST","OPTIONS") //r.HandleFunc("/api/v1/detections/siem/node_health", shuffle.HandleTenzirHealthUpdate).Methods("POST","OPTIONS")
// Introduced in 0.9.21 to handle notifications for e.g. failed Workflow // Introduced in 0.9.21 to handle notifications for e.g. failed Workflow
r.HandleFunc("/api/v1/notifications", shuffle.HandleCreateNotification).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/notifications", shuffle.HandleCreateNotification).Methods("POST", "OPTIONS")
r.HandleFunc("/api/v1/notifications", shuffle.HandleGetNotifications).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/notifications", shuffle.HandleGetNotifications).Methods("GET", "OPTIONS")
+1
View File
@@ -417,6 +417,7 @@ const App = (message, props) => {
<Apps2 <Apps2
serverside={false} serverside={false}
isLoaded={isLoaded} isLoaded={isLoaded}
isLoggedIn={isLoggedIn}
userdata={userdata} userdata={userdata}
globalUrl={globalUrl} globalUrl={globalUrl}
surfaceColor={theme.palette.surfaceColor} surfaceColor={theme.palette.surfaceColor}
+613 -202
View File
@@ -1,4 +1,4 @@
import React, { useState, useEffect, useContext } from "react"; import React, { useState, useEffect, useContext, useCallback, memo, useMemo } from "react";
import theme from "../theme.jsx"; import theme from "../theme.jsx";
import { isMobile } from "react-device-detect"; import { isMobile } from "react-device-detect";
import AppGrid from "../components/AppGrid.jsx"; import AppGrid from "../components/AppGrid.jsx";
@@ -9,6 +9,8 @@ import {
Paper, Paper,
ButtonBase, ButtonBase,
Tooltip, Tooltip,
Box,
CircularProgress,
} from "@mui/material"; } from "@mui/material";
import { Context } from "../context/ContextApi.jsx"; import { Context } from "../context/ContextApi.jsx";
import Add from '@mui/icons-material/Add'; import Add from '@mui/icons-material/Add';
@@ -16,12 +18,19 @@ import InputAdornment from '@mui/material/InputAdornment';
import Search from '@mui/icons-material/Search'; import Search from '@mui/icons-material/Search';
import ClearIcon from '@mui/icons-material/Clear'; import ClearIcon from '@mui/icons-material/Clear';
import { ClearRefinements, connectHits, connectSearchBox, connectStateResults, RefinementList } from "react-instantsearch-dom"; import { ClearRefinements, connectHits, connectSearchBox, connectStateResults, InstantSearch, RefinementList } from "react-instantsearch-dom";
import { removeQuery } from "../components/ScrollToTop.jsx"; import { removeQuery } from "../components/ScrollToTop.jsx";
import { toast } from "react-toastify"; import { toast } from "react-toastify";
import algoliasearch from "algoliasearch/lite";
const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl }) => {
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; const searchClient = algoliasearch(
"JNSS5CFDZZ",
"db08e40265e2941b9a7d8f644b6e5240"
);
const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl, deactivatedIndexes }) => {
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 = isCloud ? `/apps/${data.id}` : `https://shuffler.io/apps/${data.id}`;
const paperStyle = { const paperStyle = {
@@ -33,23 +42,75 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl }
height: 96, height: 96,
borderRadius: 8, borderRadius: 8,
boxShadow: "0px 0px 10px 0px rgba(0, 0, 0, 0.1)", boxShadow: "0px 0px 10px 0px rgba(0, 0, 0, 0.1)",
marginBottom: 20, // marginBottom: 20,
}; };
return ( return (
<Grid item xs={12} key={index}> <Grid item xs={12} key={index}>
<a href={appUrl} rel="noopener noreferrer" style={{ textDecoration: "none", color: "#f85a3e" }}> <a href={appUrl} rel="noopener noreferrer" style={{ textDecoration: "none", color: "#f85a3e" }}>
<Paper elevation={0} style={paperStyle} onMouseOver={() => setMouseHoverIndex(index)} onMouseOut={() => setMouseHoverIndex(-1)}> <Paper elevation={0} style={paperStyle} onMouseOver={() => setMouseHoverIndex(index)} onMouseOut={() => setMouseHoverIndex(-1)}>
<ButtonBase style={{ borderRadius: 6, fontSize: 16, overflow: "hidden", display: "flex", alignItems: "flex-start", width: '100%', backgroundColor: mouseHoverIndex === index ? "#2F2F2F" : "#212121" }}> <ButtonBase style={{
<img alt={data.name} src={data.small_image || data.large_image ? (data.small_image || data.large_image) : "/images/no_image.png"} style={{ width: 100, height: 100, borderRadius: 6, margin: 10, border: "1px solid #212122", boxShadow: "0px 0px 10px 0px rgba(0, 0, 0, 0.1)" }} /> borderRadius: 6,
<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)" }}> fontSize: 16,
<div style={{ display: 'flex', flexDirection: 'row', overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", marginLeft: 8, gap: 8 }}> overflow: "hidden",
display: "flex",
alignItems: "flex-start",
width: '100%',
backgroundColor: mouseHoverIndex === index ? "#2F2F2F" : "#212121"
}}>
<img
alt={data.name}
src={data.small_image || data.large_image ? (data.small_image || data.large_image) : "/images/no_image.png"}
style={{
width: 100,
height: 100,
borderRadius: 6,
margin: 10,
border: "1px solid #212122",
boxShadow: "0px 0px 10px 0px rgba(0, 0, 0, 0.1)"
}}
/>
<div style={{
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
width: 339,
gap: 8,
fontWeight: '400',
overflow: "hidden",
margin: "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
}}>
{data.name.replace(/_/g, ' ').replace(/\b\w/g, char => char.toUpperCase())} {data.name.replace(/_/g, ' ').replace(/\b\w/g, char => char.toUpperCase())}
</div> </div>
<div style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", marginLeft: 8, color: "rgba(158, 158, 158, 1)" }}> <div style={{
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
marginLeft: 8,
color: "rgba(158, 158, 158, 1)"
}}>
{data.categories ? data.categories.join(", ") : "NA"} {data.categories ? data.categories.join(", ") : "NA"}
</div> </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={{
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" }}> <div style={{ minWidth: 120, overflow: "hidden" }}>
{data.generated !== true && data.tags && data.tags.slice(0, 2).map((tag, tagIndex) => ( {data.generated !== true && data.tags && data.tags.slice(0, 2).map((tag, tagIndex) => (
<span key={tagIndex}> <span key={tagIndex}>
@@ -59,11 +120,44 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl }
))} ))}
</div> </div>
{/* Deactivate button logic */} {/* Deactivate button logic */}
{data.generated === true ? ( {mouseHoverIndex === index ?
<Button style={{ marginLeft: 15, width: 102, height: 35, borderRadius: 200, backgroundColor: "rgba(73, 73, 73, 1)", color: "rgba(241, 241, 241, 1)", textTransform: "none" }}> <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) => {
event.preventDefault();
event.stopPropagation();
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.");
}
})
.catch(error => {
console.log("app error: ", error.toString());
});
}}>
Deactivate Deactivate
</Button> </Button>
) : null} : null}
</div> </div>
</div> </div>
</ButtonBase> </ButtonBase>
@@ -73,8 +167,364 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl }
); );
}; };
// Component to fetch all public app from the algolia.
const Hits = memo(({
userdata,
hits,
setIsAnyAppActivated,
searchQuery,
globalUrl,
setIsLoggedIn,
isLoading,
isLoggedIn,
}) => {
var counted = 0;
const [hoverEffect, setHoverEffect] = useState(-1);
const [allActivatedAppIds, setAllActivatedAppIds] = useState(userdata?.active_apps);
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io" || window.location.host === "localhost:3000";
const [deactivatedIndexes, setDeactivatedIndexes] = React.useState([]);
const normalizedString = (name) => {
if (typeof name === 'string') {
return name.replace(/_/g, ' ');
} else {
return name;
}
};
// const fetchUserData = useCallback(async () => {
// try {
// const response = await fetch(`${globalUrl}/api/v1/me`, {
// credentials: "include",
// headers: {
// 'Content-Type': 'application/json',
// },
// });
// const responseJson = await response.json();
// console.log("responseJson : user data", responseJson)
// if (responseJson.success) {
// setUserdata(responseJson);
// setAllActivatedAppIds(responseJson.active_apps);
// setIsLoggedIn(true);
// } else {
// setIsLoggedIn(false);
// }
// } catch (error) {
// console.log("Failed login check: ", error);
// }
// }, [globalUrl]); // Added globalUrl as a dependency
// useEffect(() => {
// fetchUserData();
// }, [fetchUserData]); // Ensure fetchUserData is stable
//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 [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);
}, []);
const memoizedHits = useMemo(() => hits, [hits]);
return (
<div>
{!isLoading ? (
<div>
{memoizedHits?.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",
}}
>
{memoizedHits.map((data, index) => {
const appUrl =
isCloud
? `/apps/${data.objectID}?queryID=${data.__queryID}`
: `https://shuffler.io/apps/${data.objectID}?queryID=${data.__queryID}`;
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={{
backgroundColor: hoverEffect === index ? "rgba(26, 26, 26, 1)" : "#1A1A1A",
color: "rgba(241, 241, 241, 1)",
cursor: "pointer",
position: "relative",
width: 365,
height: 96,
borderRadius: 8,
boxShadow: "0px 0px 10px 0px rgba(0, 0, 0, 0.1)",
marginBottom: 20,
}}
onMouseEnter={() => {
setHoverEffect(index);
}}
onMouseLeave={() => {
setHoverEffect(-1);
}}
>
<ButtonBase style={{
borderRadius: 6,
fontSize: 16,
overflow: "hidden",
display: "flex",
alignItems: "flex-start",
width: '100%',
backgroundColor: hoverEffect === index ? "#2F2F2F" : "#212121"
}}>
<img
alt={data.name}
src={data.image_url ? data.image_url : "/images/no_image.png"}
style={{
width: 100,
height: 100,
borderRadius: 6,
margin: 10,
border: "1px solid #212122",
boxShadow: "0px 0px 10px 0px rgba(0, 0, 0, 0.1)"
}}
/>
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
width: 339,
gap: 8,
fontWeight: '400',
overflow: "hidden",
margin: "12px 0",
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, }}>
{hoverEffect === 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 }}>
{hoverEffect === 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>
</ButtonBase>
</Paper>
</a>
</Grid>
</Zoom>
);
})
}
</div>
</Grid >
)}
</div>
) : (
<div><Box sx={{ position: 'absolute', top: '30%', left: '50%', }}> <CircularProgress /></Box></div>
)}
</div>
);
});
const Apps2 = (props) => { const Apps2 = (props) => {
const { globalUrl, isLoaded, serverside, userdata } = props; const { globalUrl, isLoaded, serverside, userdata, isLoggedIn } = props;
let navigate = useNavigate(); let navigate = useNavigate();
const { leftSideBarOpenByClick } = useContext(Context); const { leftSideBarOpenByClick } = useContext(Context);
const location = useLocation(); const location = useLocation();
@@ -91,22 +541,13 @@ const Apps2 = (props) => {
const [selectedTagsForUserAndOrgApps, setSelectedTagsForUserAndOrgApps] = useState([]); const [selectedTagsForUserAndOrgApps, setSelectedTagsForUserAndOrgApps] = useState([]);
const [appsToShow, setAppsToShow] = useState([]); const [appsToShow, setAppsToShow] = useState([]);
const [deactivatedIndexes, setDeactivatedIndexes] = React.useState([]); const [deactivatedIndexes, setDeactivatedIndexes] = React.useState([]);
const [IsAnyAppActivated, setIsAnyAppActivated] = useState(false);
const [mouseHoverIndex, setMouseHoverIndex] = useState(-1); const [mouseHoverIndex, setMouseHoverIndex] = useState(-1);
var counted = 0; var counted = 0;
const [showNoAppFound, setShowNoAppFound] = useState(false); const [showNoAppFound, setShowNoAppFound] = useState(false);
const isCloud = const CustomHits = connectHits(Hits)
window.location.host === "localhost:3002" ||
window.location.host === "shuffler.io";
useEffect(() => {
const timer = setTimeout(() => {
setShowNoAppFound(true);
}, 1000);
return () => clearTimeout(timer);
}, []);
useEffect(() => { useEffect(() => {
const queryParams = new URLSearchParams(location.search); const queryParams = new URLSearchParams(location.search);
@@ -124,45 +565,43 @@ const Apps2 = (props) => {
useEffect(() => { useEffect(() => {
if (currTab === 1) { const fetchApps = async () => {
const baseUrl = globalUrl; const baseUrl = globalUrl;
const userAppsUrl = `${baseUrl}/api/v1/users/me/apps`; let url;
fetch(userAppsUrl, {
const userId = userdata?.id;
if (currTab === 1 && userId) {
url = `${baseUrl}/api/v1/users/${userId}/apps`;
} else if (currTab === 0) {
url = `${baseUrl}/api/v1/apps`;
} else {
return; // No need to fetch if not in the relevant tabs
}
setIsLoading(true);
try {
const response = await fetch(url, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
}) });
.then((response) => response.json()) const data = await response.json();
.then((data) => { if (currTab === 1) {
console.log("data from userApps", data)
setUserApps(data); setUserApps(data);
setIsLoading(false); setIsLoading(false);
})
.catch((err) => {
console.error("Error fetching user apps:", err);
});
} else if (currTab === 0) { } else if (currTab === 0) {
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) => {
console.log("data of orgApps", data)
setOrgApps(data); setOrgApps(data);
setIsLoading(false) setIsLoading(false);
})
.catch((err) => {
console.error("Error fetching apps:", err);
});
} }
}, [currTab]); } catch (err) {
console.error("Error fetching apps:", err);
}
};
fetchApps();
}, [currTab, globalUrl]); // Added globalUrl to dependencies to avoid stale closures
@@ -174,8 +613,6 @@ const Apps2 = (props) => {
}, [serverside]); }, [serverside]);
useEffect(() => {
const findTopCategories = () => { const findTopCategories = () => {
const categoryCountMap = {}; const categoryCountMap = {};
@@ -230,18 +667,6 @@ const Apps2 = (props) => {
return topTags; return topTags;
}; };
const categories = findTopCategories();
if (categories) {
setCategories(categories)
}
console.log(categories)
const tags = findTopTags();
if (tags) {
setLabels(tags);
}
console.log(tags)
}, [currTab])
const handleCreateApp = () => { const handleCreateApp = () => {
navigate('/create-app'); navigate('/create-app');
}; };
@@ -280,11 +705,16 @@ const Apps2 = (props) => {
const handleTabChange = (newTab) => { const handleTabChange = (newTab) => {
console.log("Current Tab", newTab)
setCurrTab(newTab); setCurrTab(newTab);
if (currTab === 0) { if (newTab === 0) {
setAppsToShow(orgApps) setAppsToShow(orgApps)
const categories = findTopCategories();
const labels = findTopTags();
setCategories(categories);
setLabels(labels);
} }
if (currTab === 1) { if (newTab === 1) {
setAppsToShow(userApps) setAppsToShow(userApps)
} }
const newQueryParam = newTab === 0 ? 'org_apps' : newTab === 1 ? 'my_apps' : 'all_apps'; const newQueryParam = newTab === 0 ? 'org_apps' : newTab === 1 ? 'my_apps' : 'all_apps';
@@ -292,7 +722,6 @@ const Apps2 = (props) => {
queryParams.set('tab', newQueryParam); queryParams.set('tab', newQueryParam);
queryParams.delete('q'); queryParams.delete('q');
window.history.replaceState({}, '', `${location.pathname}?${queryParams.toString()}`); window.history.replaceState({}, '', `${location.pathname}?${queryParams.toString()}`);
console.log("Current Tab", currTab)
console.log("Apps to show", appsToShow) console.log("Apps to show", appsToShow)
}; };
@@ -315,130 +744,10 @@ const Apps2 = (props) => {
}); });
const SearchBox = ({ refine, 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());
};
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">
<Search 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) => {
setSearchQuery(event.currentTarget.value);
removeQuery("q");
refine(event.currentTarget.value);
}}
onKeyDown={(event) => {
if (event.key === "Enter") {
event.preventDefault();
}
}}
limit={5}
/>
{/*isSearchStalled ? 'My search is stalled' : ''*/}
</form>
);
};
const CustomSearchBox = connectSearchBox(SearchBox);
// const CustomHits = connectHits(Hits);
return ( return (
<InstantSearch searchClient={searchClient} indexName="appsearch">
<div style={boxStyle}> <div style={boxStyle}>
<Typography variant="h4" style={{ marginBottom: 20, paddingLeft: 15, textTransform: 'none' }}> <Typography variant="h4" style={{ marginBottom: 20, paddingLeft: 15, textTransform: 'none' }}>
Apps Apps
@@ -501,11 +810,24 @@ const Apps2 = (props) => {
<MenuItem value="" > <MenuItem value="" >
All Categories All Categories
</MenuItem> </MenuItem>
{categories?.map((category) => ( {
currTab === 0 &&
(
categories?.map((category) => (
<MenuItem value={category.category}> <MenuItem value={category.category}>
{category.category} {category.category}
</MenuItem> </MenuItem>
))} ))
)
}
{
currTab === 2 &&
(
<div style={{ padding: "5px 10px" }}>
<RefinementList attribute="categories" />
</div>
)
}
</Select> </Select>
</div> </div>
@@ -521,12 +843,24 @@ const Apps2 = (props) => {
<MenuItem value=""> <MenuItem value="">
All Labels All Labels
</MenuItem> </MenuItem>
{labels?.map((tag) => ( {
currTab === 0 &&
(
labels?.map((tag) => (
<MenuItem value={tag.tag}> <MenuItem value={tag.tag}>
{tag.tag} {tag.tag}
</MenuItem> </MenuItem>
))} ))
{/* Add label options here */} )
}
{
currTab === 2 &&
(
<div style={{ padding: "5px 10px" }}>
<RefinementList attribute="action_labels" />
</div>
)
}
</Select> </Select>
</div> </div>
<div style={{ flex: 1, marginRight: 10 }}> <div style={{ flex: 1, marginRight: 10 }}>
@@ -542,20 +876,97 @@ const Apps2 = (props) => {
</div> </div>
</div> </div>
<div> <div>
{
currTab === 0 &&
(orgApps.length > 0 ? (
<div style={{ rowGap: 16, columnGap: 16, marginTop: 16, display: "flex", flexWrap: "wrap", justifyContent: "flex-start", overflowY: "auto", overflowX: "hidden", maxHeight: 570, scrollbarWidth: "thin", scrollbarColor: "#494949 #2f2f2f", maxHeight: 570, minHeight: 570 }}>
{
isLoading ? <div
style={{
width: "100%",
height: "100%",
display: "flex",
justifyContent: "center",
alignItems: "center",
}}
> <CircularProgress /></div>
:
(
filteredUserAppdata?.map((data, index) => (
<AppCard key={index} data={data} index={index} mouseHoverIndex={mouseHoverIndex} setMouseHoverIndex={setMouseHoverIndex} globalUrl={globalUrl} deactivatedIndexes={deactivatedIndexes} />
))
)
}
</div>
) : <div
style={{
marginTop: 100,
fontSize: 20,
fontWeight: 500,
width: "100%",
textAlign: "center",
}}
>No apps found</div>
)
}
{
currTab === 1 &&
(userApps.length > 0 ? (
<div style={{ rowGap: 16, columnGap: 16, marginTop: 16, display: "flex", flexWrap: "wrap", justifyContent: "flex-start", overflowY: "auto", overflowX: "hidden", maxHeight: 570, scrollbarWidth: "thin", scrollbarColor: "#494949 #2f2f2f", maxHeight: 570 }}> <div style={{ rowGap: 16, columnGap: 16, marginTop: 16, display: "flex", flexWrap: "wrap", justifyContent: "flex-start", overflowY: "auto", overflowX: "hidden", maxHeight: 570, scrollbarWidth: "thin", scrollbarColor: "#494949 #2f2f2f", maxHeight: 570 }}>
{orgApps.map((data, index) => ( {
<AppCard key={index} data={data} index={index} mouseHoverIndex={mouseHoverIndex} setMouseHoverIndex={setMouseHoverIndex} globalUrl={globalUrl} /> isLoading ? <div
))} style={{
width: "100%",
height: "100%",
display: "flex",
justifyContent: "center",
alignItems: "center",
}}
> <CircularProgress /></div>
:
(
userApps.map((data, index) => (
<AppCard key={index} data={data} index={index} mouseHoverIndex={mouseHoverIndex} setMouseHoverIndex={setMouseHoverIndex} globalUrl={globalUrl} deactivatedIndexes={deactivatedIndexes} />
))
)
}
</div> </div>
) : <div
style={{
marginTop: 100,
fontSize: 20,
fontWeight: 500,
width: "100%",
textAlign: "center",
}}
>No apps found</div>
)
}
{
currTab === 2 &&
<CustomHits
isLoggedIn={isLoggedIn}
userdata={userdata}
setIsAnyAppActivated={setIsAnyAppActivated}
hitsPerPage={5}
globalUrl={globalUrl}
searchQuery={searchQuery}
mouseHoverIndex={mouseHoverIndex}
setMouseHoverIndex={setMouseHoverIndex}
/>
}
</div> </div>
<AppGrid {/* <AppGrid
maxRows={4} maxRows={4}
showSuggestion={true} showSuggestion={true}
globalUrl={globalUrl} globalUrl={globalUrl}
isMobile={isMobile} isMobile={isMobile}
userdata={userdata} userdata={userdata}
/> /> */}
</div > </div >
</InstantSearch>
); );
}; };