@@ -1273,10 +1362,10 @@ const AppAuthTab = memo((props) => {
{
- setDeleteConfirmTarget(data);
- setDeleteConfirmOpen(true);
+ deleteAuthentication(data);
}}
>
@@ -1311,27 +1400,21 @@ const AppAuthTab = memo((props) => {
color="secondary"
onClick={() => {
setShowDistributionPopup(true)
- let initialSelected = [];
- if (data?.suborg_distributed) {
- const allSuborg = userdata?.orgs?.map((d) => {
- if (d.creator_org !== userdata.active_org.id) return null;
- return d.id;
- });
- initialSelected = allSuborg.filter((d) => d !== null);
- } else if (data?.suborg_distribution?.length > 0) {
- initialSelected = data.suborg_distribution;
+ if(data?.suborg_distribution?.length > 0){
+ setSelectedSubOrg(data.suborg_distribution)
+ }else{
+ setSelectedSubOrg([])
+ }
+ setSelectedAuthId(data.id)
+ if (data?.suborg_distributed) {
+ const allSuborg = userdata?.orgs?.map((data, index) => {
+ if (data.creator_org !== userdata.active_org.id) {
+ return null;
+ }
+ return data.id;
+ })
+ setSelectedSubOrg(allSuborg.filter((data) => data !== null))
}
- setSelectedSubOrg(initialSelected);
- setSelectedAuthId(data.id);
- setSelectedAuthName(data?.app?.name);
- const suborgs = (userdata?.orgs || []).filter(o => o.creator_org === userdata?.active_org?.id);
- const sorted = [...suborgs].sort((a, b) => {
- const aS = initialSelected.includes(a.id);
- const bS = initialSelected.includes(b.id);
- if (aS !== bS) return bS - aS;
- return a.name.localeCompare(b.name);
- });
- setDistribOrgOrder(sorted.map(o => o.id));
}}
/>
diff --git a/frontend/src/components/AppCreationModal.jsx b/frontend/src/components/AppCreationModal.jsx
index e1e62e28..6a66f0e0 100644
--- a/frontend/src/components/AppCreationModal.jsx
+++ b/frontend/src/components/AppCreationModal.jsx
@@ -708,7 +708,7 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud, startOpenA
fontWeight: 500,
fontFamily: theme?.typography?.fontFamily,
}}>
- Generate an app based on documentation
+ Generate an app based on documentation (beta)
{
diff --git a/frontend/src/components/AppGrid.jsx b/frontend/src/components/AppGrid.jsx
index 68b4f7db..cd37fb23 100644
--- a/frontend/src/components/AppGrid.jsx
+++ b/frontend/src/components/AppGrid.jsx
@@ -1,9 +1,10 @@
-import React, { useEffect, useState, useRef, useMemo } from "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";
@@ -26,7 +27,7 @@ import {
InstantSearch,
Configure,
connectSearchBox,
- connectInfiniteHits,
+ connectHits,
connectHitInsights,
RefinementList,
ClearRefinements,
@@ -38,7 +39,6 @@ import aa from "search-insights";
import { useLocation } from 'react-router-dom';
import "./FilterCSS.css";
-import SearchContactForm from "../components/SearchContactForm.jsx";
import {
Zoom,
@@ -54,7 +54,7 @@ import {
const searchClient = algoliasearch(
"JNSS5CFDZZ",
- "eb5fd80aa6ed5ab4730d836cff3ea283"
+ "c8f882473ff42d41158430be09ec2b4e"
);
//const searchClient = algoliasearch("L55H18ZINA", "a19be455e7e75ee8f20a93d26b9fc6d6")
@@ -77,13 +77,62 @@ const AppGrid = (props) => {
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 = "";
@@ -99,11 +148,10 @@ const AppGrid = (props) => {
const params = Object.fromEntries(urlSearchParams.entries());
const foundQuery = params["q"];
if (foundQuery !== null && foundQuery !== undefined) {
+ console.log("Got query: ", foundQuery);
refine(foundQuery);
defaultSearch = foundQuery;
- if (searchQuery !== foundQuery) {
- setSearchQuery(foundQuery);
- }
+ searchQuery = foundQuery
}
}
//}, [])
@@ -186,13 +234,7 @@ const AppGrid = (props) => {
onChange={(event) => {
const value = event.currentTarget.value;
setSearchQuery(value);
- const urlSearchParams = new URLSearchParams(window.location.search);
- if (value) {
- urlSearchParams.set("q", value);
- } else {
- urlSearchParams.delete("q");
- }
- window.history.replaceState({}, '', `${window.location.pathname}?${urlSearchParams.toString()}`);
+ removeQuery("q");
debouncedRefine(value);
}}
onKeyDown={(event) => {
@@ -210,21 +252,6 @@ const AppGrid = (props) => {
const [currTab, setCurrTab] = useState(0);
const location = useLocation();
- const conditionalSearchClient = useMemo(() => ({
- ...searchClient,
- search(requests) {
- if (currTab !== 0) {
- return Promise.resolve({
- results: requests.map(() => ({
- hits: [], nbHits: 0, page: 0, nbPages: 0, hitsPerPage: 0,
- processingTimeMS: 0, exhaustiveNbHits: true, query: "", params: "",
- })),
- });
- }
- return searchClient.search(requests);
- },
- }), [currTab]);
-
useEffect(() => {
const queryParams = new URLSearchParams(location.search);
const tabParam = queryParams.get('tab');
@@ -242,6 +269,7 @@ const AppGrid = (props) => {
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()}`);
};
@@ -253,8 +281,6 @@ const AppGrid = (props) => {
// Component to fetch all public app from the algolia.
const Hits = ({
hits,
- hasMore,
- refineNext,
insights,
setIsAnyAppActivated,
searchQuery
@@ -262,32 +288,6 @@ const AppGrid = (props) => {
const [mouseHoverIndex, setMouseHoverIndex] = useState(-1);
var counted = 0;
const [hoverEffect, setHoverEffect] = useState(-1);
- const [isLoadingMore, setIsLoadingMore] = useState(false);
- const loadMoreRef = useRef(null);
- const scrollContainerRef = useRef(null);
- const isFetchingMore = useRef(false);
-
- useEffect(() => {
- isFetchingMore.current = false;
- setIsLoadingMore(false);
- }, [hits.length]);
-
- useEffect(() => {
- return; // infinite scroll disabled
- if (!loadMoreRef.current || !scrollContainerRef.current) return;
- const observer = new IntersectionObserver(
- (entries) => {
- if (entries[0].isIntersecting && hasMore && !isFetchingMore.current) {
- isFetchingMore.current = true;
- setIsLoadingMore(true);
- refineNext();
- }
- },
- { root: scrollContainerRef.current, rootMargin: "200px" }
- );
- observer.observe(loadMoreRef.current);
- return () => observer.disconnect();
- }, [hasMore, refineNext]);
const normalizedString = (name) => {
if (typeof name === 'string') {
@@ -359,11 +359,11 @@ const AppGrid = (props) => {
} else {
//toast.success(`App ${type}d Successfully!`);
if (type === 'activate') {
- setAllActivatedAppIds(prev => [...(prev || []), data.objectID]);
+ setAllActivatedAppIds(prev => [...prev, data.objectID]);
setIsAnyAppActivated(true);
}
if (type === 'deactivate') {
- const updatedIds = (allActivatedAppIds || []).filter(id => id !== data.objectID);
+ const updatedIds = allActivatedAppIds.filter(id => id !== data.objectID);
setAllActivatedAppIds(updatedIds);
}
}
@@ -373,16 +373,6 @@ const AppGrid = (props) => {
});
}
- const sortedHits = useMemo(() => {
- const list = [...(hits || [])];
- if (!allActivatedAppIds?.length) return list;
- return list.sort((a, b) => {
- const aActive = allActivatedAppIds.includes(a.objectID) ? 1 : 0;
- const bActive = allActivatedAppIds.includes(b.objectID) ? 1 : 0;
- return bActive - aActive;
- });
- }, [hits, hits?.length, allActivatedAppIds]);
-
let workflowDelay = 0;
const isHeader = true;
const paperStyle = {
@@ -414,7 +404,6 @@ const AppGrid = (props) => {
) : (
{
scrollbarColor: "#494949 #2f2f2f",
}}
>
- {sortedHits.map((data, index) => {
+ {hits?.map((data, index) => {
const appUrl =
isCloud === true ?
`/apps/${data.objectID}`
@@ -640,12 +629,6 @@ const AppGrid = (props) => {
);
})
}
-
- {isLoadingMore && (
-
-
-
- )}
)}
@@ -943,7 +926,7 @@ const AppGrid = (props) => {
//Component to display all apps.
const AllApps = ({ setIsAnyAppActivated }) => {
- var [searchQuery, setSearchQuery] = useState(() => new URLSearchParams(window.location.search).get('q') || "");
+ var [searchQuery, setSearchQuery] = useState("");
return (
{
}}
onClick={() => {
setSearchQuery('');
- removeQuery("q");
}}
/>
)}
@@ -1033,15 +1015,7 @@ const AppGrid = (props) => {
placeholder="Search your Activated or self-built apps"
id="shuffle_search_field"
onChange={(event) => {
- const value = event.currentTarget.value;
- setSearchQuery(value);
- const urlSearchParams = new URLSearchParams(window.location.search);
- if (value) {
- urlSearchParams.set("q", value);
- } else {
- urlSearchParams.delete("q");
- }
- window.history.replaceState({}, '', `${window.location.pathname}?${urlSearchParams.toString()}`);
+ setSearchQuery(event.currentTarget.value);
}}
onKeyDown={(event) => {
if(event.key === "Enter") {
@@ -1618,7 +1592,7 @@ const AppGrid = (props) => {
//Component to fetch all apps created by user and Org
const UserAndOrgApps = ({ selectedCategoryForUsersAndOgsApps, selectedTagsForUserAndOrgApps, selectedOptionOfCreatedWith, setselectedCategoryForUsersAndOgsApps, setSelectedTagsForUserAndOrgApps, setSelectedOptionOfCreatedWith }) => {
- const [searchQuery, setSearchQuery] = useState(() => new URLSearchParams(window.location.search).get('q') || "");
+ const [searchQuery, setSearchQuery] = useState("");
const [appsToShow, setAppsToShow] = useState([]);
useEffect(() => {
if (currTab === 1) {
@@ -2029,7 +2003,7 @@ const AppGrid = (props) => {
};
const CustomSearchBox = connectSearchBox(SearchBox);
- const CustomHits = connectInfiniteHits(Hits);
+ const CustomHits = connectHits(Hits);
const DisplayAllAppsTab = () => {
const [selectedCategoryForUsersAndOgsApps, setselectedCategoryForUsersAndOgsApps] = useState([]);
@@ -2038,7 +2012,7 @@ const AppGrid = (props) => {
return (
-
+
{currTab === 0 ? (
@@ -2063,7 +2037,7 @@ const AppGrid = (props) => {
/>
- {currTab === 0 && }
+
);
@@ -2086,7 +2060,80 @@ const AppGrid = (props) => {
>
{showSuggestion === true ? (
-
+
+
+ Can't find what you're looking for?
+
+
+ setFormMail(e.target.value)}
+ />
+ setMessage(e.target.value)}
+ />
+
+
+
+ {formMessage}
+
+
) : null}
diff --git a/frontend/src/components/AppModal.jsx b/frontend/src/components/AppModal.jsx
index b0f8728f..38c8581f 100644
--- a/frontend/src/components/AppModal.jsx
+++ b/frontend/src/components/AppModal.jsx
@@ -35,7 +35,7 @@ import { Context } from '../context/ContextApi.jsx';
const searchClient = algoliasearch(
"JNSS5CFDZZ",
- "33e4e3564f4f060e96e0531957bed552"
+ "c8f882473ff42d41158430be09ec2b4e"
);;
const AppModal = ({ open, onClose, app, globalUrl, getApps}) => {
diff --git a/frontend/src/components/AppSearch1.jsx b/frontend/src/components/AppSearch1.jsx
index d551841d..449ad925 100644
--- a/frontend/src/components/AppSearch1.jsx
+++ b/frontend/src/components/AppSearch1.jsx
@@ -13,7 +13,7 @@ import {
InputAdornment,
Typography,
} from '@mui/material';
-const searchClient = algoliasearch("JNSS5CFDZZ", "33e4e3564f4f060e96e0531957bed552")
+const searchClient = algoliasearch("JNSS5CFDZZ", "c8f882473ff42d41158430be09ec2b4e")
const Appsearch = props => {
const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, newSelectedApp, setNewSelectedApp, defaultSearch, showSearch, ConfiguredHits, userdata, cy, isCreatorPage, actionImageList, setActionImageList, setUserSpecialzedApp, placeholder,
diff --git a/frontend/src/components/Appsearch.jsx b/frontend/src/components/Appsearch.jsx
index 5b529d9d..904cf7c2 100644
--- a/frontend/src/components/Appsearch.jsx
+++ b/frontend/src/components/Appsearch.jsx
@@ -20,7 +20,7 @@ import {
} from '@mui/material';
import aa from 'search-insights'
-const searchClient = algoliasearch("JNSS5CFDZZ", "33e4e3564f4f060e96e0531957bed552")
+const searchClient = algoliasearch("JNSS5CFDZZ", "c8f882473ff42d41158430be09ec2b4e")
const Appsearch = props => {
const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, newSelectedApp, setNewSelectedApp, defaultSearch, showSearch, ConfiguredHits, userdata, cy, isCreatorPage, actionImageList, setActionImageList, setUserSpecialzedApp, inputHeight, apps, } = props
const { themeMode } = useContext(Context)
diff --git a/frontend/src/components/BillingStats.jsx b/frontend/src/components/BillingStats.jsx
index 05796cbd..d2d2e304 100644
--- a/frontend/src/components/BillingStats.jsx
+++ b/frontend/src/components/BillingStats.jsx
@@ -92,11 +92,6 @@ const AppStats = (defaultprops) => {
const statKey = syncStats === true ? "onprem_stats" : "daily_statistics"
const dailyStats = inputdata[statKey]
if (dailyStats === undefined || dailyStats === null) {
- setAppruns(undefined)
- setWorkflowRuns(undefined)
- setSubflowRuns(undefined)
- setChildOrgsAppRuns(undefined)
- setApprunCosts(undefined)
return
}
@@ -383,17 +378,6 @@ const AppStats = (defaultprops) => {
return
}
- if (syncStats && (statistics[statKey] === undefined || statistics[statKey] === null)) {
- setOnpremAppRuns(0)
- setFilteredStatistics(statistics)
- setAppruns(undefined)
- setWorkflowRuns(undefined)
- setSubflowRuns(undefined)
- setChildOrgsAppRuns(undefined)
- setApprunCosts(undefined)
- return
- }
-
// Calculate month to date cost
var mtd_cost = 0
for (let key in statistics[statKey]) {
diff --git a/frontend/src/components/CacheView.jsx b/frontend/src/components/CacheView.jsx
index 4c9fd46c..ef5719e7 100644
--- a/frontend/src/components/CacheView.jsx
+++ b/frontend/src/components/CacheView.jsx
@@ -1,8 +1,6 @@
import React, { useState, useEffect, useContext, memo } from "react";
import { makeStyles } from "@mui/styles";
import { getTheme } from "../theme.jsx";
-import SubOrgDistributionDialog from "./SubOrgDistributionDialog.jsx";
-import DeleteConfirmDialog from "./DeleteConfirmDialog.jsx";
import { toast } from 'react-toastify';
import ReactJson from "react-json-view-ssr";
@@ -19,6 +17,8 @@ import {
Button,
Tabs,
Tab,
+ List,
+ ListItem,
ListItemText,
IconButton,
Dialog,
@@ -82,7 +82,6 @@ import {
Hub as HubIcon,
Key as KeyIcon,
FlashOn as FlashOnIcon,
- Search as SearchIcon,
} from "@mui/icons-material";
import { Context } from "../context/ContextApi.jsx";
@@ -128,9 +127,6 @@ const CacheView = memo((props) => {
const [showDistributionPopup, setShowDistributionPopup] = useState(false);
const [selectedSubOrg, setSelectedSubOrg] = useState([]);
const [selectedCacheKey, setSelectedCacheKey] = useState("");
- const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false);
- const [deleteConfirmTarget, setDeleteConfirmTarget] = useState(null);
- const [distribOrgOrder, setDistribOrgOrder] = useState([]);
const [totalAmount, setTotalAmount] = useState(0);
const [page, setPage] = useState(0);
const [pageSize, setPageSize] = useState(50)
@@ -237,7 +233,7 @@ const CacheView = memo((props) => {
{
"name": "Enrich",
- "description": "Enriches the data. Uses regex keys and runs a workflow in the background. Added to the 'enrichments' key.",
+ "description": "Enriches the data. Only runs on valid JSON data AND if the 'enrichment' field does not exist.",
"type": "singul",
"options": [{
"key": "",
@@ -335,14 +331,6 @@ const CacheView = memo((props) => {
// In order to make linking weird urls from workflow page work.
if (urlParams.get("src") == "workflow") {
- if (categoryParam === "OCSF") {
- const newParam = "shuffle-security incidents"
-
- urlParams.set("category", newParam)
- window.history.replaceState({}, '', `${window.location.pathname}?${urlParams.toString()}`)
- categoryParam = newParam
- }
-
if (categoryParam?.toLowerCase().startsWith("list")) {
const newParam = categoryParam.substring(5).replaceAll("%20", "_")
@@ -582,26 +570,17 @@ const CacheView = memo((props) => {
.then((response) => {
if (response.status === 200) {
if (refreshList === undefined || refreshList === null || refreshList === true) {
+
toast.success("Deleted datastore entry");
setTimeout(() => {
listOrgCache(orgId, selectedCategory, 0, pageSize, page)
}, 1000);
}
} else {
- if (refreshList === undefined || refreshList === null || refreshList === true) {
- setTimeout(() => {
- listOrgCache(orgId, selectedCategory, 0, pageSize, page)
- }, 1000);
- }
toast.error(`Failed deleting entry ${key} in category ${itemCategory || selectedCategory}. If this persists, please contact support@shuffler.io.`)
}
})
.catch((error) => {
- if (refreshList === undefined || refreshList === null || refreshList === true) {
- setTimeout(() => {
- listOrgCache(orgId, selectedCategory, 0, pageSize, page)
- }, 1000);
- }
toast(error.toString());
});
};
@@ -852,11 +831,6 @@ const CacheView = memo((props) => {
Category: {dataValue.category}
: null}
- {dataValue?.enrichments !== undefined && dataValue?.enrichments !== null && dataValue.enrichments.length > 0 ?
-
- Enrichments: {dataValue.enrichments.length}
-
- : null}
{dataValue?.tags !== undefined && dataValue?.tags !== null && dataValue?.tags?.length > 0 ?
@@ -926,6 +900,33 @@ const CacheView = memo((props) => {
);
+ const handleSelectSubOrg = (id, action) => {
+ if (action === "all") {
+ const childOrgs = userdata.orgs.filter(
+ (data) => data.creator_org === userdata.active_org.id
+ );
+ setSelectedSubOrg((prev) => {
+ if (prev.length === childOrgs.length) {
+ // If all child orgs are already selected, clear the selection
+ return [];
+ } else {
+ // Otherwise, select all child org IDs
+ return childOrgs.map((data) => data.id);
+ }
+ });
+ } else if (action === "none") {
+ setSelectedSubOrg([]);
+ } else {
+ setSelectedSubOrg((prev) => {
+ if (prev.includes(id)) {
+ return prev.filter((data) => data !== id);
+ } else {
+ return [...prev, id];
+ }
+ });
+ }
+ };
+
const changeDistribution = (id, selectedSubOrg) => {
editFileConfig(id, [...new Set(selectedSubOrg)], selectedCategory)
@@ -939,6 +940,8 @@ const CacheView = memo((props) => {
selected_suborgs: selectedSubOrg,
category: category === undefined || category === "" || category === "default" ? "" : category,
}
+
+ console.log("data: ", data);
const url = `${globalUrl}/api/v1/orgs/${orgId}/cache/config`;
@@ -972,41 +975,98 @@ const CacheView = memo((props) => {
};
+ const cacheDistributionModal = showDistributionPopup ? (
+
+ ) : null;
const saveAutomation = (allAutomation, settings) => {
// Check if icon is a string. Otherwise make it empty.
@@ -1699,10 +1759,6 @@ const CacheView = memo((props) => {
enableClipboard={(copy) => {
handleReactJsonClipboard(copy)
}}
- onSelect={(select) => {
- //currentParams.set("category", selectedCategory);
- //HandleJsonCopy(validate.result, select, "exec");
- }}
collapseStringsAfterLength={theme.palette.jsonCollapseStringsAfterLength}
iconStyle={theme.palette.jsonIconStyle}
displayDataTypes={false}
@@ -1879,7 +1935,6 @@ const CacheView = memo((props) => {
"workflow_id": data.workflow_id,
"category": data.category,
"tags": data.tags,
- "enrichments": data.enrichments,
})
setValue(newvalue)
setModalOpen(true)
@@ -1964,8 +2019,7 @@ const CacheView = memo((props) => {
onClick={(e) => {
e.preventDefault()
e.stopPropagation()
- setDeleteConfirmTarget({ key: data.key, category: data.category })
- setDeleteConfirmOpen(true)
+ deleteEntry(orgId, data.key, data.category)
}}
>