@@ -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/Billing.jsx b/frontend/src/components/Billing.jsx
index 959ce875..0a4a03e4 100644
--- a/frontend/src/components/Billing.jsx
+++ b/frontend/src/components/Billing.jsx
@@ -52,7 +52,6 @@ import {
Cancel as CancelIcon,
Shield as ShieldIcon,
Cancel as XCircleIcon,
- LockOutlined as LockIcon,
FlashOn as ZapIcon,
People as UsersIcon,
FmdGoodOutlined as FmdGoodOutlinedIcon,
@@ -204,7 +203,7 @@ const ProductionStatus = ({ selectedOrganization, userdata, isCloud, theme }) =>
>
{isProdStatusOn
? 'Your organization has full access to all enterprise features and capabilities.'
- : 'Your organization is running on the open-source plan. Upgrade to Enterprise to remove limits and unlock advanced capabilities.'}
+ : 'View your current limits and available features. Upgrade to unlock enterprise capabilities.'}
{/* Features Grid */}
@@ -224,8 +223,8 @@ const ProductionStatus = ({ selectedOrganization, userdata, isCloud, theme }) =>
})
.map((feature, index) => {
const Icon = feature.icon;
- const isAvailable = isProdStatusOn;
- const statusColor = isAvailable ? colors.success : colors.warning;
+ const isAvailable = isProdStatusOn && feature.isActive;
+ const statusColor = isAvailable ? colors.success : colors.disabled;
const bgColor = isAvailable ? themeMode === "dark" ? "#212121" : "#ffffff" : colors.disabledBg;
return (
@@ -236,34 +235,19 @@ const ProductionStatus = ({ selectedOrganization, userdata, isCloud, theme }) =>
alignItems: 'center',
gap: 14,
padding: '14px 16px',
- paddingLeft: !isAvailable ? 20 : 16,
borderRadius: 10,
background: bgColor,
border: `1px solid ${isAvailable ? colors.success + '40' : colors.border}`,
transition: 'all 0.2s',
- position: 'relative',
- overflow: 'hidden',
}}
>
- {!isAvailable && (
-
- )}
-
{/* Icon */}
fontSize: 15,
fontWeight: 600,
color: colors.textPrimary,
- marginBottom: 3,
+ marginBottom: 4,
}}
>
{feature.label}
@@ -288,7 +272,7 @@ const ProductionStatus = ({ selectedOrganization, userdata, isCloud, theme }) =>
@@ -301,8 +285,8 @@ const ProductionStatus = ({ selectedOrganization, userdata, isCloud, theme }) =>
style={{ color: colors.success, fontSize: 20, flexShrink: 0 }}
/>
) : (
-
)}
@@ -320,323 +304,80 @@ const ProductionStatus = ({ selectedOrganization, userdata, isCloud, theme }) =>
/>
{!isProdStatusOn && (
-
- {/* Header */}
-
-
-
-
-
-
- Unlock Shuffle Enterprise
-
-
- Scale your security operations without limits
-
-
-
-
- {/* Body */}
-
-
- {[
- { icon: ZapIcon, text: 'Higher App Run Limits' },
- { icon: UsersIcon, text: 'Multi-Tenant Support' },
- { icon: ShieldIcon, text: 'Enterprise SLA' },
- { icon: FmdGoodOutlinedIcon, text: 'Multi-Location Deploy' },
- ].map((item, i) => {
- const ItemIcon = item.icon;
- return (
-
-
-
- {item.text}
-
-
- );
- })}
-
-
-
- Purpose-built for security teams that need scalability, high availability, and dedicated
- expert support to run mission-critical workflows in production.{' '}
-
- Learn more
-
+ background: themeMode == "dark" ? "#212121" : "#ffffff",
+ border: `1px solid ${colors.border}`,
+ marginBottom: 20,
+ }}
+ >
+
+
+
+
+ About Shuffle Enterprise
-
-
- }
- >
- Upgrade Now
-
- }
+
- Talk to Sales
-
-
+ Shuffle Enterprise is designed for organizations that require scalability, high
+ availability, dedicated support, and robust infrastructure to run mission-critical
+ workflows in production environments.
learn more
+
+ )}
+
+ {/* CTA Section */}
+ {!isProdStatusOn && (
+
+
+
+ Ready to upgrade?
+
+
+ Contact our team to learn more about enterprise features and pricing.
+
+
+
}
+ endIcon={
}
+ >
+ Contact Sales
+
+
)}
);
};
-const AppRunsQueueCard = memo(({ environment, isAirGapped, isCloudSynching, totalRuns, limit, theme, navigate }) => {
-
- const usagePct = limit > 0 ? (totalRuns / limit) * 100 : 0;
- const hardPauseLimit = limit * 2;
- const hardPausePct = hardPauseLimit > 0 ? Math.min((totalRuns / hardPauseLimit) * 100, 100) : 0;
- const mainBarPct = Math.min(usagePct, 100);
-
- const queueSize = environment?.queue !== undefined && environment?.queue !== null
- ? Math.max(0, environment.queue)
- : 0;
-
- const isThrottled = isCloudSynching ? false : (isAirGapped ? hardPausePct >= 100 : usagePct >= 100);
-
- let status, statusColor, statusBg;
- if (isThrottled) {
- status = 'Throttled';
- statusColor = '#ef4444';
- statusBg = 'rgba(239, 68, 68, 0.12)';
- } else if (!isCloudSynching && usagePct >= 80) {
- status = 'Warning';
- statusColor = '#f59e0b';
- statusBg = 'rgba(245, 158, 11, 0.12)';
- } else {
- status = 'Healthy';
- statusColor = theme.palette.green;
- statusBg = `${theme.palette.green}1f`;
- }
-
- const throttleRate = isThrottled ? '1/min' : '\u2014';
- const estClearTime = isThrottled && queueSize > 0 ? `${queueSize} min` : '\u2014';
- const mainBarColor = isThrottled ? '#ef4444' : usagePct >= 80 && !isCloudSynching ? '#f59e0b' : theme.palette.green;
-
- const envTypeLabel = environment?.run_type === 'cloud' ? 'Cloud' : 'On-prem';
- const envName = environment?.Name || environment?.name || 'Default';
-
- const borderColor = theme.palette.slateGrayColor;
- const trackBg = theme.palette.slateGrayColor;
- const mutedText = theme.palette.text.secondary;
-
- return (
-
- {/* Title row */}
-
-
- {envTypeLabel} - {envName} · App runs / month
-
-
-
- {status}
-
-
-
- {/* Main number */}
-
-
- {totalRuns.toLocaleString()}
-
-
- / {limit.toLocaleString()}
-
-
-
- {isAirGapped && !isCloudSynching && (
-
- No throttle until {hardPauseLimit.toLocaleString()} runs · 2× your plan limit
-
- )}
-
- {/* Main usage bar */}
-
-
- {/* 80% threshold marker */}
-
-
-
- 0
- 80% threshold
- {limit.toLocaleString()}
-
-
- {/* Throttle limit row */}
- {isAirGapped && (
- <>
-
-
- Burst throttle threshold (2× limit) · workflows throttle to 1/min above this
-
-
- {totalRuns.toLocaleString()} / {hardPauseLimit.toLocaleString()}
-
-
-
-
- >
- )}
-
- {/* Alert box for Warning / Throttled */}
- {status !== 'Healthy' && (
-
-
- {isThrottled ? 'Running slow \u2014 workflows are still running' : 'Approaching your monthly limit'}
-
-
- {isThrottled
- ? isAirGapped
- ? `You've exceeded the burst threshold of ${hardPauseLimit.toLocaleString()} runs (2\u00d7 your plan limit). Your workflows are still running \u2014 there is no hard stop. Executions slow to 1 per minute until next month.`
- : `You've exceeded your ${limit.toLocaleString()} monthly limit. Your instance keeps running \u2014 executions slow to 1 per minute until next month. Nothing is lost. You can view or clear the queue from the Locations tab.`
- : isAirGapped
- ? `You've used ${totalRuns.toLocaleString()} of ${limit.toLocaleString()} app runs. Workflows run normally \u2014 slowdown only begins at ${hardPauseLimit.toLocaleString()} runs (2\u00d7 your plan limit). No action needed.`
- : `You've used ${totalRuns.toLocaleString()} of ${limit.toLocaleString()} app runs (${Math.max(0, limit - totalRuns).toLocaleString()} remaining). If you reach 100%, executions continue at a reduced rate of 1 per minute, nothing stops or is lost.`
- }
-
-
-
- )}
-
- {/* Stats row */}
-
- {[
- { label: 'Queued jobs', value: queueSize },
- { label: 'Throttle rate', value: throttleRate },
- { label: 'Est. clear time', value: estClearTime },
- ].map((stat, i) => (
-
-
- {stat.label}
-
-
- {stat.value}
-
-
- ))}
-
-
- );
-});
-
const Billing = memo((props) => {
const { globalUrl, userdata, serverside, billingInfo, stripeKey,isLoaded, selectedOrganization, handleGetOrg, clickedFromOrgTab, removeCookie} = props;
//const alert = useAlert();
@@ -672,7 +413,7 @@ const Billing = memo((props) => {
const [statistics, setStatistics] = useState([])
const [monthlyAppRunsParent, setMonthlyAppRunsParent] = useState(0)
const [monthlyAllSuborgExecutions, setMonthlyAllSuborgExecutions] = useState(0)
- const [billingEnvironments, setBillingEnvironments] = useState([])
+
useEffect(() => {
if (monthlyAppRunsParent > 0 || monthlyAllSuborgExecutions > 0) {
const percentage = ((monthlyAppRunsParent + monthlyAllSuborgExecutions) / userdata.app_execution_limit) * 100;
@@ -784,29 +525,6 @@ const Billing = memo((props) => {
}, [])
- const getBillingEnvironments = () => {
- fetch(globalUrl + "/api/v1/getenvironments", {
- method: "GET",
- headers: {
- "Content-Type": "application/json",
- Accept: "application/json",
- },
- credentials: "include",
- })
- .then((response) => {
- if (response.status !== 200) return;
- return response.json();
- })
- .then((responseJson) => {
- if (responseJson && Array.isArray(responseJson)) {
- setBillingEnvironments(responseJson);
- }
- })
- .catch((error) => {
- console.log("Error fetching environments for billing:", error);
- });
- };
-
const getStats = (orgid) => {
if (orgid === undefined || orgid === null) {
@@ -845,9 +563,6 @@ const Billing = memo((props) => {
useEffect(() => {
if (selectedOrganization && selectedOrganization?.id?.length > 0) {
getStats(selectedOrganization.id);
- if (!isCloud) {
- getBillingEnvironments();
- }
}
}, [selectedOrganization]);
@@ -2672,17 +2387,6 @@ const Billing = memo((props) => {
const isChildOrg = userdata?.active_org?.creator_org !== "" && userdata?.active_org?.creator_org !== undefined && userdata?.active_org?.creator_org !== null
- const activeQueueEnvs = Array.isArray(billingEnvironments) ? billingEnvironments.filter(env => env != null && !env.archived && env.Type !== 'cloud') : [];
- const totalQueueSize = activeQueueEnvs.reduce((sum, env) => sum + Math.max(0, env?.queue || 0), 0);
- const aggregatedQueueEnv = {
- Name: `${activeQueueEnvs.length} Runtime Location${activeQueueEnvs.length !== 1 ? 's' : ''}`,
- run_type: 'on-prem',
- queue: totalQueueSize,
- };
- const appExecLimit = selectedOrganization?.sync_features?.app_executions?.limit ?? 0;
- const isAirGapped = selectedOrganization != null && (selectedOrganization.cloud_sync_active === true || selectedOrganization.cloud_sync === true) ? false : appExecLimit < 300000 ? false : true;
- const isCloudSynching = selectedOrganization != null && selectedOrganization.cloud_sync === true && appExecLimit >= 300000;
-
useEffect(() => {
if (isChildOrg && currentTab === 0) {
setCurrentTab(1);
@@ -3096,28 +2800,6 @@ const Billing = memo((props) => {
) : null*/}
-
- {/* Queue Management */}
- {!isCloud && activeQueueEnvs.length > 0 && !isChildOrg && (
-
-
- Queue Management
-
-
- Real-time status of your app run usage and workflow queue across all runtime locations.
-
-
-
- )}
-
{!isChildOrg && isCloud && (
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)
}}
>