Tabs are working except myApps and categories and labels are displayed
This commit is contained in:
+7
-24
@@ -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",
|
||||||
@@ -414,13 +413,13 @@ func isGitNoProxy(rawURL string) bool {
|
|||||||
}
|
}
|
||||||
host := parsedURL.Hostname()
|
host := parsedURL.Hostname()
|
||||||
|
|
||||||
for _,value := range noProxyList {
|
for _, value := range noProxyList {
|
||||||
value = strings.TrimSpace(value)
|
value = strings.TrimSpace(value)
|
||||||
|
|
||||||
if host == value {
|
if host == value {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(value, "*.") && strings.HasSuffix(host, value[2:]){
|
if strings.HasPrefix(value, "*.") && strings.HasSuffix(host, value[2:]) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -428,7 +427,7 @@ func isGitNoProxy(rawURL string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func checkGitProxy(cloneOptions *git.CloneOptions) *git.CloneOptions {
|
func checkGitProxy(cloneOptions *git.CloneOptions) *git.CloneOptions {
|
||||||
if os.Getenv("HTTP_PROXY") != "" && !isGitNoProxy(cloneOptions.URL){
|
if os.Getenv("HTTP_PROXY") != "" && !isGitNoProxy(cloneOptions.URL) {
|
||||||
cloneOptions.ProxyOptions = gitProxy.ProxyOptions{
|
cloneOptions.ProxyOptions = gitProxy.ProxyOptions{
|
||||||
URL: os.Getenv("HTTP_PROXY"),
|
URL: os.Getenv("HTTP_PROXY"),
|
||||||
}
|
}
|
||||||
@@ -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")
|
||||||
@@ -5266,15 +5250,14 @@ func initHandlers() {
|
|||||||
// This structure is horrendous. Needs fixing after we got the prototype up
|
// This structure is horrendous. Needs fixing after we got the prototype up
|
||||||
r.HandleFunc("/api/v1/detections/{detectionType}/connect", shuffle.HandleDetectionAutoConnect).Methods("GET", "OPTIONS")
|
r.HandleFunc("/api/v1/detections/{detectionType}/connect", shuffle.HandleDetectionAutoConnect).Methods("GET", "OPTIONS")
|
||||||
r.HandleFunc("/api/v1/detections/{detection_type}", shuffle.HandleGetDetectionRules).Methods("GET", "OPTIONS")
|
r.HandleFunc("/api/v1/detections/{detection_type}", shuffle.HandleGetDetectionRules).Methods("GET", "OPTIONS")
|
||||||
r.HandleFunc("/api/v1/detections/{triggerId}/selected_rules", shuffle.HandleGetSelectedRules).Methods("GET","OPTIONS")
|
r.HandleFunc("/api/v1/detections/{triggerId}/selected_rules", shuffle.HandleGetSelectedRules).Methods("GET", "OPTIONS")
|
||||||
r.HandleFunc("/api/v1/detections/{triggerId}/selected_rules/save", shuffle.HandleSaveSelectedRules).Methods("POST","OPTIONS")
|
r.HandleFunc("/api/v1/detections/{triggerId}/selected_rules/save", shuffle.HandleSaveSelectedRules).Methods("POST", "OPTIONS")
|
||||||
r.HandleFunc("/api/v1/detections/{action}", shuffle.HandleFolderToggle).Methods("PUT", "OPTIONS")
|
r.HandleFunc("/api/v1/detections/{action}", shuffle.HandleFolderToggle).Methods("PUT", "OPTIONS")
|
||||||
|
|
||||||
// This is weird.
|
// This is weird.
|
||||||
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")
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
+614
-203
@@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user