leumas-universal-crud-react
Version:
Leumas Universal CRUD to a dynamic Endpoint, Setup your own Dynamic Endpoint and Use Leumas API to send to your MONGO clusters
337 lines (285 loc) • 11.8 kB
JSX
import { useState , useEffect } from 'react';
import {LeumasBaseStyle} from "../../styles/baseStyles"
import useAuthUser from 'react-auth-kit/hooks/useAuthUser';
import {getItemsByOwner} from "../UniversalCrud/UniversalCrudHelpers"
import SaveButton from "../UniversalCrud/SaveButton"
import EditItem from "../UniversalCrud/EditItem"
import DeleteItem from "../UniversalCrud/DeleteItem"
import axios from "axios"
import {AtomSpinner} from "react-epic-spinners"
import { runBlocks } from './Helpers/runBlocks';
import CustomDropdown from '../UniversalCrud/CustomDropdown';
import {useParams} from 'react-router-dom'
import { useTheme } from "../../Theme/ThemeContext";
const lightThemeColors = {
background: '#FAFAFA',
primary: '#0077C0',
secondary: '#C7EEFF',
text: '#1D242B',
};
const darkThemeColors = {
background: '#1D242B',
primary: '#0077C0',
secondary: '#2A3B4D',
text: '#FAFAFA',
cardText: '#E0E0E0',
};
const useColors = (theme) => {
return theme === 'dark' ? darkThemeColors : lightThemeColors;
};
const DEFAULT_ENDPOINT = `${import.meta.env.VITE_REACT_APP_LEUMAS_API_ENDPOINT}/wildcards/bulk-request`;
export const EndpointBuilder = () => {
const auth = useAuthUser()
const { theme } = useTheme();
const colors = useColors(theme);
const [models , setModels] = useState([]);
// Structuring data for your backend schema
const [message, setMessage] = useState("");
const [messageType , setMessageType] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [results, setResults] = useState([]);
const [selectedEndpoint, setSelectedEndpoint] = useState(null);
const [endpointName, setEndpointName] = useState("");
const {id} = useParams()
const [bulkEndpoints, setBulkEndpoints] = useState({
endpoints: [{
method: 'GET',
endpoint: '',
body: '{}',
headers: '{}',
params: '{}',
}]
});
console.log(models)
const addBlock = () => {
setBulkEndpoints(prev => ({
...prev,
endpoints: [
...prev.endpoints,
{
method: 'GET',
endpoint: '',
body: '{}',
headers: '{}',
params: '{}',
}
]
}));
};
const deleteBlock = (index) => {
const newEndpoints = [...bulkEndpoints.endpoints];
newEndpoints.splice(index, 1);
setBulkEndpoints({
...bulkEndpoints,
endpoints: newEndpoints
});
};
const handleChange = (index, field, value) => {
const newEndpoints = [...bulkEndpoints.endpoints];
newEndpoints[index][field] = value;
setBulkEndpoints({
...bulkEndpoints,
endpoints: newEndpoints
});
};
const handleRunBlocks = () => {
setIsLoading(true); // Start the loading spinner
runBlocks(DEFAULT_ENDPOINT, bulkEndpoints.endpoints,
(data) => {
console.log(data);
setResults(data);
setIsLoading(false); // Stop the loading spinner
},
(error) => {
console.error(error);
setIsLoading(false); // Stop the loading spinner
}
);
};
const fetchEndpoints = () => {
getItemsByOwner("Endpoint", auth?.id, "LeumasAPI", auth?.token)
.then((data) => {
if(!data || data.length === 0) {
console.warn("No endpoints found for the user.");
} else {
setModels(data);
console.log("Fetched endpoints:", data);
}
})
.catch((error) => {
console.error("Error fetching endpoints:", error.message);
});
};
useEffect(() => {
getItemsByOwner("Endpoint", auth?.id, "LeumasAPI", auth?.token)
.then((data) => {
if (!data || data.length === 0) {
console.warn("No endpoints found for the user.");
} else {
setModels(data);
console.log("Fetched endpoints:", data);
if (id) {
alert("Existing ID " + id);
console.log(data);
const matchedEndpoint = data.find(item => item._id === id);
if (matchedEndpoint) {
setBulkEndpoints(matchedEndpoint);
}
}
}
})
.catch((error) => {
console.error("Error fetching endpoints:", error.message);
});
}, [auth?.id, id]);
useEffect(() => {
// When selectedEndpoint changes, update the state
if (selectedEndpoint) {
setBulkEndpoints(prev => ({
...prev,
endpoints: selectedEndpoint.endpoints
}));
}
}, [selectedEndpoint]);
const handleEndpointSelectChange = (e) => {
console.log(e)
setModels(e.endpoints)
setBulkEndpoints(e)
}
const handleSaveSuccess = () => {
fetchEndpoints(); // Refetch the QR codes
setMessageType("success");
setMessage("Success in creating model, please refresh to view");
setIsLoading(false);
};
const endpointData = {
owner: auth?.id,
...bulkEndpoints,
};
function getBorderColor(requestIndex) {
const result = results.find(res => res.index === requestIndex);
if (!result) return '';
if (result.success) return ' border-4 border-green-500';
if (!result.success) return ' border-4 border-red-500';
return '';
}
const inputStyle = "border border-gray-300 rounded-lg px-3 py-2 w-full focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition bg-transparent";
const transparent = "bg-blue-400 text-white"
return (
<div className="flex sm:flex-row flex-col flex-grow z-10 w-full">
<aside className='flex flex-col min-w-[150px] p-4' style={{ backgroundColor: colors.background, color: colors.text }}>
<div className='flex sm:flex-col items-center justify-center'>
<button onClick={addBlock} className={`${LeumasBaseStyle} max-w-[150px] `} style={{ backgroundColor: colors.primary, color: colors.text }}>
Add New
</button>
<button onClick={handleRunBlocks} className={`${LeumasBaseStyle} max-w-[150px]`} style={{ backgroundColor: colors.primary, color: colors.text }}>
Run Endpoints
</button>
<SaveButton
model={"Endpoint"}
data={models}
editMode={id ? true : false}
isLoading={isLoading}
setIsLoading={setIsLoading}
setMessage={setMessage}
setMessageType={setMessageType}
onSuccess={handleSaveSuccess}
styles={` mb-3 p-0 border`}
/>
</div>
<input
type="text"
id="endpointName"
placeholder="Name"
className={`w-full rounded-lg p-2`}
value={endpointName}
onChange={(e) => setEndpointName(e.target.value)}
style={{ backgroundColor: colors.secondary, color: colors.text }}
/>
</aside>
<div className='flex flex-col w-full'>
<CustomDropdown
model="Endpoint"
modelArray={models}
endpoint="LeumasAPI"
token={auth?.token}
onSelectedChange={(e) => window.location.href = `/endpoints/bulk/${e._id}`}
/>
<div className={`results w-full mx-auto border-2 rounded-lg shadow-lg overflow-auto max-h-[100px] min-h-[100px] overflow-y-scroll p-2`} style={{ backgroundColor: colors.secondary, borderColor: colors.primary, color: colors.text }}>
{results.length <= 0 ? <p className='border h-full text-center flex items-center justify-center min-h-[80px] rounded-lg'>Sorry no results yet</p> : null}
{results.map((result, index) => (
<div key={index} className="mb-4 last:mb-0">
<p className="font-mono" style={{ color: colors.primary }}>Block {result.index}: {result.message}</p>
<pre className="whitespace-pre-wrap p-4 rounded" style={{ backgroundColor: colors.background, color: colors.cardText }}>
{JSON.stringify(result.data || result.error, null, 2)}
</pre>
</div>
))}
</div>
<div className='grid sm:grid-cols-2 grid-cols-1 max-h-[800px] min-w-[400px] overflow-y-scroll w-full gap-2' style={{ backgroundColor: colors.background, color: colors.text }}>
{isLoading ? (
<div className="col-span-2 flex justify-center items-center h-60">
<AtomSpinner />
</div>
) : (
bulkEndpoints.endpoints.map((request, index) => (
<div key={index} className={`${LeumasBaseStyle} min-w-[250px] ${getBorderColor(index)} border`} style={{ backgroundColor: colors.secondary, color: colors.text }}>
<p style={{ color: colors.text }}>Block {index}</p>
<div className="flex space-x-2">
<select
value={request.method}
onChange={(e) => handleChange(index, 'method', e.target.value)}
className={`${inputStyle}`}
style={{ backgroundColor: colors.secondary, color: colors.text }}
>
<option value="GET">GET</option>
<option value="POST">POST</option>
<option value="PUT">PUT</option>
<option value="DELETE">DELETE</option>
</select>
<input
type="text"
value={request.endpoint}
onChange={(e) => handleChange(index, 'endpoint', e.target.value)}
className={`${inputStyle} flex-grow`}
style={{ backgroundColor: colors.secondary, color: colors.text }}
placeholder="Enter endpoint URL"
/>
</div>
<label style={{ color: colors.text }} className="block mt-2">Body</label>
<textarea
value={request.body}
onChange={(e) => handleChange(index, 'body', e.target.value)}
className={inputStyle}
style={{ backgroundColor: colors.secondary, color: colors.text }}
placeholder="Body (JSON)"
rows="3"
/>
<label style={{ color: colors.text }} className="block mt-2">Headers</label>
<textarea
value={request.headers}
onChange={(e) => handleChange(index, 'headers', e.target.value)}
className={inputStyle}
style={{ backgroundColor: colors.secondary, color: colors.text }}
placeholder="Headers (JSON)"
rows="2"
/>
<label style={{ color: colors.text }} className="block mt-2">Params</label>
<textarea
value={request.params}
onChange={(e) => handleChange(index, 'params', e.target.value)}
className={inputStyle}
style={{ backgroundColor: colors.secondary, color: colors.text }}
placeholder="Params (JSON)"
rows="2"
/>
<button onClick={() => deleteBlock(index)} className={LeumasBaseStyle} style={{ backgroundColor: colors.primary, color: colors.text }}>Delete</button>
</div>
))
)}
</div>
</div>
</div>
);
};
export default EndpointBuilder