leumas-private-shared
Version:
Private React JSX Package For Leumas Shared Components, Headers, Footers, Asides, Login Pages, API Key Manager and much more. Styles and everything reusable to avoid DRY code across all of our subdomains
308 lines (265 loc) • 11 kB
JSX
import { useState , useEffect } from 'react';
import {LeumasBaseStyle} from "../../../styles/baseStyles"
import {useAuthUser} from "react-auth-kit"
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 Spinner2 from "../../../components/Loaders/Spinner2"
import { runBlocks } from './Helpers/runBlocks';
import React from 'react';
const DEFAULT_ENDPOINT = `${import.meta.env.VITE_REACT_APP_LEUMAS_API_ENDPOINT}/wildcards/bulk-request`;
export const EndpointBuilder = () => {
const auth = useAuthUser()
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 [bulkEndpoints, setBulkEndpoints] = useState({
endpoints: [{
method: 'GET',
endpoint: '',
body: '{}',
headers: '{}',
params: '{}',
}]
});
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);
}
})
.catch((error) => {
console.error("Error fetching endpoints:", error.message);
});
}, [auth()?.id]);
useEffect(() => {
// When selectedEndpoint changes, update the state
if (selectedEndpoint) {
setBulkEndpoints(prev => ({
...prev,
endpoints: selectedEndpoint.endpoints
}));
}
}, [selectedEndpoint]);
const handleEndpointSelectChange = (e) => {
const selectedId = e.target.value;
const endpointToLoad = models.find(model => model._id === selectedId);
setSelectedEndpoint(endpointToLoad);
}
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 ">
<aside className='flex flex-col min-w-[150px] max-w-[250px]'>
<button onClick={addBlock} className={LeumasBaseStyle}>Add New</button>
<button onClick={handleRunBlocks} className={LeumasBaseStyle}>Run Endpoints</button>
<EditItem
model={"Endpoint"}
id={selectedEndpoint?._id}
endpoint={"LeumasAPI"}
data={endpointData}
token={auth()?.token}
/>
<DeleteItem
model={"Endpoint"}
id={selectedEndpoint?._id}
endpoint={"LeumasAPI"}
token={auth()?.token}
/>
<SaveButton
model={"Endpoint"}
data={endpointData}
isLoading={isLoading}
setIsLoading={setIsLoading}
setMessage={setMessage}
setMessageType={setMessageType}
onSuccess={handleSaveSuccess} // Add this prop to handle the save success
/>
<div className={`${LeumasBaseStyle}`}>
<label htmlFor="endpointName" className="block mb-2 text-sm font-medium ">Endpoint Model Name:</label>
<input
type="text"
id="endpointName"
placeholder="Enter endpoint model name"
className={` ${LeumasBaseStyle} w-full`}
value={endpointName}
onChange={(e) => setEndpointName(e.target.value)}
/>
</div>
<div className={` ${LeumasBaseStyle}`}>
<label htmlFor="endpointSelector" className="block mb-2 text-sm font-medium ">Select Endpoint:</label>
<select
id="endpointSelector"
className={` ${LeumasBaseStyle} w-full`}
value={selectedEndpoint || ""}
onChange={()=>handleEndpointSelectChange}
>
{/* <option value="" disabled>Select an endpoint...</option> */}
{models.map((endpoint, index) => (
<option key={index} value={endpoint._id}>
{endpoint.name || `Endpoint ${index + 1}`}
</option>
))}
</select>
</div>
</aside>
<div className='flex flex-col w-full'>
<div className={`results bg-gray-800 text-white p-2 w-full mx-auto border-2 border-gray-700 rounded-lg shadow-lg overflow-auto max-h-[100px] overflow-y-scroll`}>
{results.map((result, index) => (
<div key={index} className="mb-4 last:mb-0">
<p className="text-blue-500 font-mono">Block {result.index}: {result.message}</p>
<pre className="whitespace-pre-wrap bg-gray-900 text-blue-300 p-4 rounded">
{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'>
{isLoading ? (
<div className="col-span-2 flex justify-center items-center h-60">
<Spinner2 loading={isLoading} />
</div>
) : (
bulkEndpoints.endpoints.map((request, index) => (
<div key={index} className={`${LeumasBaseStyle} min-w-[250px] ${getBorderColor(index)} border`}>
<p>Block {index}</p>
<div className="flex space-x-2">
<select
value={request.method}
onChange={(e) => handleChange(index, 'method', e.target.value)}
className={`${inputStyle}`}
>
<option className={transparent} value="GET">GET</option>
<option className={transparent} value="POST">POST</option>
<option className={transparent} value="PUT">PUT</option>
<option className={transparent}value="DELETE">DELETE</option>
</select>
<input
type="text"
value={request.endpoint}
onChange={(e) => handleChange(index, 'endpoint', e.target.value)}
className={`${inputStyle} flex-grow`}
placeholder="Enter endpoint URL"
/>
</div>
<label className="block mt-2">Body</label>
<textarea
value={request.body}
onChange={(e) => handleChange(index, 'body', e.target.value)}
className={inputStyle}
placeholder="Body (JSON)"
rows="3"
/>
<label className="block mt-2">Headers</label>
<textarea
value={request.headers}
onChange={(e) => handleChange(index, 'headers', e.target.value)}
className={inputStyle}
placeholder="Headers (JSON)"
rows="2"
/>
<label className="block mt-2">Params</label>
<textarea
value={request.params}
onChange={(e) => handleChange(index, 'params', e.target.value)}
className={inputStyle}
placeholder="Params (JSON)"
rows="2"
/>
<button onClick={() => deleteBlock(index)} className={LeumasBaseStyle}>Delete</button>
</div>
))
)}
</div>
</div>
</div>
);
};
export default EndpointBuilder