import React, { useState, useEffect, useContext, memo } from "react"; import { toast } from "react-toastify"; import theme from "../theme.jsx"; import { v4 as uuidv4, v5 as uuidv5, validate as isUUID, } from "uuid"; import { Paper, Tooltip, Typography, Divider, Button, ButtonGroup, Grid, Card, Chip, Switch, Autocomplete, TextField, MenuItem, IconButton, } from "@mui/material"; import { OpenInNew as OpenInNewIcon, } from "@mui/icons-material"; import { makeStyles } from "@mui/styles"; import { Context } from "../context/ContextApi.jsx"; import { useNavigate, Link } from "react-router-dom"; import Priority from "../components/Priority.jsx"; import { constrainMatrix } from "reaviz"; //import { useAlert const useStyles = makeStyles({ notchedOutline: { borderColor: "#f85a3e !important", }, }); const Priorities = memo((props) => { const { globalUrl, userdata,clickedFromOrgTab,selectedOrganization, handleEditOrg, serverside, billingInfo, stripeKey, checkLogin, setAdminTab, setCurTab, notifications, setNotifications, } = props; const [showDismissed, setShowDismissed] = React.useState(false); const [showRead, setShowRead] = React.useState(false); const [appFramework, setAppFramework] = React.useState({}); const [selectedWorkflow, setSelectedWorkflow] = React.useState("NO HIGHLIGHT"); const [selectedExecutionId, setSelectedExecutionId] = React.useState("NO HIGHLIGHT"); const [highlightKMS, setHighlightKMS] = React.useState(false) const [workflows, setWorkflows] = React.useState([]) const [openNotification, setOpenNotification] = React.useState(false); const [workflow, setWorkflow] = React.useState({}) const [notificationWorkflow, setNotificationWorkflow] = React.useState( selectedOrganization.defaults === undefined ? "" : selectedOrganization.defaults.notification_workflow === undefined || selectedOrganization.defaults.notification_workflow.length === 0 ? "" : selectedOrganization.defaults.notification_workflow ); let navigate = useNavigate(); const classes = useStyles(); useEffect(() => { getFramework() // Check "workflow" and "execution_id" in URL const urlParams = new URLSearchParams(window.location.search) const workflow = urlParams.get("workflow") const execution_id = urlParams.get("execution_id") const kms = urlParams.get("kms") if (kms !== null && kms !== undefined && kms.length > 0 && kms === "true") { toast.info("KMS-related notifications are highlighted.") setHighlightKMS(true) } if (execution_id !== null) { setSelectedExecutionId(execution_id) //toast.info("Execution-related notifications are highlighted.") } if (workflow !== null) { setSelectedWorkflow(workflow) toast.info("Workflow-related notifications are highlighted.") } }, []) useEffect(() => { if (selectedOrganization === undefined || selectedOrganization === null || selectedOrganization?.id === undefined || selectedOrganization?.id === null || selectedOrganization?.id.length === 0) { return } if(workflows?.length === 0) { getAvailableWorkflows() } if (notificationWorkflow !== selectedOrganization?.defaults?.notification_workflow) { setNotificationWorkflow(selectedOrganization?.defaults?.notification_workflow) } }, [selectedOrganization]) if (userdata === undefined || userdata === null) { return } const getFramework = () => { fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", { method: "GET", headers: { "Content-Type": "application/json", Accept: "application/json", }, credentials: "include", }) .then((response) => { if (response.status !== 200) { console.log("Status not 200 for framework!"); } return response.json(); }) .then((responseJson) => { if (responseJson.success === false) { setAppFramework({}) if (responseJson.reason !== undefined) { //toast("Failed loading: " + responseJson.reason) } else { //toast("Failed to load framework for your org.") } } else { setAppFramework(responseJson) } }) .catch((error) => { console.log("err in framework: ", error.toString()); }) } const clearNotifications = () => { // Don't really care about the logout toast("Clearing notifications") fetch(`${globalUrl}/api/v1/notifications/clear`, { credentials: "include", method: "GET", headers: { "Content-Type": "application/json", }, }) .then(function (response) { if (response.status !== 200) { console.log("Error in response"); } return response.json(); }) .then(function (responseJson) { if (responseJson.success === true) { // Reload the UI const newNotifications = notifications.map((notification) => { notification.read = true return notification }) setNotifications(newNotifications) setShowRead(true) } else { toast("Failed dismissing notifications. Please try again later."); } }) .catch((error) => { console.log("error in notification dismissal: ", error); //removeCookie("session_token", {path: "/"}) }); }; const dismissNotification = (alert_id, disabled) => { var notificationurl = `${globalUrl}/api/v1/notifications/${alert_id}/markasread` if (disabled === true) { notificationurl += "?disabled=true" } else if (disabled === false) { notificationurl += "?disabled=false" } fetch(notificationurl , { credentials: "include", method: "GET", headers: { "Content-Type": "application/json", }, }) .then(function (response) { if (response.status !== 200) { console.log("Error in response"); } return response.json(); }) .then(function (responseJson) { if (responseJson.success === true) { // Mark current one as read var newNotifications = notifications.map((notification) => { if (notification.id === alert_id) { notification.read = true } return notification }) if (disabled === true) { toast("Notification disabled, and will not be shown again.") newNotifications = newNotifications.map((notification) => { if (notification.id === alert_id) { notification.ignored = true } return notification }) console.log("NEW NOTIFICATIONS: ", newNotifications); } else if (disabled === false) { toast("Notification re-enabled successfully") newNotifications = newNotifications.map((notification) => { if (notification.id === alert_id) { notification.ignored = false } return notification }) } else { toast("Notification dismissed successfully") } //const newNotifications = notifications.filter( // (data) => data.id !== alert_id //) //console.log("NEW NOTIFICATIONS: ", newNotifications); if (setNotifications !== undefined && newNotifications !== undefined) { setNotifications(newNotifications) } } else { toast("Failed dismissing notification. Please try again later."); } }) .catch((error) => { console.log("error in notification dismissal: ", error); //removeCookie("session_token", {path: "/"}) }) } const notificationWidth = "100%" const imagesize = 22 const boxColor = "#86c142" const getAvailableWorkflows = () => { fetch(globalUrl + "/api/v1/workflows", { method: "GET", headers: { "Content-Type": "application/json", Accept: "application/json", }, credentials: "include", }) .then((response) => { if (response.status !== 200) { console.log("Status not 200 for workflows :O!"); return; } return response.json(); }) .then((responseJson) => { if (responseJson !== undefined) { // Add parent notification workflow if it's a child org // selectedOrganization, if (selectedOrganization.creator_org !== undefined && selectedOrganization.creator_org !== null && selectedOrganization.creator_org.length > 0) { // Add to start of the list responseJson.unshift({ "name": "Parent-Org's Notification Workflow", "id": "parent", }) } setWorkflows(responseJson) if (selectedOrganization.defaults !== undefined && selectedOrganization.defaults.notification_workflow !== undefined) { const workflow = responseJson.find((workflow) => workflow.id === selectedOrganization.defaults.notification_workflow) if (workflow !== undefined && workflow !== null) { setWorkflow(workflow) } } } }) .catch((error) => { console.log("Error getting workflows: " + error); }) } const handleWorkflowSelectionUpdate = (e, isUserinput) => { if (e.target.value === undefined || e.target.value === null || e.target.value.id === undefined) { console.log("Returning as there's no id") return null } setOpenNotification(false) setWorkflow(e.target.value) setNotificationWorkflow(e.target.value.id) handleEditOrg( selectedOrganization?.name, selectedOrganization.description, selectedOrganization.id, selectedOrganization.image, { app_download_repo: selectedOrganization?.defaults?.app_download_repo, app_download_branch: selectedOrganization?.defaults?.app_download_branch, workflow_download_repo: selectedOrganization?.defaults?.workflow_download_repo, workflow_download_branch: selectedOrganization?.defaults?.workflow_download_branch, notification_workflow: e.target.value.id, documentation_reference: selectedOrganization?.defaults?.documentation_reference, workflow_upload_repo: selectedOrganization?.defaults?.workflow_upload_repo, workflow_upload_branch: selectedOrganization?.defaults?.workflow_upload_branch, workflow_upload_username: selectedOrganization?.defaults?.workflow_upload_username, workflow_upload_token: selectedOrganization?.defaults?.workflow_upload_token, newsletter: selectedOrganization?.defaults?.newsletter, weekly_recommendations: selectedOrganization?.defaults?.weekly_recommendations, }, { sso_entrypoint: selectedOrganization?.sso_config?.sso_entrypoint, sso_certificate: selectedOrganization?.sso_config?.sso_certificate, client_id: selectedOrganization?.sso_config?.client_id, client_secret: selectedOrganization?.sso_config?.client_secret, openid_authorization: selectedOrganization?.sso_config?.openid_authorization, openid_token: selectedOrganization?.sso_config?.openid_token, SSORequired: selectedOrganization?.sso_config?.SSORequired, auto_provision: selectedOrganization?.sso_config?.auto_provision, } ) } return (
Notification Workflow The notification workflow triggers when an error occurs in one of your workflows. Each individual one will only start a workflow once every 2 minutes. You can point child org notifications into the parent org notification by choosing it in the list.
{workflows !== undefined && workflows !== null && workflows.length > 0 ? { setOpenNotification(true); }} onClose={() => { setOpenNotification(false); }} freeSolo //autoSelect value={workflows?.find(w => w.id === notificationWorkflow) || null} classes={{ inputRoot: classes.inputRoot }} ListboxProps={{ style: { backgroundColor: "#212121", color: "white", }, }} getOptionLabel={(option) => { if ( option === undefined || option === null || option.name === undefined || option.name === null ) { return "No Workflow Selected"; } const newname = ( option.name.charAt(0).toUpperCase() + option.name.substring(1) ).replaceAll("_", " "); return newname; }} options={workflows} fullWidth style={{ backgroundColor: "#212121", borderRadius: theme.palette?.borderRadius, height: 35, marginBottom: 40, }} onChange={(event, newValue) => { console.log("Found value: ", newValue) var parsedinput = { target: { value: newValue } } // For variables if (typeof newValue === 'string' && newValue.startsWith("$")) { parsedinput = { target: { value: { "name": newValue, "id": newValue, "actions": [], "triggers": [], } } } } handleWorkflowSelectionUpdate(parsedinput) }} renderOption={(props, data, state) => { if (data.id === workflow.id) { data = workflow; } return ( {data.image !== undefined && data.image !== null && data.image.length > 0 ? {data.name} : null} Choose {data.name} } placement="bottom"> { props.onMouseDown?.(null); var parsedinput = { target: { value: data } } handleWorkflowSelectionUpdate(parsedinput) }} > {data.name} ) }} renderInput={(params) => { return ( ); }} /> : { setNotificationWorkflow(e.target.value); }} /> } {/*
{orgSaveButton}
*/}
{notificationWorkflow === undefined || notificationWorkflow === null || notificationWorkflow.length === 0 ? null :
{ if (notificationWorkflow === "parent") { toast.error("Can't open parent org's notification workflow from here.") return } window.open(`/workflows/${notificationWorkflow}?view=executions`, "_blank") }} >
} Notifications ({ notifications?.filter((notification) => showRead === true || notification.read === false).length }) Notifications help you find potential problems with your workflows and apps.  Learn more
{ setShowRead(!showRead); }} />  Show read {notifications !== undefined && notifications !== null && notifications.length > 1 ? ( ) : null}
{clickedFromOrgTab? null : }

Suggestions

Suggestions are tasks identified by Shuffle to help you discover ways to protect your and customers' company.
These range from simple configurations in Shuffle to Usecases you may have missed.  Learn more
{ setShowDismissed(!showDismissed); }} />  Show dismissed {userdata.priorities === null || userdata.priorities === undefined || userdata.priorities.length === 0 ? No Suggestions found : userdata.priorities.map((priority, index) => { if (showDismissed === false && priority.active === false) { return null } return ( ) }) }
) }) export default Priorities; const NotificationItem = memo((props) => { const {data, selectedExecutionId, selectedWorkflow, highlightKMS, userdata, imagesize, boxColor, clickedFromOrgTab, notificationWidth, dismissNotification} = props var image = ""; var orgName = ""; var orgId = ""; var highlighted = selectedExecutionId === "" && selectedWorkflow === "" ? false : data.reference_url === undefined || data.reference_url === null || data.reference_url.length === 0 ? false : data.reference_url.includes(selectedExecutionId) || data.reference_url.includes(selectedWorkflow) if (!highlighted && highlightKMS) { if (data.title !== undefined && data.title !== null && data.title.toLowerCase().includes("kms")) { highlighted = true } else if (data.description !== undefined && data.description !== null && data.description.toLowerCase().includes("kms")) { highlighted = true } } if (userdata.orgs !== undefined) { const foundOrg = userdata.orgs.find((org) => org.id === data["org_id"]); if (foundOrg !== undefined && foundOrg !== null) { //position: "absolute", bottom: 5, right: -5, const imageStyle = { width: imagesize, height: imagesize, pointerEvents: "none", marginLeft: data.creator_org !== undefined && data.creator_org.length > 0 ? 20 : 0, borderRadius: 10, border: foundOrg.id === userdata.active_org.id ? `3px solid ${boxColor}` : null, cursor: "pointer", marginRight: 10, }; image = foundOrg.image === "" ? ( {foundOrg.name} ) : ( {foundOrg.name} {}} /> ); orgName = foundOrg.name; orgId = foundOrg.id; } } return (
{data.amount === 1 && data.read === false ? : null} {data.ignored === true ? : null} {data.read === false ? : } {data.title}
{data.image !== undefined && data.image !== null && data.image.length > 0 ? {data.title} : null } {data.description}
{data.read === false ? ( ) : null} First seen:{" "} {new Date(data.created_at * 1000).toISOString().slice(0, 19)} Last seen:{" "} {new Date(data.updated_at * 1000).toISOString().slice(0, 19)} Times seen: {data.amount}
); }) const NotificationComponent = memo(({notifications, showRead, selectedExecutionId, selectedWorkflow, highlightKMS, userdata, imagesize, boxColor, clickedFromOrgTab, notificationWidth, dismissNotification}) => { return(
{notifications === null || notifications === undefined || notifications?.length === 0 ? ( null ) :
{notifications?.map((notification, index) => { if (showRead === false && notification.read === true) { return null } return ( ) })}
}
) }) // const PaddingWrapper = memo(({children, clickedFromOrgTab}) => { // const { leftSideBarOpenByClick } = useContext(Context) // return( //
// {children} //
// ) // }) // const Wrapper = memo(({children, clickedFromOrgTab}) => { // return( // // {children} // // ) // })