Fixed Tenzir force start from frontend

This commit is contained in:
Frikky
2025-01-09 22:31:53 +01:00
parent 2fd7f65831
commit aab73b5ecc
14 changed files with 2164 additions and 1564 deletions
+62 -28
View File
@@ -11,6 +11,8 @@ import {
Button, Button,
Stack, Stack,
Avatar, Avatar,
Skeleton,
Tooltip,
} from '@mui/material'; } from '@mui/material';
import CloseIcon from '@mui/icons-material/Close'; import CloseIcon from '@mui/icons-material/Close';
@@ -38,6 +40,7 @@ const AppModal = ({ open, onClose, app, globalUrl }) => {
const [inputUsecase, setInputUsecase] = useState({}) const [inputUsecase, setInputUsecase] = useState({})
const [latestUsecase, setLatestUsecase] = useState([]) const [latestUsecase, setLatestUsecase] = useState([])
const [foundAppUsecase, setFoundAppUsecase] = useState({}) const [foundAppUsecase, setFoundAppUsecase] = useState({})
const [usecaseLoading, setUsecaseLoading] = useState(false)
const navigate = useNavigate(); const navigate = useNavigate();
const parseUsecase = (subcase) => { const parseUsecase = (subcase) => {
const srcdata = findSpecificApp(frameworkData, subcase.type) const srcdata = findSpecificApp(frameworkData, subcase.type)
@@ -142,6 +145,19 @@ const AppModal = ({ open, onClose, app, globalUrl }) => {
}) })
setLatestUsecase(newUsecases) setLatestUsecase(newUsecases)
if (newUsecases?.length > 0) {
const foundCategory = newUsecases?.find((category) =>
category?.list?.some((subcase) => subcase?.srcapp === app?.name || subcase?.dstapp === app?.name)
);
const foundSubcase = foundCategory?.list?.find(
(subcase) => subcase?.srcapp === app?.name || subcase?.dstapp === app?.name
);
setFoundAppUsecase(foundSubcase);
}
setUsecaseLoading(false)
// Matching workflows with usecases // Matching workflows with usecases
if (responseJson.success !== false) { if (responseJson.success !== false) {
if (workflows !== undefined && workflows !== null && workflows.length > 0) { if (workflows !== undefined && workflows !== null && workflows.length > 0) {
@@ -275,23 +291,12 @@ const AppModal = ({ open, onClose, app, globalUrl }) => {
useEffect(() => { useEffect(() => {
setUsecaseLoading(true)
getAvailableWorkflows() getAvailableWorkflows()
getFramework() getFramework()
}, [app]) }, [app])
useEffect(() => {
const foundCategory = latestUsecase?.find((category) =>
category?.list?.some((subcase) => subcase?.srcapp === app?.name || subcase?.dstapp === app?.name)
);
const foundSubcase = foundCategory?.list?.find(
(subcase) => subcase?.srcapp === app?.name || subcase?.dstapp === app?.name
);
setFoundAppUsecase(foundSubcase);
}, [latestUsecase])
const downloadApp = (inputdata) => { const downloadApp = (inputdata) => {
const id = inputdata.id; const id = inputdata.id;
@@ -389,9 +394,8 @@ const AppModal = ({ open, onClose, app, globalUrl }) => {
newAppname = newAppname?.replaceAll("_", " "); newAppname = newAppname?.replaceAll("_", " ");
} }
var canEditApp = userdata.admin === "true" || userdata?.id === app?.owner || app?.owner === "" || (userdata.admin === "true" && userdata.active_org.id === app?.reference_org) || !app?.generated
var canEditApp = userdata !== undefined && (userdata?.admin === "true" || userdata?.id === app?.owner || app?.owner === "" || (userdata?.admin === "true" && userdata?.active_org?.id === app?.reference_org)) || !app?.generated
return ( return (
<Dialog <Dialog
@@ -501,6 +505,13 @@ const AppModal = ({ open, onClose, app, globalUrl }) => {
app?.private_id !== undefined && app?.private_id !== undefined &&
app?.private_id?.length > 0 && app?.private_id?.length > 0 &&
app?.generated ? ( app?.generated ? (
<Tooltip title="Download OpenAPI"
placement="top"
arrow
sx={{
fontFamily: theme?.typography?.fontFamily
}}
>
<Button <Button
variant="contained" variant="contained"
sx={{ sx={{
@@ -522,7 +533,9 @@ const AppModal = ({ open, onClose, app, globalUrl }) => {
}} }}
> >
<CloudDownloadOutlined /> <CloudDownloadOutlined />
</Button>) : null} </Button>
</Tooltip>
) : null}
<Button <Button
variant="contained" variant="contained"
sx={{ sx={{
@@ -655,6 +668,16 @@ const AppModal = ({ open, onClose, app, globalUrl }) => {
justifyContent: "start", justifyContent: "start",
width: "100%" width: "100%"
}}> }}>
{usecaseLoading ? (
<Skeleton
variant="text"
width="55%"
sx={{
fontSize: "16px",
mb: "16px",
}}
/>
) : (
<div style={{ <div style={{
fontFamily: theme?.typography?.fontFamily, fontFamily: theme?.typography?.fontFamily,
fontSize: "16px", fontSize: "16px",
@@ -666,10 +689,11 @@ const AppModal = ({ open, onClose, app, globalUrl }) => {
(foundAppUsecase?.srcapp !== undefined && foundAppUsecase?.dstapp !== undefined) ? ( (foundAppUsecase?.srcapp !== undefined && foundAppUsecase?.dstapp !== undefined) ? (
"Connect " + foundAppUsecase?.srcapp?.replaceAll("_", " ") + " to " + foundAppUsecase?.dstapp?.replaceAll("_", " ") "Connect " + foundAppUsecase?.srcapp?.replaceAll("_", " ") + " to " + foundAppUsecase?.dstapp?.replaceAll("_", " ")
) : ( ) : (
"Connect " + app?.name + " to any tool" "Connect " + app?.name.replaceAll("_", " ") + " to any tool"
) )
} }
</div> </div>
)}
<Box sx={{ <Box sx={{
bgcolor: '#2F2F2F', bgcolor: '#2F2F2F',
@@ -679,11 +703,19 @@ const AppModal = ({ open, onClose, app, globalUrl }) => {
alignItems: 'center', alignItems: 'center',
mb: 3 mb: 3
}}> }}>
{usecaseLoading ? (
<Stack direction="row" spacing={2} alignItems="center" sx={{ width: '100%' }}>
<Stack direction="row" spacing={-1}> <Stack direction="row" spacing={-1}>
{ <Skeleton variant="circular" width={32} height={32} />
foundAppUsecase === undefined ? ( <Skeleton variant="circular" width={32} height={32} />
<Avatar sx={{ width: 32, height: 32, bgcolor: 'background.paper', border: 1, borderColor: 'divider' }}> </Stack>
<Search sx={{ color: 'text.primary', zIndex: 10, fontSize: 18 }} /> <Skeleton variant="text" sx={{ flexGrow: 1 }} width={200} />
</Stack>
) : (
<>
<Stack direction="row" spacing={-1}>
{foundAppUsecase?.srcapp === undefined ? (
<Avatar sx={{ width: 32, height: 32, bgcolor: 'background.paper', border: 1, borderColor: 'divider' }} src={app?.image_url || app?.large_image}>
</Avatar> </Avatar>
) : ( ) : (
<Avatar <Avatar
@@ -697,10 +729,8 @@ const AppModal = ({ open, onClose, app, globalUrl }) => {
zIndex: 10 zIndex: 10
}} }}
/> />
) )}
} {foundAppUsecase?.dstapp === undefined ? (
{
foundAppUsecase === undefined ? (
<Avatar sx={{ width: 32, height: 32, bgcolor: 'background.paper', border: 1, borderColor: 'divider' }}> <Avatar sx={{ width: 32, height: 32, bgcolor: 'background.paper', border: 1, borderColor: 'divider' }}>
<AddIcon sx={{ color: 'text.primary', zIndex: 10, fontSize: 18 }} /> <AddIcon sx={{ color: 'text.primary', zIndex: 10, fontSize: 18 }} />
</Avatar> </Avatar>
@@ -716,12 +746,13 @@ const AppModal = ({ open, onClose, app, globalUrl }) => {
zIndex: 10 zIndex: 10
}} }}
/> />
) )}
}
</Stack> </Stack>
<Typography sx={{ ml: 2, fontSize: "16px", letterSpacing: "0.5px" }}> <Typography sx={{ ml: 2, fontSize: "16px", letterSpacing: "0.5px" }}>
{foundAppUsecase?.name || "Search for a Usecase"} {foundAppUsecase?.name || "Search for a Usecase"}
</Typography> </Typography>
</>
)}
</Box> </Box>
</div> </div>
@@ -742,10 +773,13 @@ const AppModal = ({ open, onClose, app, globalUrl }) => {
minWidth: '200px' minWidth: '200px'
}} }}
onClick={() => { onClick={() => {
navigate("/usecases2") navigate("/usecases")
}} }}
disabled={usecaseLoading}
> >
Find a Usecase {
(foundAppUsecase !== undefined && foundAppUsecase !== null && usecaseLoading === false) ? "See usecase" : "Find a Usecase"
}
</Button> </Button>
</div> </div>
</DialogContent> </DialogContent>
+7 -7
View File
@@ -536,15 +536,16 @@ const AppSelection = props => {
})} })}
</Grid> </Grid>
</div> </div>
{
!isAppPage && (
<>
{!moreButton ? ( {!moreButton ? (
<div style={{ width: "100%", marginLeft: isMobile ? 80 : 200, marginBottom: 20, textAlign: isMobile ? "center" : null }}> <div style={{ width: "100%", marginLeft: isMobile ? 80 : 200, marginBottom: 20, textAlign: isMobile ? "center" : null }}>
<Link style={{ color: "#FF8444" }} onClick={() => { <Link style={{ color: "#FF8444" }} onClick={() => {
setMoreButton(true) setMoreButton(true)
setTimeout(() => { setTimeout(() => {
if (isAppPage) {
navigate("/apps?tab=all_apps")
} else {
navigate("/welcome?tab=2") navigate("/welcome?tab=2")
}
}, 250) }, 250)
}} }}
>See More Apps</Link> >See More Apps</Link>
@@ -552,15 +553,14 @@ const AppSelection = props => {
<div style={{ flexDirection: "row", width: isMobile ? 340 : null, textAlign: isMobile ? "center" : null }}> <div style={{ flexDirection: "row", width: isMobile ? 340 : null, textAlign: isMobile ? "center" : null }}>
<Button variant="contained" type="submit" fullWidth style={bottomButtonStyle} onClick={() => { <Button variant="contained" type="submit" fullWidth style={bottomButtonStyle} onClick={() => {
navigate("/usecases2") navigate("/usecases")
if(!isAppPage) {
setActiveStep(2) setActiveStep(2)
}
}}> }}>
See usecases See usecases
</Button> </Button>
</div> </div>
</>
)
}
</div> </div>
</Fade> </Fade>
) )
+1
View File
@@ -537,6 +537,7 @@ const CacheView = memo((props) => {
padding: "15px 5px", padding: "15px 5px",
maxHeight: 300, maxHeight: 300,
verticalAlign: "middle", verticalAlign: "middle",
maxWidth: 300,
}} }}
primary={validate.valid ? primary={validate.valid ?
<ReactJson <ReactJson
+408 -16
View File
@@ -22,6 +22,9 @@ import {
DialogActions, DialogActions,
Typography, Typography,
Skeleton, Skeleton,
Checkbox,
Chip,
Menu,
} from "@mui/material"; } from "@mui/material";
import { import {
@@ -35,6 +38,7 @@ import {
Publish as PublishIcon, Publish as PublishIcon,
Clear as ClearIcon, Clear as ClearIcon,
Add as AddIcon, Add as AddIcon,
SelectAll,
} from "@mui/icons-material"; } from "@mui/icons-material";
import Dropzone from "../components/Dropzone.jsx"; import Dropzone from "../components/Dropzone.jsx";
@@ -61,6 +65,14 @@ const Files = memo((props) => {
const [downloadBranch, setDownloadBranch] = React.useState("main"); const [downloadBranch, setDownloadBranch] = React.useState("main");
const [downloadFolder, setDownloadFolder] = React.useState("translation_standards"); const [downloadFolder, setDownloadFolder] = React.useState("translation_standards");
const [contentLoading, setContentLoading] = React.useState(false) const [contentLoading, setContentLoading] = React.useState(false)
const [selectAllChecked, setSelectAllChecked] = React.useState(false)
const [selectedFiles, setSelectedFiles] = useState([]);
const [selectedFileId, setSelectedFileId] = useState([])
const [showFileCategoryPopup, setShowFileCategoryPopup] = useState(false)
const [updateToThisCategory, setUpdateToThisCategory] = useState("")
const [showDistributionPopup, setShowDistributionPopup] = useState(false)
const [selectedSubOrg, setSelectedSubOrg] = useState([])
const [fileIdSelectedForDistribution, setFileIdSelectedForDistribution] = useState("")
//const alert = useAlert(); //const alert = useAlert();
const allowedFileTypes = ["txt", "py", "yaml", "yml","json", "html", "js", "csv", "log", "eml", "msg", "md", "xml", "sh", "bat", "ps1", "psm1", "psd1", "ps1xml", "pssc", "psc1", "response"] const allowedFileTypes = ["txt", "py", "yaml", "yml","json", "html", "js", "csv", "log", "eml", "msg", "md", "xml", "sh", "bat", "ps1", "psm1", "psd1", "ps1xml", "pssc", "psc1", "response"]
var upload = ""; var upload = "";
@@ -80,6 +92,56 @@ const Files = memo((props) => {
} }
const changeDistribution = (id, selectedSubOrg) => {
editFileConfig(id, "suborg_distribute", [...new Set(selectedSubOrg)])
}
const editFileConfig = (id, parentAction, selectedSubOrg) => {
const data = {
id: id,
action: parentAction !== undefined && parentAction !== null ? parentAction : "change_category",
selected_suborgs: selectedSubOrg,
}
const url = globalUrl + "/api/v1/files/" + id + "/config";
fetch(url, {
mode: "cors",
method: "POST",
body: JSON.stringify(data),
credentials: "include",
crossDomain: true,
withCredentials: true,
headers: {
"Content-Type": "application/json; charset=utf-8",
},
})
.then((response) =>
response.json().then((responseJson) => {
if (responseJson["success"] === false) {
toast("Failed overwriting files");
} else {
toast("Successfully updated file!");
setTimeout(() => {
getFiles();
}, 1000);
}
})
)
.catch((error) => {
toast("Err: " + error.toString());
});
};
const handleFileCheckboxChange = (index) => {
setSelectedFiles((prevSelected) => {
const updatedSelected = [...prevSelected];
updatedSelected[index] = !updatedSelected[index];
return updatedSelected;
});
};
const runUpdateText = (text) =>{ const runUpdateText = (text) =>{
fetch(`${globalUrl}/api/v1/files/${openFileId}/edit`, { fetch(`${globalUrl}/api/v1/files/${openFileId}/edit`, {
method: "PUT", method: "PUT",
@@ -137,6 +199,7 @@ const Files = memo((props) => {
if (responseJson.files !== undefined && responseJson.files !== null) { if (responseJson.files !== undefined && responseJson.files !== null) {
setFiles(responseJson.files); setFiles(responseJson.files);
setShowLoader(false) setShowLoader(false)
setShowDistributionPopup(false)
} else if (responseJson.list !== undefined && responseJson.list !== null) { } else if (responseJson.list !== undefined && responseJson.list !== null) {
// Set the "namespace" field in all items // Set the "namespace" field in all items
if (namespace !== undefined && namespace !== null) { if (namespace !== undefined && namespace !== null) {
@@ -152,6 +215,7 @@ const Files = memo((props) => {
} else { } else {
setFiles([]); setFiles([]);
setShowLoader(false) setShowLoader(false)
setShowDistributionPopup(false)
} }
if (namespace === undefined || namespace === null || namespace === "default") { if (namespace === undefined || namespace === null || namespace === "default") {
@@ -256,7 +320,7 @@ const Files = memo((props) => {
zIndex: 1000, zIndex: 1000,
minWidth: "800px", minWidth: "800px",
minHeight: "320px", minHeight: "320px",
overflow: "hidden", overflow: "auto",
'& .MuiDialogContent-root': { '& .MuiDialogContent-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
}, },
@@ -400,6 +464,127 @@ const Files = memo((props) => {
</Dialog> </Dialog>
: null : null
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 fileDistributionModal = showDistributionPopup ? (
<Dialog
open={showDistributionPopup}
onClose={() => setShowDistributionPopup(false)}
PaperProps={{
sx: {
borderRadius: theme?.palette?.DialogStyle?.borderRadius,
border: theme?.palette?.DialogStyle?.border,
fontFamily: theme?.typography?.fontFamily,
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
zIndex: 1000,
minWidth: "600px",
minHeight: "320px",
overflow: "auto",
'& .MuiDialogContent-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
'& .MuiDialogTitle-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
'& .MuiDialogActions-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
},
}}
>
<DialogTitle>
<div style={{ color: "rgba(255,255,255,0.9)" }}>
Select sub-org to distribute files
</div>
</DialogTitle>
<DialogContent style={{ color: "rgba(255,255,255,0.65)" }}>
<MenuItem value="none" onClick={()=> {handleSelectSubOrg(null, "none")}}>None</MenuItem>
<MenuItem value="all" onClick={()=> {handleSelectSubOrg(null, "all")}}>All</MenuItem>
{userdata.orgs.map((data, index) => {
if (data.creator_org !== userdata.active_org.id) {
return null;
}
const imagesize = 22;
const imageStyle = {
width: imagesize,
height: imagesize,
pointerEvents: "none",
marginRight: 10,
marginLeft: data.id === userdata.active_org.id ? 0 : 20,
};
const image = data.image === "" ? (
<img alt={data.name} src={theme.palette.defaultImage} style={imageStyle} />
) : (
<img alt={data.name} src={data.image} style={imageStyle} />
);
return (
<MenuItem
key={index}
value={data.id}
onClick={() => handleSelectSubOrg(data.id)}
style={{ display: "flex", alignItems: "center" }}
>
<Checkbox
checked={selectedSubOrg.includes(data.id)}
/>
{image}
<span style={{ marginLeft: 8 }}>{data.name}</span>
</MenuItem>
);
})}
<div style={{ display: "flex", marginTop: 20 }}>
<Button
style={{ borderRadius: "2px", textTransform: 'none', fontSize:16, color: "#ff8544" }}
onClick={() => setShowDistributionPopup(false)}
color="primary"
>
Cancel
</Button>
<Button
variant="contained"
style={{ borderRadius: "2px", textTransform: 'none', fontSize:16, color: "#1a1a1a", backgroundColor: "#ff8544", marginLeft: 10 }}
onClick={() => {
changeDistribution(fileIdSelectedForDistribution, selectedSubOrg);
}}
color="primary"
>
Submit
</Button>
</div>
</DialogContent>
</Dialog>
): null
const deleteFile = (file) => { const deleteFile = (file) => {
fetch(globalUrl + "/api/v1/files/" + file.id, { fetch(globalUrl + "/api/v1/files/" + file.id, {
method: "DELETE", method: "DELETE",
@@ -636,7 +821,7 @@ const Files = memo((props) => {
setTimeout(() => { setTimeout(() => {
getFiles() getFiles()
}, 2500); }, 3000);
}; };
const uploadFile = (e) => { const uploadFile = (e) => {
@@ -649,6 +834,68 @@ const Files = memo((props) => {
uploadFiles(files); uploadFiles(files);
}; };
const handleUpdateFileCategory = (namespace) => {
if (selectedFiles.length === 0 && !selectAllChecked) {
toast("Please select files to update category")
return
}
if (namespace === undefined || namespace === null || namespace === "") {
toast("Please select a category to update files to")
return
}
const url = globalUrl + `/api/v1/files/namespaces/${namespace}/share`
const data = {
SelectedFiles: selectedFileId,
}
setShowLoader(true)
fetch(url, {
mode: "cors",
method: "POST",
body: JSON.stringify(data),
credentials: "include",
crossDomain: true,
withCredentials: true,
headers: {
"Content-Type": "application/json; charset=utf-8",
},
})
.then((response) =>
response.json().then((responseJson) => {
if (responseJson["success"] === false) {
toast("Failed overwriting files");
} else {
setSelectAllChecked(false)
setSelectedFiles([])
setSelectedFileId([])
setShowFileCategoryPopup(false)
setSelectedCategory(namespace)
setTimeout(() => {
getFiles();
toast("Successfully updated file!");
if (window.location.search.includes("category=")) {
const newurl = window.location.href.replace(/category=[^&]+/, `category=${namespace}`)
window.history.pushState({ path: newurl }, "", newurl)
} else {
window.history.pushState({ path: window.location.href }, "", `${window.location.href}&category=${namespace}`)
}
}, 1000);
}
}
))
.catch((error) => {
toast("Err: " + error.toString());
});
}
return ( return (
<Dropzone <Dropzone
style={{ style={{
@@ -659,6 +906,7 @@ const Files = memo((props) => {
}} }}
onDrop={uploadFile} onDrop={uploadFile}
> >
{fileDistributionModal}
<div style={{width: "100%", minHeight: 1100, boxSizing: 'border-box', padding: "27px 10px 19px 27px", height:"100%", backgroundColor: '#212121',borderTopRightRadius: '8px', borderBottomRightRadius: 8, borderLeft: "1px solid #494949", }}> <div style={{width: "100%", minHeight: 1100, boxSizing: 'border-box', padding: "27px 10px 19px 27px", height:"100%", backgroundColor: '#212121',borderTopRightRadius: '8px', borderBottomRightRadius: 8, borderLeft: "1px solid #494949", }}>
<div style={{height: "100%", maxHeight: 1700,overflowY: 'auto', scrollbarColor: '#494949 transparent', scrollbarWidth: 'thin'}}> <div style={{height: "100%", maxHeight: 1700,overflowY: 'auto', scrollbarColor: '#494949 transparent', scrollbarWidth: 'thin'}}>
@@ -739,14 +987,19 @@ const Files = memo((props) => {
}} }}
value={selectedCategory} value={selectedCategory}
onChange={(event) => { onChange={(event) => {
if (selectAllChecked || selectedFiles.length > 0) {
setUpdateToThisCategory(event.target.value)
setShowFileCategoryPopup(true)
return
}
setSelectedCategory(event.target.value) setSelectedCategory(event.target.value)
if (event.target.value === "all" || event.target.value === "default") { if (event.target.value === "all" || event.target.value === "default") {
getFiles() getFiles()
} else { } else {
getFiles(event.target.value) getFiles(event.target.value)
} }
// Add it to the url as a query // Add it to the url as a query
if (window.location.search.includes("category=")) { if (window.location.search.includes("category=")) {
const newurl = window.location.href.replace(/category=[^&]+/, `category=${event.target.value}`) const newurl = window.location.href.replace(/category=[^&]+/, `category=${event.target.value}`)
@@ -768,14 +1021,41 @@ const Files = memo((props) => {
); );
})} })}
</Select> </Select>
<Dialog
PaperProps={{
sx: {
borderRadius: theme?.palette?.DialogStyle?.borderRadius,
border: theme?.palette?.DialogStyle?.border,
fontFamily: theme?.typography?.fontFamily,
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
zIndex: 1000,
'& .MuiDialogContent-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
'& .MuiDialogTitle-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
'& .MuiDialogActions-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
},
}} open={showFileCategoryPopup} onClose={() => setShowFileCategoryPopup(false)}>
<DialogTitle>File Categories</DialogTitle>
<DialogContent>
Please note that your selected files ({selectedFileId?.length}) will be moved to the <kbd>{updateToThisCategory}</kbd> category.
</DialogContent>
<DialogActions>
<Button onClick={() => setShowFileCategoryPopup(false)} style={{fontSize: 16, textTransform: 'none'}}>Close</Button>
<Button onClick={() => handleUpdateFileCategory(updateToThisCategory)} style={{fontSize: 16, textTransform: 'none', color: "#1a1a1a", backgroundColor: "#ff8544"}}>Update</Button>
</DialogActions>
</Dialog>
</FormControl> </FormControl>
) : null} ) : null}
<div style={{display: "inline-flex", position:"relative", top: 8}}> <div style={{display: "inline-flex", position:"relative", top: 8}}>
{renderTextBox ? {renderTextBox ?
<Tooltip title={"Close"} style={{}} aria-label={""}> <Tooltip title={"Close"} style={{}} aria-label={""}>
<Button <Button
style={{ marginLeft: 5, marginRight: 15 }} style={{ marginLeft: 5, marginRight: 15, height: 35, borderRadius: 4, backgroundColor: "#494949", textTransform: 'none', fontSize: 16, color: "#f1f1f1" }}
color="primary" color="primary"
onClick={() => { onClick={() => {
setRenderTextBox(false); setRenderTextBox(false);
@@ -803,10 +1083,24 @@ const Files = memo((props) => {
{renderTextBox && <TextField {renderTextBox && <TextField
onKeyPress={(event)=>{ onKeyPress={(event)=>{
handleKeyDown(event); handleKeyDown(event);
if(event.key === 'Enter' && selectedFileId.length > 0){
setShowFileCategoryPopup(true)
setUpdateToThisCategory(event.target.value)
}
}}
style={{
height: 35,
width: 200,
marginTop: 0,
}} }}
InputProps={{ InputProps={{
style: { style: {
color: "white", color: "white",
height: 35,
fontSize: 16,
borderRadius: 4,
paddingTop: 0,
}, },
}} }}
color="primary" color="primary"
@@ -816,7 +1110,6 @@ const Files = memo((props) => {
defaultValue={""} defaultValue={""}
autoFocus autoFocus
/>}</div> />}</div>
<ShuffleCodeEditor <ShuffleCodeEditor
isCloud={isCloud} isCloud={isCloud}
expansionModalOpen={openEditor} expansionModalOpen={openEditor}
@@ -852,11 +1145,51 @@ const Files = memo((props) => {
tableLayout: "auto", tableLayout: "auto",
display: "table", display: "table",
minWidth: 800, minWidth: 800,
overflowX: "auto" overflowX: "auto",
paddingBottom: 0,
}} }}
> >
<ListItem style={{width:isSelectedFiles?"100%":null, borderBottom:isSelectedFiles?"1px solid #494949":null, display: 'table-row'}}> <ListItem
{["Name", "Workflow", "Md5", "Status", "Filesize", "Actions"].map((header, index) => ( style={{
borderBottom: "1px solid #494949" ,
display: "table-row"
}}
>
{[
<Tooltip title={"Select all files"} style={{}} aria-label={""}>
<Checkbox
sx={{padding: 0}}
onChange={() => {
setSelectAllChecked((prev) => !prev);
setSelectedFiles((prev) => {
if (prev.length === files.length) {
return []
} else {
return files.map((_, index) => !prev.includes(index))
}
})
if (selectAllChecked) {
setSelectedFileId([])
} else {
setSelectedFileId(
files
.filter((file) => file.namespace === selectedCategory)
.map((file) => file.id)
);
}
}}
/>
</Tooltip>,
"Name",
"Workflow",
"Md5",
"Status",
"Filesize",
"Actions",
"Distribution"
]
.filter(Boolean)
.map((header, index) => (
<ListItemText <ListItemText
key={index} key={index}
primary={header} primary={header}
@@ -870,7 +1203,7 @@ const Files = memo((props) => {
}} }}
primaryTypographyProps={{ primaryTypographyProps={{
style: { style: {
paddingLeft: 10, paddingLeft: 10
} }
}} }}
/> />
@@ -885,7 +1218,7 @@ const Files = memo((props) => {
backgroundColor: "#212121", backgroundColor: "#212121",
}} }}
> >
{Array(6) {Array(8)
.fill() .fill()
.map((_, colIndex) => ( .map((_, colIndex) => (
<ListItemText <ListItemText
@@ -928,7 +1261,7 @@ const Files = memo((props) => {
if (index % 2 === 0) { if (index % 2 === 0) {
bgColor = isSelectedFiles ? "#1A1A1A":"#1f2023"; bgColor = isSelectedFiles ? "#1A1A1A":"#1f2023";
} }
const isDistributed = file?.suborg_distribution?.length > 0 ? true : false;
const filenamesplit = file.filename.split(".") const filenamesplit = file.filename.split(".")
const iseditable = file.filesize < 2000000 && file.status === "active" && allowedFileTypes.includes(filenamesplit[filenamesplit.length-1]) const iseditable = file.filesize < 2000000 && file.status === "active" && allowedFileTypes.includes(filenamesplit[filenamesplit.length-1])
return ( return (
@@ -953,6 +1286,26 @@ const Files = memo((props) => {
primary={new Date(file.updated_at * 1000).toISOString()} primary={new Date(file.updated_at * 1000).toISOString()}
/> />
*/} */}
<ListItemText
style={{
display: 'table-cell',
overflow: "hidden",
textAlign: "center",
}}
>
<Checkbox
style={{ padding: 0 }}
disabled={file.org_id !== selectedOrganization.id}
checked={!!selectedFiles[index] || selectAllChecked}
onChange={() => {handleFileCheckboxChange(index); setSelectedFileId(prev => {
if (prev.includes(file.id)) {
return prev.filter((item) => item !== file.id)
} else {
return [...prev, file.id]
}
})}}
/>
</ListItemText>
<ListItemText <ListItemText
primaryTypographyProps={{ primaryTypographyProps={{
style: { style: {
@@ -1064,7 +1417,7 @@ const Files = memo((props) => {
> >
<span> <span>
<IconButton <IconButton
disabled={!iseditable} disabled={!iseditable || file.org_id !== selectedOrganization.id}
style = {{padding: "6px", }} style = {{padding: "6px", }}
onClick={() => { onClick={() => {
setOpenEditor(true) setOpenEditor(true)
@@ -1137,7 +1490,6 @@ const Files = memo((props) => {
<IconButton <IconButton
style = {{padding: "6px"}} style = {{padding: "6px"}}
onClick={() => { onClick={() => {
console.log("file is : ", file)
navigator.clipboard.writeText(file.id); navigator.clipboard.writeText(file.id);
document.execCommand("copy"); document.execCommand("copy");
@@ -1154,7 +1506,7 @@ const Files = memo((props) => {
> >
<span> <span>
<IconButton <IconButton
disabled={file.status !== "active"} disabled={file.status !== "active" || file.org_id !== selectedOrganization.id}
style={{ padding: "6px" }} style={{ padding: "6px" }}
onClick={() => { onClick={() => {
deleteFile(file); deleteFile(file);
@@ -1166,7 +1518,7 @@ const Files = memo((props) => {
viewBox="0 0 24 24" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
style={{ style={{
stroke: file.status === "active" ? "#fd4c62" : "#c8c8c8", stroke: file.status === "active" && file.org_id === selectedOrganization.id ? "#fd4c62" : "#c8c8c8",
}} }}
> >
<path <path
@@ -1192,6 +1544,46 @@ const Files = memo((props) => {
// overflow: "hidden", // overflow: "hidden",
}} }}
/> />
<ListItemText primaryTypographyProps={{
style: {
padding: 8
}
}} style={{ display: "table-cell", textAlign: 'center', verticalAlign: 'middle'}} >
{selectedOrganization.id !== undefined && file?.org_id !== selectedOrganization.id ?
<Tooltip
title="Parent organization controlled file. You can use, but not modify this file. Contact an admin of your parent organization if you need changes to this."
placement="top"
>
<Chip
label={"Parent"}
variant="contained"
color="secondary"
style={{display: "table-cell",}}
/>
</Tooltip>
:
<Tooltip
title="Distributed to sub-organizations. This means the sub organizations can use this file, but can not modify it."
placement="top"
>
<Checkbox
disabled={selectedOrganization.creator_org !== undefined && selectedOrganization.creator_org !== null && selectedOrganization.creator_org !== "" ? true : false}
checked={isDistributed}
style={{ }}
color="secondary"
onClick={() => {
setShowDistributionPopup(true)
if(file?.suborg_distribution?.length > 0){
setSelectedSubOrg(file.suborg_distribution)
}else{
setSelectedSubOrg([])
}
setFileIdSelectedForDistribution(file.id)
}}
/>
</Tooltip>
}
</ListItemText>
</ListItem> </ListItem>
); );
}) })
+41 -14
View File
@@ -58,6 +58,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
const navigate = useNavigate(); const navigate = useNavigate();
const {setLeftSideBarOpenByClick, leftSideBarOpenByClick, setSearchBarModalOpen, searchBarModalOpen} = useContext(Context); const {setLeftSideBarOpenByClick, leftSideBarOpenByClick, setSearchBarModalOpen, searchBarModalOpen} = useContext(Context);
const [expandLeftNav, setExpandLeftNav] = useState(false); const [expandLeftNav, setExpandLeftNav] = useState(false);
const [activeOrgName, setActiveOrgName] = useState( const [activeOrgName, setActiveOrgName] = useState(
userdata?.active_org?.name || "Select Organziation" userdata?.active_org?.name || "Select Organziation"
@@ -261,17 +262,20 @@ useEffect(() => {
},[currentPath]); },[currentPath]);
useEffect(() => { useEffect(() => {
UpdateTabStatus(); UpdateTabStatus()
const expandLeftNav1 = localStorage.getItem("expandLeftNav");
const expandLeftNav1 = localStorage.getItem("expandLeftNav")
if (expandLeftNav1 === "false") { if (expandLeftNav1 === "false") {
setLeftSideBarOpenByClick(false); setLeftSideBarOpenByClick(false)
setLeftSideBarOpenByClick(false);
} else { } else {
setLeftSideBarOpenByClick(true); const currentLocation = window?.location?.pathname
setLeftSideBarOpenByClick(true); if (currentLocation?.includes('/workflows/')) {
setExpandLeftNav(true); } else {
setLeftSideBarOpenByClick(true)
setExpandLeftNav(true)
} }
}, []); }
}, [])
const getAvailableWorkflows = useCallback((amount) => { const getAvailableWorkflows = useCallback((amount) => {
@@ -533,7 +537,7 @@ useEffect(() => {
<Divider style={{ marginBottom: 10, }} /> <Divider style={{ marginBottom: 10, }} />
<Typography color="textSecondary" align="center" style={{ marginTop: 5, marginBottom: 5, fontSize: 18 }}> <Typography color="textSecondary" align="center" style={{ marginTop: 5, marginBottom: 5, fontSize: 18 }}>
Version: 2.0.0-rc Version: 2.0.0-rc2
</Typography> </Typography>
</Menu> </Menu>
</span> </span>
@@ -591,6 +595,10 @@ useEffect(() => {
org_id: orgId, org_id: orgId,
}; };
if (userdata?.active_org?.id === orgId) {
return
}
localStorage.setItem("globalUrl", ""); localStorage.setItem("globalUrl", "");
localStorage.setItem("getting_started_sidebar", "open"); localStorage.setItem("getting_started_sidebar", "open");
@@ -765,11 +773,18 @@ useEffect(() => {
"UK": "gb" "UK": "gb"
}; };
region = regionMapping[region_url] || "gb"; region = regionMapping[region_url] || "eu";
return `https://flagcdn.com/48x36/${region}.png`; return `https://flagcdn.com/48x36/${region}.png`;
}; };
useEffect(() => {
if (window?.location?.pathname?.includes("/workflows/")) {
setExpandLeftNav(false);
}
}, [window?.location?.pathname]);
return ( return (
<div <div
style={{ style={{
@@ -786,6 +801,18 @@ useEffect(() => {
zoom: 0.8, zoom: 0.8,
height: "calc((100vh - 32px)*1.2)", height: "calc((100vh - 32px)*1.2)",
}} }}
onMouseLeave={() => {
if (window?.location?.pathname?.includes("/workflows/")) {
setExpandLeftNav(false);
}
}}
onMouseOver={() => {
if (window?.location?.pathname?.includes("/workflows/")) {
setExpandLeftNav(true);
}
}
}
> >
{modalView} {modalView}
<Box <Box
@@ -1169,10 +1196,6 @@ useEffect(() => {
}} }}
style={{ style={{
...ButtonStyle, ...ButtonStyle,
backgroundColor:
currentOpenTab === "security"
? "#2f2f2f"
: "transparent",
}} }}
onMouseOver={(event)=>{ onMouseOver={(event)=>{
event.currentTarget.style.backgroundColor = "#2f2f2f"; event.currentTarget.style.backgroundColor = "#2f2f2f";
@@ -1443,6 +1466,8 @@ useEffect(() => {
</Button> </Button>
</Link> </Link>
</Box> </Box>
{recentworkflows?.length > 0 ?
<Box <Box
style={{ style={{
display: "flex", display: "flex",
@@ -1487,6 +1512,8 @@ useEffect(() => {
}) } }) }
</Box> </Box>
</Box> </Box>
: null }
</Box> </Box>
<Box <Box
style={{ style={{
@@ -630,7 +630,7 @@ const CodeEditor = (props) => {
for (let occ = 0; occ < variable_occurence.length; occ++) { for (let occ = 0; occ < variable_occurence.length; occ++) {
const fixedVariable = fixVariable(variable_occurence[occ]) const fixedVariable = fixVariable(variable_occurence[occ])
var correctVariable = availableVariables.includes(fixedVariable.toLowerCase()) var correctVariable = availableVariables.includes(fixedVariable)
var startCh = dollar_occurence[occ] var startCh = dollar_occurence[occ]
var endCh = dollar_occurence[occ] + dollar_occurence_len[occ] var endCh = dollar_occurence[occ] + dollar_occurence_len[occ]
@@ -746,7 +746,7 @@ const CodeEditor = (props) => {
if (!valuefound) { if (!valuefound) {
} }
if (!valuefound && availableVariables.includes(fixedVariable.toLowerCase())) { if (!valuefound && availableVariables.includes(fixedVariable)) {
var shouldbreak = false var shouldbreak = false
for (var k = 0; k < actionlist.length; k++) { for (var k = 0; k < actionlist.length; k++) {
var parsedPaths = [] var parsedPaths = []
@@ -1084,7 +1084,7 @@ const CodeEditor = (props) => {
maxHeight: isMobile ? "100%" : 700, maxHeight: isMobile ? "100%" : 700,
border: theme.palette.defaultBorder, border: theme.palette.defaultBorder,
padding: isMobile ? "25px 10px 25px 10px" : 25, padding: isMobile ? "25px 10px 25px 10px" : 25,
zoom: 0.8, // zoom: 0.8,
backgroundColor: "black", backgroundColor: "black",
}, },
}} }}
+8 -1
View File
@@ -3,11 +3,18 @@ export const Context = createContext();
export const AppContext =(props) => { export const AppContext =(props) => {
const currentLocation = window?.location?.pathname;
// Left side bar global states // Left side bar global states
const [searchBarModalOpen, setSearchBarModalOpen] = useState(false); const [searchBarModalOpen, setSearchBarModalOpen] = useState(false);
const [leftSideBarOpenByClick, setLeftSideBarOpenByClick] = useState(false); const [leftSideBarOpenByClick, setLeftSideBarOpenByClick] = useState(currentLocation?.includes('/workflows/') ? false : true)
const [windowWidth, setWindowWidth] = useState(window.innerWidth); const [windowWidth, setWindowWidth] = useState(window.innerWidth);
useEffect(() => {
if (currentLocation?.includes('/workflows/') && leftSideBarOpenByClick === true) {
setLeftSideBarOpenByClick(false)
}
}, [leftSideBarOpenByClick])
//Calculate window width //Calculate window width
useEffect(() => { useEffect(() => {
+15 -16
View File
@@ -12,18 +12,18 @@ const Admin2 = (props) => {
const [orgRequest, setOrgRequest] = React.useState(true); const [orgRequest, setOrgRequest] = React.useState(true);
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
const handleGetOrg = (orgId) => { const handleGetOrg = (orgId) => {
if ( // if (
serverside !== true && // serverside !== true &&
window.location.search !== undefined && // window.location.search !== undefined &&
window.location.search !== null // window.location.search !== null
) { // ) {
const urlSearchParams = new URLSearchParams(window.location.search); // const urlSearchParams = new URLSearchParams(window.location.search);
const params = Object.fromEntries(urlSearchParams.entries()); // const params = Object.fromEntries(urlSearchParams.entries());
const foundorgid = params["org_id"]; // const foundorgid = params["org_id"];
if (foundorgid !== undefined && foundorgid !== null) { // if (foundorgid !== undefined && foundorgid !== null) {
orgId = foundorgid; // orgId = foundorgid;
} // }
} // }
console.log("getting organization details for: ", orgId); console.log("getting organization details for: ", orgId);
// if (orgId === undefined) { // if (orgId === undefined) {
@@ -154,16 +154,15 @@ const Admin2 = (props) => {
}); });
}; };
useEffect(() => {
const urlSearchParams = new URLSearchParams(window.location.search); const urlSearchParams = new URLSearchParams(window.location.search);
const params = Object.fromEntries(urlSearchParams.entries()); const params = Object.fromEntries(urlSearchParams.entries());
const foundOrgID = params["org_id"] const foundOrgID = params["org_id"]
useEffect(() => {
if(foundOrgID !== null && foundOrgID !== undefined && userdata?.support && foundOrgID?.length > 0) { if(foundOrgID !== null && foundOrgID !== undefined && userdata?.support && foundOrgID?.length > 0) {
handleClickChangeOrg(foundOrgID) handleClickChangeOrg(foundOrgID)
} }
}, [foundOrgID]); }, [userdata]);
const handleClickChangeOrg = (orgId) => { const handleClickChangeOrg = (orgId) => {
// Don't really care about the logout // Don't really care about the logout
+17 -11
View File
@@ -399,7 +399,7 @@ const AngularWorkflow = (defaultprops) => {
props.match = {} props.match = {}
props.match.params = params props.match.params = params
const { leftSideBarOpenByClick, windowWidth } = useContext(Context) const { setLeftSideBarOpenByClick, leftSideBarOpenByClick, windowWidth } = useContext(Context)
const [workflowAsCode, setWorkflowAsCode] = useState(false); const [workflowAsCode, setWorkflowAsCode] = useState(false);
var to_be_copied = ""; var to_be_copied = "";
@@ -8662,6 +8662,9 @@ const releaseToConnectLabel = "Release to Connect"
getApps() getApps()
fetchUsecases() fetchUsecases()
setLeftSideBarOpenByClick(false)
localStorage.setItem("expandLeftNav", false)
const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search; const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search;
// FIXME: Don't check specific one here // FIXME: Don't check specific one here
@@ -10568,6 +10571,9 @@ const releaseToConnectLabel = "Release to Connect"
const CustomAppHits = connectHits(AppHits) const CustomAppHits = connectHits(AppHits)
var shuffleToolsApp = apps.find((app) => app.name === "Shuffle Tools") var shuffleToolsApp = apps.find((app) => app.name === "Shuffle Tools")
if (shuffleToolsApp !== undefined && shuffleToolsApp !== null) {
shuffleToolsApp = JSON.parse(JSON.stringify(shuffleToolsApp))
}
var viewedApps = [] var viewedApps = []
return ( return (
@@ -10620,24 +10626,24 @@ const releaseToConnectLabel = "Release to Connect"
<div style={{ <div style={{
display: "flex", display: "flex",
}}> }}>
<ParsedAppPaper small={true} action={"repeat_back_to_me"} app={JSON.parse(JSON.stringify(shuffleToolsApp))} /> <ParsedAppPaper small={true} action={"repeat_back_to_me"} app={shuffleToolsApp} />
<div style={{marginLeft: 5, }}/> <div style={{marginLeft: 5, }}/>
<ParsedAppPaper small={true} action={"filter_list"} app={JSON.parse(JSON.stringify(shuffleToolsApp))} /> <ParsedAppPaper small={true} action={"filter_list"} app={shuffleToolsApp} />
<div style={{marginLeft: 5, }}/> <div style={{marginLeft: 5, }}/>
<ParsedAppPaper small={true} action={"execute_python"} app={JSON.parse(JSON.stringify(shuffleToolsApp))} /> <ParsedAppPaper small={true} action={"execute_python"} app={shuffleToolsApp} />
<div style={{marginLeft: 5, }}/> <div style={{marginLeft: 5, }}/>
<ParsedAppPaper small={true} action={"parse_ioc"} app={JSON.parse(JSON.stringify(shuffleToolsApp))} /> <ParsedAppPaper small={true} action={"parse_ioc"} app={shuffleToolsApp} />
</div> </div>
<div style={{ <div style={{
display: "flex", display: "flex",
}}> }}>
<ParsedAppPaper small={true} action={"set_cache_value"} app={JSON.parse(JSON.stringify(shuffleToolsApp))} /> <ParsedAppPaper small={true} action={"set_cache_value"} app={shuffleToolsApp} />
<div style={{marginLeft: 5, }}/> <div style={{marginLeft: 5, }}/>
<ParsedAppPaper small={true} action={"get_file_meta"} app={JSON.parse(JSON.stringify(shuffleToolsApp))} /> <ParsedAppPaper small={true} action={"get_file_meta"} app={shuffleToolsApp} />
<div style={{marginLeft: 5, }}/> <div style={{marginLeft: 5, }}/>
<ParsedAppPaper small={true} action={"merge_lists"} app={JSON.parse(JSON.stringify(shuffleToolsApp))} /> <ParsedAppPaper small={true} action={"merge_lists"} app={shuffleToolsApp} />
<div style={{marginLeft: 5, }}/> <div style={{marginLeft: 5, }}/>
<ParsedAppPaper small={true} action={"send_sms_shuffle"} app={JSON.parse(JSON.stringify(shuffleToolsApp))} /> <ParsedAppPaper small={true} action={"send_sms_shuffle"} app={shuffleToolsApp} />
</div> </div>
</div> </div>
: null} : null}
@@ -16242,7 +16248,7 @@ const releaseToConnectLabel = "Release to Connect"
{!distributedFromParent ? {!distributedFromParent ?
isCorrectOrg ? null : isCorrectOrg ? null :
<Typography variant="body2"> <Typography variant="body2" style={{marginLeft: 10, }}>
<b>Warning</b>: Change <span <b>Warning</b>: Change <span
style={{color: "#FF8544", cursor: "pointer", pointerEvents: "auto", }} style={{color: "#FF8544", cursor: "pointer", pointerEvents: "auto", }}
onClick={() => { onClick={() => {
@@ -16313,7 +16319,7 @@ const releaseToConnectLabel = "Release to Connect"
>Active Organization</span> to edit this Workflow. >Active Organization</span> to edit this Workflow.
</Typography> </Typography>
: :
<Typography variant="body2" color="textSecondary"> <Typography variant="body2" color="textSecondary" style={{marginLeft: 10, }}>
Warning: This workflow is controlled by your parent org and may not be editable. Warning: This workflow is controlled by your parent org and may not be editable.
</Typography> </Typography>
} }
+9 -2
View File
@@ -69,6 +69,7 @@ const ApiExplorerWrapper = (props) => {
const [authenticationType, setAuthenticationType] = React.useState(""); const [authenticationType, setAuthenticationType] = React.useState("");
const [appAuthentication, setAppAuthentication] = useState([]); const [appAuthentication, setAppAuthentication] = useState([]);
const [selectedMeta, setSelectedMeta] = useState(undefined); const [selectedMeta, setSelectedMeta] = useState(undefined);
const [appLoaded, setAppLoaded] = useState(false);
const [selectedAction, setSelectedAction] = useState( const [selectedAction, setSelectedAction] = useState(
{ {
"app_name": selectedAppData.name, "app_name": selectedAppData.name,
@@ -258,6 +259,7 @@ const ApiExplorerWrapper = (props) => {
parsedapp.body === undefined ? parsedapp : JSON.parse(parsedapp.body); parsedapp.body === undefined ? parsedapp : JSON.parse(parsedapp.body);
setOpenapi(data); setOpenapi(data);
setAppLoaded(true);
}; };
const handleAppAuthenticationType = (selectedAppData) => { const handleAppAuthenticationType = (selectedAppData) => {
@@ -773,7 +775,7 @@ const ApiExplorerWrapper = (props) => {
); );
}; };
const skeletonLoader = ( const SkeletonLoader = () => (
<Box <Box
sx={{ sx={{
display: "flex", display: "flex",
@@ -892,6 +894,7 @@ const ApiExplorerWrapper = (props) => {
</Box> </Box>
); );
const AuthenticationData = (props) => { const AuthenticationData = (props) => {
const selectedApp = props.app; const selectedApp = props.app;
@@ -1756,7 +1759,10 @@ const ApiExplorerWrapper = (props) => {
return ( return (
<Wrapper isLoggedIn={isLoggedIn} isLoaded={isLoaded}> <Wrapper isLoggedIn={isLoggedIn} isLoaded={isLoaded}>
<Suspense fallback={skeletonLoader}> {appLoaded === false ? (
<SkeletonLoader />
) : (
<Suspense fallback={SkeletonLoader}>
{authenticationModal} {authenticationModal}
<ApiExplorer <ApiExplorer
openapi={openapi} openapi={openapi}
@@ -1770,6 +1776,7 @@ const ApiExplorerWrapper = (props) => {
isLoaded={isLoaded} isLoaded={isLoaded}
/> />
</Suspense> </Suspense>
)}
</Wrapper> </Wrapper>
); );
}; };
+5 -3
View File
@@ -5811,9 +5811,11 @@ const AppCreator = (defaultprops) => {
variant="outlined" variant="outlined"
color="secondary" color="secondary"
onClick={() => { onClick={() => {
var urlParams = new URLSearchParams(window.location.search); var urlParams = new URLSearchParams(window.location.search)
if (!urlParams.has("id")) { if (urlParams.has("id")) {
window.open(`/apis/${app.id}`, "_blank") window.open(`/apis/${urlParams.get("id")}`, "_blank")
} else if (props.match.params.appid !== undefined && props.match.params.appid !== null && props.match.params.appid.length > 0) {
window.open(`/apis/${props.match.params.appid}`, "_blank")
} else { } else {
toast.error("Build the app first.") toast.error("Build the app first.")
} }
+122 -6
View File
@@ -1,7 +1,6 @@
import React, { useState, useEffect, useContext, useCallback, memo, useMemo, useRef } from "react"; import React, { useState, useEffect, useContext, useCallback, memo, useMemo, useRef } 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 { useLocation, useNavigate } from "react-router-dom"; import { useLocation, useNavigate } from "react-router-dom";
import { import {
TextField, Button, Typography, MenuItem, Select, Tabs, Tab, Zoom, TextField, Button, Typography, MenuItem, Select, Tabs, Tab, Zoom,
@@ -75,6 +74,22 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl,
style={paperStyle} style={paperStyle}
onMouseOver={() => setMouseHoverIndex(index)} onMouseOver={() => setMouseHoverIndex(index)}
onMouseOut={() => setMouseHoverIndex(-1)} onMouseOut={() => setMouseHoverIndex(-1)}
>
<Tooltip
title="View app details"
placement="top"
componentsProps={{
tooltip: {
sx: {
backgroundColor: "rgba(33, 33, 33, 1)",
color: "rgba(241, 241, 241, 1)",
fontSize: 14,
border: "1px solid rgba(73, 73, 73, 1)",
fontFamily: theme?.typography?.fontFamily,
}
},
}}
arrow
> >
<ButtonBase <ButtonBase
style={{ style={{
@@ -111,7 +126,7 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl,
fontWeight: '400', fontWeight: '400',
overflow: "hidden", overflow: "hidden",
margin: "12px 0", margin: "12px 0",
fontFamily: theme?.typography?.fontFamily fontFamily: theme?.typography?.fontFamily,
}}> }}>
<div style={{ <div style={{
display: 'flex', display: 'flex',
@@ -207,6 +222,8 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl,
bgcolor: "rgba(93, 93, 93, 1)", bgcolor: "rgba(93, 93, 93, 1)",
}, },
}} }}
onMouseDown={(e) => e.stopPropagation()}
onMouseUp={(e) => e.stopPropagation()}
onClick={(event) => { onClick={(event) => {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
@@ -241,6 +258,7 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl,
</div> </div>
</div> </div>
</ButtonBase> </ButtonBase>
</Tooltip>
</Paper> </Paper>
</Grid> </Grid>
); );
@@ -263,10 +281,11 @@ const Hits = ({
const [allActivatedAppIds, setAllActivatedAppIds] = useState([]); const [allActivatedAppIds, setAllActivatedAppIds] = useState([]);
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
const [deactivatedIndexes, setDeactivatedIndexes] = React.useState([]); const [deactivatedIndexes, setDeactivatedIndexes] = React.useState([]);
const [isLoading, setIsLoading] = useState(true) const [isLoading, setIsLoading] = useState(false)
useEffect(() => { useEffect(() => {
var baseurl = globalUrl; var baseurl = globalUrl;
setIsLoading(true)
fetch(baseurl + "/api/v1/me", { fetch(baseurl + "/api/v1/me", {
credentials: "include", credentials: "include",
headers: { headers: {
@@ -361,7 +380,34 @@ const Hits = ({
( (
<div> <div>
{hits?.length === 0 && searchQuery.length >= 0 ? ( {hits?.length === 0 && searchQuery.length >= 0 ? (
<Typography>No apps found</Typography> <div style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
minHeight: 400,
gap: 16,
color: "#F1F1F1",
fontFamily: theme?.typography?.fontFamily
}}>
<SearchIcon style={{ fontSize: 48, color: 'rgba(255, 255, 255, 0.7)' }} />
<Typography variant="h6" style={{
fontFamily: theme?.typography?.fontFamily,
textAlign: "center"
}}>
No apps found matching your search criteria
</Typography>
<Typography
variant="body1"
style={{
color: 'rgba(255, 255, 255, 0.7)',
textAlign: "center",
maxWidth: 400
}}
>
Try adjusting your search terms or filters to find what you're looking for
</Typography>
</div>
) : ( ) : (
<div <div
style={{ style={{
@@ -412,6 +458,22 @@ const Hits = ({
onMouseLeave={() => { onMouseLeave={() => {
setHoverEffect(-1); setHoverEffect(-1);
}} }}
>
<Tooltip
title="View app details"
placement="top"
componentsProps={{
tooltip: {
sx: {
backgroundColor: "rgba(33, 33, 33, 1)",
color: "rgba(241, 241, 241, 1)",
fontSize: 14,
border: "1px solid rgba(73, 73, 73, 1)",
fontFamily: theme?.typography?.fontFamily,
}
},
}}
arrow
> >
<ButtonBase style={{ <ButtonBase style={{
borderRadius: 6, borderRadius: 6,
@@ -448,8 +510,9 @@ const Hits = ({
gap: 6, gap: 6,
fontWeight: '400', fontWeight: '400',
overflow: "hidden", overflow: "hidden",
margin: "12px 0", fontFamily: theme?.typography?.fontFamily,
fontFamily: theme?.typography?.fontFamily marginTop: 8,
marginLeft: 16,
}} }}
> >
<div <div
@@ -571,6 +634,8 @@ const Hits = ({
fontFamily: theme?.typography?.fontFamily, fontFamily: theme?.typography?.fontFamily,
fontSize: 16, fontSize: 16,
}} }}
onMouseDown={(e) => e.stopPropagation()}
onMouseUp={(e) => e.stopPropagation()}
onClick={(event) => { onClick={(event) => {
handleActivateButton(event, data, "deactivate"); handleActivateButton(event, data, "deactivate");
}}> }}>
@@ -588,6 +653,8 @@ const Hits = ({
fontFamily: theme?.typography?.fontFamily, fontFamily: theme?.typography?.fontFamily,
fontSize: 16 fontSize: 16
}} }}
onMouseDown={(e) => e.stopPropagation()}
onMouseUp={(e) => e.stopPropagation()}
onClick={(event) => { onClick={(event) => {
handleActivateButton(event, data, "activate"); handleActivateButton(event, data, "activate");
}} }}
@@ -601,6 +668,7 @@ const Hits = ({
</div> </div>
</div> </div>
</ButtonBase> </ButtonBase>
</Tooltip>
</Paper> </Paper>
</Zoom> </Zoom>
); );
@@ -872,6 +940,42 @@ const filterApps = (apps, searchQuery, selectedCategory, selectedLabel) => {
}); });
}; };
const LoginPrompt = () => {
const navigate = useNavigate();
return (
<div style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
minHeight: 400,
gap: 20,
color: "#F1F1F1",
fontFamily: theme?.typography?.fontFamily
}}>
<Typography variant="h6" style={{ fontFamily: theme?.typography?.fontFamily }}>
Log in to see your organization's apps
</Typography>
<Button
variant="contained"
onClick={() => navigate("/login")}
style={{
backgroundColor: "#FF8544",
color: "#1A1A1A",
textTransform: "none",
fontFamily: theme?.typography?.fontFamily,
fontSize: 16,
padding: "10px 20px",
borderRadius: 4,
fontWeight: 600
}}
>
Log In
</Button>
</div>
)
};
// Add this new component for the app skeleton // Add this new component for the app skeleton
const AppSkeleton = () => { const AppSkeleton = () => {
return ( return (
@@ -1895,6 +1999,7 @@ const Apps2 = (props) => {
fullWidth fullWidth
variant="outlined" variant="outlined"
placeholder="Search for apps" placeholder="Search for apps"
disabled={!isLoggedIn}
value={searchQuery} value={searchQuery}
id="shuffle_search_field" id="shuffle_search_field"
onChange={handleSearchChange} onChange={handleSearchChange}
@@ -1945,6 +2050,7 @@ const Apps2 = (props) => {
variant="outlined" variant="outlined"
value={selectedCategory} value={selectedCategory}
onChange={handleCategoryChange} onChange={handleCategoryChange}
disabled={!isLoggedIn}
displayEmpty displayEmpty
multiple multiple
style={{ style={{
@@ -2009,6 +2115,7 @@ const Apps2 = (props) => {
variant="outlined" variant="outlined"
value={selectedLabel} value={selectedLabel}
onChange={handleLabelChange} onChange={handleLabelChange}
disabled={!isLoggedIn}
displayEmpty displayEmpty
multiple multiple
style={{ style={{
@@ -2067,6 +2174,7 @@ const Apps2 = (props) => {
variant="contained" variant="contained"
color="primary" color="primary"
onClick={handleCreateApp} onClick={handleCreateApp}
disabled={!isLoggedIn}
style={{ style={{
height: "100%", height: "100%",
width: '100%', width: '100%',
@@ -2092,6 +2200,7 @@ const Apps2 = (props) => {
{isLoading ? ( {isLoading ? (
<LoadingGrid /> <LoadingGrid />
) : ( ) : (
isLoggedIn && !isLoading ? (
<> <>
{appsToShow?.length > 0 && appsToShow !== undefined && !isLoading ? ( {appsToShow?.length > 0 && appsToShow !== undefined && !isLoading ? (
<div style={{ <div style={{
@@ -2137,6 +2246,9 @@ const Apps2 = (props) => {
</div> </div>
)} )}
</> </>
) : (
<LoginPrompt />
)
)} )}
</div> </div>
) )
@@ -2147,6 +2259,7 @@ const Apps2 = (props) => {
{isLoading ? ( {isLoading ? (
<LoadingGrid /> <LoadingGrid />
) : ( ) : (
isLoggedIn && !isLoading ? (
<> <>
{appsToShow?.length > 0 && appsToShow !== undefined ? ( {appsToShow?.length > 0 && appsToShow !== undefined ? (
<div style={{ <div style={{
@@ -2182,6 +2295,9 @@ const Apps2 = (props) => {
</div> </div>
)} )}
</> </>
) : (
<LoginPrompt />
)
)} )}
</div> </div>
) )
+16 -12
View File
@@ -716,6 +716,8 @@ const Workflows2 = (props) => {
const [videoViewOpen, setVideoViewOpen] = React.useState(false) const [videoViewOpen, setVideoViewOpen] = React.useState(false)
const [gettingStartedItems, setGettingStartedItems] = React.useState([]) const [gettingStartedItems, setGettingStartedItems] = React.useState([])
const [selectedWorkflowIndexes, setSelectedWorkflowIndexes] = React.useState([]) const [selectedWorkflowIndexes, setSelectedWorkflowIndexes] = React.useState([])
const [page, setPage] = React.useState(0);
const [pageSize, setPageSize] = React.useState(100);
const [highlightIds, setHighlightIds] = React.useState([]) const [highlightIds, setHighlightIds] = React.useState([])
const [apps, setApps] = React.useState([]); const [apps, setApps] = React.useState([]);
@@ -751,15 +753,6 @@ const Workflows2 = (props) => {
navigate(`${location.pathname}?${queryParams.toString()}`); navigate(`${location.pathname}?${queryParams.toString()}`);
}; };
useEffect(() => {
if (currTab === 2) {
setIsLoadingPublicWorkflow(true);
// Simulate loading time for the Algolia search results
setTimeout(() => {
setIsLoadingPublicWorkflow(false);
}, 2500);
}
}, [currTab])
@@ -1322,6 +1315,8 @@ const Workflows2 = (props) => {
if (responseJson !== undefined && responseJson !== null) { if (responseJson !== undefined && responseJson !== null) {
if (responseJson.success === false) { if (responseJson.success === false) {
} else if (responseJson.length === 0) { } else if (responseJson.length === 0) {
// When there are no workflows, we can set the loading to false
setIsLoadingWorkflow(false)
if (currTab !== 2) { if (currTab !== 2) {
toast("No workflows found. Showing workflow discovery") toast("No workflows found. Showing workflow discovery")
setCurrTab(2) setCurrTab(2)
@@ -2483,6 +2478,7 @@ const Workflows2 = (props) => {
} }
} }
return ( return (
<div style={{ width: "100%", minWidth: 320, position: "relative", border: highlightIds.includes(data.id) ? "2px solid #f85a3e" : isDistributed || hasSuborgs ? "2px solid #40E0D0" : "inherit", borderRadius: theme.palette?.borderRadius, backgroundColor: "#212121", fontFamily: theme?.typography?.fontFamily }}> <div style={{ width: "100%", minWidth: 320, position: "relative", border: highlightIds.includes(data.id) ? "2px solid #f85a3e" : isDistributed || hasSuborgs ? "2px solid #40E0D0" : "inherit", borderRadius: theme.palette?.borderRadius, backgroundColor: "#212121", fontFamily: theme?.typography?.fontFamily }}>
<Paper square style={paperAppStyle}> <Paper square style={paperAppStyle}>
@@ -2537,7 +2533,7 @@ const Workflows2 = (props) => {
maxWidth: 310, maxWidth: 310,
padding: "12px 0", padding: "12px 0",
}}> }}>
{(data?.image !== undefined || data?.image_url !== undefined) ? ( {(data?.image !== undefined || (data?.image_url !== undefined && data?.image_url.length > 0)) ? (
<div style={{ <div style={{
marginBottom: 15, marginBottom: 15,
borderRadius: theme.palette?.borderRadius, borderRadius: theme.palette?.borderRadius,
@@ -3371,7 +3367,15 @@ const Workflows2 = (props) => {
className={classes.datagrid} className={classes.datagrid}
rows={rows} rows={rows}
columns={columns} columns={columns}
pageSize={100} page={page}
onPageChange={(newPage) => {
setPage(newPage)
}}
pageSize={pageSize}
onPageSizeChange={(newPageSize) => {
setPageSize(newPageSize);
}}
rowsPerPageOptions={[25, 50, 100, 150]}
checkboxSelection checkboxSelection
autoHeight autoHeight
density="standard" density="standard"
@@ -3962,7 +3966,7 @@ const Workflows2 = (props) => {
setIsLoadingWorkflow(false); setIsLoadingWorkflow(false);
} }
}, [currTab, workflows, userdata, filteredWorkflows]) }, [currTab, workflows, userdata, filteredWorkflows, filters])
+5
View File
@@ -2298,6 +2298,10 @@ func main() {
} else if incRequest.Type == "START_TENZIR" { } else if incRequest.Type == "START_TENZIR" {
log.Printf("[INFO] Got job to start tenzir") log.Printf("[INFO] Got job to start tenzir")
// Manual command = overrides to allow starting of Tenzir from the frontend anyway.
os.Setenv("SHUFFLE_SKIP_PIPELINES", "false")
tenzirDisabled = false
err := deployTenzirNode() err := deployTenzirNode()
if err != nil { if err != nil {
if strings.Contains(fmt.Sprintf("%s", err), "node available") { if strings.Contains(fmt.Sprintf("%s", err), "node available") {
@@ -3456,6 +3460,7 @@ func sendPipelineHealthStatus() (shuffle.LakeConfig, error) {
if (!strings.Contains(err.Error(), "SHUFFLE_SKIP_PIPELINES") && !strings.Contains(err.Error(), "Kubernetes not implemented for Tenzir node")) && !strings.Contains(err.Error(), "Tenzir Node is already running") && !strings.Contains(err.Error(), "docker daemon") { if (!strings.Contains(err.Error(), "SHUFFLE_SKIP_PIPELINES") && !strings.Contains(err.Error(), "Kubernetes not implemented for Tenzir node")) && !strings.Contains(err.Error(), "Tenzir Node is already running") && !strings.Contains(err.Error(), "docker daemon") {
log.Printf("[ERROR] Tenzir node connection problem: %s", err) log.Printf("[ERROR] Tenzir node connection problem: %s", err)
} else { } else {
tenzirDisabled = true tenzirDisabled = true
log.Printf("[ERROR] Disabling pipelines: %s. You will need to restart the Orborus to fix this.", err) log.Printf("[ERROR] Disabling pipelines: %s. You will need to restart the Orborus to fix this.", err)