import React, { useState, useRef, useEffect } from 'react'
import { useNavigate, Link } from 'react-router-dom';
import {
Dialog,
DialogTitle,
DialogContent,
Typography,
Paper,
Button,
FormControl,
TextField,
DialogActions,
IconButton,
CircularProgress
} from '@mui/material'
// Icons
import CloseIcon from '@mui/icons-material/Close'
import PublishIcon from '@mui/icons-material/Publish'
import AutoFixHighIcon from '@mui/icons-material/AutoFixHigh'
import CreateIcon from '@mui/icons-material/Create'
import { toast } from 'react-toastify'
import YAML from "yaml";
import Dropzone from "./Dropzone.jsx";
const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud, startOpenApi = false, prefillOpenApiData = "" }) => {
const [openApiModal, setOpenApiModal] = useState(false)
const [generateAppModal, setGenerateAppModal] = useState(false)
const [openApi, setOpenApi] = useState("")
const [openApiData, setOpenApiData] = useState("")
const [openApiError, setOpenApiError] = useState("")
const [validation, setValidation] = useState(false)
const [appValidation, setAppValidation] = useState("")
const [isDropzone, setIsDropzone] = useState(false)
const navigate = useNavigate()
const upload = useRef()
useEffect(() => {
if (open && (startOpenApi || (prefillOpenApiData && prefillOpenApiData.length > 0))) {
if (prefillOpenApiData && prefillOpenApiData.length > 0) {
setOpenApiData(prefillOpenApiData)
setIsDropzone(true)
}
setOpenApiModal(true)
}
}, [open, startOpenApi, prefillOpenApiData])
// Style for the create options
const AppCreateButton = ({ text, func, icon }) => {
const [hover, setHover] = React.useState(false)
const makeFancy = text?.includes("Generate")
const parsedStyle = {
flex: 1,
padding: "30px 20px 20px",
margin: 12,
backgroundColor: hover && !makeFancy ? theme.palette.surfaceColor : "transparent",
cursor: hover ? "pointer" : "default",
textAlign: "center",
minHeight: 180,
maxHeight: 180,
borderRadius: 8,
border: makeFancy
? "1px solid transparent"
: hover
? ""
: "1px solid rgba(255,255,255,0.3)",
borderImage: makeFancy ? "linear-gradient(to right, #ff8544 0%, #ec517c 50%, #9c5af2 100%) 1" : "none",
transition: 'all 0.2s ease-in-out',
paddingBottom: isCloud ? 0 : 175,
}
return (
setHover(true)}
onMouseLeave={() => setHover(false)}
onClick={func}
style={parsedStyle}
>
{icon}
{text}
)
}
const uploadFile = (e) => {
const isDropzone =
e.dataTransfer === undefined ? false : e.dataTransfer.files.length > 0;
const files = isDropzone ? e.dataTransfer.files : e.target.files;
const reader = new FileReader();
try {
reader.addEventListener("load", (e) => {
const content = e.target.result;
setOpenApiData(content);
setIsDropzone(isDropzone);
setOpenApiModal(true);
});
} catch (e) {
console.log("Error in dropzone: ", e);
}
try {
reader.readAsText(files[0]);
} catch (error) {
toast("Failed to read file");
}
};
useEffect(() => {
if (openApiData.length > 0) {
setOpenApiError("");
validateOpenApi(openApiData);
}
}, [openApiData]);
// useEffect(() => {
// console.log("APPVALID: ", appValidation)
// redirectOpenApi()
// }, [appValidation])
useEffect(() => {
if (appValidation && isDropzone) {
redirectOpenApi();
setIsDropzone(false);
}
}, [appValidation, isDropzone]);
const validateDocumentationUrl = () => {
setValidation(true);
// curl https://doc-to-openapi-stbuwivzoq-nw.a.run.app/doc_to_openapi -d '{"url": "https://gitlab.com/rhab/PyOTRS/-/raw/main/pyotrs/lib.py?ref_type=heads"}' -H "Content-Type: application/json"
const urldata = {
"url": openApi,
}
//fetch("http://localhost:8080/doc_to_openapi", {
//fetch("https://doc-to-openapi-stbuwivzoq-nw.a.run.app/doc_to_openapi", {
fetch("https://doc-to-openapi-stbuwivzoq-nw.a.run.app/api/v1/doc_to_openapi", {
method: "POST",
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify(urldata),
})
.then((response) => {
setValidation(false);
if (response.status !== 200) {
toast("Error in generation: " + response.status);
setOpenApiError("Error in generation - bad status: " + response.status);
return response.text();
}
return response.json();
})
.then((responseJson) => {
// Check if openapi or swagger in string of the json
var parsedtext = responseJson
try {
parsedtext = JSON.stringify(responseJson);
if (parsedtext.indexOf("openapi") === -1 && parsedtext.indexOf("swagger") === -1) {
setValidation(false)
setOpenApiError("Error in generation: " + parsedtext)
return
}
} catch (e) {
setValidation(false);
setOpenApiError("Error in generation (2): " + e.toString());
return;
}
console.log("Validating response!");
validateOpenApi(parsedtext)
})
.catch((error) => {
setValidation(false);
toast(error.toString());
setOpenApiError(error.toString());
});
}
const validateRemote = () => {
setValidation(true);
fetch(globalUrl + "/api/v1/get_openapi_uri", {
method: "POST",
headers: {
Accept: "application/json",
},
body: JSON.stringify(openApi),
credentials: "include",
})
.then((response) => {
setValidation(false);
if (response.status !== 200) {
return response.json();
}
return response.text();
})
.then((responseJson) => {
if (typeof responseJson !== "string" && !responseJson.success) {
console.log(responseJson.reason);
if (responseJson.reason !== undefined) {
setOpenApiError(responseJson.reason);
} else {
setOpenApiError("Undefined issue with OpenAPI validation");
}
return;
}
console.log("Validating response!");
validateOpenApi(responseJson);
})
.catch((error) => {
toast(error.toString());
setOpenApiError(error.toString());
});
};
const escapeApiData = (apidata) => {
//console.log(apidata)
try {
return JSON.stringify(JSON.parse(apidata));
} catch (error) {
console.log("JSON DECODE ERROR - TRY YAML");
}
try {
const parsed = YAML.parse(YAML.stringify(apidata));
//const parsed = YAML.parse(apidata))
console.log(YAML.stringify(parsed))
return YAML.stringify(parsed);
} catch (error) {
console.log("YAML DECODE ERROR - TRY SOMETHING ELSE?: " + error);
setOpenApiError("Local error: " + error.toString());
}
return "";
};
const validateOpenApi = (openApidata) => {
var newApidata = escapeApiData(openApidata);
if (newApidata === "") {
// Used to return here
newApidata = openApidata;
return;
}
//console.log(newApidata)
setValidation(true);
fetch(globalUrl + "/api/v1/validate_openapi", {
method: "POST",
headers: {
Accept: "application/json",
},
body: openApidata,
credentials: "include",
})
.then((response) => {
setValidation(false);
return response.json();
})
.then((responseJson) => {
if (responseJson?.success === true) {
setAppValidation(responseJson?.id)
navigate(`/apps/new?id=${responseJson?.id}`)
} else {
if (responseJson.reason !== undefined) {
setOpenApiError(responseJson.reason)
}
toast("An error occurred in the response");
}
})
.catch((error) => {
setValidation(false);
toast(error.toString());
setOpenApiError(error.toString());
});
};
const redirectOpenApi = () => {
if (appValidation === undefined || appValidation === null || appValidation.length === 0) {
return
}
toast.success("Successfully validated OpenAPI. Redirecting to app creation. Remember to save the app to be able to use it.", {
// Disable autoclose
autoClose: 10000,
})
navigate(`/apps/new?id=${appValidation}`)
}
// const validateOpenApi = (openApidata) => {
// var newApidata = escapeApiData(openApidata);
// if (newApidata === "") {
// // Used to return here
// newApidata = openApidata;
// return;
// }
// setValidation(true)
// const url = `${globalUrl}/api/v1/verify_openapi`
// fetch(url, {
// method: "POST",
// headers: {
// "Content-Type": "application/json",
// Accept: "application/json",
// },
// body: JSON.stringify(openApidata),
// credentials: "include",
// })
// .then((response) => response.json())
// .then((responseJson) => {
// if (responseJson.success === false) {
// setOpenApiError(responseJson.reason)
// setValidation(false)
// setAppValidation("")
// } else {
// setAppValidation(responseJson.id)
// setValidation(false)
// }
// })
// .catch((error) => {
// setOpenApiError("Failed loading: " + error.toString())
// setValidation(false)
// setAppValidation("")
// })
// }
// Validation and redirect functions
const circularLoader = validation ? : null
const errorText = openApiError?.length > 0 ?
Error: {openApiError}
: null
// Common dialog styles
const dialogStyle = {
borderRadius: 2,
border: theme.palette.DialogStyle.border,
minWidth: '500px',
fontFamily: theme?.typography?.fontFamily,
backgroundColor: theme.palette.DialogStyle.backgroundColor,
zIndex: 1000,
'& .MuiDialogContent-root': {
backgroundColor: theme.palette.DialogStyle.backgroundColor,
padding: '24px',
fontFamily: theme?.typography?.fontFamily,
},
'& .MuiDialogTitle-root': {
backgroundColor: theme.palette.DialogStyle.backgroundColor,
padding: '24px',
fontFamily: theme?.typography?.fontFamily,
},
'& .MuiDialogActions-root': {
backgroundColor: theme.palette.DialogStyle.backgroundColor,
padding: '16px 24px',
fontFamily: theme?.typography?.fontFamily,
},
'& .MuiTypography-root': {
fontFamily: theme?.typography?.fontFamily,
},
'& .MuiButton-root': {
fontFamily: theme?.typography?.fontFamily,
},
transition: 'all 0.2s ease-in-out',
};
return (
<>
{/* Main App Creation Modal */}
{/* OpenAPI Modal */}
{/* Generate App Modal */}
>
)
}
export default AppCreationModal